Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DWARF 眼中的 C++

DWARF 眼中的 C++

DWARF 如何关联 C++ 程序的语义元素、文本、和代码生成。

视频: https://www.bilibili.com/video/BV1tZqBBmEwS (from 10:00)

Avatar for Zhihao Yuan

Zhihao Yuan

December 20, 2025
Tweet

More Decks by Zhihao Yuan

Other Decks in Programming

Transcript

  1. 眼眼眼眼眼眼 • 在 Linux 在在在在在在“在在在在在在在在在在在” • GCC 在 -g 在在在在在在在在在在在在在在在在在在在在在在在在

    • strip 在在在在在在在在在在在在在在在在 • 在 Windows 在在在在在在“在在在 pdb” • MSVC 在 /Zi 在在在在在在在在在在在在在在在在在在在在 .pdb 在在在 • 在在在在在在 Microsoft symbol server 在在在在 pdb 4
  2. PDB 眼眼眼眼眼眼眼 • 在在在在在在在在在在 Multi-stream file • MSVC 在 /Z7

    在在在在在在在在在在在 PE 在在 .obj 在在在 • 在在 GCC & Clang • 在在在在在在在在在 “PDB” • PE (Portable Executable) 在在在在在在 PDB 在在 5
  3. DWARF 眼眼眼眼眼眼眼眼眼眼眼眼眼眼眼 • Mingw-w64 在在在在在 PE 在在在在在在在在 GCC 在在 •

    在在在在在在 DWARF • ELF 在在在在在在在在在在在在在在在在在在在在在在 • objcopy --only-keep-debug foo foo.debug • 新的文件同样是 ELF 格式 • Mac 在在在在在在在在在在在在在在 .dSYM bundle • 在在在在在在在在在在在 .dSYM 在在在在在在在在在在在在 • 在在在在在在在在在在在 Mach-O 在在 • GCC 在 -gsplit-dwarf 在在在在在 .o 在在在在在在 .dwo 在在 • 在在 ELF 在在在在在在在在 Split DWARF sections在在在在在在在在在 6
  4. Section • 在在 ELF 在在 (.text, .rodata) 在在在 • 在在

    .debug_info • 在在在在在在在在在在在在在在在在在在在在在在 • 在 Mach-O 在在在在在在在在在在在 sections 在在 _debug_info • 在在 sections 在在在在在在在 sections 在在在在在 • 在在在在在在在在在在在在 section, 在在在在在在在在在 8
  5. 眼眼 Sections 眼眼眼眼眼 • 在在在在在在在在在在在在在在在在在在在 .debug_info • 在在在在在在在在在在在在在在在在在在 (line program)

    .debug_line • 在在在在在在在在在在在 .debug_ranges, .debug_loc (pre-V5) .debug_rnglists, .debug_loclists • 在在在在在在在在 .debug_names 9
  6. Compilation Unit (CU) • 在在 C++ 在在在在在 translation unit 在在在在在

    DWARF 在 compilation unit 在在在在在在 • 在在 translation unit 在在在在在在 compilation unit • 在在 DWARF 在在在在在 compilation units 在在 10
  7. 眼眼 namespace test_dwarf { class MyCls { int data_; public:

    [[gnu::visibility("default")]] void callme(int a); }; } test_dwarf.h 12
  8. 眼眼 #include "test_dwarf.h" #include <stdio.h> namespace test_dwarf { void MyCls::callme(int

    a) { printf("total %d\n", a + data_); } } test_dwarf.cc clang++ -shared \ -fvisibility=hidden \ -g \ -o libtest_dwarf.so \ test_dwarf.cc 13
  9. Debugging Information Entry (DIE) • 在在在在在在在在在在在在在在在在 • 在在在在在在在在在在在在在在在 namespace, block

    在在在在 在在在在在在在在在在在在在在在在在 AST node • 在在在在在在在在在在 • 在 DIE 在在在在在在在在在 • CU 在在在在在在在在在在在 DW_TAG_compile_unit 14
  10. llvm-dwarfdump 0x00000040: DW_TAG_compile_unit DW_AT_producer ("clang version 15.0.7") DW_AT_language (DW_LANG_C_plus_plus_14) DW_AT_name

    ("test_dwarf.cc") DW_AT_str_offsets_base (0x00000008) DW_AT_stmt_list (0x0000007e) DW_AT_comp_dir ("/[…]/dwarf-talk") DW_AT_low_pc (0x0000000000000668) DW_AT_high_pc (0x00000000000006a4) DW_AT_addr_base (0x00000008) 15
  11. llvm-dwarfdump 0x0000007a: DW_TAG_namespace DW_AT_name ("test_dwarf") 0x0000007c: DW_TAG_class_type DW_AT_calling_convention (DW_CC_pass_by_value) DW_AT_name

    ("MyCls") DW_AT_byte_size (0x04) DW_AT_decl_file ("/[…]/./test_dwarf.h") DW_AT_decl_line (4) 0x00000082: DW_TAG_member DW_AT_name ("data_") DW_AT_type (0x0000009e "int") DW_AT_decl_file ("/[…]/./test_dwarf.h") DW_AT_decl_line (6) DW_AT_data_member_location (0x00) 16
  12. Line Program • 在在在在在在在在在在在在在在在在在在在在在在在在在 • 在在在在在在在在 DWARF 在在在在 • 在在

    CU 在在在在在在 line program • 在在在在在在在在在在在在在在在在在 line program 在在在在在 在在在在在在 • 在在在在在在在在在在在在在在在在在在在在在在 • 在在 0 在在在在在在在在在在在在在在在在在在在在在在在在 17
  13. llvm-dwarfdump --debug-line include_directories[ 0] = "/[…]/dwarf-talk" include_directories[ 1] = "."

    file_names[ 0]: name: "test_dwarf.cc" dir_index: 0 md5_checksum: 7ae991f9f03b6f91e76554985ffc7a43 file_names[ 1]: name: "test_dwarf.h" dir_index: 1 md5_checksum: bca4b50156de4765525f13bb4dc127af Address Line Column File ISA Discriminator Flags ------------------ ------ ------ ------ --- ------------- ------------- 0x0000000000000668 9 0 0 0 0 is_stmt 0x0000000000000680 10 26 0 0 0 is_stmt prologue_end 0x0000000000000684 10 30 0 0 0 0x0000000000000688 10 28 0 0 0 0x0000000000000694 10 5 0 0 0 0x0000000000000698 11 1 0 0 0 is_stmt 0x00000000000006a4 11 1 0 0 0 is_stmt end_sequence 18
  14. 眼眼 Line Program (V5) 1 #include "test_dwarf.h" 2 3 #include

    <stdio.h> 4 5 namespace test_dwarf 6 { 7 8 void MyCls::callme(int a) 9 { 10 printf("total %d\n", a + data_); 11 } 12 13 } include_directories[ 0] = "/[…]/dwarf-talk" include_directories[ 1] = "." file_names[ 0]: name: "test_dwarf.cc" dir_index: 0 md5_checksum: 7ae991f9f03b6f91e76554985ffc7a43 file_names[ 1]: name: "test_dwarf.h" dir_index: 1 md5_checksum: bca4b50156de4765525f13bb4dc127af Address Line Column File ISA Discriminator Flags ------------------ ------ ------ ------ --- ------------- ------------- 0x0000000000000668 9 0 0 0 0 is_stmt 0x0000000000000680 10 26 0 0 0 is_stmt prologue_end 0x0000000000000684 10 30 0 0 0 0x0000000000000688 10 28 0 0 0 0x0000000000000694 10 5 0 0 0 0x0000000000000698 11 1 0 0 0 is_stmt 0x00000000000006a4 11 1 0 0 0 is_stmt end_sequence 19
  15. pyelftools 眼眼眼眼 : dwarf_decode_address.py def process_file(filename, address): print('Processing file:', filename)

    with open(filename, 'rb') as f: elffile = ELFFile(f) if not elffile.has_dwarf_info(): print(' file has no DWARF info') return # starting point for all DWARF-based processing in pyelftools. dwarfinfo = elffile.get_dwarf_info() funcname = decode_funcname(dwarfinfo, address) file, line = decode_file_line(dwarfinfo, address) 22
  16. pyelftools 眼眼眼眼 : dwarf_decode_address.py def decode_funcname(dwarfinfo, address): for CU in

    dwarfinfo.iter_CUs(): for DIE in CU.iter_DIEs(): try: if DIE.tag == 'DW_TAG_subprogram': lowpc = DIE.attributes['DW_AT_low_pc'].value highpc_attr = DIE.attributes['DW_AT_high_pc'] # […] omitted if lowpc <= address < highpc: return DIE.attributes['DW_AT_name'].value except KeyError: continue return None 23
  17. 眼眼眼眼眼眼眼眼眼眼 • 在在 CU • 在在 tag 在 DW_TAG_subprogram 在

    DIE • 在在在在在 [𝑙𝑜𝑤𝑝𝑐, ℎ𝑖𝑔ℎ𝑝𝑐) 在在在在在在在在在在 • “pc” = Program Counter 24
  18. 眼眼眼眼眼眼眼 眼眼眼眼 DIE for DIE in CU.iter_DIEs(): if DIE.tag ==

    'DW_TAG_subprogram': # … 眼眼眼眼眼眼眼眼 stack = [CU.get_top_DIE()] while stack: DIE = stack.pop() if DIE.tag == 'DW_TAG_subprogram': # … stack.extend(filter(can_nest, DIE.iter_children())) 25
  19. 眼眼眼眼眼眼眼眼眼 def can_nest(die: DIE) -> bool: return die.tag in [

    "DW_TAG_namespace", "DW_TAG_structure_type", "DW_TAG_class_type", "DW_TAG_union_type", "DW_TAG_subprogram", "DW_TAG_inlined_subroutine", "DW_TAG_lexical_block", # DW_TAG_try_block, # DW_TAG_catch_block, ] • DWARF 并不区分 C++ 的成员函数、构造函数、析构函数、等等 26
  20. 眼眼 > python dwarf_decode_address.py 0x0000000000000694 libtest_dwarf.so > /[…]/dwarf_decode_address.py(72)decode_funcname() -> if

    lowpc <= address < highpc: (Pdb) lowpc 1640 (Pdb) highpc 1700 (Pdb) address 1684 (Pdb) n > /[…]/dwarf_decode_address.py(73)decode_funcname() -> return DIE.attributes['DW_AT_name'].value (Pdb) KeyError: 'DW_AT_name' 27
  21. 眼眼眼眼眼眼眼眼眼 0x0000008b: DW_TAG_subprogram DW_AT_linkage_name ("_ZN10test_dwarf5MyCls6callmeEi") DW_AT_name ("callme") DW_AT_decl_file ("/[…]/dwarf-talk/./test_dwarf.h") DW_AT_decl_line

    (9) DW_AT_declaration (true) DW_AT_external (true) DW_AT_accessibility (DW_ACCESS_public) 0x000000a7: DW_TAG_subprogram DW_AT_low_pc (0x0000000000000668) DW_AT_high_pc (0x00000000000006a4) DW_AT_frame_base (DW_OP_reg29 W29) DW_AT_object_pointer (0x000000b9) DW_AT_decl_file ("/[…]/dwarf-talk/test_dwarf.cc") DW_AT_decl_line (8) DW_AT_specification (0x0000008b "_ZN10test_dwarf5MyCls6callmeEi") 28
  22. DW_AT_specification class MyCls { int data_; public: [[gnu::visibility("default")]] void callme(int

    a); }; void MyCls::callme(int a) { printf("total %d\n", a + data_); } DW_TAG_subprogram DW_AT_linkage_name ("_ZN10test_dwarf5MyCls6callmeEi") DW_AT_name ("callme") DW_AT_decl_file ("/[…]/dwarf- talk/./test_dwarf.h") DW_AT_declaration (true) DW_AT_external (true) DW_AT_accessibility (DW_ACCESS_public) DW_TAG_subprogram DW_AT_low_pc (0x0000000000000668) DW_AT_high_pc (0x00000000000006a4) DW_AT_decl_file ("/[…]/dwarf- talk/test_dwarf.cc") DW_AT_specification (0x0000008b) 29
  23. 眼眼眼眼眼眼眼眼眼 • DWARF 在在在在在在在在在在在在在在 • 在在 (declaration) • 在在 (definition)

    • 在在在在 (out-of-line instance) • 在在在在 tag 在 DW_TAG_subprogram 在 DIE • 在在 DW_AT_abstract_origin 在在在在在在在在在在在在在在在在在在 在在在在在在在在在 • 在在 DW_AT_specification 在在在在在在在在在在在在在在 • 在在在在在在在在在在在 DW_AT_declaration 在在在在在在在在在 • 在在 C++ 在在在 DWARF 在在在在在在在在在在在 30
  24. 眼眼眼眼眼眼眼 namespace test_dwarf { class MyCls { int data_ =

    getchar(); public: MyCls() = default; # … test_dwarf.h 在在在在在 defaulted 在在 nontrivial 32
  25. 眼眼眼眼眼眼眼 (gcc) #include "test_dwarf.h #include <stdio.h> extern "C" [[gnu::visibility("default")]] void

    enter(int x) { static test_dwarf::MyCls obj; obj.callme(x); } test_cpplib.cc g++ -std=c++20 \ -shared \ -fvisibility=hidden \ -o libtest_dwarf.so \ -g test_*.cc 33
  26. 眼眼眼眼眼眼眼 0x000016c6: DW_TAG_subprogram DW_AT_external (true) DW_AT_name ("MyCls") DW_AT_decl_file ("/[…]/test_dwarf.h") DW_AT_decl_line

    (12) DW_AT_linkage_name ("_ZN10test_dwarf5MyClsC4Ev") DW_AT_accessibility (DW_ACCESS_public) DW_AT_declaration (true) DW_AT_defaulted (DW_DEFAULTED_in_class) DW_AT_object_pointer (0x000016dc) 0x0000185b: DW_TAG_subprogram DW_AT_specification (0x000016c6 "_ZN10test_dwarf5MyClsC4Ev") DW_AT_object_pointer (0x00001869) DW_AT_inline (DW_INL_declared_not_inlined) 34
  27. 眼眼眼眼眼眼眼眼眼 0x00001873: DW_TAG_subprogram DW_AT_abstract_origin (0x0000185b "_ZN10test_dwarf5MyClsC4Ev") DW_AT_linkage_name ("_ZN10test_dwarf5MyClsC2Ev") DW_AT_object_pointer (0x00001896)

    DW_AT_low_pc (0x0000000000000b14) DW_AT_high_pc (0x0000000000000b3c) DW_AT_frame_base (DW_OP_call_frame_cfa) DW_AT_call_all_tail_calls (true) 0x0000185b: DW_TAG_subprogram DW_AT_specification (0x000016c6 "_ZN10test_dwarf5MyClsC4Ev") DW_AT_object_pointer (0x00001869) DW_AT_inline (DW_INL_declared_not_inlined) 35
  28. 眼眼眼眼眼 • DW_TAG_inlined_subroutine 在在在在在在在在在在 • 在在在 DW_AT_abstract_origin 在在 • 在在

    DIE 在在在在在在在在在在在在在在在在 • 在在在在在在在在在在 • 在在在在在在在在 • 在在在在在在在在在在在在在在在在在在在在在在 [𝑙𝑜𝑤𝑝𝑐, ℎ𝑖𝑔ℎ𝑝𝑐) 在在 在在 DIE 在在在在 36
  29. DW_AT_ranges 0x00000086: DW_TAG_inlined_subroutine DW_AT_abstract_origin (0x00004528 "_ZN10test_dwarf5MyClsC2Ev") DW_AT_ranges (indexed (0x0) rangelist

    = 0x00000053 [0x00000000000009d4, 0x00000000000009f0) [0x00000000000009f8, 0x0000000000000a04) [0x0000000000000a0c, 0x0000000000000a10)) DW_AT_call_file ("/[…]/test_cpplib.cc") DW_AT_call_line (7) DW_AT_call_column (0x1e) • 在在 .debug_rnglists 在 .debug_ranges (pre-V5) 在在在在在在 • API 在在在在在在在在在在在在在在在 • 在在在在在在在在 stateful encoding 37
  30. Lambda 眼眼眼 (gcc) 0x00001774: DW_TAG_structure_type DW_AT_byte_size (0x04) DW_AT_decl_file ("/[…]/dwarf-talk/p3/test_cpplib.cc") DW_AT_decl_line

    (9) 0x00001779: DW_TAG_subprogram DW_AT_name ("~<lambda>") DW_AT_artificial (true) DW_AT_declaration (true) DW_AT_object_pointer (0x00001786) 0x00001791: DW_TAG_subprogram DW_AT_name ("operator()") DW_AT_type (0x00000073 "int") DW_AT_artificial (true) DW_AT_object_pointer (0x000017b9) DW_AT_low_pc (0x0000000000000f28) DW_AT_high_pc (0x0000000000000f4c) 38
  31. DW_AT_artificial • 在在在在在在 DIE 在在在在在在在在在在在在在在在在在在 • 在在在在在在在 flag • GCC

    在在在在在在在在在 operator() 在 • C++ 在在在在在在在 lambda 在在在在在在在在 operator() • 在在在在在在在在在在在在在在在在在 DIE 在在在 lambda 在在在在 • Clang 在在在在在在在在在在在在在在在 llvm/llvm-project#151269 39
  32. 眼眼 • 在在 parent 在在在在在在在在在在在在在在在在在 • 在在 compile units 在

    embarrassingly parallel • 在在 pyelftools 在在在在 • 在在在在在在 ELF 在在在在在在在 41
  33. Name mangling • 在在在 linkage 在 entity, 在在在在 demangle DW_AT_linkage_name

    • 在在在 demangler 在在在 mangled name 在在在 AST • __cxa_demangle 在在在在在在在在在 • llvm::itaniumDemangle • python-itanium_demangler: Pure Python Itanium C++ ABI demangler 42
  34. 眼眼眼眼眼 • 在在在在在 Clang 在在在在在在在在在 DIE 在在在在在在在在 enclosing 在在在 children

    • Clang 在在在 tail merging 在在在在在在在在在在在在在在在在 在在在在在在在 DWARF 在在在在在在 • Clang 在在在 vtable homing 在在在在在在在在在在在在 translation unit 在在在在在在在在在在在在在在 DIE 在在在 compile unit • 在在 Clang 在在 43
  35. 眼眼眼眼 Separable Debug Info 眼眼 ELF • .gnu_debuglink 在在在 在

    .debug 在在在在在在在在 在在在在在在在在在在在 • GNU Build ID (.note.gnu.build-id) 在在在 在在在在在 .debug 在在在在 在在在在在 Mach-O • 在在在在在在在 .dSYM 在在在 在在在在在在在在 UUID (LC_UUID) • .dSYM 在在在在在在在在在在 在在 Contents/Resources/DWARF 44