[The World of Linkers—Lab 12] Make Debug Information Follow Addresses
Add debug sections, symbols, and section headers to class10's static ET_EXEC and class11's static PIE. Preserve the executable layout while making source locations, symbols, and debug references readable by GDB, addr2line, and readelf. Theory 11 explains DWARF coordinates, tombstones, and debug-file organization.
Project: class12 code and tests; private-repository access is required. Retain the complete upstream layout, including merged-piece mappings and the TLS template. The supplied link and link_pie connect the ET_EXEC and static-PIE pipelines; the CLI selects PIE with --pie.
Implementation tasks
Complete five tasks in the class12 crate library. The class12 README specifies input types, field encodings, diagnostics, and composition requirements.
| Task | Interface | Result |
|---|---|---|
| C12.1 | collect_debug | Group .debug_* by name, retaining input order and each contribution's group offset |
| C12.2 | relocate_debug | Patch one field in its target coordinate system, preserving bytes on error |
| C12.3 | encode_symbols | Encode locals-first .symtab, name-sharing .strtab, and first_global |
| C12.4 | attach_sections | Append nonallocated data and section headers; update the ELF section-table fields |
| C12.5 | finish | Resolve debug references, construct output sections and symbols, and integrate the upstream result |
Two constraints govern the implementation. First, debug references use distinct coordinates. Ordinary image values are VAs for ET_EXEC and RVAs for PIE; debug-section references are section offsets; DTPOFF and STT_TLS use TLS module offsets. ABS constants and unresolved WEAK zero do not undergo image-base conversion. A TLS module offset must not become the TP-relative offset used by machine code.
Second, appending metadata must preserve the entry point, program headers, and allocated contents. Only e_shoff, e_shentsize, e_shnum, and e_shstrndx may change in the original file prefix. Added nonallocated contents must not enlarge PT_LOAD. Retained symbols follow the final layout; references to code removed by GC receive tombstones. The README defines their values and the rules for merged pieces and TLS section identities.
Use a numeric check to keep the coordinates separate. If an ET_EXEC symbol has address 0x401120, the same static PIE layout represents it as RVA 0x1120; a debug section may begin at file offset 0x2a0, which is not a code address; a TLS symbol may have module offset 0x38, which is not a TP-relative offset. A relocation written into .debug_* uses the debug section's file-relative coordinate, ordinary image references use VA for ET_EXEC and RVA for PIE, and STT_TLS uses the template offset. Only after startup establishes a thread pointer can that value become a TP-relative address.
The supported subset consists of uncompressed debug sections, ordinary section numbering, and the listed relocations. Concatenate debug strings directly. Reject compressed sections, PC-relative debug relocations, references to other metadata, and outputs requiring extended section numbering with the specified errors.
Separate the image from the appended indexes
finish must not lay out the upstream image again. First borrow final addresses and piece mappings from Lowered, resolve debug relocations, symbols, and tombstones; then append .debug_*, .symtab, .strtab, and the section-header table, changing only the four permitted ELF-header fields. This lets you verify independently that the original PT_LOAD image still runs and that tools can interpret the new indexes.
GDB and addr2line test source locations and symbol addresses; readelf tests section-header and relocation structure. A program that still returns 42 does not prove debug coordinates, and a file that readelf accepts does not prove that appended metadata left the load image unchanged.
Trace each task to its tests
| Task | Key tests | Property established |
|---|---|---|
C12.1 collect_debug | multiple_compilation_units_keep_independent_debug_coordinates | Section-local coordinates remain independent across compilation units. |
C12.2 relocate_debug | symbols_and_line_tables_agree_with_gnu_ld, rejects_unsupported_debug_inputs | Debug fields use the correct coordinate and unsupported encodings are explicit errors. |
C12.3 encode_symbols | symbols_and_line_tables_agree_with_gnu_ld, zero_sized_tls_symbol_keeps_its_nobits_section_identity | Local/global order, string indexes, and TLS/NOBITS identity agree. |
C12.4 attach_sections | attach_appends_data_and_section_table, attach_respects_extended_numbering_boundary | Appending metadata preserves the loaded prefix and encodes section-table boundary rules. |
C12.5 finish | appending_sections_preserves_the_class8_image, cli_output_is_debuggable | The program keeps its original layout while tools can read symbols, lines, and sections. |
GDB, addr2line, and readelf cover different responsibilities; one readable artifact cannot replace the other evidence.
Acceptance
From the repository root, on native x86-64 Linux:
cargo test --locked -p class12cargo test --locked -p class12 --releasepython3 scripts/grade.py class12Acceptance checks three aspects:
- Interface contracts: grouping, alignment, coordinate conversion, failure atomicity, symbol order, and section-header encoding.
- Tool interpretation: multi-compilation-unit breakpoints, source lines, and cross-file backtraces, compared with GNU ld's GDB sessions; live addresses and tombstones checked with addr2line and readelf.
- Composition: preservation of the upstream file prefix, combinations of merged constants, CFI, GOT, and TLS, and CLI/library equivalence.
GDB variable checks cover the capabilities of the supplied startup. Automatic TLS-variable lookup also requires a runtime discovery protocol. The startup does not provide a complete libc/thread_db protocol, so that capability is outside acceptance.