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