DWARF debug format
Contract
| Field | Bound contract |
|---|---|
| Trigger | DWARF sections in an ELF binary need listing or reading, .dwo split files need producing or packaging, debuginfod needs configuring, or LTO and stripping change what debug info survives. |
| Authority | Read-only. Emits analysis and commands for the operator to run on the target; no file writes, no rollback needed. No remote mutation. |
| Side effect | Inspection commands and a verdict in chat. Nothing is written. |
| Done | The debug info question is answered from the binary's own sections, or the missing piece (.dwo, debug package, build ID) is named. |
Inputs
- Binary or object file (required): the ELF file whose debug info is in question.
- Toolchain (optional):
readelfandllvm-dwarfdumpcover most queries;dwarfdumpandeu-stripcome from libdwarf and elfutils packages. - Build ID (optional): needed for debuginfod lookups.
Procedure
List the DWARF sections.
readelf -S prog | grep "\.debug"Section Contents .debug_infoDIEs: types, variables, functions .debug_abbrevAbbreviation table for .debug_info.debug_lineSource line to address mapping .debug_strIdentifier strings .debug_loc/.debug_loclistsVariable location expressions (DWARF 4 / DWARF 5) .debug_ranges/.debug_rnglistsNon-contiguous address ranges (DWARF 4 / DWARF 5) .debug_arangesAddress to compilation unit lookup .debug_pubnames/.debug_namesGlobal name index (DWARF 4 / DWARF 5) .debug_frameDWARF call frame information; .eh_frameis the runtime unwinding variant.debug_addrAddress table (DWARF 5) .debug_line_strLine-table strings (DWARF 5) Done when: the present sections are listed and named.
Inspect the contents.
readelf --debug-dump=info prog # DIEs readelf --debug-dump=lines prog # line table llvm-dwarfdump --debug-info prog # more readable DIE dump llvm-dwarfdump --statistics prog # debug info size and quality metrics dwarfdump prog # full dump, when libdwarf's dwarfdump is installedDone when: the target section's contents are on screen.
Read the DIE structure. Debug info is a tree of Debug Information Entries. Each DIE has a tag (
DW_TAG_*) and attributes (DW_AT_*).DW_TAG_compile_unit DW_AT_producer : "GNU C17 13.2.0" DW_AT_name : "main.c" DW_AT_comp_dir : "/home/user/project" DW_TAG_subprogram DW_AT_name : "add" DW_AT_low_pc : 0x401130 # function start DW_AT_high_pc : 0x401150 # function end DW_TAG_formal_parameter DW_AT_name : "a" DW_AT_location : DW_OP_reg5 # x86-64 register rdiCommon tags:
compile_unit,subprogram,variable,formal_parameter,typedef,structure_type,member,array_type,pointer_type,base_type. Common attributes:name,type,location,low_pc,high_pc,byte_size,encoding,file,line. Done when: the DIE of interest is located and its attributes are read.Work with split DWARF.
-gsplit-dwarfwrites debug info to.dwosidecars so the linker never processes it.gcc -g -gsplit-dwarf -O2 -c main.c -o main.o # main.o plus main.dwo gcc main.o -o prog # prog references main.dwo dwarfdump prog | grep dwo_name # DW_AT_GNU_dwo_name holds the path dwp -o prog.dwp prog # GNU: pack .dwo files into one .dwp llvm-dwp -o prog.dwp prog # LLVM equivalentGDB resolves
.dwoand.dwpfiles placed next to the binary. Done when: the binary links without debug input and GDB still resolves symbols.Configure debuginfod for remote symbols.
export DEBUGINFOD_URLS="https://debuginfod.elfutils.org/" gdb /usr/bin/git # fetches missing debug info over HTTP debuginfod-find debuginfo <build-id-or-path> debuginfod-find source <build-id> /path/to/source.c(gdb) set debuginfod enabled on (gdb) set debuginfod verbose 1Run a private server with
debuginfod -d /var/cache/debuginfod -p 8002 /path/to/binaries/and pointDEBUGINFOD_URLSathttp://localhost:8002. Done when: GDB fetches symbols for a stripped system binary.Judge LTO's effect.
-fltogenerates DWARF after link-time optimization, so merged, inlined, or eliminated entities lose their debug entries.-flto=thin(Clang) keeps more. For maximum debug info, build a separate-Og -gbinary without LTO. In Rust, the dev profile already defaults tolto = "off"; enablingltoin a release profile trades debug detail for optimization. Done when: the LTO/debug tradeoff is stated for the build in question.Strip binaries while keeping symbols.
objcopy --only-keep-debug prog prog.debug strip --strip-debug prog objcopy --add-gnu-debuglink=prog.debug prog # GDB finds prog.debug automatically eu-strip -f prog.debug prog # elfutils: split in one step readelf -n prog | grep -i debug # verify the link llvm-dwarfdump --statistics prog # check what debug info remains size --format=SysV prog # section sizesDone when: the stripped binary resolves symbols through its debug link or a symbol store.
Failure and recovery
readelf -Sshows no.debug_*sections: the binary was built without-gor was stripped. Rebuild with-gor locate the matching debug file by build ID..dwofiles not found: they must sit next to the objects or be packed into a.dwp. CheckDW_AT_GNU_dwo_namefor the expected path.- debuginfod fetch fails: confirm
DEBUGINFOD_URLSis set in the environment GDB inherits, and that the distro runs a server for that package. - LTO build loses the variable being chased: rebuild that translation unit without
-flto, or debug the-Ogbuild instead. - DWARF version mismatch: older tools cannot parse DWARF 5 sections. Use a current
readelforllvm-dwarfdump, or rebuild with-gdwarf-4.
Output
An answer grounded in the binary's own sections: the DIE or line-table entry found, the .dwo/debuginfod path configured, or the named reason the debug info is absent.