From 212ea02a0743a3bd4fcab1288682d3d780da960b Mon Sep 17 00:00:00 2001
From: Bjorn Neergaard <bjorn@neersighted.com>
Date: Sat, 9 Feb 2019 19:39:23 +0000
Subject: [PATCH] Fix cross-compiling i686-pc-windows-gnu from Linux

This is still very rough and serves as a proof-of-concept for fixing
Linux -> 32-bit MinGW cross compilation workflow. Currently, clang and
GCC's MinGW targets both only support DW2 (DWARF) or SJLJ (Set Jump Long
Jump) unwinding on 32-bit Windows.

The default for GCC (and the way it is shipped on every major distro) is
to use SJLJ on Windows, as DWARF cannot traverse non-DWARF frames. This
would work fine, except for the fact that libgcc (our C runtime on the
MinGW platform) exports symbols under a different name when configured
to use SJLJ-style unwinding, and uses a preprocessor macro internally to
alias them.

Because of this, we have to detect this scenario and link to the correct
symbols ourselves. Linking has been tested with a full bootstrap on both
x86_64-unknown-linux-gnu and i686-pc-windows-gnu, as well as
cross-compilation of some of my own projects.

Obviously, the detection is a bit unrefined. Right now we
unconditionally use SJLJ when compiling Linux -> MinGW. I'd like to add
feature detection using compiler build flags or autotools-style
compilation and object analysis. Input on the best way to proceed here
is welcome.

Also, currently there is copy-pasted/duplicated code in libunwind.
Ideally, this could be reduced, but this would likely require a
rethinking of how iOS is special-cased above, to avoid further
duplication. Input on how to best structure this file is requested.

diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 831053bc0f7..13ec5da3e3d 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -208,6 +208,11 @@ pub fn std_cargo(builder: &Builder<'_>,
         let mut features = builder.std_features();
         features.push_str(&compiler_builtins_c_feature);
 
+        // FIXME: Temporary detection of SJLJ MinGW compilers.
+        if builder.config.build.contains("linux") && target == "i686-pc-windows-gnu" {
+            features.push_str(" sjlj_eh");
+        }
+
         if compiler.stage != 0 && builder.config.sanitizers {
             // This variable is used by the sanitizer runtime crates, e.g.
             // rustc_lsan, to build the sanitizer runtime from C code
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml
index a22e162bbff..e1c8f164916 100644
--- a/src/libstd/Cargo.toml
+++ b/src/libstd/Cargo.toml
@@ -78,6 +78,7 @@ panic_immediate_abort = ["core/panic_immediate_abort"]
 # https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml
 std_detect_file_io = []
 std_detect_dlsym_getauxval = []
+sjlj_eh = ["unwind/sjlj_eh"]
 
 [package.metadata.fortanix-sgx]
 # Maximum possible number of threads when testing
diff --git a/src/libtest/Cargo.toml b/src/libtest/Cargo.toml
index 170fbb984cf..b1b0be71067 100644
--- a/src/libtest/Cargo.toml
+++ b/src/libtest/Cargo.toml
@@ -29,3 +29,4 @@ llvm-libunwind = ["std/llvm-libunwind"]
 panic-unwind = ["std/panic_unwind"]
 panic_immediate_abort = ["std/panic_immediate_abort"]
 profiler = ["std/profiler"]
+sjlj_eh = ["std/sjlj_eh"]
diff --git a/src/libunwind/Cargo.toml b/src/libunwind/Cargo.toml
index 77bcfffd506..9d9fc5fbff5 100644
--- a/src/libunwind/Cargo.toml
+++ b/src/libunwind/Cargo.toml
@@ -26,3 +26,4 @@ cc = { version = "1.0.1" }
 
 [features]
 llvm-libunwind = []
+sjlj_eh = []
diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs
index 0b39503c0d0..ec8681c2985 100644
--- a/src/libunwind/libunwind.rs
+++ b/src/libunwind/libunwind.rs
@@ -74,8 +74,8 @@ pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reaso
                any(target_os = "fuchsia", target_os = "linux")),
            link(name = "unwind", kind = "static"))]
 extern "C" {
-    #[unwind(allowed)]
-    pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
+    #[cfg_attr(stage0, unwind)]
+    #[cfg_attr(not(stage0), unwind(allowed))]
     pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
     pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void;
     pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
@@ -216,32 +216,50 @@ if #[cfg(all(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm
 } // cfg_if!
 
 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"))]
+if #[cfg(all(target_os = "ios", target_arch = "arm"))] {
+    // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace()
     extern "C" {
-        #[unwind(allowed)]
-        pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
+        #[cfg_attr(stage0, unwind)]
+        #[cfg_attr(not(stage0), unwind(allowed))]
+        pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
+        pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
+    }
+
+    #[inline]
+    pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code {
+        _Unwind_SjLj_RaiseException(exc)
+    }
+
+} else if #[cfg(feature = "sjlj_eh")] {
+    extern "C" {
+        #[cfg_attr(stage0, unwind)]
+        #[cfg_attr(not(stage0), unwind(allowed))]
+        pub fn _Unwind_SjLj_Resume(e: *mut _Unwind_Exception) -> !;
+        pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
         pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
                                  trace_argument: *mut c_void)
                                  -> _Unwind_Reason_Code;
     }
-} 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(allowed)]
-        pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
+
+    #[inline]
+    pub unsafe fn _Unwind_Resume(exc: *mut _Unwind_Exception) -> ! {
+        _Unwind_SjLj_Resume(exc)
     }
 
     #[inline]
     pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code {
         _Unwind_SjLj_RaiseException(exc)
     }
+} else {
+    extern "C" {
+        #[cfg_attr(stage0, unwind)]
+        #[cfg_attr(not(stage0), unwind(allowed))]
+        pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
+        pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
+        pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
+                                 trace_argument: *mut c_void)
+                                 -> _Unwind_Reason_Code;
+    }
 }
 } // cfg_if!
 
-- 
2.27.0

