assembly 弹出x86堆栈以访问函数参数时出现分段错误

x6h2sr28  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(137)

我正在尝试链接x86汇编和C。
我的C程序:

extern int plus_10(int);

# include <stdio.h>

int main() {
    int x = plus_10(40);
    printf("%d\n", x);
    return 0;
}

我的汇编程序:

[bits 32]

section .text

global plus_10
plus_10:
    pop edx
    mov eax, 10
    add eax, edx
    ret

我编译和链接这两个如下:

gcc -c prog.c -o prog_c.o -m32
nasm -f elf32 prog.asm -o prog_asm.o
gcc prog_c.o prog_asm.o -m32

然而,当我运行结果文件时,我得到了一个分段错误。
但是当我把
弹出EDX

移动EDX,[特别是+4]
程序运行正常。2有没有人能解释一下为什么会发生这种情况?

ig9co6j1

ig9co6j11#

这可能是int x = plus_10(40);的汇编代码

push    40                      ; push argument
        call    plus_10                 ; call function
retadd: add     esp, 4                  ; clean up stack (dummy pop)
        ; result of the function call is in EAX, per the calling convention

        ; if compiled without optimization, the caller might just store it:
        mov     DWORD PTR [ebp-x], eax  ; store return value
                                        ; (in eax) in x

现在,当你调用plus_10时,call指令将地址retadd压入堆栈,它实际上是push + jmpret实际上是pop eip

plus_10函数中,堆栈如下所示:

|  ...   |
+--------+
|   40   |  <- ESP+4 points here (the function argument)
+--------+
| retadd |  <- ESP points here
+--------+

ESP指向包含返回地址的内存位置。
现在如果你使用pop edx,返回地址会变成edx,堆栈看起来像这样:

|  ...   |
+--------+
|   40   |  <- ESP points here
+--------+

现在,如果您在此时执行ret,程序实际上将跳转到地址40,并且很可能出现segfault或以其他一些不可预测的方式运行。
编译器生成的实际汇编代码可能不同,但这说明了问题所在。
顺便说一句,编写函数的一种更有效的方法是:对于这个小函数的非内联版本,大多数编译器都会这样做。

global plus_10
plus_10:
    mov   eax,  [esp+4]    ; retval = first arg
    add   eax,  10         ; retval += 10
    ret

这比

mov   eax,  10
    add   eax,  [esp+4]        ; decode to a load + add.
    ret

相关问题