计算机基础

c语言的汇编分析

// 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

关于作者

程序员,软件工程师,java, golang, rust, c, python,vue, Springboot, mybatis, mysql,elasticsearch, docker, maven, gcc, linux, ubuntu, centos, axum,llm, paddlepaddle, onlyoffice,minio,银河麒麟,中科方德,rpm