Skip to content

Symbols from static libraries used by dependent crates are no longer included into cdylib #96192

Closed
@goffrie

Description

@goffrie
Contributor

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.

Activity

goffrie

goffrie commented on Apr 18, 2022

@goffrie
ContributorAuthor

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.

self-assigned this
on Apr 18, 2022
petrochenkov

petrochenkov commented on Apr 19, 2022

@petrochenkov
Contributor

It becomes clearer what happens if you build test.rs as an executable.

rustc test.rs --crate-type bin -Lnative=. -lstatic=lib --extern dep=libdep.rlib
  = note: /usr/bin/ld: /home/we/linkrepr/libdep.rlib(dep.dep.468be17b-cgu.0.rcgu.o): in function `dep::inner':
          dep.468be17b-cgu.0:(.text._ZN3dep5inner17h014ce670d35af615E+0x3): undefined reference to `symbol'

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.

removed their assignment
on Apr 19, 2022
goffrie

goffrie commented on Apr 20, 2022

@goffrie
ContributorAuthor

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 a rules_rust issue, not really a rustc one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @goffrie@petrochenkov

        Issue actions

          Symbols from static libraries used by dependent crates are no longer included into cdylib · Issue #96192 · rust-lang/rust