调用例程后访问推送的参数 [英] Accessing pushed args after calling a routine

查看:66
本文介绍了调用例程后访问推送的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习使用 FreeDOS 和 nasm 的 x86 汇编.我有这个小测试程序,它所做的只是在屏幕上打印 A 并退出.

如果我不使用 Write 例程,它就可以正常工作.

但似乎正在发生的事情是,当我将 A 推入堆栈然后调用 Write 时,它​​将下一个 IP 放入堆栈,当我在例程中弹出 A 时,我得到的是 IP 而不是我推入的值./p>

我确定这很简单,但我没有看到问题.

段数据;数据段开始段码;代码段开始..开始:标签 1:最佳:按A"打电话 写移动啊,4ch移动, 021 小时写:流行音乐动啊,02h21 小时回复结尾:mov ah, 4ch ;退出mov al, 0 ;退出代码0int 21h ;调用intr段堆栈类=堆栈;堆栈段的开始第 512 章

解决方案

这就是应该工作的方式.调用函数会将返回地址压入堆栈.所以,当你的函数进入时,栈顶将是返回地址,而不是你之前压入的地址.

在 32 位代码中,您现在可以直接使用堆栈指针来访问先前推送的值(类似于 [esp+4][esp+2] 在 16 位模式下),但这对于只有 16 位寻址模式和有限的寄存器选择(不包括 [sp])的纯 16 位汇编是不可能的.

通常的方法是将 bp 设置为 帧指针,您可以从中随机访问堆栈帧,包括堆栈参数或您为其保留空间的任何本地变量.

写:推 bp ;保存之前的 bp 值以免丢失mov bp, sp ;将 bp(基指针")设置为当前堆栈指针位置mov dx, [bp+4] ;从堆栈中获取参数动啊,02h21 小时mov sp, bp ;恢复堆栈指针流行 bp ;恢复基指针的值返回 2 ;指示返回后应从堆栈中弹出多少字节

我们在这里使用 mov dx, [bp+4] 而不是 pop dx.此时,[bp] 将是之前的 bp 值(因为它是在 bp 分配给 sp),[bp+2] 是返回地址,[bp+4] 是你的第一个参数.

(请记住,堆栈是向下增长的,这就是为什么这里需要 +4 而不是 -4.)

另外,当你返回时,你必须确保参数从堆栈中移除.您可以让调用者清理或使用 ret 以及要删除的字节数作为参数.这是一个额外的 sp += n after 弹出返回地址.在您的情况下,ret 2 将为此函数实现 callee-pops.

I am learning x86 assembly using FreeDOS and nasm. I have this little test program that all it does is print A to the screen and exits.

It works fine if I don't use the Write routine.

But what seems to be happening is when I push A on to the stack then call Write it places the next IP on to the stack and when I pop off A in the routine I get the IP not the value I pushed.

I am sure it is something simple but I don't see the issue.

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

解决方案

This is how it is supposed to work. Calling into a function pushes the return address on the stack. So, when your function is entered, the top of the stack will be return address and not what you previously pushed.

In 32-bit code you could now just use the stack pointer directly to access the previously pushed value (something like [esp+4] or [esp+2] in 16-bit mode), but this is not possible with pure 16-bit assembly with only 16-bit addressing modes and their limited choice of registers (not including [sp]).

The normal way is to set up bp as a frame pointer from which you have random access to your stack frame, including stack args or any local vars you reserve space for.

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

Instead of pop dx, we use mov dx, [bp+4] here. At this point, [bp] would be the previous bp value (since it was last pushed before bp was assigned to sp), [bp+2] would be the return address, and [bp+4] your first argument.

(Remember that the stack grows downwards, that's why you need +4 and not -4 here.)

Also, when you return, you have to be sure that the argument is removed from the stack. You can either let the caller clean up or use ret with the number of bytes to remove as argument. It's an extra sp += n after popping the return address. In your case, ret 2 would implement callee-pops for this function.

这篇关于调用例程后访问推送的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆