Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/unwind.rs
3068 views
1
//! Represents information relating to function unwinding.
2
3
use crate::machinst::RealReg;
4
5
#[cfg(feature = "enable-serde")]
6
use serde_derive::{Deserialize, Serialize};
7
8
#[cfg(feature = "unwind")]
9
pub mod systemv;
10
11
#[cfg(feature = "unwind")]
12
pub mod winx64;
13
14
#[cfg(feature = "unwind")]
15
pub mod winarm64;
16
17
#[cfg(feature = "unwind")]
18
/// CFA-based unwind information used on SystemV.
19
pub type CfaUnwindInfo = systemv::UnwindInfo;
20
21
/// Expected unwind info type.
22
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23
#[non_exhaustive]
24
pub enum UnwindInfoKind {
25
/// No unwind info.
26
None,
27
/// SystemV CIE/FDE unwind info.
28
#[cfg(feature = "unwind")]
29
SystemV,
30
/// Windows X64 Unwind info
31
#[cfg(feature = "unwind")]
32
Windows,
33
}
34
35
/// Represents unwind information for a single function.
36
#[derive(Clone, Debug, PartialEq, Eq)]
37
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
38
#[non_exhaustive]
39
pub enum UnwindInfo {
40
/// Windows x64 ABI unwind information.
41
#[cfg(feature = "unwind")]
42
WindowsX64(winx64::UnwindInfo),
43
/// System V ABI unwind information.
44
#[cfg(feature = "unwind")]
45
SystemV(CfaUnwindInfo),
46
/// Windows Arm64 ABI unwind information.
47
#[cfg(feature = "unwind")]
48
WindowsArm64(winarm64::UnwindInfo),
49
}
50
51
/// Unwind pseudoinstruction used in VCode backends: represents that
52
/// at the present location, an action has just been taken.
53
///
54
/// VCode backends always emit unwind info that is relative to a frame
55
/// pointer, because we are planning to allow for dynamic frame allocation,
56
/// and because it makes the design quite a lot simpler in general: we don't
57
/// have to be precise about SP adjustments throughout the body of the function.
58
///
59
/// We include only unwind info for prologues at this time. Note that unwind
60
/// info for epilogues is only necessary if one expects to unwind while within
61
/// the last few instructions of the function (after FP has been restored) or
62
/// if one wishes to instruction-step through the epilogue and see a backtrace
63
/// at every point. This is not necessary for correct operation otherwise and so
64
/// we simplify the world a bit by omitting epilogue information. (Note that
65
/// some platforms also don't require or have a way to describe unwind
66
/// information for epilogues at all: for example, on Windows, the `UNWIND_INFO`
67
/// format only stores information for the function prologue.)
68
///
69
/// Because we are defining an abstraction over multiple unwind formats (at
70
/// least Windows/fastcall and System V) and multiple architectures (at least
71
/// x86-64 and aarch64), we have to be a little bit flexible in how we describe
72
/// the frame. However, it turns out that a least-common-denominator prologue
73
/// works for all of the cases we have to worry about today!
74
///
75
/// We assume the stack looks something like this:
76
///
77
///
78
/// ```plain
79
/// +----------------------------------------------+
80
/// | stack arg area, etc (according to ABI) |
81
/// | ... |
82
/// SP at call --> +----------------------------------------------+
83
/// | return address (pushed by HW or SW) |
84
/// +----------------------------------------------+
85
/// | old frame pointer (FP) |
86
/// FP in this --> +----------------------------------------------+
87
/// function | clobbered callee-save registers |
88
/// | ... |
89
/// start of --> +----------------------------------------------+
90
/// clobbers | (rest of function's frame, irrelevant here) |
91
/// | ... |
92
/// SP in this --> +----------------------------------------------+
93
/// function
94
/// ```
95
///
96
/// We assume that the prologue consists of:
97
///
98
/// * `PushFrameRegs`: A push operation that adds the old FP to the stack (and
99
/// maybe the link register, on architectures that do not push return addresses
100
/// in hardware)
101
/// * `DefineFrame`: An update that sets FP to SP to establish a new frame
102
/// * `SaveReg`: A number of stores or pushes to the stack to save clobbered registers
103
///
104
/// Each of these steps has a corresponding pseudo-instruction. At each step,
105
/// we need some information to determine where the current stack frame is
106
/// relative to SP or FP. When the `PushFrameRegs` occurs, we need to know how
107
/// much SP was decremented by, so we can allow the unwinder to continue to find
108
/// the caller's frame. When we define the new frame, we need to know where FP
109
/// is in relation to "SP at call" and also "start of clobbers", because
110
/// different unwind formats define one or the other of those as the anchor by
111
/// which we define the frame. Finally, when registers are saved, we need to
112
/// know which ones, and where.
113
///
114
/// Different unwind formats work differently; here is a whirlwind tour of how
115
/// they define frames to help understanding:
116
///
117
/// - Windows unwind information defines a frame that must start below the
118
/// clobber area, because all clobber-save offsets are non-negative. We set it
119
/// at the "start of clobbers" in the figure above. The `UNWIND_INFO` contains
120
/// a "frame pointer offset" field; when we define the new frame, the frame is
121
/// understood to be the value of FP (`RBP`) *minus* this offset. In other
122
/// words, the FP is *at the frame pointer offset* relative to the
123
/// start-of-clobber-frame. We use the "FP offset down to clobber area" offset
124
/// to generate this info.
125
///
126
/// - System V unwind information defines a frame in terms of the CFA
127
/// (call-frame address), which is equal to the "SP at call" above. SysV
128
/// allows negative offsets, so there is no issue defining clobber-save
129
/// locations in terms of CFA. The format allows us to define CFA flexibly in
130
/// terms of any register plus an offset; we define it in terms of FP plus
131
/// the clobber-to-caller-SP offset once FP is established.
132
///
133
/// Note that certain architectures impose limits on offsets: for example, on
134
/// Windows, the base of the clobber area must not be more than 240 bytes below
135
/// FP.
136
///
137
/// Unwind pseudoinstructions are emitted inline by ABI code as it generates
138
/// a prologue. Thus, for the usual case, a prologue might look like (using x64
139
/// as an example):
140
///
141
/// ```plain
142
/// push rbp
143
/// unwind UnwindInst::PushFrameRegs { offset_upward_to_caller_sp: 16 }
144
/// mov rbp, rsp
145
/// unwind UnwindInst::DefineNewFrame { offset_upward_to_caller_sp: 16,
146
/// offset_downward_to_clobbers: 16 }
147
/// sub rsp, 32
148
/// mov [rsp+16], r12
149
/// unwind UnwindInst::SaveReg { reg: R12, clobber_offset: 0 }
150
/// mov [rsp+24], r13
151
/// unwind UnwindInst::SaveReg { reg: R13, clobber_offset: 8 }
152
/// ...
153
/// ```
154
#[derive(Clone, Debug, PartialEq, Eq)]
155
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
156
pub enum UnwindInst {
157
/// The frame-pointer register for this architecture has just been pushed to
158
/// the stack (and on architectures where return-addresses are not pushed by
159
/// hardware, the link register as well). The FP has not been set to this
160
/// frame yet. The current location of SP is such that
161
/// `offset_upward_to_caller_sp` is the distance to SP-at-callsite (our
162
/// caller's frame).
163
PushFrameRegs {
164
/// The offset from the current SP (after push) to the SP at
165
/// caller's callsite.
166
offset_upward_to_caller_sp: u32,
167
},
168
/// The frame-pointer register for this architecture has just been
169
/// set to the current stack location. We wish to define a new
170
/// frame that is anchored on this new FP value. Offsets are provided
171
/// upward to the caller's stack frame and downward toward the clobber
172
/// area. We expect this pseudo-op to come after `PushFrameRegs`.
173
DefineNewFrame {
174
/// The offset from the current SP and FP value upward to the value of
175
/// SP at the callsite that invoked us.
176
offset_upward_to_caller_sp: u32,
177
/// The offset from the current SP and FP value downward to the start of
178
/// the clobber area.
179
offset_downward_to_clobbers: u32,
180
},
181
/// The stack pointer was adjusted to allocate the stack.
182
StackAlloc {
183
/// Size to allocate.
184
size: u32,
185
},
186
/// The stack slot at the given offset from the clobber-area base has been
187
/// used to save the given register.
188
///
189
/// Given that `CreateFrame` has occurred first with some
190
/// `offset_downward_to_clobbers`, `SaveReg` with `clobber_offset` indicates
191
/// that the value of `reg` is saved on the stack at address `FP -
192
/// offset_downward_to_clobbers + clobber_offset`.
193
SaveReg {
194
/// The offset from the start of the clobber area to this register's
195
/// stack location.
196
clobber_offset: u32,
197
/// The saved register.
198
reg: RealReg,
199
},
200
/// Computes the value of the given register in the caller as stack offset.
201
/// Typically used to unwind the stack pointer if the default rule does not apply.
202
/// The `clobber_offset` is computed the same way as for the `SaveReg` rule.
203
RegStackOffset {
204
/// The offset from the start of the clobber area to this register's value.
205
clobber_offset: u32,
206
/// The register whose value is to be set.
207
reg: RealReg,
208
},
209
/// Defines if the aarch64-specific pointer authentication available for ARM v8.3+ devices
210
/// is enabled for certain pointers or not.
211
Aarch64SetPointerAuth {
212
/// Whether return addresses (hold in LR) contain a pointer-authentication code.
213
return_addresses: bool,
214
},
215
}
216
217
struct Writer<'a> {
218
buf: &'a mut [u8],
219
offset: usize,
220
}
221
222
impl<'a> Writer<'a> {
223
pub fn new(buf: &'a mut [u8]) -> Self {
224
Self { buf, offset: 0 }
225
}
226
227
fn write_u8(&mut self, v: u8) {
228
self.buf[self.offset] = v;
229
self.offset += 1;
230
}
231
232
fn write_u16_le(&mut self, v: u16) {
233
self.buf[self.offset..(self.offset + 2)].copy_from_slice(&v.to_le_bytes());
234
self.offset += 2;
235
}
236
237
fn write_u16_be(&mut self, v: u16) {
238
self.buf[self.offset..(self.offset + 2)].copy_from_slice(&v.to_be_bytes());
239
self.offset += 2;
240
}
241
242
fn write_u32_le(&mut self, v: u32) {
243
self.buf[self.offset..(self.offset + 4)].copy_from_slice(&v.to_le_bytes());
244
self.offset += 4;
245
}
246
247
fn write_u32_be(&mut self, v: u32) {
248
self.buf[self.offset..(self.offset + 4)].copy_from_slice(&v.to_be_bytes());
249
self.offset += 4;
250
}
251
}
252
253