$npx -y skills add mohitmishra786/low-level-dev-skills --skill dwarf-debug-formatDWARF debug format skill for understanding debug information. Use when inspecting DWARF sections with dwarfdump, working with split DWARF (.dwo files), setting up debuginfod for remote symbol resolution, or understanding how DWARF interacts with LTO and symbol stripping. Activate
| 1 | # DWARF Debug Format |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | Guide agents through understanding and working with DWARF debug information: the key DWARF sections, using `dwarfdump` and `readelf` for inspection, split DWARF (`.dwo` files), `debuginfod` for remote symbol servers, and how LTO and stripping affect debug info. |
| 6 | |
| 7 | ## Triggers |
| 8 | |
| 9 | - "What are the DWARF sections in an ELF binary?" |
| 10 | - "How do I inspect DWARF debug info in a binary?" |
| 11 | - "How does split DWARF work?" |
| 12 | - "How do I set up debuginfod for automatic debug symbols?" |
| 13 | - "Why does GDB say 'no debugging symbols found'?" |
| 14 | - "How does LTO affect DWARF debug information?" |
| 15 | |
| 16 | ## Workflow |
| 17 | |
| 18 | ### 1. DWARF sections overview |
| 19 | |
| 20 | ```bash |
| 21 | # Show all sections in a binary (including DWARF) |
| 22 | readelf -S prog | grep "\.debug" |
| 23 | |
| 24 | # Common DWARF sections: |
| 25 | # .debug_info — types, variables, functions (DIEs — Debug Info Entries) |
| 26 | # .debug_abbrev — abbreviation table for .debug_info encoding |
| 27 | # .debug_line — line number table (source line → address mapping) |
| 28 | # .debug_str — string table for identifiers |
| 29 | # .debug_loc — location expressions (where variables live) |
| 30 | # .debug_ranges — non-contiguous address ranges for CUs |
| 31 | # .debug_aranges — fast lookup: address → compilation unit |
| 32 | # .debug_pubnames — global variable and function names |
| 33 | # .debug_frame — call frame information (for unwinding) |
| 34 | # .debug_types — type unit entries (DWARF 4+) |
| 35 | # .debug_rnglists — range lists (DWARF 5) |
| 36 | # .debug_loclists — location lists (DWARF 5) |
| 37 | # .debug_addr — address table (DWARF 5) |
| 38 | # .debug_line_str — line number string table (DWARF 5) |
| 39 | ``` |
| 40 | |
| 41 | ### 2. Inspecting DWARF with dwarfdump |
| 42 | |
| 43 | ```bash |
| 44 | # Install dwarfdump |
| 45 | apt-get install dwarfdump # Ubuntu (part of libdwarf) |
| 46 | brew install libdwarf # macOS |
| 47 | |
| 48 | # Dump all DWARF info |
| 49 | dwarfdump prog |
| 50 | |
| 51 | # Dump specific sections |
| 52 | dwarfdump --debug-info prog # types, variables, functions |
| 53 | dwarfdump --debug-line prog # line number table |
| 54 | dwarfdump --debug-loc prog # variable locations |
| 55 | dwarfdump --debug-frame prog # call frame info |
| 56 | |
| 57 | # readelf alternatives (less verbose) |
| 58 | readelf --debug-dump=info prog | head -100 |
| 59 | readelf --debug-dump=lines prog | head -50 |
| 60 | |
| 61 | # llvm-dwarfdump (more readable output) |
| 62 | llvm-dwarfdump prog |
| 63 | llvm-dwarfdump --debug-info prog | grep "DW_AT_name" |
| 64 | llvm-dwarfdump --statistics prog # summarize DWARF sizes |
| 65 | ``` |
| 66 | |
| 67 | ### 3. Reading DWARF DIE structure |
| 68 | |
| 69 | ```text |
| 70 | # Sample dwarfdump output for a variable |
| 71 | DW_TAG_compile_unit |
| 72 | DW_AT_producer : "GNU C17 13.2.0" |
| 73 | DW_AT_language : DW_LANG_C11 |
| 74 | DW_AT_name : "main.c" |
| 75 | DW_AT_comp_dir : "/home/user/project" |
| 76 | |
| 77 | DW_TAG_subprogram |
| 78 | DW_AT_name : "add" |
| 79 | DW_AT_type : <0x42> → points to int type DIE |
| 80 | DW_AT_low_pc : 0x401130 ← function start address |
| 81 | DW_AT_high_pc : 0x401150 ← function end address |
| 82 | |
| 83 | DW_TAG_formal_parameter |
| 84 | DW_AT_name : "a" |
| 85 | DW_AT_type : <0x42> |
| 86 | DW_AT_location : DW_OP_reg5 (rdi) ← lives in register rdi |
| 87 | |
| 88 | DW_TAG_formal_parameter |
| 89 | DW_AT_name : "b" |
| 90 | DW_AT_location : DW_OP_reg4 (rsi) ← lives in register rsi |
| 91 | ``` |
| 92 | |
| 93 | Tags (DW_TAG_*): `compile_unit`, `subprogram`, `variable`, `formal_parameter`, `typedef`, `structure_type`, `member`, `array_type`, `pointer_type`, `base_type` |
| 94 | |
| 95 | Attributes (DW_AT_*): `name`, `type`, `location`, `low_pc`, `high_pc`, `byte_size`, `encoding`, `file`, `line` |
| 96 | |
| 97 | ### 4. Split DWARF (.dwo files) |
| 98 | |
| 99 | Split DWARF separates debug info from the linked binary into `.dwo` sidecar files, reducing linker input size: |
| 100 | |
| 101 | ```bash |
| 102 | # Compile with split DWARF |
| 103 | gcc -g -gsplit-dwarf -O2 -c main.c -o main.o |
| 104 | # Creates: main.o (object without full DWARF) + main.dwo (debug info) |
| 105 | |
| 106 | # Link (linker only processes small .o files, not large DWARF) |
| 107 | gcc main.o -o prog |
| 108 | # prog references main.dwo but doesn't embed it |
| 109 | |
| 110 | # GDB finds .dwo files via DW_AT_GNU_dwo_name attribute in prog |
| 111 | gdb prog # works if main.dwo is in the same directory |
| 112 | |
| 113 | # Inspect the split reference |
| 114 | readelf -S prog | grep "\.gnu_debuglink\|dwo" |
| 115 | dwarfdump prog | grep "dwo_name" # shows path to .dwo files |
| 116 | |
| 117 | # Package .dwo files into a single .dwp for distribution |
| 118 | dwp -o prog.dwp prog # GNU dwp tool |
| 119 | llvm-dwp -o prog.dwp prog # LLVM version |
| 120 | # With .dwp next to prog, GDB finds all debug info |
| 121 | ``` |
| 122 | |
| 123 | ### 5. debuginfod — remote debug symbol server |
| 124 | |
| 125 | `debuginfod` serves debug symbols, source code, and executables via HTTP: |
| 126 | |
| 127 | ```bash |
| 128 | # Client: configure to use a debuginfod server |
| 129 | export DEBUGINFOD_URLS="https://debuginfod.elfutils.org/" |
| 130 | |
| 131 | # GDB uses it automatically when symbols are missing |
| 132 | gdb /usr/bin/git |
| 133 | # GDB will fetch debug sym |