rust-lang/rust

C/C++ code linked to Rust code can't use some clang builtins

glandium opened this issue · 3 comments

This is theoretically a problem that applies to all platforms, but in practice, at least on Linux, the rust compiler is saved by the forced linkage of libgcc_s (which, incidentally, is not really desirable when using clang's --rtlib argument, but that's another story). It is a problem on x86_64 Android.

Here are steps to reproduce the problem:

Download and unpack Android NDK r25c:

$ wget https://dl.google.com/android/repository/android-ndk-r25c-linux.zip
$ unzip android-ndk-r25c-linux.zip

Create a test crate:

$ cargo new --bin testcase
$ cd testcase
$ cat <<EOF >> Cargo.toml
[build-dependencies]
cc = "1.0"
EOF
$ cat <<EOF > build.rs
fn main() {
    cc::Build::new().file("foo.cpp").compile("foo");
}
EOF
$ cat <<EOF > src/main.rs
#[link(name="foo")]
extern {
    fn foo(d: f64);
}

fn main() {
    unsafe {
        foo(42.0);
    }
}
EOF
$ cat <<EOF > foo.cpp
extern "C" __float128 foo(double d) {
    return d;
}
EOF

Compile the crate (with some manual work that presumable cargo-apk would do):

$ export RUSTFLAGS="-Clinker=$PWD/../android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -Clink-arg=--target=x86_64-linux-android31 -Clink-arg=--sysroot=/tmp/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/sysroot"
$ export CC=$PWD/../android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/clang
$ export AR=$PWD/../android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar
$ cargo build --target=x86_64-linux-android

This fails to link with, eventually:

  = note: ld: error: undefined symbol: __extenddftf2
          >>> referenced by foo.cpp:2
          >>>               foo.o:(foo) in archive /tmp/testcase/target/x86_64-linux-android/debug/build/testcase-7b8046ba34d79ef2/out/libfoo.a
          clang-14: error: linker command failed with exit code 1 (use -v to see invocation)

The __extenddftft2 symbol comes from the compilation of the function foo, for the extension of the 64-bits double to a 128-bits double. It is part of libclang_rt that comes with clang. But because rustc calls the linker (clang) with -nodefaultlibs, the clang runtime is not compiled in. And because the equivalent compiler-builtins from rustc doesn't contain all the clang builtins, this fails to link.

When linking dynamic libraries, the link actually goes through without an error, but yields an error when the library is loaded at runtime because of the missing __extenddftft2 symbol. You'll find multiple people running into this problem over the years, but somehow I didn't find it reported against the rust compiler (example: https://github.com/mozilla/application-services/issues/5436).

Because the __extenddftft2 symbol exists in rustc's compiler-builtins for aarch64 android, it's not a problem on that platform.

But because rustc calls the linker (clang) with -nodefaultlibs, the clang runtime is not compiled in.

The first step in investigating this issue would be to find out why rustc currently passes -nodefaultlibs. It sounds like if we removed that flag, it would fix this bug?

See 28fa81a.

Has anyone else had a chance to look at this issue more? With Rust's standard library only targeting NDK 25 in recent versions, I think that this issue has become more problematic for projects using the same NDK.

AFAICT, the symbol has to exist somewhere in clang's compiler-rt library if manually linking to it (as per the workaround everyone is using) fixes the issue. LLVM also has a codegen test for x86_64-android-linux with the symbol, so it seems to be expected. Optimistically, maybe Rust's compiler-builtins just needs to include something else or change a flag?