ELF inspection
An ELF binary reports its own structure. Every question about dependencies, symbols, sections, or hardening is one readelf, objdump, nm, or ldd query away. This skill is read-only; transformation of binaries belongs to binutils.
Contract
| Field |
Bound contract |
| Trigger |
The task inspects what a binary depends on, why it is large, which symbols it exports or needs, whether it is PIE or RELRO-hardened, or why a symbol is undefined at link time or load time. |
| Authority |
Read-only. The commands read the named binaries and print to stdout; no file is written, so no rollback applies. No remote mutation. |
| Side effect |
None. Output goes to the chat or the terminal. |
| Done |
The question about the binary is answered with the matching tool output quoted, and every quoted fact comes from the binary itself. |
Inputs
- The binary or library to inspect: required.
- The question: required. Dependency, symbol, size, hardening, relocation, or build identity.
- A symbol name or address: optional, narrows the query.
Procedure
- Classify the file.
file reports architecture, linkage, and stripped state; size reports text, data, and bss. Done when: the type and the stripped state are known, because they select the tools for later steps.
file prog
size --format=sysv prog
- List dynamic dependencies with
ldd. A not found row names the deployment gap. Done when: every DT_NEEDED entry resolves, or the missing one is named.
ldd ./prog
ldd -v ./prog # include version requirements
ldd executes the loader against the binary. Never run it on an untrusted binary; use readelf -d for those.
- Query symbols with
nm. -D reads the dynamic table, -C demangles, -u lists what the binary needs. Done when: the symbol is found with its type, or its absence is proven.
nm -D ./libfoo.so # exported dynamic symbols
nm -C prog # demangled
nm -u prog # undefined symbols
nm -S --defined-only prog # with sizes
Type codes: T/t code, D/d initialized data, B/b bss, R/r read-only data, upper for global and lower for local, U undefined, W/w weak, V weak object.
- Read structure with
readelf. It needs no execution and parses every ELF. Done when: the requested section, segment, or table is printed.
readelf -h prog # header: class, machine, type, entry
readelf -S prog # sections
readelf -l prog # program headers, segments
readelf -d prog # dynamic section, raw form of ldd
readelf -s prog # symbol tables
readelf -r prog # relocations
readelf -n prog # notes, build ID
readelf --debug-dump=info prog # DWARF
- Disassemble with
objdump. -S interleaves source when the binary carries -g. Done when: the code around the address or symbol is shown in the requested syntax.
objdump -d -M intel prog
objdump -d -S prog
objdump -s -j .rodata prog # hex dump of one section
objdump -p prog # private headers, DT_NEEDED entries
- Check hardening state. PIE means
ET_DYN on an executable; full RELRO needs GNU_RELRO plus BIND_NOW; a non-executable stack means GNU_STACK flags RW, not RWE. Done when: each property is reported present or absent from the binary's own headers.
readelf -h prog | grep 'Type:'
readelf -l prog | grep GNU_RELRO
readelf -d prog | grep BIND_NOW
readelf -l prog | grep GNU_STACK
nm prog | grep __stack_chk_fail # stack protector
checksec --file=prog runs the same checks in one call and is a separate install.
- Analyze size. Rank symbols by size, then rank sections. For per-object contribution, rebuild the link with
-Wl,--print-map or run bloaty, a separate install. Done when: the largest contributors are named with numbers.
size --format=sysv prog | sort -k2 -nr | head
nm -S --defined-only prog | sort -k2 -nr | head -20
- Read the build ID. It identifies the exact build for
debuginfod lookups and pairs the binary with its .debug file. Done when: the ID is quoted.
readelf -n prog | grep 'Build ID'
- Run the diagnosis flows. Done when: the reported error traces to its cause in the binary.
- Undefined symbol at load time:
nm -D libfoo.so | grep mysymbol to see whether the expected provider exports it, then ldd ./prog | grep libfoo to see whether the loader found that provider.
- Binary too large: steps 1 and 7, then decide between stripping debug info (
binutils), removing sections, or restructuring data.
- Unexpected dependency:
readelf -d prog | grep NEEDED, then trace who pulls it in with the link map.
Failure and recovery
| Failure class |
Behavior |
nm prints no symbols |
The binary is stripped. Inspect the dynamic table with nm -D, or point the tools at the .debug file or unstripped build. |
ldd reports not a dynamic executable |
The binary is static or for another architecture. Confirm with file, and use the triplet-prefixed tools for foreign objects. |
| Section names absent |
The binary may be stripped of the section header table. Read it through program headers with readelf -l. |
| Two builds disagree |
Compare build IDs from readelf -n before comparing anything else; a mismatch means the inputs differ. |
Output
A quoted-answer report: the tool command, its relevant output lines, and the conclusion drawn from them. The command table, symbol type list, and section map are in references/cheatsheet.md.
1---2name: elf-inspection3description: Use when examining ELF binaries with readelf, objdump, nm, or ldd: dependencies, symbols, sections, relocations, build IDs, or hardening. Not for modifying binaries: use binutils.4---56# ELF inspection78An ELF binary reports its own structure. Every question about dependencies, symbols, sections, or hardening is one `readelf`, `objdump`, `nm`, or `ldd` query away. This skill is read-only; transformation of binaries belongs to `binutils`.910## Contract1112| Field | Bound contract |13|---|---|14| Trigger | The task inspects what a binary depends on, why it is large, which symbols it exports or needs, whether it is PIE or RELRO-hardened, or why a symbol is undefined at link time or load time. |15| Authority | Read-only. The commands read the named binaries and print to stdout; no file is written, so no rollback applies. No remote mutation. |16| Side effect | None. Output goes to the chat or the terminal. |17| Done | The question about the binary is answered with the matching tool output quoted, and every quoted fact comes from the binary itself. |1819## Inputs2021- The binary or library to inspect: required.22- The question: required. Dependency, symbol, size, hardening, relocation, or build identity.23- A symbol name or address: optional, narrows the query.2425## Procedure26271. Classify the file. `file` reports architecture, linkage, and stripped state; `size` reports text, data, and bss. Done when: the type and the stripped state are known, because they select the tools for later steps.2829```bash30file prog31size --format=sysv prog32```33342. List dynamic dependencies with `ldd`. A `not found` row names the deployment gap. Done when: every `DT_NEEDED` entry resolves, or the missing one is named.3536```bash37ldd ./prog38ldd -v ./prog # include version requirements39```4041`ldd` executes the loader against the binary. Never run it on an untrusted binary; use `readelf -d` for those.42433. Query symbols with `nm`. `-D` reads the dynamic table, `-C` demangles, `-u` lists what the binary needs. Done when: the symbol is found with its type, or its absence is proven.4445```bash46nm -D ./libfoo.so # exported dynamic symbols47nm -C prog # demangled48nm -u prog # undefined symbols49nm -S --defined-only prog # with sizes50```5152Type codes: `T`/`t` code, `D`/`d` initialized data, `B`/`b` bss, `R`/`r` read-only data, upper for global and lower for local, `U` undefined, `W`/`w` weak, `V` weak object.53544. Read structure with `readelf`. It needs no execution and parses every ELF. Done when: the requested section, segment, or table is printed.5556```bash57readelf -h prog # header: class, machine, type, entry58readelf -S prog # sections59readelf -l prog # program headers, segments60readelf -d prog # dynamic section, raw form of ldd61readelf -s prog # symbol tables62readelf -r prog # relocations63readelf -n prog # notes, build ID64readelf --debug-dump=info prog # DWARF65```66675. Disassemble with `objdump`. `-S` interleaves source when the binary carries `-g`. Done when: the code around the address or symbol is shown in the requested syntax.6869```bash70objdump -d -M intel prog71objdump -d -S prog72objdump -s -j .rodata prog # hex dump of one section73objdump -p prog # private headers, DT_NEEDED entries74```75766. Check hardening state. PIE means `ET_DYN` on an executable; full RELRO needs `GNU_RELRO` plus `BIND_NOW`; a non-executable stack means `GNU_STACK` flags `RW`, not `RWE`. Done when: each property is reported present or absent from the binary's own headers.7778```bash79readelf -h prog | grep 'Type:'80readelf -l prog | grep GNU_RELRO81readelf -d prog | grep BIND_NOW82readelf -l prog | grep GNU_STACK83nm prog | grep __stack_chk_fail # stack protector84```8586`checksec --file=prog` runs the same checks in one call and is a separate install.87887. Analyze size. Rank symbols by size, then rank sections. For per-object contribution, rebuild the link with `-Wl,--print-map` or run `bloaty`, a separate install. Done when: the largest contributors are named with numbers.8990```bash91size --format=sysv prog | sort -k2 -nr | head92nm -S --defined-only prog | sort -k2 -nr | head -2093```94958. Read the build ID. It identifies the exact build for `debuginfod` lookups and pairs the binary with its `.debug` file. Done when: the ID is quoted.9697```bash98readelf -n prog | grep 'Build ID'99```1001019. Run the diagnosis flows. Done when: the reported error traces to its cause in the binary.102103- Undefined symbol at load time: `nm -D libfoo.so | grep mysymbol` to see whether the expected provider exports it, then `ldd ./prog | grep libfoo` to see whether the loader found that provider.104- Binary too large: steps 1 and 7, then decide between stripping debug info (`binutils`), removing sections, or restructuring data.105- Unexpected dependency: `readelf -d prog | grep NEEDED`, then trace who pulls it in with the link map.106107## Failure and recovery108109| Failure class | Behavior |110|---|---|111| `nm` prints `no symbols` | The binary is stripped. Inspect the dynamic table with `nm -D`, or point the tools at the `.debug` file or unstripped build. |112| `ldd` reports `not a dynamic executable` | The binary is static or for another architecture. Confirm with `file`, and use the triplet-prefixed tools for foreign objects. |113| Section names absent | The binary may be stripped of the section header table. Read it through program headers with `readelf -l`. |114| Two builds disagree | Compare build IDs from `readelf -n` before comparing anything else; a mismatch means the inputs differ. |115116## Output117118A quoted-answer report: the tool command, its relevant output lines, and the conclusion drawn from them. The command table, symbol type list, and section map are in `references/cheatsheet.md`.