在 NASM 中调用函数之前,%rsp 是否应该与 16 字节边界对齐?
Should %rsp be aligned to 16-byte boundary before calling a function in NASM?
我从NASM的文档中看到了以下规则:
The stack pointer %rsp must be aligned to a 16-byte boundary before making a call. Fine, but the process of making a call pushes the return address (8 bytes) on the stack, so when a function gets control, %rsp is not aligned. You have to make that extra space yourself, by pushing something or subtracting 8 from %rsp.
我有一段 NASM 汇编代码如下:
在我调用“_start”中的“inc”函数之前,%rsp 应该位于8 字节的边界,这违反了NASM 文档中描述的规则。但实际上,一切都很顺利。那么,我该如何理解呢?
我是在 Ubuntu 20.04 LTS (x86_64) 下构建的。
global _start
section .data
init:
db 0x2
section .rodata
codes:
db '0123456789abcdef'
section .text
inc:
mov rax, [rsp+8] ; read param from the stack;
add rax, 0x1
ret
print:
lea rsi, [codes + rax]
mov rax, 1
mov rdi, 1
mov rdx, 1
syscall
ret
_start:
; enable AC check;
pushf
or dword [rsp], 1<<18
popf
mov rdi, [init] ; move the first 8 bytes of init to %rdi;
push rdi ; %rsp -> 8 bytes;
call inc
pop r11 ; clean stack by the caller;
call print
mov rax, 60
xor rdi, rdi
syscall
ABI 是一组规则,用于规定函数应如何表现才能相互操作。一侧的每条规则都与另一侧允许的假设配对。在这种情况下,关于调用者堆栈对齐的规则是关于被调用者堆栈对齐的允许假设。由于您的 inc
函数不依赖于 16 字节堆栈对齐,因此可以使用仅 8 字节对齐的堆栈调用该特定函数。
如果您想知道为什么在启用 AC 时它没有中断,那是因为您只从堆栈加载 8 字节值,而堆栈仍然是 8 字节对齐的。如果你也做了 sub rsp, 4
或其他破坏 8 字节对齐的事情,那么你会得到一个总线错误。
ABI 变得重要的地方是当情况不是您自己在汇编中编写的一个函数调用您自己在汇编中编写的另一个函数时。其他人的库(包括 C 标准库)中的函数,或者您从 C 编译而不是用汇编编写的函数,都有权执行 movaps [rsp - 24], xmm0
或其他操作,如果您不正确,它会中断在调用之前对齐堆栈。
旁注:ABI 还说明了您应该如何传递参数(调用约定),但您只是将它们传递到任何地方。同样,在您自己的程序集中很好,但是如果您尝试从 C 中调用它们,它们 肯定会 中断。
我从NASM的文档中看到了以下规则:
The stack pointer %rsp must be aligned to a 16-byte boundary before making a call. Fine, but the process of making a call pushes the return address (8 bytes) on the stack, so when a function gets control, %rsp is not aligned. You have to make that extra space yourself, by pushing something or subtracting 8 from %rsp.
我有一段 NASM 汇编代码如下:
在我调用“_start”中的“inc”函数之前,%rsp 应该位于8 字节的边界,这违反了NASM 文档中描述的规则。但实际上,一切都很顺利。那么,我该如何理解呢?
我是在 Ubuntu 20.04 LTS (x86_64) 下构建的。
global _start
section .data
init:
db 0x2
section .rodata
codes:
db '0123456789abcdef'
section .text
inc:
mov rax, [rsp+8] ; read param from the stack;
add rax, 0x1
ret
print:
lea rsi, [codes + rax]
mov rax, 1
mov rdi, 1
mov rdx, 1
syscall
ret
_start:
; enable AC check;
pushf
or dword [rsp], 1<<18
popf
mov rdi, [init] ; move the first 8 bytes of init to %rdi;
push rdi ; %rsp -> 8 bytes;
call inc
pop r11 ; clean stack by the caller;
call print
mov rax, 60
xor rdi, rdi
syscall
ABI 是一组规则,用于规定函数应如何表现才能相互操作。一侧的每条规则都与另一侧允许的假设配对。在这种情况下,关于调用者堆栈对齐的规则是关于被调用者堆栈对齐的允许假设。由于您的 inc
函数不依赖于 16 字节堆栈对齐,因此可以使用仅 8 字节对齐的堆栈调用该特定函数。
如果您想知道为什么在启用 AC 时它没有中断,那是因为您只从堆栈加载 8 字节值,而堆栈仍然是 8 字节对齐的。如果你也做了 sub rsp, 4
或其他破坏 8 字节对齐的事情,那么你会得到一个总线错误。
ABI 变得重要的地方是当情况不是您自己在汇编中编写的一个函数调用您自己在汇编中编写的另一个函数时。其他人的库(包括 C 标准库)中的函数,或者您从 C 编译而不是用汇编编写的函数,都有权执行 movaps [rsp - 24], xmm0
或其他操作,如果您不正确,它会中断在调用之前对齐堆栈。
旁注:ABI 还说明了您应该如何传递参数(调用约定),但您只是将它们传递到任何地方。同样,在您自己的程序集中很好,但是如果您尝试从 C 中调用它们,它们 肯定会 中断。