SchrodingerZhu / snmalloc-rs

rust bindings of snmalloc
MIT License
119 stars 16 forks source link

`smalloc-sys` - Allow `libatomic` to be statically linked #187

Open polarathene opened 1 week ago

polarathene commented 1 week ago

Problem

libatomic is forced to link dynamically, preventing static builds. Is there a reason for this?

Reproduction

#[global_allocator]
static ALLOCATOR: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc;

fn main() {
    println!("Hello, world!");
}
docker run --rm -it --workdir /example alpine 
apk add rustup gcc g++ cmake make nano
rustup-init -y --profile minimal && source "$HOME/.cargo/env"
cargo init && cargo add snmalloc-rs

# Add the two allocator lines above to `src/main.rs` then compile a static build:
nano src/main.rs
RUSTFLAGS="-C relocation-model=static -C target-feature=+crt-static" cargo build \
  --release \
  --target x86_64-unknown-linux-musl

Failure linking libatomic.so:

  = note: /usr/lib/gcc/x86_64-alpine-linux-musl/13.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: attempted static link of dynamic object `/usr/lib/gcc/x86_64-alpine-linux-musl/13.2.1/../../../../lib/libatomic.so'
          collect2: error: ld returned 1 exit status

libatomic.a is available:

$ ls -l /usr/lib | grep atomic

-rw-r--r--    1 root     root        111956 Mar 12  2024 libatomic.a
lrwxrwxrwx    1 root     root            18 Oct 10 00:29 libatomic.so -> libatomic.so.1.2.0
lrwxrwxrwx    1 root     root            18 Oct 10 00:28 libatomic.so.1 -> libatomic.so.1.2.0
-rwxr-xr-x    1 root     root         26248 Mar 12  2024 libatomic.so.1.2.0

Adding to the RUSTFLAGS ENV value with -C link-arg=/usr/lib/libatomic.a, -L /usr/lib -l static=atomic, or -C prefer-dynamic=no all fail to get this library linked statically.


Cause

https://github.com/microsoft/mimalloc/issues/634

Few older platforms require libatomic for atomic operations but many of them don't (ARMv7 and higher, i686 and higher). mimalloc however uses it irrespective of target platform when libatomic is installed

This is also the case with snmalloc?

cc:

https://github.com/SchrodingerZhu/snmalloc-rs/blob/a564ef910b1209aedd4de4941baa69788c83d740/snmalloc-sys/build.rs#L131-L137

cmake:

https://github.com/SchrodingerZhu/snmalloc-rs/blob/a564ef910b1209aedd4de4941baa69788c83d740/snmalloc-sys/build.rs#L290

https://github.com/SchrodingerZhu/snmalloc-rs/blob/a564ef910b1209aedd4de4941baa69788c83d740/snmalloc-sys/build.rs#L295-L298


Reference - mimalloc-sys

For the mimalloc-sys rust crate this was resolved with an additional conditional (_related report, associated issue/PR was resolved in May 2024_):

let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("target_arch not defined!");

// on armv6 we need to link with libatomic
if target_os == "linux" && target_arch == "arm" {
    println!("cargo:rustc-link-lib=dylib=atomic");
}

However the use of dylib forced dynamically linking the library breaking support for static builds. That was fixed in June 2024.

SchrodingerZhu commented 1 week ago

I remember snmalloc emits references to certain __atomic* symbols. Many platforms does not ship a static version of libatomic due to its nature (there is some datum inside libatomic that ought to be shared by processes). Tweaking architectural feature flags can eliminate such symbol references but I need to check with @mjp41 and test it on my side.