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