GDB 汇编输出——无优化模式
学习如何在不进行代码优化的情况下编译、加载并执行 GDB 反汇编程序。
算术程序
让我们以一种在 C 和 C++ 中都能正常工作的形式重写我们的算术程序。对应的汇编语言指令已写在注释中:
1int a, b; 2int main(int argc, char* argv[]) 3{ 4 // 将值赋给变量 a 和 b 5 a = 1; // movl $1, a 6 b = 1; // movl $1, b 7 // 将 b 和 a 相加,结果存入变量 b 8 b = b + a; // mov a, %eax 9 // mov b, %edx 10 // add %edx, %eax 11 // mov %eax, b 12 // 将变量 a 的值加一 13 ++a; // mov a, %eax 14 // add $1, %eax 15 // mov %eax, a 16 // 将 a 与 b 相乘,结果存入 b 17 b = b * a; // mov b, %edx 18 // mov a, %eax 19 // imul %edx, %eax 20 // mov %eax, b 21 22 // 结果:(a) = 2 且 (b) = 4 23 // 返回 0 表示程序成功执行 24 return 0; 25}
无优化模式下的反汇编
在调试过程中,如果能不进行代码优化地重新编译,会更容易跟踪程序的运行进度。初始化 GDB 环境的步骤如下:
注意:你可以在本课末尾提供的编程沙箱中练习所有命令。
让我们以无优化模式(默认)进行编译和链接:
1gcc ArithmeticProjectC.cpp -o ArithmeticProjectC
这将生成一个可执行二进制文件,我们可以将其加载到 GDB 中检查汇编代码。
首先,使用程序作为参数运行 GDB:
1gdb ./ArithmeticProjectC
执行上述命令后,输出应如下所示:
接下来,我们在 C++ 的 main 函数中设置一个 breakpoint。这将在该点暂停程序执行,使我们有机会检查内存和寄存器。可以使用符号名或函数名(如 main)代替代码的内存地址:
1break main
在 GDB 环境中执行 break main 后,我们会得到如下结果:
在启动调试程序之前,GDB 必须禁用 Linux 内核的地址空间随机化,以避免相同内存地址被重新分配。以下命令用于关闭此功能:
1set disable-randomization off
然后执行(run)程序。程序将在之前设置的断点处停止:
1run
我们可以预期得到如下结果:
现在我们使用 disass 反汇编 main 函数:
1disass main
我们可以观察到如下输出:
现在我们重复显示与我们的 C++ 代码相对应的部分反汇编输出:
我们可以直接将其转换为原始汇编代码,并在注释中添加相应的伪代码:
1movl $1, a # 1 -> (a) 2movl $1, b # 1 -> (b) 3mov b, %edx # (b) + (a) -> (b) 4mov a, %eax 5add %edx, %eax 6mov %eax, b 7mov a, %eax # (a) + 1 -> (a) 8add $1, %eax 9mov %eax, a 10mov b, %edx # (b) * (a) -> (b) 11mov a, %eax 12imul %edx, %eax 13mov %eax, b
现在我们可以退出 GDB:
1q
输入此命令后,将得到如下输出:
命令总结
在本课中,我们学习了如何在不进行代码优化的情况下运行程序。为此,我们需要按以下顺序执行命令。
- 编译和链接程序的命令:
1gcc ArithmeticProjectC.cpp -o ArithmeticProjectC
- 启动 gdb 并加载程序的命令:
1gdb ./ArithmeticProjectC
- 在程序中插入断点的命令:
1break main
- 禁用程序地址空间随机化的命令:
1set disable-randomization off
- 执行程序的命令:
1run
- 反汇编程序的命令:
1disass main
- 退出当前函数的命令:
1finish
- 继续函数执行的命令:
1c
- 函数执行完成后,退出 GDB 环境:
1q
自行尝试
现在轮到你实践上述命令,查看 GDB 的反汇编输出。点击“运行”按钮,执行摘要中提到的命令。
No configuration found for ID: e0a2221639b32936a02accc9006839efdad115003faf43249a95aaca6b3c9d6f