Path: blob/main/cranelift/codegen/src/isa/unwind.rs
1693 views
//! Represents information relating to function unwinding.12use crate::machinst::RealReg;34#[cfg(feature = "enable-serde")]5use serde_derive::{Deserialize, Serialize};67#[cfg(feature = "unwind")]8pub mod systemv;910#[cfg(feature = "unwind")]11pub mod winx64;1213#[cfg(feature = "unwind")]14pub mod winarm64;1516/// CFA-based unwind information used on SystemV.17pub type CfaUnwindInfo = systemv::UnwindInfo;1819/// Expected unwind info type.20#[derive(Debug, Clone, Copy, PartialEq, Eq)]21#[non_exhaustive]22pub enum UnwindInfoKind {23/// No unwind info.24None,25/// SystemV CIE/FDE unwind info.26#[cfg(feature = "unwind")]27SystemV,28/// Windows X64 Unwind info29#[cfg(feature = "unwind")]30Windows,31}3233/// Represents unwind information for a single function.34#[derive(Clone, Debug, PartialEq, Eq)]35#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]36#[non_exhaustive]37pub enum UnwindInfo {38/// Windows x64 ABI unwind information.39#[cfg(feature = "unwind")]40WindowsX64(winx64::UnwindInfo),41/// System V ABI unwind information.42#[cfg(feature = "unwind")]43SystemV(CfaUnwindInfo),44/// Windows Arm64 ABI unwind information.45#[cfg(feature = "unwind")]46WindowsArm64(winarm64::UnwindInfo),47}4849/// Unwind pseudoinstruction used in VCode backends: represents that50/// at the present location, an action has just been taken.51///52/// VCode backends always emit unwind info that is relative to a frame53/// pointer, because we are planning to allow for dynamic frame allocation,54/// and because it makes the design quite a lot simpler in general: we don't55/// have to be precise about SP adjustments throughout the body of the function.56///57/// We include only unwind info for prologues at this time. Note that unwind58/// info for epilogues is only necessary if one expects to unwind while within59/// the last few instructions of the function (after FP has been restored) or60/// if one wishes to instruction-step through the epilogue and see a backtrace61/// at every point. This is not necessary for correct operation otherwise and so62/// we simplify the world a bit by omitting epilogue information. (Note that63/// some platforms also don't require or have a way to describe unwind64/// information for epilogues at all: for example, on Windows, the `UNWIND_INFO`65/// format only stores information for the function prologue.)66///67/// Because we are defining an abstraction over multiple unwind formats (at68/// least Windows/fastcall and System V) and multiple architectures (at least69/// x86-64 and aarch64), we have to be a little bit flexible in how we describe70/// the frame. However, it turns out that a least-common-denominator prologue71/// works for all of the cases we have to worry about today!72///73/// We assume the stack looks something like this:74///75///76/// ```plain77/// +----------------------------------------------+78/// | stack arg area, etc (according to ABI) |79/// | ... |80/// SP at call --> +----------------------------------------------+81/// | return address (pushed by HW or SW) |82/// +----------------------------------------------+83/// | old frame pointer (FP) |84/// FP in this --> +----------------------------------------------+85/// function | clobbered callee-save registers |86/// | ... |87/// start of --> +----------------------------------------------+88/// clobbers | (rest of function's frame, irrelevant here) |89/// | ... |90/// SP in this --> +----------------------------------------------+91/// function92/// ```93///94/// We assume that the prologue consists of:95///96/// * `PushFrameRegs`: A push operation that adds the old FP to the stack (and97/// maybe the link register, on architectures that do not push return addresses98/// in hardware)99/// * `DefineFrame`: An update that sets FP to SP to establish a new frame100/// * `SaveReg`: A number of stores or pushes to the stack to save clobbered registers101///102/// Each of these steps has a corresponding pseudo-instruction. At each step,103/// we need some information to determine where the current stack frame is104/// relative to SP or FP. When the `PushFrameRegs` occurs, we need to know how105/// much SP was decremented by, so we can allow the unwinder to continue to find106/// the caller's frame. When we define the new frame, we need to know where FP107/// is in relation to "SP at call" and also "start of clobbers", because108/// different unwind formats define one or the other of those as the anchor by109/// which we define the frame. Finally, when registers are saved, we need to110/// know which ones, and where.111///112/// Different unwind formats work differently; here is a whirlwind tour of how113/// they define frames to help understanding:114///115/// - Windows unwind information defines a frame that must start below the116/// clobber area, because all clobber-save offsets are non-negative. We set it117/// at the "start of clobbers" in the figure above. The `UNWIND_INFO` contains118/// a "frame pointer offset" field; when we define the new frame, the frame is119/// understood to be the value of FP (`RBP`) *minus* this offset. In other120/// words, the FP is *at the frame pointer offset* relative to the121/// start-of-clobber-frame. We use the "FP offset down to clobber area" offset122/// to generate this info.123///124/// - System V unwind information defines a frame in terms of the CFA125/// (call-frame address), which is equal to the "SP at call" above. SysV126/// allows negative offsets, so there is no issue defining clobber-save127/// locations in terms of CFA. The format allows us to define CFA flexibly in128/// terms of any register plus an offset; we define it in terms of FP plus129/// the clobber-to-caller-SP offset once FP is established.130///131/// Note that certain architectures impose limits on offsets: for example, on132/// Windows, the base of the clobber area must not be more than 240 bytes below133/// FP.134///135/// Unwind pseudoinstructions are emitted inline by ABI code as it generates136/// a prologue. Thus, for the usual case, a prologue might look like (using x64137/// as an example):138///139/// ```plain140/// push rbp141/// unwind UnwindInst::PushFrameRegs { offset_upward_to_caller_sp: 16 }142/// mov rbp, rsp143/// unwind UnwindInst::DefineNewFrame { offset_upward_to_caller_sp: 16,144/// offset_downward_to_clobbers: 16 }145/// sub rsp, 32146/// mov [rsp+16], r12147/// unwind UnwindInst::SaveReg { reg: R12, clobber_offset: 0 }148/// mov [rsp+24], r13149/// unwind UnwindInst::SaveReg { reg: R13, clobber_offset: 8 }150/// ...151/// ```152#[derive(Clone, Debug, PartialEq, Eq)]153#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]154pub enum UnwindInst {155/// The frame-pointer register for this architecture has just been pushed to156/// the stack (and on architectures where return-addresses are not pushed by157/// hardware, the link register as well). The FP has not been set to this158/// frame yet. The current location of SP is such that159/// `offset_upward_to_caller_sp` is the distance to SP-at-callsite (our160/// caller's frame).161PushFrameRegs {162/// The offset from the current SP (after push) to the SP at163/// caller's callsite.164offset_upward_to_caller_sp: u32,165},166/// The frame-pointer register for this architecture has just been167/// set to the current stack location. We wish to define a new168/// frame that is anchored on this new FP value. Offsets are provided169/// upward to the caller's stack frame and downward toward the clobber170/// area. We expect this pseudo-op to come after `PushFrameRegs`.171DefineNewFrame {172/// The offset from the current SP and FP value upward to the value of173/// SP at the callsite that invoked us.174offset_upward_to_caller_sp: u32,175/// The offset from the current SP and FP value downward to the start of176/// the clobber area.177offset_downward_to_clobbers: u32,178},179/// The stack pointer was adjusted to allocate the stack.180StackAlloc {181/// Size to allocate.182size: u32,183},184/// The stack slot at the given offset from the clobber-area base has been185/// used to save the given register.186///187/// Given that `CreateFrame` has occurred first with some188/// `offset_downward_to_clobbers`, `SaveReg` with `clobber_offset` indicates189/// that the value of `reg` is saved on the stack at address `FP -190/// offset_downward_to_clobbers + clobber_offset`.191SaveReg {192/// The offset from the start of the clobber area to this register's193/// stack location.194clobber_offset: u32,195/// The saved register.196reg: RealReg,197},198/// Computes the value of the given register in the caller as stack offset.199/// Typically used to unwind the stack pointer if the default rule does not apply.200/// The `clobber_offset` is computed the same way as for the `SaveReg` rule.201RegStackOffset {202/// The offset from the start of the clobber area to this register's value.203clobber_offset: u32,204/// The register whose value is to be set.205reg: RealReg,206},207/// Defines if the aarch64-specific pointer authentication available for ARM v8.3+ devices208/// is enabled for certain pointers or not.209Aarch64SetPointerAuth {210/// Whether return addresses (hold in LR) contain a pointer-authentication code.211return_addresses: bool,212},213}214215struct Writer<'a> {216buf: &'a mut [u8],217offset: usize,218}219220impl<'a> Writer<'a> {221pub fn new(buf: &'a mut [u8]) -> Self {222Self { buf, offset: 0 }223}224225fn write_u8(&mut self, v: u8) {226self.buf[self.offset] = v;227self.offset += 1;228}229230fn write_u16_le(&mut self, v: u16) {231self.buf[self.offset..(self.offset + 2)].copy_from_slice(&v.to_le_bytes());232self.offset += 2;233}234235fn write_u16_be(&mut self, v: u16) {236self.buf[self.offset..(self.offset + 2)].copy_from_slice(&v.to_be_bytes());237self.offset += 2;238}239240fn write_u32_le(&mut self, v: u32) {241self.buf[self.offset..(self.offset + 4)].copy_from_slice(&v.to_le_bytes());242self.offset += 4;243}244245fn write_u32_be(&mut self, v: u32) {246self.buf[self.offset..(self.offset + 4)].copy_from_slice(&v.to_be_bytes());247self.offset += 4;248}249}250251252