Path: blob/main/cranelift/codegen/src/isa/unwind/winx64.rs
1693 views
//! Windows x64 ABI unwind information.12use alloc::vec::Vec;3use log::warn;4#[cfg(feature = "enable-serde")]5use serde_derive::{Deserialize, Serialize};67use crate::binemit::CodeOffset;8use crate::isa::unwind::UnwindInst;9use crate::result::{CodegenError, CodegenResult};1011use super::Writer;1213/// Maximum (inclusive) size of a "small" stack allocation14const SMALL_ALLOC_MAX_SIZE: u32 = 128;15/// Maximum (inclusive) size of a "large" stack allocation that can represented in 16-bits16const LARGE_ALLOC_16BIT_MAX_SIZE: u32 = 524280;1718/// The supported unwind codes for the x64 Windows ABI.19///20/// See: <https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64>21/// Only what is needed to describe the prologues generated by the Cranelift x86 ISA are represented here.22/// Note: the Cranelift x86 ISA RU enum matches the Windows unwind GPR encoding values.23#[derive(Clone, Debug, PartialEq, Eq)]24#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]25pub(crate) enum UnwindCode {26PushRegister {27instruction_offset: u8,28reg: u8,29},30SaveReg {31instruction_offset: u8,32reg: u8,33stack_offset: u32,34},35SaveXmm {36instruction_offset: u8,37reg: u8,38stack_offset: u32,39},40StackAlloc {41instruction_offset: u8,42size: u32,43},44SetFPReg {45instruction_offset: u8,46},47}4849impl UnwindCode {50fn emit(&self, writer: &mut Writer) {51enum UnwindOperation {52PushNonvolatileRegister = 0,53LargeStackAlloc = 1,54SmallStackAlloc = 2,55SetFPReg = 3,56SaveNonVolatileRegister = 4,57SaveNonVolatileRegisterFar = 5,58SaveXmm128 = 8,59SaveXmm128Far = 9,60}6162match self {63Self::PushRegister {64instruction_offset,65reg,66} => {67writer.write_u8(*instruction_offset);68writer.write_u8((*reg << 4) | (UnwindOperation::PushNonvolatileRegister as u8));69}70Self::SaveReg {71instruction_offset,72reg,73stack_offset,74}75| Self::SaveXmm {76instruction_offset,77reg,78stack_offset,79} => {80let is_xmm = match self {81Self::SaveXmm { .. } => true,82_ => false,83};84let (op_small, op_large) = if is_xmm {85(UnwindOperation::SaveXmm128, UnwindOperation::SaveXmm128Far)86} else {87(88UnwindOperation::SaveNonVolatileRegister,89UnwindOperation::SaveNonVolatileRegisterFar,90)91};92writer.write_u8(*instruction_offset);93let scaled_stack_offset = stack_offset / 16;94if scaled_stack_offset <= core::u16::MAX as u32 {95writer.write_u8((*reg << 4) | (op_small as u8));96writer.write_u16_le(scaled_stack_offset as u16);97} else {98writer.write_u8((*reg << 4) | (op_large as u8));99writer.write_u16_le(*stack_offset as u16);100writer.write_u16_le((stack_offset >> 16) as u16);101}102}103Self::StackAlloc {104instruction_offset,105size,106} => {107// Stack allocations on Windows must be a multiple of 8 and be at least 1 slot108assert!(*size >= 8);109assert!((*size % 8) == 0);110111writer.write_u8(*instruction_offset);112if *size <= SMALL_ALLOC_MAX_SIZE {113writer.write_u8(114((((*size - 8) / 8) as u8) << 4) | UnwindOperation::SmallStackAlloc as u8,115);116} else if *size <= LARGE_ALLOC_16BIT_MAX_SIZE {117writer.write_u8(UnwindOperation::LargeStackAlloc as u8);118writer.write_u16_le((*size / 8) as u16);119} else {120writer.write_u8((1 << 4) | (UnwindOperation::LargeStackAlloc as u8));121writer.write_u32_le(*size);122}123}124Self::SetFPReg { instruction_offset } => {125writer.write_u8(*instruction_offset);126writer.write_u8(UnwindOperation::SetFPReg as u8);127}128}129}130131fn node_count(&self) -> usize {132match self {133Self::StackAlloc { size, .. } => {134if *size <= SMALL_ALLOC_MAX_SIZE {1351136} else if *size <= LARGE_ALLOC_16BIT_MAX_SIZE {1372138} else {1393140}141}142Self::SaveXmm { stack_offset, .. } | Self::SaveReg { stack_offset, .. } => {143if *stack_offset <= core::u16::MAX as u32 {1442145} else {1463147}148}149_ => 1,150}151}152}153154pub(crate) enum MappedRegister {155Int(u8),156Xmm(u8),157}158159/// Maps UnwindInfo register to Windows x64 unwind data.160pub(crate) trait RegisterMapper<Reg> {161/// Maps a Reg to a Windows unwind register number.162fn map(reg: Reg) -> MappedRegister;163}164165/// Represents Windows x64 unwind information.166///167/// For information about Windows x64 unwind info, see:168/// <https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64>169#[derive(Clone, Debug, PartialEq, Eq)]170#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]171pub struct UnwindInfo {172pub(crate) flags: u8,173pub(crate) prologue_size: u8,174pub(crate) frame_register: Option<u8>,175pub(crate) frame_register_offset: u8,176pub(crate) unwind_codes: Vec<UnwindCode>,177}178179impl UnwindInfo {180/// Gets the emit size of the unwind information, in bytes.181pub fn emit_size(&self) -> usize {182let node_count = self.node_count();183184// Calculation of the size requires no SEH handler or chained info185assert!(self.flags == 0);186187// Size of fixed part of UNWIND_INFO is 4 bytes188// Then comes the UNWIND_CODE nodes (2 bytes each)189// Then comes 2 bytes of padding for the unwind codes if necessary190// Next would come the SEH data, but we assert above that the function doesn't have SEH data1911924 + (node_count * 2) + if (node_count & 1) == 1 { 2 } else { 0 }193}194195/// Emits the unwind information into the given mutable byte slice.196///197/// This function will panic if the slice is not at least `emit_size` in length.198pub fn emit(&self, buf: &mut [u8]) {199const UNWIND_INFO_VERSION: u8 = 1;200201let node_count = self.node_count();202assert!(node_count <= 256);203204let mut writer = Writer::new(buf);205206writer.write_u8((self.flags << 3) | UNWIND_INFO_VERSION);207writer.write_u8(self.prologue_size);208writer.write_u8(node_count as u8);209210if let Some(reg) = self.frame_register {211writer.write_u8((self.frame_register_offset << 4) | reg);212} else {213writer.write_u8(0);214}215216// Unwind codes are written in reverse order (prologue offset descending)217for code in self.unwind_codes.iter().rev() {218code.emit(&mut writer);219}220221// To keep a 32-bit alignment, emit 2 bytes of padding if there's an odd number of 16-bit nodes222if (node_count & 1) == 1 {223writer.write_u16_le(0);224}225226// Ensure the correct number of bytes was emitted227assert_eq!(writer.offset, self.emit_size());228}229230fn node_count(&self) -> usize {231self.unwind_codes232.iter()233.fold(0, |nodes, c| nodes + c.node_count())234}235}236237const UNWIND_RBP_REG: u8 = 5;238239pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<crate::machinst::Reg>>(240insts: &[(CodeOffset, UnwindInst)],241) -> CodegenResult<UnwindInfo> {242let mut unwind_codes = vec![];243let mut frame_register_offset = 0;244let mut max_unwind_offset = 0;245for &(instruction_offset, ref inst) in insts {246let instruction_offset = ensure_unwind_offset(instruction_offset)?;247match inst {248&UnwindInst::PushFrameRegs { .. } => {249unwind_codes.push(UnwindCode::PushRegister {250instruction_offset,251reg: UNWIND_RBP_REG,252});253}254&UnwindInst::DefineNewFrame {255offset_downward_to_clobbers,256..257} => {258frame_register_offset = ensure_unwind_offset(offset_downward_to_clobbers)?;259unwind_codes.push(UnwindCode::SetFPReg { instruction_offset });260}261&UnwindInst::StackAlloc { size } => {262unwind_codes.push(UnwindCode::StackAlloc {263instruction_offset,264size,265});266}267&UnwindInst::SaveReg {268clobber_offset,269reg,270} => match MR::map(reg.into()) {271MappedRegister::Int(reg) => {272unwind_codes.push(UnwindCode::SaveReg {273instruction_offset,274reg,275stack_offset: clobber_offset,276});277}278MappedRegister::Xmm(reg) => {279unwind_codes.push(UnwindCode::SaveXmm {280instruction_offset,281reg,282stack_offset: clobber_offset,283});284}285},286&UnwindInst::RegStackOffset { .. } => {287unreachable!("only supported with DWARF");288}289&UnwindInst::Aarch64SetPointerAuth { .. } => {290unreachable!("no aarch64 on x64");291}292}293max_unwind_offset = instruction_offset;294}295296Ok(UnwindInfo {297flags: 0,298prologue_size: max_unwind_offset,299frame_register: Some(UNWIND_RBP_REG),300frame_register_offset,301unwind_codes,302})303}304305fn ensure_unwind_offset(offset: u32) -> CodegenResult<u8> {306if offset > 255 {307warn!("function prologues cannot exceed 255 bytes in size for Windows x64");308return Err(CodegenError::CodeTooLarge);309}310Ok(offset as u8)311}312313314