尝试在 x86-64 机器上写一个打印字符串的函数 print(char* str, size_t n),使用 int 80h 进行系统调用输出字符串,代码如下:

1
2
3
4
5
6
7
print:
mov eax, 4
mov ebx, 1
mov rcx, rdi
mov rdx, rsi
int 0x80
ret

发现实际运行有问题,如果字符串在栈上,不能打印出字符串。而定义在 .data 区的字符串却可以正常输出。经过分析发现原因如下:
int 80h 是 x86 的 32 位系统调用指令,使用 eax, edi, esi, ecx 等寄存器传递参数。如果在 64 位机器中使用 int 80h 进行输出字符串时,将字符串地址放在 rcx 寄存器中。然而,int 80h 处理系统调用时,会以为这是一个 32 地址,因此只读取 32 位的 ecx 地址,这很有可能是一个错误地址。如果字符串在.data 区,其地址一般是 0x4xxx,能用 32 位寄存器保存,因此可以正常使用 int 80h 输出。然而,若字符串在栈区,其地址较高,一般是 0x7ffxxxxxxxx,无法用 32 位寄存器保存,int 80h 只会能读取到 ecx 中 32 位的地址,这个地址是错误的,因此无法输出。
解决方案是使用 64 位的系统调用 syscall 指令:

1
2
3
4
5
6
7
print:
mov rdx, rsi
mov rsi, rdi
mov rdi, 1
mov rax, 1
syscall
ret

参考文章:https://stackoverflow.com/questions/22503944/using-interrupt-0x80-on-64-bit-linux