Quelle op.rs
Sprache: unbekannt
|
|
//! 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))?;
}
--> --------------------
--> maximum size reached
--> --------------------
[ Dauer der Verarbeitung: 0.47 Sekunden
(vorverarbeitet)
]
|
2026-04-04
|