assemblyruststatic-librariesrust-cargoffi

Failing to link static libraries during Rust unit testing


I have been working on this simple example. This entire project is being compiled on a (Linux AArch64) Raspberry Pi 3B.

Say I have a simple assembly function (that does SIMD addition on AArch64) defined as so:

.text

.global _add_vecu8_16
.type _add_vecu8_16, %function

._add_vecu8_16:
    ldr q0, [x0]
    ldr q1, [x1]

    add v2.16b, v0.16b, v1.16b
    str q2, [x2]
    ret

I convert this into some static library (libneon.a) and import this function into a Rust library. This static library is inside the 'asm/build' folder, which is in the root folder of the project. The Rust code being:

#[link(name = "neon", kind = "static")]
unsafe extern "C" {
    pub(crate) fn _add_vecu8_16(v0: *const u8, v1: *const u8, result: *mut u8);
}

I have defined the VecU8_16 type. It also implements the Add trait.

#[derive(Clone, Copy)]
pub struct VecU8_16 {
    pub data: [u8; 16]
}

impl Add for VecU8_16 {
    type Output = VecU8_16;

    fn add(self, rhs: Self) -> Self::Output {
        let mut result = Self {
            data: [0; 16]
        };

        unsafe {
            _add_vecu8_16(&self.data[0], &rhs.data[0], &mut result.data[0]);
        }

        result
    }
}

I also add a unit-test function in Rust to test this setup. The test function looks like so:

#[test]
fn add_u8() {
    let v0 = VecU8_16 {
        data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
    };

    let v1 = VecU8_16 {
        data: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    };

    assert_eq!([17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17], (v0 + v1).data);
}

The Rust library successfully finds the static library and successfully compiles with no errors when I use cargo build or cargo build --release.

This is my config.toml:

[target.aarch64-unknown-linux-gnu]
rustflags = ["-Lasm/build", "-lstatic=neon"]

However, when i run cargo test, it fails to load the static library for some reason and tells that '_add_vecu8_16' isnt resolved and recommends me to link to the library which has been done already.

On running cargo test --verbose, I get this:

Compiling neon-rs v0.1.0 (/home/radon/neon-rs)
     Running `/home/radon/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/rustc --crate-name neon_rs --edition=2024 src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=80 --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 --test --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=6f065afc968b0ba4 -C extra-filename=-04ee3bcf5bd69891 --out-dir /home/radon/neon-rs/target/debug/deps -C incremental=/home/radon/neon-rs/target/debug/incremental -L dependency=/home/radon/neon-rs/target/debug/deps -Lasm/build -lstatic=neon`
error: linking with `cc` failed: exit status: 1
  |
  = note:  "cc" "/tmp/rustc2UcG6o/symbols.o" "<25 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "-lneon" "<sysroot>/lib/rustlib/aarch64-unknown-linux-gnu/lib/{libtest-*,libgetopts-*,libunicode_width-*,librustc_std_workspace_std-*,libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,libcfg_if-*,liblibc-*,liballoc-*,librustc_std_workspace_core-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "asm/build" "-L" "<sysroot>/lib/rustlib/aarch64-unknown-linux-gnu/lib" "-o" "/home/radon/neon-rs/target/debug/deps/neon_rs-04ee3bcf5bd69891" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: /usr/bin/ld: /home/radon/neon-rs/target/debug/deps/neon_rs-04ee3bcf5bd69891.9gl10j5lzk7lvi88uj1o6po6t.rcgu.o: in function `neon_rs::vec_u8::<impl core::ops::arith::Add for neon_rs::types::VecU8_16>::add':
          /home/radon/neon-rs/src/vec_u8/mod.rs:14: undefined reference to `_add_vecu8_16'
          collect2: error: ld returned 1 exit status
          
  = note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib)

error: could not compile `neon-rs` (lib test) due to 1 previous error

Caused by:
  process didn't exit successfully: `/home/radon/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/rustc --crate-name neon_rs --edition=2024 src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=80 --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 --test --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=6f065afc968b0ba4 -C extra-filename=-04ee3bcf5bd69891 --out-dir /home/radon/neon-rs/target/debug/deps -C incremental=/home/radon/neon-rs/target/debug/incremental -L dependency=/home/radon/neon-rs/target/debug/deps -Lasm/build -lstatic=neon` (exit status: 1

Am I missing any test configurations?

Apart from this, I also wanted to know how a Rust binary project could use this Rust library. The Rust library depends on a local static library. So my best guess is that the static library is pre-compiled and shipped along with the library source (say if you'd want to put it in crates.io)? Or is it better to use build.rs and ship the assembly source?

Im using clang to compile those assembly files.

Some insight on this would be helpful.


Solution

  • I got to realise that there were typos in my assembly code that was causing the tests to not compile. Thanks for the help.