Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libunwind fix and cleanup #84124

Merged
merged 1 commit into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,14 @@ changelog-seen = 2

# Use LLVM libunwind as the implementation for Rust's unwinder.
# Accepted values are 'in-tree' (formerly true), 'system' or 'no' (formerly false).
# This option only applies for Linux and Fuchsia targets.
# On Linux target, if crt-static is not enabled, 'no' means dynamic link to
# `libgcc_s.so`, 'in-tree' means static link to the in-tree build of llvm libunwind
# and 'system' means dynamic link to `libunwind.so`. If crt-static is enabled,
# the behavior is depend on the libc. On musl target, 'no' and 'in-tree' both
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does musl support linking with static libgcc at all?
If it does, then linking to libunwind when llvm-libunwind = 'no' is specified doesn't look right, perhaps rustbuild should have different defaults for musl instead.

I don't suggest doing it in this PR, but could you make an issue for this (if musl does support linking with static libgcc)?
Otherwise adding the fact that musl doesn't support linking with static libgcc to the docs should be enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does musl support linking with static libgcc at all?

musl support linking with libgcc_eh.a

If it does, then linking to libunwind when llvm-libunwind = 'no' is specified doesn't look right, perhaps rustbuild should have different defaults for musl instead.

This is the default behavior for musl target since libunwind crate was added. Maybe rust-lang/compiler-team#422 would fix this situation.

# means static link to the in-tree build of llvm libunwind, and 'system' means
# static link to `libunwind.a` provided by system. Due to the limitation of glibc,
# it must link to `libgcc_eh.a` to get a working output, and this option have no effect.
#llvm-libunwind = 'no'

# Enable Windows Control Flow Guard checks in the standard library.
Expand Down
7 changes: 7 additions & 0 deletions library/unwind/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,12 @@ cfg-if = "0.1.8"
cc = "1.0.67"

[features]

# Only applies for Linux and Fuchsia targets
# Static link to the in-tree build of llvm libunwind
llvm-libunwind = []

# Only applies for Linux and Fuchsia targets
# If crt-static is enabled, static link to `libunwind.a` provided by system
# If crt-static is disabled, dynamic link to `libunwind.so` provided by system
system-llvm-libunwind = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So llvm-libunwind and system-llvm-libunwind are actually incompatible, what the...
I always assumed that the later "elaborates" for former and tells which exactly libunwind is used if it's indeed used.
Such is life with cargo features.

146 changes: 74 additions & 72 deletions library/unwind/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ fn main() {
println!("cargo:rerun-if-changed=build.rs");
let target = env::var("TARGET").expect("TARGET was not set");

if cfg!(feature = "system-llvm-libunwind") {
if cfg!(target_os = "linux") && cfg!(feature = "system-llvm-libunwind") {
// linking for Linux is handled in lib.rs
return;
}

Expand Down Expand Up @@ -57,101 +58,102 @@ mod llvm_libunwind {
pub fn compile() {
let target = env::var("TARGET").expect("TARGET was not set");
let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap();
let target_endian_little = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap() != "big";
let cfg = &mut cc::Build::new();

cfg.cpp(true);
cfg.cpp_set_stdlib(None);
cfg.warnings(false);
let mut cc_cfg = cc::Build::new();
let mut cpp_cfg = cc::Build::new();
let root = Path::new("../../src/llvm-project/libunwind");

// libunwind expects a __LITTLE_ENDIAN__ macro to be set for LE archs, cf. #65765
if target_endian_little {
cfg.define("__LITTLE_ENDIAN__", Some("1"));
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_env == "msvc" {
// Don't pull in extra libraries on MSVC
cfg.flag("/Zl");
cfg.flag("/EHsc");
cfg.define("_CRT_SECURE_NO_WARNINGS", None);
cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
} else if target.contains("x86_64-fortanix-unknown-sgx") {
cfg.cpp(false);

cfg.static_flag(true);
cfg.opt_level(3);

cfg.flag("-nostdinc++");
cfg.flag("-fno-exceptions");
cfg.flag("-fno-rtti");
cfg.flag("-fstrict-aliasing");
cfg.flag("-funwind-tables");
cfg.flag("-fvisibility=hidden");
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.flag_if_supported("-fvisibility-global-new-delete-hidden");
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);
}
}

cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
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);
} else {
cfg.flag("-std=c99");
cfg.flag("-std=c++11");
cfg.flag("-nostdinc++");
cfg.flag("-fno-exceptions");
cfg.flag("-fno-rtti");
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.flag_if_supported("-fvisibility-global-new-delete-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 unwind_sources = vec![
"Unwind-EHABI.cpp",
"Unwind-seh.cpp",
let mut c_sources = vec![
"Unwind-sjlj.c",
"UnwindLevel1-gcc-ext.c",
"UnwindLevel1.c",
"UnwindRegistersRestore.S",
"UnwindRegistersSave.S",
"libunwind.cpp",
];

if target_vendor == "apple" {
unwind_sources.push("Unwind_AppleExtras.cpp");
}
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") {
unwind_sources.push("UnwindRustSgx.c");
c_sources.push("UnwindRustSgx.c");
}

let root = Path::new("../../src/llvm-project/libunwind");
cfg.include(root.join("include"));
for src in unwind_sources {
cfg.file(root.join("src").join(src));
for src in c_sources {
cc_cfg.file(root.join("src").join(src).canonicalize().unwrap());
}

if target_env == "musl" {
// use the same C compiler command to compile C++ code so we do not need to setup the
// C++ compiler env variables on the builders
cfg.cpp(false);
// linking for musl is handled in lib.rs
cfg.cargo_metadata(false);
println!("cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap());
for src in cpp_sources {
cpp_cfg.file(root.join("src").join(src).canonicalize().unwrap());
}

cfg.compile("unwind");
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" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, great, so there is a way to get *.o files from the cc crate.

cc_cfg.object(&obj);
count += 1;
}
}
}
assert_eq!(cpp_len, count, "Can't get object files from {:?}", &out_dir);
cc_cfg.compile("unwind");
}
}
21 changes: 17 additions & 4 deletions library/unwind/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,22 @@ cfg_if::cfg_if! {
}

#[cfg(target_env = "musl")]
#[link(name = "unwind", kind = "static", cfg(target_feature = "crt-static"))]
#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))]
extern "C" {}
cfg_if::cfg_if! {
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")]
extern "C" {}
} else if #[cfg(feature = "system-llvm-libunwind")] {
#[link(name = "unwind", kind = "static-nobundle", 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 = "gcc_s", cfg(not(target_feature = "crt-static")))]
extern "C" {}
}
}

// When building with crt-static, we get `gcc_eh` from the `libc` crate, since
// glibc needs it, and needs it listed later on the linker command line. We
Expand Down Expand Up @@ -68,5 +81,5 @@ extern "C" {}
extern "C" {}

#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
#[link(name = "unwind", kind = "static-nobundle")]
#[link(name = "unwind", kind = "static")]
extern "C" {}