diff options
Diffstat (limited to 'vendor/gimli/src/read/op.rs')
-rw-r--r-- | vendor/gimli/src/read/op.rs | 4140 |
1 files changed, 0 insertions, 4140 deletions
diff --git a/vendor/gimli/src/read/op.rs b/vendor/gimli/src/read/op.rs deleted file mode 100644 index 4cb681e..0000000 --- a/vendor/gimli/src/read/op.rs +++ /dev/null @@ -1,4140 +0,0 @@ -//! Functions for parsing and evaluating DWARF expressions. - -#[cfg(feature = "read")] -use alloc::vec::Vec; -use core::mem; - -use super::util::{ArrayLike, ArrayVec}; -use crate::common::{DebugAddrIndex, DebugInfoOffset, Encoding, Register}; -use crate::constants; -use crate::read::{Error, Reader, ReaderOffset, Result, StoreOnHeap, UnitOffset, Value, ValueType}; - -/// A reference to a DIE, either relative to the current CU or -/// relative to the section. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DieReference<T = usize> { - /// A CU-relative reference. - UnitRef(UnitOffset<T>), - /// A section-relative reference. - DebugInfoRef(DebugInfoOffset<T>), -} - -/// A single decoded DWARF expression operation. -/// -/// DWARF expression evaluation is done in two parts: first the raw -/// bytes of the next part of the expression are decoded; and then the -/// decoded operation is evaluated. This approach lets other -/// consumers inspect the DWARF expression without reimplementing the -/// decoding operation. -/// -/// Multiple DWARF opcodes may decode into a single `Operation`. For -/// example, both `DW_OP_deref` and `DW_OP_xderef` are represented -/// using `Operation::Deref`. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Operation<R, Offset = <R as Reader>::Offset> -where - R: Reader<Offset = Offset>, - Offset: ReaderOffset, -{ - /// Dereference the topmost value of the stack. - Deref { - /// The DIE of the base type or 0 to indicate the generic type - base_type: UnitOffset<Offset>, - /// The size of the data to dereference. - size: u8, - /// True if the dereference operation takes an address space - /// argument from the stack; false otherwise. - space: bool, - }, - /// Drop an item from the stack. - Drop, - /// Pick an item from the stack and push it on top of the stack. - /// This operation handles `DW_OP_pick`, `DW_OP_dup`, and - /// `DW_OP_over`. - Pick { - /// The index, from the top of the stack, of the item to copy. - index: u8, - }, - /// Swap the top two stack items. - Swap, - /// Rotate the top three stack items. - Rot, - /// Take the absolute value of the top of the stack. - Abs, - /// Bitwise `and` of the top two values on the stack. - And, - /// Divide the top two values on the stack. - Div, - /// Subtract the top two values on the stack. - Minus, - /// Modulus of the top two values on the stack. - Mod, - /// Multiply the top two values on the stack. - Mul, - /// Negate the top of the stack. - Neg, - /// Bitwise `not` of the top of the stack. - Not, - /// Bitwise `or` of the top two values on the stack. - Or, - /// Add the top two values on the stack. - Plus, - /// Add a constant to the topmost value on the stack. - PlusConstant { - /// The value to add. - value: u64, - }, - /// Logical left shift of the 2nd value on the stack by the number - /// of bits given by the topmost value on the stack. - Shl, - /// Right shift of the 2nd value on the stack by the number of - /// bits given by the topmost value on the stack. - Shr, - /// Arithmetic left shift of the 2nd value on the stack by the - /// number of bits given by the topmost value on the stack. - Shra, - /// Bitwise `xor` of the top two values on the stack. - Xor, - /// Branch to the target location if the top of stack is nonzero. - Bra { - /// The relative offset to the target bytecode. - target: i16, - }, - /// Compare the top two stack values for equality. - Eq, - /// Compare the top two stack values using `>=`. - Ge, - /// Compare the top two stack values using `>`. - Gt, - /// Compare the top two stack values using `<=`. - Le, - /// Compare the top two stack values using `<`. - Lt, - /// Compare the top two stack values using `!=`. - Ne, - /// Unconditional branch to the target location. - Skip { - /// The relative offset to the target bytecode. - target: i16, - }, - /// Push an unsigned constant value on the stack. This handles multiple - /// DWARF opcodes. - UnsignedConstant { - /// The value to push. - value: u64, - }, - /// Push a signed constant value on the stack. This handles multiple - /// DWARF opcodes. - SignedConstant { - /// The value to push. - value: i64, - }, - /// Indicate that this piece's location is in the given register. - /// - /// Completes the piece or expression. - Register { - /// The register number. - register: Register, - }, - /// Find the value of the given register, add the offset, and then - /// push the resulting sum on the stack. - RegisterOffset { - /// The register number. - register: Register, - /// The offset to add. - offset: i64, - /// The DIE of the base type or 0 to indicate the generic type - base_type: UnitOffset<Offset>, - }, - /// Compute the frame base (using `DW_AT_frame_base`), add the - /// given offset, and then push the resulting sum on the stack. - FrameOffset { - /// The offset to add. - offset: i64, - }, - /// No operation. - Nop, - /// Push the object address on the stack. - PushObjectAddress, - /// Evaluate a DWARF expression as a subroutine. The expression - /// comes from the `DW_AT_location` attribute of the indicated - /// DIE. - Call { - /// The DIE to use. - offset: DieReference<Offset>, - }, - /// Compute the address of a thread-local variable and push it on - /// the stack. - TLS, - /// Compute the call frame CFA and push it on the stack. - CallFrameCFA, - /// Terminate a piece. - Piece { - /// The size of this piece in bits. - size_in_bits: u64, - /// The bit offset of this piece. If `None`, then this piece - /// was specified using `DW_OP_piece` and should start at the - /// next byte boundary. - bit_offset: Option<u64>, - }, - /// The object has no location, but has a known constant value. - /// - /// Represents `DW_OP_implicit_value`. - /// Completes the piece or expression. - ImplicitValue { - /// The implicit value to use. - data: R, - }, - /// The object has no location, but its value is at the top of the stack. - /// - /// Represents `DW_OP_stack_value`. - /// Completes the piece or expression. - StackValue, - /// The object is a pointer to a value which has no actual location, - /// such as an implicit value or a stack value. - /// - /// Represents `DW_OP_implicit_pointer`. - /// Completes the piece or expression. - ImplicitPointer { - /// The `.debug_info` offset of the value that this is an implicit pointer into. - value: DebugInfoOffset<Offset>, - /// The byte offset into the value that the implicit pointer points to. - byte_offset: i64, - }, - /// Evaluate an expression at the entry to the current subprogram, and push it on the stack. - /// - /// Represents `DW_OP_entry_value`. - EntryValue { - /// The expression to be evaluated. - expression: R, - }, - /// This represents a parameter that was optimized out. - /// - /// The offset points to 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 { - /// The DIE to use. - offset: UnitOffset<Offset>, - }, - /// Relocate the address if needed, and push it on the stack. - /// - /// Represents `DW_OP_addr`. - Address { - /// The offset to add. - address: u64, - }, - /// 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 { - /// The index of the address in `.debug_addr`. - index: DebugAddrIndex<Offset>, - }, - /// 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 { - /// The index of the address in `.debug_addr`. - index: DebugAddrIndex<Offset>, - }, - /// Interpret the value bytes as a constant of a given type, and push it on the stack. - /// - /// Represents `DW_OP_const_type`. - TypedLiteral { - /// The DIE of the base type. - base_type: UnitOffset<Offset>, - /// The value bytes. - value: R, - }, - /// Pop the top stack entry, convert it to a different type, and push it on the stack. - /// - /// Represents `DW_OP_convert`. - Convert { - /// The DIE of the base type. - base_type: UnitOffset<Offset>, - }, - /// 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 { - /// The DIE of the base type. - base_type: UnitOffset<Offset>, - }, - /// The index of a local in the currently executing function. - /// - /// Represents `DW_OP_WASM_location 0x00`. - /// Completes the piece or expression. - WasmLocal { - /// The index of the local. - index: u32, - }, - /// The index of a global. - /// - /// Represents `DW_OP_WASM_location 0x01` or `DW_OP_WASM_location 0x03`. - /// Completes the piece or expression. - WasmGlobal { - /// The index of the global. - index: u32, - }, - /// The index of an item on the operand stack. - /// - /// Represents `DW_OP_WASM_location 0x02`. - /// Completes the piece or expression. - WasmStack { - /// The index of the stack item. 0 is the bottom of the operand stack. - index: u32, - }, -} - -#[derive(Debug)] -enum OperationEvaluationResult<R: Reader> { - Piece, - Incomplete, - Complete { location: Location<R> }, - Waiting(EvaluationWaiting<R>, EvaluationResult<R>), -} - -/// A single location of a piece of the result of a DWARF expression. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Location<R, Offset = <R as Reader>::Offset> -where - R: Reader<Offset = Offset>, - Offset: ReaderOffset, -{ - /// The piece is empty. Ordinarily this means the piece has been - /// optimized away. - Empty, - /// The piece is found in a register. - Register { - /// The register number. - register: Register, - }, - /// The piece is found in memory. - Address { - /// The address. - address: u64, - }, - /// The piece has no location but its value is known. - Value { - /// The value. - value: Value, - }, - /// The piece is represented by some constant bytes. - Bytes { - /// The value. - value: R, - }, - /// The piece is a pointer to a value which has no actual location. - ImplicitPointer { - /// The `.debug_info` offset of the value that this is an implicit pointer into. - value: DebugInfoOffset<Offset>, - /// The byte offset into the value that the implicit pointer points to. - byte_offset: i64, - }, -} - -impl<R, Offset> Location<R, Offset> -where - R: Reader<Offset = Offset>, - Offset: ReaderOffset, -{ - /// Return true if the piece is empty. - pub fn is_empty(&self) -> bool { - matches!(*self, Location::Empty) - } -} - -/// The description of a single piece of the result of a DWARF -/// expression. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Piece<R, Offset = <R as Reader>::Offset> -where - R: Reader<Offset = Offset>, - Offset: ReaderOffset, -{ - /// If given, the size of the piece in bits. If `None`, there - /// must be only one piece whose size is all of the object. - pub size_in_bits: Option<u64>, - /// If given, the bit offset of the piece within the location. - /// If the location is a `Location::Register` or `Location::Value`, - /// then this offset is from the least significant bit end of - /// the register or value. - /// If the location is a `Location::Address` then the offset uses - /// the bit numbering and direction conventions of the language - /// and target system. - /// - /// If `None`, the piece starts at the location. If the - /// location is a register whose size is larger than the piece, - /// then placement within the register is defined by the ABI. - pub bit_offset: Option<u64>, - /// Where this piece is to be found. - pub location: Location<R, Offset>, -} - -// A helper function to handle branch offsets. -fn compute_pc<R: Reader>(pc: &R, bytecode: &R, offset: i16) -> Result<R> { - let pc_offset = pc.offset_from(bytecode); - let new_pc_offset = pc_offset.wrapping_add(R::Offset::from_i16(offset)); - if new_pc_offset > bytecode.len() { - Err(Error::BadBranchTarget(new_pc_offset.into_u64())) - } else { - let mut new_pc = bytecode.clone(); - new_pc.skip(new_pc_offset)?; - Ok(new_pc) - } -} - -fn generic_type<O: ReaderOffset>() -> UnitOffset<O> { - UnitOffset(O::from_u64(0).unwrap()) -} - -impl<R, Offset> Operation<R, Offset> -where - R: Reader<Offset = Offset>, - Offset: ReaderOffset, -{ - /// Parse a single DWARF expression operation. - /// - /// This is useful when examining a DWARF expression for reasons other - /// than direct evaluation. - /// - /// `bytes` points to a the operation to decode. It should point into - /// the same array as `bytecode`, which should be the entire - /// expression. - pub fn parse(bytes: &mut R, encoding: Encoding) -> Result<Operation<R, Offset>> { - let opcode = bytes.read_u8()?; - let name = constants::DwOp(opcode); - match name { - constants::DW_OP_addr => { - let address = bytes.read_address(encoding.address_size)?; - Ok(Operation::Address { address }) - } - constants::DW_OP_deref => Ok(Operation::Deref { - base_type: generic_type(), - size: encoding.address_size, - space: false, - }), - constants::DW_OP_const1u => { - let value = bytes.read_u8()?; - Ok(Operation::UnsignedConstant { - value: u64::from(value), - }) - } - constants::DW_OP_const1s => { - let value = bytes.read_i8()?; - Ok(Operation::SignedConstant { - value: i64::from(value), - }) - } - constants::DW_OP_const2u => { - let value = bytes.read_u16()?; - Ok(Operation::UnsignedConstant { - value: u64::from(value), - }) - } - constants::DW_OP_const2s => { - let value = bytes.read_i16()?; - Ok(Operation::SignedConstant { - value: i64::from(value), - }) - } - constants::DW_OP_const4u => { - let value = bytes.read_u32()?; - Ok(Operation::UnsignedConstant { - value: u64::from(value), - }) - } - constants::DW_OP_const4s => { - let value = bytes.read_i32()?; - Ok(Operation::SignedConstant { - value: i64::from(value), - }) - } - constants::DW_OP_const8u => { - let value = bytes.read_u64()?; - Ok(Operation::UnsignedConstant { value }) - } - constants::DW_OP_const8s => { - let value = bytes.read_i64()?; - Ok(Operation::SignedConstant { value }) - } - constants::DW_OP_constu => { - let value = bytes.read_uleb128()?; - Ok(Operation::UnsignedConstant { value }) - } - constants::DW_OP_consts => { - let value = bytes.read_sleb128()?; - Ok(Operation::SignedConstant { value }) - } - constants::DW_OP_dup => Ok(Operation::Pick { index: 0 }), - constants::DW_OP_drop => Ok(Operation::Drop), - constants::DW_OP_over => Ok(Operation::Pick { index: 1 }), - constants::DW_OP_pick => { - let value = bytes.read_u8()?; - Ok(Operation::Pick { index: value }) - } - constants::DW_OP_swap => Ok(Operation::Swap), - constants::DW_OP_rot => Ok(Operation::Rot), - constants::DW_OP_xderef => Ok(Operation::Deref { - base_type: generic_type(), - size: encoding.address_size, - space: true, - }), - constants::DW_OP_abs => Ok(Operation::Abs), - constants::DW_OP_and => Ok(Operation::And), - constants::DW_OP_div => Ok(Operation::Div), - constants::DW_OP_minus => Ok(Operation::Minus), - constants::DW_OP_mod => Ok(Operation::Mod), - constants::DW_OP_mul => Ok(Operation::Mul), - constants::DW_OP_neg => Ok(Operation::Neg), - constants::DW_OP_not => Ok(Operation::Not), - constants::DW_OP_or => Ok(Operation::Or), - constants::DW_OP_plus => Ok(Operation::Plus), - constants::DW_OP_plus_uconst => { - let value = bytes.read_uleb128()?; - Ok(Operation::PlusConstant { value }) - } - constants::DW_OP_shl => Ok(Operation::Shl), - constants::DW_OP_shr => Ok(Operation::Shr), - constants::DW_OP_shra => Ok(Operation::Shra), - constants::DW_OP_xor => Ok(Operation::Xor), - constants::DW_OP_bra => { - let target = bytes.read_i16()?; - Ok(Operation::Bra { target }) - } - constants::DW_OP_eq => Ok(Operation::Eq), - constants::DW_OP_ge => Ok(Operation::Ge), - constants::DW_OP_gt => Ok(Operation::Gt), - constants::DW_OP_le => Ok(Operation::Le), - constants::DW_OP_lt => Ok(Operation::Lt), - constants::DW_OP_ne => Ok(Operation::Ne), - constants::DW_OP_skip => { - let target = bytes.read_i16()?; - Ok(Operation::Skip { target }) - } - constants::DW_OP_lit0 - | constants::DW_OP_lit1 - | constants::DW_OP_lit2 - | constants::DW_OP_lit3 - | constants::DW_OP_lit4 - | constants::DW_OP_lit5 - | constants::DW_OP_lit6 - | constants::DW_OP_lit7 - | constants::DW_OP_lit8 - | constants::DW_OP_lit9 - | constants::DW_OP_lit10 - | constants::DW_OP_lit11 - | constants::DW_OP_lit12 - | constants::DW_OP_lit13 - | constants::DW_OP_lit14 - | constants::DW_OP_lit15 - | constants::DW_OP_lit16 - | constants::DW_OP_lit17 - | constants::DW_OP_lit18 - | constants::DW_OP_lit19 - | constants::DW_OP_lit20 - | constants::DW_OP_lit21 - | constants::DW_OP_lit22 - | constants::DW_OP_lit23 - | constants::DW_OP_lit24 - | constants::DW_OP_lit25 - | constants::DW_OP_lit26 - | constants::DW_OP_lit27 - | constants::DW_OP_lit28 - | constants::DW_OP_lit29 - | constants::DW_OP_lit30 - | constants::DW_OP_lit31 => Ok(Operation::UnsignedConstant { - value: (opcode - constants::DW_OP_lit0.0).into(), - }), - constants::DW_OP_reg0 - | constants::DW_OP_reg1 - | constants::DW_OP_reg2 - | constants::DW_OP_reg3 - | constants::DW_OP_reg4 - | constants::DW_OP_reg5 - | constants::DW_OP_reg6 - | constants::DW_OP_reg7 - | constants::DW_OP_reg8 - | constants::DW_OP_reg9 - | constants::DW_OP_reg10 - | constants::DW_OP_reg11 - | constants::DW_OP_reg12 - | constants::DW_OP_reg13 - | constants::DW_OP_reg14 - | constants::DW_OP_reg15 - | constants::DW_OP_reg16 - | constants::DW_OP_reg17 - | constants::DW_OP_reg18 - | constants::DW_OP_reg19 - | constants::DW_OP_reg20 - | constants::DW_OP_reg21 - | constants::DW_OP_reg22 - | constants::DW_OP_reg23 - | constants::DW_OP_reg24 - | constants::DW_OP_reg25 - | constants::DW_OP_reg26 - | constants::DW_OP_reg27 - | constants::DW_OP_reg28 - | constants::DW_OP_reg29 - | constants::DW_OP_reg30 - | constants::DW_OP_reg31 => Ok(Operation::Register { - register: Register((opcode - constants::DW_OP_reg0.0).into()), - }), - constants::DW_OP_breg0 - | constants::DW_OP_breg1 - | constants::DW_OP_breg2 - | constants::DW_OP_breg3 - | constants::DW_OP_breg4 - | constants::DW_OP_breg5 - | constants::DW_OP_breg6 - | constants::DW_OP_breg7 - | constants::DW_OP_breg8 - | constants::DW_OP_breg9 - | constants::DW_OP_breg10 - | constants::DW_OP_breg11 - | constants::DW_OP_breg12 - | constants::DW_OP_breg13 - | constants::DW_OP_breg14 - | constants::DW_OP_breg15 - | constants::DW_OP_breg16 - | constants::DW_OP_breg17 - | constants::DW_OP_breg18 - | constants::DW_OP_breg19 - | constants::DW_OP_breg20 - | constants::DW_OP_breg21 - | constants::DW_OP_breg22 - | constants::DW_OP_breg23 - | constants::DW_OP_breg24 - | constants::DW_OP_breg25 - | constants::DW_OP_breg26 - | constants::DW_OP_breg27 - | constants::DW_OP_breg28 - | constants::DW_OP_breg29 - | constants::DW_OP_breg30 - | constants::DW_OP_breg31 => { - let value = bytes.read_sleb128()?; - Ok(Operation::RegisterOffset { - register: Register((opcode - constants::DW_OP_breg0.0).into()), - offset: value, - base_type: generic_type(), - }) - } - constants::DW_OP_regx => { - let register = bytes.read_uleb128().and_then(Register::from_u64)?; - Ok(Operation::Register { register }) - } - constants::DW_OP_fbreg => { - let value = bytes.read_sleb128()?; - Ok(Operation::FrameOffset { offset: value }) - } - constants::DW_OP_bregx => { - let register = bytes.read_uleb128().and_then(Register::from_u64)?; - let offset = bytes.read_sleb128()?; - Ok(Operation::RegisterOffset { - register, - offset, - base_type: generic_type(), - }) - } - constants::DW_OP_piece => { - let size = bytes.read_uleb128()?; - Ok(Operation::Piece { - size_in_bits: 8 * size, - bit_offset: None, - }) - } - constants::DW_OP_deref_size => { - let size = bytes.read_u8()?; - Ok(Operation::Deref { - base_type: generic_type(), - size, - space: false, - }) - } - constants::DW_OP_xderef_size => { - let size = bytes.read_u8()?; - Ok(Operation::Deref { - base_type: generic_type(), - size, - space: true, - }) - } - constants::DW_OP_nop => Ok(Operation::Nop), - constants::DW_OP_push_object_address => Ok(Operation::PushObjectAddress), - constants::DW_OP_call2 => { - let value = bytes.read_u16().map(R::Offset::from_u16)?; - Ok(Operation::Call { - offset: DieReference::UnitRef(UnitOffset(value)), - }) - } - constants::DW_OP_call4 => { - let value = bytes.read_u32().map(R::Offset::from_u32)?; - Ok(Operation::Call { - offset: DieReference::UnitRef(UnitOffset(value)), - }) - } - constants::DW_OP_call_ref => { - let value = bytes.read_offset(encoding.format)?; - Ok(Operation::Call { - offset: DieReference::DebugInfoRef(DebugInfoOffset(value)), - }) - } - constants::DW_OP_form_tls_address | constants::DW_OP_GNU_push_tls_address => { - Ok(Operation::TLS) - } - constants::DW_OP_call_frame_cfa => Ok(Operation::CallFrameCFA), - constants::DW_OP_bit_piece => { - let size = bytes.read_uleb128()?; - let offset = bytes.read_uleb128()?; - Ok(Operation::Piece { - size_in_bits: size, - bit_offset: Some(offset), - }) - } - constants::DW_OP_implicit_value => { - let len = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - let data = bytes.split(len)?; - Ok(Operation::ImplicitValue { data }) - } - constants::DW_OP_stack_value => Ok(Operation::StackValue), - constants::DW_OP_implicit_pointer | constants::DW_OP_GNU_implicit_pointer => { - let value = if encoding.version == 2 { - bytes - .read_address(encoding.address_size) - .and_then(Offset::from_u64)? - } else { - bytes.read_offset(encoding.format)? - }; - let byte_offset = bytes.read_sleb128()?; - Ok(Operation::ImplicitPointer { - value: DebugInfoOffset(value), - byte_offset, - }) - } - constants::DW_OP_addrx | constants::DW_OP_GNU_addr_index => { - let index = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - Ok(Operation::AddressIndex { - index: DebugAddrIndex(index), - }) - } - constants::DW_OP_constx | constants::DW_OP_GNU_const_index => { - let index = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - Ok(Operation::ConstantIndex { - index: DebugAddrIndex(index), - }) - } - constants::DW_OP_entry_value | constants::DW_OP_GNU_entry_value => { - let len = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - let expression = bytes.split(len)?; - Ok(Operation::EntryValue { expression }) - } - constants::DW_OP_GNU_parameter_ref => { - let value = bytes.read_u32().map(R::Offset::from_u32)?; - Ok(Operation::ParameterRef { - offset: UnitOffset(value), - }) - } - constants::DW_OP_const_type | constants::DW_OP_GNU_const_type => { - let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - let len = bytes.read_u8()?; - let value = bytes.split(R::Offset::from_u8(len))?; - Ok(Operation::TypedLiteral { - base_type: UnitOffset(base_type), - value, - }) - } - constants::DW_OP_regval_type | constants::DW_OP_GNU_regval_type => { - let register = bytes.read_uleb128().and_then(Register::from_u64)?; - let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - Ok(Operation::RegisterOffset { - register, - offset: 0, - base_type: UnitOffset(base_type), - }) - } - constants::DW_OP_deref_type | constants::DW_OP_GNU_deref_type => { - let size = bytes.read_u8()?; - let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - Ok(Operation::Deref { - base_type: UnitOffset(base_type), - size, - space: false, - }) - } - constants::DW_OP_xderef_type => { - let size = bytes.read_u8()?; - let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - Ok(Operation::Deref { - base_type: UnitOffset(base_type), - size, - space: true, - }) - } - constants::DW_OP_convert | constants::DW_OP_GNU_convert => { - let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - Ok(Operation::Convert { - base_type: UnitOffset(base_type), - }) - } - constants::DW_OP_reinterpret | constants::DW_OP_GNU_reinterpret => { - let base_type = bytes.read_uleb128().and_then(R::Offset::from_u64)?; - Ok(Operation::Reinterpret { - base_type: UnitOffset(base_type), - }) - } - constants::DW_OP_WASM_location => match bytes.read_u8()? { - 0x0 => { - let index = bytes.read_uleb128_u32()?; - Ok(Operation::WasmLocal { index }) - } - 0x1 => { - let index = bytes.read_uleb128_u32()?; - Ok(Operation::WasmGlobal { index }) - } - 0x2 => { - let index = bytes.read_uleb128_u32()?; - Ok(Operation::WasmStack { index }) - } - 0x3 => { - let index = bytes.read_u32()?; - Ok(Operation::WasmGlobal { index }) - } - _ => Err(Error::InvalidExpression(name)), - }, - _ => Err(Error::InvalidExpression(name)), - } - } -} - -#[derive(Debug)] -enum EvaluationState<R: Reader> { - Start(Option<u64>), - Ready, - Error(Error), - Complete, - Waiting(EvaluationWaiting<R>), -} - -#[derive(Debug)] -enum EvaluationWaiting<R: Reader> { - Memory, - Register { offset: i64 }, - FrameBase { offset: i64 }, - Tls, - Cfa, - AtLocation, - EntryValue, - ParameterRef, - RelocatedAddress, - IndexedAddress, - TypedLiteral { value: R }, - Convert, - Reinterpret, -} - -/// The state of an `Evaluation` after evaluating a DWARF expression. -/// The evaluation is either `Complete`, or it requires more data -/// to continue, as described by the variant. -#[derive(Debug, PartialEq)] -pub enum EvaluationResult<R: Reader> { - /// The `Evaluation` is complete, and `Evaluation::result()` can be called. - Complete, - /// The `Evaluation` needs a value from memory to proceed further. Once the - /// caller determines what value to provide it should resume the `Evaluation` - /// by calling `Evaluation::resume_with_memory`. - RequiresMemory { - /// The address of the value required. - address: u64, - /// The size of the value required. This is guaranteed to be at most the - /// word size of the target architecture. - size: u8, - /// If not `None`, a target-specific address space value. - space: Option<u64>, - /// The DIE of the base type or 0 to indicate the generic type - base_type: UnitOffset<R::Offset>, - }, - /// The `Evaluation` needs a value from a register to proceed further. Once - /// the caller determines what value to provide it should resume the - /// `Evaluation` by calling `Evaluation::resume_with_register`. - RequiresRegister { - /// The register number. - register: Register, - /// The DIE of the base type or 0 to indicate the generic type - base_type: UnitOffset<R::Offset>, - }, - /// The `Evaluation` needs the frame base address to proceed further. Once - /// the caller determines what value to provide it should resume the - /// `Evaluation` by calling `Evaluation::resume_with_frame_base`. The frame - /// base address is the address produced by the location description in the - /// `DW_AT_frame_base` attribute of the current function. - RequiresFrameBase, - /// The `Evaluation` needs a value from TLS to proceed further. Once the - /// caller determines what value to provide it should resume the - /// `Evaluation` by calling `Evaluation::resume_with_tls`. - RequiresTls(u64), - /// The `Evaluation` needs the CFA to proceed further. Once the caller - /// determines what value to provide it should resume the `Evaluation` by - /// calling `Evaluation::resume_with_call_frame_cfa`. - RequiresCallFrameCfa, - /// The `Evaluation` needs the DWARF expression at the given location to - /// proceed further. Once the caller determines what value to provide it - /// should resume the `Evaluation` by calling - /// `Evaluation::resume_with_at_location`. - RequiresAtLocation(DieReference<R::Offset>), - /// The `Evaluation` needs the value produced by evaluating a DWARF - /// expression at the entry point of the current subprogram. Once the - /// caller determines what value to provide it should resume the - /// `Evaluation` by calling `Evaluation::resume_with_entry_value`. - RequiresEntryValue(Expression<R>), - /// The `Evaluation` needs the value of the parameter at the given location - /// in the current function's caller. Once the caller determines what value - /// to provide it should resume the `Evaluation` by calling - /// `Evaluation::resume_with_parameter_ref`. - RequiresParameterRef(UnitOffset<R::Offset>), - /// The `Evaluation` needs an address to be relocated to proceed further. - /// Once the caller determines what value to provide it should resume the - /// `Evaluation` by calling `Evaluation::resume_with_relocated_address`. - RequiresRelocatedAddress(u64), - /// The `Evaluation` needs an address from the `.debug_addr` section. - /// This address may also need to be relocated. - /// Once the caller determines what value to provide it should resume the - /// `Evaluation` by calling `Evaluation::resume_with_indexed_address`. - RequiresIndexedAddress { - /// The index of the address in the `.debug_addr` section, - /// relative to the `DW_AT_addr_base` of the compilation unit. - index: DebugAddrIndex<R::Offset>, - /// Whether the address also needs to be relocated. - relocate: bool, - }, - /// The `Evaluation` needs the `ValueType` for the base type DIE at - /// the give unit offset. Once the caller determines what value to provide it - /// should resume the `Evaluation` by calling - /// `Evaluation::resume_with_base_type`. - RequiresBaseType(UnitOffset<R::Offset>), -} - -/// The bytecode for a DWARF expression or location description. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Expression<R: Reader>(pub R); - -impl<R: Reader> Expression<R> { - /// Create an evaluation for this expression. - /// - /// The `encoding` is determined by the - /// [`CompilationUnitHeader`](struct.CompilationUnitHeader.html) or - /// [`TypeUnitHeader`](struct.TypeUnitHeader.html) that this expression - /// relates to. - /// - /// # Examples - /// ```rust,no_run - /// use gimli::Expression; - /// # let endian = gimli::LittleEndian; - /// # let debug_info = gimli::DebugInfo::from(gimli::EndianSlice::new(&[], endian)); - /// # let unit = debug_info.units().next().unwrap().unwrap(); - /// # let bytecode = gimli::EndianSlice::new(&[], endian); - /// let expression = gimli::Expression(bytecode); - /// let mut eval = expression.evaluation(unit.encoding()); - /// let mut result = eval.evaluate().unwrap(); - /// ``` - #[cfg(feature = "read")] - #[inline] - pub fn evaluation(self, encoding: Encoding) -> Evaluation<R> { - Evaluation::new(self.0, encoding) - } - - /// Return an iterator for the operations in the expression. - pub fn operations(self, encoding: Encoding) -> OperationIter<R> { - OperationIter { - input: self.0, - encoding, - } - } -} - -/// An iterator for the operations in an expression. -#[derive(Debug, Clone, Copy)] -pub struct OperationIter<R: Reader> { - input: R, - encoding: Encoding, -} - -impl<R: Reader> OperationIter<R> { - /// Read the next operation in an expression. - pub fn next(&mut self) -> Result<Option<Operation<R>>> { - if self.input.is_empty() { - return Ok(None); - } - match Operation::parse(&mut self.input, self.encoding) { - Ok(op) => Ok(Some(op)), - Err(e) => { - self.input.empty(); - Err(e) - } - } - } - - /// Return the current byte offset of the iterator. - pub fn offset_from(&self, expression: &Expression<R>) -> R::Offset { - self.input.offset_from(&expression.0) - } -} - -#[cfg(feature = "fallible-iterator")] -impl<R: Reader> fallible_iterator::FallibleIterator for OperationIter<R> { - type Item = Operation<R>; - type Error = Error; - - fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { - OperationIter::next(self) - } -} - -/// Specification of what storage should be used for [`Evaluation`]. -/// -#[cfg_attr( - feature = "read", - doc = " -Normally you would only need to use [`StoreOnHeap`], which places the stacks and the results -on the heap using [`Vec`]. This is the default storage type parameter for [`Evaluation`]. -" -)] -/// -/// If you need to avoid [`Evaluation`] from allocating memory, e.g. for signal safety, -/// you can provide you own storage specification: -/// ```rust,no_run -/// # use gimli::*; -/// # let bytecode = EndianSlice::new(&[], LittleEndian); -/// # let encoding = unimplemented!(); -/// # let get_register_value = |_, _| Value::Generic(42); -/// # let get_frame_base = || 0xdeadbeef; -/// # -/// struct StoreOnStack; -/// -/// impl<R: Reader> EvaluationStorage<R> for StoreOnStack { -/// type Stack = [Value; 64]; -/// type ExpressionStack = [(R, R); 4]; -/// type Result = [Piece<R>; 1]; -/// } -/// -/// let mut eval = Evaluation::<_, StoreOnStack>::new_in(bytecode, encoding); -/// let mut result = eval.evaluate().unwrap(); -/// while result != EvaluationResult::Complete { -/// match result { -/// EvaluationResult::RequiresRegister { register, base_type } => { -/// let value = get_register_value(register, base_type); -/// result = eval.resume_with_register(value).unwrap(); -/// }, -/// EvaluationResult::RequiresFrameBase => { -/// let frame_base = get_frame_base(); -/// result = eval.resume_with_frame_base(frame_base).unwrap(); -/// }, -/// _ => unimplemented!(), -/// }; -/// } -/// -/// let result = eval.as_result(); -/// println!("{:?}", result); -/// ``` -pub trait EvaluationStorage<R: Reader> { - /// The storage used for the evaluation stack. - type Stack: ArrayLike<Item = Value>; - /// The storage used for the expression stack. - type ExpressionStack: ArrayLike<Item = (R, R)>; - /// The storage used for the results. - type Result: ArrayLike<Item = Piece<R>>; -} - -#[cfg(feature = "read")] -impl<R: Reader> EvaluationStorage<R> for StoreOnHeap { - type Stack = Vec<Value>; - type ExpressionStack = Vec<(R, R)>; - type Result = Vec<Piece<R>>; -} - -/// A DWARF expression evaluator. -/// -/// # Usage -/// A DWARF expression may require additional data to produce a final result, -/// such as the value of a register or a memory location. Once initial setup -/// is complete (i.e. `set_initial_value()`, `set_object_address()`) the -/// consumer calls the `evaluate()` method. That returns an `EvaluationResult`, -/// which is either `EvaluationResult::Complete` or a value indicating what -/// data is needed to resume the `Evaluation`. The consumer is responsible for -/// producing that data and resuming the computation with the correct method, -/// as documented for `EvaluationResult`. Only once an `EvaluationResult::Complete` -/// is returned can the consumer call `result()`. -/// -/// This design allows the consumer of `Evaluation` to decide how and when to -/// produce the required data and resume the computation. The `Evaluation` can -/// be driven synchronously (as shown below) or by some asynchronous mechanism -/// such as futures. -/// -/// # Examples -/// ```rust,no_run -/// use gimli::{Evaluation, EvaluationResult, Expression}; -/// # let bytecode = gimli::EndianSlice::new(&[], gimli::LittleEndian); -/// # let encoding = unimplemented!(); -/// # let get_register_value = |_, _| gimli::Value::Generic(42); -/// # let get_frame_base = || 0xdeadbeef; -/// -/// let mut eval = Evaluation::new(bytecode, encoding); -/// let mut result = eval.evaluate().unwrap(); -/// while result != EvaluationResult::Complete { -/// match result { -/// EvaluationResult::RequiresRegister { register, base_type } => { -/// let value = get_register_value(register, base_type); -/// result = eval.resume_with_register(value).unwrap(); -/// }, -/// EvaluationResult::RequiresFrameBase => { -/// let frame_base = get_frame_base(); -/// result = eval.resume_with_frame_base(frame_base).unwrap(); -/// }, -/// _ => unimplemented!(), -/// }; -/// } -/// -/// let result = eval.result(); -/// println!("{:?}", result); -/// ``` -#[derive(Debug)] -pub struct Evaluation<R: Reader, S: EvaluationStorage<R> = StoreOnHeap> { - bytecode: R, - encoding: Encoding, - object_address: Option<u64>, - max_iterations: Option<u32>, - iteration: u32, - state: EvaluationState<R>, - - // Stack operations are done on word-sized values. We do all - // operations on 64-bit values, and then mask the results - // appropriately when popping. - addr_mask: u64, - - // The stack. - stack: ArrayVec<S::Stack>, - - // The next operation to decode and evaluate. - pc: R, - - // If we see a DW_OP_call* operation, the previous PC and bytecode - // is stored here while evaluating the subroutine. - expression_stack: ArrayVec<S::ExpressionStack>, - - value_result: Option<Value>, - result: ArrayVec<S::Result>, -} - -#[cfg(feature = "read")] -impl<R: Reader> Evaluation<R> { - /// Create a new DWARF expression evaluator. - /// - /// The new evaluator is created without an initial value, without - /// an object address, and without a maximum number of iterations. - pub fn new(bytecode: R, encoding: Encoding) -> Self { - Self::new_in(bytecode, encoding) - } - - /// Get the result of this `Evaluation`. - /// - /// # Panics - /// Panics if this `Evaluation` has not been driven to completion. - pub fn result(self) -> Vec<Piece<R>> { - match self.state { - EvaluationState::Complete => self.result.into_vec(), - _ => { - panic!("Called `Evaluation::result` on an `Evaluation` that has not been completed") - } - } - } -} - -impl<R: Reader, S: EvaluationStorage<R>> Evaluation<R, S> { - /// Create a new DWARF expression evaluator. - /// - /// The new evaluator is created without an initial value, without - /// an object address, and without a maximum number of iterations. - pub fn new_in(bytecode: R, encoding: Encoding) -> Self { - let pc = bytecode.clone(); - Evaluation { - bytecode, - encoding, - object_address: None, - max_iterations: None, - iteration: 0, - state: EvaluationState::Start(None), - addr_mask: if encoding.address_size == 8 { - !0u64 - } else { - (1 << (8 * u64::from(encoding.address_size))) - 1 - }, - stack: Default::default(), - expression_stack: Default::default(), - pc, - value_result: None, - result: Default::default(), - } - } - - /// Set an initial value to be pushed on the DWARF expression - /// evaluator's stack. This can be used in cases like - /// `DW_AT_vtable_elem_location`, which require a value on the - /// stack before evaluation commences. If no initial value is - /// set, and the expression uses an opcode requiring the initial - /// value, then evaluation will fail with an error. - /// - /// # Panics - /// Panics if `set_initial_value()` has already been called, or if - /// `evaluate()` has already been called. - pub fn set_initial_value(&mut self, value: u64) { - match self.state { - EvaluationState::Start(None) => { - self.state = EvaluationState::Start(Some(value)); - } - _ => panic!( - "`Evaluation::set_initial_value` was called twice, or after evaluation began." - ), - }; - } - - /// Set the enclosing object's address, as used by - /// `DW_OP_push_object_address`. If no object address is set, and - /// the expression uses an opcode requiring the object address, - /// then evaluation will fail with an error. - pub fn set_object_address(&mut self, value: u64) { - self.object_address = Some(value); - } - - /// Set the maximum number of iterations to be allowed by the - /// expression evaluator. - /// - /// An iteration corresponds approximately to the evaluation of a - /// single operation in an expression ("approximately" because the - /// implementation may allow two such operations in some cases). - /// The default is not to have a maximum; once set, it's not - /// possible to go back to this default state. This value can be - /// set to avoid denial of service attacks by bad DWARF bytecode. - pub fn set_max_iterations(&mut self, value: u32) { - self.max_iterations = Some(value); - } - - fn pop(&mut self) -> Result<Value> { - match self.stack.pop() { - Some(value) => Ok(value), - None => Err(Error::NotEnoughStackItems), - } - } - - fn push(&mut self, value: Value) -> Result<()> { - self.stack.try_push(value).map_err(|_| Error::StackFull) - } - - fn evaluate_one_operation(&mut self) -> Result<OperationEvaluationResult<R>> { - let operation = Operation::parse(&mut self.pc, self.encoding)?; - - match operation { - Operation::Deref { - base_type, - size, - space, - } => { - let entry = self.pop()?; - let addr = entry.to_u64(self.addr_mask)?; - let addr_space = if space { - let entry = self.pop()?; - let value = entry.to_u64(self.addr_mask)?; - Some(value) - } else { - None - }; - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::Memory, - EvaluationResult::RequiresMemory { - address: addr, - size, - space: addr_space, - base_type, - }, - )); - } - - Operation::Drop => { - self.pop()?; - } - Operation::Pick { index } => { - let len = self.stack.len(); - let index = index as usize; - if index >= len { - return Err(Error::NotEnoughStackItems); - } - let value = self.stack[len - index - 1]; - self.push(value)?; - } - Operation::Swap => { - let top = self.pop()?; - let next = self.pop()?; - self.push(top)?; - self.push(next)?; - } - Operation::Rot => { - let one = self.pop()?; - let two = self.pop()?; - let three = self.pop()?; - self.push(one)?; - self.push(three)?; - self.push(two)?; - } - - Operation::Abs => { - let value = self.pop()?; - let result = value.abs(self.addr_mask)?; - self.push(result)?; - } - Operation::And => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.and(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Div => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.div(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Minus => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.sub(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Mod => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.rem(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Mul => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.mul(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Neg => { - let v = self.pop()?; - let result = v.neg(self.addr_mask)?; - self.push(result)?; - } - Operation::Not => { - let value = self.pop()?; - let result = value.not(self.addr_mask)?; - self.push(result)?; - } - Operation::Or => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.or(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Plus => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.add(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::PlusConstant { value } => { - let lhs = self.pop()?; - let rhs = Value::from_u64(lhs.value_type(), value)?; - let result = lhs.add(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Shl => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.shl(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Shr => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.shr(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Shra => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.shra(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Xor => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.xor(rhs, self.addr_mask)?; - self.push(result)?; - } - - Operation::Bra { target } => { - let entry = self.pop()?; - let v = entry.to_u64(self.addr_mask)?; - if v != 0 { - self.pc = compute_pc(&self.pc, &self.bytecode, target)?; - } - } - - Operation::Eq => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.eq(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Ge => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.ge(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Gt => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.gt(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Le => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.le(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Lt => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.lt(rhs, self.addr_mask)?; - self.push(result)?; - } - Operation::Ne => { - let rhs = self.pop()?; - let lhs = self.pop()?; - let result = lhs.ne(rhs, self.addr_mask)?; - self.push(result)?; - } - - Operation::Skip { target } => { - self.pc = compute_pc(&self.pc, &self.bytecode, target)?; - } - - Operation::UnsignedConstant { value } => { - self.push(Value::Generic(value))?; - } - - Operation::SignedConstant { value } => { - self.push(Value::Generic(value as u64))?; - } - - Operation::RegisterOffset { - register, - offset, - base_type, - } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::Register { offset }, - EvaluationResult::RequiresRegister { - register, - base_type, - }, - )); - } - - Operation::FrameOffset { offset } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::FrameBase { offset }, - EvaluationResult::RequiresFrameBase, - )); - } - - Operation::Nop => {} - - Operation::PushObjectAddress => { - if let Some(value) = self.object_address { - self.push(Value::Generic(value))?; - } else { - return Err(Error::InvalidPushObjectAddress); - } - } - - Operation::Call { offset } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::AtLocation, - EvaluationResult::RequiresAtLocation(offset), - )); - } - - Operation::TLS => { - let entry = self.pop()?; - let index = entry.to_u64(self.addr_mask)?; - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::Tls, - EvaluationResult::RequiresTls(index), - )); - } - - Operation::CallFrameCFA => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::Cfa, - EvaluationResult::RequiresCallFrameCfa, - )); - } - - Operation::Register { register } => { - let location = Location::Register { register }; - return Ok(OperationEvaluationResult::Complete { location }); - } - - Operation::ImplicitValue { ref data } => { - let location = Location::Bytes { - value: data.clone(), - }; - return Ok(OperationEvaluationResult::Complete { location }); - } - - Operation::StackValue => { - let value = self.pop()?; - let location = Location::Value { value }; - return Ok(OperationEvaluationResult::Complete { location }); - } - - Operation::ImplicitPointer { value, byte_offset } => { - let location = Location::ImplicitPointer { value, byte_offset }; - return Ok(OperationEvaluationResult::Complete { location }); - } - - Operation::EntryValue { ref expression } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::EntryValue, - EvaluationResult::RequiresEntryValue(Expression(expression.clone())), - )); - } - - Operation::ParameterRef { offset } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::ParameterRef, - EvaluationResult::RequiresParameterRef(offset), - )); - } - - Operation::Address { address } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::RelocatedAddress, - EvaluationResult::RequiresRelocatedAddress(address), - )); - } - - Operation::AddressIndex { index } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::IndexedAddress, - EvaluationResult::RequiresIndexedAddress { - index, - relocate: true, - }, - )); - } - - Operation::ConstantIndex { index } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::IndexedAddress, - EvaluationResult::RequiresIndexedAddress { - index, - relocate: false, - }, - )); - } - - Operation::Piece { - size_in_bits, - bit_offset, - } => { - let location = if self.stack.is_empty() { - Location::Empty - } else { - let entry = self.pop()?; - let address = entry.to_u64(self.addr_mask)?; - Location::Address { address } - }; - self.result - .try_push(Piece { - size_in_bits: Some(size_in_bits), - bit_offset, - location, - }) - .map_err(|_| Error::StackFull)?; - return Ok(OperationEvaluationResult::Piece); - } - - Operation::TypedLiteral { base_type, value } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::TypedLiteral { value }, - EvaluationResult::RequiresBaseType(base_type), - )); - } - Operation::Convert { base_type } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::Convert, - EvaluationResult::RequiresBaseType(base_type), - )); - } - Operation::Reinterpret { base_type } => { - return Ok(OperationEvaluationResult::Waiting( - EvaluationWaiting::Reinterpret, - EvaluationResult::RequiresBaseType(base_type), - )); - } - Operation::WasmLocal { .. } - | Operation::WasmGlobal { .. } - | Operation::WasmStack { .. } => { - return Err(Error::UnsupportedEvaluation); - } - } - - Ok(OperationEvaluationResult::Incomplete) - } - - /// Get the result if this is an evaluation for a value. - /// - /// Returns `None` if the evaluation contained operations that are only - /// valid for location descriptions. - /// - /// # Panics - /// Panics if this `Evaluation` has not been driven to completion. - pub fn value_result(&self) -> Option<Value> { - match self.state { - EvaluationState::Complete => self.value_result, - _ => { - panic!("Called `Evaluation::value_result` on an `Evaluation` that has not been completed") - } - } - } - - /// Get the result of this `Evaluation`. - /// - /// # Panics - /// Panics if this `Evaluation` has not been driven to completion. - pub fn as_result(&self) -> &[Piece<R>] { - match self.state { - EvaluationState::Complete => &self.result, - _ => { - panic!( - "Called `Evaluation::as_result` on an `Evaluation` that has not been completed" - ) - } - } - } - - /// Evaluate a DWARF expression. This method should only ever be called - /// once. If the returned `EvaluationResult` is not - /// `EvaluationResult::Complete`, the caller should provide the required - /// value and resume the evaluation by calling the appropriate resume_with - /// method on `Evaluation`. - pub fn evaluate(&mut self) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Start(initial_value) => { - if let Some(value) = initial_value { - self.push(Value::Generic(value))?; - } - self.state = EvaluationState::Ready; - } - EvaluationState::Ready => {} - EvaluationState::Error(err) => return Err(err), - EvaluationState::Complete => return Ok(EvaluationResult::Complete), - EvaluationState::Waiting(_) => panic!(), - }; - - match self.evaluate_internal() { - Ok(r) => Ok(r), - Err(e) => { - self.state = EvaluationState::Error(e); - Err(e) - } - } - } - - /// Resume the `Evaluation` with the provided memory `value`. This will apply - /// the provided memory value to the evaluation and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresMemory`. - pub fn resume_with_memory(&mut self, value: Value) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::Memory) => { - self.push(value)?; - } - _ => panic!( - "Called `Evaluation::resume_with_memory` without a preceding `EvaluationResult::RequiresMemory`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided `register` value. This will apply - /// the provided register value to the evaluation and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresRegister`. - pub fn resume_with_register(&mut self, value: Value) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::Register { offset }) => { - let offset = Value::from_u64(value.value_type(), offset as u64)?; - let value = value.add(offset, self.addr_mask)?; - self.push(value)?; - } - _ => panic!( - "Called `Evaluation::resume_with_register` without a preceding `EvaluationResult::RequiresRegister`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided `frame_base`. This will - /// apply the provided frame base value to the evaluation and continue - /// evaluating opcodes until the evaluation is completed, reaches an error, - /// or needs more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresFrameBase`. - pub fn resume_with_frame_base(&mut self, frame_base: u64) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::FrameBase { offset }) => { - self.push(Value::Generic(frame_base.wrapping_add(offset as u64)))?; - } - _ => panic!( - "Called `Evaluation::resume_with_frame_base` without a preceding `EvaluationResult::RequiresFrameBase`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided `value`. This will apply - /// the provided TLS value to the evaluation and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresTls`. - pub fn resume_with_tls(&mut self, value: u64) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::Tls) => { - self.push(Value::Generic(value))?; - } - _ => panic!( - "Called `Evaluation::resume_with_tls` without a preceding `EvaluationResult::RequiresTls`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided `cfa`. This will - /// apply the provided CFA value to the evaluation and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresCallFrameCfa`. - pub fn resume_with_call_frame_cfa(&mut self, cfa: u64) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::Cfa) => { - self.push(Value::Generic(cfa))?; - } - _ => panic!( - "Called `Evaluation::resume_with_call_frame_cfa` without a preceding `EvaluationResult::RequiresCallFrameCfa`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided `bytes`. This will - /// continue processing the evaluation with the new expression provided - /// until the evaluation is completed, reaches an error, or needs more - /// information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresAtLocation`. - pub fn resume_with_at_location(&mut self, mut bytes: R) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::AtLocation) => { - if !bytes.is_empty() { - let mut pc = bytes.clone(); - mem::swap(&mut pc, &mut self.pc); - mem::swap(&mut bytes, &mut self.bytecode); - self.expression_stack.try_push((pc, bytes)).map_err(|_| Error::StackFull)?; - } - } - _ => panic!( - "Called `Evaluation::resume_with_at_location` without a precedeing `EvaluationResult::RequiresAtLocation`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided `entry_value`. This will - /// apply the provided entry value to the evaluation and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresEntryValue`. - pub fn resume_with_entry_value(&mut self, entry_value: Value) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::EntryValue) => { - self.push(entry_value)?; - } - _ => panic!( - "Called `Evaluation::resume_with_entry_value` without a preceding `EvaluationResult::RequiresEntryValue`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided `parameter_value`. This will - /// apply the provided parameter value to the evaluation and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresParameterRef`. - pub fn resume_with_parameter_ref( - &mut self, - parameter_value: u64, - ) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::ParameterRef) => { - self.push(Value::Generic(parameter_value))?; - } - _ => panic!( - "Called `Evaluation::resume_with_parameter_ref` without a preceding `EvaluationResult::RequiresParameterRef`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided relocated `address`. This will use the - /// provided relocated address for the operation that required it, and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with - /// `EvaluationResult::RequiresRelocatedAddress`. - pub fn resume_with_relocated_address(&mut self, address: u64) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::RelocatedAddress) => { - self.push(Value::Generic(address))?; - } - _ => panic!( - "Called `Evaluation::resume_with_relocated_address` without a preceding `EvaluationResult::RequiresRelocatedAddress`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided indexed `address`. This will use the - /// provided indexed address for the operation that required it, and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with - /// `EvaluationResult::RequiresIndexedAddress`. - pub fn resume_with_indexed_address(&mut self, address: u64) -> Result<EvaluationResult<R>> { - match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::IndexedAddress) => { - self.push(Value::Generic(address))?; - } - _ => panic!( - "Called `Evaluation::resume_with_indexed_address` without a preceding `EvaluationResult::RequiresIndexedAddress`" - ), - }; - - self.evaluate_internal() - } - - /// Resume the `Evaluation` with the provided `base_type`. This will use the - /// provided base type for the operation that required it, and continue evaluating - /// opcodes until the evaluation is completed, reaches an error, or needs - /// more information again. - /// - /// # Panics - /// Panics if this `Evaluation` did not previously stop with `EvaluationResult::RequiresBaseType`. - pub fn resume_with_base_type(&mut self, base_type: ValueType) -> Result<EvaluationResult<R>> { - let value = match self.state { - EvaluationState::Error(err) => return Err(err), - EvaluationState::Waiting(EvaluationWaiting::TypedLiteral { ref value }) => { - Value::parse(base_type, value.clone())? - } - EvaluationState::Waiting(EvaluationWaiting::Convert) => { - let entry = self.pop()?; - entry.convert(base_type, self.addr_mask)? - } - EvaluationState::Waiting(EvaluationWaiting::Reinterpret) => { - let entry = self.pop()?; - entry.reinterpret(base_type, self.addr_mask)? - } - _ => panic!( - "Called `Evaluation::resume_with_base_type` without a preceding `EvaluationResult::RequiresBaseType`" - ), - }; - self.push(value)?; - self.evaluate_internal() - } - - fn end_of_expression(&mut self) -> bool { - while self.pc.is_empty() { - match self.expression_stack.pop() { - Some((newpc, newbytes)) => { - self.pc = newpc; - self.bytecode = newbytes; - } - None => return true, - } - } - false - } - - fn evaluate_internal(&mut self) -> Result<EvaluationResult<R>> { - while !self.end_of_expression() { - self.iteration += 1; - if let Some(max_iterations) = self.max_iterations { - if self.iteration > max_iterations { - return Err(Error::TooManyIterations); - } - } - - let op_result = self.evaluate_one_operation()?; - match op_result { - OperationEvaluationResult::Piece => {} - OperationEvaluationResult::Incomplete => { - if self.end_of_expression() && !self.result.is_empty() { - // We saw a piece earlier and then some - // unterminated piece. It's not clear this is - // well-defined. - return Err(Error::InvalidPiece); - } - } - OperationEvaluationResult::Complete { location } => { - if self.end_of_expression() { - if !self.result.is_empty() { - // We saw a piece earlier and then some - // unterminated piece. It's not clear this is - // well-defined. - return Err(Error::InvalidPiece); - } - self.result - .try_push(Piece { - size_in_bits: None, - bit_offset: None, - location, - }) - .map_err(|_| Error::StackFull)?; - } else { - // If there are more operations, then the next operation must - // be a Piece. - match Operation::parse(&mut self.pc, self.encoding)? { - Operation::Piece { - size_in_bits, - bit_offset, - } => { - self.result - .try_push(Piece { - size_in_bits: Some(size_in_bits), - bit_offset, - location, - }) - .map_err(|_| Error::StackFull)?; - } - _ => { - let value = - self.bytecode.len().into_u64() - self.pc.len().into_u64() - 1; - return Err(Error::InvalidExpressionTerminator(value)); - } - } - } - } - OperationEvaluationResult::Waiting(waiting, result) => { - self.state = EvaluationState::Waiting(waiting); - return Ok(result); - } - } - } - - // If no pieces have been seen, use the stack top as the - // result. - if self.result.is_empty() { - let entry = self.pop()?; - self.value_result = Some(entry); - let addr = entry.to_u64(self.addr_mask)?; - self.result - .try_push(Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Address { address: addr }, - }) - .map_err(|_| Error::StackFull)?; - } - - self.state = EvaluationState::Complete; - Ok(EvaluationResult::Complete) - } -} - -#[cfg(test)] -// Tests require leb128::write. -#[cfg(feature = "write")] -mod tests { - use super::*; - use crate::common::Format; - use crate::constants; - use crate::endianity::LittleEndian; - use crate::leb128; - use crate::read::{EndianSlice, Error, Result, UnitOffset}; - use crate::test_util::GimliSectionMethods; - use core::usize; - use test_assembler::{Endian, Section}; - - fn encoding4() -> Encoding { - Encoding { - format: Format::Dwarf32, - version: 4, - address_size: 4, - } - } - - fn encoding8() -> Encoding { - Encoding { - format: Format::Dwarf64, - version: 4, - address_size: 8, - } - } - - #[test] - fn test_compute_pc() { - // Contents don't matter for this test, just length. - let bytes = [0, 1, 2, 3, 4]; - let bytecode = &bytes[..]; - let ebuf = &EndianSlice::new(bytecode, LittleEndian); - - assert_eq!(compute_pc(ebuf, ebuf, 0), Ok(*ebuf)); - assert_eq!( - compute_pc(ebuf, ebuf, -1), - Err(Error::BadBranchTarget(usize::MAX as u64)) - ); - assert_eq!(compute_pc(ebuf, ebuf, 5), Ok(ebuf.range_from(5..))); - assert_eq!( - compute_pc(&ebuf.range_from(3..), ebuf, -2), - Ok(ebuf.range_from(1..)) - ); - assert_eq!( - compute_pc(&ebuf.range_from(2..), ebuf, 2), - Ok(ebuf.range_from(4..)) - ); - } - - fn check_op_parse_simple<'input>( - input: &'input [u8], - expect: &Operation<EndianSlice<'input, LittleEndian>>, - encoding: Encoding, - ) { - let buf = EndianSlice::new(input, LittleEndian); - let mut pc = buf; - let value = Operation::parse(&mut pc, encoding); - match value { - Ok(val) => { - assert_eq!(val, *expect); - assert_eq!(pc.len(), 0); - } - _ => panic!("Unexpected result"), - } - } - - fn check_op_parse_eof(input: &[u8], encoding: Encoding) { - let buf = EndianSlice::new(input, LittleEndian); - let mut pc = buf; - match Operation::parse(&mut pc, encoding) { - Err(Error::UnexpectedEof(id)) => { - assert!(buf.lookup_offset_id(id).is_some()); - } - - _ => panic!("Unexpected result"), - } - } - - fn check_op_parse<F>( - input: F, - expect: &Operation<EndianSlice<LittleEndian>>, - encoding: Encoding, - ) where - F: Fn(Section) -> Section, - { - let input = input(Section::with_endian(Endian::Little)) - .get_contents() - .unwrap(); - for i in 1..input.len() { - check_op_parse_eof(&input[..i], encoding); - } - check_op_parse_simple(&input, expect, encoding); - } - - #[test] - fn test_op_parse_onebyte() { - // Doesn't matter for this test. - let encoding = encoding4(); - - // Test all single-byte opcodes. - #[rustfmt::skip] - let inputs = [ - ( - constants::DW_OP_deref, - Operation::Deref { - base_type: generic_type(), - size: encoding.address_size, - space: false, - }, - ), - (constants::DW_OP_dup, Operation::Pick { index: 0 }), - (constants::DW_OP_drop, Operation::Drop), - (constants::DW_OP_over, Operation::Pick { index: 1 }), - (constants::DW_OP_swap, Operation::Swap), - (constants::DW_OP_rot, Operation::Rot), - ( - constants::DW_OP_xderef, - Operation::Deref { - base_type: generic_type(), - size: encoding.address_size, - space: true, - }, - ), - (constants::DW_OP_abs, Operation::Abs), - (constants::DW_OP_and, Operation::And), - (constants::DW_OP_div, Operation::Div), - (constants::DW_OP_minus, Operation::Minus), - (constants::DW_OP_mod, Operation::Mod), - (constants::DW_OP_mul, Operation::Mul), - (constants::DW_OP_neg, Operation::Neg), - (constants::DW_OP_not, Operation::Not), - (constants::DW_OP_or, Operation::Or), - (constants::DW_OP_plus, Operation::Plus), - (constants::DW_OP_shl, Operation::Shl), - (constants::DW_OP_shr, Operation::Shr), - (constants::DW_OP_shra, Operation::Shra), - (constants::DW_OP_xor, Operation::Xor), - (constants::DW_OP_eq, Operation::Eq), - (constants::DW_OP_ge, Operation::Ge), - (constants::DW_OP_gt, Operation::Gt), - (constants::DW_OP_le, Operation::Le), - (constants::DW_OP_lt, Operation::Lt), - (constants::DW_OP_ne, Operation::Ne), - (constants::DW_OP_lit0, Operation::UnsignedConstant { value: 0 }), - (constants::DW_OP_lit1, Operation::UnsignedConstant { value: 1 }), - (constants::DW_OP_lit2, Operation::UnsignedConstant { value: 2 }), - (constants::DW_OP_lit3, Operation::UnsignedConstant { value: 3 }), - (constants::DW_OP_lit4, Operation::UnsignedConstant { value: 4 }), - (constants::DW_OP_lit5, Operation::UnsignedConstant { value: 5 }), - (constants::DW_OP_lit6, Operation::UnsignedConstant { value: 6 }), - (constants::DW_OP_lit7, Operation::UnsignedConstant { value: 7 }), - (constants::DW_OP_lit8, Operation::UnsignedConstant { value: 8 }), - (constants::DW_OP_lit9, Operation::UnsignedConstant { value: 9 }), - (constants::DW_OP_lit10, Operation::UnsignedConstant { value: 10 }), - (constants::DW_OP_lit11, Operation::UnsignedConstant { value: 11 }), - (constants::DW_OP_lit12, Operation::UnsignedConstant { value: 12 }), - (constants::DW_OP_lit13, Operation::UnsignedConstant { value: 13 }), - (constants::DW_OP_lit14, Operation::UnsignedConstant { value: 14 }), - (constants::DW_OP_lit15, Operation::UnsignedConstant { value: 15 }), - (constants::DW_OP_lit16, Operation::UnsignedConstant { value: 16 }), - (constants::DW_OP_lit17, Operation::UnsignedConstant { value: 17 }), - (constants::DW_OP_lit18, Operation::UnsignedConstant { value: 18 }), - (constants::DW_OP_lit19, Operation::UnsignedConstant { value: 19 }), - (constants::DW_OP_lit20, Operation::UnsignedConstant { value: 20 }), - (constants::DW_OP_lit21, Operation::UnsignedConstant { value: 21 }), - (constants::DW_OP_lit22, Operation::UnsignedConstant { value: 22 }), - (constants::DW_OP_lit23, Operation::UnsignedConstant { value: 23 }), - (constants::DW_OP_lit24, Operation::UnsignedConstant { value: 24 }), - (constants::DW_OP_lit25, Operation::UnsignedConstant { value: 25 }), - (constants::DW_OP_lit26, Operation::UnsignedConstant { value: 26 }), - (constants::DW_OP_lit27, Operation::UnsignedConstant { value: 27 }), - (constants::DW_OP_lit28, Operation::UnsignedConstant { value: 28 }), - (constants::DW_OP_lit29, Operation::UnsignedConstant { value: 29 }), - (constants::DW_OP_lit30, Operation::UnsignedConstant { value: 30 }), - (constants::DW_OP_lit31, Operation::UnsignedConstant { value: 31 }), - (constants::DW_OP_reg0, Operation::Register { register: Register(0) }), - (constants::DW_OP_reg1, Operation::Register { register: Register(1) }), - (constants::DW_OP_reg2, Operation::Register { register: Register(2) }), - (constants::DW_OP_reg3, Operation::Register { register: Register(3) }), - (constants::DW_OP_reg4, Operation::Register { register: Register(4) }), - (constants::DW_OP_reg5, Operation::Register { register: Register(5) }), - (constants::DW_OP_reg6, Operation::Register { register: Register(6) }), - (constants::DW_OP_reg7, Operation::Register { register: Register(7) }), - (constants::DW_OP_reg8, Operation::Register { register: Register(8) }), - (constants::DW_OP_reg9, Operation::Register { register: Register(9) }), - (constants::DW_OP_reg10, Operation::Register { register: Register(10) }), - (constants::DW_OP_reg11, Operation::Register { register: Register(11) }), - (constants::DW_OP_reg12, Operation::Register { register: Register(12) }), - (constants::DW_OP_reg13, Operation::Register { register: Register(13) }), - (constants::DW_OP_reg14, Operation::Register { register: Register(14) }), - (constants::DW_OP_reg15, Operation::Register { register: Register(15) }), - (constants::DW_OP_reg16, Operation::Register { register: Register(16) }), - (constants::DW_OP_reg17, Operation::Register { register: Register(17) }), - (constants::DW_OP_reg18, Operation::Register { register: Register(18) }), - (constants::DW_OP_reg19, Operation::Register { register: Register(19) }), - (constants::DW_OP_reg20, Operation::Register { register: Register(20) }), - (constants::DW_OP_reg21, Operation::Register { register: Register(21) }), - (constants::DW_OP_reg22, Operation::Register { register: Register(22) }), - (constants::DW_OP_reg23, Operation::Register { register: Register(23) }), - (constants::DW_OP_reg24, Operation::Register { register: Register(24) }), - (constants::DW_OP_reg25, Operation::Register { register: Register(25) }), - (constants::DW_OP_reg26, Operation::Register { register: Register(26) }), - (constants::DW_OP_reg27, Operation::Register { register: Register(27) }), - (constants::DW_OP_reg28, Operation::Register { register: Register(28) }), - (constants::DW_OP_reg29, Operation::Register { register: Register(29) }), - (constants::DW_OP_reg30, Operation::Register { register: Register(30) }), - (constants::DW_OP_reg31, Operation::Register { register: Register(31) }), - (constants::DW_OP_nop, Operation::Nop), - (constants::DW_OP_push_object_address, Operation::PushObjectAddress), - (constants::DW_OP_form_tls_address, Operation::TLS), - (constants::DW_OP_GNU_push_tls_address, Operation::TLS), - (constants::DW_OP_call_frame_cfa, Operation::CallFrameCFA), - (constants::DW_OP_stack_value, Operation::StackValue), - ]; - - let input = []; - check_op_parse_eof(&input[..], encoding); - - for item in inputs.iter() { - let (opcode, ref result) = *item; - check_op_parse(|s| s.D8(opcode.0), result, encoding); - } - } - - #[test] - fn test_op_parse_twobyte() { - // Doesn't matter for this test. - let encoding = encoding4(); - - let inputs = [ - ( - constants::DW_OP_const1u, - 23, - Operation::UnsignedConstant { value: 23 }, - ), - ( - constants::DW_OP_const1s, - (-23i8) as u8, - Operation::SignedConstant { value: -23 }, - ), - (constants::DW_OP_pick, 7, Operation::Pick { index: 7 }), - ( - constants::DW_OP_deref_size, - 19, - Operation::Deref { - base_type: generic_type(), - size: 19, - space: false, - }, - ), - ( - constants::DW_OP_xderef_size, - 19, - Operation::Deref { - base_type: generic_type(), - size: 19, - space: true, - }, - ), - ]; - - for item in inputs.iter() { - let (opcode, arg, ref result) = *item; - check_op_parse(|s| s.D8(opcode.0).D8(arg), result, encoding); - } - } - - #[test] - fn test_op_parse_threebyte() { - // Doesn't matter for this test. - let encoding = encoding4(); - - // While bra and skip are 3-byte opcodes, they aren't tested here, - // but rather specially in their own function. - let inputs = [ - ( - constants::DW_OP_const2u, - 23, - Operation::UnsignedConstant { value: 23 }, - ), - ( - constants::DW_OP_const2s, - (-23i16) as u16, - Operation::SignedConstant { value: -23 }, - ), - ( - constants::DW_OP_call2, - 1138, - Operation::Call { - offset: DieReference::UnitRef(UnitOffset(1138)), - }, - ), - ( - constants::DW_OP_bra, - (-23i16) as u16, - Operation::Bra { target: -23 }, - ), - ( - constants::DW_OP_skip, - (-23i16) as u16, - Operation::Skip { target: -23 }, - ), - ]; - - for item in inputs.iter() { - let (opcode, arg, ref result) = *item; - check_op_parse(|s| s.D8(opcode.0).L16(arg), result, encoding); - } - } - - #[test] - fn test_op_parse_fivebyte() { - // There are some tests here that depend on address size. - let encoding = encoding4(); - - let inputs = [ - ( - constants::DW_OP_addr, - 0x1234_5678, - Operation::Address { - address: 0x1234_5678, - }, - ), - ( - constants::DW_OP_const4u, - 0x1234_5678, - Operation::UnsignedConstant { value: 0x1234_5678 }, - ), - ( - constants::DW_OP_const4s, - (-23i32) as u32, - Operation::SignedConstant { value: -23 }, - ), - ( - constants::DW_OP_call4, - 0x1234_5678, - Operation::Call { - offset: DieReference::UnitRef(UnitOffset(0x1234_5678)), - }, - ), - ( - constants::DW_OP_call_ref, - 0x1234_5678, - Operation::Call { - offset: DieReference::DebugInfoRef(DebugInfoOffset(0x1234_5678)), - }, - ), - ]; - - for item in inputs.iter() { - let (op, arg, ref expect) = *item; - check_op_parse(|s| s.D8(op.0).L32(arg), expect, encoding); - } - } - - #[test] - #[cfg(target_pointer_width = "64")] - fn test_op_parse_ninebyte() { - // There are some tests here that depend on address size. - let encoding = encoding8(); - - let inputs = [ - ( - constants::DW_OP_addr, - 0x1234_5678_1234_5678, - Operation::Address { - address: 0x1234_5678_1234_5678, - }, - ), - ( - constants::DW_OP_const8u, - 0x1234_5678_1234_5678, - Operation::UnsignedConstant { - value: 0x1234_5678_1234_5678, - }, - ), - ( - constants::DW_OP_const8s, - (-23i64) as u64, - Operation::SignedConstant { value: -23 }, - ), - ( - constants::DW_OP_call_ref, - 0x1234_5678_1234_5678, - Operation::Call { - offset: DieReference::DebugInfoRef(DebugInfoOffset(0x1234_5678_1234_5678)), - }, - ), - ]; - - for item in inputs.iter() { - let (op, arg, ref expect) = *item; - check_op_parse(|s| s.D8(op.0).L64(arg), expect, encoding); - } - } - - #[test] - fn test_op_parse_sleb() { - // Doesn't matter for this test. - let encoding = encoding4(); - - let values = [ - -1i64, - 0, - 1, - 0x100, - 0x1eee_eeee, - 0x7fff_ffff_ffff_ffff, - -0x100, - -0x1eee_eeee, - -0x7fff_ffff_ffff_ffff, - ]; - for value in values.iter() { - let mut inputs = vec![ - ( - constants::DW_OP_consts.0, - Operation::SignedConstant { value: *value }, - ), - ( - constants::DW_OP_fbreg.0, - Operation::FrameOffset { offset: *value }, - ), - ]; - - for i in 0..32 { - inputs.push(( - constants::DW_OP_breg0.0 + i, - Operation::RegisterOffset { - register: Register(i.into()), - offset: *value, - base_type: UnitOffset(0), - }, - )); - } - - for item in inputs.iter() { - let (op, ref expect) = *item; - check_op_parse(|s| s.D8(op).sleb(*value), expect, encoding); - } - } - } - - #[test] - fn test_op_parse_uleb() { - // Doesn't matter for this test. - let encoding = encoding4(); - - let values = [ - 0, - 1, - 0x100, - (!0u16).into(), - 0x1eee_eeee, - 0x7fff_ffff_ffff_ffff, - !0u64, - ]; - for value in values.iter() { - let mut inputs = vec![ - ( - constants::DW_OP_constu, - Operation::UnsignedConstant { value: *value }, - ), - ( - constants::DW_OP_plus_uconst, - Operation::PlusConstant { value: *value }, - ), - ]; - - if *value <= (!0u16).into() { - inputs.push(( - constants::DW_OP_regx, - Operation::Register { - register: Register::from_u64(*value).unwrap(), - }, - )); - } - - if *value <= (!0u32).into() { - inputs.extend(&[ - ( - constants::DW_OP_addrx, - Operation::AddressIndex { - index: DebugAddrIndex(*value as usize), - }, - ), - ( - constants::DW_OP_constx, - Operation::ConstantIndex { - index: DebugAddrIndex(*value as usize), - }, - ), - ]); - } - - // FIXME - if *value < !0u64 / 8 { - inputs.push(( - constants::DW_OP_piece, - Operation::Piece { - size_in_bits: 8 * value, - bit_offset: None, - }, - )); - } - - for item in inputs.iter() { - let (op, ref expect) = *item; - let input = Section::with_endian(Endian::Little) - .D8(op.0) - .uleb(*value) - .get_contents() - .unwrap(); - check_op_parse_simple(&input, expect, encoding); - } - } - } - - #[test] - fn test_op_parse_bregx() { - // Doesn't matter for this test. - let encoding = encoding4(); - - let uvalues = [0, 1, 0x100, !0u16]; - let svalues = [ - -1i64, - 0, - 1, - 0x100, - 0x1eee_eeee, - 0x7fff_ffff_ffff_ffff, - -0x100, - -0x1eee_eeee, - -0x7fff_ffff_ffff_ffff, - ]; - - for v1 in uvalues.iter() { - for v2 in svalues.iter() { - check_op_parse( - |s| s.D8(constants::DW_OP_bregx.0).uleb((*v1).into()).sleb(*v2), - &Operation::RegisterOffset { - register: Register(*v1), - offset: *v2, - base_type: UnitOffset(0), - }, - encoding, - ); - } - } - } - - #[test] - fn test_op_parse_bit_piece() { - // Doesn't matter for this test. - let encoding = encoding4(); - - let values = [0, 1, 0x100, 0x1eee_eeee, 0x7fff_ffff_ffff_ffff, !0u64]; - - for v1 in values.iter() { - for v2 in values.iter() { - let input = Section::with_endian(Endian::Little) - .D8(constants::DW_OP_bit_piece.0) - .uleb(*v1) - .uleb(*v2) - .get_contents() - .unwrap(); - check_op_parse_simple( - &input, - &Operation::Piece { - size_in_bits: *v1, - bit_offset: Some(*v2), - }, - encoding, - ); - } - } - } - - #[test] - fn test_op_parse_implicit_value() { - // Doesn't matter for this test. - let encoding = encoding4(); - - let data = b"hello"; - - check_op_parse( - |s| { - s.D8(constants::DW_OP_implicit_value.0) - .uleb(data.len() as u64) - .append_bytes(&data[..]) - }, - &Operation::ImplicitValue { - data: EndianSlice::new(&data[..], LittleEndian), - }, - encoding, - ); - } - - #[test] - fn test_op_parse_const_type() { - // Doesn't matter for this test. - let encoding = encoding4(); - - let data = b"hello"; - - check_op_parse( - |s| { - s.D8(constants::DW_OP_const_type.0) - .uleb(100) - .D8(data.len() as u8) - .append_bytes(&data[..]) - }, - &Operation::TypedLiteral { - base_type: UnitOffset(100), - value: EndianSlice::new(&data[..], LittleEndian), - }, - encoding, - ); - check_op_parse( - |s| { - s.D8(constants::DW_OP_GNU_const_type.0) - .uleb(100) - .D8(data.len() as u8) - .append_bytes(&data[..]) - }, - &Operation::TypedLiteral { - base_type: UnitOffset(100), - value: EndianSlice::new(&data[..], LittleEndian), - }, - encoding, - ); - } - - #[test] - fn test_op_parse_regval_type() { - // Doesn't matter for this test. - let encoding = encoding4(); - - check_op_parse( - |s| s.D8(constants::DW_OP_regval_type.0).uleb(1).uleb(100), - &Operation::RegisterOffset { - register: Register(1), - offset: 0, - base_type: UnitOffset(100), - }, - encoding, - ); - check_op_parse( - |s| s.D8(constants::DW_OP_GNU_regval_type.0).uleb(1).uleb(100), - &Operation::RegisterOffset { - register: Register(1), - offset: 0, - base_type: UnitOffset(100), - }, - encoding, - ); - } - - #[test] - fn test_op_parse_deref_type() { - // Doesn't matter for this test. - let encoding = encoding4(); - - check_op_parse( - |s| s.D8(constants::DW_OP_deref_type.0).D8(8).uleb(100), - &Operation::Deref { - base_type: UnitOffset(100), - size: 8, - space: false, - }, - encoding, - ); - check_op_parse( - |s| s.D8(constants::DW_OP_GNU_deref_type.0).D8(8).uleb(100), - &Operation::Deref { - base_type: UnitOffset(100), - size: 8, - space: false, - }, - encoding, - ); - check_op_parse( - |s| s.D8(constants::DW_OP_xderef_type.0).D8(8).uleb(100), - &Operation::Deref { - base_type: UnitOffset(100), - size: 8, - space: true, - }, - encoding, - ); - } - - #[test] - fn test_op_convert() { - // Doesn't matter for this test. - let encoding = encoding4(); - - check_op_parse( - |s| s.D8(constants::DW_OP_convert.0).uleb(100), - &Operation::Convert { - base_type: UnitOffset(100), - }, - encoding, - ); - check_op_parse( - |s| s.D8(constants::DW_OP_GNU_convert.0).uleb(100), - &Operation::Convert { - base_type: UnitOffset(100), - }, - encoding, - ); - } - - #[test] - fn test_op_reinterpret() { - // Doesn't matter for this test. - let encoding = encoding4(); - - check_op_parse( - |s| s.D8(constants::DW_OP_reinterpret.0).uleb(100), - &Operation::Reinterpret { - base_type: UnitOffset(100), - }, - encoding, - ); - check_op_parse( - |s| s.D8(constants::DW_OP_GNU_reinterpret.0).uleb(100), - &Operation::Reinterpret { - base_type: UnitOffset(100), - }, - encoding, - ); - } - - #[test] - fn test_op_parse_implicit_pointer() { - for op in &[ - constants::DW_OP_implicit_pointer, - constants::DW_OP_GNU_implicit_pointer, - ] { - check_op_parse( - |s| s.D8(op.0).D32(0x1234_5678).sleb(0x123), - &Operation::ImplicitPointer { - value: DebugInfoOffset(0x1234_5678), - byte_offset: 0x123, - }, - encoding4(), - ); - - check_op_parse( - |s| s.D8(op.0).D64(0x1234_5678).sleb(0x123), - &Operation::ImplicitPointer { - value: DebugInfoOffset(0x1234_5678), - byte_offset: 0x123, - }, - encoding8(), - ); - - check_op_parse( - |s| s.D8(op.0).D64(0x1234_5678).sleb(0x123), - &Operation::ImplicitPointer { - value: DebugInfoOffset(0x1234_5678), - byte_offset: 0x123, - }, - Encoding { - format: Format::Dwarf32, - version: 2, - address_size: 8, - }, - ) - } - } - - #[test] - fn test_op_parse_entry_value() { - for op in &[ - constants::DW_OP_entry_value, - constants::DW_OP_GNU_entry_value, - ] { - let data = b"hello"; - check_op_parse( - |s| s.D8(op.0).uleb(data.len() as u64).append_bytes(&data[..]), - &Operation::EntryValue { - expression: EndianSlice::new(&data[..], LittleEndian), - }, - encoding4(), - ); - } - } - - #[test] - fn test_op_parse_gnu_parameter_ref() { - check_op_parse( - |s| s.D8(constants::DW_OP_GNU_parameter_ref.0).D32(0x1234_5678), - &Operation::ParameterRef { - offset: UnitOffset(0x1234_5678), - }, - encoding4(), - ) - } - - #[test] - fn test_op_wasm() { - // Doesn't matter for this test. - let encoding = encoding4(); - - check_op_parse( - |s| s.D8(constants::DW_OP_WASM_location.0).D8(0).uleb(1000), - &Operation::WasmLocal { index: 1000 }, - encoding, - ); - check_op_parse( - |s| s.D8(constants::DW_OP_WASM_location.0).D8(1).uleb(1000), - &Operation::WasmGlobal { index: 1000 }, - encoding, - ); - check_op_parse( - |s| s.D8(constants::DW_OP_WASM_location.0).D8(2).uleb(1000), - &Operation::WasmStack { index: 1000 }, - encoding, - ); - check_op_parse( - |s| s.D8(constants::DW_OP_WASM_location.0).D8(3).D32(1000), - &Operation::WasmGlobal { index: 1000 }, - encoding, - ); - } - - enum AssemblerEntry { - Op(constants::DwOp), - Mark(u8), - Branch(u8), - U8(u8), - U16(u16), - U32(u32), - U64(u64), - Uleb(u64), - Sleb(u64), - } - - fn assemble(entries: &[AssemblerEntry]) -> Vec<u8> { - let mut result = Vec::new(); - - struct Marker(Option<usize>, Vec<usize>); - - let mut markers = Vec::new(); - for _ in 0..256 { - markers.push(Marker(None, Vec::new())); - } - - fn write(stack: &mut Vec<u8>, index: usize, mut num: u64, nbytes: u8) { - for i in 0..nbytes as usize { - stack[index + i] = (num & 0xff) as u8; - num >>= 8; - } - } - - fn push(stack: &mut Vec<u8>, num: u64, nbytes: u8) { - let index = stack.len(); - for _ in 0..nbytes { - stack.push(0); - } - write(stack, index, num, nbytes); - } - - for item in entries { - match *item { - AssemblerEntry::Op(op) => result.push(op.0), - AssemblerEntry::Mark(num) => { - assert!(markers[num as usize].0.is_none()); - markers[num as usize].0 = Some(result.len()); - } - AssemblerEntry::Branch(num) => { - markers[num as usize].1.push(result.len()); - push(&mut result, 0, 2); - } - AssemblerEntry::U8(num) => result.push(num), - AssemblerEntry::U16(num) => push(&mut result, u64::from(num), 2), - AssemblerEntry::U32(num) => push(&mut result, u64::from(num), 4), - AssemblerEntry::U64(num) => push(&mut result, num, 8), - AssemblerEntry::Uleb(num) => { - leb128::write::unsigned(&mut result, num).unwrap(); - } - AssemblerEntry::Sleb(num) => { - leb128::write::signed(&mut result, num as i64).unwrap(); - } - } - } - - // Update all the branches. - for marker in markers { - if let Some(offset) = marker.0 { - for branch_offset in marker.1 { - let delta = offset.wrapping_sub(branch_offset + 2) as u64; - write(&mut result, branch_offset, delta, 2); - } - } - } - - result - } - - fn check_eval_with_args<F>( - program: &[AssemblerEntry], - expect: Result<&[Piece<EndianSlice<LittleEndian>>]>, - encoding: Encoding, - object_address: Option<u64>, - initial_value: Option<u64>, - max_iterations: Option<u32>, - f: F, - ) where - for<'a> F: Fn( - &mut Evaluation<EndianSlice<'a, LittleEndian>>, - EvaluationResult<EndianSlice<'a, LittleEndian>>, - ) -> Result<EvaluationResult<EndianSlice<'a, LittleEndian>>>, - { - let bytes = assemble(program); - let bytes = EndianSlice::new(&bytes, LittleEndian); - - let mut eval = Evaluation::new(bytes, encoding); - - if let Some(val) = object_address { - eval.set_object_address(val); - } - if let Some(val) = initial_value { - eval.set_initial_value(val); - } - if let Some(val) = max_iterations { - eval.set_max_iterations(val); - } - - let result = match eval.evaluate() { - Err(e) => Err(e), - Ok(r) => f(&mut eval, r), - }; - - match (result, expect) { - (Ok(EvaluationResult::Complete), Ok(pieces)) => { - let vec = eval.result(); - assert_eq!(vec.len(), pieces.len()); - for i in 0..pieces.len() { - assert_eq!(vec[i], pieces[i]); - } - } - (Err(f1), Err(f2)) => { - assert_eq!(f1, f2); - } - otherwise => panic!("Unexpected result: {:?}", otherwise), - } - } - - fn check_eval( - program: &[AssemblerEntry], - expect: Result<&[Piece<EndianSlice<LittleEndian>>]>, - encoding: Encoding, - ) { - check_eval_with_args(program, expect, encoding, None, None, None, |_, result| { - Ok(result) - }); - } - - #[test] - fn test_eval_arith() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - // Indices of marks in the assembly. - let done = 0; - let fail = 1; - - #[rustfmt::skip] - let program = [ - Op(DW_OP_const1u), U8(23), - Op(DW_OP_const1s), U8((-23i8) as u8), - Op(DW_OP_plus), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const2u), U16(23), - Op(DW_OP_const2s), U16((-23i16) as u16), - Op(DW_OP_plus), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const4u), U32(0x1111_2222), - Op(DW_OP_const4s), U32((-0x1111_2222i32) as u32), - Op(DW_OP_plus), - Op(DW_OP_bra), Branch(fail), - - // Plus should overflow. - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_const1u), U8(1), - Op(DW_OP_plus), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_plus_uconst), Uleb(1), - Op(DW_OP_bra), Branch(fail), - - // Minus should underflow. - Op(DW_OP_const1s), U8(0), - Op(DW_OP_const1u), U8(1), - Op(DW_OP_minus), - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_abs), - Op(DW_OP_const1u), U8(1), - Op(DW_OP_minus), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const4u), U32(0xf078_fffe), - Op(DW_OP_const4u), U32(0x0f87_0001), - Op(DW_OP_and), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const4u), U32(0xf078_fffe), - Op(DW_OP_const4u), U32(0xf000_00fe), - Op(DW_OP_and), - Op(DW_OP_const4u), U32(0xf000_00fe), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - // Division is signed. - Op(DW_OP_const1s), U8(0xfe), - Op(DW_OP_const1s), U8(2), - Op(DW_OP_div), - Op(DW_OP_plus_uconst), Uleb(1), - Op(DW_OP_bra), Branch(fail), - - // Mod is unsigned. - Op(DW_OP_const1s), U8(0xfd), - Op(DW_OP_const1s), U8(2), - Op(DW_OP_mod), - Op(DW_OP_neg), - Op(DW_OP_plus_uconst), Uleb(1), - Op(DW_OP_bra), Branch(fail), - - // Overflow is defined for multiplication. - Op(DW_OP_const4u), U32(0x8000_0001), - Op(DW_OP_lit2), - Op(DW_OP_mul), - Op(DW_OP_lit2), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const4u), U32(0xf0f0_f0f0), - Op(DW_OP_const4u), U32(0xf0f0_f0f0), - Op(DW_OP_xor), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const4u), U32(0xf0f0_f0f0), - Op(DW_OP_const4u), U32(0x0f0f_0f0f), - Op(DW_OP_or), - Op(DW_OP_not), - Op(DW_OP_bra), Branch(fail), - - // In 32 bit mode, values are truncated. - Op(DW_OP_const8u), U64(0xffff_ffff_0000_0000), - Op(DW_OP_lit2), - Op(DW_OP_div), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1u), U8(0xff), - Op(DW_OP_lit1), - Op(DW_OP_shl), - Op(DW_OP_const2u), U16(0x1fe), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1u), U8(0xff), - Op(DW_OP_const1u), U8(50), - Op(DW_OP_shl), - Op(DW_OP_bra), Branch(fail), - - // Absurd shift. - Op(DW_OP_const1u), U8(0xff), - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_shl), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_lit1), - Op(DW_OP_shr), - Op(DW_OP_const4u), U32(0x7fff_ffff), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_const1u), U8(0xff), - Op(DW_OP_shr), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_lit1), - Op(DW_OP_shra), - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_const1u), U8(0xff), - Op(DW_OP_shra), - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - // Success. - Op(DW_OP_lit0), - Op(DW_OP_nop), - Op(DW_OP_skip), Branch(done), - - Mark(fail), - Op(DW_OP_lit1), - - Mark(done), - Op(DW_OP_stack_value), - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(0), - }, - }]; - - check_eval(&program, Ok(&result), encoding4()); - } - - #[test] - fn test_eval_arith64() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - // Indices of marks in the assembly. - let done = 0; - let fail = 1; - - #[rustfmt::skip] - let program = [ - Op(DW_OP_const8u), U64(0x1111_2222_3333_4444), - Op(DW_OP_const8s), U64((-0x1111_2222_3333_4444i64) as u64), - Op(DW_OP_plus), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_constu), Uleb(0x1111_2222_3333_4444), - Op(DW_OP_consts), Sleb((-0x1111_2222_3333_4444i64) as u64), - Op(DW_OP_plus), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_lit1), - Op(DW_OP_plus_uconst), Uleb(!0u64), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_lit1), - Op(DW_OP_neg), - Op(DW_OP_not), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), - Op(DW_OP_const1u), U8(63), - Op(DW_OP_shr), - Op(DW_OP_lit1), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), - Op(DW_OP_const1u), U8(62), - Op(DW_OP_shra), - Op(DW_OP_plus_uconst), Uleb(2), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_lit1), - Op(DW_OP_const1u), U8(63), - Op(DW_OP_shl), - Op(DW_OP_const8u), U64(0x8000_0000_0000_0000), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - // Success. - Op(DW_OP_lit0), - Op(DW_OP_nop), - Op(DW_OP_skip), Branch(done), - - Mark(fail), - Op(DW_OP_lit1), - - Mark(done), - Op(DW_OP_stack_value), - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(0), - }, - }]; - - check_eval(&program, Ok(&result), encoding8()); - } - - #[test] - fn test_eval_compare() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - // Indices of marks in the assembly. - let done = 0; - let fail = 1; - - #[rustfmt::skip] - let program = [ - // Comparisons are signed. - Op(DW_OP_const1s), U8(1), - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_lt), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_const1s), U8(1), - Op(DW_OP_gt), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(1), - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_le), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_const1s), U8(1), - Op(DW_OP_ge), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const1s), U8(0xff), - Op(DW_OP_const1s), U8(1), - Op(DW_OP_eq), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_const4s), U32(1), - Op(DW_OP_const1s), U8(1), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - // Success. - Op(DW_OP_lit0), - Op(DW_OP_nop), - Op(DW_OP_skip), Branch(done), - - Mark(fail), - Op(DW_OP_lit1), - - Mark(done), - Op(DW_OP_stack_value), - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(0), - }, - }]; - - check_eval(&program, Ok(&result), encoding4()); - } - - #[test] - fn test_eval_stack() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - #[rustfmt::skip] - let program = [ - Op(DW_OP_lit17), // -- 17 - Op(DW_OP_dup), // -- 17 17 - Op(DW_OP_over), // -- 17 17 17 - Op(DW_OP_minus), // -- 17 0 - Op(DW_OP_swap), // -- 0 17 - Op(DW_OP_dup), // -- 0 17 17 - Op(DW_OP_plus_uconst), Uleb(1), // -- 0 17 18 - Op(DW_OP_rot), // -- 18 0 17 - Op(DW_OP_pick), U8(2), // -- 18 0 17 18 - Op(DW_OP_pick), U8(3), // -- 18 0 17 18 18 - Op(DW_OP_minus), // -- 18 0 17 0 - Op(DW_OP_drop), // -- 18 0 17 - Op(DW_OP_swap), // -- 18 17 0 - Op(DW_OP_drop), // -- 18 17 - Op(DW_OP_minus), // -- 1 - Op(DW_OP_stack_value), - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(1), - }, - }]; - - check_eval(&program, Ok(&result), encoding4()); - } - - #[test] - fn test_eval_lit_and_reg() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - let mut program = Vec::new(); - program.push(Op(DW_OP_lit0)); - for i in 0..32 { - program.push(Op(DwOp(DW_OP_lit0.0 + i))); - program.push(Op(DwOp(DW_OP_breg0.0 + i))); - program.push(Sleb(u64::from(i))); - program.push(Op(DW_OP_plus)); - program.push(Op(DW_OP_plus)); - } - - program.push(Op(DW_OP_bregx)); - program.push(Uleb(0x1234)); - program.push(Sleb(0x1234)); - program.push(Op(DW_OP_plus)); - - program.push(Op(DW_OP_stack_value)); - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(496), - }, - }]; - - check_eval_with_args( - &program, - Ok(&result), - encoding4(), - None, - None, - None, - |eval, mut result| { - while result != EvaluationResult::Complete { - result = eval.resume_with_register(match result { - EvaluationResult::RequiresRegister { - register, - base_type, - } => { - assert_eq!(base_type, UnitOffset(0)); - Value::Generic(u64::from(register.0).wrapping_neg()) - } - _ => panic!(), - })?; - } - Ok(result) - }, - ); - } - - #[test] - fn test_eval_memory() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - // Indices of marks in the assembly. - let done = 0; - let fail = 1; - - #[rustfmt::skip] - let program = [ - Op(DW_OP_addr), U32(0x7fff_ffff), - Op(DW_OP_deref), - Op(DW_OP_const4u), U32(0xffff_fffc), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_addr), U32(0x7fff_ffff), - Op(DW_OP_deref_size), U8(2), - Op(DW_OP_const4u), U32(0xfffc), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_lit1), - Op(DW_OP_addr), U32(0x7fff_ffff), - Op(DW_OP_xderef), - Op(DW_OP_const4u), U32(0xffff_fffd), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_lit1), - Op(DW_OP_addr), U32(0x7fff_ffff), - Op(DW_OP_xderef_size), U8(2), - Op(DW_OP_const4u), U32(0xfffd), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_lit17), - Op(DW_OP_form_tls_address), - Op(DW_OP_constu), Uleb(!17), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_lit17), - Op(DW_OP_GNU_push_tls_address), - Op(DW_OP_constu), Uleb(!17), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_addrx), Uleb(0x10), - Op(DW_OP_deref), - Op(DW_OP_const4u), U32(0x4040), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - Op(DW_OP_constx), Uleb(17), - Op(DW_OP_form_tls_address), - Op(DW_OP_constu), Uleb(!27), - Op(DW_OP_ne), - Op(DW_OP_bra), Branch(fail), - - // Success. - Op(DW_OP_lit0), - Op(DW_OP_nop), - Op(DW_OP_skip), Branch(done), - - Mark(fail), - Op(DW_OP_lit1), - - Mark(done), - Op(DW_OP_stack_value), - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(0), - }, - }]; - - check_eval_with_args( - &program, - Ok(&result), - encoding4(), - None, - None, - None, - |eval, mut result| { - while result != EvaluationResult::Complete { - result = match result { - EvaluationResult::RequiresMemory { - address, - size, - space, - base_type, - } => { - assert_eq!(base_type, UnitOffset(0)); - let mut v = address << 2; - if let Some(value) = space { - v += value; - } - v &= (1u64 << (8 * size)) - 1; - eval.resume_with_memory(Value::Generic(v))? - } - EvaluationResult::RequiresTls(slot) => eval.resume_with_tls(!slot)?, - EvaluationResult::RequiresRelocatedAddress(address) => { - eval.resume_with_relocated_address(address)? - } - EvaluationResult::RequiresIndexedAddress { index, relocate } => { - if relocate { - eval.resume_with_indexed_address(0x1000 + index.0 as u64)? - } else { - eval.resume_with_indexed_address(10 + index.0 as u64)? - } - } - _ => panic!(), - }; - } - - Ok(result) - }, - ); - } - - #[test] - fn test_eval_register() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - for i in 0..32 { - #[rustfmt::skip] - let program = [ - Op(DwOp(DW_OP_reg0.0 + i)), - // Included only in the "bad" run. - Op(DW_OP_lit23), - ]; - let ok_result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Register { - register: Register(i.into()), - }, - }]; - - check_eval(&program[..1], Ok(&ok_result), encoding4()); - - check_eval( - &program, - Err(Error::InvalidExpressionTerminator(1)), - encoding4(), - ); - } - - #[rustfmt::skip] - let program = [ - Op(DW_OP_regx), Uleb(0x1234) - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Register { - register: Register(0x1234), - }, - }]; - - check_eval(&program, Ok(&result), encoding4()); - } - - #[test] - fn test_eval_context() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - // Test `frame_base` and `call_frame_cfa` callbacks. - #[rustfmt::skip] - let program = [ - Op(DW_OP_fbreg), Sleb((-8i8) as u64), - Op(DW_OP_call_frame_cfa), - Op(DW_OP_plus), - Op(DW_OP_neg), - Op(DW_OP_stack_value) - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(9), - }, - }]; - - check_eval_with_args( - &program, - Ok(&result), - encoding8(), - None, - None, - None, - |eval, result| { - match result { - EvaluationResult::RequiresFrameBase => {} - _ => panic!(), - }; - match eval.resume_with_frame_base(0x0123_4567_89ab_cdef)? { - EvaluationResult::RequiresCallFrameCfa => {} - _ => panic!(), - }; - eval.resume_with_call_frame_cfa(0xfedc_ba98_7654_3210) - }, - ); - - // Test `evaluate_entry_value` callback. - #[rustfmt::skip] - let program = [ - Op(DW_OP_entry_value), Uleb(8), U64(0x1234_5678), - Op(DW_OP_stack_value) - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(0x1234_5678), - }, - }]; - - check_eval_with_args( - &program, - Ok(&result), - encoding8(), - None, - None, - None, - |eval, result| { - let entry_value = match result { - EvaluationResult::RequiresEntryValue(mut expression) => { - expression.0.read_u64()? - } - _ => panic!(), - }; - eval.resume_with_entry_value(Value::Generic(entry_value)) - }, - ); - - // Test missing `object_address` field. - #[rustfmt::skip] - let program = [ - Op(DW_OP_push_object_address), - ]; - - check_eval_with_args( - &program, - Err(Error::InvalidPushObjectAddress), - encoding4(), - None, - None, - None, - |_, _| panic!(), - ); - - // Test `object_address` field. - #[rustfmt::skip] - let program = [ - Op(DW_OP_push_object_address), - Op(DW_OP_stack_value), - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(0xff), - }, - }]; - - check_eval_with_args( - &program, - Ok(&result), - encoding8(), - Some(0xff), - None, - None, - |_, result| Ok(result), - ); - - // Test `initial_value` field. - #[rustfmt::skip] - let program = [ - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Address { - address: 0x1234_5678, - }, - }]; - - check_eval_with_args( - &program, - Ok(&result), - encoding8(), - None, - Some(0x1234_5678), - None, - |_, result| Ok(result), - ); - } - - #[test] - fn test_eval_empty_stack() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - #[rustfmt::skip] - let program = [ - Op(DW_OP_stack_value) - ]; - - check_eval(&program, Err(Error::NotEnoughStackItems), encoding4()); - } - - #[test] - fn test_eval_call() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - #[rustfmt::skip] - let program = [ - Op(DW_OP_lit23), - Op(DW_OP_call2), U16(0x7755), - Op(DW_OP_call4), U32(0x7755_aaee), - Op(DW_OP_call_ref), U32(0x7755_aaee), - Op(DW_OP_stack_value) - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(23), - }, - }]; - - check_eval_with_args( - &program, - Ok(&result), - encoding4(), - None, - None, - None, - |eval, result| { - let buf = EndianSlice::new(&[], LittleEndian); - match result { - EvaluationResult::RequiresAtLocation(_) => {} - _ => panic!(), - }; - - eval.resume_with_at_location(buf)?; - - match result { - EvaluationResult::RequiresAtLocation(_) => {} - _ => panic!(), - }; - - eval.resume_with_at_location(buf)?; - - match result { - EvaluationResult::RequiresAtLocation(_) => {} - _ => panic!(), - }; - - eval.resume_with_at_location(buf) - }, - ); - - // DW_OP_lit2 DW_OP_mul - const SUBR: &[u8] = &[0x32, 0x1e]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { - value: Value::Generic(184), - }, - }]; - - check_eval_with_args( - &program, - Ok(&result), - encoding4(), - None, - None, - None, - |eval, result| { - let buf = EndianSlice::new(SUBR, LittleEndian); - match result { - EvaluationResult::RequiresAtLocation(_) => {} - _ => panic!(), - }; - - eval.resume_with_at_location(buf)?; - - match result { - EvaluationResult::RequiresAtLocation(_) => {} - _ => panic!(), - }; - - eval.resume_with_at_location(buf)?; - - match result { - EvaluationResult::RequiresAtLocation(_) => {} - _ => panic!(), - }; - - eval.resume_with_at_location(buf) - }, - ); - } - - #[test] - fn test_eval_pieces() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - // Example from DWARF 2.6.1.3. - #[rustfmt::skip] - let program = [ - Op(DW_OP_reg3), - Op(DW_OP_piece), Uleb(4), - Op(DW_OP_reg4), - Op(DW_OP_piece), Uleb(2), - ]; - - let result = [ - Piece { - size_in_bits: Some(32), - bit_offset: None, - location: Location::Register { - register: Register(3), - }, - }, - Piece { - size_in_bits: Some(16), - bit_offset: None, - location: Location::Register { - register: Register(4), - }, - }, - ]; - - check_eval(&program, Ok(&result), encoding4()); - - // Example from DWARF 2.6.1.3 (but hacked since dealing with fbreg - // in the tests is a pain). - #[rustfmt::skip] - let program = [ - Op(DW_OP_reg0), - Op(DW_OP_piece), Uleb(4), - Op(DW_OP_piece), Uleb(4), - Op(DW_OP_addr), U32(0x7fff_ffff), - Op(DW_OP_piece), Uleb(4), - ]; - - let result = [ - Piece { - size_in_bits: Some(32), - bit_offset: None, - location: Location::Register { - register: Register(0), - }, - }, - Piece { - size_in_bits: Some(32), - bit_offset: None, - location: Location::Empty, - }, - Piece { - size_in_bits: Some(32), - bit_offset: None, - location: Location::Address { - address: 0x7fff_ffff, - }, - }, - ]; - - check_eval_with_args( - &program, - Ok(&result), - encoding4(), - None, - None, - None, - |eval, mut result| { - while result != EvaluationResult::Complete { - result = match result { - EvaluationResult::RequiresRelocatedAddress(address) => { - eval.resume_with_relocated_address(address)? - } - _ => panic!(), - }; - } - - Ok(result) - }, - ); - - #[rustfmt::skip] - let program = [ - Op(DW_OP_implicit_value), Uleb(5), - U8(23), U8(24), U8(25), U8(26), U8(0), - ]; - - const BYTES: &[u8] = &[23, 24, 25, 26, 0]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Bytes { - value: EndianSlice::new(BYTES, LittleEndian), - }, - }]; - - check_eval(&program, Ok(&result), encoding4()); - - #[rustfmt::skip] - let program = [ - Op(DW_OP_lit7), - Op(DW_OP_stack_value), - Op(DW_OP_bit_piece), Uleb(5), Uleb(0), - Op(DW_OP_bit_piece), Uleb(3), Uleb(0), - ]; - - let result = [ - Piece { - size_in_bits: Some(5), - bit_offset: Some(0), - location: Location::Value { - value: Value::Generic(7), - }, - }, - Piece { - size_in_bits: Some(3), - bit_offset: Some(0), - location: Location::Empty, - }, - ]; - - check_eval(&program, Ok(&result), encoding4()); - - #[rustfmt::skip] - let program = [ - Op(DW_OP_lit7), - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Address { address: 7 }, - }]; - - check_eval(&program, Ok(&result), encoding4()); - - #[rustfmt::skip] - let program = [ - Op(DW_OP_implicit_pointer), U32(0x1234_5678), Sleb(0x123), - ]; - - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::ImplicitPointer { - value: DebugInfoOffset(0x1234_5678), - byte_offset: 0x123, - }, - }]; - - check_eval(&program, Ok(&result), encoding4()); - - #[rustfmt::skip] - let program = [ - Op(DW_OP_reg3), - Op(DW_OP_piece), Uleb(4), - Op(DW_OP_reg4), - ]; - - check_eval(&program, Err(Error::InvalidPiece), encoding4()); - - #[rustfmt::skip] - let program = [ - Op(DW_OP_reg3), - Op(DW_OP_piece), Uleb(4), - Op(DW_OP_lit0), - ]; - - check_eval(&program, Err(Error::InvalidPiece), encoding4()); - } - - #[test] - fn test_eval_max_iterations() { - // It's nice if an operation and its arguments can fit on a single - // line in the test program. - use self::AssemblerEntry::*; - use crate::constants::*; - - #[rustfmt::skip] - let program = [ - Mark(1), - Op(DW_OP_skip), Branch(1), - ]; - - check_eval_with_args( - &program, - Err(Error::TooManyIterations), - encoding4(), - None, - None, - Some(150), - |_, _| panic!(), - ); - } - - #[test] - fn test_eval_typed_stack() { - use self::AssemblerEntry::*; - use crate::constants::*; - - let base_types = [ - ValueType::Generic, - ValueType::U16, - ValueType::U32, - ValueType::F32, - ]; - - // TODO: convert, reinterpret - #[rustfmt::skip] - let tests = [ - ( - &[ - Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234), - Op(DW_OP_stack_value), - ][..], - Value::U16(0x1234), - ), - ( - &[ - Op(DW_OP_regval_type), Uleb(0x1234), Uleb(1), - Op(DW_OP_stack_value), - ][..], - Value::U16(0x2340), - ), - ( - &[ - Op(DW_OP_addr), U32(0x7fff_ffff), - Op(DW_OP_deref_type), U8(2), Uleb(1), - Op(DW_OP_stack_value), - ][..], - Value::U16(0xfff0), - ), - ( - &[ - Op(DW_OP_lit1), - Op(DW_OP_addr), U32(0x7fff_ffff), - Op(DW_OP_xderef_type), U8(2), Uleb(1), - Op(DW_OP_stack_value), - ][..], - Value::U16(0xfff1), - ), - ( - &[ - Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234), - Op(DW_OP_convert), Uleb(2), - Op(DW_OP_stack_value), - ][..], - Value::U32(0x1234), - ), - ( - &[ - Op(DW_OP_const_type), Uleb(2), U8(4), U32(0x3f80_0000), - Op(DW_OP_reinterpret), Uleb(3), - Op(DW_OP_stack_value), - ][..], - Value::F32(1.0), - ), - ]; - for &(program, value) in &tests { - let result = [Piece { - size_in_bits: None, - bit_offset: None, - location: Location::Value { value }, - }]; - - check_eval_with_args( - program, - Ok(&result), - encoding4(), - None, - None, - None, - |eval, mut result| { - while result != EvaluationResult::Complete { - result = match result { - EvaluationResult::RequiresMemory { - address, - size, - space, - base_type, - } => { - let mut v = address << 4; - if let Some(value) = space { - v += value; - } - v &= (1u64 << (8 * size)) - 1; - let v = Value::from_u64(base_types[base_type.0], v)?; - eval.resume_with_memory(v)? - } - EvaluationResult::RequiresRegister { - register, - base_type, - } => { - let v = Value::from_u64( - base_types[base_type.0], - u64::from(register.0) << 4, - )?; - eval.resume_with_register(v)? - } - EvaluationResult::RequiresBaseType(offset) => { - eval.resume_with_base_type(base_types[offset.0])? - } - EvaluationResult::RequiresRelocatedAddress(address) => { - eval.resume_with_relocated_address(address)? - } - _ => panic!("Unexpected result {:?}", result), - } - } - Ok(result) - }, - ); - } - } -} |