cranelift_codegen/isa/unwind.rs
1//! Represents information relating to function unwinding.
2
3use crate::machinst::RealReg;
4
5#[cfg(feature = "enable-serde")]
6use serde_derive::{Deserialize, Serialize};
7
8#[cfg(feature = "unwind")]
9pub mod systemv;
10
11#[cfg(feature = "unwind")]
12pub mod winx64;
13
14#[cfg(feature = "unwind")]
15pub mod winarm64;
16
17#[cfg(feature = "unwind")]
18/// CFA-based unwind information used on SystemV.
19pub type CfaUnwindInfo = systemv::UnwindInfo;
20
21/// Expected unwind info type.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23#[non_exhaustive]
24pub 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]
39pub 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))]
156pub 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
217struct Writer<'a> {
218 buf: &'a mut [u8],
219 offset: usize,
220}
221
222impl<'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}