64 位 linux 中的系统调用
man syscall 的表格在这里提供了很好的概述:
arch/ABI instruction syscall # retval Notes
──────────────────────────────────────────────────────────────────
i386 int $0x80 eax eax
x86_64 syscall rax rax See below
arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 arg7 Notes
──────────────────────────────────────────────────────────────────
i386 ebx ecx edx esi edi ebp -
x86_64 rdi rsi rdx r10 r8 r9 -
我省略了这里不相关的行。在 32 位模式下,参数在ebx、ecx 等中传输,系统调用号在eax。在 64 位模式下略有不同:所有寄存器现在都是 64 位宽,因此具有不同的名称。系统调用号仍在eax 中,现在变为rax。但是参数现在传入rdi, rsi等。另外这里用syscall指令代替int 0x80来触发系统调用。
参数的顺序也可以在手册页中阅读,这里是man 2 ioctl和man 2 read:
int ioctl(int fd, unsigned long request, ...);
ssize_t read(int fd, void *buf, size_t count);
所以这里int fd的值在rdi,第二个参数在rsi等等。
如何摆脱等待换行符
首先在内存中创建一个termios结构(在.bss部分):
termios:
c_iflag resd 1 ; input mode flags
c_oflag resd 1 ; output mode flags
c_cflag resd 1 ; control mode flags
c_lflag resd 1 ; local mode flags
c_line resb 1 ; line discipline
c_cc resb 19 ; control characters
然后获取当前终端设置并禁用规范模式:
; Get current settings
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5401 ; request: TCGETS
mov rdx, termios ; request data
syscall
; Modify flags
and byte [c_lflag], 0FDh ; Clear ICANON to disable canonical mode
; Write termios structure back
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5402 ; request: TCSETS
mov rdx, termios ; request data
syscall
现在您可以使用sys_read 读取按键:
mov eax, 0 ; syscall number: SYS_read
mov edi, 0 ; int fd: STDIN_FILENO
mov rsi, buf ; void* buf
mov rdx, len ; size_t count
syscall
然后检查rax中的返回值:它包含读取的字符数。
(Or a -errno code on error,例如,如果您通过在 bash 中运行 ./a.out <&- 来关闭标准输入。使用 strace 打印程序进行的系统调用的解码跟踪,因此您不需要实际编写错误处理玩具实验。)
参考资料: