Skip to content

Commit 658e345

Browse files
committed
Auto merge of #65646 - Amanieu:foreign-exceptions, r=<try>
Allow foreign exceptions to unwind through Rust code This PR allows C++ exceptions (and any other exceptions) to safely unwind through Rust code. In particular: - Drop code will be executed as the exception unwinds through the stack, as with a Rust panic. - `catch_unwind` will *not* catch the exception, instead the exception will silently continue unwinding past it. Incidentally, Rust panics can unwind just fine through C++ code as long as you mark the function with `#[unwind(allowed)]`. I've added a test for it to be sure. I haven't updated any of the documentation, so officially unwinding through FFI is still UB. However this is a step towards making it well-defined. Fixes #65441 cc @gnzlbg r? @alexcrichton
2 parents 46e6c53 + be4cd9e commit 658e345

File tree

13 files changed

+302
-191
lines changed

13 files changed

+302
-191
lines changed

src/ci/azure-pipelines/try.yml

+59-33
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ variables:
66
- group: prod-credentials
77

88
jobs:
9-
- job: Linux
10-
timeoutInMinutes: 600
11-
pool:
12-
vmImage: ubuntu-16.04
13-
steps:
14-
- template: steps/run.yml
15-
strategy:
16-
matrix:
17-
dist-x86_64-linux:
18-
IMAGE: dist-x86_64-linux
19-
DEPLOY: 1
20-
21-
dist-x86_64-linux-alt:
22-
IMAGE: dist-x86_64-linux
23-
DEPLOY_ALT: 1
9+
# - job: Linux
10+
# timeoutInMinutes: 600
11+
# pool:
12+
# vmImage: ubuntu-16.04
13+
# steps:
14+
# - template: steps/run.yml
15+
# strategy:
16+
# matrix:
17+
# dist-x86_64-linux:
18+
# IMAGE: dist-x86_64-linux
19+
# DEPLOY: 1
20+
#
21+
# dist-x86_64-linux-alt:
22+
# IMAGE: dist-x86_64-linux
23+
# DEPLOY_ALT: 1
2424

2525
# The macOS and Windows builds here are currently disabled due to them not being
2626
# overly necessary on `try` builds. We also don't actually have anything that
@@ -53,24 +53,50 @@ jobs:
5353
# NO_LLVM_ASSERTIONS: 1
5454
# NO_DEBUG_ASSERTIONS: 1
5555
#
56-
# - job: Windows
57-
# timeoutInMinutes: 600
58-
# pool:
59-
# vmImage: 'vs2017-win2016'
60-
# steps:
61-
# - template: steps/run.yml
62-
# strategy:
63-
# matrix:
64-
# dist-x86_64-msvc:
65-
# RUST_CONFIGURE_ARGS: >
66-
# --build=x86_64-pc-windows-msvc
67-
# --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc
68-
# --enable-full-tools
69-
# --enable-profiler
70-
# SCRIPT: python x.py dist
71-
# DIST_REQUIRE_ALL_TOOLS: 1
72-
# DEPLOY: 1
73-
#
56+
- job: Windows
57+
timeoutInMinutes: 600
58+
pool:
59+
vmImage: 'vs2017-win2016'
60+
steps:
61+
- template: steps/run.yml
62+
strategy:
63+
matrix:
64+
# 32/64 bit MSVC tests
65+
x86_64-msvc-1:
66+
MSYS_BITS: 64
67+
RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler
68+
SCRIPT: make ci-subset-1
69+
# FIXME(#59637)
70+
NO_DEBUG_ASSERTIONS: 1
71+
NO_LLVM_ASSERTIONS: 1
72+
i686-msvc-1:
73+
MSYS_BITS: 32
74+
RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
75+
SCRIPT: make ci-subset-1
76+
# FIXME(#59637)
77+
NO_DEBUG_ASSERTIONS: 1
78+
NO_LLVM_ASSERTIONS: 1
79+
i686-mingw-1:
80+
MSYS_BITS: 32
81+
RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
82+
SCRIPT: make ci-mingw-subset-1
83+
MINGW_URL: https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc
84+
MINGW_ARCHIVE: i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z
85+
MINGW_DIR: mingw32
86+
# FIXME(#59637)
87+
NO_DEBUG_ASSERTIONS: 1
88+
NO_LLVM_ASSERTIONS: 1
89+
x86_64-mingw-1:
90+
MSYS_BITS: 64
91+
SCRIPT: make ci-mingw-subset-1
92+
RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu
93+
MINGW_URL: https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc
94+
MINGW_ARCHIVE: x86_64-6.3.0-release-posix-seh-rt_v5-rev2.7z
95+
MINGW_DIR: mingw64
96+
# FIXME(#59637)
97+
NO_DEBUG_ASSERTIONS: 1
98+
NO_LLVM_ASSERTIONS: 1
99+
74100
# dist-x86_64-msvc-alt:
75101
# MSYS_BITS: 64
76102
# RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler

src/doc/unstable-book/src/language-features/lang-items.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,8 @@ the source code.
249249
- Runtime
250250
- `start`: `libstd/rt.rs`
251251
- `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC)
252-
- `eh_personality`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU)
252+
- `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)
253253
- `eh_personality`: `libpanic_unwind/seh.rs` (SEH)
254-
- `eh_unwind_resume`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU)
255254
- `eh_unwind_resume`: `libpanic_unwind/gcc.rs` (GCC)
256255
- `msvc_try_filter`: `libpanic_unwind/seh.rs` (SEH)
257256
- `panic`: `libcore/panicking.rs`

src/libpanic_unwind/dwarf/eh.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub enum EHAction {
5151

5252
pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
5353

54-
pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>)
54+
pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>, foreign_exception: bool)
5555
-> Result<EHAction, ()>
5656
{
5757
if lsda.is_null() {
@@ -96,7 +96,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>)
9696
return Ok(EHAction::None)
9797
} else {
9898
let lpad = lpad_base + cs_lpad;
99-
return Ok(interpret_cs_action(cs_action, lpad))
99+
return Ok(interpret_cs_action(cs_action, lpad, foreign_exception))
100100
}
101101
}
102102
}
@@ -121,15 +121,17 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>)
121121
// Can never have null landing pad for sjlj -- that would have
122122
// been indicated by a -1 call site index.
123123
let lpad = (cs_lpad + 1) as usize;
124-
return Ok(interpret_cs_action(cs_action, lpad))
124+
return Ok(interpret_cs_action(cs_action, lpad, foreign_exception))
125125
}
126126
}
127127
}
128128
}
129129

130-
fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction {
130+
fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction {
131131
if cs_action == 0 {
132132
EHAction::Cleanup(lpad)
133+
} else if foreign_exception {
134+
EHAction::None
133135
} else {
134136
EHAction::Catch(lpad)
135137
}

src/libpanic_unwind/gcc.rs

+51-17
Original file line numberDiff line numberDiff line change
@@ -133,23 +133,20 @@ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
133133
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
134134
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
135135

136-
// The personality routine for most of our targets, except ARM, which has a slightly different ABI
137-
// (however, iOS goes here as it uses SjLj unwinding). Also, the 64-bit Windows implementation
138-
// lives in seh64_gnu.rs
139-
#[cfg(all(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm"))))]
140-
#[lang = "eh_personality"]
141-
#[no_mangle]
136+
// Shared version of the default personality routine, which is used directly on
137+
// most targets and indirectly on Windows x86_64 via SEH.
142138
#[allow(unused)]
143-
unsafe extern "C" fn rust_eh_personality(version: c_int,
144-
actions: uw::_Unwind_Action,
145-
exception_class: uw::_Unwind_Exception_Class,
146-
exception_object: *mut uw::_Unwind_Exception,
147-
context: *mut uw::_Unwind_Context)
148-
-> uw::_Unwind_Reason_Code {
139+
unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
140+
actions: uw::_Unwind_Action,
141+
exception_class: uw::_Unwind_Exception_Class,
142+
exception_object: *mut uw::_Unwind_Exception,
143+
context: *mut uw::_Unwind_Context)
144+
-> uw::_Unwind_Reason_Code {
149145
if version != 1 {
150146
return uw::_URC_FATAL_PHASE1_ERROR;
151147
}
152-
let eh_action = match find_eh_action(context) {
148+
let foreign_exception = exception_class != rust_exception_class();
149+
let eh_action = match find_eh_action(context, foreign_exception) {
153150
Ok(action) => action,
154151
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
155152
};
@@ -175,6 +172,23 @@ unsafe extern "C" fn rust_eh_personality(version: c_int,
175172
}
176173
}
177174

175+
// The personality routine for most of our targets, except ARM, which has a slightly different ABI
176+
// (however, iOS goes here as it uses SjLj unwinding). Also, the 64-bit Windows implementation
177+
// uses a different personality (below) but eventually also calls rust_eh_personality_impl.
178+
#[cfg(all(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm")),
179+
not(all(windows, target_arch = "x86_64", target_env = "gnu"))))]
180+
#[lang = "eh_personality"]
181+
#[no_mangle]
182+
#[allow(unused)]
183+
unsafe extern "C" fn rust_eh_personality(version: c_int,
184+
actions: uw::_Unwind_Action,
185+
exception_class: uw::_Unwind_Exception_Class,
186+
exception_object: *mut uw::_Unwind_Exception,
187+
context: *mut uw::_Unwind_Context)
188+
-> uw::_Unwind_Reason_Code {
189+
rust_eh_personality_impl(version, actions, exception_class, exception_object, context)
190+
}
191+
178192
// ARM EHABI personality routine.
179193
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
180194
#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "netbsd")))]
@@ -215,7 +229,9 @@ unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State,
215229
// _Unwind_Context in our libunwind bindings and fetch the required data from there directly,
216230
// bypassing DWARF compatibility functions.
217231

218-
let eh_action = match find_eh_action(context) {
232+
let exception_class = unsafe { (*exception_object).exception_class };
233+
let foreign_exception = exception_class != rust_exception_class();
234+
let eh_action = match find_eh_action(context, foreign_exception) {
219235
Ok(action) => action,
220236
Err(_) => return uw::_URC_FAILURE,
221237
};
@@ -259,7 +275,25 @@ unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State,
259275
}
260276
}
261277

262-
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
278+
// On MinGW targets, the unwinding mechanism is SEH however the unwind handler
279+
// data (aka LSDA) uses GCC-compatible encoding.
280+
#[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))]
281+
#[lang = "eh_personality"]
282+
#[no_mangle]
283+
#[allow(nonstandard_style)]
284+
unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut uw::EXCEPTION_RECORD,
285+
establisherFrame: uw::LPVOID,
286+
contextRecord: *mut uw::CONTEXT,
287+
dispatcherContext: *mut uw::DISPATCHER_CONTEXT)
288+
-> uw::EXCEPTION_DISPOSITION {
289+
uw::_GCC_specific_handler(exceptionRecord,
290+
establisherFrame,
291+
contextRecord,
292+
dispatcherContext,
293+
rust_eh_personality_impl)
294+
}
295+
296+
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context, foreign_exception: bool)
263297
-> Result<EHAction, ()>
264298
{
265299
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
@@ -273,11 +307,11 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
273307
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
274308
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
275309
};
276-
eh::find_eh_action(lsda, &eh_context)
310+
eh::find_eh_action(lsda, &eh_context, foreign_exception)
277311
}
278312

279313
// See docs in the `unwind` module.
280-
#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
314+
#[cfg(all(target_os="windows", any(target_arch = "x86", target_arch = "x86_64"), target_env="gnu"))]
281315
#[lang = "eh_unwind_resume"]
282316
#[unwind(allowed)]
283317
unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {

src/libpanic_unwind/lib.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
//! essentially gets categorized into three buckets currently:
66
//!
77
//! 1. MSVC targets use SEH in the `seh.rs` file.
8-
//! 2. The 64-bit MinGW target half-uses SEH and half-use gcc-like information
9-
//! in the `seh64_gnu.rs` module.
10-
//! 3. All other targets use libunwind/libgcc in the `gcc/mod.rs` module.
8+
//! 2. Emscripten uses C++ exceptions in the `emcc.rs` file.
9+
//! 3. All other targets use libunwind/libgcc in the `gcc.rs` file.
1110
//!
1211
//! More documentation about each implementation can be found in the respective
1312
//! module.
@@ -49,9 +48,6 @@ cfg_if::cfg_if! {
4948
} else if #[cfg(target_env = "msvc")] {
5049
#[path = "seh.rs"]
5150
mod imp;
52-
} else if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] {
53-
#[path = "seh64_gnu.rs"]
54-
mod imp;
5551
} else {
5652
// Rust runtime's startup objects depend on these symbols, so make them public.
5753
#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]

0 commit comments

Comments
 (0)