如何简单查看一个程序的汇编过程

本文讲解了如何使用GDB对C语言程序进行调试,涉及单步跟踪、寄存器分析及函数调用时栈的变化,适合IT技术人员学习.

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.简单的程序
#include <stdio.h>
int fun(int a,int b)
{
        int j =0;
        j = a+b;
        return j;
}
int main()
{
        int x = 0x5;
        int y = 0x6;
        int z = 0x8;
        z = fun(x,y);

        printf("hello\n");
        return 0;
}

如何查看这个简单的程序是如何执行下来的呢?

简单的c语言程序可以直接了当的看完,具体调试时我们使用命令

gcc test.c -g

此时生成的二进制机器码文件,此时的文件生成的代码是给linux看的,所以我们看起来很难理解,所以我们退而求其次,我们求汇编成的代码,以 .s 结尾

gcc test.c -S -o test.s

此时生成 a.out 文件,我们使用 gdb程序来运行 a.out

gdb遵循GUN GPL 原则开源

为了方便观看程序执行过程,所以我们需要对程序进行断点处理

命令  b 就是需要打断点的位置

成功打入断点,这里在main函数之前发生了很多事情,但是我们并不用关心这些。

之后使用命令 r 开跑(run),到main停止

注:

1.了解单步跟踪和单步跟踪进入的区别,单步跟踪即一步一步的运行程序,逐语句是一个语句一个语句执行,逐过程是一个过程的执行,当下一步为调用函数时,我们需要进入到函数内部,那就是单步跟踪进入,看函数是如何一步一步执行的,而不是跳过函数调用过程直接给出结果,我们该如何做呢?

在gdb中,需要使用命令 s 和 n。

s即 执行一行代码,如果该行代码中有函数调用,那么进入该函数;

n即 执行一行代码,一并执行包含的函数调用。

2.了解p命令和寄存器rbp,rsp

p命令即为打印,打印出rsp,rbp的地址以及我们下一个执行语句的地址

我们可以直接发现rsp地址高于main的地址,rsp就是指向栈里的地址,代码段在低地址。

当我们执行s 时,发现rsp,rbp位置并没有发生改变,我们使用x/16x打印十六进制的值,打印十六个,从rsp开始打印(每一段为四个字节即64位下的int)

我们发现地址是从rsp开始向上打印

当我们再次执行s时

所执行的为红色箭头所指那一步,push 了rbp

此时我们查看地址发现rsp地址低了八位,就是我们压入栈rbp的地址

我们再次进行  s 操作,此时我们执行的是上面的 movq 这段代码,也就是将rbp指针移动到rsp的位置

此时我们的栈顶指针等于了栈底指针,也就是空栈

再次s,执行上面的subq  语句

我们发现rsp动了,rbp没动,跟上面的地址一样,动了16个位置,就是分配给main的栈针

再次s,执行,将5放在rbp偏移12的位置

也就是这个位置

再次s,执行这一段,把6存放在rbp偏移8的位置上

再次s,执行下面这一段

我们这里的 5 6 8就是我们上面c 语言代码中定义的 x y z。

接下来我们应该执行到了上面代码中main的调用函数的位置

再次s,将 6 也就是y,放在了edx的寄存器中

我们再次s,执行下面第一行代码,也就是x=5的位置搬到寄存器中

再次s,执行下面第一行,做了内存的交接

再次s,此时才是 call fun

此时我们看一下rsp和rbp的位置,发现位置没有改变

此时我们s,进入到fun函数里面我们发现rsp变了位置,少了2也就是8个位置,这个8就是地址

这个地址其实是我们执行的fun函数下一行的语句的地址,不然我们怎么知道函数执行完返回到哪里,我们后面的printf该如何进行呢,就是把后面语句的地址一并压入到了栈中

再次s,地址不变,执行了endbr64,意思是上个栈结束了,到了新的fun里面

再次s之后,我们发现地址又少了8

我们看一下rsp,其实就是我们把上一个main里面的栈底指针地址拿了过来,方便我们返回函数时,回到main里面的位置,这是我们回去的地址

我们再次s,得到rsp和rbp地址一样了,也就是执行了

相当于我们main函数里面的将栈置空

再次s执行

再次s执行

再次s执行

就是内存的偏移,和前面一样

这里的-20 和 -24 的位置就是 5 6 的位置,也就是我们传进来的 xy。 

再次 s ,执行addl 相加

s之后,我们发现eax中的值变成了两者的和,这里的  -4 位置就是 j 的位置

s后,这个汇编动作其实是多余的,提升效率可以删掉

我们s 之后再次查看地址,发现并没有改变,rsp rmp 都是0

再次s之后,fun调用结束

查看地址rsp rmp 我们发现rsp地址高了 8位,也就是我们之前存进栈的main地址,并赋值给了rbp,所以rbp又回到了调用之前的main 的栈底地址位置(这就是之前我们为什么要把main的栈底地址压入到fun的栈中)

再次s,rbp归位了所以不动,rsp栈顶继续压,移动了8位置,并且这里的movl执行就是返回值给main里面的z,接下来执行printf,程序结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值