Skip to content

On PE/COFF a header-only dependency's exported template instances stay in contract (2026-10-26)

Index: Known gaps

Status (2026-10-26): OPEN. A limit of ADR-089's 2026-10-25 amendment.

workflows/dump/dependency_internals.py moves a header-only dependency's functions and variables out of contract only when the library's binary does not define them as its own. The binary's export table supplies that answer:

Format Library's own Coalesced copy (not the library's)
ELF GLOBAL binding WEAK, GNU_UNIQUE
Mach-O an export without the weak-definition flag N_WEAK_DEF / EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION
PE/COFF every export none recorded

A linked DLL keeps no COMDAT evidence. IMAGE_COMDAT_SELECT_* is an auxiliary record of an object file's section symbol, and the linker resolves it. A .drectve -export: directive is also object-file only. The export directory holds a name, an ordinal and an optional forwarder per row (PeExport), and every row is a plain RVA into a merged section. mingw's auto-export (--export-all-symbols, the default with no dllexport) lists the dependency's template instances and hidden friends next to the library's own functions. Nothing in the DLL tells them apart.

false-positive — On Windows, the -mavx2 against -mavx512f rebuild of tests/field_shapes/header_only_dependency_internals reports the dependency's hidden_friend_*, func_added/func_removed and exported_not_public findings in contract, and the verdict is BREAKING instead of COMPATIBLE_WITH_RISK. That shape runs on linux and darwin only for this reason. Records, typedefs and an inline variable the DLL does not export are still classified on PE, because they do not depend on the export table.

Not done, deliberately: guessing coalescing from the name (a template argument list, a dependency namespace). An explicit instantiation or an out-of-line function the library really exports would read as a dependency copy, and the finding would leave the contract on no evidence.

Closing it needs build evidence: the library's object files (L3) read for COFF COMDAT sections, the way buildsource/comdat_groups.py reads ELF SHT_GROUP. That module states the PE/COFF reader as a separate extractor. Feed its symbols in as the coalesced set when a dump has them.