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