Exhibit · article form
One hello.c, two builds: what -g -O0 and -O2 (stripped) really differ by
Captured from the side-by-side story of the two builds — the 17,024-byte debug (-g -O0) vs the 14,472-byte release (-O2, stripped) — 9 steps.
One hello.c, two builds, side by side. The left pane is the debug build — -g -O0, 17,024 bytes. The right is the release build — -O2, stripped, 14,472 bytes. Same six-line source, same output, 2,552 bytes apart. Scroll to walk the differences — every number is in the bytes on screen.
Start at the inventory. The debug file carries 37 sections; the release file 29. Highlighted is the section header table — the file's own table of contents. It sits at offset 0x3940 in debug but 0x3148 in release: everything after the stripped sections slides down. And release's 29 are a strict subset of debug's 37 — stripping only removes; it invents nothing.
.symtab — 864 bytes, 36 symbols: the name table that lets a debugger say main instead of 0x1139. Select it and the release pane comes up empty — it exists only in debug; that is the note above the panes talking. strip deleted exactly this.
Right behind it, .strtab — 486 bytes of the actual name strings .symtab points into. Also debug-only. Together the symbol machinery costs 1,350 bytes — and buys every human-readable name a debugger shows you.
The DWARF group: six .debug_* sections, 594 bytes in all, only in the debug build. Highlighted is .debug_line — 86 bytes mapping instructions back to hello.c lines. This is why a debugger can step through source. The release build cut the wire.
Now something identical in both panes: .dynsym — 168 bytes, 7 dynamic symbols — with .dynstr at 141 bytes. What the dynamic linker needs at runtime survives stripping untouched. Strip removes what humans read, never what the loader reads.
The twist. Debug .text: 275 bytes. Release .text: 281 bytes — the optimized build's code is 6 bytes bigger, and the entry point moves from 0x1040 to 0x1060. -O2 is not a compressor; it trades bytes for speed where it likes. The savings live elsewhere.
So where did 2,552 bytes go? 1,944 in stripped sections — 864 + 486 of symbols, 594 of DWARF. 512 in the section header table itself — eight fewer entries at 64 bytes each. The remainder is the shrunken section-name table and alignment slack. Nothing else moved: both files load with the same four PT_LOAD segments.
Same program, same segments, same hello, world. One strip apart: 17,024 → 14,472 bytes — names and line-maps gone, loader untouched. Now poke at it yourself: click any section in either pane, or open the free-explore room from the link under the title.