在汇编程序中调用C函数

从汇编程序中调用C语言函数的方法实际上在上面已经给出。在上面C语言例子对应的汇编程序代码中,我们可以看出汇编程序语句是如何调用swap()函数的。现在我们对调用方法作一总结。

在汇编程序调用一个C函数时,程序需要首先按照逆向顺序把函数参数压入栈中,即函数最后(最右边的)一个参数先入栈,而最左边的第1个参数在最后调用指令之前入栈,如图3-6所示。然后执行CALL指令去执行被调用的函数。在调用函数返回后,程序需要再把先前压入栈中的函数参数清除掉。

 
图3-6  调用函数时压入堆栈的参数

在执行CALL指令时,CPU会把CALL指令的下一条指令的地址压入栈中(见图3-6中的EIP)。如果调用还涉及代码特权级变化,那么CPU会进行堆栈切换,并且把当前堆栈指针、段描述符和调用参数压入新堆栈中。由于Linux内核中只使用中断门和陷阱门方式处理特权级变化时的调用情况,并没有使用CALL指令来处理特权级变化的情况,因此这里对特权级变化时的CALL指令使用方式不再进行说明。

汇编中调用C函数比较"自由",只要是在栈中适当位置的内容就都可以作为参数供C函数使用。这里仍然以图3-6中具有3个参数的函数调用为例,如果我们没有专门为调用函数func()压入参数就直接调用它的话,那么func()函数仍然会把存放EIP位置以上的栈中其他内容作为自己的参数使用。如果我们为调用func()而仅仅明确地压入了第1、第2个参数,那么func()函数的第3个参数p3就会直接使用p2前的栈中内容。在Linux 0.1x内核代码中就有几处使用了这种方式。例如在kernel/sys_call.s汇编程序中第231行上调用copy_process()函数(kernel/fork.c中第68行)的情况。在汇编程序函数_sys_fork中虽然只把5个参数压入了栈中,但是copy_process()却带有多达17个参数(见下面的程序)。

// kernel/sys_call.s汇编程序_sys_fork部分。
226push %gs
227pushl %esi
228pushl %edi
229pushl %ebp
230pushl %eax
231call _copy_process # 调用C函数copy_process()(kernel/fork.c,68)。
232addl $20,%esp   # 丢弃这里所有压栈内容。
233 1:  ret
// kernel/fork.c程序。
68 int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
69  long ebx,long ecx,long edx, long orig_eax,
70  long fs,long es,long ds,
71 long eip,long cs,long eflags,long esp,long ss)

我们知道,参数越是最后入栈,越是靠近C函数参数左侧。因此实际上调用copy_process()函数之前入栈5个寄存器值就是copy_process()函数的最左面的5个参数。按顺序它们分别对应为入栈的eax(nr)、ebp、edi、esi和寄存器gs的值。而随后的其余参数实际上直接对应堆栈上已有的内容。这些内容是从进入系统调用中断处理过程开始,直到调用本系统调用处理过程时逐步入栈的各寄存器的值。

参数none是sys_call.s程序第99行上利用地址跳转表sys_call_table[](定义在include/linux/ sys.h,93行)调用_sys_fork时的下一条指令的返回地址值。随后的参数是刚进入system_call时在85~91行压入栈的寄存器ebx、ecx、edx、原eax和段寄存器fs、es、ds。最后5个参数是CPU执行中断指令压入返回地址eip和cs、标志寄存器eflags、用户栈地址esp和ss。因为系统调用涉及程序特权级变化,所以CPU会把标志寄存器值和用户栈地址也压入堆栈。在调用C函数copy_process()返回后,_sys_fork也只把自己压入的5个参数丢弃掉,栈中其他值均保存着。其他采用上述用法的函数还有kernel/signal.c中的do_signal()、fs/exec.c中的do_execve()等,请读者自行分析。

另外,我们说汇编程序调用C函数比较自由的另一个原因是我们可以根本不用CALL指令而采用JMP指令来同样达到调用函数的目的。方法是在参数入栈后把下一条要执行的指令地址人工压入栈中,然后直接使用JMP指令跳转到被调用函数开始地址处去执行函数。此后当函数执行完成时就会执行RET指令,把人工压入栈中的下一条指令地址弹出,作为函数返回的地址。Linux内核中也有多处用到了这种函数调用方法,例如kernel/asm.s程序第62行调用执行traps.c中的do_int3()函数的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值