Skip to content

FreeBSD tier-2 cross-link under Zig 0.16: link staged libs by path, only when used - #1930

Merged
paul-hammant merged 1 commit into
mainfrom
fix/freebsd-cross-zig016-tier2-linking
Sep 7, 2026
Merged

FreeBSD tier-2 cross-link under Zig 0.16: link staged libs by path, only when used#1930
paul-hammant merged 1 commit into
mainfrom
fix/freebsd-cross-zig016-tier2-linking

Conversation

@paul-hammant

Copy link
Copy Markdown
Collaborator

Fixes FreeBSD cross-linking failing ld.lld: cannot find entry symbol _start +
error: libc not available — reported downstream (aeo's asks/) as an
OpenSSL-specific failure blocking the aeo CLI's FreeBSD asset.

What it actually was

Not crypto-specific. Reproduced on the real crossbuild kit: a program
importing only std.io fails identically. The trigger is a Zig version
mismatch
tools/ae_cross.c's FreeBSD/tier-2 wiring is written for Zig
0.16
(its own comments say so: "Zig 0.16 supplies its CRT/libc", "resolves
target-root -L paths beneath --sysroot"), but the crossbuild kit and CI pinned
Zig 0.13, which does not resolve the base libc/CRT from --sysroot for a
FreeBSD target → libc not available.

The Zig 0.13→0.16 bump (companion commits in aether-crossbuild deps.lock
and the aeo release workflows) fixes the reported error and exposes two
tier-2 linking bugs that 0.13 had masked — fixed in this PR:

1. Over-linking staged-but-unused libs

The CROSSBUILD_SYSROOT probe added -l<name> for every archive staged in the
sysroot, regardless of use. A sysroot with openssl+pcre2+zlib plus the contrib
veneers stages libpcre2-8.a / libaether_host_ruby.a / … even for a program
importing none of them. Zig 0.13 tolerated the dangling -ls; Zig 0.16
hard-errors
(unable to find dynamic system library 'pcre2-8'). The probe now
also gates on the program's resolved import closure — the raw // aether-link:
header codegen emits — so a lib links only when staged AND requested.

2. Mangled -L under --sysroot

Tier-2 libs used -L$CROSSBUILD_SYSROOT/lib -lNAME. Zig 0.16 resolves a bare
-L beneath --sysroot, so an absolute -L=/abs/sysroots/… became
<base-sysroot>/abs/sysroots/… and the libs were never found. They are now
linked by absolute archive path ($CROSSBUILD_SYSROOT/lib/libNAME.a) — a
plain input file, immune to the rewriting. openssl (ssl+crypto) and the sqlite
veneer keep their multi-archive link order.

windows.yml's hardcoded zig fetch is bumped to 0.16 with the archive rename
(0.16 is zig-<arch>-<os>-<ver>, was <os>-<arch>). aether's own
scripts/get-zig.sh was already on 0.16.

Verification

On the real crossbuild kit (Zig 0.16, fully-staged FreeBSD15 sysroot):

  • std.cryptography program → valid FreeBSD ELF; link carries only
    libssl.a + libcrypto.a by absolute path (no -L, no unused
    python/ruby/pcre2).
  • std.io-only and std.regex programs → valid FreeBSD ELFs.
  • linux-musl and windows cross builds unaffected; make test 409/409.

The FreeBSD-cross CI leg (ci-freebsd-cross, already on 0.16 via
scripts/get-zig.sh) exercises this path.

Companion changes (separate repos)

  • aether-crossbuild: deps.lock zig 0.13.0 → 0.16.0 (+ get-zig.sh comment).
  • aeo: release-aeo.yml / release-aeo-agent.yml zig pin 0.13.0 → 0.16.0
    and the archive-name rename; once this lands, aeo bumps AEO_FREEBSD_MIN_AE
    and drops continue-on-error on its FreeBSD jobs.

🤖 Generated with Claude Code

…bs by path, only when used

FreeBSD cross-linking a program that reaches the final link died with
`ld.lld: cannot find entry symbol _start` + `error: libc not available`.
Reported downstream as an OpenSSL-specific failure (aeo CLI's FreeBSD asset),
but it was not crypto-specific: a program importing only std.io failed
identically. Root cause: tools/ae_cross.c's FreeBSD/tier-2 wiring was written
for Zig 0.16 (its own comments say so), but the crossbuild kit and CI pinned
Zig 0.13, which does not resolve the base libc/CRT from --sysroot for a FreeBSD
target. Bumping to 0.16 (separate commits in aether-crossbuild/deps.lock and
the aeo release workflows) fixes the libc/_start error and exposes two tier-2
bugs 0.13 had masked, fixed here:

- **Over-linking.** The CROSSBUILD_SYSROOT probe appended `-l<name>` for every
  archive STAGED in the sysroot, regardless of whether the program used it. A
  sysroot provisioned with openssl+pcre2+zlib plus the contrib veneers stages
  libpcre2-8.a / libaether_host_ruby.a / … even for a program that imports none
  of them. Zig 0.13 tolerated the dangling `-l`s; Zig 0.16 hard-errors
  (`unable to find dynamic system library 'pcre2-8'`). The probe now also gates
  on the program's resolved import closure — the raw `// aether-link:` header
  codegen emits — so a lib is linked only when it is BOTH staged AND requested.

- **Mangled -L under --sysroot.** Tier-2 libs were linked as
  `-L$CROSSBUILD_SYSROOT/lib -lNAME`. Zig 0.16 resolves a bare `-L` beneath
  `--sysroot`, so an absolute `-L=/abs/sysroots/…` became
  `<base-sysroot>/abs/sysroots/…` and the libs were never found. They are now
  linked BY ABSOLUTE ARCHIVE PATH (`$CROSSBUILD_SYSROOT/lib/libNAME.a`), a plain
  input file immune to the sysroot L-path rewriting. openssl (ssl+crypto) and
  the sqlite veneer (veneer before backing lib) keep their multi-archive order.

windows.yml's hardcoded zig fetch is bumped 0.13.0 -> 0.16.0 with the archive
rename (0.16 is zig-<arch>-<os>-<ver>, was <os>-<arch>). aether's own
scripts/get-zig.sh was already on 0.16.

Verified on the real crossbuild kit (Zig 0.16, fully-staged FreeBSD15 sysroot):
a std.cryptography program, a std.io-only program, and a std.regex program all
cross-link to valid FreeBSD ELFs; the crypto link carries only libssl.a +
libcrypto.a by absolute path (no -L, no unused python/ruby/pcre2). linux-musl
and windows cross builds unaffected; `make test` 409/409.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@paul-hammant
paul-hammant force-pushed the fix/freebsd-cross-zig016-tier2-linking branch from a83374c to 7a0c5e3 Compare September 7, 2026 05:48
@paul-hammant
paul-hammant merged commit ed0b094 into main Sep 7, 2026
27 checks passed
@paul-hammant
paul-hammant deleted the fix/freebsd-cross-zig016-tier2-linking branch September 7, 2026 06:36
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.

1 participant