Skip to content

Commit 7c67e97

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 8318ef2 + bffc90a commit 7c67e97

File tree

32 files changed

+335
-329
lines changed

32 files changed

+335
-329
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

+2-22
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
5252
5353
#[lang = "eh_personality"] extern fn rust_eh_personality() {}
5454
#[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } }
55-
#[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
5655
#[no_mangle] pub extern fn rust_eh_register_frames () {}
5756
#[no_mangle] pub extern fn rust_eh_unregister_frames () {}
5857
```
@@ -67,7 +66,7 @@ Other features provided by lang items include:
6766
marked with lang items; those specific four are `eq`, `ord`,
6867
`deref`, and `add` respectively.
6968
- stack unwinding and general failure; the `eh_personality`,
70-
`eh_unwind_resume`, `fail` and `fail_bounds_checks` lang items.
69+
`fail` and `fail_bounds_checks` lang items.
7170
- the traits in `std::marker` used to indicate types of
7271
various kinds; lang items `send`, `sync` and `copy`.
7372
- the marker types and variance indicators found in
@@ -130,12 +129,6 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
130129
pub extern fn rust_eh_personality() {
131130
}
132131
133-
// This function may be needed based on the compilation target.
134-
#[lang = "eh_unwind_resume"]
135-
#[no_mangle]
136-
pub extern fn rust_eh_unwind_resume() {
137-
}
138-
139132
#[lang = "panic_impl"]
140133
#[no_mangle]
141134
pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
@@ -173,12 +166,6 @@ pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 {
173166
pub extern fn rust_eh_personality() {
174167
}
175168
176-
// This function may be needed based on the compilation target.
177-
#[lang = "eh_unwind_resume"]
178-
#[no_mangle]
179-
pub extern fn rust_eh_unwind_resume() {
180-
}
181-
182169
#[lang = "panic_impl"]
183170
#[no_mangle]
184171
pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
@@ -215,11 +202,6 @@ compiler. When a panic happens, this controls the message that's displayed on
215202
the screen. While the language item's name is `panic_impl`, the symbol name is
216203
`rust_begin_panic`.
217204

218-
A third function, `rust_eh_unwind_resume`, is also needed if the `custom_unwind_resume`
219-
flag is set in the options of the compilation target. It allows customizing the
220-
process of resuming unwind at the end of the landing pads. The language item's name
221-
is `eh_unwind_resume`.
222-
223205
## List of all language items
224206

225207
This is a list of all language items in Rust along with where they are located in
@@ -249,10 +231,8 @@ the source code.
249231
- Runtime
250232
- `start`: `libstd/rt.rs`
251233
- `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC)
252-
- `eh_personality`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU)
234+
- `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)
253235
- `eh_personality`: `libpanic_unwind/seh.rs` (SEH)
254-
- `eh_unwind_resume`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU)
255-
- `eh_unwind_resume`: `libpanic_unwind/gcc.rs` (GCC)
256236
- `msvc_try_filter`: `libpanic_unwind/seh.rs` (SEH)
257237
- `panic`: `libcore/panicking.rs`
258238
- `panic_bounds_check`: `libcore/panicking.rs`

src/libpanic_abort/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ pub mod personalities {
122122
// Note that we don't execute landing pads, so this is never called, so it's
123123
// body is empty.
124124
#[no_mangle]
125+
#[cfg(bootstrap)]
125126
#[cfg(all(target_os = "windows", target_env = "gnu"))]
126127
pub extern fn rust_eh_unwind_resume() {}
127128

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

+52-25
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@
3535
//!
3636
//! Once stack has been unwound down to the handler frame level, unwinding stops
3737
//! and the last personality routine transfers control to the catch block.
38-
//!
39-
//! ## `eh_personality` and `eh_unwind_resume`
40-
//!
41-
//! These language items are used by the compiler when generating unwind info.
42-
//! The first one is the personality routine described above. The second one
43-
//! allows compilation target to customize the process of resuming unwind at the
44-
//! end of the landing pads. `eh_unwind_resume` is used only if
45-
//! `custom_unwind_resume` flag in the target options is set.
4638
4739
#![allow(private_no_mangle_fns)]
4840

@@ -133,23 +125,20 @@ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
133125
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
134126
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
135127

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]
128+
// Shared version of the default personality routine, which is used directly on
129+
// most targets and indirectly on Windows x86_64 via SEH.
142130
#[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 {
131+
unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
132+
actions: uw::_Unwind_Action,
133+
exception_class: uw::_Unwind_Exception_Class,
134+
exception_object: *mut uw::_Unwind_Exception,
135+
context: *mut uw::_Unwind_Context)
136+
-> uw::_Unwind_Reason_Code {
149137
if version != 1 {
150138
return uw::_URC_FATAL_PHASE1_ERROR;
151139
}
152-
let eh_action = match find_eh_action(context) {
140+
let foreign_exception = exception_class != rust_exception_class();
141+
let eh_action = match find_eh_action(context, foreign_exception) {
153142
Ok(action) => action,
154143
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
155144
};
@@ -175,6 +164,23 @@ unsafe extern "C" fn rust_eh_personality(version: c_int,
175164
}
176165
}
177166

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

218-
let eh_action = match find_eh_action(context) {
224+
let exception_class = unsafe { (*exception_object).exception_class };
225+
let foreign_exception = exception_class != rust_exception_class();
226+
let eh_action = match find_eh_action(context, foreign_exception) {
219227
Ok(action) => action,
220228
Err(_) => return uw::_URC_FAILURE,
221229
};
@@ -259,7 +267,25 @@ unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State,
259267
}
260268
}
261269

262-
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
270+
// On MinGW targets, the unwinding mechanism is SEH however the unwind handler
271+
// data (aka LSDA) uses GCC-compatible encoding.
272+
#[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))]
273+
#[lang = "eh_personality"]
274+
#[no_mangle]
275+
#[allow(nonstandard_style)]
276+
unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut uw::EXCEPTION_RECORD,
277+
establisherFrame: uw::LPVOID,
278+
contextRecord: *mut uw::CONTEXT,
279+
dispatcherContext: *mut uw::DISPATCHER_CONTEXT)
280+
-> uw::EXCEPTION_DISPOSITION {
281+
uw::_GCC_specific_handler(exceptionRecord,
282+
establisherFrame,
283+
contextRecord,
284+
dispatcherContext,
285+
rust_eh_personality_impl)
286+
}
287+
288+
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context, foreign_exception: bool)
263289
-> Result<EHAction, ()>
264290
{
265291
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
@@ -273,11 +299,12 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
273299
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
274300
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
275301
};
276-
eh::find_eh_action(lsda, &eh_context)
302+
eh::find_eh_action(lsda, &eh_context, foreign_exception)
277303
}
278304

279305
// See docs in the `unwind` module.
280-
#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
306+
#[cfg(bootstrap)]
307+
#[cfg(all(target_os="windows", any(target_arch = "x86", target_arch = "x86_64"), target_env="gnu"))]
281308
#[lang = "eh_unwind_resume"]
282309
#[unwind(allowed)]
283310
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)