/// A reference to a DIE, either relative to the current CU or /// relative to the section. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pubenum 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)] pubenum 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,
},
}
/// A single location of a piece of the result of a DWARF expression. #[derive(Debug, Clone, Copy, PartialEq)] pubenum 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. pubfn 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)] pubstruct 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 { letmut new_pc = bytecode.clone();
new_pc.skip(new_pc_offset)?;
Ok(new_pc)
}
}
/// 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)] pubenum 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)] pubstruct 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] pubfn evaluation(self, encoding: Encoding) -> Evaluation<R> {
Evaluation::new(self.0, encoding)
}
/// Return an iterator for the operations in the expression. pubfn operations(self, encoding: Encoding) -> OperationIter<R> {
OperationIter {
input: self.0,
encoding,
}
}
}
/// An iterator for the operations in an expression. #[derive(Debug, Clone, Copy)] pubstruct OperationIter<R: Reader> {
input: R,
encoding: Encoding,
}
impl<R: Reader> OperationIter<R> { /// Read the next operation in an expression. pubfn next(&mutself) -> Result<Option<Operation<R>>> { ifself.input.is_empty() { return Ok(None);
} match Operation::parse(&mutself.input, self.encoding) {
Ok(op) => Ok(Some(op)),
Err(e) => { self.input.empty();
Err(e)
}
}
}
/// Return the current byte offset of the iterator. pubfn 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;
/// 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); /// ``` pubtrait 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)] pubstruct 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>,
#[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. pubfn 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. pubfn result(self) -> Vec<Piece<R>> { matchself.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. pubfn 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. pubfn set_initial_value(&mutself, value: u64) { matchself.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. pubfn set_object_address(&mutself, 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. pubfn set_max_iterations(&mutself, value: u32) { self.max_iterations = Some(value);
}
fn evaluate_one_operation(&mutself) -> Result<OperationEvaluationResult<R>> { let operation = Operation::parse(&mutself.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::StackValue => { let value = self.pop()?; let location = Location::Value { value }; return Ok(OperationEvaluationResult::Complete { location });
}
/// 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. pubfn value_result(&self) -> Option<Value> { matchself.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. pubfn as_result(&self) -> &[Piece<R>] { matchself.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`. pubfn evaluate(&mutself) -> Result<EvaluationResult<R>> { matchself.state {
EvaluationState::Start(initial_value) => { iflet 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!(),
};
/// 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`. pubfn resume_with_memory(&mutself, value: Value) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_register(&mutself, value: Value) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_frame_base(&mutself, frame_base: u64) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_tls(&mutself, value: u64) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_call_frame_cfa(&mutself, cfa: u64) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_at_location(&mutself, mut bytes: R) -> Result<EvaluationResult<R>> { matchself.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::AtLocation) => { if !bytes.is_empty() { letmut pc = bytes.clone();
mem::swap(&mut pc, &mutself.pc);
mem::swap(&mut bytes, &mutself.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`. pubfn resume_with_entry_value(&mutself, entry_value: Value) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_parameter_ref(
&mutself,
parameter_value: u64,
) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_relocated_address(&mutself, address: u64) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_indexed_address(&mutself, address: u64) -> Result<EvaluationResult<R>> { matchself.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`. pubfn resume_with_base_type(&mutself, base_type: ValueType) -> Result<EvaluationResult<R>> { let value = matchself.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()
}
let op_result = self.evaluate_one_operation()?; match op_result {
OperationEvaluationResult::Piece => {}
OperationEvaluationResult::Incomplete => { ifself.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 } => { ifself.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(&mutself.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. ifself.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)?;
}
#[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);
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();
fn write(stack: &mut [u8], index: usize, mut num: u64, nbytes: u8) { for i in0..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 _ in0..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 { iflet 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);
}
}
}
#[test] fn test_eval_arith() { // It's nice if an operation and its arguments can fit on a single // line in the test program. useself::AssemblerEntry::*; usecrate::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),
// 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),
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. useself::AssemblerEntry::*; usecrate::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),
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. useself::AssemblerEntry::*; usecrate::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),
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. useself::AssemblerEntry::*; usecrate::constants::*;
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. useself::AssemblerEntry::*; usecrate::constants::*;
letmut program = Vec::new();
program.push(Op(DW_OP_lit0)); for i in0..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));
}
#[test] fn test_eval_memory() { // It's nice if an operation and its arguments can fit on a single // line in the test program. useself::AssemblerEntry::*; usecrate::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),
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)); letmut v = address << 2; iflet 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.0as u64)?
} else {
eval.resume_with_indexed_address(10 + index.0as 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. useself::AssemblerEntry::*; usecrate::constants::*;
for i in0..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()),
},
}];
#[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. useself::AssemblerEntry::*; usecrate::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),
},
}];
#[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. useself::AssemblerEntry::*; usecrate::constants::*;
#[rustfmt::skip] let program = [
Op(DW_OP_stack_value)
];
#[test] fn test_eval_call() { // It's nice if an operation and its arguments can fit on a single // line in the test program. useself::AssemblerEntry::*; usecrate::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!(),
};
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. useself::AssemblerEntry::*; usecrate::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),
];
// 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),
];
#[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. useself::AssemblerEntry::*; usecrate::constants::*;
#[rustfmt::skip] let program = [
Mark(1),
Op(DW_OP_skip), Branch(1),
];
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,
} => { letmut v = address << 4; iflet 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)
},
);
}
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.93Bemerkung:
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.