1. Abstract
.
2. Introduction
3. Intel
3.1 结论
GCC 编译出来的代码,参数传递都是通过堆栈的。请参考文档 [1] 。
注意:不要和 Intel X86 架构里的系统调用的参数传递弄混了。
3.2 代码
下面通过一个例子来说明问题。
3.2.1 源代码
源代码如下
#include <stdio.h>
int func (int a, int b)
{
return 5;
}
int main(int argc, char ** argv)
{
int i = 0, value = -1;
func(i, value);
} // end
3.2.2 编译过程
gcc simple_call.c
3.2.3 汇编代码
objdump -d a.out
输出:
……
08048344 <func>:
8048344: 55 push %ebp
8048345: 89 e5 mov %esp,%ebp
8048347: b8 05 00 00 00 mov $0x5,%eax // 可见, Intel 是通过寄存器 eax 存放返回值的。
804834c: 5d pop %ebp
804834d: c3 ret
08048349 <main>:
8048349: 8d 4c 24 04 lea 0x4(%esp),%ecx
804834d: 83 e4 f0 and $0xfffffff0,%esp
8048350: ff 71 fc pushl -0x4(%ecx)
8048353: 55 push %ebp
8048354: 89 e5 mov %esp,%ebp
8048356: 51 push %ecx
8048357: 83 ec 18 sub $0x18,%esp
804835a: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp) // int i = 0
8048361: c7 45 f8 ff ff ff ff movl $0xffffffff,-0x8(%ebp) // value = -1
8048368: 8b 45 f8 mov -0x8(%ebp),%eax
804836b: 89 44 24 04 mov %eax,0x4(%esp) // 第二个参数传递
804836f: 8b 45 f4 mov -0xc(%ebp),%eax
8048372: 89 04 24 mov %eax,(%esp) // 第一个参数传递
8048375: e8 ca ff ff ff call 8048344 <func>
804837a: 83 c4 18 add $0x18,%esp
804837d: 59 pop %ecx
804837e: 5d pop %ebp
804837f: 8d 61 fc lea -0x4(%ecx),%esp
8048382: c3 ret
……
4. ARM
4.1 结论
GCC 编译出来的代码,参数传递小于 4 个,是通过 r0~r3 传递的,从第 5 个开始,都是通过堆栈传递的。关于 fp 的作用,请参考文档 [2] 。
4.2 代码
下面通过一个例子来说明问题。
4.2.1 源代码
源代码如下
#include <stdio.h>
int func (int a, int b, int c, int d, int e, int f, int g)
{
a = a+1;
b = b-1;
return b;
}
int main(int argc, char ** argv)
{
int i = 0, value = 1, ret;
ret = func(i, value, 2, 3, 4, 5, 6);
} // end
4.2.2 编译过程
gcc simple_call.c
4.2.3 汇编代码
objdump -d a.out
输出:
……
00008350 <func>:
8350: e1a0c00d mov ip, sp
8354: e92dd800 stmdb sp!, {fp, ip, lr, pc}
8358: e24cb004 sub fp, ip, #4 ; 0x4
835c: e24dd010 sub sp, sp, #16 ; 0x10
8360: e50b0010 str r0, [fp, #-16]
8364: e50b1014 str r1, [fp, #-20]
8368: e50b2018 str r2, [fp, #-24]
836c: e50b301c str r3, [fp, #-28]
8370: e51b3010 ldr r3, [fp, #-16]
8374: e2833001 add r3, r3, #1 ; 0x1
8378: e50b3010 str r3, [fp, #-16]
837c: e51b3014 ldr r3, [fp, #-20]
8380: e2433001 sub r3, r3, #1 ; 0x1
8384: e50b3014 str r3, [fp, #-20]
8388: e51b3014 ldr r3, [fp, #-20]
838c: e1a00003 mov r0, r3 // 将 r3 赋给 r0, 可以看出 ARM 是通过寄存器 r0 存放返回值的。
8390: e24bd00c sub sp, fp, #12 ; 0xc
8394: e89da800 ldmia sp, {fp, sp, pc}
00008398 <main>:
8398: e1a0c00d mov ip, sp
839c: e92dd800 stmdb sp!, {fp, ip, lr, pc}
83a0: e24cb004 sub fp, ip, #4 ; 0x4
83a4: e24dd028 sub sp, sp, #40 ; 0x28
83a8: e50b0010 str r0, [fp, #-16]
83ac: e50b1014 str r1, [fp, #-20]
83b0: e3a03000 mov r3, #0 ; 0x0
83b4: e50b3018 str r3, [fp, #-24] // 将第一个参数暂时放在堆栈里
83b8: e3a03001 mov r3, #1 ; 0x1
83bc: e50b301c str r3, [fp, #-28] // 将第二个参数暂时放在堆栈里
83c0: e3a03004 mov r3, #4 ; 0x4
83c4: e58d3000 str r3, [sp] // 将第五个参数放在堆栈里,注意,后面不会去出来了。
83c8: e3a03005 mov r3, #5 ; 0x5
83cc: e58d3004 str r3, [sp, #4] // 将第六个参数放在堆栈里,注意,后面不会去出来了。
83d0: e3a03006 mov r3, #6 ; 0x6
83d4: e58d3008 str r3, [sp, #8] // 将第七个参数放在堆栈里,注意,后面不会去出来了。
83d8: e51b0018 ldr r0, [fp, #-24] // 将第一个参数从堆栈中取出,故而第一个参数仍然是通过寄存器传递的。
83dc: e51b101c ldr r1, [fp, #-28] // 将第二个参数从堆栈中取出,故而第一个参数仍然是通过寄存器传递的。
83e0: e3a02002 mov r2, #2 ; 0x2// 将第三个参数直接付给寄存器
83e4: e3a03003 mov r3, #3 ; 0x3// 将第四个参数直接付给寄存器
83e8: ebffffd8 bl 8350 <func>
83ec: e50b0020 str r0, [fp, #-32]
83f0: e24bd00c sub sp, fp, #12 ; 0xc
83f4: e89da800 ldmia sp, {fp, sp, pc}
……
5. References
[2]. APCS(ARM 过程调用标准 ) 二 , http://www.52rd.com/Blog/Detail_RD.Blog_zjhfqq_7625.html