使用 Dwarf DebugInfo 和源代码将 Var 映射到声明

The*_*mad 2 c debugging static-analysis debug-symbols dwarf

给定变量访问(不是声明)的行号,我如何确定它的类型(或它在.info树中的声明 DIE )?

看下面的代码:

void foo()
{
   {
      struct A *b;
   }

   {
      struct B *b;

      b = malloc(sizeof(struct B));
   }
}
Run Code Online (Sandbox Code Playgroud)

假设我有这个源代码,它是用DWARF格式的调试信息编译的。如何使用源代码和调试信息确定变量b的类型struct B *

我的意思是如何离线自动化?问题是在源代码(例如,行号)和范围信息之间没有映射的.info部分DWARF。在上面的示例中,使用调试信息,我们可以确定存在一个类型struct A * 为 的子代的foo()变量和一个类型struct B *为 的另一个子代的变量foo()。解析源代码有助于确定发生访问的嵌套级别,但无法将访问的变量映射到其类型。因为在同一级别有两种类型b被访问。

如果有办法强制编译器在调试信息中包含更多的信息,问题就可以解决。例如,将DW_AT_high_pc和添加DW_AT_low_pc到 DIE 类型的调试信息DW_TAG_lexical_block将有所帮助。

Rob*_*ris 5

你已经回答了几乎所有你自己的问题;只缺少两件事。

首先,文件名/行号和程序计数器之间的关系被编码为.debug_line,而不是.debug_info

其次,变量不是 的孩子foo():每个都是词法块的孩子。程序结构的相关部分看起来像

DW_TAG_compile_unit
    DW_TAG_subprogram
        DW_TAG_lexical_block
            DW_TAG_variable
        DW_TAG_lexical_block
            DW_TAG_variable
Run Code Online (Sandbox Code Playgroud)

词法块应与地址范围相关联,但这可能会使用DW_AT_ranges代替DW_AT_low_pc/进行编码DW_AT_high_pc;如果是这种情况,那么您需要解释.debug_ranges.

为了说明手头的案例,我使用cc -g(Oracle Linux 上的 gcc 4.8.5)编译了以下内容...

  1 #include <stdlib.h>
  2 
  3 struct A { int a; };
  4 struct B { int b; };
  5 
  6 void foo()
  7 {
  8     {
  9         struct A *b;
 10     }
 11 
 12     {
 13         struct B *b;
 14         b = malloc(sizeof (struct B));
 15     }
 16 }
Run Code Online (Sandbox Code Playgroud)

...并使用 'readelf -w' 解码 DWARF。第 14 行出现在行号表中:

  [0x00000032]  Special opcode 124: advance Address by 8 to 0x8 and Line by 7 to 14
Run Code Online (Sandbox Code Playgroud)

这意味着我们对地址 0x8 感兴趣。DIE 层次结构包括

<0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)

<1><96>: Abbrev Number: 6 (DW_TAG_subprogram)
   <9d>   DW_AT_low_pc      : 0x0
   <a5>   DW_AT_high_pc     : 0x18

<2><b3>: Abbrev Number: 7 (DW_TAG_lexical_block)
   <b4>   DW_AT_low_pc      : 0x8
   <bc>   DW_AT_high_pc     : 0xe

<3><c4>: Abbrev Number: 8 (DW_TAG_variable)
   <c5>   DW_AT_name        : b
   <c7>   DW_AT_decl_file   : 1
   <c8>   DW_AT_decl_line   : 13
   <c9>   DW_AT_type        : <0xd2>
Run Code Online (Sandbox Code Playgroud)

0xb3 处的 DIE 不包含任何进一步的词法块,因此它代表地址 0x8 处的最紧密范围。因此,此时名称“b”必须指代 DIE 的 0xc4 子节点。该变量的类型由

 <1><d2>: Abbrev Number: 9 (DW_TAG_pointer_type)
    <d3>   DW_AT_byte_size   : 8
    <d4>   DW_AT_type        : <0x81>

 <1><81>: Abbrev Number: 4 (DW_TAG_structure_type)
    <82>   DW_AT_name        : B
    <84>   DW_AT_byte_size   : 4

 <2><8b>: Abbrev Number: 5 (DW_TAG_member)
    <8c>   DW_AT_name        : b
    <90>   DW_AT_type        : <0x34>
    <94>   DW_AT_data_member_location: 0

 <1><34>: Abbrev Number: 3 (DW_TAG_base_type)
    <35>   DW_AT_byte_size   : 4
    <36>   DW_AT_encoding    : 5    (signed)
    <37>   DW_AT_name        : int
Run Code Online (Sandbox Code Playgroud)

编辑:

在您自己的回答中,您给出了 mplayer 的反例,其中存在没有相应地址范围的词法块。这样的 DWARF 不符合标准:DWARF 2 的 §3.4 指出词法块条目具有 DW_AT_low_pc 和 DW_AT_high_pc 属性,并没有暗示这些是可选的。假设您使用 gcc,此错误的一个可能候选者是“内联词法块缺失范围的 DWARF 调试信息”。默认的 mplayer 配置包括 -O2 优化,它打开内联;您将看到这反映在父DW_TAG_subprogramfor 中draw_vertices(),从中获取示例代码。该错误的解决方法是添加-fno-inline到编译器选项;这似乎并没有抑制所有内联,因此您可能希望完全禁用优化。