GCC中C与汇编代码间参数传递及返回值机制问询
GCC下C与汇编的参数传递及返回值规则
嘿,这个问题问到点子上了——搞C和汇编混合编程的时候,最核心的就是摸透GCC的调用约定,不然参数传递和返回值分分钟出问题!下面我就针对最常用的32位x86和64位x86_64架构,结合你写的int f(int arg)函数,把规则讲得明明白白:
1. 返回值的传递逻辑
不管是32位还是64位架构,对于int这类整数型返回值,规则特别统一:
- 32位x86:返回值会存在
%eax寄存器里 - 64位x86_64:返回值会存在
%rax寄存器里
举个例子,你的函数f如果要返回arg + 1,只要把计算结果放到对应的寄存器里,直接ret就行,完全不需要操作栈。
2. 参数的传递方式
这里不同架构差异很大,咱们分开说:
2.1 32位x86(i386 System V ABI)
这种架构下GCC默认用栈传递参数,而且是从右往左入栈(不过你的函数只有一个参数,所以逻辑更简单):
- 当C代码调用
f(arg)时,会先把arg的值push到栈上,再执行call指令调用f - call指令会把返回地址压到栈顶,所以在
f的汇编实现里,参数arg的位置是%esp + 4(栈顶是返回地址,占4字节) - 通常写汇编函数时,会先建立栈帧:
push %ebp然后mov %esp, %ebp,这样参数就可以通过%ebp + 8来访问(%ebp本身占4字节,加上返回地址的4字节,偏移量就是8)
2.2 64位x86_64(System V AMD64 ABI)
这是寄存器优先的传递方式,效率更高:
- 前6个整数/指针类型的参数,会依次放到
%rdi,%rsi,%rdx,%rcx,%r8,%r9这6个寄存器里 - 你的函数
f只有一个int参数,所以这个arg会直接存在%rdi寄存器里(因为int是32位,也可以用它的低32位寄存器%edi) - 如果参数数量超过6个,剩下的才会用栈传递,同样遵循从右往左入栈的规则
3. 关于你的编译选项
你写的编译选项是gcc -Wall -g -std=c99 -pedantic -g(注意笔误啦,是gcc不是gccc),这些选项里没有修改调用约定的参数,所以GCC会自动采用目标架构的默认调用约定——比如你在64位系统上编译,就用System V AMD64 ABI;32位系统上就用i386的栈传递规则。
4. 给你两个实际的汇编例子
x86_64架构下的实现
如果你的f函数要返回参数加1,汇编代码可以这么写:
.global f f: movl %edi, %eax # 把参数(%edi是%rdi的低32位)放到返回值寄存器%eax addl $1, %eax # 参数加1 ret # 返回,%eax里就是结果
32位x86架构下的实现
.global f f: push %ebp # 保存旧的栈帧基址 mov %esp, %ebp # 建立新的栈帧 movl 8(%ebp), %eax# 从栈中取出参数(%ebp+8是第一个参数的位置) addl $1, %eax # 参数加1 pop %ebp # 恢复旧栈帧 ret # 返回
内容的提问来源于stack exchange,提问作者ArekBulski
相关产品推荐
相关产品推荐

