往期地址:
- 从编译角度看c和c++混合编译
- stripped文件描述以及gdb反汇编工具使用
- 操作系统的信号量操作以及实战中的踩坑分析
- 程序的机器级表示——Intel x86 汇编讲解
- 操作系统系列(四)——栈与函数调用关系
本期主题:
程序的机器级表示—— 过程
目录
1. 过程的定义
过程是软件中一种重要的抽象,提供了代码的封装方式,用一组指定的参数和可选的返回值实现了某种功能。
不同编程语言中,过程的形式多样:
函数(function)、方法(method)、子例程(subroutine)、处理函数(handler) 等等。
现在假设这么一个场景:
过程P调用过程Q,Q执行后返回到P。这些动作包括下面这些机制:
- 传递控制。 在进入到过程Q的时候,程序计数器必须被设置为Q的起始地址,然后在返回时,要把
程序计数器设置为P调用Q后面那条指令的地址
。 - 传递数据。 P必须能够向Q提供一个或多个参数,Q必须能向P返回一个值。
- 分配和释放空间。 在开始时,Q要为局部变量分配空间,而在返回前,又要释放掉这些存储空间。
2. 运行时的栈
下图展示了一个较为通用的栈帧结构。
展示的过程是从过程P调用到过程Q,从上图可以看到这么一些信息:
- 当前正在执行的过程的帧总是在栈顶;
- 当过程P调用过程Q时,会把返回地址压入栈中。指明当Q返回时,要从P程序的哪个位置继续执行。
- 当过程Q执行时,会扩展当前栈的边界,分配自己所需要的栈帧空间,用来保存 寄存器的值、分配局部空间变量、为过程设置参数等。
3. 一些gdb的基础调试命令
- 开始运行gdb,gdb xxx
- 打断点,break xxx,例如在main函数打断点就是break main
- 想同步看汇编代码、源码,layout split命令,也可以layout next不停切换,找到自己想要的布局
- 单步执行,step和next,两者差异在于是否会跳进函数,step会进入函数,next不会
- 单步执行汇编,si
- 看当前栈帧信息, info frame
- 看当前栈帧回溯,backtrace
- 看寄存器信息,info register xxx
- 看memory信息,x/[n][f][u] address
- n 表示要查看的单位数目(例如:4 表示查看4个单位)。
- f 表示显示格式,可以是以下选项之一:
x:十六进制
d:十进制
u:无符号十进制
o:八进制
t:二进制
f:浮点数
a:地址
i:指令(反汇编指令)
c:字符
s:字符串
- u 表示每个单位的大小,可以是以下选项之一:
b:字节(1字节)
h:半字(2字节)
w:字(4字节)
g:巨字(8字节)
例如想看某个address开始的 连续20个word的内容,并用16进制显示,就输入如下命令:
x/20wx address
4.例子
1. 前提
看一个非常简单的代码,预期通过这份代码理解
- 当前栈帧的结构
- 当调用时,如何压栈,返回地址和调用参数的布局
- 当返回时,如何变化
2. c源码
#include <stdio.h>
int func1(int a, int b) {
int c = a + b;
return c;
}
int main() {
int arg1 = 10, arg2 = 20;
int ret;
ret = func1(arg1, arg2);
return 0;
}
3.调试过程
编译
使用-g -O0 编译选项
$ gcc -g -O0 -o example example.c
$ file example
example: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=aef991cd91436aeb886bbc89ab23cb7f4e252fc3, with debug_info, not stripped
gdb调试基础
gdb example //开始启动调试
break main //在main函数处打断点
run //运行
layout split //同时显示源码和汇编代码
输入上面的命令之后,能看到如下状态:
在main处一开始打断点的状态
- rip寄存器和栈帧信息
- rip寄存器指向下一条指令的地址
- 栈帧 level0,表示是当前栈帧
- 存的rip寄存器信息(调用帧的信息)
即将跳转到func1的状态
- 此时rbp、rsp、rip寄存器的状态如下:
跳转到func1之后的状态
当前栈帧地址的上面一段地址里的内容就返回地址,对应着我们这个示意图
TODO: 但是这里的两个参数 0xa和0x14没有对应上
按照我的预期,这两个参数应该在0x5…54639这个地址的前面,但是不知道为什么在f…fe270的栈帧里
有懂的朋友可以在评论区指导一下,谢谢