Skip to content

Linking to a static library does not always include exported symbols #99721

@mkroening

Description

@mkroening
Contributor

Similarly to #47384, statics in a static library that are marked as used are not included in the resulting binary if a non-inlined function is used in the same module.

Example

This code can be found at mkroening/rust-issue-99721.

staticlib.rs:

mod note {
    #[used]
    #[link_section = ".note.my-note"]
    static MY_NOTE: u8 = 0;

    #[inline(always)]
    pub fn dummy() {}
}

#[no_mangle]
pub extern "C" fn staticlib() {
    note::dummy();
}

bin.rs:

fn main() {
    extern "C" {
        fn staticlib();
    }

    unsafe {
        staticlib();
    }
}

Build using:

rustc --crate-type staticlib staticlib.rs
rustc -L native=. -l static=staticlib bin.rs

Display static library notes:

$ readelf -Wn libstaticlib.a
[...]
File: libstaticlib.a(staticlib.staticlib.68b5aa53-cgu.0.rcgu.o)

Displaying notes found in: .note.my-note
  Owner                Data size 	Description
readelf: Warning: Corrupt note: only 1 byte remains, not enough for a full note
[...]

Display binary notes:

$ readelf -Wn bin
Displaying notes found in: .note.gnu.property
  Owner                Data size 	Description
  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0	      Properties: x86 ISA needed: x86-64-baseline

Displaying notes found in: .note.gnu.build-id
  Owner                Data size 	Description
  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)	   Build ID: 258d83311faf1a32cb84dd090ccc57abdbb1cb38

Displaying notes found in: .note.ABI-tag
  Owner                Data size 	Description
  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI version tag)	    OS: Linux, ABI: 4.4.0

If note::dummy is made inline(never), the symbol is included in the resulting binary:

$ readelf -Wn bin
Displaying notes found in: .note.gnu.property
  Owner                Data size 	Description
  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0	      Properties: x86 ISA needed: x86-64-baseline

Displaying notes found in: .note.gnu.build-id
  Owner                Data size 	Description
  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)	   Build ID: 72d6efcf61e2b33563094c3f179ebcc511db7485

Displaying notes found in: .note.ABI-tag
  Owner                Data size 	Description
  GNU                  0x00000010	NT_GNU_ABI_TAG (ABI version tag)	    OS: Linux, ABI: 4.4.0

Displaying notes found in: .note.my-note
  Owner                Data size 	Description
readelf: Warning: Corrupt note: only 1 byte remains, not enough for a full note

Activity

mkroening

mkroening commented on Jul 25, 2022

@mkroening
ContributorAuthor
nbdd0121

nbdd0121 commented on Jul 25, 2022

@nbdd0121
Contributor

That's the nature of static library; there's not much we can do about it unless we squeeze everything into one single object file or reference all object files from every single one of them.

mkroening

mkroening commented on Jul 25, 2022

@mkroening
ContributorAuthor

That makes sense.

I just found that you can force link the symbol with -Clink-arg=--undefined=<symbol_name> when building the binary. I was hoping there would be some general way of doing this that does not require modifying the binary.

I'll resort to using the inline(never) approach in the meantime, although that is really not a nice way of forcing a reference into the object file.

carbotaniuman

carbotaniuman commented on Jul 28, 2022

@carbotaniuman
Contributor

You need [used(linker)] for this to work. That has spotty availability sadly, but there are workarounds.

nbdd0121

nbdd0121 commented on Jul 28, 2022

@nbdd0121
Contributor

You need [used(linker)] for this to work. That has spotty availability sadly, but there are workarounds.

That won't work either. Object file inside static libs will not participate in linker if it does not provide any undefined symbol. SHF_GNU_RETAIN are not special in this regard.

mkroening

mkroening commented on Jul 28, 2022

@mkroening
ContributorAuthor

You need [used(linker)] for this to work. That has spotty availability sadly, but there are workarounds.

Yeah, that does not work unfortunately.

petrochenkov

petrochenkov commented on Jul 31, 2022

@petrochenkov
Contributor

There's a similar issue that can be fixed.

You link a staticlib (written in C, or Rust, or whatever) to a binary, and you want to preserve some symbol from it.

// bin.rs

#[link(name = "my_staticlib")]
extern "C" {
    #[used(linker)]
    fn some_symbol_from_my_staticlib_that_needs_to_be_kept();

   #[used(linker)]
   static SAME_BUT_STATIC: u8;
}

Unfortunately, #[used] attributes do not work on foreign items, but it probably should.
(It should also work on (foreign) functions too, but this is what #94348 is about.)

If it worked it would refer to the symbol in question from symbols.o thus keeping the corresponding object file during linking.

mkroening

mkroening commented on Aug 1, 2022

@mkroening
ContributorAuthor

Interesting!

Since in our use case we want to distribute the static library, this won't work. It's great that you can use --undefined from within Rust, though. For our problem I have now resorted to having a macro in the excluded module to define the static in the rood module while keeping the logic separate.

added
A-linkageArea: linking into static, shared libraries and binaries
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
C-discussionCategory: Discussion or questions that doesn't represent real issues.
and removed
C-bugCategory: This is a bug.
on Jan 26, 2024
bjorn3

bjorn3 commented on May 9, 2025

@bjorn3
Member

For pulling in the right object files, #[used] nowadays causes symbol to be added to symbols.o. So all that is needed to fix this would be to make #[used] an alias for #[used(linker)], which #140872 does if accepted.

nbdd0121

nbdd0121 commented on May 10, 2025

@nbdd0121
Contributor

For pulling in the right object files, #[used] nowadays causes symbol to be added to symbols.o. So all that is needed to fix this would be to make #[used] an alias for #[used(linker)], which #140872 does if accepted.

I don't see how the linked PR would fix this issue. See #99721 (comment)

bjorn3

bjorn3 commented on May 10, 2025

@bjorn3
Member

As I said, because we reference #[used] statics from symbols.o the object files containg them should be pulled in even if otherwise unreferenced.

nbdd0121

nbdd0121 commented on May 10, 2025

@nbdd0121
Contributor

This is a staticlib, #[used] marking within the staticlib itself cannot propagate out to the user.

bjorn3

bjorn3 commented on May 21, 2025

@bjorn3
Member

Right, this would either need --whole-archive or something symbols.o like where symbols.o in turn gets pulled in by every codegen unit.

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

    A-linkageArea: linking into static, shared libraries and binariesC-discussionCategory: Discussion or questions that doesn't represent real issues.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @nbdd0121@ChrisDenton@petrochenkov@fmease@bjorn3

        Issue actions

          Linking to a static library does not always include exported symbols · Issue #99721 · rust-lang/rust