调用例程后访问推送的参数
Accessing pushed args after calling a routine
我正在使用 FreeDOS 和 nasm 学习 x86 汇编。我有这个小测试程序,它所做的就是将 A 打印到屏幕并退出。
如果我不使用 Write 例程,它工作正常。
但是似乎正在发生的事情是,当我将 A 压入堆栈然后调用 Write 时,它会将下一个 IP 压入堆栈,当我在例程中弹出 A 时,我得到的是 IP 而不是我压入的值.
我确定这很简单,但我没有看到问题所在。
segment data ; start of data segment
segment code ; start of code segment
..start:
label1:
top:
push 'A'
call Write
mov ah, 4ch
mov al, 0
int 21h
Write:
pop dx
mov ah, 02h
int 21h
ret
end:
mov ah, 4ch ;exit
mov al, 0 ;exit code 0
int 21h ;call intr
segment stack class=stack ; start of stack segment
resb 512
这就是应该的工作方式。调用函数会将 return 地址压入堆栈。因此,当您的函数被输入时,堆栈顶部将是 return 地址,而不是您之前推送的地址。
在 32 位代码中,您现在可以直接使用堆栈指针来访问先前推送的值(类似于 16 位模式中的 [esp+4]
或 [esp+2]
),但这不是可能只有 16 位寻址模式的纯 16 位汇编及其有限的寄存器选择(不包括 [sp]
)。
通常的方法是将 bp
设置为 frame pointer,您可以从中随机访问堆栈框架,包括堆栈参数或您为 space 保留的任何本地变量.
Write:
push bp ; Save previous value of bp so it won't get lost
mov bp, sp ; Set bp ("base pointer") to current stack pointer position
mov dx, [bp+4] ; Get argument from stack
mov ah, 02h
int 21h
mov sp, bp ; Restore stack pointer
pop bp ; Restore value of base pointer
ret 2 ; Indicate how many bytes should be popped from stack after return
我们在这里使用 mov dx, [bp+4]
而不是 pop dx
。此时,[bp]
将是先前的 bp
值(因为它是在 bp
分配给 sp
之前最后一次推送),[bp+2]
将是 return 地址,[bp+4]
你的第一个参数。
(记住堆栈向下增长,这就是为什么你需要 +4
而不是 -4
的原因。)
此外,当您 return 时,您必须确保参数已从堆栈中移除。您可以让调用者清理或使用 ret
和要删除的字节数作为参数。这是一个额外的 sp += n
after 弹出 return 地址。在您的情况下,ret 2
将为此函数实现 callee-pops。
我正在使用 FreeDOS 和 nasm 学习 x86 汇编。我有这个小测试程序,它所做的就是将 A 打印到屏幕并退出。
如果我不使用 Write 例程,它工作正常。
但是似乎正在发生的事情是,当我将 A 压入堆栈然后调用 Write 时,它会将下一个 IP 压入堆栈,当我在例程中弹出 A 时,我得到的是 IP 而不是我压入的值.
我确定这很简单,但我没有看到问题所在。
segment data ; start of data segment
segment code ; start of code segment
..start:
label1:
top:
push 'A'
call Write
mov ah, 4ch
mov al, 0
int 21h
Write:
pop dx
mov ah, 02h
int 21h
ret
end:
mov ah, 4ch ;exit
mov al, 0 ;exit code 0
int 21h ;call intr
segment stack class=stack ; start of stack segment
resb 512
这就是应该的工作方式。调用函数会将 return 地址压入堆栈。因此,当您的函数被输入时,堆栈顶部将是 return 地址,而不是您之前推送的地址。
在 32 位代码中,您现在可以直接使用堆栈指针来访问先前推送的值(类似于 16 位模式中的 [esp+4]
或 [esp+2]
),但这不是可能只有 16 位寻址模式的纯 16 位汇编及其有限的寄存器选择(不包括 [sp]
)。
通常的方法是将 bp
设置为 frame pointer,您可以从中随机访问堆栈框架,包括堆栈参数或您为 space 保留的任何本地变量.
Write:
push bp ; Save previous value of bp so it won't get lost
mov bp, sp ; Set bp ("base pointer") to current stack pointer position
mov dx, [bp+4] ; Get argument from stack
mov ah, 02h
int 21h
mov sp, bp ; Restore stack pointer
pop bp ; Restore value of base pointer
ret 2 ; Indicate how many bytes should be popped from stack after return
我们在这里使用 mov dx, [bp+4]
而不是 pop dx
。此时,[bp]
将是先前的 bp
值(因为它是在 bp
分配给 sp
之前最后一次推送),[bp+2]
将是 return 地址,[bp+4]
你的第一个参数。
(记住堆栈向下增长,这就是为什么你需要 +4
而不是 -4
的原因。)
此外,当您 return 时,您必须确保参数已从堆栈中移除。您可以让调用者清理或使用 ret
和要删除的字节数作为参数。这是一个额外的 sp += n
after 弹出 return 地址。在您的情况下,ret 2
将为此函数实现 callee-pops。