1mod arch;
2mod find_fde;
3mod frame;
4
5use core::ffi::c_void;
6use core::ptr;
7use gimli::Register;
8
9use crate::abi::*;
10use crate::arch::*;
11use crate::util::*;
12use arch::*;
13use find_fde::FDEFinder;
14use frame::Frame;
15
16#[cfg(feature = "fde-custom")]
17pub use find_fde::custom_eh_frame_finder;
18
19fn with_context<T, F: FnOnce(&mut Context) -> T>(f: F) -> T {
21 use core::mem::ManuallyDrop;
22
23 union Data<T, F> {
24 f: ManuallyDrop<F>,
25 t: ManuallyDrop<T>,
26 }
27
28 extern "C" fn delegate<T, F: FnOnce(&mut Context) -> T>(ctx: &mut Context, ptr: *mut ()) {
29 unsafe {
33 let data = &mut *ptr.cast::<Data<T, F>>();
34 let t = ManuallyDrop::take(&mut data.f)(ctx);
35 data.t = ManuallyDrop::new(t);
36 }
37 }
38
39 let mut data = Data {
40 f: ManuallyDrop::new(f),
41 };
42 save_context(delegate::<T, F>, ptr::addr_of_mut!(data).cast());
43 unsafe { ManuallyDrop::into_inner(data.t) }
44}
45
46#[repr(C)]
47pub struct UnwindException {
48 pub exception_class: u64,
49 pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
50 private_1: Option<UnwindStopFn>,
51 private_2: usize,
52 private_unused: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE - 2],
53}
54
55pub struct UnwindContext<'a> {
56 frame: Option<&'a Frame>,
57 ctx: &'a mut Context,
58 signal: bool,
59}
60
61#[unsafe(no_mangle)]
62pub extern "C" fn _Unwind_GetGR(unwind_ctx: &UnwindContext<'_>, index: c_int) -> usize {
63 unwind_ctx.ctx[Register(index as u16)]
64}
65
66#[unsafe(no_mangle)]
67pub extern "C" fn _Unwind_GetCFA(unwind_ctx: &UnwindContext<'_>) -> usize {
68 unwind_ctx.ctx[Arch::SP]
69}
70
71#[unsafe(no_mangle)]
72pub extern "C" fn _Unwind_SetGR(unwind_ctx: &mut UnwindContext<'_>, index: c_int, value: usize) {
73 unwind_ctx.ctx[Register(index as u16)] = value;
74}
75
76#[unsafe(no_mangle)]
77pub extern "C" fn _Unwind_GetIP(unwind_ctx: &UnwindContext<'_>) -> usize {
78 unwind_ctx.ctx[Arch::RA]
79}
80
81#[unsafe(no_mangle)]
82pub extern "C" fn _Unwind_GetIPInfo(
83 unwind_ctx: &UnwindContext<'_>,
84 ip_before_insn: &mut c_int,
85) -> usize {
86 *ip_before_insn = unwind_ctx.signal as _;
87 unwind_ctx.ctx[Arch::RA]
88}
89
90#[unsafe(no_mangle)]
91pub extern "C" fn _Unwind_SetIP(unwind_ctx: &mut UnwindContext<'_>, value: usize) {
92 unwind_ctx.ctx[Arch::RA] = value;
93}
94
95#[unsafe(no_mangle)]
96pub extern "C" fn _Unwind_GetLanguageSpecificData(unwind_ctx: &UnwindContext<'_>) -> *mut c_void {
97 unwind_ctx
98 .frame
99 .map(|f| f.lsda() as *mut c_void)
100 .unwrap_or(ptr::null_mut())
101}
102
103#[unsafe(no_mangle)]
104pub extern "C" fn _Unwind_GetRegionStart(unwind_ctx: &UnwindContext<'_>) -> usize {
105 unwind_ctx.frame.map(|f| f.initial_address()).unwrap_or(0)
106}
107
108#[unsafe(no_mangle)]
109pub extern "C" fn _Unwind_GetTextRelBase(unwind_ctx: &UnwindContext<'_>) -> usize {
110 unwind_ctx
111 .frame
112 .map(|f| f.bases().eh_frame.text.unwrap() as _)
113 .unwrap_or(0)
114}
115
116#[unsafe(no_mangle)]
117pub extern "C" fn _Unwind_GetDataRelBase(unwind_ctx: &UnwindContext<'_>) -> usize {
118 unwind_ctx
119 .frame
120 .map(|f| f.bases().eh_frame.data.unwrap() as _)
121 .unwrap_or(0)
122}
123
124#[unsafe(no_mangle)]
125pub extern "C" fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
126 find_fde::get_finder()
127 .find_fde(pc as usize - 1)
128 .map(|r| r.fde.initial_address() as usize as _)
129 .unwrap_or(ptr::null_mut())
130}
131
132macro_rules! try1 {
133 ($e: expr) => {{
134 match $e {
135 Ok(v) => v,
136 Err(_) => return UnwindReasonCode::FATAL_PHASE1_ERROR,
137 }
138 }};
139}
140
141macro_rules! try2 {
142 ($e: expr) => {{
143 match $e {
144 Ok(v) => v,
145 Err(_) => return UnwindReasonCode::FATAL_PHASE2_ERROR,
146 }
147 }};
148}
149
150#[inline(never)]
151#[unsafe(no_mangle)]
152pub unsafe extern "C-unwind" fn _Unwind_RaiseException(
153 exception: *mut UnwindException,
154) -> UnwindReasonCode {
155 with_context(|saved_ctx| {
156 let mut ctx = saved_ctx.clone();
158 let mut signal = false;
159 loop {
160 if let Some(frame) = try1!(Frame::from_context(&ctx, signal)) {
161 if let Some(personality) = frame.personality() {
162 let result = unsafe {
163 personality(
164 1,
165 UnwindAction::SEARCH_PHASE,
166 (*exception).exception_class,
167 exception,
168 &mut UnwindContext {
169 frame: Some(&frame),
170 ctx: &mut ctx,
171 signal,
172 },
173 )
174 };
175
176 match result {
177 UnwindReasonCode::CONTINUE_UNWIND => (),
178 UnwindReasonCode::HANDLER_FOUND => {
179 break;
180 }
181 _ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
182 }
183 }
184
185 ctx = try1!(frame.unwind(&ctx));
186 signal = frame.is_signal_trampoline();
187 } else {
188 return UnwindReasonCode::END_OF_STACK;
189 }
190 }
191
192 let handler_cfa = ctx[Arch::SP] - signal as usize;
194 unsafe {
195 (*exception).private_1 = None;
196 (*exception).private_2 = handler_cfa;
197 }
198
199 let code = raise_exception_phase2(exception, saved_ctx, handler_cfa);
200 match code {
201 UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(saved_ctx) },
202 _ => code,
203 }
204 })
205}
206
207fn raise_exception_phase2(
208 exception: *mut UnwindException,
209 ctx: &mut Context,
210 handler_cfa: usize,
211) -> UnwindReasonCode {
212 let mut signal = false;
213 loop {
214 if let Some(frame) = try2!(Frame::from_context(ctx, signal)) {
215 let frame_cfa = ctx[Arch::SP] - signal as usize;
216 if let Some(personality) = frame.personality() {
217 let code = unsafe {
218 personality(
219 1,
220 UnwindAction::CLEANUP_PHASE
221 | if frame_cfa == handler_cfa {
222 UnwindAction::HANDLER_FRAME
223 } else {
224 UnwindAction::empty()
225 },
226 (*exception).exception_class,
227 exception,
228 &mut UnwindContext {
229 frame: Some(&frame),
230 ctx,
231 signal,
232 },
233 )
234 };
235
236 match code {
237 UnwindReasonCode::CONTINUE_UNWIND => (),
238 UnwindReasonCode::INSTALL_CONTEXT => {
239 frame.adjust_stack_for_args(ctx);
240 return UnwindReasonCode::INSTALL_CONTEXT;
241 }
242 _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
243 }
244 }
245
246 *ctx = try2!(frame.unwind(ctx));
247 signal = frame.is_signal_trampoline();
248 } else {
249 return UnwindReasonCode::FATAL_PHASE2_ERROR;
250 }
251 }
252}
253
254#[inline(never)]
255#[unsafe(no_mangle)]
256pub unsafe extern "C-unwind" fn _Unwind_ForcedUnwind(
257 exception: *mut UnwindException,
258 stop: UnwindStopFn,
259 stop_arg: *mut c_void,
260) -> UnwindReasonCode {
261 with_context(|ctx| {
262 unsafe {
263 (*exception).private_1 = Some(stop);
264 (*exception).private_2 = stop_arg as _;
265 }
266
267 let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
268 match code {
269 UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(ctx) },
270 _ => code,
271 }
272 })
273}
274
275fn force_unwind_phase2(
276 exception: *mut UnwindException,
277 ctx: &mut Context,
278 stop: UnwindStopFn,
279 stop_arg: *mut c_void,
280) -> UnwindReasonCode {
281 let mut signal = false;
282 loop {
283 let frame = try2!(Frame::from_context(ctx, signal));
284
285 let code = unsafe {
286 stop(
287 1,
288 UnwindAction::FORCE_UNWIND
289 | UnwindAction::END_OF_STACK
290 | if frame.is_none() {
291 UnwindAction::END_OF_STACK
292 } else {
293 UnwindAction::empty()
294 },
295 (*exception).exception_class,
296 exception,
297 &mut UnwindContext {
298 frame: frame.as_ref(),
299 ctx,
300 signal,
301 },
302 stop_arg,
303 )
304 };
305 match code {
306 UnwindReasonCode::NO_REASON => (),
307 _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
308 }
309
310 if let Some(frame) = frame {
311 if let Some(personality) = frame.personality() {
312 let code = unsafe {
313 personality(
314 1,
315 UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE,
316 (*exception).exception_class,
317 exception,
318 &mut UnwindContext {
319 frame: Some(&frame),
320 ctx,
321 signal,
322 },
323 )
324 };
325
326 match code {
327 UnwindReasonCode::CONTINUE_UNWIND => (),
328 UnwindReasonCode::INSTALL_CONTEXT => {
329 frame.adjust_stack_for_args(ctx);
330 return UnwindReasonCode::INSTALL_CONTEXT;
331 }
332 _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
333 }
334 }
335
336 *ctx = try2!(frame.unwind(ctx));
337 signal = frame.is_signal_trampoline();
338 } else {
339 return UnwindReasonCode::END_OF_STACK;
340 }
341 }
342}
343
344#[inline(never)]
345#[unsafe(no_mangle)]
346pub unsafe extern "C-unwind" fn _Unwind_Resume(exception: *mut UnwindException) -> ! {
347 with_context(|ctx| {
348 let code = match unsafe { (*exception).private_1 } {
349 None => {
350 let handler_cfa = unsafe { (*exception).private_2 };
351 raise_exception_phase2(exception, ctx, handler_cfa)
352 }
353 Some(stop) => {
354 let stop_arg = unsafe { (*exception).private_2 as _ };
355 force_unwind_phase2(exception, ctx, stop, stop_arg)
356 }
357 };
358 assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
359
360 unsafe { restore_context(ctx) }
361 })
362}
363
364#[inline(never)]
365#[unsafe(no_mangle)]
366pub unsafe extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
367 exception: *mut UnwindException,
368) -> UnwindReasonCode {
369 let stop = match unsafe { (*exception).private_1 } {
370 None => return unsafe { _Unwind_RaiseException(exception) },
371 Some(v) => v,
372 };
373
374 with_context(|ctx| {
375 let stop_arg = unsafe { (*exception).private_2 as _ };
376 let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
377 assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
378
379 unsafe { restore_context(ctx) }
380 })
381}
382
383#[unsafe(no_mangle)]
384pub unsafe extern "C" fn _Unwind_DeleteException(exception: *mut UnwindException) {
385 if let Some(cleanup) = unsafe { (*exception).exception_cleanup } {
386 unsafe { cleanup(UnwindReasonCode::FOREIGN_EXCEPTION_CAUGHT, exception) };
387 }
388}
389
390#[inline(never)]
391#[unsafe(no_mangle)]
392pub extern "C-unwind" fn _Unwind_Backtrace(
393 trace: UnwindTraceFn,
394 trace_argument: *mut c_void,
395) -> UnwindReasonCode {
396 with_context(|ctx| {
397 let mut ctx = ctx.clone();
398 let mut signal = false;
399 let mut skipping = cfg!(feature = "hide-trace");
400
401 loop {
402 let frame = try1!(Frame::from_context(&ctx, signal));
403 if !skipping {
404 let code = trace(
405 &UnwindContext {
406 frame: frame.as_ref(),
407 ctx: &mut ctx,
408 signal,
409 },
410 trace_argument,
411 );
412 match code {
413 UnwindReasonCode::NO_REASON => (),
414 _ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
415 }
416 }
417 if let Some(frame) = frame {
418 if skipping {
419 if frame.initial_address() == _Unwind_Backtrace as usize {
420 skipping = false;
421 }
422 }
423 ctx = try1!(frame.unwind(&ctx));
424 signal = frame.is_signal_trampoline();
425 } else {
426 return UnwindReasonCode::END_OF_STACK;
427 }
428 }
429 })
430}