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