分类: LINUX
2015-06-21 18:09:46
原文地址:浅析linux系统函数调用的工作机制 作者:automaton
使用如下命令编译成汇编代码,然后删除汇编代码中用于链接的辅助信息:
gcc –S –o main.s main.c -m32
处理后的汇编代码为:
我们从程序入口main函数开始分析栈帧的变化过程,程序从main.s第17行开始运行,执行完第21行后堆栈内容如下:
接着运行第22行的call指令,call指令指示处理器在新的内存地址执行指令,以实现对函数的调用。具体操作是把返回地址压入栈并且把被调用过程的地址复制到指令指针寄存器中。执行第22行后,将函数f的调用地址存入指令指针寄存器EIP,栈帧内容如下:
然后开始执行函数f,执行第9行和第10行用于建立一个栈帧,并且将EBP设为这个栈帧的基址:
第11行ESP值减4,指向内存中低地址的下一下字;第12行使用寄存器相对寻址方式将函数的形式参数x复制到到寄存器EAX;第13行将EAX中的数即传入的参数复制到ESP所指向的内存,用做调用函数g时的参数。执行完第13行以后的栈帧内容如下:
第14行将调用函数g,执行完以后栈帧内容如下:
执行第14行后开始调用函数g,第2行和第3行用于建立函数g的栈帧,并且将EBP设为这个栈帧的基址。执行完第3行以后栈帧内容如下:
第4行使用寄存器相对寻址方式将所传入参数复制到通用寄存器EAX;第5行将EAX中的值加上立即数314,相加后的和仍保存在EAX中。执行完第5行后的栈帧内容如下:
第6行的popl指令后用于恢复上一个栈帧的基址,准备从函数g中返回,执行第6行后栈帧内容如下:
接着执行第7行的ret指令,函数g执行返回到函数f中。ret指令作用是使处理器返回到程序中子例程被调用的地方继续执行。具体操作是从栈上弹出由call指令保存的返回地址到指令指针寄存器EIP。执行完ret指令后栈帧内容如下:
ret指令执行后函数g调用完毕,返回到第15行。leave指令用于释放一个子例程的栈帧,等价与以下两条指令: