• Linux- DWARF调试文件格式


    基本概念

    DWARF是一个用于在可执行程序和其源代码之间进行关联的调试文件格式。当开发者使用调试编译选项(例如,使用gcc时的-g标志)编译程序时,编译器会生成这种格式的调试信息。这些信息在后续的调试过程中非常有用,例如,使用gdb(GNU调试器)。

    以下是DWARF的一些主要特点和相关的详细信息:

    1. 历史DWARF起初是为了满足UNIX系统上的高效、紧凑和跨平台的调试需求而设计的。自那时起,它已经经历了多个版本,每个版本都增加了新的特性。

    2. 版本:从DWARF 1DWARF 5,每个版本都引入了新的特性和改进,以支持新的编程语言特性、编译器优化等。

    3. 数据结构DWARF信息由一系列的记录组成,这些记录描述了源代码的结构、变量、数据类型等。它们以所谓的"Debugging Information Entries" (DIEs)的形式存储。

    4. 查看工具:有一些工具可以查看和处理DWARF调试信息。例如,readelf(与binutils套件一起提供)可以使用-wi选项来查看DWARF信息。

    5. 优势

      • 跨平台DWARF是一个可扩展的格式,可以支持多种不同的架构和操作系统。
      • 紧凑:尽管包含大量的调试信息,但DWARF的设计使其尽可能紧凑,避免浪费存储空间。
      • 灵活性DWARF可以描述各种编程语言构造和复杂的数据类型。
    6. 与其他格式的比较:除了DWARF,还有其他的调试格式,例如stabsPE/COFF。但DWARF因其丰富的特性和跨平台支持而受到许多现代系统的青睐。

    总之,DWARF是一个强大的调试信息格式,允许开发者在程序的执行过程中访问源代码级别的详细信息。对于开发者或者正在学习调试技术的读者来说,了解DWARF和如何使用它将非常有益。


    示例

    下面,我们用一个简单的C语言程序,展示它如何与DWARF调试信息交互。

    假设我们有一个简单的C程序example.c

    #include 
    
    int add(int a, int b) {
        return a + b;
    }
    
    int main() {
        int sum = add(5, 7);
        printf("The sum is: %d\n", sum);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们使用GCC编译器编译这个程序,并使用-g选项来生成DWARF调试信息:

    $ gcc -g -o example example.c
    
    • 1

    现在,可以使用readelf命令来查看生成的DWARF信息。例如,查看DWARF头部信息:

    $ readelf --debug-dump=info example
    
    • 1

    这会列出关于源代码结构的很多信息,比如变量、函数等,并与其在源代码中的位置进行关联。

    为了进一步调试程序,可以使用gdb

    $ gdb ./example
    
    • 1

    在GDB中,可以进行各种操作,如设置断点、查看变量值等。所有这些操作都是基于DWARF调试信息的。

    例如,可以在add函数上设置断点:

    (gdb) b add
    
    • 1

    然后运行程序:

    (gdb) run
    
    • 1

    程序将在add函数处暂停执行,此时,可以查看和修改变量值、单步执行等。

    总的来说,DWARF调试信息提供了程序的详细视图,使得像gdb这样的调试器可以与源代码进行交互。


    DWARF调试信息

    下面,我们来详细了解DWARF调试信息。

    $ readelf --debug-dump=info example
    
    Contents of the .debug_info section:
    
      Compilation Unit @ offset 0x0:
       Length:        0xf4 (32-bit)
       Version:       5
       Unit Type:     DW_UT_compile (1)
       Abbrev Offset: 0x0
       Pointer Size:  8
     <0><c>: Abbrev Number: 3 (DW_TAG_compile_unit)
        <d>   DW_AT_producer    : (indirect string, offset: 0x0): GNU C17 11.4.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection
        <11>   DW_AT_language    : 29       (C11)
        <12>   DW_AT_name        : (indirect line string, offset: 0x29): example.c
        <16>   DW_AT_comp_dir    : (indirect line string, offset: 0x0): /home/majn/python_project/socket_project
        <1a>   DW_AT_low_pc      : 0x1149
        <22>   DW_AT_high_pc     : 0x56
        <2a>   DW_AT_stmt_list   : 0x0
     <1><2e>: Abbrev Number: 1 (DW_TAG_base_type)
        <2f>   DW_AT_byte_size   : 8
        <30>   DW_AT_encoding    : 7        (unsigned)
        <31>   DW_AT_name        : (indirect string, offset: 0x8e): long unsigned int
     <1><35>: Abbrev Number: 1 (DW_TAG_base_type)
        <36>   DW_AT_byte_size   : 4
        <37>   DW_AT_encoding    : 7        (unsigned)
        <38>   DW_AT_name        : (indirect string, offset: 0x93): unsigned int
     <1><3c>: Abbrev Number: 1 (DW_TAG_base_type)
        <3d>   DW_AT_byte_size   : 1
        <3e>   DW_AT_encoding    : 8        (unsigned char)
        <3f>   DW_AT_name        : (indirect string, offset: 0xa0): unsigned char
     <1><43>: Abbrev Number: 1 (DW_TAG_base_type)
        <44>   DW_AT_byte_size   : 2
        <45>   DW_AT_encoding    : 7        (unsigned)
        <46>   DW_AT_name        : (indirect string, offset: 0xbc): short unsigned int
     <1><4a>: Abbrev Number: 1 (DW_TAG_base_type)
        <4b>   DW_AT_byte_size   : 1
        <4c>   DW_AT_encoding    : 6        (signed char)
        <4d>   DW_AT_name        : (indirect string, offset: 0xa2): signed char
     <1><51>: Abbrev Number: 1 (DW_TAG_base_type)
        <52>   DW_AT_byte_size   : 2
        <53>   DW_AT_encoding    : 5        (signed)
        <54>   DW_AT_name        : (indirect string, offset: 0xd6): short int
     <1><58>: Abbrev Number: 4 (DW_TAG_base_type)
        <59>   DW_AT_byte_size   : 4
        <5a>   DW_AT_encoding    : 5        (signed)
        <5b>   DW_AT_name        : int
     <1><5f>: Abbrev Number: 1 (DW_TAG_base_type)
        <60>   DW_AT_byte_size   : 8
        <61>   DW_AT_encoding    : 5        (signed)
        <62>   DW_AT_name        : (indirect string, offset: 0xb3): long int
     <1><66>: Abbrev Number: 1 (DW_TAG_base_type)
        <67>   DW_AT_byte_size   : 1
        <68>   DW_AT_encoding    : 6        (signed char)
        <69>   DW_AT_name        : (indirect string, offset: 0xa9): char
     <1><6d>: Abbrev Number: 5 (DW_TAG_const_type)
        <6e>   DW_AT_type        : <0x66>
     <1><72>: Abbrev Number: 6 (DW_TAG_subprogram)
        <73>   DW_AT_external    : 1
        <73>   DW_AT_name        : (indirect string, offset: 0xcf): printf
        <77>   DW_AT_decl_file   : 2
        <78>   DW_AT_decl_line   : 356
        <7a>   DW_AT_decl_column : 12
        <7b>   DW_AT_prototyped  : 1
        <7b>   DW_AT_type        : <0x58>
        <7f>   DW_AT_declaration : 1
        <7f>   DW_AT_sibling     : <0x8a>
     <2><83>: Abbrev Number: 7 (DW_TAG_formal_parameter)
        <84>   DW_AT_type        : <0x8a>
     <2><88>: Abbrev Number: 8 (DW_TAG_unspecified_parameters)
     <2><89>: Abbrev Number: 0
     <1><8a>: Abbrev Number: 9 (DW_TAG_pointer_type)
        <8b>   DW_AT_byte_size   : 8
        <8c>   DW_AT_type        : <0x6d>
     <1><90>: Abbrev Number: 10 (DW_TAG_subprogram)
        <91>   DW_AT_external    : 1
        <91>   DW_AT_name        : (indirect string, offset: 0xae): main
        <95>   DW_AT_decl_file   : 1
        <96>   DW_AT_decl_line   : 7
        <97>   DW_AT_decl_column : 5
        <98>   DW_AT_type        : <0x58>
        <9c>   DW_AT_low_pc      : 0x1161
        <a4>   DW_AT_high_pc     : 0x3e
        <ac>   DW_AT_frame_base  : 1 byte block: 9c         (DW_OP_call_frame_cfa)
        <ae>   DW_AT_call_all_tail_calls: 1
        <ae>   DW_AT_sibling     : <0xc2>
     <2><b2>: Abbrev Number: 11 (DW_TAG_variable)
        <b3>   DW_AT_name        : sum
        <b7>   DW_AT_decl_file   : 1
        <b8>   DW_AT_decl_line   : 8
        <b9>   DW_AT_decl_column : 9
        <ba>   DW_AT_type        : <0x58>
        <be>   DW_AT_location    : 2 byte block: 91 6c      (DW_OP_fbreg: -20)
     <2><c1>: Abbrev Number: 0
     <1><c2>: Abbrev Number: 12 (DW_TAG_subprogram)
        <c3>   DW_AT_external    : 1
        <c3>   DW_AT_name        : add
        <c7>   DW_AT_decl_file   : 1
        <c8>   DW_AT_decl_line   : 3
        <c9>   DW_AT_decl_column : 5
        <ca>   DW_AT_prototyped  : 1
        <ca>   DW_AT_type        : <0x58>
        <ce>   DW_AT_low_pc      : 0x1149
        <d6>   DW_AT_high_pc     : 0x18
        <de>   DW_AT_frame_base  : 1 byte block: 9c         (DW_OP_call_frame_cfa)
        <e0>   DW_AT_call_all_calls: 1
     <2><e0>: Abbrev Number: 2 (DW_TAG_formal_parameter)
        <e1>   DW_AT_name        : a
        <e3>   DW_AT_decl_file   : 1
        <e3>   DW_AT_decl_line   : 3
        <e3>   DW_AT_decl_column : 13
        <e4>   DW_AT_type        : <0x58>
        <e8>   DW_AT_location    : 2 byte block: 91 6c      (DW_OP_fbreg: -20)
     <2><eb>: Abbrev Number: 2 (DW_TAG_formal_parameter)
        <ec>   DW_AT_name        : b
        <ee>   DW_AT_decl_file   : 1
        <ee>   DW_AT_decl_line   : 3
        <ee>   DW_AT_decl_column : 20
        <ef>   DW_AT_type        : <0x58>
        <f3>   DW_AT_location    : 2 byte block: 91 68      (DW_OP_fbreg: -24)
     <2><f6>: Abbrev Number: 0
     <1><f7>: Abbrev Number: 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121

    这是一个.debug_info节的内容,该节是由readelf从ELF文件中提取的。这个节包含了程序的DWARF调试信息。以下是这些输出信息的简要说明:

    1. Compilation Unit: 表示源文件的一个编译单位。这里是example.c

      • DW_AT_producer: 编译器和其版本信息。
      • DW_AT_language: 源代码的编程语言。
      • DW_AT_name: 源文件名。
      • DW_AT_comp_dir: 源文件的编译目录。
      • DW_AT_low_pc 和 DW_AT_high_pc: 表示程序地址范围。
    2. DW_TAG_base_type: 这是基础类型的定义,例如unsigned intunsigned char等。

      • DW_AT_byte_size: 数据类型的大小。
      • DW_AT_encoding: 数据类型的编码,例如signed、unsigned等。
      • DW_AT_name: 数据类型的名称。
    3. DW_TAG_subprogram: 代表一个函数或子程序。

      • DW_AT_external: 表明这个函数是否是外部的。
      • DW_AT_name: 函数名。
      • DW_AT_decl_file、DW_AT_decl_line、DW_AT_decl_column: 声明函数的文件、行号和列号。
      • DW_AT_prototyped: 表明该函数是否有原型。
      • DW_AT_type: 函数返回的数据类型。
    4. DW_TAG_formal_parameter: 表示函数的一个形式参数。

      • DW_AT_name: 参数名称。
      • DW_AT_decl_file、DW_AT_decl_line、DW_AT_decl_column: 参数在源文件中的位置。
      • DW_AT_type: 参数的数据类型。
      • DW_AT_location: 参数在堆栈上的位置。
    5. DW_TAG_pointer_type: 表示指针类型。

      • DW_AT_byte_size: 指针的大小,通常为地址大小。
      • DW_AT_type: 指针所指向的数据类型。
    6. DW_TAG_const_type: 表示常量类型。

    7. DW_TAG_unspecified_parameters: 表示函数可能有不确定数量的参数。

    8. DW_TAG_variable: 表示一个变量。

      • DW_AT_name: 变量名。
      • DW_AT_type: 变量的数据类型。
      • DW_AT_location: 变量的位置,可能是寄存器或堆栈上的位置。

    上述内容只是一个粗略的说明,DWARF格式包含了大量的复杂信息,用于在不运行程序的情况下,对其进行详细的分析和调试。


    在DWARF调试信息中,DW_AT_type: <0x58>表示一个引用或指针,它指向另一个DWARF条目。具体来说,这是一个偏移量,它从当前编译单元的开始处计算,指向描述该类型的DWARF条目。

    在给定的输出中,DW_AT_type: <0x58>意味着该属性引用了偏移量为0x58的条目以获取其类型信息。

    为了理解这代表什么,我们应该查找偏移量为0x58的条目。在提供的输出中,可以看到以下条目:

    <1><58>: Abbrev Number: 4 (DW_TAG_base_type)
       <59>   DW_AT_byte_size   : 4
       <5a>   DW_AT_encoding    : 5        (signed)
       <5b>   DW_AT_name        : int
    
    • 1
    • 2
    • 3
    • 4

    这意味着DW_AT_type: <0x58>引用的是一个基础类型,它是一个带符号的整数int,大小为4字节。


    DW_AT_stmt_list

    DW_AT_stmt_list 是一个属性,用于在DWARF调试信息中引用一个行号信息表的偏移量。这个属性关联了源代码的行号和生成的机器代码之间的映射,使得调试工具能够准确地确定执行的源代码行。

    具体来说,当我们在调试器中设置断点或在运行时遇到错误时,DW_AT_stmt_list 使得调试器可以准确地显示源代码中的相关行。

    值得注意的是,DW_AT_stmt_list 的值是一个偏移量,指向 .debug_line 节区中的一个位置,该位置包含与编译单元关联的行号程序。

    这允许开发者和调试工具从机器代码位置回溯到源代码位置,从而可以进行更有效的调试。


    Abbrev Number

    Abbrev Number 在 DWARF 调试信息中是一个非常重要的概念,用于表示一个 “abbreviation” 的唯一标识符。它与一个特定的 “abbreviation” 对象关联,该对象定义了一个特定的 DIE(Debugging Information Entry)的结构和属性。简单地说,它是一个用于描述 DIE 结构的模板。

    让我们详细解析一下:

    1. DIE (Debugging Information Entry): 在 DWARF 中,调试信息由许多的 DIEs 组成。每个 DIE 描述了编译单位中的某个实体,如变量、类型、函数等。每个 DIE 都有一个 “tag”(如 DW_TAG_base_type),用于描述其表示的实体类型。

    2. Abbreviations: 由于调试信息可能会很大,DWARF 使用一种缩写机制来减少所需的空间。这种机制通过定义一组 “abbreviations” 来工作,每个 “abbreviation” 描述了一种特定的 DIE 结构(包括其标签和属性)。这些 “abbreviations” 存储在 .debug_abbrev 节区中。

    3. Abbrev Number: 每个 “abbreviation” 都有一个唯一的编号,称为 Abbrev Number。这允许 .debug_info 节区中的 DIEs 通过简单地引用这个编号来描述其结构,而不是重复每个属性和标签。

    因此,当我们在 .debug_info 节区中看到 Abbrev Number: 1,这意味着可以查找 .debug_abbrev 节区中的编号为 1 的 “abbreviation”,从而知道该 DIE 的结构和属性。


    DW_AT_low_pc 和 DW_AT_high_pc

    在DWARF调试信息中,DW_AT_low_pcDW_AT_high_pc 是两个非常重要的属性,它们定义了一个代码范围的开始和结束地址。

    1. DW_AT_low_pc: 这是代码段的起始地址。在上面给出的例子中,DW_AT_low_pc: 0x1149 表示相关的代码段(可能是一个函数或其他代码块)从地址 0x1149 开始。

    2. DW_AT_high_pc: 在早期的DWARF版本中,这是代码段的结束地址。但在后来的版本中(尤其是DWARF4之后),这的含义发生了变化,它代表与DW_AT_low_pc的偏移量,而不是实际的结束地址。在上面给出的例子中,DW_AT_high_pc: 0x56 很可能表示代码段的长度是 0x56 字节。因此,真正的结束地址将是 0x1149 + 0x56

    为了确定 DW_AT_high_pc 的确切含义(是否是结束地址还是偏移量),需要查看DWARF版本以及可能存在的其他属性,如DW_AT_ranges。但在大多数情况下,考虑到新版本的DWARF,可以假设DW_AT_high_pc 表示从DW_AT_low_pc 的偏移量。


    DW_AT_decl_file

    在DWARF调试信息中,DW_AT_decl_file 属性表示源文件的索引,它指向文件名列表(通常存储在 .debug_line 节中的文件名表中)。

    在上面的例子中,DW_AT_decl_file : 1 表示这个特定的实体(可能是一个变量、函数等)是在文件名列表中索引为1的文件中声明的。

    为了找出索引1真正代表的文件名,需要查看 .debug_line 节,并找到对应的文件名表。这个表通常会列出编译单元中用到的所有文件,每个文件都有一个唯一的索引号,从1开始。这样,DWARF信息可以通过简单地引用索引号来指代一个特定的文件,而不是多次重复文件的完整路径名,从而节省空间。

  • 相关阅读:
    数据治理:如何利用元数据管理数据资产
    基于springboot实现家具销售电商平台管理系统项目【项目源码+论文说明】
    【洛谷P1081】开车旅行【链表,倍增】
    20PIN直插百兆网络变压器 H82022D
    动规(19)-并查集基础题——城镇道路
    Linux系统firewalld防火墙的基本操作
    谐云携手EMQ ,打造车联网平台联合解决方案
    栈的应用----括号匹配问题
    C# ppt文件转换为pdf文件
    活动预告丨EMNLP 2022半监督和强化对话系统研讨会12月7日线上召开!
  • 原文地址:https://blog.csdn.net/weixin_43844521/article/details/133801263