代码之家  ›  专栏  ›  技术社区  ›  TheAhmad

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

  •  2
  • TheAhmad  · 技术社区  · 6 年前

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

    查看以下代码:

    void foo()
    {
       {
          struct A *b;
       }
    
       {
          struct B *b;
    
          b = malloc(sizeof(struct B));
       }
    }
    

    假设我有这个源代码,并且它是用中的调试信息编译的 DWARF 总体安排如何确定该变量 b 属于类型 struct B *

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

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

    2 回复  |  直到 6 年前
        1
  •  5
  •   Robert Harris    6 年前

    你已经回答了几乎所有你自己的问题;只缺两样东西。

    首先,将文件名/行号与程序计数器之间的关系编码为 .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
    

    词法块应该与地址范围相关联,但可以使用 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 }
    

    ...并用“readelf-w”来解码侏儒。第14行显示在行号表中:

      [0x00000032]  Special opcode 124: advance Address by 8 to 0x8 and Line by 7 to 14
    

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

    <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>
    

    0xb3处的DIE不包含任何其他词法块,因此它表示地址0x8处的最紧范围。因此,此时,名称“b”必须指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
    

    编辑:

    在您自己的回答中,您给出了一个mplayer的反例,其中有没有相应地址范围的词法块。这种侏儒不符合标准:侏儒2的§3.4规定,词汇块条目具有DW_AT_low_pc和DW_AT_high_pc属性,并且没有暗示这些属性是可选的。假设您使用的是gcc,则可能出现此错误的原因是“ DWARF debug info for inlined lexical blocks missing range “。默认的mplayer配置包括-O2优化,它打开内联;您将在父级中看到这一点。” DW_TAG_subprogram 对于 draw_vertices() ,从中获取示例代码。该错误的解决方法是添加 -fno-inline 到编译器选项;这似乎并没有抑制所有内联,所以您可能希望完全禁用优化。

        2
  •  1
  •   TheAhmad    6 年前

    以下是的输出 objdump --dwarf=info mplayer 对于 MPlayer-1.3.0 使用编译 -gdwarf-2 选项

    <2><4000e>: Abbrev Number: 43 (DW_TAG_lexical_block)
    <3><4000f>: Abbrev Number: 37 (DW_TAG_variable)
    <40010>   DW_AT_name        : px
    <40013>   DW_AT_decl_file   : 1
    <40014>   DW_AT_decl_line   : 2079
    <40016>   DW_AT_type        : <0x38aed>
    <3><4001a>: Abbrev Number: 37 (DW_TAG_variable)
    <4001b>   DW_AT_name        : py
    <4001e>   DW_AT_decl_file   : 1
    <4001f>   DW_AT_decl_line   : 2080
    <40021>   DW_AT_type        : <0x38aed>
    <3><40025>: Abbrev Number: 0
    <2><40026>: Abbrev Number: 0
    

    正如您在偏移处看到的 0x4000e ,有一个没有属性的词法块。相应的源代码位于 libvo/gl_common.c:2078 :

    for (i = 0; i < 4; i++) {
    int px = 2*i;
    int py = 2*i + 1;
    mpglTexCoord2f(texcoords[px], texcoords[py]);
    if (is_yv12) {
      mpglMultiTexCoord2f(GL_TEXTURE1, texcoords2[px], texcoords2[py]);
      mpglMultiTexCoord2f(GL_TEXTURE2, texcoords2[px], texcoords2[py]);
    }
    if (use_stipple)
      mpglMultiTexCoord2f(GL_TEXTURE3, texcoords3[px], texcoords3[py]);
    mpglVertex2f(vertices[px], vertices[py]);
    }
    

    该块是for块。还有更多类似的lexical\u块实例。

    我的解决方案由两部分组成:

    1) 源代码分析:

    查找访问目标变量的范围(左大括号和右大括号周围)。实际上,我们只需要存储左大括号的行号。

    在作用域树中查找作用域的级别(该树显示与中类似的父/子关系 .info .

    此时,我们有了与变量访问相对应的范围的起始行,以及范围树中的范围级别(例如,原始问题中描述的代码中的第12行和第2级)。

    2) DebugInfo分析:

    现在,我们可以分析适当的CU并查找该目标变量的声明。重要的一点是,只有行号小于访问点行号的声明才有效。考虑到这一点,我们可以搜索全球范围,并继续进行更深层次的排序。

    范围大于访问范围的声明无效。与目标变量作用域相同的声明只有在其行号介于目标作用域的起始行和变量访问的行号之间时才有效。