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