summaryrefslogtreecommitdiffstats
path: root/vendor/gimli-0.26.2/src/read/op.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gimli-0.26.2/src/read/op.rs')
-rw-r--r--vendor/gimli-0.26.2/src/read/op.rs4114
1 files changed, 4114 insertions, 0 deletions
diff --git a/vendor/gimli-0.26.2/src/read/op.rs b/vendor/gimli-0.26.2/src/read/op.rs
new file mode 100644
index 000000000..88ea20297
--- /dev/null
+++ b/vendor/gimli-0.26.2/src/read/op.rs
@@ -0,0 +1,4114 @@
+//! 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)
+ },
+ );
+ }
+ }
+}