Menu

Searches titles, sections and text. ↑ ↓ to move, Enter to open, Esc to close. Opens with / or Ctrl+K.

    Contact

    DWARF as a chain of trust

    Debug information does not change program behavior. That is convenient for debugging and awkward when a safety verdict rests on it.


    Debug information was invented so that a debugger could show you a source line while you step through machine code. It does that job well, and nothing about that job is safety-critical.

    The moment you use it to project coverage measured on object code back to the source level, it becomes something else: an element of the safety assurance chain. Our own internal notes put it plainly - its quality must be critically questioned, as it is traditionally only used for debugging.

    This article is about what changes when metadata is promoted to evidence.

    DWARF is produced by the toolchain and is therefore an evidence input, not an independent ground truth. Its use requires executable identity checks, supported producer/version knowledge, consistency checks against the object code and explicit treatment of ambiguous or missing ranges. The tested compiler, linker and DWARF versions, relevant options, language and target combinations, and known producer-specific limitations belong in the published support claim - “supports DWARF” is too broad to be a useful product claim.

    Four structural properties

    1. It is functionally silent, so ordinary application tests do not validate it

    Debug information normally does not affect the target program’s functional behavior. A binary with a thoroughly broken line table can pass every requirements-based test campaign you build, including the one that produced your coverage report - so a successful campaign does not, by itself, show that the line table or location information is correct.

    Confidence has to come from dedicated metadata verification instead: defined operational requirements, producer and version test corpora, consistency checks, differential tests, regression tests and - where certification credit is taken - an appropriate qualification or independent-verification argument. The same caution applies to compiler output generally: testing contributes evidence, but does not guarantee that every code-generation defect will be exposed.

    2. The format is defined, but optimized source attribution is not uniquely prescribed

    Ask what line an instruction belongs to after it was hoisted out of a loop and fused with another. Under optimization this is not a hard question; it is an ill-posed one. There is no single right answer, because the instruction genuinely descends from more than one place - or from a place that no longer exists.

    The format says so itself. Section 1.3.12 of the DWARF standard is titled “Permissive Rather Than Prescriptive” and states that it “does not specify in detail what a particular producer must generate for any source to object conversion” and that differences between producers are “differences in quality-of-implementation”. That is the correct engineering decision for a debugger format. DWARF specifies the representation and semantics of its data while deliberately leaving producers latitude in how optimized source constructs are represented. A coverage use therefore needs an additional operational specification: which source obligations must be mapped, which ambiguities are permitted, how inlining, cloning and folding are represented, and what constitutes an unresolved result. That operational specification can be validated and, where needed, qualified.

    Dedicated checks can detect missing ranges, malformed tables, non-monotonic mappings, producer regressions and disagreement with an independent mapping. What they cannot do is make an inherently many-to-many optimized mapping unique. A report should therefore distinguish detected defects, detected ambiguity and residual undetected risk.

    What the latitude looks like in practice - the jumping cursor, the merged location, the line number 0 - is what the debug information actually says.

    3. The trusted computing base is large, and empirically fragile

    The DWARF line table is not a lookup table. It is a bytecode program for a state machine that a consumer has to interpret. That state machine, and the compiler code that emits its program, are in the trust chain of your coverage verdict.

    Two public observations about how well that goes:

    • The LLVM project maintains a dedicated facility for the problem, debugify: a pair of passes, one attaching synthetic debug information to every instruction of a module, the other checking after a transformation that it is still there and reporting what was lost. The facility exists because optimization passes can lose or misrepresent debug metadata and regressions need automated detection; it demonstrates that preservation is testable and taken seriously. How often loss occurs is a separate question, answered only by measured studies - such as the one below.
    • The first systematic study of the question produced 47 confirmed bug reports against GCC and LLVM, every one of them affecting the then-current development version, spread across all optimization levels - including GCC’s -Og, the level whose purpose is a better debugging experience. The numbers, in context.

    And the emission changes with each compiler release. Your evidence base moves with every toolchain update, silently.

    4. Degradation is silent

    Take the three properties above together and the consequence is the interesting one: the dedicated checks from property 2 narrow the problem, but they do not close it. Detected defects and detected ambiguity can be reported; what remains is residual undetected risk - and inside that remainder there is no error state to detect, no assertion that fires, no anomaly in the report. A coverage report built on subtly wrong debug information looks exactly like a correct one.

    Compare two chains

    The comparison that matters is not “which one is true”. Both chains rest on compiler output. The difference is what happens when a link is wrong.

    Verdict via debug informationVerdict via symbol table and anchors
    Is the artifact functionally load-bearing?no - metadata onlyyes - the linker consumes the same symbols to build the binary
    Is the attribution criterion uniquely defined?not under optimization - an operational specification must supply itexplicit and address-resolvable for each validated anchor-manifest entry; the source-obligation association still requires validation
    Size of the trusted computing basea bytecode interpreter plus the emitting passesread a table, disassemble at an address
    Failure modesilentmissing, duplicate or unexpectedly resolved symbols are directly detectable; wrong placement or manifest association can stay silent unless independently checked

    The honest caveat belongs here too: a symbol table is not axiomatically true either. The difference is not truth against falsehood. It is checkable with a small trusted base and detectable degradation against a mapping that ordinary application tests do not validate, whose optimized attribution is not uniquely prescribed, and whose residual degradation is silent.

    The same symmetry applies to anchors: post-link checks can detect missing, duplicate or unexpectedly resolved symbols, but the method still depends on compiler and linker behavior, manifest correctness, anchor placement, and validation of the intended source obligation.

    compiler emits line and column info
    compiler emits line and column info
    linker resolves anchor symbols
    linker resolves anchor symbols
    tool maps addresses to source lines
    tool maps addresses to source lines
    anchor execution appears in the trace
    anchor execution appears in the trace
    source-level coverage verdict
    source-level coverage verdict
    source-level coverage verdict
    source-level coverage verdict
    If a link is wrong
    program behavior is unchanged
    no test can detect it: silent
    If a link is wrong...
    If a link is wrong
    symbol missing or collided
    visible in the symbol table: detected
    If a link is wrong...
    Verdict via debug information
    metadata about the program
    Verdict via debug information...
    Verdict via symbol table
    no test can detect it: silent
    Verdict via symbol table...
    The claim is not that one is true and the other false. It is that one failure mode is detectable and the other is not.

    Why this is not an optimization-level argument

    The mapping problem described in the previous article gets worse as you optimize. The assurance question, however, remains at every optimization level - ambiguity is generally greater under optimization, but even at -O0 the qualification question stands: how would you demonstrate, to an assessor, that the debug information used to produce your source-level verdict is correct?

    The available answers are not satisfying:

    • “The compiler is widely used.” True, and it applies to code generation, which is verified through the product. It does not transfer to metadata that the product does not exercise.
    • “We spot-checked it.” Sampling finds systematic errors, not the individual wrong attribution that moved one line from uncovered to covered.
    • “The debugger shows the right lines.” At -O0. That is the regime where the mapping problem is smallest.

    What to take from this

    If your coverage evidence is produced at source level on an unoptimized build, this chain is short and the risk is low.

    If it is produced by mapping object-level measurements back to source through debug information on an optimized build, then the debug information is part of your argument, and it is worth asking your tool vendor - and yourself - how that part is verified.

    The question is not rhetorical, and it is not aimed at any particular product. It applies to every tool in the class, ours included, which is why we would rather write it down than wait to be asked.

    Sources

    The factual statements in this article that rely on external sources are supported by four publicly available documents, listed below for reference and verification:

    Where a statement rests on our own reasoning or our internal engineering notes, it says so at that statement.

    One step to take: write down the compiler, version and options behind your current coverage report, and ask your tool vendor which of those producer combinations are validated for source attribution - and how missing or ambiguous ranges appear in the report.


    Related: why the mapping breaks under optimization