问题描述
我尝试遵循https://cs.lmu.edu/~ray/notes/nasmtutorial/提供的一个非常简单的示例。 我特意在下面的行中进行了注释,以确保堆栈未按照x64调用约定的要求与16个字节的边界对齐。但是程序仍然可以继续工作。请有人能回答为什么不遵守电话惯例的原因,我期待某种细分错误。
; sub rsp,8 ; must align stack before call
为了运行该程序:(Ubuntu 20.04 lts,gcc 9.3.0)
nasm -felf64 echo.asm && gcc echo.o && ./a.out 123ABC
; -----------------------------------------------------------------------------
; A 64-bit program that displays its command line arguments,one per line.
;
; On entry,rdi will contain argc and rsi will contain argv.
; -----------------------------------------------------------------------------
global main
extern puts
section .text
main:
push rdi ; save registers that puts uses
push rsi
; sub rsp,8 ; must align stack before call
mov rdi,[rsi] ; the argument string to display
call puts WRT ..plt ; print it
; add rsp,8 ; restore %rsp to pre-aligned value
pop rsi ; restore registers puts used
pop rdi
add rsi,8 ; point to next argument
dec rdi ; count down
jnz main ; if not done counting keep going
ret
解决方法
祝你好运。
需要堆栈对齐的主要原因之一是,函数可以安全地使用SSE对齐的数据指令,例如movaps
,如果与未对齐的数据一起使用,则会出错。但是,如果puts
碰巧不使用任何此类指令,或执行其他真正需要堆栈对齐的操作,则不会发生错误(尽管可能仍然会降低性能)。
编译器有权假设堆栈是对齐的,并且如果感觉可以使用这些指令。因此,在任何时候,如果您的libc进行了升级或重新编译,则可能会发生puts
的新版本使用此类指令,并且您的代码将神秘地开始失败的情况。
很明显,您不想要那样,所以按照您的意愿对齐织补堆栈。
在C或汇编程序编程中,相对很少有这样的情况,可以保证违反这样的规则以段错误或其他任何可预测的方式失败;取而代之的是人们说“行为未定义”之类的事情,这意味着它可能以您可以想象的任何方式失败,或者再次失败。因此,您真的无法从非法代码会发生起作用的实验中得出任何结论。反复试验不是学习汇编编程的好方法。