about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-11-01 17:15:29 +0000
committerbors <bors@rust-lang.org>2015-11-01 17:15:29 +0000
commit71409184dc00604537ce03312dc17ba986c329d9 (patch)
treeca73303814b925bd964ae123c4a288defc506057 /src/libstd/sys
parent6d43fef3aaf64b2e7df14ca676a3a39b723ed7f3 (diff)
parent0332ee9f6336c17e8e8e7524cd2db1c6a92c1a30 (diff)
downloadrust-71409184dc00604537ce03312dc17ba986c329d9.tar.gz
rust-71409184dc00604537ce03312dc17ba986c329d9.zip
Auto merge of #29177 - vadimcn:rtstuff, r=alexcrichton
Note: for now, this change only affects `-windows-gnu` builds.

So why was this `libgcc` dylib dependency needed in the first place?
The stack unwinder needs to know about locations of unwind tables of all the modules loaded in the current process.  The easiest portable way of achieving this is to have each module register itself with the unwinder when loaded into the process.  All modules compiled by GCC do this by calling the __register_frame_info() in their startup code (that's `crtbegin.o` and `crtend.o`, which are automatically linked into any gcc output).
Another important piece is that there should be only one copy of the unwinder (and thus unwind tables registry) in the process.  This pretty much means that the unwinder must be in a shared library (unless everything is statically linked). 

Now, Rust compiler tries very hard to make sure that any given Rust crate appears in the final output just once.   So if we link the unwinder statically to one of Rust's crates, everything should be fine.

Unfortunately, GCC startup objects are built under assumption that `libgcc` is the one true place for the unwind info registry, so I couldn't find any better way than to replace them.  So out go `crtbegin`/`crtend`, in come `rsbegin`/`rsend`!  

A side benefit of this change is that rustc is now more in control of the command line that goes to the linker, so we could stop using `gcc` as the linker driver and just invoke `ld` directly.
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/common/libunwind.rs51
-rw-r--r--src/libstd/sys/common/unwind/gcc.rs33
-rw-r--r--src/libstd/sys/common/unwind/mod.rs41
-rw-r--r--src/libstd/sys/common/unwind/seh64_gnu.rs11
-rw-r--r--src/libstd/sys/windows/c.rs1
5 files changed, 82 insertions, 55 deletions
diff --git a/src/libstd/sys/common/libunwind.rs b/src/libstd/sys/common/libunwind.rs
index da7ebbf4ed3..75bb11216e1 100644
--- a/src/libstd/sys/common/libunwind.rs
+++ b/src/libstd/sys/common/libunwind.rs
@@ -99,35 +99,23 @@ pub type _Unwind_Exception_Cleanup_Fn =
         extern "C" fn(unwind_code: _Unwind_Reason_Code,
                       exception: *mut _Unwind_Exception);
 
-#[cfg(any(all(target_os = "linux", not(target_env = "musl")),
-          target_os = "freebsd"))]
-#[link(name = "gcc_s")]
-extern {}
-
-#[cfg(all(target_os = "linux", target_env = "musl", not(test)))]
-#[link(name = "unwind", kind = "static")]
-extern {}
-
-#[cfg(any(target_os = "android", target_os = "openbsd"))]
-#[link(name = "gcc")]
-extern {}
-
-#[cfg(all(target_os = "netbsd", not(target_vendor = "rumprun")))]
-#[link(name = "gcc")]
-extern {}
-
-#[cfg(all(target_os = "netbsd", target_vendor = "rumprun"))]
-#[link(name = "unwind")]
-extern {}
-
-#[cfg(target_os = "dragonfly")]
-#[link(name = "gcc_pic")]
-extern {}
-
-#[cfg(target_os = "bitrig")]
-#[link(name = "c++abi")]
-extern {}
-
+#[cfg_attr(any(all(target_os = "linux", not(target_env = "musl")),
+               target_os = "freebsd"),
+           link(name = "gcc_s"))]
+#[cfg_attr(all(target_os = "linux", target_env = "musl", not(test)),
+           link(name = "unwind", kind = "static"))]
+#[cfg_attr(any(target_os = "android", target_os = "openbsd"),
+           link(name = "gcc"))]
+#[cfg_attr(all(target_os = "netbsd", not(target_vendor = "rumprun")),
+           link(name = "gcc"))]
+#[cfg_attr(all(target_os = "netbsd", target_vendor = "rumprun"),
+           link(name = "unwind"))]
+#[cfg_attr(target_os = "dragonfly",
+           link(name = "gcc_pic"))]
+#[cfg_attr(target_os = "bitrig",
+           link(name = "c++abi"))]
+#[cfg_attr(all(target_os = "windows", target_env="gnu"),
+           link(name = "gcc_eh"))]
 extern "C" {
     // iOS on armv7 uses SjLj exceptions and requires to link
     // against corresponding routine (..._SjLj_...)
@@ -142,6 +130,11 @@ extern "C" {
                                    -> _Unwind_Reason_Code;
 
     pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
+
+    // remove cfg after new snapshot
+    #[cfg(not(all(stage0, target_os="windows", target_arch="x86_64")))]
+    #[unwind]
+    pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
 }
 
 // ... and now we just providing access to SjLj counterspart
diff --git a/src/libstd/sys/common/unwind/gcc.rs b/src/libstd/sys/common/unwind/gcc.rs
index 361cef08c11..0a598b55951 100644
--- a/src/libstd/sys/common/unwind/gcc.rs
+++ b/src/libstd/sys/common/unwind/gcc.rs
@@ -231,3 +231,36 @@ pub mod eabi {
         }
     }
 }
+
+// See docs in the `unwind` module.
+#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu", not(test)))]
+#[lang = "eh_unwind_resume"]
+#[unwind]
+unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
+    uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception);
+}
+
+#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
+pub mod eh_frame_registry {
+    // The implementation of stack unwinding is (for now) deferred to libgcc_eh, however Rust
+    // crates use these Rust-specific entry points to avoid potential clashes with GCC runtime.
+    // See also: rtbegin.rs, `unwind` module.
+
+    #[link(name = "gcc_eh")]
+    extern {
+        fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8);
+        fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8);
+    }
+    #[cfg(not(test))]
+    #[no_mangle]
+    pub unsafe extern fn rust_eh_register_frames(eh_frame_begin: *const u8,
+                                                 object: *mut u8) {
+        __register_frame_info(eh_frame_begin, object);
+    }
+    #[cfg(not(test))]
+    #[no_mangle]
+    pub  unsafe extern fn rust_eh_unregister_frames(eh_frame_begin: *const u8,
+                                                   object: *mut u8) {
+        __deregister_frame_info(eh_frame_begin, object);
+    }
+}
diff --git a/src/libstd/sys/common/unwind/mod.rs b/src/libstd/sys/common/unwind/mod.rs
index d87ab56d4e1..e455d163ed9 100644
--- a/src/libstd/sys/common/unwind/mod.rs
+++ b/src/libstd/sys/common/unwind/mod.rs
@@ -34,28 +34,35 @@
 //! object being thrown, and to decide whether it should be caught at that stack
 //! frame.  Once the handler frame has been identified, cleanup phase begins.
 //!
-//! In the cleanup phase, personality routines invoke cleanup code associated
-//! with their stack frames (i.e. destructors).  Once stack has been unwound down
-//! to the handler frame level, unwinding stops and the last personality routine
-//! transfers control to its catch block.
+//! In the cleanup phase, the unwinder invokes each personality routine again.
+//! This time it decides which (if any) cleanup code needs to be run for
+//! the current stack frame.  If so, the control is transferred to a special branch
+//! in the function body, the "landing pad", which invokes destructors, frees memory,
+//! etc.  At the end of the landing pad, control is transferred back to the unwinder
+//! and unwinding resumes.
 //!
-//! ## Frame unwind info registration
+//! Once stack has been unwound down to the handler frame level, unwinding stops
+//! and the last personality routine transfers control to the catch block.
 //!
-//! Each module has its own frame unwind info section (usually ".eh_frame"), and
-//! unwinder needs to know about all of them in order for unwinding to be able to
-//! cross module boundaries.
+//! ## `eh_personality` and `eh_unwind_resume`
 //!
-//! On some platforms, like Linux, this is achieved by dynamically enumerating
-//! currently loaded modules via the dl_iterate_phdr() API and finding all
-//! .eh_frame sections.
+//! These language items are used by the compiler when generating unwind info.
+//! The first one is the personality routine described above.  The second one
+//! allows compilation target to customize the process of resuming unwind at the
+//! end of the landing pads.  `eh_unwind_resume` is used only if `custom_unwind_resume`
+//! flag in the target options is set.
 //!
-//! Others, like Windows, require modules to actively register their unwind info
-//! sections by calling __register_frame_info() API at startup.  In the latter
-//! case it is essential that there is only one copy of the unwinder runtime in
-//! the process.  This is usually achieved by linking to the dynamic version of
-//! the unwind runtime.
+//! ## Frame unwind info registration
 //!
-//! Currently Rust uses unwind runtime provided by libgcc.
+//! Each module's image contains a frame unwind info section (usually ".eh_frame").
+//! When a module is loaded/unloaded into the process, the unwinder must be informed
+//! about the location of this section in memory. The methods of achieving that vary
+//! by the platform.
+//! On some (e.g. Linux), the unwinder can discover unwind info sections on its own
+//! (by dynamically enumerating currently loaded modules via the dl_iterate_phdr() API
+//! and finding their ".eh_frame" sections);
+//! Others, like Windows, require modules to actively register their unwind info
+//! sections via unwinder API (see `rust_eh_register_frames`/`rust_eh_unregister_frames`).
 
 #![allow(dead_code)]
 #![allow(unused_imports)]
diff --git a/src/libstd/sys/common/unwind/seh64_gnu.rs b/src/libstd/sys/common/unwind/seh64_gnu.rs
index 9478678fda9..92f059d68e1 100644
--- a/src/libstd/sys/common/unwind/seh64_gnu.rs
+++ b/src/libstd/sys/common/unwind/seh64_gnu.rs
@@ -190,17 +190,10 @@ unsafe extern fn rust_eh_personality(
     ExceptionContinueSearch
 }
 
-// The `resume` instruction, found at the end of the landing pads, and whose job
-// is to resume stack unwinding, is typically lowered by LLVM into a call to
-// `_Unwind_Resume` routine.  To avoid confusion with the same symbol exported
-// from libgcc, we redirect it to `rust_eh_unwind_resume`.
-// Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume`
-// must be marked `pub` + `#[no_mangle]`.  (Can we make it a lang item?)
-
-#[lang = "eh_unwind_resume"]
 #[cfg(not(test))]
+#[lang = "eh_unwind_resume"]
 #[unwind]
-unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) {
+unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) -> ! {
     let params = [panic_ctx as ULONG_PTR];
     RaiseException(RUST_PANIC,
                    EXCEPTION_NONCONTINUABLE,
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index a9eb4db2f53..b168081ff8a 100644
--- a/src/libstd/sys/windows/c.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -356,6 +356,7 @@ pub type PVECTORED_EXCEPTION_HANDLER = extern "system"
 
 #[link(name = "ws2_32")]
 #[link(name = "userenv")]
+#[link(name = "shell32")]
 extern "system" {
     pub fn WSAStartup(wVersionRequested: libc::WORD,
                       lpWSAData: LPWSADATA) -> libc::c_int;