From 52a33655be8f2afc1ac666d24b5850badb23b3e7 Mon Sep 17 00:00:00 2001
From: 12101111 <w12101111@gmail.com>
Date: Mon, 24 May 2021 17:53:09 +0800
Subject: [PATCH] cleanup and fix compiling of libunwind

fix conditional compiling of llvm-libunwind feaure for musl target.
update document of llvm-libunwind feature.
---
 config.toml.example       |   8 +++
 library/unwind/Cargo.toml |   7 ++
 library/unwind/build.rs   | 144 +++++++++++++++++++-------------------
 library/unwind/src/lib.rs |  21 ++++--
 4 files changed, 105 insertions(+), 75 deletions(-)

diff --git a/config.toml.example b/config.toml.example
index 16952a5ced8..df2fb448b7d 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -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 
+# 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.
diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml
index f42ded62585..9273ad0c3a4 100644
--- a/library/unwind/Cargo.toml
+++ b/library/unwind/Cargo.toml
@@ -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 = []
diff --git a/library/unwind/build.rs b/library/unwind/build.rs
index d8bf152e4d6..96df3fc5ac4 100644
--- a/library/unwind/build.rs
+++ b/library/unwind/build.rs
@@ -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;
     }
 
@@ -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();
+        let mut cc_cfg = cc::Build::new();
+        let mut cpp_cfg = cc::Build::new();
+        let root = Path::new("../../src/llvm-project/libunwind");
 
-        cfg.cpp(true);
-        cfg.cpp_set_stdlib(None);
-        cfg.warnings(false);
+        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");
 
-        // libunwind expects a __LITTLE_ENDIAN__ macro to be set for LE archs, cf. #65765
-        if target_endian_little {
-            cfg.define("__LITTLE_ENDIAN__", Some("1"));
+        // 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);
+        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.static_flag(true);
-            cfg.opt_level(3);
-
-            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("-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");
-
-            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");
-            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" {
+                    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 be5e56c71e3..eaeec72fbb5 100644
--- a/library/unwind/src/lib.rs
+++ b/library/unwind/src/lib.rs
@@ -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
@@ -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" {}