c语言如何调用汇编函数,C语言函数调用的汇编实现

本文详细分析了一段C代码及其对应的汇编输出,探讨了参数传递、函数调用过程、堆栈操作和寄存器使用。通过示例展示了在Ubuntu 12.04 + GCC 4.6.3环境下,函数fun1和fun2如何处理参数,并解释了call指令的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

int fun2()

{

return 2;

}

int fun1(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k)

{

int aa;

int bb;

aa = 0x11;

bb = 0x22;

fun2();

aa = h;

bb = i;

return 1;

}

int main()

{

fun1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);

return 0;

}

00000000004004b4 :

4004b4:55 push %rbp

4004b5:48 89 e5 mov %rsp,%rbp

4004b8:b8 02 00 00 00 mov $0x2,%eax

4004bd:5d pop %rbp

4004be:c3 retq

00000000004004bf :

4004bf:55 push %rbp

4004c0:48 89 e5 mov %rsp,%rbp

4004c3:48 83 ec 28 sub $0x28,%rsp

4004c7:89 7d ec mov %edi,-0x14(%rbp)

4004ca:89 75 e8 mov %esi,-0x18(%rbp)

4004cd:89 55 e4 mov %edx,-0x1c(%rbp)

4004d0:89 4d e0 mov %ecx,-0x20(%rbp)

4004d3:44 89 45 dc mov %r8d,-0x24(%rbp)

4004d7:44 89 4d d8 mov %r9d,-0x28(%rbp)

4004db:c7 45 f8 11 00 00 00 movl $0x11,-0x8(%rbp)

4004e2:c7 45 fc 22 00 00 00 movl $0x22,-0x4(%rbp)

4004e9:b8 00 00 00 00 mov $0x0,%eax

4004ee:e8 c1 ff ff ff callq 4004b4

4004f3:8b 45 18 mov 0x18(%rbp),%eax

4004f6:89 45 f8 mov %eax,-0x8(%rbp)

4004f9:8b 45 20 mov 0x20(%rbp),%eax

4004fc:89 45 fc mov %eax,-0x4(%rbp)

4004ff:b8 01 00 00 00 mov $0x1,%eax

400504:c9 leaveq

400505:c3 retq

0000000000400506 :

400506:55 push %rbp

400507:48 89 e5 mov %rsp,%rbp

40050a:48 83 ec 28 sub $0x28,%rsp

40050e:c7 44 24 20 0b 00 00 movl $0xb,0x20(%rsp)

400515:00

400516:c7 44 24 18 0a 00 00 movl $0xa,0x18(%rsp)

40051d:00

40051e:c7 44 24 10 09 00 00 movl $0x9,0x10(%rsp)

400525:00

400526:c7 44 24 08 08 00 00 movl $0x8,0x8(%rsp)

40052d:00

40052e:c7 04 24 07 00 00 00 movl $0x7,(%rsp)

400535:41 b9 06 00 00 00 mov $0x6,%r9d

40053b:41 b8 05 00 00 00 mov $0x5,%r8d

400541:b9 04 00 00 00 mov $0x4,%ecx

400546:ba 03 00 00 00 mov $0x3,%edx

40054b:be 02 00 00 00 mov $0x2,%esi

400550:bf 01 00 00 00 mov $0x1,%edi

400555:e8 65 ff ff ff callq 4004bf

40055a:b8 00 00 00 00 mov $0x0,%eax

40055f:c9 leaveq

400560:c3 retq

以上是在ubuntu12.04 + gcc4.6.3 + intel i7上测试的C代码和反汇编结果。

fun1函数之所以设置那么多的参数,主要是因为传递参数在汇编中有两种不同的方法。

一种是用寄存器传递,一种是压栈传递。

当参数数量比较少时,编译器就可能完全使用寄存器传递的方法。

因此设置了这些参数,可以保证编译器必须会用到压栈的传递方法。

我们先看main函数:

400535:41 b9 06 00 00 00 mov $0x6,%r9d

40053b:41 b8 05 00 00 00 mov $0x5,%r8d

400541:b9 04 00 00 00 mov $0x4,%ecx

400546:ba 03 00 00 00 mov $0x3,%edx

40054b:be 02 00 00 00 mov $0x2,%esi

400550:bf 01 00 00 00 mov $0x1,%edi从这几句汇编可以看出,前6个参数是通过寄存器传递的。

40050a:48 83 ec 28 sub $0x28,%rsp

40050e:c7 44 24 20 0b 00 00 movl $0xb,0x20(%rsp)

400515:00

400516:c7 44 24 18 0a 00 00 movl $0xa,0x18(%rsp)

40051d:00

40051e:c7 44 24 10 09 00 00 movl $0x9,0x10(%rsp)

400525:00

400526:c7 44 24 08 08 00 00 movl $0x8,0x8(%rsp)

40052d:00

40052e:c7 04 24 07 00 00 00 movl $0x7,(%rsp)

而后5个则是通过压栈传递的。

但是这里并没有用到push指令,而是通过mov把变量写进堆栈。

因为前面那句

40050a:48 83 ec 28 sub $0x28,%rsp已经把这些变量的空间都预留出来了,所以可以直接写进去了。

参数都准备好了,就可以调用函数fun1了。

400555:e8 65 ff ff ff callq 4004bf call这个指令,其实隐藏着一个压栈动作.

压进去的是0x40055a,也就是call指令的下一条指令的地址。

作用就是为了调用完fun1之后回到main函数继续执行。

要执行的指令自然就是call指令的顺序下一条指令了。

进入fun1之后,首先出现的是

4004bf:55 push %rbp

4004c0:48 89 e5 mov %rsp,%rbp这两句汇编基本出现在所有的函数的开头,

作用就是把bp保存起来,再把bp设置成sp,这样的话,sp在后面就能够随意的修改了。

直到函数的末尾,把bp赋值给sp,再pop出bp不就解决了。

事实上,fun1里也确实是这么实现的,只是它用了一条更简洁的指令

400504:c9 leaveqleave指令其实就相当与bp->sp, pop bp这两句的作用。

那这样之后,堆栈不就和刚进去函数时一样了啊。

最后再通过

400505:c3 retq

把刚才call压栈的返回地址pop到eip寄存器,就此就回到了main函数。

让我们继续看fun1的实现

4004c3:48 83 ec 28 sub $0x28,%rsp

4004c7:89 7d ec mov %edi,-0x14(%rbp)

4004ca:89 75 e8 mov %esi,-0x18(%rbp)

4004cd:89 55 e4 mov %edx,-0x1c(%rbp)

4004d0:89 4d e0 mov %ecx,-0x20(%rbp)

4004d3:44 89 45 dc mov %r8d,-0x24(%rbp)

4004d7:44 89 4d d8 mov %r9d,-0x28(%rbp)

4004db:c7 45 f8 11 00 00 00 movl $0x11,-0x8(%rbp)

4004e2:c7 45 fc 22 00 00 00 movl $0x22,-0x4(%rbp)

函数内的零时变量aa,bb被存放在堆栈的偏上位置,而后是存放刚才用寄存器传递进来的其他变量。

但是可以发现,-0xc(%rbp)和-0x10(%rbp)并没有被用到,直接就被跳掉了,有点奇怪。

之后是调用fun2函数

4004ee:e8 c1 ff ff ff callq 4004b4 接下来的两句赋值语句则被翻译成

4004f3:8b 45 18 mov 0x18(%rbp),%eax

4004f6:89 45 f8 mov %eax,-0x8(%rbp)

4004f9:8b 45 20 mov 0x20(%rbp),%eax

4004fc:89 45 fc mov %eax,-0x4(%rbp)变量h是通过压栈传递进来的,所以需要通过ebp来寻找。

0x20(%rbp) = i

0x18(%rbp) = h

0x10(%rbp) = g

0x8(%rbp) = 返回地址0x40055a

0x0(%rbp) = bp

然后把函数返回值放入eax寄存器中,就可以返回了。

4004ff:b8 01 00 00 00 mov $0x1,%eax

至此,整个函数的调用分析基本就结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值