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 #[cfg(feature = "unwind")]
18 /// CFA-based unwind information used on SystemV.
19 pub type CfaUnwindInfo = systemv::UnwindInfo;
20 
21 /// Expected unwind info type.
22 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
23 #[non_exhaustive]
24 pub 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]
39 pub 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))]
156 pub 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 
217 struct Writer<'a> {
218     buf: &'a mut [u8],
219     offset: usize,
220 }
221 
222 impl<'a> Writer<'a> {
new(buf: &'a mut [u8]) -> Self223     pub fn new(buf: &'a mut [u8]) -> Self {
224         Self { buf, offset: 0 }
225     }
226 
write_u8(&mut self, v: u8)227     fn write_u8(&mut self, v: u8) {
228         self.buf[self.offset] = v;
229         self.offset += 1;
230     }
231 
write_u16_le(&mut self, v: u16)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 
write_u16_be(&mut self, v: u16)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 
write_u32_le(&mut self, v: u32)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 
write_u32_be(&mut self, v: u32)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 }
252