dwarf-debug-format · git:20260905.fd53e99 · 2026-09-05 · sha256 7928f58017948b2a
dwarf-debug-format git:20260905.fd53e99A
Immutable. This exact content is served forever at /api/v1/blob/7928f58017948b2a.
---
name: dwarf-debug-format
description: 'Use when inspecting .debug_info or .debug_line sections with dwarfdump or readelf, working with split-DWARF .dwo files, setting up debuginfod, or checking how LTO and stripping affect debug info.'
---
# 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
1. Binary or object file (required): the ELF file whose debug info is in question.
2. Toolchain (optional): `readelf` and `llvm-dwarfdump` cover most queries; `dwarfdump` and `eu-strip` come from libdwarf and elfutils packages.
3. Build ID (optional): needed for debuginfod lookups.
## Procedure
1. List the DWARF sections.
```bash
readelf -S prog | grep "\.debug"
```
| Section | Contents |
|---|---|
| `.debug_info` | DIEs: types, variables, functions |
| `.debug_abbrev` | Abbreviation table for `.debug_info` |
| `.debug_line` | Source line to address mapping |
| `.debug_str` | Identifier strings |
| `.debug_loc` / `.debug_loclists` | Variable location expressions (DWARF 4 / DWARF 5) |
| `.debug_ranges` / `.debug_rnglists` | Non-contiguous address ranges (DWARF 4 / DWARF 5) |
| `.debug_aranges` | Address to compilation unit lookup |
| `.debug_pubnames` / `.debug_names` | Global name index (DWARF 4 / DWARF 5) |
| `.debug_frame` | DWARF call frame information; `.eh_frame` is the runtime unwinding variant |
| `.debug_addr` | Address table (DWARF 5) |
| `.debug_line_str` | Line-table strings (DWARF 5) |
Done when: the present sections are listed and named.
2. Inspect the contents.
```bash
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 installed
```
Done when: the target section's contents are on screen.
3. Read the DIE structure. Debug info is a tree of Debug Information Entries. Each DIE has a tag (`DW_TAG_*`) and attributes (`DW_AT_*`).
```text
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 rdi
```
Common 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.
4. Work with split DWARF. `-gsplit-dwarf` writes debug info to `.dwo` sidecars so the linker never processes it.
```bash
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 equivalent
```
GDB resolves `.dwo` and `.dwp` files placed next to the binary. Done when: the binary links without debug input and GDB still resolves symbols.
5. Configure debuginfod for remote symbols.
```bash
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
(gdb) set debuginfod enabled on
(gdb) set debuginfod verbose 1
```
Run a private server with `debuginfod -d /var/cache/debuginfod -p 8002 /path/to/binaries/` and point `DEBUGINFOD_URLS` at `http://localhost:8002`. Done when: GDB fetches symbols for a stripped system binary.
6. Judge LTO's effect. `-flto` generates 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 -g` binary without LTO. In Rust, the dev profile already defaults to `lto = "off"`; enabling `lto` in a release profile trades debug detail for optimization. Done when: the LTO/debug tradeoff is stated for the build in question.
7. Strip binaries while keeping symbols.
```bash
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 sizes
```
Done when: the stripped binary resolves symbols through its debug link or a symbol store.
## Failure and recovery
- `readelf -S` shows no `.debug_*` sections: the binary was built without `-g` or was stripped. Rebuild with `-g` or locate the matching debug file by build ID.
- `.dwo` files not found: they must sit next to the objects or be packed into a `.dwp`. Check `DW_AT_GNU_dwo_name` for the expected path.
- debuginfod fetch fails: confirm `DEBUGINFOD_URLS` is 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 `-Og` build instead.
- DWARF version mismatch: older tools cannot parse DWARF 5 sections. Use a current `readelf` or `llvm-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.