【发布时间】:2020-07-03 11:46:46
【问题描述】:
我从 NASM 的文档中看到了以下规则:
在进行调用之前,堆栈指针 %rsp 必须与 16 字节边界对齐。很好,但是进行调用的过程会将返回地址(8 个字节)压入堆栈,因此当函数获得控制权时,%rsp 未对齐。你必须自己腾出额外的空间,通过推动一些东西或从 %rsp 中减去 8。
我有一个 NASM 汇编代码的 sn-p 如下:
在我调用“_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
【问题讨论】:
-
对于您自己的函数,您可以使用任何您想要的约定。请注意,标准约定使用寄存器来传递参数(适用条款和条件,请阅读细则:))此外,即使使用 3rd 方函数,如果您未按要求对齐
rsp,天空也不一定会塌陷,它取决于被调用函数在做什么。将浮点数传递给诸如printf之类的可变参数函数通常会因堆栈指针未对齐而崩溃。 -
顺便说一句,
inc也是指令的名称;我建议不要将其用作标签名称。此外,NASM 文档几乎肯定没有说“%rsp”,除非它引用了另一个文档(例如 x86-64 System V ABI),因为%装饰是 AT&T 语法,而不是 NASM Intel 语法 -
仅作记录,这看起来效率很低,并且只处理 4 位数字,即使您加载了 8 个 字节(16 个半字节)。请参阅How to convert a binary integer number to a hex string? 了解不会浪费时间进行函数调用的简单高效的循环。我猜你只是为了学习函数而使用函数。
-
而不是
pop r11 ; clean stack by the caller;,你可以更好地(不破坏任何寄存器)简单地添加你推送到rsp的大小 -
@Tommylee2k:
pop在其他两个堆栈操作(如ret和call)之间实际上比add rsp,8更有效。这就是为什么clang使用它的原因,例如。 Why does this function push RAX to the stack as the first operation? / What is the stack engine in the Sandybridge microarchitecture? / NASM should I pop function argument after calling a function?
标签: linux assembly x86-64 memory-alignment calling-convention