From: 12101111 Date: Mon, 26 Jul 2021 14:47:07 +0000 (+0800) Subject: build llvm libunwind.a in rustbuild X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=4c9896f279357896d31d811fddc2025d72259498;p=rust.git build llvm libunwind.a in rustbuild --- diff --git a/library/unwind/build.rs b/library/unwind/build.rs index 0529d24a274..1d0b4a59a28 100644 --- a/library/unwind/build.rs +++ b/library/unwind/build.rs @@ -4,36 +4,18 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); let target = env::var("TARGET").expect("TARGET was not set"); - if cfg!(target_os = "linux") && cfg!(feature = "system-llvm-libunwind") { - // linking for Linux is handled in lib.rs - return; - } - - if cfg!(feature = "llvm-libunwind") - && ((target.contains("linux") && !target.contains("musl")) || target.contains("fuchsia")) - { - // Build the unwinding from libunwind C/C++ source code. - llvm_libunwind::compile(); - } else if target.contains("x86_64-fortanix-unknown-sgx") { - llvm_libunwind::compile(); - } else if target.contains("linux") { - // linking for Linux is handled in lib.rs - if target.contains("musl") { - llvm_libunwind::compile(); - } else if target.contains("android") { - let build = cc::Build::new(); + if target.contains("android") { + let build = cc::Build::new(); - // Since ndk r23 beta 3 `libgcc` was replaced with `libunwind` thus - // check if we have `libunwind` available and if so use it. Otherwise - // fall back to `libgcc` to support older ndk versions. - let has_unwind = - build.is_flag_supported("-lunwind").expect("Unable to invoke compiler"); + // Since ndk r23 beta 3 `libgcc` was replaced with `libunwind` thus + // check if we have `libunwind` available and if so use it. Otherwise + // fall back to `libgcc` to support older ndk versions. + let has_unwind = build.is_flag_supported("-lunwind").expect("Unable to invoke compiler"); - if has_unwind { - println!("cargo:rustc-link-lib=unwind"); - } else { - println!("cargo:rustc-link-lib=gcc"); - } + if has_unwind { + println!("cargo:rustc-link-lib=unwind"); + } else { + println!("cargo:rustc-link-lib=gcc"); } } else if target.contains("freebsd") { println!("cargo:rustc-link-lib=gcc_s"); @@ -63,111 +45,3 @@ fn main() { // redox is handled in lib.rs } } - -mod llvm_libunwind { - use std::env; - use std::path::Path; - - /// Compile the libunwind C/C++ source code. - pub fn compile() { - let target = env::var("TARGET").expect("TARGET was not set"); - let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); - let mut cc_cfg = cc::Build::new(); - let mut cpp_cfg = cc::Build::new(); - let root = Path::new("../../src/llvm-project/libunwind"); - - cpp_cfg.cpp(true); - cpp_cfg.cpp_set_stdlib(None); - cpp_cfg.flag("-nostdinc++"); - cpp_cfg.flag("-fno-exceptions"); - cpp_cfg.flag("-fno-rtti"); - cpp_cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); - - // Don't set this for clang - // By default, Clang builds C code in GNU C17 mode. - // By default, Clang builds C++ code according to the C++98 standard, - // with many C++11 features accepted as extensions. - if cpp_cfg.get_compiler().is_like_gnu() { - cpp_cfg.flag("-std=c++11"); - cc_cfg.flag("-std=c99"); - } - - if target.contains("x86_64-fortanix-unknown-sgx") || target_env == "musl" { - // use the same GCC C compiler command to compile C++ code so we do not need to setup the - // C++ compiler env variables on the builders. - // Don't set this for clang++, as clang++ is able to compile this without libc++. - if cpp_cfg.get_compiler().is_like_gnu() { - cpp_cfg.cpp(false); - } - } - - for cfg in [&mut cc_cfg, &mut cpp_cfg].iter_mut() { - cfg.warnings(false); - cfg.flag("-fstrict-aliasing"); - cfg.flag("-funwind-tables"); - cfg.flag("-fvisibility=hidden"); - cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); - cfg.include(root.join("include")); - cfg.cargo_metadata(false); - - if target.contains("x86_64-fortanix-unknown-sgx") { - cfg.static_flag(true); - cfg.opt_level(3); - cfg.flag("-fno-stack-protector"); - cfg.flag("-ffreestanding"); - cfg.flag("-fexceptions"); - - // easiest way to undefine since no API available in cc::Build to undefine - cfg.flag("-U_FORTIFY_SOURCE"); - cfg.define("_FORTIFY_SOURCE", "0"); - cfg.define("RUST_SGX", "1"); - cfg.define("__NO_STRING_INLINES", None); - cfg.define("__NO_MATH_INLINES", None); - cfg.define("_LIBUNWIND_IS_BAREMETAL", None); - cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None); - cfg.define("NDEBUG", None); - } - } - - let mut c_sources = vec![ - "Unwind-sjlj.c", - "UnwindLevel1-gcc-ext.c", - "UnwindLevel1.c", - "UnwindRegistersRestore.S", - "UnwindRegistersSave.S", - ]; - - let cpp_sources = vec!["Unwind-EHABI.cpp", "Unwind-seh.cpp", "libunwind.cpp"]; - let cpp_len = cpp_sources.len(); - - if target.contains("x86_64-fortanix-unknown-sgx") { - c_sources.push("UnwindRustSgx.c"); - } - - for src in c_sources { - cc_cfg.file(root.join("src").join(src).canonicalize().unwrap()); - } - - for src in cpp_sources { - cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap()); - } - - let out_dir = env::var("OUT_DIR").unwrap(); - println!("cargo:rustc-link-search=native={}", &out_dir); - - cpp_cfg.compile("unwind-cpp"); - - let mut count = 0; - for entry in std::fs::read_dir(&out_dir).unwrap() { - let obj = entry.unwrap().path().canonicalize().unwrap(); - if let Some(ext) = obj.extension() { - if ext == "o" { - cc_cfg.object(&obj); - count += 1; - } - } - } - assert_eq!(cpp_len, count, "Can't get object files from {:?}", &out_dir); - cc_cfg.compile("unwind"); - } -} diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 53b13b9043b..06384b15926 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![unstable(feature = "panic_unwind", issue = "32837")] #![feature(link_cfg)] +#![feature(native_link_modifiers)] +#![feature(native_link_modifiers_bundle)] #![feature(nll)] #![feature(staged_api)] #![feature(static_nobundle)] @@ -42,14 +44,14 @@ if #[cfg(all(feature = "llvm-libunwind", feature = "system-llvm-libunwind"))] { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); } else if #[cfg(feature = "llvm-libunwind")] { - #[link(name = "unwind", kind = "static")] + #[link(name = "unwind", kind = "static", modifiers = "-bundle")] extern "C" {} } else if #[cfg(feature = "system-llvm-libunwind")] { - #[link(name = "unwind", kind = "static-nobundle", cfg(target_feature = "crt-static"))] + #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] extern "C" {} } else { - #[link(name = "unwind", kind = "static", cfg(target_feature = "crt-static"))] + #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] extern "C" {} } @@ -77,10 +79,10 @@ extern "C" {} #[cfg(target_os = "redox")] -#[link(name = "gcc_eh", kind = "static-nobundle", cfg(target_feature = "crt-static"))] +#[link(name = "gcc_eh", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] extern "C" {} #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] -#[link(name = "unwind", kind = "static")] +#[link(name = "unwind", kind = "static", modifiers = "-bundle")] extern "C" {} diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 196be74decb..5e15fe75a24 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -77,9 +77,18 @@ pub enum _Unwind_Context {} pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); + +// FIXME: The `#[link]` attributes on `extern "C"` block marks those symbols declared in +// the block are reexported in dylib build of libstd. This is needed when build rustc with +// feature `llvm-libunwind', as no other cdylib will provided those _Unwind_* symbols. +// However the `link` attribute is duplicated multiple times and does not just export symbol, +// a better way to manually export symbol would be another attribute like `#[export]`. +// See the logic in function rustc_codegen_ssa::src::back::exported_symbols, module +// rustc_codegen_ssa::src::back::symbol_export, rustc_middle::middle::exported_symbols +// and RFC 2841 #[cfg_attr( all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")), - link(name = "unwind", kind = "static") + link(name = "unwind", kind = "static", modifiers = "-bundle") )] extern "C-unwind" { pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; @@ -106,9 +115,10 @@ pub enum _Unwind_Action { } pub use _Unwind_Action::*; - #[cfg_attr(all(feature = "llvm-libunwind", - any(target_os = "fuchsia", target_os = "linux")), - link(name = "unwind", kind = "static"))] + #[cfg_attr( + all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")), + link(name = "unwind", kind = "static", modifiers = "-bundle") + )] extern "C" { pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word; pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word); @@ -163,9 +173,10 @@ enum _Unwind_VRS_DataRepresentation { pub const UNWIND_SP_REG: c_int = 13; pub const UNWIND_IP_REG: c_int = 15; - #[cfg_attr(all(feature = "llvm-libunwind", - any(target_os = "fuchsia", target_os = "linux")), - link(name = "unwind", kind = "static"))] + #[cfg_attr( + all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")), + link(name = "unwind", kind = "static", modifiers = "-bundle") + )] extern "C" { fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, regclass: _Unwind_VRS_RegClass, @@ -228,9 +239,10 @@ pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void { cfg_if::cfg_if! { if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] { // Not 32-bit iOS - #[cfg_attr(all(feature = "llvm-libunwind", - any(target_os = "fuchsia", target_os = "linux")), - link(name = "unwind", kind = "static"))] + #[cfg_attr( + all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")), + link(name = "unwind", kind = "static", modifiers = "-bundle") + )] extern "C-unwind" { pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code; } @@ -241,9 +253,6 @@ pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, } } else { // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace() - #[cfg_attr(all(feature = "llvm-libunwind", - any(target_os = "fuchsia", target_os = "linux")), - link(name = "unwind", kind = "static"))] extern "C-unwind" { pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code; } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index d2598995478..afddbc1da9e 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -23,7 +23,7 @@ use crate::builder::Cargo; use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; -use crate::config::TargetSelection; +use crate::config::{LlvmLibunwind, TargetSelection}; use crate::dist; use crate::native; use crate::tool::SourceType; @@ -234,6 +234,18 @@ fn copy_self_contained_objects( } } + if target.contains("musl") + || target.contains("x86_64-fortanix-unknown-sgx") + || builder.config.llvm_libunwind == LlvmLibunwind::InTree + && (target.contains("linux") || target.contains("fuchsia")) + { + let libunwind_path = builder.ensure(native::Libunwind { target }); + let libunwind_source = libunwind_path.join("libunwind.a"); + let libunwind_target = libdir_self_contained.join("libunwind.a"); + builder.copy(&libunwind_source, &libunwind_target); + target_deps.push((libunwind_target, DependencyType::TargetSelfContained)); + } + target_deps } diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 2172b01706d..0a23d4fff6b 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -10,7 +10,7 @@ use std::env; use std::env::consts::EXE_EXTENSION; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::fs::{self, File}; use std::io; use std::path::{Path, PathBuf}; @@ -952,3 +952,154 @@ fn run(self, builder: &Builder<'_>) -> Self::Output { out_dir } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Libunwind { + pub target: TargetSelection, +} + +impl Step for Libunwind { + type Output = PathBuf; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/llvm-project/libunwind") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Libunwind { target: run.target }); + } + + /// Build linunwind.a + fn run(self, builder: &Builder<'_>) -> Self::Output { + if builder.config.dry_run { + return PathBuf::new(); + } + + let out_dir = builder.native_dir(self.target).join("libunwind"); + let root = builder.src.join("src/llvm-project/libunwind"); + + if up_to_date(&root, &out_dir.join("libunwind.a")) { + return out_dir; + } + + builder.info(&format!("Building libunwind.a for {}", self.target.triple)); + t!(fs::create_dir_all(&out_dir)); + + let mut cc_cfg = cc::Build::new(); + let mut cpp_cfg = cc::Build::new(); + + cpp_cfg.cpp(true); + cpp_cfg.cpp_set_stdlib(None); + cpp_cfg.flag("-nostdinc++"); + cpp_cfg.flag("-fno-exceptions"); + cpp_cfg.flag("-fno-rtti"); + cpp_cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); + + for cfg in [&mut cc_cfg, &mut cpp_cfg].iter_mut() { + if let Some(ar) = builder.ar(self.target) { + cfg.archiver(ar); + } + cfg.target(&self.target.triple); + cfg.host(&builder.config.build.triple); + cfg.warnings(false); + cfg.debug(false); + // get_compiler() need set opt_level first. + cfg.opt_level(3); + cfg.flag("-fstrict-aliasing"); + cfg.flag("-funwind-tables"); + cfg.flag("-fvisibility=hidden"); + cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); + cfg.include(root.join("include")); + cfg.cargo_metadata(false); + cfg.out_dir(&out_dir); + + if self.target.contains("x86_64-fortanix-unknown-sgx") { + cfg.static_flag(true); + cfg.flag("-fno-stack-protector"); + cfg.flag("-ffreestanding"); + cfg.flag("-fexceptions"); + + // easiest way to undefine since no API available in cc::Build to undefine + cfg.flag("-U_FORTIFY_SOURCE"); + cfg.define("_FORTIFY_SOURCE", "0"); + cfg.define("RUST_SGX", "1"); + cfg.define("__NO_STRING_INLINES", None); + cfg.define("__NO_MATH_INLINES", None); + cfg.define("_LIBUNWIND_IS_BAREMETAL", None); + cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None); + cfg.define("NDEBUG", None); + } + } + + cc_cfg.compiler(builder.cc(self.target)); + if let Ok(cxx) = builder.cxx(self.target) { + cpp_cfg.compiler(cxx); + } else { + cc_cfg.compiler(builder.cc(self.target)); + } + + // Don't set this for clang + // By default, Clang builds C code in GNU C17 mode. + // By default, Clang builds C++ code according to the C++98 standard, + // with many C++11 features accepted as extensions. + if cc_cfg.get_compiler().is_like_gnu() { + cc_cfg.flag("-std=c99"); + } + if cpp_cfg.get_compiler().is_like_gnu() { + cpp_cfg.flag("-std=c++11"); + } + + if self.target.contains("x86_64-fortanix-unknown-sgx") || self.target.contains("musl") { + // use the same GCC C compiler command to compile C++ code so we do not need to setup the + // C++ compiler env variables on the builders. + // Don't set this for clang++, as clang++ is able to compile this without libc++. + if cpp_cfg.get_compiler().is_like_gnu() { + cpp_cfg.cpp(false); + cpp_cfg.compiler(builder.cc(self.target)); + } + } + + let mut c_sources = vec![ + "Unwind-sjlj.c", + "UnwindLevel1-gcc-ext.c", + "UnwindLevel1.c", + "UnwindRegistersRestore.S", + "UnwindRegistersSave.S", + ]; + + let cpp_sources = vec!["Unwind-EHABI.cpp", "Unwind-seh.cpp", "libunwind.cpp"]; + let cpp_len = cpp_sources.len(); + + if self.target.contains("x86_64-fortanix-unknown-sgx") { + c_sources.push("UnwindRustSgx.c"); + } + + for src in c_sources { + cc_cfg.file(root.join("src").join(src).canonicalize().unwrap()); + } + + for src in &cpp_sources { + cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap()); + } + + cpp_cfg.compile("unwind-cpp"); + + // FIXME: https://github.com/alexcrichton/cc-rs/issues/545#issuecomment-679242845 + let mut count = 0; + for entry in fs::read_dir(&out_dir).unwrap() { + let file = entry.unwrap().path().canonicalize().unwrap(); + if file.is_file() && file.extension() == Some(OsStr::new("o")) { + // file name starts with "Unwind-EHABI", "Unwind-seh" or "libunwind" + let file_name = file.file_name().unwrap().to_str().expect("UTF-8 file name"); + if cpp_sources.iter().any(|f| file_name.starts_with(&f[..f.len() - 4])) { + cc_cfg.object(&file); + count += 1; + } + } + } + assert_eq!(cpp_len, count, "Can't get object files from {:?}", &out_dir); + + cc_cfg.compile("unwind"); + out_dir + } +}