我正在尝试将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
但是,当我运行生成的文件时,出现了分段错误。
但是当我更换
pop edx
使用
mov edx,[esp + 4]
程序运行正常。有人可以解释为什么会这样吗?
答案 0 :(得分:31)
这是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
时,地址retadd
被call
指令压入堆栈。实际上是push
+ jmp
,而ret
实际上是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,最有可能出现段错误或以其他某些不可预测的方式运行。
编译器生成的实际汇编代码可能有所不同,但这说明了问题。
顺便说一句,一种更有效的函数编写方法是:对于该微型函数的非内联版本,大多数编译器都会启用优化功能。
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