diff --git a/Cargo.toml b/Cargo.toml index 0705dbd9..15ad600c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ core = { version = "1.0.0", optional = true, package = 'rustc-std-workspace-core [build-dependencies] cc = { optional = true, version = "1.0" } +ar = { optional = true, version = "0.8" } [dev-dependencies] panic-handler = { path = 'crates/panic-handler' } @@ -42,7 +43,13 @@ default = ["compiler-builtins"] # Enable compilation of C code in compiler-rt, filling in some more optimized # implementations and also filling in unimplemented intrinsics -c = ["cc"] +c-vendor = ["cc"] + +# Link against system clang_rt.* libraries. +# LLVM_CONFIG or CLANG (more reliable) must be set. +c-system = ["ar"] + +c = ["c-vendor"] # Flag this library as the unstable compiler-builtins lib compiler-builtins = [] diff --git a/build.rs b/build.rs index b520b624..abb544e5 100644 --- a/build.rs +++ b/build.rs @@ -40,7 +40,7 @@ fn main() { // mangling names though we assume that we're also in test mode so we don't // build anything and we rely on the upstream implementation of compiler-rt // functions - if !cfg!(feature = "mangled-names") && cfg!(feature = "c") { + if !cfg!(feature = "mangled-names") && cfg!(any(feature = "c-vendor", feature = "c-system")) { // Don't use a C compiler for these targets: // // * wasm32 - clang 8 for wasm is somewhat hard to come by and it's @@ -50,8 +50,10 @@ fn main() { // compiler nor is cc-rs ready for compilation to riscv (at this // time). This can probably be removed in the future if !target.contains("wasm32") && !target.contains("nvptx") && !target.starts_with("riscv") { - #[cfg(feature = "c")] - c::compile(&llvm_target); + #[cfg(feature = "c-vendor")] + c_vendor::compile(&llvm_target); + #[cfg(feature = "c-system")] + c_system::compile(&llvm_target); } } @@ -73,17 +75,14 @@ fn main() { } } -#[cfg(feature = "c")] -mod c { - extern crate cc; - +#[cfg(any(feature = "c-vendor", feature = "c-system"))] +mod sources { use std::collections::BTreeMap; use std::env; - use std::path::PathBuf; - struct Sources { + pub struct Sources { // SYMBOL -> PATH TO SOURCE - map: BTreeMap<&'static str, &'static str>, + pub map: BTreeMap<&'static str, &'static str>, } impl Sources { @@ -120,39 +119,11 @@ mod c { } } - /// Compile intrinsics from the compiler-rt C source code - pub fn compile(llvm_target: &[&str]) { + pub fn get_sources(llvm_target: &[&str]) -> Sources { let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); - let cfg = &mut cc::Build::new(); - - cfg.warnings(false); - - if target_env == "msvc" { - // Don't pull in extra libraries on MSVC - cfg.flag("/Zl"); - - // Emulate C99 and C++11's __func__ for MSVC prior to 2013 CTP - cfg.define("__func__", Some("__FUNCTION__")); - } else { - // Turn off various features of gcc and such, mostly copying - // compiler-rt's build system already - cfg.flag("-fno-builtin"); - cfg.flag("-fvisibility=hidden"); - cfg.flag("-ffreestanding"); - // Avoid the following warning appearing once **per file**: - // clang: warning: optimization flag '-fomit-frame-pointer' is not supported for target 'armv7' [-Wignored-optimization-argument] - // - // Note that compiler-rt's build system also checks - // - // `check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG)` - // - // in https://github.com/rust-lang/compiler-rt/blob/c8fbcb3/cmake/config-ix.cmake#L19. - cfg.flag_if_supported("-fomit-frame-pointer"); - cfg.define("VISIBILITY_HIDDEN", None); - } let mut sources = Sources::new(); sources.extend(&[ @@ -414,6 +385,48 @@ mod c { sources.remove(&["__aeabi_cdcmp", "__aeabi_cfcmp"]); } + sources + } +} + +#[cfg(feature = "c-vendor")] +mod c_vendor { + extern crate cc; + + use sources; + use std::env; + use std::path::PathBuf; + + /// Compile intrinsics from the compiler-rt C source code + pub fn compile(llvm_target: &[&str]) { + let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); + let cfg = &mut cc::Build::new(); + cfg.warnings(false); + + if target_env == "msvc" { + // Don't pull in extra libraries on MSVC + cfg.flag("/Zl"); + + // Emulate C99 and C++11's __func__ for MSVC prior to 2013 CTP + cfg.define("__func__", Some("__FUNCTION__")); + } else { + // Turn off various features of gcc and such, mostly copying + // compiler-rt's build system already + cfg.flag("-fno-builtin"); + cfg.flag("-fvisibility=hidden"); + cfg.flag("-ffreestanding"); + // Avoid the following warning appearing once **per file**: + // clang: warning: optimization flag '-fomit-frame-pointer' is not supported for target 'armv7' [-Wignored-optimization-argument] + // + // Note that compiler-rt's build system also checks + // + // `check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG)` + // + // in https://github.com/rust-lang/compiler-rt/blob/c8fbcb3/cmake/config-ix.cmake#L19. + cfg.flag_if_supported("-fomit-frame-pointer"); + cfg.define("VISIBILITY_HIDDEN", None); + } + // When compiling the C code we require the user to tell us where the // source code is, and this is largely done so when we're compiling as // part of rust-lang/rust we can use the same llvm-project repository as @@ -431,6 +444,7 @@ mod c { // use of that macro in lib/builtins/int_util.h in compiler-rt. cfg.flag_if_supported(&format!("-ffile-prefix-map={}=.", root.display())); + let sources = sources::get_sources(llvm_target); let src_dir = root.join("lib/builtins"); for (sym, src) in sources.map.iter() { let src = src_dir.join(src); @@ -442,3 +456,166 @@ mod c { cfg.compile("libcompiler-rt.a"); } } + +#[cfg(feature = "c-system")] +mod c_system { + extern crate ar; + + use std::collections::HashMap; + use std::env; + use std::fs::File; + use std::path::{Path, PathBuf}; + use std::process::{Command, Output}; + use std::str; + + use sources; + + fn success_output(err: &str, cmd: &mut Command) -> Output { + let output = cmd.output().expect(err); + let status = output.status; + if !status.success() { + panic!("{}: {:?}", err, status.code()); + } + output + } + + // This can be obtained by adding the line: + // message(STATUS "All builtin supported architectures: ${ALL_BUILTIN_SUPPORTED_ARCH}") + // to the bottom of compiler-rt/cmake/builtin-config-ix.cmake, then running + // cmake and looking at the output. + const ALL_SUPPORTED_ARCHES : &'static str = "i386;x86_64;arm;armhf;armv6m;armv7m;armv7em;armv7;armv7s;armv7k;aarch64;hexagon;mips;mipsel;mips64;mips64el;powerpc64;powerpc64le;riscv32;riscv64;wasm32;wasm64"; + + // This function recreates the logic of getArchNameForCompilerRTLib, + // defined in clang/lib/Driver/ToolChain.cpp. + fn get_arch_name_for_compiler_rtlib() -> String { + let target = env::var("TARGET").unwrap(); + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); + let r = match target_arch.as_str() { + "arm" => { + if target.ends_with("eabihf") && target_os != "windows" { + "armhf" + } else { + "arm" + } + } + "x86" => { + if target_os == "android" { + "i686" + } else { + "i386" + } + } + _ => target_arch.as_str(), + }; + r.to_string() + } + + fn find_library(dirs: I, libname: &str) -> Result> + where + I: Iterator, + { + let mut paths = Vec::new(); + for dir in dirs { + let try_path = dir.join(format!("lib{}.a", libname)); + if try_path.exists() { + return Ok(try_path.to_path_buf()); + } else { + paths.push(format!("{:?}", try_path)) + } + } + Err(paths) + } + + /// Link against system clang runtime libraries + pub fn compile(llvm_target: &[&str]) { + let target = env::var("TARGET").unwrap(); + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); + let compiler_rt_arch = get_arch_name_for_compiler_rtlib(); + let out_dir = env::var("OUT_DIR").unwrap(); + + if ALL_SUPPORTED_ARCHES + .split(";") + .find(|x| *x == compiler_rt_arch) + == None + { + return; + } + + println!("cargo:rerun-if-env-changed=CLANG"); + println!("cargo:rerun-if-env-changed=LLVM_CONFIG"); + + let fullpath = if let Ok(clang) = env::var("CLANG") { + let output = success_output( + "failed to find clang's compiler-rt", + Command::new(clang) + .arg(format!("--target={}", target)) + .arg("--rtlib=compiler-rt") + .arg("--print-libgcc-file-name"), + ); + let path = str::from_utf8(&output.stdout).unwrap().trim_end(); + Path::new(path).to_path_buf() + } else if let Ok(llvm_config) = env::var("LLVM_CONFIG") { + // fallback if clang is not installed + let (subpath, libname) = match target_os.as_str() { + "linux" => ("linux", format!("clang_rt.builtins-{}", &compiler_rt_arch)), + "macos" => ("darwin", "clang_rt.builtins_osx_dynamic".to_string()), + _ => panic!("unsupported target os: {}", target_os), + }; + let output = success_output( + "failed to find llvm-config's lib dir", + Command::new(llvm_config).arg("--libdir"), + ); + let libdir = str::from_utf8(&output.stdout).unwrap().trim_end(); + let paths = std::fs::read_dir(Path::new(libdir).join("clang")) + .unwrap() + .map(|e| e.unwrap().path().join("lib").join(subpath)); + match find_library(paths, &libname) { + Ok(p) => p, + Err(paths) => panic!( + "failed to find llvm-config's compiler-rt: {}", + paths.join(":") + ), + } + } else { + panic!("neither CLANG nor LLVM_CONFIG could be read"); + }; + + let mut index = 0; + let mut files = HashMap::new(); + let mut orig = ar::Archive::new(File::open(&fullpath).unwrap()); + while let Some(entry_result) = orig.next_entry() { + let entry = entry_result.unwrap(); + let name = str::from_utf8(entry.header().identifier()).unwrap(); + files.insert(name.to_owned(), index); + index += 1; + } + + let sources = sources::get_sources(llvm_target); + let mut new = + ar::Builder::new(File::create(Path::new(&out_dir).join("libcompiler-rt.a")).unwrap()); + for (sym, _src) in sources.map.iter() { + let &i = { + let sym_ = if sym.starts_with("__") { + &sym[2..] + } else { + &sym + }; + match files.get(&format!("{}.c.o", sym_)) { + Some(i) => i, + None => match files.get(&format!("{}.S.o", sym_)) { + Some(i) => i, + None => panic!("could not find expected symbol {} in {:?}", sym, &fullpath), + }, + } + }; + let mut entry = orig.jump_to_entry(i).unwrap(); + // TODO: ar really should have an append_entry to avoid the clone + new.append(&entry.header().clone(), &mut entry).unwrap(); + println!("cargo:rustc-cfg={}=\"optimized-c\"", sym); + } + + println!("cargo:rustc-link-search=native={}", out_dir); + println!("cargo:rustc-link-lib=static={}", "compiler-rt"); + } +} diff --git a/ci/docker/aarch64-unknown-linux-gnu/Dockerfile b/ci/docker/aarch64-unknown-linux-gnu/Dockerfile index 9e2559f4..8d031d18 100644 --- a/ci/docker/aarch64-unknown-linux-gnu/Dockerfile +++ b/ci/docker/aarch64-unknown-linux-gnu/Dockerfile @@ -1,10 +1,24 @@ FROM ubuntu:18.04 -RUN apt-get update && \ +RUN dpkg --add-architecture arm64 && \ + sed -e "\,security.ubuntu.com/ubuntu,p;s,security.ubuntu.com/ubuntu,ports.ubuntu.com/ubuntu-ports,g" \ + -e "\,archive.ubuntu.com/ubuntu,p;s,archive.ubuntu.com/ubuntu,ports.ubuntu.com/ubuntu-ports,g" \ + -i /etc/apt/sources.list && \ + sed -e "\,security.ubuntu.com/ubuntu,s.^deb .deb [ arch=amd64,i386 ] .g" \ + -e "\,archive.ubuntu.com/ubuntu,s.^deb .deb [ arch=amd64,i386 ] .g" \ + -e "\,ports.ubuntu.com/ubuntu-ports,s.^deb .deb [ arch=arm64 ] .g" \ + -i /etc/apt/sources.list && \ + apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ gcc-aarch64-linux-gnu libc6-dev-arm64-cross \ - qemu-user-static + clang xz-utils \ + qemu-user-static && \ + cd /tmp && \ + apt-get download libclang-common-6.0-dev:arm64 && \ + ar xf libclang-common-6.0-dev_*_arm64.deb && \ + tar -C/ -xf data.tar.xz ."$(dirname $(clang -rtlib=compiler-rt --print-libgcc-file-name))" ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-aarch64-static \ QEMU_LD_PREFIX=/usr/aarch64-linux-gnu \ + CLANG=clang \ RUST_TEST_THREADS=1 diff --git a/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile b/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile index 6617af15..478b57a5 100644 --- a/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile +++ b/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile @@ -1,9 +1,24 @@ FROM ubuntu:18.04 -RUN apt-get update && \ +RUN dpkg --add-architecture armhf && \ + sed -e "\,security.ubuntu.com/ubuntu,p;s,security.ubuntu.com/ubuntu,ports.ubuntu.com/ubuntu-ports,g" \ + -e "\,archive.ubuntu.com/ubuntu,p;s,archive.ubuntu.com/ubuntu,ports.ubuntu.com/ubuntu-ports,g" \ + -i /etc/apt/sources.list && \ + sed -e "\,security.ubuntu.com/ubuntu,s.^deb .deb [ arch=amd64,i386 ] .g" \ + -e "\,archive.ubuntu.com/ubuntu,s.^deb .deb [ arch=amd64,i386 ] .g" \ + -e "\,ports.ubuntu.com/ubuntu-ports,s.^deb .deb [ arch=armhf ] .g" \ + -i /etc/apt/sources.list && \ + apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev ca-certificates \ - gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user-static + gcc-arm-linux-gnueabihf libc6-dev-armhf-cross \ + clang xz-utils \ + qemu-user-static && \ + cd /tmp && \ + apt-get download libclang-common-6.0-dev:armhf && \ + ar xf libclang-common-6.0-dev_*_armhf.deb && \ + tar -C/ -xf data.tar.xz ."$(dirname $(clang -rtlib=compiler-rt --print-libgcc-file-name))" ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm-static \ QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ + CLANG=clang \ RUST_TEST_THREADS=1 diff --git a/ci/docker/i686-unknown-linux-gnu/Dockerfile b/ci/docker/i686-unknown-linux-gnu/Dockerfile index 5783e28e..9e3daab2 100644 --- a/ci/docker/i686-unknown-linux-gnu/Dockerfile +++ b/ci/docker/i686-unknown-linux-gnu/Dockerfile @@ -1,4 +1,6 @@ FROM ubuntu:18.04 RUN apt-get update && \ apt-get install -y --no-install-recommends \ - gcc-multilib libc6-dev ca-certificates + gcc-multilib libc6-dev ca-certificates \ + clang +ENV CLANG=clang diff --git a/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile b/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile index 63ea9af9..9069eba7 100644 --- a/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile +++ b/ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile @@ -1,13 +1,27 @@ FROM ubuntu:18.04 -RUN apt-get update && \ +RUN dpkg --add-architecture ppc64el && \ + sed -e "\,security.ubuntu.com/ubuntu,p;s,security.ubuntu.com/ubuntu,ports.ubuntu.com/ubuntu-ports,g" \ + -e "\,archive.ubuntu.com/ubuntu,p;s,archive.ubuntu.com/ubuntu,ports.ubuntu.com/ubuntu-ports,g" \ + -i /etc/apt/sources.list && \ + sed -e "\,security.ubuntu.com/ubuntu,s.^deb .deb [ arch=amd64,i386 ] .g" \ + -e "\,archive.ubuntu.com/ubuntu,s.^deb .deb [ arch=amd64,i386 ] .g" \ + -e "\,ports.ubuntu.com/ubuntu-ports,s.^deb .deb [ arch=ppc64el ] .g" \ + -i /etc/apt/sources.list && \ + apt-get update && \ apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user-static ca-certificates \ + gcc libc6-dev ca-certificates \ gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross \ - qemu-system-ppc + clang xz-utils \ + qemu-user-static qemu-system-ppc && \ + cd /tmp && \ + apt-get download libclang-common-6.0-dev:ppc64el && \ + ar xf libclang-common-6.0-dev_*_ppc64el.deb && \ + tar -C/ -xf data.tar.xz ."$(dirname $(clang -rtlib=compiler-rt --print-libgcc-file-name))" ENV CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_LINKER=powerpc64le-linux-gnu-gcc \ CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64le-static \ QEMU_CPU=POWER8 \ QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu \ + CLANG=clang \ RUST_TEST_THREADS=1 diff --git a/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/ci/docker/x86_64-unknown-linux-gnu/Dockerfile index 98000f4e..27d3b78d 100644 --- a/ci/docker/x86_64-unknown-linux-gnu/Dockerfile +++ b/ci/docker/x86_64-unknown-linux-gnu/Dockerfile @@ -1,4 +1,4 @@ FROM ubuntu:18.04 RUN apt-get update && \ apt-get install -y --no-install-recommends \ - gcc libc6-dev ca-certificates + gcc libc6-dev ca-certificates clang diff --git a/ci/run.sh b/ci/run.sh index ae32806e..254f8da6 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -18,6 +18,10 @@ cargo build --target $1 cargo build --target $1 --release cargo build --target $1 --features c cargo build --target $1 --release --features c +if [ -n "$CLANG" -o -n "$LLVM_CONFIG" ]; then +cargo build --target $1 --features c-system +cargo build --target $1 --release --features c-system +fi PREFIX=$(echo $1 | sed -e 's/unknown-//')- case $1 in @@ -77,6 +81,10 @@ RUSTFLAGS="-C debug-assertions=no" $build_intrinsics RUSTFLAGS="-C debug-assertions=no" $build_intrinsics --release RUSTFLAGS="-C debug-assertions=no" $build_intrinsics --features c RUSTFLAGS="-C debug-assertions=no" $build_intrinsics --features c --release +if [ -n "$CLANG" -o -n "$LLVM_CONFIG" ]; then +RUSTFLAGS="-C debug-assertions=no" $build_intrinsics --features c-system +RUSTFLAGS="-C debug-assertions=no" $build_intrinsics --features c-system --release +fi # Verify that there are no undefined symbols to `panic` within our # implementations diff --git a/testcrate/Cargo.toml b/testcrate/Cargo.toml index 3b99b574..d86f551d 100644 --- a/testcrate/Cargo.toml +++ b/testcrate/Cargo.toml @@ -23,6 +23,8 @@ utest-macros = { git = "https://github.com/japaric/utest" } [features] c = ["compiler_builtins/c"] +c-vendor = ["compiler_builtins/c-vendor"] +c-system = ["compiler_builtins/c-system"] mem = ["compiler_builtins/mem"] mangled-names = ["compiler_builtins/mangled-names"] default = ["mangled-names"]