Skip to content

Commit 38e6e5d

Browse files
committed
rustc: Use C++ personalities on MSVC
Currently the compiler has two relatively critical bugs in the implementation of MSVC unwinding: * #33112 - faults like segfaults and illegal instructions will run destructors in Rust, meaning we keep running code after a super-fatal exception has happened. * #33116 - When compiling with LTO plus `-Z no-landing-pads` (or `-C panic=abort` with the previous commit) LLVM won't remove all `invoke` instructions, meaning that some landing pads stick around and cleanups may be run due to the previous bug. These both stem from the flavor of "personality function" that Rust uses for unwinding on MSVC. On 32-bit this is `_except_handler3` and on 64-bit this is `__C_specific_handler`, but they both essentially are the "most generic" personality functions for catching exceptions and running cleanups. That is, thse two personalities will run cleanups for all exceptions unconditionally, so when we use them we run cleanups for **all SEH exceptions** (include things like segfaults). Note that this also explains why LLVM won't optimize away `invoke` instructions. These functions can legitimately still unwind (the `nounwind` attribute only seems to apply to "C++ exception-like unwining"). Also note that the standard library only *catches* Rust exceptions, not others like segfaults and illegal instructions. LLVM has support for another personality, `__CxxFrameHandler3`, which does not run cleanups for general exceptions, only C++ exceptions thrown by `_CxxThrowException`. This essentially ideally matches our use case, so this commit moves us over to using this well-known personality function as well as exception-throwing function. This doesn't *seem* to pull in any extra runtime dependencies just yet, but if it does we can perhaps try to work out how to implement more of it in Rust rather than relying on MSVCRT runtime bits. More details about how this is actually implemented can be found in the changes itself, but this... Closes #33112 Closes #33116
1 parent 0ec321f commit 38e6e5d

File tree

8 files changed

+337
-201
lines changed

8 files changed

+337
-201
lines changed

src/libpanic_abort/lib.rs

+36-13
Original file line numberDiff line numberDiff line change
@@ -93,20 +93,43 @@ pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 {
9393
// Essentially this symbol is just defined to get wired up to libcore/libstd
9494
// binaries, but it should never be called as we don't link in an unwinding
9595
// runtime at all.
96-
#[no_mangle]
9796
#[cfg(not(stage0))]
98-
pub extern fn rust_eh_personality() {}
97+
pub mod personalities {
9998

100-
// Similar to above, this corresponds to the `eh_unwind_resume` lang item that's
101-
// only used on Windows currently.
102-
#[no_mangle]
103-
#[cfg(all(not(stage0), target_os = "windows", target_env = "gnu"))]
104-
pub extern fn rust_eh_unwind_resume() {}
99+
#[no_mangle]
100+
#[cfg(not(all(target_os = "windows",
101+
target_env = "gnu",
102+
target_arch = "x86_64")))]
103+
pub extern fn rust_eh_personality() {}
105104

106-
#[no_mangle]
107-
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
108-
pub extern fn rust_eh_register_frames() {}
105+
// On x86_64-pc-windows-gnu we use our own personality function that needs
106+
// to return `ExceptionContinueSearch` as we're passing on all our frames.
107+
#[no_mangle]
108+
#[cfg(all(target_os = "windows",
109+
target_env = "gnu",
110+
target_arch = "x86_64"))]
111+
pub extern fn rust_eh_personality(_record: usize,
112+
_frame: usize,
113+
_context: usize,
114+
_dispatcher: usize) -> u32 {
115+
1 // `ExceptionContinueSearch`
116+
}
109117

110-
#[no_mangle]
111-
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
112-
pub extern fn rust_eh_unregister_frames() {}
118+
// Similar to above, this corresponds to the `eh_unwind_resume` lang item
119+
// that's only used on Windows currently.
120+
//
121+
// Note that we don't execute landing pads, so this is never called, so it's
122+
// body is empty.
123+
#[no_mangle]
124+
#[cfg(all(target_os = "windows", target_env = "gnu"))]
125+
pub extern fn rust_eh_unwind_resume() {}
126+
127+
// These two are called by our startup objects on i686-pc-windows-gnu, but
128+
// they don't need to do anything so the bodies are nops.
129+
#[no_mangle]
130+
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
131+
pub extern fn rust_eh_register_frames() {}
132+
#[no_mangle]
133+
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
134+
pub extern fn rust_eh_unregister_frames() {}
135+
}

0 commit comments

Comments
 (0)