forked from bytecodealliance/wasmtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtrap_encoding.rs
More file actions
286 lines (226 loc) · 11.6 KB
/
trap_encoding.rs
File metadata and controls
286 lines (226 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
use core::fmt;
use object::{Bytes, LittleEndian, U32};
/// Information about trap.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TrapInformation {
/// The offset of the trapping instruction in native code.
///
/// This is relative to the beginning of the function.
pub code_offset: u32,
/// Code of the trap.
pub trap_code: Trap,
}
macro_rules! generate_trap_type {
(pub enum Trap {
$(
$(#[$doc:meta])*
$name:ident = $msg:tt,
)*
}) => {
#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[expect(missing_docs, reason = "self-describing variants")]
pub enum Trap {
$(
$(#[$doc])*
$name,
)*
}
impl Trap {
/// Converts a byte back into a `Trap` if its in-bounds
pub fn from_u8(byte: u8) -> Option<Trap> {
$(
if byte == Trap::$name as u8 {
return Some(Trap::$name);
}
)*
None
}
}
impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let desc = match self {
$(Self::$name => $msg,)*
};
write!(f, "wasm trap: {desc}")
}
}
}
}
// The code can be accessed from the c-api, where the possible values are
// translated into enum values defined there:
//
// * the const assertions in c-api/src/trap.rs, and
// * `wasmtime_trap_code_enum` in c-api/include/wasmtime/trap.h.
//
// These need to be kept in sync.
generate_trap_type! {
pub enum Trap {
/// The current stack space was exhausted.
StackOverflow = "call stack exhausted",
/// An out-of-bounds memory access.
MemoryOutOfBounds = "out of bounds memory access",
/// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address.
HeapMisaligned = "unaligned atomic",
/// An out-of-bounds access to a table.
TableOutOfBounds = "undefined element: out of bounds table access",
/// Indirect call to a null table entry.
IndirectCallToNull = "uninitialized element",
/// Signature mismatch on indirect call.
BadSignature = "indirect call type mismatch",
/// An integer arithmetic operation caused an overflow.
IntegerOverflow = "integer overflow",
/// An integer division by zero.
IntegerDivisionByZero = "integer divide by zero",
/// Failed float-to-int conversion.
BadConversionToInteger = "invalid conversion to integer",
/// Code that was supposed to have been unreachable was reached.
UnreachableCodeReached = "wasm `unreachable` instruction executed",
/// Execution has potentially run too long and may be interrupted.
Interrupt = "interrupt",
/// When wasm code is configured to consume fuel and it runs out of fuel
/// then this trap will be raised.
OutOfFuel = "all fuel consumed by WebAssembly",
/// Used to indicate that a trap was raised by atomic wait operations on non shared memory.
AtomicWaitNonSharedMemory = "atomic wait on non-shared memory",
/// Call to a null reference.
NullReference = "null reference",
/// Attempt to access beyond the bounds of an array.
ArrayOutOfBounds = "out of bounds array access",
/// Attempted an allocation that was too large to succeed.
AllocationTooLarge = "allocation size too large",
/// Attempted to cast a reference to a type that it is not an instance of.
CastFailure = "cast failure",
/// When the `component-model` feature is enabled this trap represents a
/// scenario where one component tried to call another component but it
/// would have violated the reentrance rules of the component model,
/// triggering a trap instead.
CannotEnterComponent = "cannot enter component instance",
/// Async-lifted export failed to produce a result by calling `task.return`
/// before returning `STATUS_DONE` and/or after all host tasks completed.
NoAsyncResult = "async-lifted export failed to produce a result",
/// We are suspending to a tag for which there is no active handler.
UnhandledTag = "unhandled tag",
/// Attempt to resume a continuation twice.
ContinuationAlreadyConsumed = "continuation already consumed",
/// A Pulley opcode was executed at runtime when the opcode was disabled at
/// compile time.
DisabledOpcode = "pulley opcode disabled at compile time was executed",
/// Async event loop deadlocked; i.e. it cannot make further progress given
/// that all host tasks have completed and any/all host-owned stream/future
/// handles have been dropped.
AsyncDeadlock = "deadlock detected: event loop cannot make further progress",
/// When the `component-model` feature is enabled this trap represents a
/// scenario where a component instance tried to call an import or intrinsic
/// when it wasn't allowed to, e.g. from a post-return function.
CannotLeaveComponent = "cannot leave component instance",
/// A synchronous task attempted to make a potentially blocking call prior
/// to returning.
CannotBlockSyncTask = "cannot block a synchronous task before returning",
/// A component tried to lift a `char` with an invalid bit pattern.
InvalidChar = "invalid `char` bit pattern",
/// Debug assertion generated for a fused adapter regarding the expected
/// completion of a string encoding operation.
DebugAssertStringEncodingFinished = "should have finished string encoding",
/// Debug assertion generated for a fused adapter regarding a string
/// encoding operation.
DebugAssertEqualCodeUnits = "code units should be equal",
/// Debug assertion generated for a fused adapter regarding the alignment of
/// a pointer.
DebugAssertPointerAligned = "pointer should be aligned",
/// Debug assertion generated for a fused adapter regarding the upper bits
/// of a 64-bit value.
DebugAssertUpperBitsUnset = "upper bits should be unset",
/// A component tried to lift or lower a string past the end of its memory.
StringOutOfBounds = "string content out-of-bounds",
/// A component tried to lift or lower a list past the end of its memory.
ListOutOfBounds = "list content out-of-bounds",
/// A component used an invalid discriminant when lowering a variant value.
InvalidDiscriminant = "invalid variant discriminant",
/// A component passed an unaligned pointer when lifting or lowering a
/// value.
UnalignedPointer = "unaligned pointer",
/// `task.cancel` was called by a task which has not been cancelled.
TaskCancelNotCancelled = "`task.cancel` called by task which has not been cancelled",
/// `task.return` or `task.cancel` was called more than once for the
/// current task.
TaskCancelOrReturnTwice = "`task.return` or `task.cancel` called more than once for current task",
/// `subtask.cancel` was called after terminal status was already
/// delivered.
SubtaskCancelAfterTerminal = "`subtask.cancel` called after terminal status delivered",
/// Invalid `task.return` signature and/or options for the current task.
TaskReturnInvalid = "invalid `task.return` signature and/or options for current task",
/// Cannot drop waitable set with waiters in it.
WaitableSetDropHasWaiters = "cannot drop waitable set with waiters",
/// Cannot drop a subtask which has not yet resolved.
SubtaskDropNotResolved = "cannot drop a subtask which has not yet resolved",
/// Start function does not match the expected type.
ThreadNewIndirectInvalidType = "start function does not match expected type (currently only `(i32) -> ()` is supported)",
/// The start function index points to an uninitialized function.
ThreadNewIndirectUninitialized = "the start function index points to an uninitialized function",
/// Backpressure-related intrinsics overflowed the built-in 16-bit
/// counter.
BackpressureOverflow = "backpressure counter overflow",
/// Invalid code returned from `callback` of `async`-lifted function.
UnsupportedCallbackCode = "unsupported callback code",
/// Cannot resume a thread which is not suspended.
CannotResumeThread = "cannot resume thread which is not suspended",
/// Cannot issue a read/write on a future/stream while there is a
/// pending operation already.
ConcurrentFutureStreamOp = "cannot have concurrent operations active on a future/stream",
/// A reference count (for e.g. an `error-context`) overflowed.
ReferenceCountOverflow = "reference count overflow",
/// A read/write on a stream must be <2**28 items.
StreamOpTooBig = "stream read/write count too large",
// if adding a variant here be sure to update `trap.rs` and `trap.h` as
// mentioned above
}
}
impl core::error::Error for Trap {}
/// Decodes the provided trap information section and attempts to find the trap
/// code corresponding to the `offset` specified.
///
/// The `section` provided is expected to have been built by
/// `TrapEncodingBuilder` above. Additionally the `offset` should be a relative
/// offset within the text section of the compilation image.
pub fn lookup_trap_code(section: &[u8], offset: usize) -> Option<Trap> {
let (offsets, traps) = parse(section)?;
// The `offsets` table is sorted in the trap section so perform a binary
// search of the contents of this section to find whether `offset` is an
// entry in the section. Note that this is a precise search because trap pcs
// should always be precise as well as our metadata about them, which means
// we expect an exact match to correspond to a trap opcode.
//
// Once an index is found within the `offsets` array then that same index is
// used to lookup from the `traps` list of bytes to get the trap code byte
// corresponding to this offset.
let offset = u32::try_from(offset).ok()?;
let index = offsets
.binary_search_by_key(&offset, |val| val.get(LittleEndian))
.ok()?;
debug_assert!(index < traps.len());
let byte = *traps.get(index)?;
let trap = Trap::from_u8(byte);
debug_assert!(trap.is_some(), "missing mapping for {byte}");
trap
}
fn parse(section: &[u8]) -> Option<(&[U32<LittleEndian>], &[u8])> {
let mut section = Bytes(section);
// NB: this matches the encoding written by `append_to` above.
let count = section.read::<U32<LittleEndian>>().ok()?;
let count = usize::try_from(count.get(LittleEndian)).ok()?;
let (offsets, traps) = object::slice_from_bytes::<U32<LittleEndian>>(section.0, count).ok()?;
debug_assert_eq!(traps.len(), count);
Some((offsets, traps))
}
/// Returns an iterator over all of the traps encoded in `section`, which should
/// have been produced by `TrapEncodingBuilder`.
pub fn iterate_traps(section: &[u8]) -> Option<impl Iterator<Item = (u32, Trap)> + '_> {
let (offsets, traps) = parse(section)?;
Some(
offsets
.iter()
.zip(traps)
.map(|(offset, trap)| (offset.get(LittleEndian), Trap::from_u8(*trap).unwrap())),
)
}