Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/59.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 弹出x86堆栈时出现分段错误_C_Gcc_Assembly_X86_Nasm - Fatal编程技术网

C 弹出x86堆栈时出现分段错误

C 弹出x86堆栈时出现分段错误,c,gcc,assembly,x86,nasm,C,Gcc,Assembly,X86,Nasm,我正在尝试链接x86汇编和C 我的C程序: extern int plus_10(int); # include <stdio.h> int main() { int x = plus_10(40); printf("%d\n", x); return 0; } 我编译并链接这两个文件如下: gcc -c prog.c -o prog_c.o -m32 nasm -f elf32 prog.asm -o prog_asm.o gcc prog_c.o p

我正在尝试链接x86汇编和C

我的C程序:

extern int plus_10(int);

# include <stdio.h>

int main() {
    int x = plus_10(40);
    printf("%d\n", x);
    return 0;
}
我编译并链接这两个文件如下:

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,[esp+4]


程序运行良好。有人能解释一下为什么会发生这种情况吗?

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

现在,当您调用
plus_10
时,
call
指令将地址
retadd
推送到堆栈上。它实际上是一个
push
+
jmp
,而
ret
实际上是
pop-eip

因此您的堆栈在
plus\u 10
函数中如下所示:

|  ...   |
+--------+
|   40   |  <- ESP+4 points here (the function argument)
+--------+
| retadd |  <- ESP points here
+--------+
现在,如果此时执行
ret
,程序实际上将跳转到地址40,很可能是SEGFULT或以其他不可预测的方式运行

编译器生成的实际汇编代码可能不同,但这说明了问题所在


顺便说一句,编写函数的一种更有效的方法是:对于这个小函数的非内联版本,大多数编译器在启用优化的情况下都会这样做

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

pop-edx
移动堆栈指针,
mov-edx,[esp+4]
不移动堆栈指针。通常在C语言中,由调用方清理堆栈。问得好+1@Jabberwocky但这为什么会导致分割错误呢?堆栈对于这两个函数都是通用的,对吧?因为您弹出的是返回地址而不是参数。您不能像这样使用pop。@SusmitAgrawal,因为返回地址在堆栈上。您的
pop-edx
实际上会从堆栈中弹出返回地址,当执行
ret
时,处理器会跳转到堆栈上的任何地址。不过,cdecl调用约定希望通过eax返回值。因此,不能只按自己喜欢的方式编写asm函数,它必须与编译器生成的C兼容。@Lundin显然他的平台使用cdecl约定。我还编写了可能的汇编代码,因此根据平台的不同,可能会有所不同。编辑和澄清。谢谢
|  ...   |
+--------+
|   40   |  <- ESP points here
+--------+
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