Dwarf 调试

什么是 DWARF?

DWARF(Debugging With Attributed Record Formats)是一种标准的调试信息格式,通常嵌入到二进制文件(如 ELF、Mach-O、PE 等)中。DWARF 由编译器生成,调试器使用它来提供源代码级别的调试信息,如变量名称、类型信息、源代码位置、函数调用栈等。

DWARF 格式并非特定于某一平台或架构,而是一种跨平台、跨架构的调试信息格式,广泛应用于许多操作系统和架构(如 Linux、macOS、FreeBSD、Windows 等)。

DWARF 的主要作用:

  1. 调试信息:DWARF 格式包含程序的调试信息,帮助调试器提供如下功能:

    • 显示程序的源代码和源行号。
    • 显示变量的名称、类型、作用域等信息。
    • 提供函数、类、结构体等符号的映射。
  2. 栈跟踪:DWARF 格式能描述函数调用堆栈,帮助调试器在崩溃或断点时回溯调用栈,显示程序运行过程中函数调用的关系。

  3. 程序反汇编:DWARF 也可以帮助调试器显示机器代码和源代码之间的对应关系,从而进行源代码级的调试,即使是在没有源代码的情况下,也能进行低级的调试。

DWARF 文件格式的结构:

DWARF 是基于 标签/值对 的结构,数据按块进行组织,主要包括以下几个部分:

  1. Debug Information Entries (DIEs):调试信息条目。每个 DIE 描述了程序中的一个符号(如变量、函数、类、类型等)。

  2. Attributes:每个 DIE 由多个属性(attributes)组成,属性提供了关于符号的详细信息,例如符号的名称、类型、大小等。

  3. Debug Sections:DWARF 信息存储在程序的不同段中。常见的段包括:

    • .debug_info:存储主调试信息。
    • .debug_abbrev:存储每个 DIE 所需的简略表示信息。
    • .debug_line:存储源代码的行号信息。
    • .debug_str:存储字符串(如变量名、函数名等)。
    • .debug_frame:用于描述堆栈帧信息,帮助栈展开。
  4. :DWARF 还可能包含其他类型的表,例如符号表、字符串表等,用于优化查找和存储。

如何使用 DWARF 信息?

调试器(如 GDB)通过读取二进制文件中的 DWARF 信息来执行源代码级调试。例如,使用 GDB 时,可以通过 DWARF 提供的源代码和变量信息,调试程序的执行。

例如,使用 GDB 进行调试时,GDB 会读取 DWARF 格式的调试信息,允许开发者进行以下操作:

  • 查看函数的调用栈。
  • 显示变量的值。
  • 在源代码级别单步调试。

Dwarf 示例

下面是一个简单的Dwarf示例:

首先,电脑中需要安装gcc, gcc的bin目录下一般会有objcopy, objdump这两个工具

写一个简单的C++代码,包含一个派生类,几个简单的变量

#include <stdio.h>

class CBase {
public:

    int MemberA;

    int MemberB;

    CBase() {
        MemberA = 0;
        MemberB = 0;
    }

    CBase(int a, int b) {
        MemberA = a;
        MemberB = b;
    }

};

class CDerived : public CBase {

public:

    int MemberC;

    CDerived(int a, int b, int c): CBase(a, b) {
        MemberC = c;      
    }
};

int main() {
    static CDerived d(1, 2, 3);
    int u1 = 1, u2 = 2;
    d.MemberC = (u1 + u2) * (d.MemberA - d.MemberB);
    printf("member c value: %d", d.MemberC);
    return 0;
}

用g++将它编译成exe或者elf

g++ main.cpp -o main.exe

可以用objdump命令打印它的Dwarf

objdump --dwarf=info main.exe > dump.txt

可以得到一份完整的dump文件,文件头如下所示,可以看到编译信息:

main.exe:     file format pei-x86-64

Contents of the .debug_info section:

  Compilation Unit @ offset 0:
   Length:        0x523 (32-bit)
   Version:       5
   Unit Type:     DW_UT_compile (1)
   Abbrev Offset: 0
   Pointer Size:  8
 <0><c>: Abbrev Number: 16 (DW_TAG_compile_unit)
    <d>   DW_AT_producer    : GNU C++17 14.2.0 -mtune=core2 -march=nocona -g
    <3c>   DW_AT_language    : 33	(C++14)
    <3d>   DW_AT_name        : (indirect line string, offset: 0): D:\Documents\TestDwarf\main.cpp
    <41>   DW_AT_comp_dir    : (indirect line string, offset: 0x20): D:\Documents\TestDwarf
    <45>   DW_AT_ranges      : 0xc
    <49>   DW_AT_low_pc      : 0
    <51>   DW_AT_stmt_list   : 0

对于基类,可以在dwarf信息看到类的定义,及其成员和地址偏移量

 <1><1d0>: Abbrev Number: 19 (DW_TAG_class_type)
    <1d1>   DW_AT_name        : CBase
    <1d7>   DW_AT_byte_size   : 8
    <1d8>   DW_AT_decl_file   : 2
    <1d9>   DW_AT_decl_line   : 5
    <1da>   DW_AT_decl_column : 7
    <1db>   DW_AT_sibling     : <0x253>
 <2><1df>: Abbrev Number: 9 (DW_TAG_member)
    <1e0>   DW_AT_name        : MemberA
    <1e8>   DW_AT_decl_file   : 2
    <1e8>   DW_AT_decl_line   : 8
    <1e9>   DW_AT_decl_column : 9
    <1e9>   DW_AT_type        : <0xe2>
    <1ed>   DW_AT_data_member_location: 0
    <1ee>   DW_AT_accessibility: 1	(public)
 <2><1ee>: Abbrev Number: 9 (DW_TAG_member)
    <1ef>   DW_AT_name        : MemberB
    <1f7>   DW_AT_decl_file   : 2
    <1f7>   DW_AT_decl_line   : 10
    <1f8>   DW_AT_decl_column : 9
    <1f8>   DW_AT_type        : <0xe2>
    <1fc>   DW_AT_data_member_location: 4

对于派生类,可以看到继承信息,派生类成员的地址偏移量会在基类后面

 <1><25d>: Abbrev Number: 22 (DW_TAG_class_type)
    <25e>   DW_AT_name        : (indirect string, offset: 0): CDerived
    <262>   DW_AT_byte_size   : 12
    <263>   DW_AT_decl_file   : 2
    <264>   DW_AT_decl_line   : 25
    <265>   DW_AT_decl_column : 7
    <266>   DW_AT_sibling     : <0x2b6>
 <2><26a>: Abbrev Number: 23 (DW_TAG_inheritance)
    <26b>   DW_AT_type        : <0x1d0>
    <26f>   DW_AT_data_member_location: 0
    <270>   DW_AT_accessibility: 1	(public)
 <2><271>: Abbrev Number: 9 (DW_TAG_member)
    <272>   DW_AT_name        : MemberC
    <27a>   DW_AT_decl_file   : 2
    <27a>   DW_AT_decl_line   : 29
    <27b>   DW_AT_decl_column : 9
    <27b>   DW_AT_type        : <0xe2>
    <27f>   DW_AT_data_member_location: 8
    <280>   DW_AT_accessibility: 1	(public)

对于变量,可以看到分配的地址,调试时通过地址+类型信息,可以得到变量的值
通过decl信息,则可以得到源代码的位置

 <2><381>: Abbrev Number: 11 (DW_TAG_variable)
    <382>   DW_AT_name        : d
    <384>   DW_AT_decl_file   : 2
    <384>   DW_AT_decl_line   : 38
    <385>   DW_AT_decl_column : 21
    <386>   DW_AT_type        : <0x25d>
    <38a>   DW_AT_location    : 9 byte block: 3 30 c0 0 40 1 0 0 0 	(DW_OP_addr: 14000c030)
 <2><394>: Abbrev Number: 11 (DW_TAG_variable)
    <395>   DW_AT_name        : u1
    <398>   DW_AT_decl_file   : 2
    <398>   DW_AT_decl_line   : 41
    <399>   DW_AT_decl_column : 9
    <39a>   DW_AT_type        : <0xe2>
    <39e>   DW_AT_location    : 2 byte block: 91 6c 	(DW_OP_fbreg: -20)
 <2><3a1>: Abbrev Number: 11 (DW_TAG_variable)
    <3a2>   DW_AT_name        : u2
    <3a5>   DW_AT_decl_file   : 2
    <3a5>   DW_AT_decl_line   : 41
    <3a6>   DW_AT_decl_column : 17
    <3a7>   DW_AT_type        : <0xe2>
    <3ab>   DW_AT_location    : 2 byte block: 91 68 	(DW_OP_fbreg: -24)

对更详细的dwarf信息解释,可以看官方文档

Dwarf 解析示例

某些语言的库有dwarf解析的相关sdk,如python有pyelftools用来解析Dwarf

示例代码

from elftools.elf.elffile import ELFFile
from elftools.dwarf.dwarf import DWARFError

def parse_dwarf(filename):
    # 打开 ELF 文件
    with open(filename, 'rb') as f:
        elf = ELFFile(f)

        # 查找 DWARF 部分
        if not elf.has_dwarf_info():
            print("No DWARF information found.")
            return
        
        dwarf_info = elf.get_dwarf_info()

        # 遍历 DWARF 信息
        for cu in dwarf_info.iter_CUs():  # 遍历编译单元(Compilation Unit)
            print(f"Compilation Unit: {cu.get_name()}")
            
            # 遍历调试信息条目(DIE)
            for die in cu.iter_DIEs():
                try:
                    # 打印 DIE 的类型和名称
                    if die.tag == 'DW_TAG_class_type':
                        class_name = die.attributes.get('DW_AT_name')
                        print(f"Class found: {class_name}")

                        # 打印类的成员变量(DW_TAG_member)
                        for member in die.iter_children():
                            if member.tag == 'DW_TAG_member':
                                member_name = member.attributes.get('DW_AT_name')
                                print(f"  Member: {member_name}")
                except DWARFError as e:
                    print(f"Error processing DIE: {e}")

if __name__ == "__main__":
    # 解析 DWARF 信息,假设可执行文件名为 program
    parse_dwarf('program')

运行程序后,输出如下所示:

Compilation Unit: /path/to/program
Class found: CBase
Member: MemberA
Member: MemberB
Class found: CDerived
Member: MemberC

posted @   Asp1rant  阅读(110)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示