Skip to content

Commit 895180d

Browse files
authored
wasmtime: Refactor trap-handling (bytecodealliance#9087)
This commit groups together the registers that have to be collected from a signal handler to correctly report a trap: namely, the program counter and frame pointer, as of the time that the trap occurred. I also moved the call to set_jit_trap inside test_if_trap for every platform that uses both methods. Only the implementation for Mach ports still needs to call set_jit_trap because it doesn't use test_if_trap. In addition I'm fixing an unrelated doc comment that I stumbled across while working on this.
1 parent fa9a78b commit 895180d

File tree

6 files changed

+85
-86
lines changed

6 files changed

+85
-86
lines changed

crates/wasmtime/src/runtime/func.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -984,8 +984,8 @@ impl Func {
984984
/// # Panics
985985
///
986986
/// This function will panic if called on a function belonging to an async
987-
/// store. Asynchronous stores must always use `call_async`.
988-
/// initiates a panic. Also panics if `store` does not own this function.
987+
/// store. Asynchronous stores must always use `call_async`. Also panics if
988+
/// `store` does not own this function.
989989
///
990990
/// [`WasmBacktrace`]: crate::WasmBacktrace
991991
pub fn call(

crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::runtime::vm::traphandlers::{tls, TrapTest};
1+
use crate::runtime::vm::traphandlers::{tls, TrapRegisters, TrapTest};
22
use crate::runtime::vm::VMContext;
33
use core::mem;
44

@@ -31,7 +31,7 @@ impl TrapHandler {
3131
pub fn validate_config(&self, _macos_use_mach_ports: bool) {}
3232
}
3333

34-
extern "C" fn handle_trap(ip: usize, fp: usize, has_faulting_addr: bool, faulting_addr: usize) {
34+
extern "C" fn handle_trap(pc: usize, fp: usize, has_faulting_addr: bool, faulting_addr: usize) {
3535
tls::with(|info| {
3636
let info = match info {
3737
Some(info) => info,
@@ -42,17 +42,14 @@ extern "C" fn handle_trap(ip: usize, fp: usize, has_faulting_addr: bool, faultin
4242
} else {
4343
None
4444
};
45-
let ip = ip as *const u8;
46-
let test = info.test_if_trap(ip, |_handler| {
45+
let regs = TrapRegisters { pc, fp };
46+
let test = info.test_if_trap(regs, faulting_addr, |_handler| {
4747
panic!("custom signal handlers are not supported on this platform");
4848
});
4949
match test {
5050
TrapTest::NotWasm => {}
5151
TrapTest::HandledByEmbedder => unreachable!(),
52-
TrapTest::Trap { jmp_buf, trap } => {
53-
info.set_jit_trap(ip, fp, faulting_addr, trap);
54-
unsafe { wasmtime_longjmp(jmp_buf) }
55-
}
52+
TrapTest::Trap { jmp_buf } => unsafe { wasmtime_longjmp(jmp_buf) },
5653
}
5754
})
5855
}

crates/wasmtime/src/runtime/vm/sys/unix/machports.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
use crate::runtime::module::lookup_code;
3737
use crate::runtime::vm::sys::traphandlers::wasmtime_longjmp;
38-
use crate::runtime::vm::traphandlers::tls;
38+
use crate::runtime::vm::traphandlers::{tls, TrapRegisters};
3939
use mach2::exc::*;
4040
use mach2::exception_types::*;
4141
use mach2::kern_return::*;
@@ -384,21 +384,16 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool {
384384
/// a native backtrace once we've switched back to the thread itself. After
385385
/// the backtrace is captured we can do the usual `longjmp` back to the source
386386
/// of the wasm code.
387-
unsafe extern "C" fn unwind(
388-
wasm_pc: *const u8,
389-
wasm_fp: usize,
390-
fault1: usize,
391-
fault2: usize,
392-
trap: u8,
393-
) -> ! {
387+
unsafe extern "C" fn unwind(pc: usize, fp: usize, fault1: usize, fault2: usize, trap: u8) -> ! {
394388
let jmp_buf = tls::with(|state| {
395389
let state = state.unwrap();
390+
let regs = TrapRegisters { pc, fp };
396391
let faulting_addr = match fault1 {
397392
0 => None,
398393
_ => Some(fault2),
399394
};
400395
let trap = Trap::from_u8(trap).unwrap();
401-
state.set_jit_trap(wasm_pc, wasm_fp, faulting_addr, trap);
396+
state.set_jit_trap(regs, faulting_addr, trap);
402397
state.take_jmp_buf()
403398
});
404399
debug_assert!(!jmp_buf.is_null());

crates/wasmtime/src/runtime/vm/sys/unix/signals.rs

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Trap handling on Unix based on POSIX signals.
22
3-
use crate::runtime::vm::traphandlers::{tls, TrapTest};
3+
use crate::runtime::vm::traphandlers::{tls, TrapRegisters, TrapTest};
44
use crate::runtime::vm::VMContext;
55
use std::cell::RefCell;
66
use std::io;
@@ -166,23 +166,24 @@ unsafe extern "C" fn trap_handler(
166166
// Otherwise flag ourselves as handling a trap, do the trap
167167
// handling, and reset our trap handling flag. Then we figure
168168
// out what to do based on the result of the trap handling.
169-
let (pc, fp) = get_pc_and_fp(context, signum);
170-
let test = info.test_if_trap(pc, |handler| handler(signum, siginfo, context));
169+
let faulting_addr = match signum {
170+
libc::SIGSEGV | libc::SIGBUS => Some((*siginfo).si_addr() as usize),
171+
_ => None,
172+
};
173+
let regs = get_trap_registers(context, signum);
174+
let test = info.test_if_trap(regs, faulting_addr, |handler| {
175+
handler(signum, siginfo, context)
176+
});
171177

172178
// Figure out what to do based on the result of this handling of
173179
// the trap. Note that our sentinel value of 1 means that the
174180
// exception was handled by a custom exception handler, so we
175181
// keep executing.
176-
let (jmp_buf, trap) = match test {
182+
let jmp_buf = match test {
177183
TrapTest::NotWasm => return false,
178184
TrapTest::HandledByEmbedder => return true,
179-
TrapTest::Trap { jmp_buf, trap } => (jmp_buf, trap),
180-
};
181-
let faulting_addr = match signum {
182-
libc::SIGSEGV | libc::SIGBUS => Some((*siginfo).si_addr() as usize),
183-
_ => None,
185+
TrapTest::Trap { jmp_buf } => jmp_buf,
184186
};
185-
info.set_jit_trap(pc, fp, faulting_addr, trap);
186187
// On macOS this is a bit special, unfortunately. If we were to
187188
// `siglongjmp` out of the signal handler that notably does
188189
// *not* reset the sigaltstack state of our signal handler. This
@@ -247,20 +248,20 @@ unsafe extern "C" fn trap_handler(
247248
}
248249
}
249250

250-
unsafe fn get_pc_and_fp(cx: *mut libc::c_void, _signum: libc::c_int) -> (*const u8, usize) {
251+
unsafe fn get_trap_registers(cx: *mut libc::c_void, _signum: libc::c_int) -> TrapRegisters {
251252
cfg_if::cfg_if! {
252253
if #[cfg(all(any(target_os = "linux", target_os = "android"), target_arch = "x86_64"))] {
253254
let cx = &*(cx as *const libc::ucontext_t);
254-
(
255-
cx.uc_mcontext.gregs[libc::REG_RIP as usize] as *const u8,
256-
cx.uc_mcontext.gregs[libc::REG_RBP as usize] as usize
257-
)
255+
TrapRegisters {
256+
pc: cx.uc_mcontext.gregs[libc::REG_RIP as usize] as usize,
257+
fp: cx.uc_mcontext.gregs[libc::REG_RBP as usize] as usize,
258+
}
258259
} else if #[cfg(all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64"))] {
259260
let cx = &*(cx as *const libc::ucontext_t);
260-
(
261-
cx.uc_mcontext.pc as *const u8,
262-
cx.uc_mcontext.regs[29] as usize,
263-
)
261+
TrapRegisters {
262+
pc: cx.uc_mcontext.pc as usize,
263+
fp: cx.uc_mcontext.regs[29] as usize,
264+
}
264265
} else if #[cfg(all(target_os = "linux", target_arch = "s390x"))] {
265266
// On s390x, SIGILL and SIGFPE are delivered with the PSW address
266267
// pointing *after* the faulting instruction, while SIGSEGV and
@@ -277,46 +278,46 @@ unsafe fn get_pc_and_fp(cx: *mut libc::c_void, _signum: libc::c_int) -> (*const
277278
_ => 0,
278279
};
279280
let cx = &*(cx as *const libc::ucontext_t);
280-
(
281-
(cx.uc_mcontext.psw.addr - trap_offset) as *const u8,
282-
*(cx.uc_mcontext.gregs[15] as *const usize),
283-
)
281+
TrapRegisters {
282+
pc: (cx.uc_mcontext.psw.addr - trap_offset) as usize,
283+
fp: *(cx.uc_mcontext.gregs[15] as *const usize),
284+
}
284285
} else if #[cfg(all(target_os = "macos", target_arch = "x86_64"))] {
285286
let cx = &*(cx as *const libc::ucontext_t);
286-
(
287-
(*cx.uc_mcontext).__ss.__rip as *const u8,
288-
(*cx.uc_mcontext).__ss.__rbp as usize,
289-
)
287+
TrapRegisters {
288+
pc: (*cx.uc_mcontext).__ss.__rip as usize,
289+
fp: (*cx.uc_mcontext).__ss.__rbp as usize,
290+
}
290291
} else if #[cfg(all(target_os = "macos", target_arch = "aarch64"))] {
291292
let cx = &*(cx as *const libc::ucontext_t);
292-
(
293-
(*cx.uc_mcontext).__ss.__pc as *const u8,
294-
(*cx.uc_mcontext).__ss.__fp as usize,
295-
)
293+
TrapRegisters {
294+
pc: (*cx.uc_mcontext).__ss.__pc as usize,
295+
fp: (*cx.uc_mcontext).__ss.__fp as usize,
296+
}
296297
} else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] {
297298
let cx = &*(cx as *const libc::ucontext_t);
298-
(
299-
cx.uc_mcontext.mc_rip as *const u8,
300-
cx.uc_mcontext.mc_rbp as usize,
301-
)
299+
TrapRegisters {
300+
pc: cx.uc_mcontext.mc_rip as usize,
301+
fp: cx.uc_mcontext.mc_rbp as usize,
302+
}
302303
} else if #[cfg(all(target_os = "linux", target_arch = "riscv64"))] {
303304
let cx = &*(cx as *const libc::ucontext_t);
304-
(
305-
cx.uc_mcontext.__gregs[libc::REG_PC] as *const u8,
306-
cx.uc_mcontext.__gregs[libc::REG_S0] as usize,
307-
)
305+
TrapRegisters {
306+
pc: cx.uc_mcontext.__gregs[libc::REG_PC] as usize,
307+
fp: cx.uc_mcontext.__gregs[libc::REG_S0] as usize,
308+
}
308309
} else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] {
309310
let cx = &*(cx as *const libc::mcontext_t);
310-
(
311-
cx.mc_gpregs.gp_elr as *const u8,
312-
cx.mc_gpregs.gp_x[29] as usize,
313-
)
311+
TrapRegisters {
312+
pc: cx.mc_gpregs.gp_elr as usize,
313+
fp: cx.mc_gpregs.gp_x[29] as usize,
314+
}
314315
} else if #[cfg(all(target_os = "openbsd", target_arch = "x86_64"))] {
315316
let cx = &*(cx as *const libc::ucontext_t);
316-
(
317-
cx.sc_rip as *const u8,
318-
cx.sc_rbp as usize,
319-
)
317+
TrapRegisters {
318+
pc: cx.sc_rip as usize,
319+
fp: cx.sc_rbp as usize,
320+
}
320321
}
321322
else {
322323
compile_error!("unsupported platform");

crates/wasmtime/src/runtime/vm/sys/windows/traphandlers.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::runtime::vm::traphandlers::{tls, TrapTest};
1+
use crate::runtime::vm::traphandlers::{tls, TrapRegisters, TrapTest};
22
use crate::runtime::vm::VMContext;
33
use std::ffi::c_void;
44
use std::io;
@@ -96,13 +96,18 @@ unsafe extern "system" fn exception_handler(exception_info: *mut EXCEPTION_POINT
9696
Some(info) => info,
9797
None => return ExceptionContinueSearch,
9898
};
99+
let context = &*(*exception_info).ContextRecord;
99100
cfg_if::cfg_if! {
100101
if #[cfg(target_arch = "x86_64")] {
101-
let ip = (*(*exception_info).ContextRecord).Rip as *const u8;
102-
let fp = (*(*exception_info).ContextRecord).Rbp as usize;
102+
let regs = TrapRegisters {
103+
pc: context.Rip as usize,
104+
fp: context.Rbp as usize,
105+
};
103106
} else if #[cfg(target_arch = "aarch64")] {
104-
let ip = (*(*exception_info).ContextRecord).Pc as *const u8;
105-
let fp = (*(*exception_info).ContextRecord).Anonymous.Anonymous.Fp as usize;
107+
let regs = TrapRegisters {
108+
pc: context.Pc as usize,
109+
fp: context.Anonymous.Anonymous.Fp as usize,
110+
};
106111
} else {
107112
compile_error!("unsupported platform");
108113
}
@@ -117,13 +122,10 @@ unsafe extern "system" fn exception_handler(exception_info: *mut EXCEPTION_POINT
117122
} else {
118123
None
119124
};
120-
match info.test_if_trap(ip, |handler| handler(exception_info)) {
125+
match info.test_if_trap(regs, faulting_addr, |handler| handler(exception_info)) {
121126
TrapTest::NotWasm => ExceptionContinueSearch,
122127
TrapTest::HandledByEmbedder => ExceptionContinueExecution,
123-
TrapTest::Trap { jmp_buf, trap } => {
124-
info.set_jit_trap(ip, fp, faulting_addr, trap);
125-
wasmtime_longjmp(jmp_buf)
126-
}
128+
TrapTest::Trap { jmp_buf } => wasmtime_longjmp(jmp_buf),
127129
}
128130
})
129131
}

crates/wasmtime/src/runtime/vm/traphandlers.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ impl From<wasmtime_environ::Trap> for TrapReason {
219219
}
220220
}
221221

222+
pub(crate) struct TrapRegisters {
223+
pub pc: usize,
224+
pub fp: usize,
225+
}
226+
222227
/// Return value from `test_if_trap`.
223228
pub(crate) enum TrapTest {
224229
/// Not a wasm trap, need to delegate to whatever process handler is next.
@@ -230,8 +235,6 @@ pub(crate) enum TrapTest {
230235
Trap {
231236
/// How to longjmp back to the original wasm frame.
232237
jmp_buf: *const u8,
233-
/// The trap code of this trap.
234-
trap: wasmtime_environ::Trap,
235238
},
236239
}
237240

@@ -455,7 +458,8 @@ impl CallThreadState {
455458
#[cfg_attr(miri, allow(dead_code))] // miri doesn't handle traps yet
456459
pub(crate) fn test_if_trap(
457460
&self,
458-
pc: *const u8,
461+
regs: TrapRegisters,
462+
faulting_addr: Option<usize>,
459463
call_handler: impl Fn(&SignalHandler) -> bool,
460464
) -> TrapTest {
461465
// If we haven't even started to handle traps yet, bail out.
@@ -473,19 +477,20 @@ impl CallThreadState {
473477
}
474478

475479
// If this fault wasn't in wasm code, then it's not our problem
476-
let Some((code, text_offset)) = lookup_code(pc as usize) else {
480+
let Some((code, text_offset)) = lookup_code(regs.pc) else {
477481
return TrapTest::NotWasm;
478482
};
479483

480484
let Some(trap) = code.lookup_trap_code(text_offset) else {
481485
return TrapTest::NotWasm;
482486
};
483487

488+
self.set_jit_trap(regs, faulting_addr, trap);
489+
484490
// If all that passed then this is indeed a wasm trap, so return the
485491
// `jmp_buf` passed to `wasmtime_longjmp` to resume.
486492
TrapTest::Trap {
487493
jmp_buf: self.take_jmp_buf(),
488-
trap,
489494
}
490495
}
491496

@@ -496,17 +501,16 @@ impl CallThreadState {
496501
#[cfg_attr(miri, allow(dead_code))] // miri doesn't handle traps yet
497502
pub(crate) fn set_jit_trap(
498503
&self,
499-
pc: *const u8,
500-
fp: usize,
504+
TrapRegisters { pc, fp, .. }: TrapRegisters,
501505
faulting_addr: Option<usize>,
502506
trap: wasmtime_environ::Trap,
503507
) {
504-
let backtrace = self.capture_backtrace(self.limits, Some((pc as usize, fp)));
505-
let coredump = self.capture_coredump(self.limits, Some((pc as usize, fp)));
508+
let backtrace = self.capture_backtrace(self.limits, Some((pc, fp)));
509+
let coredump = self.capture_coredump(self.limits, Some((pc, fp)));
506510
unsafe {
507511
(*self.unwind.get()).as_mut_ptr().write((
508512
UnwindReason::Trap(TrapReason::Jit {
509-
pc: pc as usize,
513+
pc,
510514
faulting_addr,
511515
trap,
512516
}),

0 commit comments

Comments
 (0)