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