use alloc::boxed::Box; use alloc::vec::Vec; use crate::common::{Encoding, Register}; use crate::constants::{self, DwOp}; use crate::leb128::write::{sleb128_size, uleb128_size}; use crate::write::{ Address, DebugInfoReference, Error, Reference, Result, UnitEntryId, UnitOffsets, Writer, }; /// The bytecode for a DWARF expression or location description. #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] pub struct Expression { operations: Vec, } impl Expression { /// Create an empty expression. #[inline] pub fn new() -> Self { Self::default() } /// Create an expression from raw bytecode. /// /// This does not support operations that require references, such as `DW_OP_addr`. #[inline] pub fn raw(bytecode: Vec) -> Self { Expression { operations: vec![Operation::Raw(bytecode)], } } /// Add an operation to the expression. /// /// This should only be used for operations that have no explicit operands. pub fn op(&mut self, opcode: DwOp) { self.operations.push(Operation::Simple(opcode)); } /// Add a `DW_OP_addr` operation to the expression. pub fn op_addr(&mut self, address: Address) { self.operations.push(Operation::Address(address)); } /// Add a `DW_OP_constu` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_constu(&mut self, value: u64) { self.operations.push(Operation::UnsignedConstant(value)); } /// Add a `DW_OP_consts` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_consts(&mut self, value: i64) { self.operations.push(Operation::SignedConstant(value)); } /// Add a `DW_OP_const_type` or `DW_OP_GNU_const_type` operation to the expression. pub fn op_const_type(&mut self, base: UnitEntryId, value: Box<[u8]>) { self.operations.push(Operation::ConstantType(base, value)); } /// Add a `DW_OP_fbreg` operation to the expression. pub fn op_fbreg(&mut self, offset: i64) { self.operations.push(Operation::FrameOffset(offset)); } /// Add a `DW_OP_bregx` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_breg(&mut self, register: Register, offset: i64) { self.operations .push(Operation::RegisterOffset(register, offset)); } /// Add a `DW_OP_regval_type` or `DW_OP_GNU_regval_type` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_regval_type(&mut self, register: Register, base: UnitEntryId) { self.operations .push(Operation::RegisterType(register, base)); } /// Add a `DW_OP_pick` operation to the expression. /// /// This may be emitted as a `DW_OP_dup` or `DW_OP_over` operation. pub fn op_pick(&mut self, index: u8) { self.operations.push(Operation::Pick(index)); } /// Add a `DW_OP_deref` operation to the expression. pub fn op_deref(&mut self) { self.operations.push(Operation::Deref { space: false }); } /// Add a `DW_OP_xderef` operation to the expression. pub fn op_xderef(&mut self) { self.operations.push(Operation::Deref { space: true }); } /// Add a `DW_OP_deref_size` operation to the expression. pub fn op_deref_size(&mut self, size: u8) { self.operations .push(Operation::DerefSize { size, space: false }); } /// Add a `DW_OP_xderef_size` operation to the expression. pub fn op_xderef_size(&mut self, size: u8) { self.operations .push(Operation::DerefSize { size, space: true }); } /// Add a `DW_OP_deref_type` or `DW_OP_GNU_deref_type` operation to the expression. pub fn op_deref_type(&mut self, size: u8, base: UnitEntryId) { self.operations.push(Operation::DerefType { size, base, space: false, }); } /// Add a `DW_OP_xderef_type` operation to the expression. pub fn op_xderef_type(&mut self, size: u8, base: UnitEntryId) { self.operations.push(Operation::DerefType { size, base, space: true, }); } /// Add a `DW_OP_plus_uconst` operation to the expression. pub fn op_plus_uconst(&mut self, value: u64) { self.operations.push(Operation::PlusConstant(value)); } /// Add a `DW_OP_skip` operation to the expression. /// /// Returns the index of the operation. The caller must call `set_target` with /// this index to set the target of the branch. pub fn op_skip(&mut self) -> usize { let index = self.next_index(); self.operations.push(Operation::Skip(!0)); index } /// Add a `DW_OP_bra` operation to the expression. /// /// Returns the index of the operation. The caller must call `set_target` with /// this index to set the target of the branch. pub fn op_bra(&mut self) -> usize { let index = self.next_index(); self.operations.push(Operation::Branch(!0)); index } /// Return the index that will be assigned to the next operation. /// /// This can be passed to `set_target`. #[inline] pub fn next_index(&self) -> usize { self.operations.len() } /// Set the target of a `DW_OP_skip` or `DW_OP_bra` operation . pub fn set_target(&mut self, operation: usize, new_target: usize) { debug_assert!(new_target <= self.next_index()); debug_assert_ne!(operation, new_target); match self.operations[operation] { Operation::Skip(ref mut target) | Operation::Branch(ref mut target) => { *target = new_target; } _ => unimplemented!(), } } /// Add a `DW_OP_call4` operation to the expression. pub fn op_call(&mut self, entry: UnitEntryId) { self.operations.push(Operation::Call(entry)); } /// Add a `DW_OP_call_ref` operation to the expression. pub fn op_call_ref(&mut self, entry: Reference) { self.operations.push(Operation::CallRef(entry)); } /// Add a `DW_OP_convert` or `DW_OP_GNU_convert` operation to the expression. /// /// `base` is the DIE of the base type, or `None` for the generic type. pub fn op_convert(&mut self, base: Option) { self.operations.push(Operation::Convert(base)); } /// Add a `DW_OP_reinterpret` or `DW_OP_GNU_reinterpret` operation to the expression. /// /// `base` is the DIE of the base type, or `None` for the generic type. pub fn op_reinterpret(&mut self, base: Option) { self.operations.push(Operation::Reinterpret(base)); } /// Add a `DW_OP_entry_value` or `DW_OP_GNU_entry_value` operation to the expression. pub fn op_entry_value(&mut self, expression: Expression) { self.operations.push(Operation::EntryValue(expression)); } /// Add a `DW_OP_regx` operation to the expression. /// /// This may be emitted as a smaller equivalent operation. pub fn op_reg(&mut self, register: Register) { self.operations.push(Operation::Register(register)); } /// Add a `DW_OP_implicit_value` operation to the expression. pub fn op_implicit_value(&mut self, data: Box<[u8]>) { self.operations.push(Operation::ImplicitValue(data)); } /// Add a `DW_OP_implicit_pointer` or `DW_OP_GNU_implicit_pointer` operation to the expression. pub fn op_implicit_pointer(&mut self, entry: Reference, byte_offset: i64) { self.operations .push(Operation::ImplicitPointer { entry, byte_offset }); } /// Add a `DW_OP_piece` operation to the expression. pub fn op_piece(&mut self, size_in_bytes: u64) { self.operations.push(Operation::Piece { size_in_bytes }); } /// Add a `DW_OP_bit_piece` operation to the expression. pub fn op_bit_piece(&mut self, size_in_bits: u64, bit_offset: u64) { self.operations.push(Operation::BitPiece { size_in_bits, bit_offset, }); } /// Add a `DW_OP_GNU_parameter_ref` operation to the expression. pub fn op_gnu_parameter_ref(&mut self, entry: UnitEntryId) { self.operations.push(Operation::ParameterRef(entry)); } /// Add a `DW_OP_WASM_location 0x0` operation to the expression. pub fn op_wasm_local(&mut self, index: u32) { self.operations.push(Operation::WasmLocal(index)); } /// Add a `DW_OP_WASM_location 0x1` operation to the expression. pub fn op_wasm_global(&mut self, index: u32) { self.operations.push(Operation::WasmGlobal(index)); } /// Add a `DW_OP_WASM_location 0x2` operation to the expression. pub fn op_wasm_stack(&mut self, index: u32) { self.operations.push(Operation::WasmStack(index)); } pub(crate) fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize { let mut size = 0; for operation in &self.operations { size += operation.size(encoding, unit_offsets); } size } pub(crate) fn write( &self, w: &mut W, mut refs: Option<&mut Vec>, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, ) -> Result<()> { // TODO: only calculate offsets if needed? let mut offsets = Vec::with_capacity(self.operations.len()); let mut offset = w.len(); for operation in &self.operations { offsets.push(offset); offset += operation.size(encoding, unit_offsets); } offsets.push(offset); for (operation, offset) in self.operations.iter().zip(offsets.iter().copied()) { debug_assert_eq!(w.len(), offset); operation.write(w, refs.as_deref_mut(), encoding, unit_offsets, &offsets)?; } Ok(()) } } /// A single DWARF operation. // // This type is intentionally not public so that we can change the // representation of expressions as needed. // // Variants are listed in the order they appear in Section 2.5. #[derive(Debug, Clone, PartialEq, Eq, Hash)] enum Operation { /// Raw bytecode. /// /// Does not support references. Raw(Vec), /// An operation that has no explicit operands. /// /// Represents: /// - `DW_OP_drop`, `DW_OP_swap`, `DW_OP_rot` /// - `DW_OP_push_object_address`, `DW_OP_form_tls_address`, `DW_OP_call_frame_cfa` /// - `DW_OP_abs`, `DW_OP_and`, `DW_OP_div`, `DW_OP_minus`, `DW_OP_mod`, `DW_OP_mul`, /// `DW_OP_neg`, `DW_OP_not`, `DW_OP_or`, `DW_OP_plus`, `DW_OP_shl`, `DW_OP_shr`, /// `DW_OP_shra`, `DW_OP_xor` /// - `DW_OP_le`, `DW_OP_ge`, `DW_OP_eq`, `DW_OP_lt`, `DW_OP_gt`, `DW_OP_ne` /// - `DW_OP_nop` /// - `DW_OP_stack_value` Simple(DwOp), /// Relocate the address if needed, and push it on the stack. /// /// Represents `DW_OP_addr`. Address(Address), /// Push an unsigned constant value on the stack. /// /// Represents `DW_OP_constu`. UnsignedConstant(u64), /// Push a signed constant value on the stack. /// /// Represents `DW_OP_consts`. SignedConstant(i64), /* TODO: requires .debug_addr write support /// Read the address at the given index in `.debug_addr, relocate the address if needed, /// and push it on the stack. /// /// Represents `DW_OP_addrx`. AddressIndex(DebugAddrIndex), /// Read the address at the given index in `.debug_addr, and push it on the stack. /// Do not relocate the address. /// /// Represents `DW_OP_constx`. ConstantIndex(DebugAddrIndex), */ /// Interpret the value bytes as a constant of a given type, and push it on the stack. /// /// Represents `DW_OP_const_type`. ConstantType(UnitEntryId, Box<[u8]>), /// Compute the frame base (using `DW_AT_frame_base`), add the /// given offset, and then push the resulting sum on the stack. /// /// Represents `DW_OP_fbreg`. FrameOffset(i64), /// Find the contents of the given register, add the offset, and then /// push the resulting sum on the stack. /// /// Represents `DW_OP_bregx`. RegisterOffset(Register, i64), /// Interpret the contents of the given register as a value of the given type, /// and push it on the stack. /// /// Represents `DW_OP_regval_type`. RegisterType(Register, UnitEntryId), /// Copy the item at a stack index and push it on top of the stack. /// /// Represents `DW_OP_pick`, `DW_OP_dup`, and `DW_OP_over`. Pick(u8), /// Pop the topmost value of the stack, dereference it, and push the /// resulting value. /// /// Represents `DW_OP_deref` and `DW_OP_xderef`. Deref { /// True if the dereference operation takes an address space /// argument from the stack; false otherwise. space: bool, }, /// Pop the topmost value of the stack, dereference it to obtain a value /// of the given size, and push the resulting value. /// /// Represents `DW_OP_deref_size` and `DW_OP_xderef_size`. DerefSize { /// True if the dereference operation takes an address space /// argument from the stack; false otherwise. space: bool, /// The size of the data to dereference. size: u8, }, /// Pop the topmost value of the stack, dereference it to obtain a value /// of the given type, and push the resulting value. /// /// Represents `DW_OP_deref_type` and `DW_OP_xderef_type`. DerefType { /// True if the dereference operation takes an address space /// argument from the stack; false otherwise. space: bool, /// The size of the data to dereference. size: u8, /// The DIE of the base type, or `None` for the generic type. base: UnitEntryId, }, /// Add an unsigned constant to the topmost value on the stack. /// /// Represents `DW_OP_plus_uconst`. PlusConstant(u64), /// Unconditional branch to the target location. /// /// The value is the index within the expression of the operation to branch to. /// This will be converted to a relative offset when writing. /// /// Represents `DW_OP_skip`. Skip(usize), /// Branch to the target location if the top of stack is nonzero. /// /// The value is the index within the expression of the operation to branch to. /// This will be converted to a relative offset when writing. /// /// Represents `DW_OP_bra`. Branch(usize), /// Evaluate a DWARF expression as a subroutine. /// /// The expression comes from the `DW_AT_location` attribute of the indicated DIE. /// /// Represents `DW_OP_call4`. Call(UnitEntryId), /// Evaluate an external DWARF expression as a subroutine. /// /// The expression comes from the `DW_AT_location` attribute of the indicated DIE, /// which may be in another compilation unit or shared object. /// /// Represents `DW_OP_call_ref`. CallRef(Reference), /// Pop the top stack entry, convert it to a different type, and push it on the stack. /// /// Represents `DW_OP_convert`. Convert(Option), /// Pop the top stack entry, reinterpret the bits in its value as a different type, /// and push it on the stack. /// /// Represents `DW_OP_reinterpret`. Reinterpret(Option), /// Evaluate an expression at the entry to the current subprogram, and push it on the stack. /// /// Represents `DW_OP_entry_value`. EntryValue(Expression), // FIXME: EntryRegister /// Indicate that this piece's location is in the given register. /// /// Completes the piece or expression. /// /// Represents `DW_OP_regx`. Register(Register), /// The object has no location, but has a known constant value. /// /// Completes the piece or expression. /// /// Represents `DW_OP_implicit_value`. ImplicitValue(Box<[u8]>), /// The object is a pointer to a value which has no actual location, such as /// an implicit value or a stack value. /// /// Completes the piece or expression. /// /// Represents `DW_OP_implicit_pointer`. ImplicitPointer { /// The DIE of the value that this is an implicit pointer into. entry: Reference, /// The byte offset into the value that the implicit pointer points to. byte_offset: i64, }, /// Terminate a piece. /// /// Represents `DW_OP_piece`. Piece { /// The size of this piece in bytes. size_in_bytes: u64, }, /// Terminate a piece with a size in bits. /// /// Represents `DW_OP_bit_piece`. BitPiece { /// The size of this piece in bits. size_in_bits: u64, /// The bit offset of this piece. bit_offset: u64, }, /// This represents a parameter that was optimized out. /// /// The entry is the definition of the parameter, and is matched to /// the `DW_TAG_GNU_call_site_parameter` in the caller that also /// points to the same definition of the parameter. /// /// Represents `DW_OP_GNU_parameter_ref`. ParameterRef(UnitEntryId), /// The index of a local in the currently executing function. /// /// Represents `DW_OP_WASM_location 0x00`. WasmLocal(u32), /// The index of a global. /// /// Represents `DW_OP_WASM_location 0x01`. WasmGlobal(u32), /// The index of an item on the operand stack. /// /// Represents `DW_OP_WASM_location 0x02`. WasmStack(u32), } impl Operation { fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize { let base_size = |base| { // Errors are handled during writes. match unit_offsets { Some(offsets) => uleb128_size(offsets.unit_offset(base)), None => 0, } }; 1 + match *self { Operation::Raw(ref bytecode) => return bytecode.len(), Operation::Simple(_) => 0, Operation::Address(_) => encoding.address_size as usize, Operation::UnsignedConstant(value) => { if value < 32 { 0 } else { uleb128_size(value) } } Operation::SignedConstant(value) => sleb128_size(value), Operation::ConstantType(base, ref value) => base_size(base) + 1 + value.len(), Operation::FrameOffset(offset) => sleb128_size(offset), Operation::RegisterOffset(register, offset) => { if register.0 < 32 { sleb128_size(offset) } else { uleb128_size(register.0.into()) + sleb128_size(offset) } } Operation::RegisterType(register, base) => { uleb128_size(register.0.into()) + base_size(base) } Operation::Pick(index) => { if index > 1 { 1 } else { 0 } } Operation::Deref { .. } => 0, Operation::DerefSize { .. } => 1, Operation::DerefType { base, .. } => 1 + base_size(base), Operation::PlusConstant(value) => uleb128_size(value), Operation::Skip(_) => 2, Operation::Branch(_) => 2, Operation::Call(_) => 4, Operation::CallRef(_) => encoding.format.word_size() as usize, Operation::Convert(base) => match base { Some(base) => base_size(base), None => 1, }, Operation::Reinterpret(base) => match base { Some(base) => base_size(base), None => 1, }, Operation::EntryValue(ref expression) => { let length = expression.size(encoding, unit_offsets); uleb128_size(length as u64) + length } Operation::Register(register) => { if register.0 < 32 { 0 } else { uleb128_size(register.0.into()) } } Operation::ImplicitValue(ref data) => uleb128_size(data.len() as u64) + data.len(), Operation::ImplicitPointer { byte_offset, .. } => { encoding.format.word_size() as usize + sleb128_size(byte_offset) } Operation::Piece { size_in_bytes } => uleb128_size(size_in_bytes), Operation::BitPiece { size_in_bits, bit_offset, } => uleb128_size(size_in_bits) + uleb128_size(bit_offset), Operation::ParameterRef(_) => 4, Operation::WasmLocal(index) | Operation::WasmGlobal(index) | Operation::WasmStack(index) => 1 + uleb128_size(index.into()), } } pub(crate) fn write( &self, w: &mut W, refs: Option<&mut Vec>, encoding: Encoding, unit_offsets: Option<&UnitOffsets>, offsets: &[usize], ) -> Result<()> { let entry_offset = |entry| match unit_offsets { Some(offsets) => { let offset = offsets.unit_offset(entry); if offset == 0 { Err(Error::UnsupportedExpressionForwardReference) } else { Ok(offset) } } None => Err(Error::UnsupportedCfiExpressionReference), }; match *self { Operation::Raw(ref bytecode) => w.write(bytecode)?, Operation::Simple(opcode) => w.write_u8(opcode.0)?, Operation::Address(address) => { w.write_u8(constants::DW_OP_addr.0)?; w.write_address(address, encoding.address_size)?; } Operation::UnsignedConstant(value) => { if value < 32 { w.write_u8(constants::DW_OP_lit0.0 + value as u8)?; } else { w.write_u8(constants::DW_OP_constu.0)?; w.write_uleb128(value)?; } } Operation::SignedConstant(value) => { w.write_u8(constants::DW_OP_consts.0)?; w.write_sleb128(value)?; } Operation::ConstantType(base, ref value) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_const_type.0)?; } else { w.write_u8(constants::DW_OP_GNU_const_type.0)?; } w.write_uleb128(entry_offset(base)?)?; w.write_udata(value.len() as u64, 1)?; w.write(value)?; } Operation::FrameOffset(offset) => { w.write_u8(constants::DW_OP_fbreg.0)?; w.write_sleb128(offset)?; } Operation::RegisterOffset(register, offset) => { if register.0 < 32 { w.write_u8(constants::DW_OP_breg0.0 + register.0 as u8)?; } else { w.write_u8(constants::DW_OP_bregx.0)?; w.write_uleb128(register.0.into())?; } w.write_sleb128(offset)?; } Operation::RegisterType(register, base) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_regval_type.0)?; } else { w.write_u8(constants::DW_OP_GNU_regval_type.0)?; } w.write_uleb128(register.0.into())?; w.write_uleb128(entry_offset(base)?)?; } Operation::Pick(index) => match index { 0 => w.write_u8(constants::DW_OP_dup.0)?, 1 => w.write_u8(constants::DW_OP_over.0)?, _ => { w.write_u8(constants::DW_OP_pick.0)?; w.write_u8(index)?; } }, Operation::Deref { space } => { if space { w.write_u8(constants::DW_OP_xderef.0)?; } else { w.write_u8(constants::DW_OP_deref.0)?; } } Operation::DerefSize { space, size } => { if space { w.write_u8(constants::DW_OP_xderef_size.0)?; } else { w.write_u8(constants::DW_OP_deref_size.0)?; } w.write_u8(size)?; } Operation::DerefType { space, size, base } => { if space { w.write_u8(constants::DW_OP_xderef_type.0)?; } else { if encoding.version >= 5 { w.write_u8(constants::DW_OP_deref_type.0)?; } else { w.write_u8(constants::DW_OP_GNU_deref_type.0)?; } } w.write_u8(size)?; w.write_uleb128(entry_offset(base)?)?; } Operation::PlusConstant(value) => { w.write_u8(constants::DW_OP_plus_uconst.0)?; w.write_uleb128(value)?; } Operation::Skip(target) => { w.write_u8(constants::DW_OP_skip.0)?; let offset = offsets[target] as i64 - (w.len() as i64 + 2); w.write_sdata(offset, 2)?; } Operation::Branch(target) => { w.write_u8(constants::DW_OP_bra.0)?; let offset = offsets[target] as i64 - (w.len() as i64 + 2); w.write_sdata(offset, 2)?; } Operation::Call(entry) => { w.write_u8(constants::DW_OP_call4.0)?; // TODO: this probably won't work in practice, because we may // only know the offsets of base type DIEs at this point. w.write_udata(entry_offset(entry)?, 4)?; } Operation::CallRef(entry) => { w.write_u8(constants::DW_OP_call_ref.0)?; let size = encoding.format.word_size(); match entry { Reference::Symbol(symbol) => w.write_reference(symbol, size)?, Reference::Entry(unit, entry) => { let refs = refs.ok_or(Error::InvalidReference)?; refs.push(DebugInfoReference { offset: w.len(), unit, entry, size, }); w.write_udata(0, size)?; } } } Operation::Convert(base) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_convert.0)?; } else { w.write_u8(constants::DW_OP_GNU_convert.0)?; } match base { Some(base) => w.write_uleb128(entry_offset(base)?)?, None => w.write_u8(0)?, } } Operation::Reinterpret(base) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_reinterpret.0)?; } else { w.write_u8(constants::DW_OP_GNU_reinterpret.0)?; } match base { Some(base) => w.write_uleb128(entry_offset(base)?)?, None => w.write_u8(0)?, } } Operation::EntryValue(ref expression) => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_entry_value.0)?; } else { w.write_u8(constants::DW_OP_GNU_entry_value.0)?; } let length = expression.size(encoding, unit_offsets); w.write_uleb128(length as u64)?; expression.write(w, refs, encoding, unit_offsets)?; } Operation::Register(register) => { if register.0 < 32 { w.write_u8(constants::DW_OP_reg0.0 + register.0 as u8)?; } else { w.write_u8(constants::DW_OP_regx.0)?; w.write_uleb128(register.0.into())?; } } Operation::ImplicitValue(ref data) => { w.write_u8(constants::DW_OP_implicit_value.0)?; w.write_uleb128(data.len() as u64)?; w.write(data)?; } Operation::ImplicitPointer { entry, byte_offset } => { if encoding.version >= 5 { w.write_u8(constants::DW_OP_implicit_pointer.0)?; } else { w.write_u8(constants::DW_OP_GNU_implicit_pointer.0)?; } let size = if encoding.version == 2 { encoding.address_size } else { encoding.format.word_size() }; match entry { Reference::Symbol(symbol) => { w.write_reference(symbol, size)?; } Reference::Entry(unit, entry) => { let refs = refs.ok_or(Error::InvalidReference)?; refs.push(DebugInfoReference { offset: w.len(), unit, entry, size, }); w.write_udata(0, size)?; } } w.write_sleb128(byte_offset)?; } Operation::Piece { size_in_bytes } => { w.write_u8(constants::DW_OP_piece.0)?; w.write_uleb128(size_in_bytes)?; } Operation::BitPiece { size_in_bits, bit_offset, } => { w.write_u8(constants::DW_OP_bit_piece.0)?; w.write_uleb128(size_in_bits)?; w.write_uleb128(bit_offset)?; } Operation::ParameterRef(entry) => { w.write_u8(constants::DW_OP_GNU_parameter_ref.0)?; w.write_udata(entry_offset(entry)?, 4)?; } Operation::WasmLocal(index) => { w.write(&[constants::DW_OP_WASM_location.0, 0])?; w.write_uleb128(index.into())?; } Operation::WasmGlobal(index) => { w.write(&[constants::DW_OP_WASM_location.0, 1])?; w.write_uleb128(index.into())?; } Operation::WasmStack(index) => { w.write(&[constants::DW_OP_WASM_location.0, 2])?; w.write_uleb128(index.into())?; } } Ok(()) } } #[cfg(feature = "read")] pub(crate) mod convert { use super::*; use crate::common::UnitSectionOffset; use crate::read::{self, Reader}; use crate::write::{ConvertError, ConvertResult, UnitEntryId, UnitId}; use std::collections::HashMap; impl Expression { /// Create an expression from the input expression. pub fn from>( from_expression: read::Expression, encoding: Encoding, dwarf: Option<&read::Dwarf>, unit: Option<&read::Unit>, entry_ids: Option<&HashMap>, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult { let convert_unit_offset = |offset: read::UnitOffset| -> ConvertResult<_> { let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?; let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; let id = entry_ids .get(&offset.to_unit_section_offset(unit)) .ok_or(ConvertError::InvalidUnitRef)?; Ok(id.1) }; let convert_debug_info_offset = |offset| -> ConvertResult<_> { // TODO: support relocations let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?; let id = entry_ids .get(&UnitSectionOffset::DebugInfoOffset(offset)) .ok_or(ConvertError::InvalidDebugInfoRef)?; Ok(Reference::Entry(id.0, id.1)) }; // Calculate offsets for use in branch/skip operations. let mut offsets = Vec::new(); let mut offset = 0; let mut from_operations = from_expression.clone().operations(encoding); while from_operations.next()?.is_some() { offsets.push(offset); offset = from_operations.offset_from(&from_expression); } offsets.push(from_expression.0.len()); let mut from_operations = from_expression.clone().operations(encoding); let mut operations = Vec::new(); while let Some(from_operation) = from_operations.next()? { let operation = match from_operation { read::Operation::Deref { base_type, size, space, } => { if base_type.0 != 0 { let base = convert_unit_offset(base_type)?; Operation::DerefType { space, size, base } } else if size != encoding.address_size { Operation::DerefSize { space, size } } else { Operation::Deref { space } } } read::Operation::Drop => Operation::Simple(constants::DW_OP_drop), read::Operation::Pick { index } => Operation::Pick(index), read::Operation::Swap => Operation::Simple(constants::DW_OP_swap), read::Operation::Rot => Operation::Simple(constants::DW_OP_rot), read::Operation::Abs => Operation::Simple(constants::DW_OP_abs), read::Operation::And => Operation::Simple(constants::DW_OP_and), read::Operation::Div => Operation::Simple(constants::DW_OP_div), read::Operation::Minus => Operation::Simple(constants::DW_OP_minus), read::Operation::Mod => Operation::Simple(constants::DW_OP_mod), read::Operation::Mul => Operation::Simple(constants::DW_OP_mul), read::Operation::Neg => Operation::Simple(constants::DW_OP_neg), read::Operation::Not => Operation::Simple(constants::DW_OP_not), read::Operation::Or => Operation::Simple(constants::DW_OP_or), read::Operation::Plus => Operation::Simple(constants::DW_OP_plus), read::Operation::PlusConstant { value } => Operation::PlusConstant(value), read::Operation::Shl => Operation::Simple(constants::DW_OP_shl), read::Operation::Shr => Operation::Simple(constants::DW_OP_shr), read::Operation::Shra => Operation::Simple(constants::DW_OP_shra), read::Operation::Xor => Operation::Simple(constants::DW_OP_xor), read::Operation::Eq => Operation::Simple(constants::DW_OP_eq), read::Operation::Ge => Operation::Simple(constants::DW_OP_ge), read::Operation::Gt => Operation::Simple(constants::DW_OP_gt), read::Operation::Le => Operation::Simple(constants::DW_OP_le), read::Operation::Lt => Operation::Simple(constants::DW_OP_lt), read::Operation::Ne => Operation::Simple(constants::DW_OP_ne), read::Operation::Bra { target } => { let offset = from_operations .offset_from(&from_expression) .wrapping_add(i64::from(target) as usize); let index = offsets .binary_search(&offset) .map_err(|_| ConvertError::InvalidBranchTarget)?; Operation::Branch(index) } read::Operation::Skip { target } => { let offset = from_operations .offset_from(&from_expression) .wrapping_add(i64::from(target) as usize); let index = offsets .binary_search(&offset) .map_err(|_| ConvertError::InvalidBranchTarget)?; Operation::Skip(index) } read::Operation::UnsignedConstant { value } => { Operation::UnsignedConstant(value) } read::Operation::SignedConstant { value } => Operation::SignedConstant(value), read::Operation::Register { register } => Operation::Register(register), read::Operation::RegisterOffset { register, offset, base_type, } => { if base_type.0 != 0 { Operation::RegisterType(register, convert_unit_offset(base_type)?) } else { Operation::RegisterOffset(register, offset) } } read::Operation::FrameOffset { offset } => Operation::FrameOffset(offset), read::Operation::Nop => Operation::Simple(constants::DW_OP_nop), read::Operation::PushObjectAddress => { Operation::Simple(constants::DW_OP_push_object_address) } read::Operation::Call { offset } => match offset { read::DieReference::UnitRef(offset) => { Operation::Call(convert_unit_offset(offset)?) } read::DieReference::DebugInfoRef(offset) => { Operation::CallRef(convert_debug_info_offset(offset)?) } }, read::Operation::TLS => Operation::Simple(constants::DW_OP_form_tls_address), read::Operation::CallFrameCFA => { Operation::Simple(constants::DW_OP_call_frame_cfa) } read::Operation::Piece { size_in_bits, bit_offset: None, } => Operation::Piece { size_in_bytes: size_in_bits / 8, }, read::Operation::Piece { size_in_bits, bit_offset: Some(bit_offset), } => Operation::BitPiece { size_in_bits, bit_offset, }, read::Operation::ImplicitValue { data } => { Operation::ImplicitValue(data.to_slice()?.into_owned().into()) } read::Operation::StackValue => Operation::Simple(constants::DW_OP_stack_value), read::Operation::ImplicitPointer { value, byte_offset } => { let entry = convert_debug_info_offset(value)?; Operation::ImplicitPointer { entry, byte_offset } } read::Operation::EntryValue { expression } => { let expression = Expression::from( read::Expression(expression), encoding, dwarf, unit, entry_ids, convert_address, )?; Operation::EntryValue(expression) } read::Operation::ParameterRef { offset } => { let entry = convert_unit_offset(offset)?; Operation::ParameterRef(entry) } read::Operation::Address { address } => { let address = convert_address(address).ok_or(ConvertError::InvalidAddress)?; Operation::Address(address) } read::Operation::AddressIndex { index } => { let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; let val = dwarf.address(unit, index)?; let address = convert_address(val).ok_or(ConvertError::InvalidAddress)?; Operation::Address(address) } read::Operation::ConstantIndex { index } => { let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; let val = dwarf.address(unit, index)?; Operation::UnsignedConstant(val) } read::Operation::TypedLiteral { base_type, value } => { let entry = convert_unit_offset(base_type)?; Operation::ConstantType(entry, value.to_slice()?.into_owned().into()) } read::Operation::Convert { base_type } => { if base_type.0 == 0 { Operation::Convert(None) } else { let entry = convert_unit_offset(base_type)?; Operation::Convert(Some(entry)) } } read::Operation::Reinterpret { base_type } => { if base_type.0 == 0 { Operation::Reinterpret(None) } else { let entry = convert_unit_offset(base_type)?; Operation::Reinterpret(Some(entry)) } } read::Operation::WasmLocal { index } => Operation::WasmLocal(index), read::Operation::WasmGlobal { index } => Operation::WasmGlobal(index), read::Operation::WasmStack { index } => Operation::WasmStack(index), }; operations.push(operation); } Ok(Expression { operations }) } } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::common::{ DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, DebugStrOffsetsBase, Format, SectionId, }; use crate::read; use crate::write::{ DebugLineStrOffsets, DebugStrOffsets, EndianVec, LineProgram, Sections, Unit, UnitTable, }; use crate::LittleEndian; use std::collections::HashMap; use std::sync::Arc; #[test] fn test_operation() { for &version in &[3, 4, 5] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut units = UnitTable::default(); let unit_id = units.add(Unit::new(encoding, LineProgram::none())); let unit = units.get_mut(unit_id); let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type); let reference = Reference::Entry(unit_id, entry_id); let mut sections = Sections::new(EndianVec::new(LittleEndian)); let debug_line_str_offsets = DebugLineStrOffsets::none(); let debug_str_offsets = DebugStrOffsets::none(); let debug_info_offsets = units .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) .unwrap(); let unit_offsets = debug_info_offsets.unit_offsets(unit_id); let debug_info_offset = unit_offsets.debug_info_offset(entry_id); let entry_offset = read::UnitOffset(unit_offsets.unit_offset(entry_id) as usize); let mut reg_expression = Expression::new(); reg_expression.op_reg(Register(23)); let operations: &[(&dyn Fn(&mut Expression), Operation, read::Operation<_>)] = &[ ( &|x| x.op_deref(), Operation::Deref { space: false }, read::Operation::Deref { base_type: read::UnitOffset(0), size: address_size, space: false, }, ), ( &|x| x.op_xderef(), Operation::Deref { space: true }, read::Operation::Deref { base_type: read::UnitOffset(0), size: address_size, space: true, }, ), ( &|x| x.op_deref_size(2), Operation::DerefSize { space: false, size: 2, }, read::Operation::Deref { base_type: read::UnitOffset(0), size: 2, space: false, }, ), ( &|x| x.op_xderef_size(2), Operation::DerefSize { space: true, size: 2, }, read::Operation::Deref { base_type: read::UnitOffset(0), size: 2, space: true, }, ), ( &|x| x.op_deref_type(2, entry_id), Operation::DerefType { space: false, size: 2, base: entry_id, }, read::Operation::Deref { base_type: entry_offset, size: 2, space: false, }, ), ( &|x| x.op_xderef_type(2, entry_id), Operation::DerefType { space: true, size: 2, base: entry_id, }, read::Operation::Deref { base_type: entry_offset, size: 2, space: true, }, ), ( &|x| x.op(constants::DW_OP_drop), Operation::Simple(constants::DW_OP_drop), read::Operation::Drop, ), ( &|x| x.op_pick(0), Operation::Pick(0), read::Operation::Pick { index: 0 }, ), ( &|x| x.op_pick(1), Operation::Pick(1), read::Operation::Pick { index: 1 }, ), ( &|x| x.op_pick(2), Operation::Pick(2), read::Operation::Pick { index: 2 }, ), ( &|x| x.op(constants::DW_OP_swap), Operation::Simple(constants::DW_OP_swap), read::Operation::Swap, ), ( &|x| x.op(constants::DW_OP_rot), Operation::Simple(constants::DW_OP_rot), read::Operation::Rot, ), ( &|x| x.op(constants::DW_OP_abs), Operation::Simple(constants::DW_OP_abs), read::Operation::Abs, ), ( &|x| x.op(constants::DW_OP_and), Operation::Simple(constants::DW_OP_and), read::Operation::And, ), ( &|x| x.op(constants::DW_OP_div), Operation::Simple(constants::DW_OP_div), read::Operation::Div, ), ( &|x| x.op(constants::DW_OP_minus), Operation::Simple(constants::DW_OP_minus), read::Operation::Minus, ), ( &|x| x.op(constants::DW_OP_mod), Operation::Simple(constants::DW_OP_mod), read::Operation::Mod, ), ( &|x| x.op(constants::DW_OP_mul), Operation::Simple(constants::DW_OP_mul), read::Operation::Mul, ), ( &|x| x.op(constants::DW_OP_neg), Operation::Simple(constants::DW_OP_neg), read::Operation::Neg, ), ( &|x| x.op(constants::DW_OP_not), Operation::Simple(constants::DW_OP_not), read::Operation::Not, ), ( &|x| x.op(constants::DW_OP_or), Operation::Simple(constants::DW_OP_or), read::Operation::Or, ), ( &|x| x.op(constants::DW_OP_plus), Operation::Simple(constants::DW_OP_plus), read::Operation::Plus, ), ( &|x| x.op_plus_uconst(23), Operation::PlusConstant(23), read::Operation::PlusConstant { value: 23 }, ), ( &|x| x.op(constants::DW_OP_shl), Operation::Simple(constants::DW_OP_shl), read::Operation::Shl, ), ( &|x| x.op(constants::DW_OP_shr), Operation::Simple(constants::DW_OP_shr), read::Operation::Shr, ), ( &|x| x.op(constants::DW_OP_shra), Operation::Simple(constants::DW_OP_shra), read::Operation::Shra, ), ( &|x| x.op(constants::DW_OP_xor), Operation::Simple(constants::DW_OP_xor), read::Operation::Xor, ), ( &|x| x.op(constants::DW_OP_eq), Operation::Simple(constants::DW_OP_eq), read::Operation::Eq, ), ( &|x| x.op(constants::DW_OP_ge), Operation::Simple(constants::DW_OP_ge), read::Operation::Ge, ), ( &|x| x.op(constants::DW_OP_gt), Operation::Simple(constants::DW_OP_gt), read::Operation::Gt, ), ( &|x| x.op(constants::DW_OP_le), Operation::Simple(constants::DW_OP_le), read::Operation::Le, ), ( &|x| x.op(constants::DW_OP_lt), Operation::Simple(constants::DW_OP_lt), read::Operation::Lt, ), ( &|x| x.op(constants::DW_OP_ne), Operation::Simple(constants::DW_OP_ne), read::Operation::Ne, ), ( &|x| x.op_constu(23), Operation::UnsignedConstant(23), read::Operation::UnsignedConstant { value: 23 }, ), ( &|x| x.op_consts(-23), Operation::SignedConstant(-23), read::Operation::SignedConstant { value: -23 }, ), ( &|x| x.op_reg(Register(23)), Operation::Register(Register(23)), read::Operation::Register { register: Register(23), }, ), ( &|x| x.op_reg(Register(123)), Operation::Register(Register(123)), read::Operation::Register { register: Register(123), }, ), ( &|x| x.op_breg(Register(23), 34), Operation::RegisterOffset(Register(23), 34), read::Operation::RegisterOffset { register: Register(23), offset: 34, base_type: read::UnitOffset(0), }, ), ( &|x| x.op_breg(Register(123), 34), Operation::RegisterOffset(Register(123), 34), read::Operation::RegisterOffset { register: Register(123), offset: 34, base_type: read::UnitOffset(0), }, ), ( &|x| x.op_regval_type(Register(23), entry_id), Operation::RegisterType(Register(23), entry_id), read::Operation::RegisterOffset { register: Register(23), offset: 0, base_type: entry_offset, }, ), ( &|x| x.op_fbreg(34), Operation::FrameOffset(34), read::Operation::FrameOffset { offset: 34 }, ), ( &|x| x.op(constants::DW_OP_nop), Operation::Simple(constants::DW_OP_nop), read::Operation::Nop, ), ( &|x| x.op(constants::DW_OP_push_object_address), Operation::Simple(constants::DW_OP_push_object_address), read::Operation::PushObjectAddress, ), ( &|x| x.op_call(entry_id), Operation::Call(entry_id), read::Operation::Call { offset: read::DieReference::UnitRef(entry_offset), }, ), ( &|x| x.op_call_ref(reference), Operation::CallRef(reference), read::Operation::Call { offset: read::DieReference::DebugInfoRef(debug_info_offset), }, ), ( &|x| x.op(constants::DW_OP_form_tls_address), Operation::Simple(constants::DW_OP_form_tls_address), read::Operation::TLS, ), ( &|x| x.op(constants::DW_OP_call_frame_cfa), Operation::Simple(constants::DW_OP_call_frame_cfa), read::Operation::CallFrameCFA, ), ( &|x| x.op_piece(23), Operation::Piece { size_in_bytes: 23 }, read::Operation::Piece { size_in_bits: 23 * 8, bit_offset: None, }, ), ( &|x| x.op_bit_piece(23, 34), Operation::BitPiece { size_in_bits: 23, bit_offset: 34, }, read::Operation::Piece { size_in_bits: 23, bit_offset: Some(34), }, ), ( &|x| x.op_implicit_value(vec![23].into()), Operation::ImplicitValue(vec![23].into()), read::Operation::ImplicitValue { data: read::EndianSlice::new(&[23], LittleEndian), }, ), ( &|x| x.op(constants::DW_OP_stack_value), Operation::Simple(constants::DW_OP_stack_value), read::Operation::StackValue, ), ( &|x| x.op_implicit_pointer(reference, 23), Operation::ImplicitPointer { entry: reference, byte_offset: 23, }, read::Operation::ImplicitPointer { value: debug_info_offset, byte_offset: 23, }, ), ( &|x| x.op_entry_value(reg_expression.clone()), Operation::EntryValue(reg_expression.clone()), read::Operation::EntryValue { expression: read::EndianSlice::new( &[constants::DW_OP_reg23.0], LittleEndian, ), }, ), ( &|x| x.op_gnu_parameter_ref(entry_id), Operation::ParameterRef(entry_id), read::Operation::ParameterRef { offset: entry_offset, }, ), ( &|x| x.op_addr(Address::Constant(23)), Operation::Address(Address::Constant(23)), read::Operation::Address { address: 23 }, ), ( &|x| x.op_const_type(entry_id, vec![23].into()), Operation::ConstantType(entry_id, vec![23].into()), read::Operation::TypedLiteral { base_type: entry_offset, value: read::EndianSlice::new(&[23], LittleEndian), }, ), ( &|x| x.op_convert(None), Operation::Convert(None), read::Operation::Convert { base_type: read::UnitOffset(0), }, ), ( &|x| x.op_convert(Some(entry_id)), Operation::Convert(Some(entry_id)), read::Operation::Convert { base_type: entry_offset, }, ), ( &|x| x.op_reinterpret(None), Operation::Reinterpret(None), read::Operation::Reinterpret { base_type: read::UnitOffset(0), }, ), ( &|x| x.op_reinterpret(Some(entry_id)), Operation::Reinterpret(Some(entry_id)), read::Operation::Reinterpret { base_type: entry_offset, }, ), ( &|x| x.op_wasm_local(1000), Operation::WasmLocal(1000), read::Operation::WasmLocal { index: 1000 }, ), ( &|x| x.op_wasm_global(1000), Operation::WasmGlobal(1000), read::Operation::WasmGlobal { index: 1000 }, ), ( &|x| x.op_wasm_stack(1000), Operation::WasmStack(1000), read::Operation::WasmStack { index: 1000 }, ), ]; let mut expression = Expression::new(); let start_index = expression.next_index(); for (f, o, _) in operations { f(&mut expression); assert_eq!(expression.operations.last(), Some(o)); } let bra_index = expression.op_bra(); let skip_index = expression.op_skip(); expression.op(constants::DW_OP_nop); let end_index = expression.next_index(); expression.set_target(bra_index, start_index); expression.set_target(skip_index, end_index); let mut w = EndianVec::new(LittleEndian); let mut refs = Vec::new(); expression .write(&mut w, Some(&mut refs), encoding, Some(&unit_offsets)) .unwrap(); for r in &refs { assert_eq!(r.unit, unit_id); assert_eq!(r.entry, entry_id); w.write_offset_at( r.offset, debug_info_offset.0, SectionId::DebugInfo, r.size, ) .unwrap(); } let read_expression = read::Expression(read::EndianSlice::new(w.slice(), LittleEndian)); let mut read_operations = read_expression.operations(encoding); for (_, _, operation) in operations { assert_eq!(read_operations.next(), Ok(Some(*operation))); } // 4 = DW_OP_skip + i16 + DW_OP_nop assert_eq!( read_operations.next(), Ok(Some(read::Operation::Bra { target: -(w.len() as i16) + 4 })) ); // 1 = DW_OP_nop assert_eq!( read_operations.next(), Ok(Some(read::Operation::Skip { target: 1 })) ); assert_eq!(read_operations.next(), Ok(Some(read::Operation::Nop))); assert_eq!(read_operations.next(), Ok(None)); // Fake the unit. let unit = read::Unit { header: read::UnitHeader::new( encoding, 0, read::UnitType::Compilation, DebugAbbrevOffset(0), DebugInfoOffset(0).into(), read::EndianSlice::new(&[], LittleEndian), ), abbreviations: Arc::new(read::Abbreviations::default()), name: None, comp_dir: None, low_pc: 0, str_offsets_base: DebugStrOffsetsBase(0), addr_base: DebugAddrBase(0), loclists_base: DebugLocListsBase(0), rnglists_base: DebugRngListsBase(0), line_program: None, dwo_id: None, }; let mut entry_ids = HashMap::new(); entry_ids.insert(debug_info_offset.into(), (unit_id, entry_id)); let convert_expression = Expression::from( read_expression, encoding, None, /* dwarf */ Some(&unit), Some(&entry_ids), &|address| Some(Address::Constant(address)), ) .unwrap(); let mut convert_operations = convert_expression.operations.iter(); for (_, operation, _) in operations { assert_eq!(convert_operations.next(), Some(operation)); } assert_eq!( convert_operations.next(), Some(&Operation::Branch(start_index)) ); assert_eq!(convert_operations.next(), Some(&Operation::Skip(end_index))); assert_eq!( convert_operations.next(), Some(&Operation::Simple(constants::DW_OP_nop)) ); } } } } }