第六章 完善内核

函数调用约定简介

函数约定主要包括:

  1. 参数的传递方式,放在寄存器还是栈中?
  2. 如果参数放在栈中,调用完后,栈中的参数由谁来清理?
  3. 参数的传递顺序,从右往左还是从左往右?
  4. 返回值,放在哪里?
  5. 调用函数时,寄存器环境由谁保存,调用者还是被调用者?

cdecl调用约定:

cdel(c declaration,即c声明),c语言使用的调用约定。

参数的传递方式,使用栈(linux中,当参数个数小于等于5个时,使用寄存器,大于5时使用栈)

参数的传递顺序,从右往左顺序入栈

eax,ecx,edx寄存器由调用者保存,其他的寄存器由被调用者保存。

函数返回值,放在eax寄存器。

调用者清理栈中参数。

int subtract(int a, int b); //被调用者
int sub = subtract(3,2); //主调用者

;主调用者:
; 从右到左将参数入栈
push 2 ;压入参数b
push 3 ;压入参数a
call subtract ;调用函数subtract
add esp, 8 ;回收栈空间

;被调用者:
push ebp ;压ebp备份
mov ebp,esp ;将esp赋值给ebp
mov eax,[ebp+0x8] ;偏移8字节处为第一个参数a
add eax,[ebp+0xc] ;偏移0xc字节处为第二个参数b,参数a和b相加后存入eax
mov esp,ebp ;为防止中间有入栈操作,用ebp恢复esp
pop ebp ;将ebp恢复
ret

call指令和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP。它们经常被共同用来实现子程序的设计。

call

CPU执行call指令时,进行两步操作:

(1) 将当前的IP或CS和IP压入栈中;

(2) 转移。

ret和retf

ret指令用栈中的数据,修改IP的内容,从而实现近转移;

retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移。

CPU执行ret指令时,相当于进行:

pop IP

CPU执行retf指令时,相当于进行:

pop IP

pop CS

汇编语言和c语言混合编程

汇编和c语言可以相互调用。

c语言函数声明为外部函数,在汇编中调用函数。汇编文件和c文件分别编译,然后链接到一起生产可执行文件。

c语言调用汇编函数也是类似流程。