您可以使用特定于体系结构的约束将参数直接放置在特定寄存器中,而无需movl
内联汇编中的指令。此外,然后您可以使用&
运算符来获取字符的地址:
#include <sys/syscall.h>
void sys_putc(char c) {
// write(int fd, const void *buf, size_t count);
int ret;
asm volatile("int $0x80"
: "=a"(ret) // outputs
: "a"(SYS_write), "b"(1), "c"(&c), "d"(1) // inputs
: "memory"); // clobbers
}
int main(void) {
sys_putc('P');
sys_putc('\n');
}
(编者注:"memory"
需要clobber,或以其他方式告诉编译器&c
已读取指向的内存。 如何指示可以使用内联ASM参数*指向的内存?)
(在这种情况下,=a(ret)
需要指示系统调用破坏EAX。我们不能将 EAX 列为破坏,因为我们需要输入操作数来使用该寄存器。"a"
约束类似于"r"
但只能选择 AL/AX/EAX/RAX . )
$ cc -m32 sys_putc.c && ./a.out
P
您还可以返回系统调用返回的写入字节数,并"0"
用作再次指示EAX的约束:
int sys_putc(char c) {
int ret;
asm volatile("int $0x80" : "=a"(ret) : "0"(SYS_write), "b"(1), "c"(&c), "d"(1) : "memory");
return ret;
}
请注意,发生错误时,系统调用返回值将是(错误文件描述符)或(错误指针)之-errno
类的代码。-EBADF
-EFAULT
正常的 libc 系统调用包装函数检查返回值 unsignedeax > -4096UL
并设置 errno + return -1
。
还要注意编译-m32
是必需的:64 位syscall
ABI 使用不同的调用号(和寄存器),但是这个 asm 是硬编码调用 32 位 ABI 的缓慢方式,int $0x80
.
在 64 位模式下编译将使用 64 位调用号sys/syscall.h
进行定义,这会破坏此代码。SYS_write
即使您使用了正确的数字,64 位堆栈地址也是如此。 如果在 64 位代码中使用 32 位 int 0x80 Linux ABI 会发生什么?- 不要那样做。