C 弹出x86堆栈时出现分段错误
我正在尝试链接x86汇编和C 我的C程序: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
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