如何正确操作装配中的堆栈?

How do I correctly manipulate the stack in assembly?

Q1) 我看到汇编代码使用 [rsp+4] 访问堆栈上的变量,而其他人使用 [rbp-4]。我假设它们都是正确的,唯一的区别是使用了堆栈帧的哪一端。

Q2) 进入函数时,我们应该push [rsp],离开时pop rsp。但是,当我将这些说明排除在外时,代码运行得很好。为什么需要它们? test.asm.

中给出了示例代码

Q3) 当在 main 中离开程序时,我们要 return 退出代码,例如0 xor rdi rdi。但是,当我离开这个命令时它仍然有效。与下面 test.asm.

中的示例相同

Q4) push 5mov [rsp], 5一样吗?

; test.asm
; Compiled as such (Linking with MSVC):
; nasm -f win64 -o test.obj test.asm
; /LINK /DEFAULTLIB:msvcrt.lib /DEFAULTLIB:legacy_stdio_definitions.lib /DEFAULTLIB:Kernel32.lib /SUBSYSTEM:console test.obj /OUT:test.exe
; Gives output:
; 1
; 2

bits 64
default rel

segment .data
    ifmt:    db "%d, 0xd, 0xa, 0x0

segment .text
    global main
    extern printf

PrintInt:
    sub     rsp, 40
    mov     rdx, rcx
    lea     rcx, [ifmt]
    call    printf
    add     rsp, 40
    ret

main:
    sub     rsp, 24
    mov     rcx, 1
    call    PrintInt
    mov     rcx, 2
    call    PrintInt
    add     rsp, 24
    ret

Q1。没错。

Q2。 push rsppush [rsp]pop rsp 几乎 永远不会 正确。可能有一些专门用途,但不适合初学者。您可能会想到 push rbppop rbp,仅当您在函数中使用 rbp 时才需要它们。

Q3。从 main 返回时,设置 eax 为退出状态,而不是 edi。如果你调用exit函数,那么将状态作为参数传递给ecx中的退出函数。如果调用者不使用退出状态,那么如果你不设置它,你不会注意到有什么不同。

Q4。 push 5lea rsp, [rsp-8]; mov qword [rsp], 5 相同。