Path: blob/main/cranelift/codegen/src/isa/unwind.rs
3068 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#[cfg(feature = "unwind")]17/// CFA-based unwind information used on SystemV.18pub type CfaUnwindInfo = systemv::UnwindInfo;1920/// Expected unwind info type.21#[derive(Debug, Clone, Copy, PartialEq, Eq)]22#[non_exhaustive]23pub enum UnwindInfoKind {24/// No unwind info.25None,26/// SystemV CIE/FDE unwind info.27#[cfg(feature = "unwind")]28SystemV,29/// Windows X64 Unwind info30#[cfg(feature = "unwind")]31Windows,32}3334/// 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]38pub enum UnwindInfo {39/// Windows x64 ABI unwind information.40#[cfg(feature = "unwind")]41WindowsX64(winx64::UnwindInfo),42/// System V ABI unwind information.43#[cfg(feature = "unwind")]44SystemV(CfaUnwindInfo),45/// Windows Arm64 ABI unwind information.46#[cfg(feature = "unwind")]47WindowsArm64(winarm64::UnwindInfo),48}4950/// Unwind pseudoinstruction used in VCode backends: represents that51/// at the present location, an action has just been taken.52///53/// VCode backends always emit unwind info that is relative to a frame54/// 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't56/// 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 unwind59/// info for epilogues is only necessary if one expects to unwind while within60/// the last few instructions of the function (after FP has been restored) or61/// if one wishes to instruction-step through the epilogue and see a backtrace62/// at every point. This is not necessary for correct operation otherwise and so63/// we simplify the world a bit by omitting epilogue information. (Note that64/// some platforms also don't require or have a way to describe unwind65/// 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 (at69/// least Windows/fastcall and System V) and multiple architectures (at least70/// x86-64 and aarch64), we have to be a little bit flexible in how we describe71/// the frame. However, it turns out that a least-common-denominator prologue72/// 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/// ```plain78/// +----------------------------------------------+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/// function93/// ```94///95/// We assume that the prologue consists of:96///97/// * `PushFrameRegs`: A push operation that adds the old FP to the stack (and98/// maybe the link register, on architectures that do not push return addresses99/// in hardware)100/// * `DefineFrame`: An update that sets FP to SP to establish a new frame101/// * `SaveReg`: A number of stores or pushes to the stack to save clobbered registers102///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 is105/// relative to SP or FP. When the `PushFrameRegs` occurs, we need to know how106/// much SP was decremented by, so we can allow the unwinder to continue to find107/// the caller's frame. When we define the new frame, we need to know where FP108/// is in relation to "SP at call" and also "start of clobbers", because109/// different unwind formats define one or the other of those as the anchor by110/// which we define the frame. Finally, when registers are saved, we need to111/// know which ones, and where.112///113/// Different unwind formats work differently; here is a whirlwind tour of how114/// they define frames to help understanding:115///116/// - Windows unwind information defines a frame that must start below the117/// clobber area, because all clobber-save offsets are non-negative. We set it118/// at the "start of clobbers" in the figure above. The `UNWIND_INFO` contains119/// a "frame pointer offset" field; when we define the new frame, the frame is120/// understood to be the value of FP (`RBP`) *minus* this offset. In other121/// words, the FP is *at the frame pointer offset* relative to the122/// start-of-clobber-frame. We use the "FP offset down to clobber area" offset123/// to generate this info.124///125/// - System V unwind information defines a frame in terms of the CFA126/// (call-frame address), which is equal to the "SP at call" above. SysV127/// allows negative offsets, so there is no issue defining clobber-save128/// locations in terms of CFA. The format allows us to define CFA flexibly in129/// terms of any register plus an offset; we define it in terms of FP plus130/// the clobber-to-caller-SP offset once FP is established.131///132/// Note that certain architectures impose limits on offsets: for example, on133/// Windows, the base of the clobber area must not be more than 240 bytes below134/// FP.135///136/// Unwind pseudoinstructions are emitted inline by ABI code as it generates137/// a prologue. Thus, for the usual case, a prologue might look like (using x64138/// as an example):139///140/// ```plain141/// push rbp142/// unwind UnwindInst::PushFrameRegs { offset_upward_to_caller_sp: 16 }143/// mov rbp, rsp144/// unwind UnwindInst::DefineNewFrame { offset_upward_to_caller_sp: 16,145/// offset_downward_to_clobbers: 16 }146/// sub rsp, 32147/// mov [rsp+16], r12148/// unwind UnwindInst::SaveReg { reg: R12, clobber_offset: 0 }149/// mov [rsp+24], r13150/// 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))]155pub enum UnwindInst {156/// The frame-pointer register for this architecture has just been pushed to157/// the stack (and on architectures where return-addresses are not pushed by158/// hardware, the link register as well). The FP has not been set to this159/// frame yet. The current location of SP is such that160/// `offset_upward_to_caller_sp` is the distance to SP-at-callsite (our161/// caller's frame).162PushFrameRegs {163/// The offset from the current SP (after push) to the SP at164/// caller's callsite.165offset_upward_to_caller_sp: u32,166},167/// The frame-pointer register for this architecture has just been168/// set to the current stack location. We wish to define a new169/// frame that is anchored on this new FP value. Offsets are provided170/// upward to the caller's stack frame and downward toward the clobber171/// area. We expect this pseudo-op to come after `PushFrameRegs`.172DefineNewFrame {173/// The offset from the current SP and FP value upward to the value of174/// SP at the callsite that invoked us.175offset_upward_to_caller_sp: u32,176/// The offset from the current SP and FP value downward to the start of177/// the clobber area.178offset_downward_to_clobbers: u32,179},180/// The stack pointer was adjusted to allocate the stack.181StackAlloc {182/// Size to allocate.183size: u32,184},185/// The stack slot at the given offset from the clobber-area base has been186/// used to save the given register.187///188/// Given that `CreateFrame` has occurred first with some189/// `offset_downward_to_clobbers`, `SaveReg` with `clobber_offset` indicates190/// that the value of `reg` is saved on the stack at address `FP -191/// offset_downward_to_clobbers + clobber_offset`.192SaveReg {193/// The offset from the start of the clobber area to this register's194/// stack location.195clobber_offset: u32,196/// The saved register.197reg: 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.202RegStackOffset {203/// The offset from the start of the clobber area to this register's value.204clobber_offset: u32,205/// The register whose value is to be set.206reg: RealReg,207},208/// Defines if the aarch64-specific pointer authentication available for ARM v8.3+ devices209/// is enabled for certain pointers or not.210Aarch64SetPointerAuth {211/// Whether return addresses (hold in LR) contain a pointer-authentication code.212return_addresses: bool,213},214}215216struct Writer<'a> {217buf: &'a mut [u8],218offset: usize,219}220221impl<'a> Writer<'a> {222pub fn new(buf: &'a mut [u8]) -> Self {223Self { buf, offset: 0 }224}225226fn write_u8(&mut self, v: u8) {227self.buf[self.offset] = v;228self.offset += 1;229}230231fn write_u16_le(&mut self, v: u16) {232self.buf[self.offset..(self.offset + 2)].copy_from_slice(&v.to_le_bytes());233self.offset += 2;234}235236fn write_u16_be(&mut self, v: u16) {237self.buf[self.offset..(self.offset + 2)].copy_from_slice(&v.to_be_bytes());238self.offset += 2;239}240241fn write_u32_le(&mut self, v: u32) {242self.buf[self.offset..(self.offset + 4)].copy_from_slice(&v.to_le_bytes());243self.offset += 4;244}245246fn write_u32_be(&mut self, v: u32) {247self.buf[self.offset..(self.offset + 4)].copy_from_slice(&v.to_be_bytes());248self.offset += 4;249}250}251252253