Cdylib uses external memcpy/memset but does not link to anything

Let's use as a simplified example crate foo, with the following Cargo.toml:

[package]
name = "foo"
version = "0.1.0"
edition = "2021"

[lib]
name = "foo"
crate-type = ["cdylib"]

[profile.release]
panic = "abort"
strip = true

And src/lib.rs:

#![no_std]

#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    extern "C" { fn panic_nonexistent() -> !; }
    unsafe { panic_nonexistent() }
}

#[no_mangle]
pub unsafe extern "C" fn foo_cpy(src: *const u8, dst: *mut u8, len: usize) {
    core::ptr::copy_nonoverlapping(src, dst, len)
}

It gets compiled to libfoo.so. But when we inspect it, we get:

$ ldd libfoo.so 
	statically linked
$ readelf -Ws libfoo.so
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND memcpy
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_finalize
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     6: 0000000000001100    15 FUNC    GLOBAL DEFAULT    9 foo_cpy

Is it correct that the generated shared library uses external memcpy, but does not link to anything?

Usually it works fine, since memset is a standard symbol and usually present during linking process. But when I tried to create a different shared library which links both libc and libfoo, I get the following warning:

Relink `libfoo.so' with `/lib/x86_64-linux-gnu/libc.so.6' for IFUNC symbol `memcpy'

I "solved" this issue by explicitly linking libfoo to libc using build script, but shouldn't it be done automatically by compiler when I build it for x86_64-unknown-linux-gnu?

Is it possible to forcibly inline implementation of built-in intrinsics like memcpy to get a truly static shared library?

1 Like

If you use #![no_std] you are responsible for ensuring that various functions required by LLVM exist. Most of them are provided in compiler_builtins, but for some like memcpy compiler_builtins defers to libc on targets that have a libc under the assumption that the version in libc is more optimized. You can depend on compiler-builtins from crates.io and enable it's mem feature to avoid the dependency on libc. You may need to add extern crate compiler_builtins; to pull in this version of compiler-builtins as opposed to the version from the sysroot.

6 Likes

Thank you! compiler_builtins is exactly what I need, but, unfortunately, I can not use Nightly toolchain for my project, same for the Firefox hack, so I guess I will stick to the build script solution for now.

Instead of a build script you can also do #[link(name = "c")] extern "C" {}.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.