发表于 2024/03/27 11:12:05 [计算机基础] 浏览次数:233
// c源代码 编译成汇编:gcc -S -o main.s main.c -m32
int g(int x)
{
return x + 3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8) + 1;
}
// 汇编代码
g:
pushl %ebp // 把ebp中存的栈地址(上一个函数即f函数的栈底的地址)压栈
movl %esp, %ebp // (ebp 和esp都指向当前栈帧的栈底,存的值上一个函数栈帧的栈底的地址)
movl 8(%ebp), %eax // 把传进来的参数8放入到eax中
addl $3, %eax // eax中的值加3 eax的值为11
popl %ebp // 弹栈,把栈中的内容(即f函数的栈底的地址)放入到ebp中,ebp指向f函数的栈底
ret // 相当于popl %eip 把调用函数(即f函数)的下一条指令(即33行)放入到eip中(转而执行33函数),同时esp+4个字节,栈空间缩小4,esp指向存参数(参数8)的栈空间
f:
pushl %ebp // 把ebp中存的栈地址(上一个函数即main函数的栈底的地址)压栈
movl %esp, %ebp // 这样esp 和ebp都指向当前栈帧的栈底(整个栈的栈顶),(存的值上一个函数栈帧的栈底的地址)
subl $4, %esp //esp减4个字节,相当于栈空间扩大4个字节,esp指针向下移
movl 8(%ebp), %eax // ebp目前指向当前栈帧的栈底, 这里是把ebp中的地址加8个字节的栈空间的内容(就是该函数的参数8)存到eax寄存器中
movl %eax, (%esp) // 把eax中的值即参数8 放到上上步扩展的4个字节的栈中间中 (传参数)
call g // 把eip中(下一行指令的地址即33行)压栈,把g函数的地址存入eip中,转而执行g函数
leave // 相当于movl %ebp,%esp; popl %ebp; movl %ebp,%esp:这样ebp和esp都执行当前函数的栈帧的栈底(存的是上一个函数的栈帧的栈底地址), popl %ebp : ebp则指向上一个函数的栈帧的栈底 ,esp加4个字节,指向调用函数的下一行指令即42行
ret // 相当于popl %eip 即 把调用函数(即main函数)的下一条指令(即42行)放入到eip中(转而执行42行的指令),同时esp+4个字节,栈空间缩小4,esp指向存参数(参数8)的栈空间
main: // 程序入口 (栈 开口向下,从高地址向低地址扩展)
pushl %ebp // (指向栈底),pubshl %ebp:把ebp寄存器中的值(栈地址程序开始存的是栈底的地址)压栈(占4个字节),同时esp减4个字节指向栈顶
movl %esp, %ebp // 这样esp 和ebp都指向栈顶了 (这两行每个函数开始之前都是固定操作,相当于进入一个新的栈帧中)
subl $4, %esp // esp减4个字节,相当于栈空间扩大4个字节,esp指针向下移
movl $8, (%esp) // 把参数8放到上一步扩展的栈空间中(传参数)
call f // 相当于pushl %eip, movl f %eip 把eip的值(下一行指令的地址,即42行)压栈,再把函数f的地址存入eip中,转而执行f函数
addl $1, %eax // 把eax的值加1 (即11+1=12)
leave // ebp和esp都指向栈底(完)
ret