Closed
Description
Here is the setup. This is all on x86_64-unknown-linux-gnu
.
lib.c
:
void symbol(void) {}
dep.rs
:
extern "C" { fn symbol(); }
pub unsafe fn inner() { symbol(); }
test.rs
:
#[no_mangle]
pub unsafe extern "C" fn outer() { dep::inner(); }
Then we run these commands:
# make a static library containing `symbol`
gcc lib.c -o lib.o -c
ar rc liblib.a lib.o
# compile an rlib that uses `symbol`
rustc dep.rs --crate-type rlib
# compile a cdylib that uses the rlib, and attempt to link the static library
rustc test.rs --crate-type cdylib -Lnative=. -lstatic=lib --extern dep=libdep.rlib
Previously, this worked and linked symbol
into the resulting cdylib:
# nm libtest.so | grep -w symbol
00000000000066b5 t symbol
However, it now results in an unresolved symbol:
# nm libtest.so | grep -w symbol
U symbol
Which seems wrong, since we asked to link liblib.a
into libtest.so
. The issue also doesn't occur if the static library is used directly from the cdylib crate.
Bisection reveals that the regression occurred in #95501, which is a rollup; the most likely culprit appears to be #93901.
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
goffrie commentedon Apr 18, 2022
I had hoped that #95604 would fix this since it seems very related, but I downloaded the try build and it does not appear to.
petrochenkov commentedon Apr 19, 2022
It becomes clearer what happens if you build test.rs as an executable.
The build will just fail, while with cdylibs remaining unresolved symbols are ok by default.
rustc
sorts libraries (both rlibs and native libraries) in topological order, so dependency libraries come after libraries that depend on them.In your build nothing says that dep.rs (libdep.rlib) depends on lib.c (liblib.a), so rustc puts liblib before libdep on the command line (besides the topological sorting the order may be considered random).
So liblib is simply dropped by the linker when it's encountered, because it doesn't provide any necessary symbols at that point.
What saved the build before #93901 is that rustc linked liblib as whole-archive in that particular case, so it wasn't dropped even if it wasn't needed.
After #93901 we try not to use whole-archive unless explicitly requested, so this case is broken.
The right fix here is to specify that dep.rs depends on lib.c, by using
#[link(name = "lib")]
or-l lib
when compiling dep.rs.goffrie commentedon Apr 20, 2022
Thanks for the detailed response! That makes sense, and the solution seems reasonable. I'm fine with saying that this is not a bug then.
For what it's worth this happened because we're using Bazel and
rules_rust
, wherein adding native dependencies to a Rust target causes them to be added only to the final crate's command line, not intermediate ones. So it's possible that other people will hit this too. But that's arules_rust
issue, not really a rustc one.