Skip to content

RFC: Support External Linkers - #3993

Open
adamncasey wants to merge 6 commits into
rust-lang:masterfrom
adamncasey:support-external-linkers-2
Open

RFC: Support External Linkers#3993
adamncasey wants to merge 6 commits into
rust-lang:masterfrom
adamncasey:support-external-linkers-2

Conversation

@adamncasey

@adamncasey adamncasey commented Aug 4, 2026

Copy link
Copy Markdown

This RFC partially stabilises a version 0 of the rlib format, and defines a 'standard library bundle' mechanism with the aim to make this a supported workflow:

A C or C++ application built in a monorepo / hermetic-build environment can link in a library built in Rust without changes to its existing link process

Getting there requires a couple of practical steps, which would come if this RFC is accepted - or could happen in parallel if desirable:

  1. Some compiler changes to support the new versions and flags - although in reality the existing rustc works without any of these flags today, they are important for the long term support for this feature.
  2. rustc documents and supports a "build crate as rlib, link with a non-rustc linker" model as a first-class workflow, with clearly stated constraints that keep long-term support feasible.
  3. Tests to ensure the workflow does not regress, at minimum for x86_64-unknown-linux-gnu.

I decided to pick this up after some conversations with folks where I work, and others within the Rust Community. My understanding is that this technique is already used today by a few large companies who are also active in the Rust community. Standardising this feels like a positive step forward, which makes it easier for more projects to adopt Rust in existing C++ codebases.

It's my first RFC & I probably didn't get everything right but hopefully I'm close enough. Happy to discuss here/on Zuilip/zoom/etc as needed to keep this moving.

Credit note: Although I am submitting this RFC, and stand by every word in it, a large majority of its content comes directly from @pcwalton's pre-RFC here: https://internals.rust-lang.org/t/pre-rfc-stabilize-a-version-of-the-rlib-format/17558 which was attached to rust-lang/rust#73632

Rendered

adamncasey and others added 5 commits August 4, 2026 07:43
Original Author: Patrick Walton.

Co-authored-by: Patrick Walton <pcwalton@fb.com>
…scope sections.

Supported Platforms note, expanded examples, reflow
the Rust compiler, driven by a variety of build systems, in a way that doesn't result in
symbol conflicts when diamond dependencies are involved.

### Standard library bundles

@bjorn3 bjorn3 Aug 5, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I was imagining this wouldn't special case the standard library. Rather we would introduce a new rstaticlib crate type to fill the following matrix:

- Rust+C ABI C ABI only
statically linked rstaticlib staticlib
dynamically linked (rust) dylib cdylib

So like the rust dylib crate type it would be usable as regular rust dependency. Just like the staticlib crate type it would be statically linked. And like all non-rlib crate types it can contain multiple crates and contains things like the allocator shim as appropriate.

This avoids special casing the standard library and would make it possible to use #[global_allocator] and work for no_std projects too. And in addition it would avoid the need for -Crlib-version as no ribs would need to be directly linked. And EIIs would trivially work. If necessary the compiler can insert any object files to wire up EII defaults when building the rstaticlib like would happen for (c)dylibs and staticlibs.

The way you did use this is to compile some crate as rstaticlib and have this crate depend on the standard library and have it or a dependency define #[global_allocator] if you need one. And then every other can be compiled as either rstaticlib or rlib (if you want to bundle it together with other rlibs to form another rstaticlib) with a dependency on this first rstaticlib. And then you link all rstaticlibs together as regular C static libraries.

View changes since the review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right in thinking you're describing something similar to the staticlib-nobundle alternative described here:
https://github.com/rust-lang/rfcs/pull/3993/changes#diff-68e8cfbb8f0b236256ec7cb366e93d1a56e4296801ff0f97fea3cc7729217b9dR472-R480
But with this archive file also including the .rmeta data (and possibly other things) which an rlib contains, so that rustc would consume it a lot like an rlib when building a rust application?

In this world I think you're saying the supported workflow would look something like this:

  1. Build at least one crate (although probably close to 1, for performance reasons since these would be large?) as an rstaticlib.
  2. Build all your other rust dependencies as an rlib
  3. Link your application by passing in the rlibs and rstaticlibs together.

Assuming I got the above right:

I agree this would avoid special casing the standard library. In practice, if the number of rstaticlib crates being built needs to be kept low, I imagine build systems choose to build exactly one of these at the bottom of the dependency tree?

Separately I'm not currently sure how we could support being able to link multiple rstaticlibs while having them export the standard library symbols as needed for linking against rlibs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For step 2 you would either build all other rust dependencies as rstaticlib or if you have an EII, you build the crate that declare the EII and the one that define it as rlib and then build an rstaticlib that bundles them together (or build the one defining the EII directly as rstaticlib, bundling the crate that declares the EII).

Separately I'm not currently sure how we could support being able to link multiple rstaticlibs while having them export the standard library symbols as needed for linking against rlibs.

If one rstaticlib depends on another rstaticlib, it will assume that all crates included in that other rstaticlib don't need to be bundled into the local rstaticlib. This is the same behavior as for rust dylibs where any crates included through an upstream dylib don't get linked in locally but rather the version in the upstream dylib is used and rustc errors if two dylibs that are used as dependency together both bundle the same crate.

@bjorn3 bjorn3 added T-lang Relevant to the language team, which will review and decide on the RFC. T-compiler Relevant to the compiler team, which will review and decide on the RFC. labels Aug 5, 2026

@teor2345 teor2345 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I'm super interested in this and would be happy to help with implementation and testing.

I have some suggestions around requiring used lang items, which it would be great for someone familiar with those details to double-check.

View changes since this review

Comment thread text/3993-support-external-linkers.md Outdated
Comment thread text/3993-support-external-linkers.md Outdated
echo $?
```

RFC Note: This works today by removing the `-C emit-std-bundle=yes` and `-C rlib-version=v0` flags.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've already said this above, but it helps to explain what doesn't work again here:

Suggested change
RFC Note: This works today by removing the `-C emit-std-bundle=yes` and `-C rlib-version=v0` flags.
RFC Note: This works today by removing the `-C emit-std-bundle=yes` and `-C rlib-version=v0` flags,
but it would fail if there was a diamond dependency.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This technique actually works today even with diamond dependencies (only the standard library builds as a staticlib, the crate builds as an rlib). This note was meant to highlight that the only implementation changes required by this RFC are compiler flags/versions for a backwards compatibility strategy.

I can expand this example to include a diamond dependency to illustrate this point?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's already a diamond dependency example in the RFC, so I think it's enough to just say it works here too. (And that's an important piece of info!)

Comment thread text/3993-support-external-linkers.md Outdated
Comment thread text/3993-support-external-linkers.md Outdated
Comment thread text/3993-support-external-linkers.md Outdated
scheme for symbols.

*Note (non-normative):* Symbols relating to global allocation and panic handling
must not be defined in the .rlib unless the crate itself defines those symbols.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this limitation apply to all lang items?

system linker.
* Exactly one of the core or std standard library bundles is supplied to the system
linker. This standard library bundle must have been built in a *compatible manner*
with all rlibs to be linked.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a subtle requirement here around lang items, which is roughly:

Suggested change
with all rlibs to be linked.
with all rlibs to be linked.
* All used [*language items*](https://rustc-dev-guide.rust-lang.org/lang-items.html) are supplied by exactly one rlib. Ordinarily, the standard library supplies these language items. But in `no_std` builds, some may be supplied by other rlibs.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a sensible wording, and this probably also sets the scene for tweaking this wording for a similar requirement for EIIs(same requirement?). I'll add this if no one objects for a couple of days

Comment thread text/3993-support-external-linkers.md Outdated
- **The CI-tested configuration.** Which compiler flags, panic strategies,
and platforms are in-scope for covering under tests.
- **Testing scope.** What should the tests check for. I.e. link test
of a diamond graph, symbol-table checks on emitted `rlib`s?, etc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be useful to test a recursive cross-language dependency:

  • a Rust crate that requires symbol A from C++ and provides symbol B
  • a C++ library that requires symbol B and provides A

This is currently possible but tricky to get right with some system linkers.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it would be good to test at least one level of language weaving. At least the use case I have in mind would likely do this. Do you specifically want two libraries which depend on each other? In my experience that type of scenario is fairly rare/problematic (maybe I'm out of date there?).

I think I would pick to have a cross-language dependency tree more like:

        A (C++ Executable)
               |
               v
        B (Rust Library)
          |           \
          v            \
   C (C++ Library)      \
          |               \
          +---------------> D (Rust Library)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both are good tests, and they increase test coverage in slightly different ways.

I agree recursion is rare, and we might not support it initially. But either way it would be good to know its status, and make sure it doesn't regress. Recursion might also be an opportunity to improve error messages or documentation more generally.

I've seen new interop users run into it in small projects when just starting out, too.

Co-authored-by: teor <teor@riseup.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-compiler Relevant to the compiler team, which will review and decide on the RFC. T-lang Relevant to the language team, which will review and decide on the RFC.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants