将源与C ++程序的程序集相关联

分析零售版本中的核心转储通常需要关联任何特定模块和源代码的objdump 。 正常情况下,如果function相当复杂,将程序集转储与源代码关联起来会变得很痛苦。 今天我试着创build一个特定模块的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这样的指令

注意所描述的答案,阅读通过汇编源代码,并根据符号(如函数调用,分支,返回语句)直观地确定模式是我通常做的。 我并不是否认它不起作用,但是当一个函数涉及到的时候,通过Assembly Listing的页面阅读是一件很痛苦的事情,而且经常会因为函数进入或者优化器简单的抛出而导致很less匹配该代码为它感到高兴。 我有一种感觉,看看Valgrind如何有效地处理优化的二进制文件,以及Windows中的WinDBG如何处理优化的二进制文件,这是我缺less的东西。 所以我虽然我会开始与编译器输出,并使用它来关联。 如果我的编译器负责修改二进制文件,它将是最好的人说如何与源关联,但不幸的是这是最没有帮助的, .loc真的是误导。 不幸的是,我经常需要通过不同平台上不可重复的转储来读取,而我花费的最less的时间是通过WinDBGdebuggingWindows Mini-Dump,并花费大量时间来debuggingLinux 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获得相对于库和系统函数的崩溃发生的记录)

请注意,尽管如此,在一些二进制格式中,可能会通过二进制文件中其他位置的小型封装来实现库函数的间接访问。 转储通常在程序流程的调用地址中仍然有相关的符号名称信息。 但即使不是这样,你也可以通过它们在二进制文件中的地址范围来识别这些外部链接包装,当你看到一个链接时,你可以找到它的代码并找出它所链接的外部函数。

但正如其他人所提到的,如果你有源代码和系统崩溃频率足够有用,你最快的选择通常是用调试符号重建,或插入日志记录输出,并获得更有用的崩溃记录。