Skip to content

fix(emit-lib): weak aether_lib_meta so N lib TUs link with no dup flags - #1591

Merged
paul-hammant merged 3 commits into
mainfrom
fix/emit-lib-meta-weak
Aug 15, 2026
Merged

fix(emit-lib): weak aether_lib_meta so N lib TUs link with no dup flags#1591
paul-hammant merged 3 commits into
mainfrom
fix/emit-lib-meta-weak

Conversation

@paul-hammant

Copy link
Copy Markdown
Collaborator

Closes #1590.

Every --emit=lib TU carries the aether_lib_meta() reflection catalog
(issue #403) with external linkage, so the 0.539 multi-TU link model —
one --emit=lib C per module, one final link, no
-Wl,--allow-multiple-definition — died with N-1
multiple definition of `aether_lib_meta' errors as soon as an
orchestrator linked two lib TUs (observed on avn: aether 0.539.0 + aeb
009c830, every ≥2-module binary). The existing multi_tu_import_link
regression used plain emission, which carries no catalog, so it passed
right through the hole.

Fix (option 1 from the issue)

The catalog entry point is now emitted weak:

#if defined(__GNUC__) || defined(__clang__)
#  define AETHER_LIB_META_WEAK __attribute__((weak))
...
AETHER_LIB_META_WEAK const struct _AetherLibMeta* aether_lib_meta(void) { ... }
  • GCC/Clang/MinGW honour the attribute across ELF, Mach-O, and COFF, so
    the no-escape-hatch link works on GNU ld and ld64 — the whole
    point of the 0.539 model.
  • A lone .so is byte-for-byte the same contract: one definition, dlsym
    unchanged — verified by rebuilding a single-module .so and running
    ae lib-info against it, plus the lib_meta / lib_meta_closures
    suites.
  • In a deliberate multi-TU link the first TU's catalog wins; the doc now
    says to treat aether_lib_meta as meaningful only on single-module
    artifacts. (Per-TU catalog symbols — option 2 — remain open as a
    follow-up if aeb ever wants per-module introspection in one artifact.)
  • Non-GNU compilers fall back to the old strong emission (single-TU
    only), same as before.

Test

multi_tu_import_link grows a phase 2 closing the coverage gap the
issue called out: the same four-module program with its three modules
emitted --emit=lib (three catalogs in one link), compiled and linked
with no duplicate-symbol flags of any kind, output pinned, plus an nm
belt that aether_lib_meta is W, not T. Before the codegen change
this phase fails with exactly the avn-reported duplicate-symbol error.

All emit-lib-adjacent suites pass locally: lib_meta, lib_meta_closures,
emit_lib_const, emit_lib_deadline, emit_lib_net, emit_csrc, emit_obj,
emit_link_requirements, multi_tu_import_link.

Downstream: once released, aeb can drop the
link_flag("-Wl,--allow-multiple-definition") workaround from its
multi-TU targets and macOS stays viable for the --emit=lib
orchestrator shape.

🤖 Generated with Claude Code

paul-hammant and others added 3 commits August 15, 2026 11:59
…gs (#1590)

Every --emit=lib TU carries the aether_lib_meta() reflection catalog
(issue #403) with external linkage, so the 0.539 multi-TU link model —
one --emit=lib C per module, one final link, NO
-Wl,--allow-multiple-definition — died with N-1 duplicate-symbol errors
the moment an orchestrator (aeb regen, observed on avn) linked two lib
TUs. The plain-emission multi_tu_import_link regression could not see
it: only --emit=lib emits the catalog.

The catalog entry point is now emitted weak (__attribute__((weak))
under GCC/Clang — honoured across ELF/Mach-O/COFF; non-GNU compilers
keep the old strong emission). A lone .so is unchanged: one
definition, same dlsym contract, `ae lib-info` verified against a
freshly built .so. In a multi-TU link the first TU's catalog wins and
the link proceeds with no escape hatch, on ld64 as well as GNU ld.

multi_tu_import_link grows a phase-2: the same four-module program with
the three modules emitted --emit=lib (three catalogs in one link), no
dedup flags, runs, plus an nm pin that aether_lib_meta is W not T.
docs/emit-lib.md documents the weak contract and the multi-TU
first-wins caveat.

All emit-lib-adjacent suites pass: lib_meta, lib_meta_closures,
emit_lib_const, emit_lib_deadline, emit_lib_net, emit_csrc, emit_obj,
emit_link_requirements, multi_tu_import_link.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The nm belt in multi_tu_import_link phase 2 grepped for "W" and
treated "T" as strong — but Mach-O nm prints a weak definition as a
plain T and only `nm -m` shows the "weak external" flag, so the check
false-failed on exactly the platform (ld64) the no-dup-flag model
exists for; the link and run steps themselves passed on macOS. The
belt now branches on uname: Darwin uses `nm -m` + "weak external",
everything else keeps the W/T check.

Also: the 0.540.0 catch-up merge auto-folded this PR's CHANGELOG entry
into the released [0.540.0] section; moved back under a fresh
[current].

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@paul-hammant
paul-hammant merged commit 98e32c7 into main Aug 15, 2026
25 checks passed
@paul-hammant
paul-hammant deleted the fix/emit-lib-meta-weak branch August 15, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

--emit=lib's aether_lib_meta catalog breaks the multi-TU no-dup-flag link model

1 participant