此代码完全基于我对 linux 参考资料的阅读。我不在linux上,所以我无法测试它,但它应该非常接近。我会使用重定向对其进行测试:a.out
#include <stdio.h>
#define SYS_READ 3
int main()
{
char buff[10]; /* Declare a buff to hold the returned string. */
ssize_t charsread; /* The number of characters returned. */
/* Use constraints to move buffer size into edx, stdin handle number
into ebx, address of buff into ecx. Also, "0" means this value
goes into the same place as parameter 0 (charsread). So eax will
hold SYS_READ (3) on input, and charsread on output. Lastly, you
MUST use the "memory" clobber since you are changing the contents
of buff without any of the constraints saying that you are.
This is a much better approach than doing the "mov" statements
inside the asm. For one thing, since gcc will be moving the
values into the registers, it can RE-USE them if you make a
second call to read more chars. */
asm volatile("int $0x80" /* Call the syscall interrupt. */
: "=a" (charsread)
: "0" (SYS_READ), "b" (STDIN_FILENO), "c" (buff), "d" (sizeof(buff))
: "memory", "cc");
printf("%d: %s", (int)charsread, buff);
return 0;
}
在下面回应 Aanchal Dalmia 的 cmets:
1) 正如 Timothy 下面所说,即使您不使用返回值,也必须让 gcc 知道 ax 寄存器正在被修改。换句话说,删除“=a”(字符读取)是不安全的,即使它看起来有效。
2) 我对你的观察感到非常困惑,除非 buff 是全局的,否则这段代码将无法工作。现在我已经安装了 linux,我能够重现错误并且我怀疑我知道问题所在。我敢打赌你在 x64 系统上使用int 0x80。这不是你应该在 64 位中调用内核的方式。
这里有一些替代代码,展示了如何在 x64 中执行此调用。请注意,函数编号和寄存器与上面的示例不同(参见http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64):
#include <stdio.h>
#define SYS_READ 0
#define STDIN_FILENO 0
int main()
{
char buff[10]; /* Declare a buff to hold the returned string. */
ssize_t charsread; /* The number of characters returned. */
/* Use constraints to move buffer size into rdx, stdin handle number
into rdi, address of buff into rsi. Also, "0" means this value
goes into the same place as parameter 0 (charsread). So eax will
hold SYS_READ on input, and charsread on output. Lastly, I
use the "memory" clobber since I am changing the contents
of buff without any of the constraints saying that I am.
This is a much better approach than doing the "mov" statements
inside the asm. For one thing, since gcc will be moving the
values into the registers, it can RE-USE them if you make a
second call to read more chars. */
asm volatile("syscall" /* Make the syscall. */
: "=a" (charsread)
: "0" (SYS_READ), "D" (STDIN_FILENO), "S" (buff), "d" (sizeof(buff))
: "rcx", "r11", "memory", "cc");
printf("%d: %s", (int)charsread, buff);
return 0;
}
需要一个比我更好的 linux 专家来解释为什么 x64 上的 int 0x80 不能与堆栈变量一起使用。但是使用 syscall 确实有效,而且在 x64 上 syscall 比 int 更快。
编辑:有人向我指出内核clobbers rcx 和 r11 在系统调用期间。不考虑这一点可能会导致各种问题,所以我将它们添加到了clobber列表中。