Upstream work · rust-lang/rust
Changing what goes inside a Rust rlib
A Rust library is an archive that the compiler reads at two different times: early, to compile the crates that depend on it, and late, when it links the final binary. Some facts were only needed at link time but lived in the early metadata, and some were guessed from file names. I worked on a series of compiler changes that move them into a new archive member that only the linking code reads.
How I came to this
I came to this work by reading the current compiler code: how rustc writes an rlib, reads it back, and links it. That is how I found the problem and started working on it.
For me, understanding Rust was more than learning Rust. It was a way to become a much better software engineer, interested in hard and practical problems.
The problem
An rlib is an ar archive. It contains the crate metadata that other crates compile against, the object files produced by code generation, and sometimes native libraries bundled into it.
When rustc needed to know which members are Rust object files, for example to read them for LTO, it looked at the member name: a Rust object file ends in .rcgu.o. But a bundled native library can put a member with any name into the archive, including one that ends in .rcgu.o. And facts that only the linker needs, like the filenames of bundled native libraries, were computed and decoded with the early metadata.
The tracking issue, opened by Vadim Petrochenkov, proposed one more metadata member for rlibs: link-time facts, read only when rustc links the rlib into something.
How far down it went
The work sits in the compiler backend and the archive format, not in language features: the archive writer and reader in rustc_codegen_ssa, the LTO readers of the LLVM and GCC backends, native library handling in rustc_metadata, and how the member is found in different object file formats.
The changes
#154861 · Add rlib digest to identify Rust object files
Merged May 5, 2026Adds the lib.rmeta-link archive member, which lists the members that are Rust object files. The LTO readers of the LLVM and GCC backends read it instead of looking at file names, and the filename heuristic is deleted. The member sits right after lib.rmeta.
#156735 · Move NativeLib::filename to the rmeta-link archive member
Merged July 11, 2026The filename of a bundled native library is only needed at link time, so it left the crate metadata. The member stores the filenames as a list aligned by position with the crate's native libraries, and only the linking code reads them.
#158194 · RmetaLinkCache
Merged June 28, 2026A cache for one link, keyed by rlib path, so each lib.rmeta-link member is decoded at most once. It was split out of the review of the previous change after a performance run regressed.
#159571 · Remove unused bundled library lookup for the local crate
Merged July 20, 2026After the move, one branch computed bundled library filenames for the local crate that nothing read. Cleanup only, no change in behavior.
#161698 · Fix rmeta-link metadata not being read on AIX
Open, waiting on authorOn XCOFF the payload is written to a different section than on other formats, and the reader did not account for it. Review asked for the target's AIX flag instead of parsing the file, and one change remains before it can merge. It is not tested on AIX: CI has no AIX runner and I do not have access to a machine.
What review changed
The pull request descriptions describe the first versions. The merged code is different, and the differences are the interesting part.
- No fallback for old rlibs. The first version kept the filename check for rlibs without the new member. The reviewers pointed out that an rlib only works with the compiler version that wrote it, so every rlib a new compiler reads has the member. The heuristic was deleted.
- Right after the crate metadata, not at the end. The issue proposed writing the member last. In review it moved next to
lib.rmeta. - Read only by the linking code. My first version of the native library change decoded the filenames through the metadata loader and patched them back. Review asked for the opposite: remove the field and read the member only during linking.
- Position, not name, as the key. A library name is not a reliable key, so the filenames are stored in the same order as the crate's native libraries.
- No metadata format version bump. I proposed one, and review showed the change did not need it.
- A performance regression. A benchmark run regressed on a large workspace. The read is now skipped for crates without bundled libraries, and the cache became its own pull request.
Where the change went next
Once the member existed, other work started to use it. bjorn3 added global assembly objects to the list (#157051). Vadim Petrochenkov gave archive entries an explicit kind (#157263), which later work on hiding internal symbols in static libraries relies on, and moved native library search out of rustc_metadata (#160272), noting that the tracking issue was now partially implemented.
Not everything in the tracking issue is done. Bundling object files into the rlib for link-time cfg has not started, and the AIX read path is still open.
Review
The first change went through several review cycles with Vadim Petrochenkov and bjorn3 before it merged. The later changes were reviewed by Vadim Petrochenkov. The evidence for everything on this page is in the pull requests and their review threads.
What is next
A longer technical write-up is planned: the archive layout before and after, why the filename heuristic existed, and how each review comment changed the design.