2021-2022-1 20212821《Linux内核原理与分析》第二周作业

  • 实验一:反汇编一个简单的C程序

  • 实验要求:

实验部分(以下命令为实验楼64位Linux虚拟机环境下适用,32位Linux环境可能会稍有不同) 使用

$ gcc –S –o main.s main.c -m32

命令编译成汇编代码,并根据本周所学知识分析其汇编代码的工作过程。

  • 实验结果如下图所示:

在这里插入图片描述
在vim中使用

g/\.s*/d

命令查看main.s中的汇编代码如下,最左侧的数字表示行号:
在这里插入图片描述

  • 上述汇编代码的工作过程
  • 首先,程序从main函数中的第一条代码开始执行(假定此时EIP=行号18),即上图中的第18行代码“pushl %ebp”;程序开始执行前,假定寄存器EBP和寄存器ESP中的初始指针的值均为2000,其中寄存器EBP指向当前函数堆栈的栈底单元,而寄存器ESP则指向当前函数堆栈的栈顶单元;该条语句将EBP中的内容压栈,则执行这条语句后,寄存器ESP减去4变为1996,函数堆栈1996地址单元所存储的内容为EBP中的内容2000,EIP加一变为19;
  • EIP=19,此时执行第19行的指令“movl %esp, %ebp”,该条指令的功能为将寄存器ESP中的内容移动到寄存器EBP中,使得EBP与ESP内容相同;执行完此条指令后,EBP=ESP=1996,EIP增一后变为20;至此,main函数的函数堆栈建立完成,栈底指针EBP=栈顶指针ESP=1996;
  • EIP=20,执行指令“subl $4, %esp”,该条指令的功能为将寄存器ESP的内容减去立即数4,变为1996-4=1992;EIP+1->21;
  • EIP=21,执行指令“movl $8, (%esp)”,该条指令将立即数8送入寄存器ESP所指向的内存单元中,即地址1992所代表的内存单元中,使得(%esp)=8;EIP+1->22;
  • EIP=22,执行指令“call f”,该指令的功能为调用函数f;该指令执行时,会先将该指令的下一条指令的地址即EIP=23压入当前函数堆栈,即ESP-4=1988,EIP=23存入地址1988所指向的内存单元中,供函数返回时使用;之后该指令会将函数f中的第一条指令的地址即9存入寄存器EIP中,使得EIP=9;
  • EIP=9,和程序进入main函数开始执行时相同,执行指令“pushl %ebp”;该条指令的功能是将上层函数即main函数的函数堆栈栈底指针即寄存器EBP中的内容压入当前函数堆栈中,用于当前函数执行完时返回到上层函数的EBP寄存器状态;执行完此条指令后,ESP-4=1984,地址1984所存储的内容为当前EBP的内容即1996;EIP+1->10;
  • EIP=10,执行指令“movl %esp, %ebp”,该条指令将寄存器ESP中的内容送入到寄存器EBP中,使得EBP与ESP内容相同;执行完此条指令后,EBP=ESP=1984;此时,f函数的函数堆栈建立完成,栈底指针EBP=栈顶指针ESP=1984;EIP+1->11;
  • EIP=11,执行指令“subl $4, %esp”,该条指令的功能为将寄存器ESP的内容减去立即数4,变为1984-4=1980;EIP+1->12;
  • EIP=12,执行指令“movl 8(%ebp), %eax”,此条指令将EBP中的内容加8之后所指向的地址单元中的内容送入到寄存器EAX中,EBP+8=1984+8=1992,地址单元1992所存储的内容为8,则将8送入EAX中,8即为main函数所传递给函数f的参数;EAX=8;EIP+1->13;
  • EIP=13,执行指令“movl %eax, (%esp)”,该条指令将EAX中的内容送入到寄存器ESP所指向的内存存储单元中;当前ESP=1980,EAX中的内容为8,即将8存入到地址1980所代表的存储单元,用于向下层函数调用传递参数;EIP+1->14;
  • EIP=14,执行指令“call g”,该指令的功能为调用函数g;该指令执行时,会先将该指令的下一条指令的地址即EIP=15压入当前函数堆栈,即ESP-4=1976,EIP=15存入地址1976所指向的内存单元中,供函数返回时使用;之后该指令会将函数g中的第一条指令的地址即2存入寄存器EIP中,使得EIP=2;
  • EIP=2,执行指令“pushl %ebp”;该条指令将上层函数即f函数的函数堆栈栈底指针即寄存器EBP中的内容压入当前函数堆栈中,用于当前函数执行完时返回到上层函数的EBP寄存器状态;执行完此条指令后,ESP-4=1972,地址1972所存储的内容为当前EBP的内容即1984;EIP+1->3;
  • EIP=3,执行指令“movl %esp, %ebp”,该条指令将寄存器ESP中的内容移动到寄存器EBP中,使得EBP与ESP内容相同;执行完此条指令后,EBP=ESP=1972;此时,g函数的函数堆栈建立完成,栈底指针EBP=栈顶指针ESP=1972;EIP+1->4;
  • EIP=4,执行指令“movl 8(%ebp), %eax”,此条指令将EBP中的内容加8之后所指向的地址单元中的内容送入到寄存器EAX中,EBP+8=1972+8=1980,地址单元1980所存储的内容为8,则将8送入EAX中,8即为函数f传递给函数g的参数;EAX=8;EIP+1->5;
  • EIP=5,执行指令“addl $3, %eax”,该条指令将立即数3与EAX中的内容相加结果存入到EAX中;3+8->EAX,EAX=11;EIP+1->6;
  • EIP=6,执行指令“popl %ebp”,将之前保存的上层函数堆栈栈底指针出栈并送入到EBP中;g函数执行完毕,用于还原到f函数的堆栈环境;ESP=1972,地址单元1972存储的内容为1984,则EBP=1984,ESP=1972+4=1976;EIP+1->7;
  • EIP=7,执行指令“ret”,该指令将执行出栈操作,并把出栈的数送入到EIP中;ESP=1976,地址单元1976存储的内容为15,则EIP=15,ESP=1976+4=1980;
  • EIP=15,执行指令“leave”,相当于执行“movl %ebp, %esp”和“popl %ebp”两条指令,用于撤销当前函数堆栈,还原到上层函数的堆栈环境;先执行第一条指令,使得ESP=EBP=1984;执行第二条指令时,地址单元1984所存储的内容为1996,即为main函数的函数堆栈栈底指针;执行指令完毕后,ESP=1988,EBP=1996;EIP+1->16;
  • EIP=16,执行指令“ret”,该指令执行出栈操作,并把出栈的数送入到EIP中;ESP=1988,地址单元1988存储的内容为23,则EIP=23,ESP=1988+4=1992;
  • EIP=23,执行指令“addl $1, %eax”,该条指令将立即数1与EAX中的内容相加结果存入到EAX中;1+11->EAX,EAX=12;EIP+1->24;
  • EIP=24,执行指令“leave”,相当于执行“movl %ebp, %esp”和“popl %ebp”两条指令;main函数执行完毕,撤销main函数的函数堆栈;执行指令完毕后,EBP=ESP=2000,还原到程序执行初始时的状态;EIP+1->25;
  • EIP=25,执行指令“ret”,main函数执行完毕,退出main函数,此时EAX=12;
  • 汇编代码工作过程动态图演示如下:

在这里插入图片描述

  • 遇到的问题及解决方案
    之前一直不清楚函数调用堆栈的工作过程,不过通过对本实验的学习,我大致了解了一个简单C程序的函数调用堆栈工作过程,特别是enter和leave两条指令,对程序执行过程有了一个更加清晰的认识。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值