涉及的问题:
- 函数调用
- 参数传递
- 函数返回
void test()
{}0x80483db <test> push %ebp
0x80483dc <test+1> mov %esp,%ebp
0x80483de <test+3> nop
0x80483df <test+4> pop %ebp
0x80483e0 <test+5> ret
// 保存返回地址
push eip
// 跳转执行函数
jmp func
push %ebp
mov %esp, %ebp # ebp = esp, mov ebp,esp in Intel syntax
sub $n, %esp # allocate space on the stack. Omit if n=0
=>
// 把 sp 设置为 bp,释放整个栈空间
ESP = EBP
// EBP向栈顶方向移动一步,恢复成调用前的样子
EBP = Pop()
// 操作结束后,bs,bp 恢复到了调用函数之前的值
不改变寄存器的值,直接 jmp 到 eax 保存的地址
=>(32 位机器)
// 栈指针
esp = esp - 4;(字长为 4),即栈向较低方向增长,小端操作系统
ss:esp = Source
int func(int a)
{
// dsth
return;
}
int main()
{
// int a;
func(a);
}int func(int a)
{
// 更新栈寄存器
push ebp
mov ebp, esp
// 取参数值
pop a
// dosth
// 设置返回值
mov eax retvalue
// 恢复栈寄存器的值
leave -> esp=ebp,pop ebp
// 跳转到之前函数的地址
ret -> pop return_addr,jump return_addr
}
int main()
{
// 更新栈寄存器
push ebp
mov ebp, esp
// 准备func参数
push a
// 调用func
call push -> push return address,jmp func
// 获取func返回值
eax
// 其他
}
总结:从一个函数调用另一个函数的过程:
原函数调用开始
- 参数压栈
- 返回地址压栈
- 将指令指针寄存器指向函数地址
调用函数开始.cpu 自动将栈指针更新
- 栈基址压栈
- 基址指针=栈指针
SI DI SP BP CS IP DS SS
SS SP维护栈地址. SS:栈顶地址 SP:栈偏移地址
64 位系统下,syscall 是默认的进入内核态的指令.此命令在 32 位 intel 处理器下不可用.
