| //! 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 { |
| match *self { |
| Location::Empty => true, |
| _ => false, |
| } |
| } |
| } |
| |
| /// 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) |
| } |
| } |
| |
| /// 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::{EndianSlice, Evaluation, EvaluationResult, Format, LittleEndian, Value}; |
| /// # let bytecode = EndianSlice::new(&[], LittleEndian); |
| /// # let encoding = unimplemented!(); |
| /// # let get_register_value = |_, _| 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>, |
| |
| 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, |
| 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) |
| } |
| |
| #[allow(clippy::cyclomatic_complexity)] |
| 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 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::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()?; |
| 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 |
| } |
| |
| #[allow(clippy::too_many_arguments)] |
| 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) |
| }, |
| ); |
| } |
| } |
| } |