我正在尝试掌握FreeBSD中的汇编语言。在开发者手册中关于UNIX过滤器的示例代码中,每次系统调用后都会重置寄存器esp。相关的代码如下:
%include 'system.inc'
section .data
hex db '0123456789ABCDEF'
buffer db 0, 0, ' '
section .text
global _start
_start:
; read a byte from stdin
push dword 1
push dword buffer
push dword stdin
sys.read
add esp, byte 12 ; <--------- Is this necessary?
or eax, eax
je .done
; convert it to hex
movzx eax, byte [buffer]
mov edx, eax
shr dl, 4
mov dl, [hex+edx]
mov [buffer], dl
and al, 0Fh
mov al, [hex+eax]
mov [buffer+1], al
; print it
push dword 3
push dword buffer
push dword stdout
sys.write
add esp, byte 12 ; <--------- Is this necessary?
jmp short _start
.done:
push dword 0
sys.exit
这与文档上一页的示例不同:
链接。
1: %include 'system.inc'
2:
3: section .data
4: hello db 'Hello, World!', 0Ah
5: hbytes equ $-hello
6:
7: section .text
8: global _start
9: _start:
10: push dword hbytes
11: push dword hello
12: push dword stdout
13: sys.write ; <--------- ESP not adjusted after this. Why?
14:
15: push dword 0
16: sys.exit
为什么这两个例子不同?为什么像
add esp, byte 12
这样的东西是必要的?系统调用不会弹出值吗?在64位FreeBSD中,因为参数不是通过栈传递的,这还有必要吗?我想象中栈指针会自己处理这些问题。
int 0x80
被用于系统调用。int 0x80
的调用规则需要在栈上推送参数,并且调用者需要自己恢复堆栈。如果你在调用int 0x80
时在栈上推送参数,那么空间必须在之后被回收。在64位代码中,系统调用是通过syscall
指令完成的。在这种情况下,系统调用参数都通过寄存器传递。在这种情况下,在执行syscall
指令后无需调整_RSP_。 - Michael Petchadd esp, byte xxx
并不是必需的,因为当完成时它们并没有返回到调用函数。它们正在执行sys.exit系统调用,这将终止该进程。系统退出调用后的代码将永远不会返回,因此堆栈的状态并不重要。 - Michael Petch