Skip to content

Experiment: do the new nightly rustc flags replace lib-patcher? - #17

Draft
claude[bot] wants to merge 1 commit into
mainfrom
experiment/native-symbol-hiding
Draft

claude[bot] wants to merge 1 commit into
mainfrom
experiment/native-symbol-hiding

Conversation

@claude

@claude claude Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Requested by Tobias Gerber · Slack thread

Two nightly rustc flags recently landed that look like they do lib-patcher's job inside the compiler. This PR is the empirical proof + CI to see whether they resolve the original problem (rust#104707) — it does not touch lib-patcher's own code.

Before / After

Before — link two Rust C staticlibs that each embed std into one Rust binary and it collides:

rust-lld: error: duplicate symbol: rust_eh_personality
rust-lld: error: duplicate symbol: std::panicking::EMPTY_PANIC
rust-lld: error: duplicate symbol: std::sys::args::unix::imp::ARGV_INIT_ARRAY

...and each archive leaks its internals as externally-visible (GLOBAL DEFAULT) symbols — the namespace pollution lib-patcher exists to clean up.

After — building both staticlibs with the two nightly flags, the exact same setup links cleanly and runs:

proda_add(10, 5) = 15 ok
proda_roundtrip(42) = 42 ok
prodb_mul(6, 7) = 42 ok
prodb_roundtrip(99) = 99 ok
All tests passed!

The internals are now GLOBAL HIDDEN and renamed with a distinct per-crate suffix, while the public C API is untouched:

rust_eh_personality.rsa59ee01d1713d366   (producer-a)   GLOBAL HIDDEN
rust_eh_personality.rs95a5502f661d55cc   (producer-b)   GLOBAL HIDDEN
proda_add / prodb_mul                                   GLOBAL DEFAULT  (unrenamed)

Isolating the roles: hide-only still fails the link (STV_HIDDEN alone does not deduplicate two definitions); rename-only links (the per-crate rename is what removes the two-staticlib clash). So on ELF you need rename for the clash and hide for the exported-surface hygiene.

How

  • -Zstaticlib-hide-internal-symbols (rust#155338) — sets ELF STV_HIDDEN / Mach-O equivalent on every defined symbol not in rustc's C-ABI export set.
  • -Zstaticlib-rename-internal-symbols (rust#156950) — suffixes those non-exported symbols per-crate so two staticlibs don't clash.

New crates under tests/native-hiding/ (producer-a, producer-b, consumer) reproduce the problem: two staticlibs (C API + std + serde_json, built with LTO so std folds into the crate object) linked into one Rust bin. tests/native-hiding/run.sh builds without and with the flags and asserts the before/after; .github/workflows/native-symbol-hiding.yml runs it on ubuntu-latest and macos-latest with nightly.

Verified locally on nightly 1.99.0 (af3d95584 2026-07-09) — both flags present and working on ELF/Linux. On macOS (Apple) ld64 is first-definition-wins so the unpatched clash can't be reproduced (as the repo's own matrix already documents); there the workflow only asserts the flagged build links and runs.

Verdict

The flags natively cover lib-patcher's core job on ELF and Apple: rename removes the two-staticlib duplicate-symbol clash, hide keeps internal + dependency symbols out of the exported surface. They do not yet replace:

  • Windows / COFF — the flags warn and no-op there; lib-patcher still renames COFF symbols itself.
  • Stable toolchains — both flags are nightly-only.
  • Arbitrary --keep-prefix — the flags key off rustc's own C-ABI export set, not a user-chosen prefix, and only work on sources you compile (not a vendored third-party .a).

So lib-patcher can be simplified (lean on the compiler where the flags apply) but not retired. This PR is the proof + CI; it deliberately does not remove any lib-patcher code.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KLEWk521QTtCbR2UzYq6du

Reproduce the original symbol problem (rust#104707) with two Rust staticlibs
that each embed std, linked into one Rust binary, and test whether the new
nightly flags -Zstaticlib-hide-internal-symbols (rust#155338) and
-Zstaticlib-rename-internal-symbols (rust#156950) resolve it natively.

- tests/native-hiding/{producer-a,producer-b,consumer}: the reproduction crates.
- tests/native-hiding/run.sh: builds without/with the flags and asserts the
  before/after (link fails + internals leak vs links, runs, hidden + renamed).
- .github/workflows/native-symbol-hiding.yml: runs it on Linux and macOS.

Verified on nightly 1.99.0 (af3d95584 2026-07-09): both flags present; on ELF
the unpatched two-staticlib link fails with duplicate symbols and the flagged
build links + runs with internals GLOBAL/HIDDEN and per-crate renamed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KLEWk521QTtCbR2UzYq6du

This branch has not been deployed

No deployments
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