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/// CFA-based unwind information used on SystemV.
18pub type CfaUnwindInfo = systemv::UnwindInfo;
19
20/// Expected unwind info type.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[non_exhaustive]
23pub 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]
38pub 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))]
155pub 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
216struct Writer<'a> {
217    buf: &'a mut [u8],
218    offset: usize,
219}
220
221impl<'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}