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