将源与C++程序的程序集列表相关联
Correlate Source with Assembly Listing of a C++ Program
在零售构建中分析核心转储通常需要关联任何特定模块的objdump
和源。通常,如果函数非常复杂,将程序集转储与源相关联会变得很痛苦。今天我尝试创建一个特定模块的assembly listing
(使用编译选项-S
(,期望我会看到一个带有汇编或某种相关性的交错源代码。不幸的是,该列表不够友好,无法关联,所以我想知道
- 给定一个核心转储,我可以从中确定崩溃位置
- 通过重新编译
objdump
- 带有
-S
选项的模块。
是否可以与消息来源进行一对一的通信?
例如,我看到程序集列表为
.LBE7923:
.loc 2 4863 0
movq %rdi, %r14
movl %esi, %r12d
movl 696(%rsp), %r15d
movq 704(%rsp), %rbp
.LBB7924:
.loc 2 4880 0
testq %rdx, %rdx
je .L2680
.LVL2123:
testl %ecx, %ecx
jle .L2680
movslq %ecx,%rax
.loc 2 4882 0
testl %r15d, %r15d
.loc 2 4880 0
leaq (%rax,%rax,4), %rax
leaq -40(%rdx,%rax,8), %rdx
movq %rdx, 64(%rsp)
但无法理解如何解释像.LVL2123
这样的标签和像.loc 2 4863 0
这样的指令
注意正如答案所描述的那样,通读程序集源代码并根据符号(如函数调用、分支、return 语句(直观地确定模式是我通常所做的。我不否认它不起作用,但是当一个函数非常复杂时,阅读程序集列表的页面是一种痛苦,并且通常您最终会列出很少匹配的列表,因为函数内联或优化器只是随心所欲地扔掉了代码。我有一种感觉,看到Valgrind
如何处理优化的二进制文件以及如何在 Windows WinDBG 中处理优化的二进制文件,我缺少一些东西。所以我会从编译器输出开始并使用它来关联。如果我的编译器负责修改二进制文件,那么说明如何与源代码相关联将是最好的人选,但不幸的是,这最没有帮助,而且.loc
确实具有误导性。不幸的是,我经常不得不阅读各种平台上不可重现的转储,我花在通过WinDBG调试Windows Mini转储和调试Linux Coredumps的大量时间上花费了最少的时间。我想这可能是我没有正确地做事,所以我提出了这个问题。
是否可以与消息来源进行一对一的通信?
答:不会,除非禁用了所有优化。编译器最初可能会每行发出一组指令(或类似指令的东西(,但优化器随后会重新排序、拆分、融合并通常完全更改它们。
如果我正在反汇编发布代码,我会查看应该与代码有明确逻辑关系的说明。例如,
.LBB7924:
.loc 2 4880 0
testq %rdx, %rdx
je .L2680
如果 %rdx
为零,则看起来像一个分支,它来自第 4880 行。找到该行,确定正在测试的变量,记下它当前已分配给%rdx
。
.LVL2123:
testl %ecx, %ecx
jle .L2680
好的,所以这个测试和分支具有相同的目标,所以接下来的任何内容都知道%rdx
和%ecx
都是非零的。原始代码的结构可能如下所示:
if (a && b) {
或者可能是:
if (!a || !b) {
优化器对两个分支进行了重新排序......
现在你已经有一些结构,希望可以与原始代码匹配,你也可以弄清楚寄存器分配。 例如,如果您知道被测试的东西是某个结构的数据成员,请向后读取以查看%rdx
从内存加载的位置:它是否从固定偏移量加载到其他寄存器?如果是这样,则该寄存器可能是目标地址。
祝你好运!
.loc
指令就是你要找的。这些表示行 #4863、4880 等。源代码和优化汇编程序之间没有完美的映射(这就是为什么您多次看到 4880 的原因(。但.loc
在于您如何知道它在文件中的位置。语法为:
.loc <file> <line> <column>
除非你静态链接到系统库,否则即使没有调试符号,二进制文件中也会有符号名称 - 链接到的系统库函数的符号名称。
这些通常可以帮助您缩小代码中的位置。 例如,如果你看到在函数foo((中它调用open((,然后调用ioctl((,然后在调用read((之前崩溃,你可能可以很容易地在foo的源代码中找到这一点。(就此而言,您甚至可能不需要转储 - 在 linux 上,您可以使用 ltrace 或 strace 获取相对于库和系统函数的崩溃发生记录(
请注意,在某些二进制格式中,可能会通过二进制文件中其他位置的微小包装器间接到库函数。 通常,转储在程序流中的调用地址处仍具有相关的符号名称信息。 但即使没有,你也可以通过它们在二进制文件中的地址范围来识别这些外部链接包装器,当你看到一个时,你可以去找到它的代码并找出它链接到哪个外部函数。
但正如其他人所提到的,如果你的源代码和系统崩溃的频率足够高,所以你最快的选择通常是使用调试符号重建,或者插入日志记录输出并获得更有用的崩溃记录。
- 内联程序集printf将整数解释为地址
- 正在解码MSVC 32位版本的程序集(作业).没有手术做什么
- 具有外部"c"和程序集的未定义函数
- 用于将C++代码转换为 Web 程序集的脚本未终止
- 为什么我的C++程序的程序集输出充满了 .ascii,没有汇编代码?
- CoreCLR 中的检测探查器 - 将帮助程序程序集加载到 dotnet 进程的方法
- 不同于按值传递和常量引用传递的程序集
- 为什么在堆栈和堆上创建变量会产生相同的程序集代码?
- C++变量在调用 x64 程序集函数后重置为 0
- 如何将C++子例程链接到 x86 程序集程序?
- Qt 网页程序集缓存
- 测试操作系统时执行程序集"sti"时虚拟框崩溃
- 为什么从 constexpr 引用生成的程序集代码与从 constexpr 指针生成的程序集代码不同?
- 将内联程序集尾调用函数尾声替换为用于x86/x64 msvc的Intrinsics
- 解析 C# 中的C++程序集
- 64 位进程中的 AnyCPU C# DLL 无法引用 64 位C++ DLL(给出错误:无法加载文件或程序集)
- 使用CLANG内联程序集创建C++预增量操作
- 是否可以在C++中基于程序集输出(.dll或.exe)定义变量
- 为什么每次生成时,本地 .NET 程序集在引用列表中(几乎)都显示为损坏
- 将源与C++程序的程序集列表相关联