wasmtime_environ/fact/traps.rs
1//! Module used to encode failure messages associated with traps in an adapter
2//! module.
3//!
4//! This module is a bit forward-looking in an attempt to help assist with
5//! debugging issues with adapter modules and their implementation. This isn't
6//! actually wired up to any decoder at this time and may end up getting deleted
7//! entirely depending on how things go.
8//!
9//! Currently in core wasm the `unreachable` instruction and other traps have no
10//! ability to assign failure messages with traps. This means that if an adapter
11//! fails all you have is the program counter into the wasm function, but
12//! there's not actually any source corresponding to wasm adapters either. This
13//! module is an attempt to assign optional string messages to `unreachable`
14//! trap instructions so, when sufficient debugging options are enabled, these
15//! trap messages could be displayed instead of a bland "unreachable" trap
16//! message.
17//!
18//! This information is currently encoded as a custom section in the wasm
19//! module.
20
21use crate::prelude::*;
22use std::collections::HashMap;
23use std::fmt;
24use wasm_encoder::Encode;
25
26#[derive(Hash, PartialEq, Eq, Copy, Clone)]
27pub enum Trap {
28 CannotLeave,
29 CannotEnter,
30 UnalignedPointer,
31 InvalidDiscriminant,
32 InvalidChar,
33 ListByteLengthOverflow,
34 StringLengthTooBig,
35 StringLengthOverflow,
36 AssertFailed(&'static str),
37}
38
39#[derive(Default)]
40pub struct TrapSection {
41 trap_to_index: HashMap<Trap, usize>,
42 trap_list: Vec<Trap>,
43 function_traps: Vec<(u32, Vec<(usize, usize)>)>,
44}
45
46impl TrapSection {
47 /// Appends a list of traps found within a function.
48 ///
49 /// The `func` is the core wasm function index that is being described. The
50 /// `traps` is a list of `(offset, trap)` where `offset` is the offset
51 /// within the function body itself and `trap` is the description of the
52 /// trap of the opcode at that offset.
53 pub fn append(&mut self, func: u32, traps: Vec<(usize, Trap)>) {
54 if traps.is_empty() {
55 return;
56 }
57
58 // Deduplicate `Trap` annotations to avoid repeating the trap string
59 // internally within the custom section.
60 let traps = traps
61 .into_iter()
62 .map(|(offset, trap)| {
63 let trap = *self.trap_to_index.entry(trap).or_insert_with(|| {
64 let idx = self.trap_list.len();
65 self.trap_list.push(trap);
66 idx
67 });
68 (offset, trap)
69 })
70 .collect();
71 self.function_traps.push((func, traps));
72 }
73
74 /// Creates the custom section payload of this section to be encoded into a
75 /// core wasm module.
76 pub fn finish(self) -> Vec<u8> {
77 let mut data = Vec::new();
78
79 // First append all trap messages which will be indexed below.
80 self.trap_list.len().encode(&mut data);
81 for trap in self.trap_list.iter() {
82 trap.to_string().encode(&mut data);
83 }
84
85 // Afterwards encode trap information for all known functions where
86 // offsets are relative to the body of the function index specified and
87 // the trap message is a pointer into the table built above this.
88 self.function_traps.len().encode(&mut data);
89 for (func, traps) in self.function_traps.iter() {
90 func.encode(&mut data);
91 traps.len().encode(&mut data);
92 for (func_offset, trap_message) in traps {
93 func_offset.encode(&mut data);
94 trap_message.encode(&mut data);
95 }
96 }
97
98 data
99 }
100}
101
102impl fmt::Display for Trap {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match self {
105 Trap::CannotLeave => "cannot leave instance".fmt(f),
106 Trap::CannotEnter => "cannot enter instance".fmt(f),
107 Trap::UnalignedPointer => "pointer not aligned correctly".fmt(f),
108 Trap::InvalidDiscriminant => "invalid variant discriminant".fmt(f),
109 Trap::InvalidChar => "invalid char value specified".fmt(f),
110 Trap::ListByteLengthOverflow => "byte size of list too large for i32".fmt(f),
111 Trap::StringLengthTooBig => "string byte size exceeds maximum".fmt(f),
112 Trap::StringLengthOverflow => "string byte size overflows i32".fmt(f),
113 Trap::AssertFailed(s) => write!(f, "assertion failure: {s}"),
114 }
115 }
116}