Skip to content

Commit 0ed12c4

Browse files
authored
Merge pull request #71 from madsmtm/c-unwind-feature
Make calling `throw` no longer UB by using "C-unwind" behind unstable feature
2 parents 9923fc0 + bb25515 commit 0ed12c4

File tree

23 files changed

+207
-62
lines changed

23 files changed

+207
-62
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ jobs:
154154
# Use --no-fail-fast, except with dinghy
155155
TESTARGS: ${{ matrix.dinghy && ' ' || '--no-fail-fast' }} ${{ matrix.test-args }}
156156
FEATURES: ${{ matrix.features || 'malloc,block,exception,catch_all,verify_message' }}
157-
UNSTABLE_FEATURES: ${{ matrix.unstable-features || 'unstable-autoreleasesafe' }}
157+
UNSTABLE_FEATURES: ${{ matrix.unstable-features || 'unstable-autoreleasesafe,unstable-c-unwind' }}
158158

159159
runs-on: ${{ matrix.os }}
160160

objc-sys/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## Unreleased - YYYY-MM-DD
88

9+
### Added
10+
* Added `unstable-c-unwind` feature.
11+
912

1013
## 0.2.0-beta.0 - 2022-06-13
1114

objc-sys/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ unstable-winobjc = ["gnustep-1-8"]
4949
# Link to ObjFW
5050
unstable-objfw = []
5151

52+
# Use nightly c_unwind feature
53+
unstable-c-unwind = []
54+
5255
# Private
5356
unstable-exception = ["cc"]
5457
unstable-docsrs = []

objc-sys/src/class.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,21 @@ pub struct objc_class {
2323
/// difference.
2424
type ivar_layout_type = u8;
2525

26+
// May call `resolveClassMethod:` or `resolveInstanceMethod:`.
27+
extern_c_unwind! {
28+
#[cfg(not(objfw))]
29+
pub fn class_getClassMethod(
30+
cls: *const objc_class,
31+
name: *const objc_selector,
32+
) -> *const objc_method;
33+
#[cfg(not(objfw))] // Available in newer versions
34+
pub fn class_getInstanceMethod(
35+
cls: *const objc_class,
36+
name: *const objc_selector,
37+
) -> *const objc_method;
38+
}
39+
40+
// TODO: Hooks registered with objc_setHook_getClass may be allowed to unwind?
2641
extern_c! {
2742
pub fn objc_getClass(name: *const c_char) -> *const objc_class;
2843
pub fn objc_getRequiredClass(name: *const c_char) -> *const objc_class;
@@ -97,19 +112,9 @@ extern_c! {
97112
#[cfg(not(objfw))]
98113
pub fn class_createInstance(cls: *const objc_class, extra_bytes: usize) -> *mut objc_object;
99114
#[cfg(not(objfw))]
100-
pub fn class_getClassMethod(
101-
cls: *const objc_class,
102-
name: *const objc_selector,
103-
) -> *const objc_method;
104-
#[cfg(not(objfw))]
105115
pub fn class_getClassVariable(cls: *const objc_class, name: *const c_char) -> *const objc_ivar;
106116
#[cfg(apple)]
107117
pub fn class_getImageName(cls: *const objc_class) -> *const c_char;
108-
#[cfg(not(objfw))] // Available in newer versions
109-
pub fn class_getInstanceMethod(
110-
cls: *const objc_class,
111-
name: *const objc_selector,
112-
) -> *const objc_method;
113118
pub fn class_getInstanceSize(cls: *const objc_class) -> usize;
114119
#[cfg(not(objfw))]
115120
pub fn class_getInstanceVariable(
@@ -140,6 +145,7 @@ extern_c! {
140145
attributes: *const objc_property_attribute_t,
141146
attributes_len: c_uint,
142147
);
148+
// TODO: Verify unwinding
143149
pub fn class_respondsToSelector(cls: *const objc_class, sel: *const objc_selector) -> BOOL;
144150
#[cfg(not(objfw))]
145151
pub fn class_setIvarLayout(cls: *mut objc_class, layout: *const ivar_layout_type);

objc-sys/src/exception.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,29 @@ pub type objc_uncaught_exception_handler =
3838
pub type objc_exception_handler =
3939
unsafe extern "C" fn(unused: *mut objc_object, context: *mut c_void);
4040

41-
extern_c! {
42-
#[cfg(any(gnustep, apple_new))]
43-
pub fn objc_begin_catch(exc_buf: *mut c_void) -> *mut objc_object;
44-
#[cfg(any(gnustep, apple_new))]
45-
pub fn objc_end_catch();
41+
#[cfg(all(feature = "unstable-exception", not(feature = "unstable-c-unwind")))]
42+
type TryCatchClosure = extern "C" fn(*mut c_void);
43+
#[cfg(all(feature = "unstable-exception", feature = "unstable-c-unwind"))]
44+
type TryCatchClosure = extern "C-unwind" fn(*mut c_void);
45+
46+
extern_c_unwind! {
4647
/// See [`objc-exception.h`].
4748
///
4849
/// [`objc-exception.h`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/objc-exception.h
4950
pub fn objc_exception_throw(exception: *mut objc_object) -> !;
5051
#[cfg(apple_new)]
5152
pub fn objc_exception_rethrow() -> !;
5253

54+
#[cfg(gnustep)]
55+
pub fn objc_exception_rethrow(exc_buf: *mut c_void) -> !;
56+
}
57+
58+
extern_c! {
59+
#[cfg(any(gnustep, apple_new))]
60+
pub fn objc_begin_catch(exc_buf: *mut c_void) -> *mut objc_object;
61+
#[cfg(any(gnustep, apple_new))]
62+
pub fn objc_end_catch();
63+
5364
#[cfg(apple_old)]
5465
pub fn objc_exception_try_enter(exception_data: *const c_void);
5566

@@ -61,9 +72,6 @@ extern_c! {
6172
// objc_exception_get_functions
6273
// objc_exception_set_functions
6374

64-
#[cfg(gnustep)]
65-
pub fn objc_exception_rethrow(exc_buf: *mut c_void) -> !;
66-
6775
#[cfg(apple_new)]
6876
pub fn objc_setExceptionMatcher(f: objc_exception_matcher) -> objc_exception_matcher;
6977
#[cfg(apple_new)]
@@ -105,7 +113,7 @@ extern_c! {
105113
/// [manual-asm]: https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/src/exception.rs
106114
#[cfg(feature = "unstable-exception")]
107115
pub fn rust_objc_sys_0_2_try_catch_exception(
108-
f: extern "C" fn(*mut c_void),
116+
f: TryCatchClosure,
109117
context: *mut c_void,
110118
error: *mut *mut objc_object,
111119
) -> c_uchar;

objc-sys/src/lib.rs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
#![allow(non_upper_case_globals)]
2626
#![allow(non_snake_case)]
2727
#![doc(html_root_url = "https://docs.rs/objc-sys/0.2.0-beta.0")]
28-
29-
// TODO: Replace `extern "C"` with `extern "C-unwind"` where applicable.
30-
// See https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html.
28+
#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))]
3129

3230
// TODO: Remove this and add "no-std" category to Cargo.toml
3331
// Requires a better solution for C-types in `no_std` crates.
@@ -51,6 +49,7 @@ macro_rules! generate_linking_tests {
5149
$(#[$m:meta])*
5250
$v:vis fn $name:ident($($a:ident: $t:ty),* $(,)?) $(-> $r:ty)?;
5351
)+}
52+
mod $test_name:ident;
5453
} => {
5554
$(#[$extern_m])*
5655
extern $abi {$(
@@ -61,10 +60,9 @@ macro_rules! generate_linking_tests {
6160
$(#[$extern_m])*
6261
#[allow(deprecated)]
6362
#[cfg(test)]
64-
mod test_linkable {
63+
mod $test_name {
6564
#[allow(unused)]
6665
use super::*;
67-
use std::println;
6866

6967
$(
7068
$(#[$m])*
@@ -73,8 +71,12 @@ macro_rules! generate_linking_tests {
7371
// Get function pointer to make the linker require the
7472
// symbol to be available.
7573
let f: unsafe extern $abi fn($($t),*) $(-> $r)? = crate::$name;
74+
// Workaround for https://github.com/rust-lang/rust/pull/92964
75+
#[cfg(feature = "unstable-c-unwind")]
76+
#[allow(clippy::useless_transmute)]
77+
let f: unsafe extern "C" fn() = unsafe { core::mem::transmute(f) };
7678
// Execute side-effect to ensure it is not optimized away.
77-
println!("{:p}", f);
79+
std::println!("{:p}", f);
7880
}
7981
)+
8082
}
@@ -95,6 +97,40 @@ macro_rules! extern_c {
9597
$(#[$m])*
9698
$v fn $name($($a: $t),*) $(-> $r)?;
9799
)+}
100+
mod test_linkable;
101+
}
102+
};
103+
}
104+
105+
// A lot of places may call `+initialize`, but the runtime guards those calls
106+
// with `@try/@catch` blocks already, so we don't need to mark every function
107+
// "C-unwind", only certain ones!
108+
macro_rules! extern_c_unwind {
109+
{
110+
$(#![$extern_m:meta])*
111+
$(
112+
$(#[$m:meta])*
113+
$v:vis fn $name:ident($($a:ident: $t:ty),* $(,)?) $(-> $r:ty)?;
114+
)+
115+
} => {
116+
#[cfg(not(feature = "unstable-c-unwind"))]
117+
generate_linking_tests! {
118+
$(#[$extern_m])*
119+
extern "C" {$(
120+
$(#[$m])*
121+
$v fn $name($($a: $t),*) $(-> $r)?;
122+
)+}
123+
mod test_linkable_unwind;
124+
}
125+
126+
#[cfg(feature = "unstable-c-unwind")]
127+
generate_linking_tests! {
128+
$(#[$extern_m])*
129+
extern "C-unwind" {$(
130+
$(#[$m])*
131+
$v fn $name($($a: $t),*) $(-> $r)?;
132+
)+}
133+
mod test_linkable_unwind;
98134
}
99135
};
100136
}

objc-sys/src/message.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ pub struct objc_super {
2020
pub super_class: *const objc_class,
2121
}
2222

23-
extern_c! {
23+
// All message sending functions should use "C-unwind"!
24+
//
25+
// Note that lookup functions won't throw exceptions themselves, but they can
26+
// call hooks, `resolveClassMethod:` and `resolveInstanceMethod:`, so we have
27+
// to make those "C-unwind" as well!
28+
extern_c_unwind! {
2429
#[cfg(any(gnustep, objfw))]
2530
pub fn objc_msg_lookup(receiver: *mut objc_object, sel: *const objc_selector) -> IMP;
2631
#[cfg(objfw)]

objc-sys/src/protocol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ extern_c! {
3030
#[cfg(not(objfw))]
3131
pub fn objc_registerProtocol(proto: *mut objc_protocol);
3232

33+
// TODO: Verify unwinding
3334
pub fn protocol_conformsToProtocol(
3435
proto: *const objc_protocol,
3536
other: *const objc_protocol,
3637
) -> BOOL;
38+
// TODO: Verify unwinding
3739
pub fn protocol_isEqual(proto: *const objc_protocol, other: *const objc_protocol) -> BOOL;
3840
pub fn protocol_getName(proto: *const objc_protocol) -> *const c_char;
3941

objc-sys/src/rc.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use core::ffi::c_void;
1414

1515
use crate::objc_object;
1616

17-
extern_c! {
17+
// All of these very rarely unwind, but may if the user defined methods
18+
// `retain`, `release`, `autorelease` or `dealloc` do.
19+
extern_c_unwind! {
1820
// Autoreleasepool
1921
// ObjFW: Defined in `autorelease.h`, not available with libobjfw-rt!
2022

objc-sys/src/various.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@ pub struct objc_ivar {
1616
_p: OpaqueData,
1717
}
1818

19+
#[cfg(not(feature = "unstable-c-unwind"))]
20+
type InnerImp = unsafe extern "C" fn();
21+
#[cfg(feature = "unstable-c-unwind")]
22+
type InnerImp = unsafe extern "C-unwind" fn();
23+
1924
/// A nullable pointer to the start of a method implementation.
2025
///
2126
/// Not all APIs are guaranteed to take NULL values; read the docs!
22-
pub type IMP = Option<unsafe extern "C" fn()>;
27+
pub type IMP = Option<InnerImp>;
2328

2429
// /// Not available on macOS x86.
2530
// ///
@@ -35,6 +40,12 @@ pub type IMP = Option<unsafe extern "C" fn()>;
3540
// pub type objc_hook_lazyClassNamer =
3641
// unsafe extern "C" fn(cls: *const crate::objc_class) -> *const c_char;
3742

43+
extern_c_unwind! {
44+
// Instead of being able to change this, it's a weak symbol on GNUStep.
45+
#[cfg(any(apple, objfw))]
46+
pub fn objc_enumerationMutation(obj: *mut objc_object);
47+
}
48+
3849
extern_c! {
3950
#[cfg(not(objfw))]
4051
pub fn imp_getBlock(imp: IMP) -> *mut objc_object;
@@ -58,9 +69,6 @@ extern_c! {
5869
#[cfg(apple)]
5970
pub fn objc_copyImageNames(out_len: *mut c_uint) -> *mut *const c_char;
6071

61-
// Instead of being able to change this, it's a weak symbol on GNUStep.
62-
#[cfg(any(apple, objfw))]
63-
pub fn objc_enumerationMutation(obj: *mut objc_object);
6472
#[cfg(any(apple, objfw))]
6573
pub fn objc_setEnumerationMutationHandler(
6674
handler: Option<unsafe extern "C" fn(obj: *mut objc_object)>,

0 commit comments

Comments
 (0)