use alloc::vec::Vec;
use core::mem;
use crate::common::{DebugAddrIndex, DebugInfoOffset, Encoding, Register};
use crate::constants;
use crate::read::{Error, Reader, ReaderOffset, Result, UnitOffset, Value, ValueType};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DieReference<T = usize> {
UnitRef(UnitOffset<T>),
DebugInfoRef(DebugInfoOffset<T>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Operation<R, Offset = <R as Reader>::Offset>
where
R: Reader<Offset = Offset>,
Offset: ReaderOffset,
{
Deref {
base_type: UnitOffset<Offset>,
size: u8,
space: bool,
},
Drop,
Pick {
index: u8,
},
Swap,
Rot,
Abs,
And,
Div,
Minus,
Mod,
Mul,
Neg,
Not,
Or,
Plus,
PlusConstant {
value: u64,
},
Shl,
Shr,
Shra,
Xor,
Bra {
target: i16,
},
Eq,
Ge,
Gt,
Le,
Lt,
Ne,
Skip {
target: i16,
},
UnsignedConstant {
value: u64,
},
SignedConstant {
value: i64,
},
Register {
register: Register,
},
RegisterOffset {
register: Register,
offset: i64,
base_type: UnitOffset<Offset>,
},
FrameOffset {
offset: i64,
},
Nop,
PushObjectAddress,
Call {
offset: DieReference<Offset>,
},
TLS,
CallFrameCFA,
Piece {
size_in_bits: u64,
bit_offset: Option<u64>,
},
ImplicitValue {
data: R,
},
StackValue,
ImplicitPointer {
value: DebugInfoOffset<Offset>,
byte_offset: i64,
},
EntryValue {
expression: R,
},
ParameterRef {
offset: UnitOffset<Offset>,
},
Address {
address: u64,
},
AddressIndex {
index: DebugAddrIndex<Offset>,
},
ConstantIndex {
index: DebugAddrIndex<Offset>,
},
TypedLiteral {
base_type: UnitOffset<Offset>,
value: R,
},
Convert {
base_type: UnitOffset<Offset>,
},
Reinterpret {
base_type: UnitOffset<Offset>,
},
}
#[derive(Debug)]
enum OperationEvaluationResult<R: Reader> {
Piece,
Incomplete,
Complete { location: Location<R> },
Waiting(EvaluationWaiting<R>, EvaluationResult<R>),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Location<R, Offset = <R as Reader>::Offset>
where
R: Reader<Offset = Offset>,
Offset: ReaderOffset,
{
Empty,
Register {
register: Register,
},
Address {
address: u64,
},
Value {
value: Value,
},
Bytes {
value: R,
},
ImplicitPointer {
value: DebugInfoOffset<Offset>,
byte_offset: i64,
},
}
impl<R, Offset> Location<R, Offset>
where
R: Reader<Offset = Offset>,
Offset: ReaderOffset,
{
pub fn is_empty(&self) -> bool {
match *self {
Location::Empty => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Piece<R, Offset = <R as Reader>::Offset>
where
R: Reader<Offset = Offset>,
Offset: ReaderOffset,
{
pub size_in_bits: Option<u64>,
pub bit_offset: Option<u64>,
pub location: Location<R, Offset>,
}
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,
{
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 = 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),
})
}
_ => 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,
}
#[derive(Debug, PartialEq)]
pub enum EvaluationResult<R: Reader> {
Complete,
RequiresMemory {
address: u64,
size: u8,
space: Option<u64>,
base_type: UnitOffset<R::Offset>,
},
RequiresRegister {
register: Register,
base_type: UnitOffset<R::Offset>,
},
RequiresFrameBase,
RequiresTls(u64),
RequiresCallFrameCfa,
RequiresAtLocation(DieReference<R::Offset>),
RequiresEntryValue(Expression<R>),
RequiresParameterRef(UnitOffset<R::Offset>),
RequiresRelocatedAddress(u64),
RequiresIndexedAddress {
index: DebugAddrIndex<R::Offset>,
relocate: bool,
},
RequiresBaseType(UnitOffset<R::Offset>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Expression<R: Reader>(pub R);
impl<R: Reader> Expression<R> {
#[inline]
pub fn evaluation(self, encoding: Encoding) -> Evaluation<R> {
Evaluation::new(self.0, encoding)
}
pub fn operations(self, encoding: Encoding) -> OperationIter<R> {
OperationIter {
input: self.0,
encoding,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct OperationIter<R: Reader> {
input: R,
encoding: Encoding,
}
impl<R: Reader> OperationIter<R> {
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)
}
}
}
pub fn offset_from(&self, expression: &Expression<R>) -> R::Offset {
self.input.offset_from(&expression.0)
}
}
#[derive(Debug)]
pub struct Evaluation<R: Reader> {
bytecode: R,
encoding: Encoding,
object_address: Option<u64>,
max_iterations: Option<u32>,
iteration: u32,
state: EvaluationState<R>,
addr_mask: u64,
stack: Vec<Value>,
pc: R,
expression_stack: Vec<(R, R)>,
result: Vec<Piece<R>>,
}
impl<R: Reader> Evaluation<R> {
pub fn new(bytecode: R, encoding: Encoding) -> Evaluation<R> {
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: Vec::new(),
expression_stack: Vec::new(),
pc,
result: Vec::new(),
}
}
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."
),
};
}
pub fn set_object_address(&mut self, value: u64) {
self.object_address = Some(value);
}
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) {
self.stack.push(value);
}
#[allow(clippy::cyclomatic_complexity)]
fn evaluate_one_operation(&mut self) -> Result<OperationEvaluationResult<R>> {
let operation = Operation::parse(&mut self.pc, self.encoding)?;
match operation {
Operation::Deref {
base_type,
size,
space,
} => {
let entry = self.pop()?;
let addr = entry.to_u64(self.addr_mask)?;
let addr_space = if space {
let entry = self.pop()?;
let value = entry.to_u64(self.addr_mask)?;
Some(value)
} else {
None
};
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::Memory,
EvaluationResult::RequiresMemory {
address: addr,
size,
space: addr_space,
base_type,
},
));
}
Operation::Drop => {
self.pop()?;
}
Operation::Pick { index } => {
let len = self.stack.len();
let index = index as usize;
if index >= len {
return Err(Error::NotEnoughStackItems);
}
let value = self.stack[len - index - 1];
self.push(value);
}
Operation::Swap => {
let top = self.pop()?;
let next = self.pop()?;
self.push(top);
self.push(next);
}
Operation::Rot => {
let one = self.pop()?;
let two = self.pop()?;
let three = self.pop()?;
self.push(one);
self.push(three);
self.push(two);
}
Operation::Abs => {
let value = self.pop()?;
let result = value.abs(self.addr_mask)?;
self.push(result);
}
Operation::And => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.and(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Div => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.div(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Minus => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.sub(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Mod => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.rem(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Mul => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.mul(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Neg => {
let v = self.pop()?;
let result = v.neg(self.addr_mask)?;
self.push(result);
}
Operation::Not => {
let value = self.pop()?;
let result = value.not(self.addr_mask)?;
self.push(result);
}
Operation::Or => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.or(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Plus => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.add(rhs, self.addr_mask)?;
self.push(result);
}
Operation::PlusConstant { value } => {
let lhs = self.pop()?;
let rhs = Value::from_u64(lhs.value_type(), value)?;
let result = lhs.add(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Shl => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.shl(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Shr => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.shr(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Shra => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.shra(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Xor => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.xor(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Bra { target } => {
let entry = self.pop()?;
let v = entry.to_u64(self.addr_mask)?;
if v != 0 {
self.pc = compute_pc(&self.pc, &self.bytecode, target)?;
}
}
Operation::Eq => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.eq(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Ge => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.ge(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Gt => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.gt(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Le => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.le(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Lt => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.lt(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Ne => {
let rhs = self.pop()?;
let lhs = self.pop()?;
let result = lhs.ne(rhs, self.addr_mask)?;
self.push(result);
}
Operation::Skip { target } => {
self.pc = compute_pc(&self.pc, &self.bytecode, target)?;
}
Operation::UnsignedConstant { value } => {
self.push(Value::Generic(value));
}
Operation::SignedConstant { value } => {
self.push(Value::Generic(value as u64));
}
Operation::RegisterOffset {
register,
offset,
base_type,
} => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::Register { offset },
EvaluationResult::RequiresRegister {
register,
base_type,
},
));
}
Operation::FrameOffset { offset } => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::FrameBase { offset },
EvaluationResult::RequiresFrameBase,
));
}
Operation::Nop => {}
Operation::PushObjectAddress => {
if let Some(value) = self.object_address {
self.push(Value::Generic(value));
} else {
return Err(Error::InvalidPushObjectAddress);
}
}
Operation::Call { offset } => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::AtLocation,
EvaluationResult::RequiresAtLocation(offset),
));
}
Operation::TLS => {
let entry = self.pop()?;
let index = entry.to_u64(self.addr_mask)?;
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::Tls,
EvaluationResult::RequiresTls(index),
));
}
Operation::CallFrameCFA => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::Cfa,
EvaluationResult::RequiresCallFrameCfa,
));
}
Operation::Register { register } => {
let location = Location::Register { register };
return Ok(OperationEvaluationResult::Complete { location });
}
Operation::ImplicitValue { ref data } => {
let location = Location::Bytes {
value: data.clone(),
};
return Ok(OperationEvaluationResult::Complete { location });
}
Operation::StackValue => {
let value = self.pop()?;
let location = Location::Value { value };
return Ok(OperationEvaluationResult::Complete { location });
}
Operation::ImplicitPointer { value, byte_offset } => {
let location = Location::ImplicitPointer { value, byte_offset };
return Ok(OperationEvaluationResult::Complete { location });
}
Operation::EntryValue { ref expression } => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::EntryValue,
EvaluationResult::RequiresEntryValue(Expression(expression.clone())),
));
}
Operation::ParameterRef { offset } => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::ParameterRef,
EvaluationResult::RequiresParameterRef(offset),
));
}
Operation::Address { address } => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::RelocatedAddress,
EvaluationResult::RequiresRelocatedAddress(address),
));
}
Operation::AddressIndex { index } => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::IndexedAddress,
EvaluationResult::RequiresIndexedAddress {
index,
relocate: true,
},
));
}
Operation::ConstantIndex { index } => {
return Ok(OperationEvaluationResult::Waiting(
EvaluationWaiting::IndexedAddress,
EvaluationResult::RequiresIndexedAddress {
index,
relocate: false,
},
));
}
Operation::Piece {
size_in_bits,
bit_offset,
} => {
let location = if self.stack.is_empty() {
Location::Empty
} else {
let entry = self.pop()?;
let address = entry.to_u64(self.addr_mask)?;
Location::Address { address }
};
self.result.push(Piece {
size_in_bits: Some(size_in_bits),
bit_offset,
location,
});
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),
));
}
}
Ok(OperationEvaluationResult::Incomplete)
}
pub fn result(self) -> Vec<Piece<R>> {
match self.state {
EvaluationState::Complete => self.result,
_ => {
panic!("Called `Evaluation::result` on an `Evaluation` that has not been completed")
}
}
}
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)
}
}
}
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()
}
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()
}
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()
}
pub fn resume_with_tls(&mut self, value: u64) -> Result<EvaluationResult<R>> {
match self.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::Tls) => {
self.push(Value::Generic(value));
}
_ => panic!(
"Called `Evaluation::resume_with_tls` without a preceding `EvaluationResult::RequiresTls`"
),
};
self.evaluate_internal()
}
pub fn resume_with_call_frame_cfa(&mut self, cfa: u64) -> Result<EvaluationResult<R>> {
match self.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::Cfa) => {
self.push(Value::Generic(cfa));
}
_ => panic!(
"Called `Evaluation::resume_with_call_frame_cfa` without a preceding `EvaluationResult::RequiresCallFrameCfa`"
),
};
self.evaluate_internal()
}
pub fn resume_with_at_location(&mut self, mut bytes: R) -> Result<EvaluationResult<R>> {
match self.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::AtLocation) => {
if !bytes.is_empty() {
let mut pc = bytes.clone();
mem::swap(&mut pc, &mut self.pc);
mem::swap(&mut bytes, &mut self.bytecode);
self.expression_stack.push((pc, bytes));
}
}
_ => panic!(
"Called `Evaluation::resume_with_at_location` without a precedeing `EvaluationResult::RequiresAtLocation`"
),
};
self.evaluate_internal()
}
pub fn resume_with_entry_value(&mut self, entry_value: Value) -> Result<EvaluationResult<R>> {
match self.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::EntryValue) => {
self.push(entry_value);
}
_ => panic!(
"Called `Evaluation::resume_with_entry_value` without a preceding `EvaluationResult::RequiresEntryValue`"
),
};
self.evaluate_internal()
}
pub fn resume_with_parameter_ref(
&mut self,
parameter_value: u64,
) -> Result<EvaluationResult<R>> {
match self.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::ParameterRef) => {
self.push(Value::Generic(parameter_value));
}
_ => panic!(
"Called `Evaluation::resume_with_parameter_ref` without a preceding `EvaluationResult::RequiresParameterRef`"
),
};
self.evaluate_internal()
}
pub fn resume_with_relocated_address(&mut self, address: u64) -> Result<EvaluationResult<R>> {
match self.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::RelocatedAddress) => {
self.push(Value::Generic(address));
}
_ => panic!(
"Called `Evaluation::resume_with_relocated_address` without a preceding `EvaluationResult::RequiresRelocatedAddress`"
),
};
self.evaluate_internal()
}
pub fn resume_with_indexed_address(&mut self, address: u64) -> Result<EvaluationResult<R>> {
match self.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::IndexedAddress) => {
self.push(Value::Generic(address));
}
_ => panic!(
"Called `Evaluation::resume_with_indexed_address` without a preceding `EvaluationResult::RequiresIndexedAddress`"
),
};
self.evaluate_internal()
}
pub fn resume_with_base_type(&mut self, base_type: ValueType) -> Result<EvaluationResult<R>> {
let value = match self.state {
EvaluationState::Error(err) => return Err(err),
EvaluationState::Waiting(EvaluationWaiting::TypedLiteral { ref value }) => {
Value::parse(base_type, value.clone())?
}
EvaluationState::Waiting(EvaluationWaiting::Convert) => {
let entry = self.pop()?;
entry.convert(base_type, self.addr_mask)?
}
EvaluationState::Waiting(EvaluationWaiting::Reinterpret) => {
let entry = self.pop()?;
entry.reinterpret(base_type, self.addr_mask)?
}
_ => panic!(
"Called `Evaluation::resume_with_base_type` without a preceding `EvaluationResult::RequiresBaseType`"
),
};
self.push(value);
self.evaluate_internal()
}
fn end_of_expression(&mut self) -> bool {
while self.pc.is_empty() {
match self.expression_stack.pop() {
Some((newpc, newbytes)) => {
self.pc = newpc;
self.bytecode = newbytes;
}
None => return true,
}
}
false
}
fn evaluate_internal(&mut self) -> Result<EvaluationResult<R>> {
while !self.end_of_expression() {
self.iteration += 1;
if let Some(max_iterations) = self.max_iterations {
if self.iteration > max_iterations {
return Err(Error::TooManyIterations);
}
}
let op_result = self.evaluate_one_operation()?;
match op_result {
OperationEvaluationResult::Piece => {}
OperationEvaluationResult::Incomplete => {
if self.end_of_expression() && !self.result.is_empty() {
return Err(Error::InvalidPiece);
}
}
OperationEvaluationResult::Complete { location } => {
if self.end_of_expression() {
if !self.result.is_empty() {
return Err(Error::InvalidPiece);
}
self.result.push(Piece {
size_in_bits: None,
bit_offset: None,
location,
});
} else {
match Operation::parse(&mut self.pc, self.encoding)? {
Operation::Piece {
size_in_bits,
bit_offset,
} => {
self.result.push(Piece {
size_in_bits: Some(size_in_bits),
bit_offset,
location,
});
}
_ => {
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 self.result.is_empty() {
let entry = self.pop()?;
let addr = entry.to_u64(self.addr_mask)?;
self.result.push(Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Address { address: addr },
});
}
self.state = EvaluationState::Complete;
Ok(EvaluationResult::Complete)
}
}
#[cfg(test)]
#[cfg(feature = "write")]
mod tests {
use super::*;
use crate::common::Format;
use crate::constants;
use crate::endianity::LittleEndian;
use crate::leb128;
use crate::read::{EndianSlice, Error, Result, UnitOffset};
use crate::test_util::GimliSectionMethods;
use core::usize;
use test_assembler::{Endian, Section};
fn encoding4() -> Encoding {
Encoding {
format: Format::Dwarf32,
version: 4,
address_size: 4,
}
}
fn encoding8() -> Encoding {
Encoding {
format: Format::Dwarf64,
version: 4,
address_size: 8,
}
}
#[test]
fn test_compute_pc() {
let bytes = [0, 1, 2, 3, 4];
let bytecode = &bytes[..];
let ebuf = &EndianSlice::new(bytecode, LittleEndian);
assert_eq!(compute_pc(ebuf, ebuf, 0), Ok(*ebuf));
assert_eq!(
compute_pc(ebuf, ebuf, -1),
Err(Error::BadBranchTarget(usize::MAX as u64))
);
assert_eq!(compute_pc(ebuf, ebuf, 5), Ok(ebuf.range_from(5..)));
assert_eq!(
compute_pc(&ebuf.range_from(3..), ebuf, -2),
Ok(ebuf.range_from(1..))
);
assert_eq!(
compute_pc(&ebuf.range_from(2..), ebuf, 2),
Ok(ebuf.range_from(4..))
);
}
fn check_op_parse_simple<'input>(
input: &'input [u8],
expect: &Operation<EndianSlice<'input, LittleEndian>>,
encoding: Encoding,
) {
let buf = EndianSlice::new(input, LittleEndian);
let mut pc = buf;
let value = Operation::parse(&mut pc, encoding);
match value {
Ok(val) => {
assert_eq!(val, *expect);
assert_eq!(pc.len(), 0);
}
_ => panic!("Unexpected result"),
}
}
fn check_op_parse_eof(input: &[u8], encoding: Encoding) {
let buf = EndianSlice::new(input, LittleEndian);
let mut pc = buf;
match Operation::parse(&mut pc, encoding) {
Err(Error::UnexpectedEof(id)) => {
assert!(buf.lookup_offset_id(id).is_some());
}
_ => panic!("Unexpected result"),
}
}
fn check_op_parse<F>(
input: F,
expect: &Operation<EndianSlice<LittleEndian>>,
encoding: Encoding,
) where
F: Fn(Section) -> Section,
{
let input = input(Section::with_endian(Endian::Little))
.get_contents()
.unwrap();
for i in 1..input.len() {
check_op_parse_eof(&input[..i], encoding);
}
check_op_parse_simple(&input, expect, encoding);
}
#[test]
fn test_op_parse_onebyte() {
let encoding = encoding4();
#[rustfmt::skip]
let inputs = [
(
constants::DW_OP_deref,
Operation::Deref {
base_type: generic_type(),
size: encoding.address_size,
space: false,
},
),
(constants::DW_OP_dup, Operation::Pick { index: 0 }),
(constants::DW_OP_drop, Operation::Drop),
(constants::DW_OP_over, Operation::Pick { index: 1 }),
(constants::DW_OP_swap, Operation::Swap),
(constants::DW_OP_rot, Operation::Rot),
(
constants::DW_OP_xderef,
Operation::Deref {
base_type: generic_type(),
size: encoding.address_size,
space: true,
},
),
(constants::DW_OP_abs, Operation::Abs),
(constants::DW_OP_and, Operation::And),
(constants::DW_OP_div, Operation::Div),
(constants::DW_OP_minus, Operation::Minus),
(constants::DW_OP_mod, Operation::Mod),
(constants::DW_OP_mul, Operation::Mul),
(constants::DW_OP_neg, Operation::Neg),
(constants::DW_OP_not, Operation::Not),
(constants::DW_OP_or, Operation::Or),
(constants::DW_OP_plus, Operation::Plus),
(constants::DW_OP_shl, Operation::Shl),
(constants::DW_OP_shr, Operation::Shr),
(constants::DW_OP_shra, Operation::Shra),
(constants::DW_OP_xor, Operation::Xor),
(constants::DW_OP_eq, Operation::Eq),
(constants::DW_OP_ge, Operation::Ge),
(constants::DW_OP_gt, Operation::Gt),
(constants::DW_OP_le, Operation::Le),
(constants::DW_OP_lt, Operation::Lt),
(constants::DW_OP_ne, Operation::Ne),
(constants::DW_OP_lit0, Operation::UnsignedConstant { value: 0 }),
(constants::DW_OP_lit1, Operation::UnsignedConstant { value: 1 }),
(constants::DW_OP_lit2, Operation::UnsignedConstant { value: 2 }),
(constants::DW_OP_lit3, Operation::UnsignedConstant { value: 3 }),
(constants::DW_OP_lit4, Operation::UnsignedConstant { value: 4 }),
(constants::DW_OP_lit5, Operation::UnsignedConstant { value: 5 }),
(constants::DW_OP_lit6, Operation::UnsignedConstant { value: 6 }),
(constants::DW_OP_lit7, Operation::UnsignedConstant { value: 7 }),
(constants::DW_OP_lit8, Operation::UnsignedConstant { value: 8 }),
(constants::DW_OP_lit9, Operation::UnsignedConstant { value: 9 }),
(constants::DW_OP_lit10, Operation::UnsignedConstant { value: 10 }),
(constants::DW_OP_lit11, Operation::UnsignedConstant { value: 11 }),
(constants::DW_OP_lit12, Operation::UnsignedConstant { value: 12 }),
(constants::DW_OP_lit13, Operation::UnsignedConstant { value: 13 }),
(constants::DW_OP_lit14, Operation::UnsignedConstant { value: 14 }),
(constants::DW_OP_lit15, Operation::UnsignedConstant { value: 15 }),
(constants::DW_OP_lit16, Operation::UnsignedConstant { value: 16 }),
(constants::DW_OP_lit17, Operation::UnsignedConstant { value: 17 }),
(constants::DW_OP_lit18, Operation::UnsignedConstant { value: 18 }),
(constants::DW_OP_lit19, Operation::UnsignedConstant { value: 19 }),
(constants::DW_OP_lit20, Operation::UnsignedConstant { value: 20 }),
(constants::DW_OP_lit21, Operation::UnsignedConstant { value: 21 }),
(constants::DW_OP_lit22, Operation::UnsignedConstant { value: 22 }),
(constants::DW_OP_lit23, Operation::UnsignedConstant { value: 23 }),
(constants::DW_OP_lit24, Operation::UnsignedConstant { value: 24 }),
(constants::DW_OP_lit25, Operation::UnsignedConstant { value: 25 }),
(constants::DW_OP_lit26, Operation::UnsignedConstant { value: 26 }),
(constants::DW_OP_lit27, Operation::UnsignedConstant { value: 27 }),
(constants::DW_OP_lit28, Operation::UnsignedConstant { value: 28 }),
(constants::DW_OP_lit29, Operation::UnsignedConstant { value: 29 }),
(constants::DW_OP_lit30, Operation::UnsignedConstant { value: 30 }),
(constants::DW_OP_lit31, Operation::UnsignedConstant { value: 31 }),
(constants::DW_OP_reg0, Operation::Register { register: Register(0) }),
(constants::DW_OP_reg1, Operation::Register { register: Register(1) }),
(constants::DW_OP_reg2, Operation::Register { register: Register(2) }),
(constants::DW_OP_reg3, Operation::Register { register: Register(3) }),
(constants::DW_OP_reg4, Operation::Register { register: Register(4) }),
(constants::DW_OP_reg5, Operation::Register { register: Register(5) }),
(constants::DW_OP_reg6, Operation::Register { register: Register(6) }),
(constants::DW_OP_reg7, Operation::Register { register: Register(7) }),
(constants::DW_OP_reg8, Operation::Register { register: Register(8) }),
(constants::DW_OP_reg9, Operation::Register { register: Register(9) }),
(constants::DW_OP_reg10, Operation::Register { register: Register(10) }),
(constants::DW_OP_reg11, Operation::Register { register: Register(11) }),
(constants::DW_OP_reg12, Operation::Register { register: Register(12) }),
(constants::DW_OP_reg13, Operation::Register { register: Register(13) }),
(constants::DW_OP_reg14, Operation::Register { register: Register(14) }),
(constants::DW_OP_reg15, Operation::Register { register: Register(15) }),
(constants::DW_OP_reg16, Operation::Register { register: Register(16) }),
(constants::DW_OP_reg17, Operation::Register { register: Register(17) }),
(constants::DW_OP_reg18, Operation::Register { register: Register(18) }),
(constants::DW_OP_reg19, Operation::Register { register: Register(19) }),
(constants::DW_OP_reg20, Operation::Register { register: Register(20) }),
(constants::DW_OP_reg21, Operation::Register { register: Register(21) }),
(constants::DW_OP_reg22, Operation::Register { register: Register(22) }),
(constants::DW_OP_reg23, Operation::Register { register: Register(23) }),
(constants::DW_OP_reg24, Operation::Register { register: Register(24) }),
(constants::DW_OP_reg25, Operation::Register { register: Register(25) }),
(constants::DW_OP_reg26, Operation::Register { register: Register(26) }),
(constants::DW_OP_reg27, Operation::Register { register: Register(27) }),
(constants::DW_OP_reg28, Operation::Register { register: Register(28) }),
(constants::DW_OP_reg29, Operation::Register { register: Register(29) }),
(constants::DW_OP_reg30, Operation::Register { register: Register(30) }),
(constants::DW_OP_reg31, Operation::Register { register: Register(31) }),
(constants::DW_OP_nop, Operation::Nop),
(constants::DW_OP_push_object_address, Operation::PushObjectAddress),
(constants::DW_OP_form_tls_address, Operation::TLS),
(constants::DW_OP_GNU_push_tls_address, Operation::TLS),
(constants::DW_OP_call_frame_cfa, Operation::CallFrameCFA),
(constants::DW_OP_stack_value, Operation::StackValue),
];
let input = [];
check_op_parse_eof(&input[..], encoding);
for item in inputs.iter() {
let (opcode, ref result) = *item;
check_op_parse(|s| s.D8(opcode.0), result, encoding);
}
}
#[test]
fn test_op_parse_twobyte() {
let encoding = encoding4();
let inputs = [
(
constants::DW_OP_const1u,
23,
Operation::UnsignedConstant { value: 23 },
),
(
constants::DW_OP_const1s,
(-23i8) as u8,
Operation::SignedConstant { value: -23 },
),
(constants::DW_OP_pick, 7, Operation::Pick { index: 7 }),
(
constants::DW_OP_deref_size,
19,
Operation::Deref {
base_type: generic_type(),
size: 19,
space: false,
},
),
(
constants::DW_OP_xderef_size,
19,
Operation::Deref {
base_type: generic_type(),
size: 19,
space: true,
},
),
];
for item in inputs.iter() {
let (opcode, arg, ref result) = *item;
check_op_parse(|s| s.D8(opcode.0).D8(arg), result, encoding);
}
}
#[test]
fn test_op_parse_threebyte() {
let encoding = encoding4();
let inputs = [
(
constants::DW_OP_const2u,
23,
Operation::UnsignedConstant { value: 23 },
),
(
constants::DW_OP_const2s,
(-23i16) as u16,
Operation::SignedConstant { value: -23 },
),
(
constants::DW_OP_call2,
1138,
Operation::Call {
offset: DieReference::UnitRef(UnitOffset(1138)),
},
),
(
constants::DW_OP_bra,
(-23i16) as u16,
Operation::Bra { target: -23 },
),
(
constants::DW_OP_skip,
(-23i16) as u16,
Operation::Skip { target: -23 },
),
];
for item in inputs.iter() {
let (opcode, arg, ref result) = *item;
check_op_parse(|s| s.D8(opcode.0).L16(arg), result, encoding);
}
}
#[test]
fn test_op_parse_fivebyte() {
let encoding = encoding4();
let inputs = [
(
constants::DW_OP_addr,
0x1234_5678,
Operation::Address {
address: 0x1234_5678,
},
),
(
constants::DW_OP_const4u,
0x1234_5678,
Operation::UnsignedConstant { value: 0x1234_5678 },
),
(
constants::DW_OP_const4s,
(-23i32) as u32,
Operation::SignedConstant { value: -23 },
),
(
constants::DW_OP_call4,
0x1234_5678,
Operation::Call {
offset: DieReference::UnitRef(UnitOffset(0x1234_5678)),
},
),
(
constants::DW_OP_call_ref,
0x1234_5678,
Operation::Call {
offset: DieReference::DebugInfoRef(DebugInfoOffset(0x1234_5678)),
},
),
];
for item in inputs.iter() {
let (op, arg, ref expect) = *item;
check_op_parse(|s| s.D8(op.0).L32(arg), expect, encoding);
}
}
#[test]
#[cfg(target_pointer_width = "64")]
fn test_op_parse_ninebyte() {
let encoding = encoding8();
let inputs = [
(
constants::DW_OP_addr,
0x1234_5678_1234_5678,
Operation::Address {
address: 0x1234_5678_1234_5678,
},
),
(
constants::DW_OP_const8u,
0x1234_5678_1234_5678,
Operation::UnsignedConstant {
value: 0x1234_5678_1234_5678,
},
),
(
constants::DW_OP_const8s,
(-23i64) as u64,
Operation::SignedConstant { value: -23 },
),
(
constants::DW_OP_call_ref,
0x1234_5678_1234_5678,
Operation::Call {
offset: DieReference::DebugInfoRef(DebugInfoOffset(0x1234_5678_1234_5678)),
},
),
];
for item in inputs.iter() {
let (op, arg, ref expect) = *item;
check_op_parse(|s| s.D8(op.0).L64(arg), expect, encoding);
}
}
#[test]
fn test_op_parse_sleb() {
let encoding = encoding4();
let values = [
-1i64,
0,
1,
0x100,
0x1eee_eeee,
0x7fff_ffff_ffff_ffff,
-0x100,
-0x1eee_eeee,
-0x7fff_ffff_ffff_ffff,
];
for value in values.iter() {
let mut inputs = vec![
(
constants::DW_OP_consts.0,
Operation::SignedConstant { value: *value },
),
(
constants::DW_OP_fbreg.0,
Operation::FrameOffset { offset: *value },
),
];
for i in 0..32 {
inputs.push((
constants::DW_OP_breg0.0 + i,
Operation::RegisterOffset {
register: Register(i.into()),
offset: *value,
base_type: UnitOffset(0),
},
));
}
for item in inputs.iter() {
let (op, ref expect) = *item;
check_op_parse(|s| s.D8(op).sleb(*value), expect, encoding);
}
}
}
#[test]
fn test_op_parse_uleb() {
let encoding = encoding4();
let values = [
0,
1,
0x100,
(!0u16).into(),
0x1eee_eeee,
0x7fff_ffff_ffff_ffff,
!0u64,
];
for value in values.iter() {
let mut inputs = vec![
(
constants::DW_OP_constu,
Operation::UnsignedConstant { value: *value },
),
(
constants::DW_OP_plus_uconst,
Operation::PlusConstant { value: *value },
),
];
if *value <= (!0u16).into() {
inputs.push((
constants::DW_OP_regx,
Operation::Register {
register: Register::from_u64(*value).unwrap(),
},
));
}
if *value <= (!0u32).into() {
inputs.extend(&[
(
constants::DW_OP_addrx,
Operation::AddressIndex {
index: DebugAddrIndex(*value as usize),
},
),
(
constants::DW_OP_constx,
Operation::ConstantIndex {
index: DebugAddrIndex(*value as usize),
},
),
]);
}
if *value < !0u64 / 8 {
inputs.push((
constants::DW_OP_piece,
Operation::Piece {
size_in_bits: 8 * value,
bit_offset: None,
},
));
}
for item in inputs.iter() {
let (op, ref expect) = *item;
let input = Section::with_endian(Endian::Little)
.D8(op.0)
.uleb(*value)
.get_contents()
.unwrap();
check_op_parse_simple(&input, expect, encoding);
}
}
}
#[test]
fn test_op_parse_bregx() {
let encoding = encoding4();
let uvalues = [0, 1, 0x100, !0u16];
let svalues = [
-1i64,
0,
1,
0x100,
0x1eee_eeee,
0x7fff_ffff_ffff_ffff,
-0x100,
-0x1eee_eeee,
-0x7fff_ffff_ffff_ffff,
];
for v1 in uvalues.iter() {
for v2 in svalues.iter() {
check_op_parse(
|s| s.D8(constants::DW_OP_bregx.0).uleb((*v1).into()).sleb(*v2),
&Operation::RegisterOffset {
register: Register(*v1),
offset: *v2,
base_type: UnitOffset(0),
},
encoding,
);
}
}
}
#[test]
fn test_op_parse_bit_piece() {
let encoding = encoding4();
let values = [0, 1, 0x100, 0x1eee_eeee, 0x7fff_ffff_ffff_ffff, !0u64];
for v1 in values.iter() {
for v2 in values.iter() {
let input = Section::with_endian(Endian::Little)
.D8(constants::DW_OP_bit_piece.0)
.uleb(*v1)
.uleb(*v2)
.get_contents()
.unwrap();
check_op_parse_simple(
&input,
&Operation::Piece {
size_in_bits: *v1,
bit_offset: Some(*v2),
},
encoding,
);
}
}
}
#[test]
fn test_op_parse_implicit_value() {
let encoding = encoding4();
let data = b"hello";
check_op_parse(
|s| {
s.D8(constants::DW_OP_implicit_value.0)
.uleb(data.len() as u64)
.append_bytes(&data[..])
},
&Operation::ImplicitValue {
data: EndianSlice::new(&data[..], LittleEndian),
},
encoding,
);
}
#[test]
fn test_op_parse_const_type() {
let encoding = encoding4();
let data = b"hello";
check_op_parse(
|s| {
s.D8(constants::DW_OP_const_type.0)
.uleb(100)
.D8(data.len() as u8)
.append_bytes(&data[..])
},
&Operation::TypedLiteral {
base_type: UnitOffset(100),
value: EndianSlice::new(&data[..], LittleEndian),
},
encoding,
);
check_op_parse(
|s| {
s.D8(constants::DW_OP_GNU_const_type.0)
.uleb(100)
.D8(data.len() as u8)
.append_bytes(&data[..])
},
&Operation::TypedLiteral {
base_type: UnitOffset(100),
value: EndianSlice::new(&data[..], LittleEndian),
},
encoding,
);
}
#[test]
fn test_op_parse_regval_type() {
let encoding = encoding4();
check_op_parse(
|s| s.D8(constants::DW_OP_regval_type.0).uleb(1).uleb(100),
&Operation::RegisterOffset {
register: Register(1),
offset: 0,
base_type: UnitOffset(100),
},
encoding,
);
check_op_parse(
|s| s.D8(constants::DW_OP_GNU_regval_type.0).uleb(1).uleb(100),
&Operation::RegisterOffset {
register: Register(1),
offset: 0,
base_type: UnitOffset(100),
},
encoding,
);
}
#[test]
fn test_op_parse_deref_type() {
let encoding = encoding4();
check_op_parse(
|s| s.D8(constants::DW_OP_deref_type.0).D8(8).uleb(100),
&Operation::Deref {
base_type: UnitOffset(100),
size: 8,
space: false,
},
encoding,
);
check_op_parse(
|s| s.D8(constants::DW_OP_GNU_deref_type.0).D8(8).uleb(100),
&Operation::Deref {
base_type: UnitOffset(100),
size: 8,
space: false,
},
encoding,
);
check_op_parse(
|s| s.D8(constants::DW_OP_xderef_type.0).D8(8).uleb(100),
&Operation::Deref {
base_type: UnitOffset(100),
size: 8,
space: true,
},
encoding,
);
}
#[test]
fn test_op_convert() {
let encoding = encoding4();
check_op_parse(
|s| s.D8(constants::DW_OP_convert.0).uleb(100),
&Operation::Convert {
base_type: UnitOffset(100),
},
encoding,
);
check_op_parse(
|s| s.D8(constants::DW_OP_GNU_convert.0).uleb(100),
&Operation::Convert {
base_type: UnitOffset(100),
},
encoding,
);
}
#[test]
fn test_op_reinterpret() {
let encoding = encoding4();
check_op_parse(
|s| s.D8(constants::DW_OP_reinterpret.0).uleb(100),
&Operation::Reinterpret {
base_type: UnitOffset(100),
},
encoding,
);
check_op_parse(
|s| s.D8(constants::DW_OP_GNU_reinterpret.0).uleb(100),
&Operation::Reinterpret {
base_type: UnitOffset(100),
},
encoding,
);
}
#[test]
fn test_op_parse_implicit_pointer() {
for op in &[
constants::DW_OP_implicit_pointer,
constants::DW_OP_GNU_implicit_pointer,
] {
check_op_parse(
|s| s.D8(op.0).D32(0x1234_5678).sleb(0x123),
&Operation::ImplicitPointer {
value: DebugInfoOffset(0x1234_5678),
byte_offset: 0x123,
},
encoding4(),
);
check_op_parse(
|s| s.D8(op.0).D64(0x1234_5678).sleb(0x123),
&Operation::ImplicitPointer {
value: DebugInfoOffset(0x1234_5678),
byte_offset: 0x123,
},
encoding8(),
);
}
}
#[test]
fn test_op_parse_entry_value() {
for op in &[
constants::DW_OP_entry_value,
constants::DW_OP_GNU_entry_value,
] {
let data = b"hello";
check_op_parse(
|s| s.D8(op.0).uleb(data.len() as u64).append_bytes(&data[..]),
&Operation::EntryValue {
expression: EndianSlice::new(&data[..], LittleEndian),
},
encoding4(),
);
}
}
#[test]
fn test_op_parse_gnu_parameter_ref() {
check_op_parse(
|s| s.D8(constants::DW_OP_GNU_parameter_ref.0).D32(0x1234_5678),
&Operation::ParameterRef {
offset: UnitOffset(0x1234_5678),
},
encoding4(),
)
}
enum AssemblerEntry {
Op(constants::DwOp),
Mark(u8),
Branch(u8),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
Uleb(u64),
Sleb(u64),
}
fn assemble(entries: &[AssemblerEntry]) -> Vec<u8> {
let mut result = Vec::new();
struct Marker(Option<usize>, Vec<usize>);
let mut markers = Vec::new();
for _ in 0..256 {
markers.push(Marker(None, Vec::new()));
}
fn write(stack: &mut Vec<u8>, index: usize, mut num: u64, nbytes: u8) {
for i in 0..nbytes as usize {
stack[index + i] = (num & 0xff) as u8;
num >>= 8;
}
}
fn push(stack: &mut Vec<u8>, num: u64, nbytes: u8) {
let index = stack.len();
for _ in 0..nbytes {
stack.push(0);
}
write(stack, index, num, nbytes);
}
for item in entries {
match *item {
AssemblerEntry::Op(op) => result.push(op.0),
AssemblerEntry::Mark(num) => {
assert!(markers[num as usize].0.is_none());
markers[num as usize].0 = Some(result.len());
}
AssemblerEntry::Branch(num) => {
markers[num as usize].1.push(result.len());
push(&mut result, 0, 2);
}
AssemblerEntry::U8(num) => result.push(num),
AssemblerEntry::U16(num) => push(&mut result, u64::from(num), 2),
AssemblerEntry::U32(num) => push(&mut result, u64::from(num), 4),
AssemblerEntry::U64(num) => push(&mut result, num, 8),
AssemblerEntry::Uleb(num) => {
leb128::write::unsigned(&mut result, num).unwrap();
}
AssemblerEntry::Sleb(num) => {
leb128::write::signed(&mut result, num as i64).unwrap();
}
}
}
for marker in markers {
if let Some(offset) = marker.0 {
for branch_offset in marker.1 {
let delta = offset.wrapping_sub(branch_offset + 2) as u64;
write(&mut result, branch_offset, delta, 2);
}
}
}
result
}
#[allow(clippy::too_many_arguments)]
fn check_eval_with_args<F>(
program: &[AssemblerEntry],
expect: Result<&[Piece<EndianSlice<LittleEndian>>]>,
encoding: Encoding,
object_address: Option<u64>,
initial_value: Option<u64>,
max_iterations: Option<u32>,
f: F,
) where
for<'a> F: Fn(
&mut Evaluation<EndianSlice<'a, LittleEndian>>,
EvaluationResult<EndianSlice<'a, LittleEndian>>,
) -> Result<EvaluationResult<EndianSlice<'a, LittleEndian>>>,
{
let bytes = assemble(program);
let bytes = EndianSlice::new(&bytes, LittleEndian);
let mut eval = Evaluation::new(bytes, encoding);
if let Some(val) = object_address {
eval.set_object_address(val);
}
if let Some(val) = initial_value {
eval.set_initial_value(val);
}
if let Some(val) = max_iterations {
eval.set_max_iterations(val);
}
let result = match eval.evaluate() {
Err(e) => Err(e),
Ok(r) => f(&mut eval, r),
};
match (result, expect) {
(Ok(EvaluationResult::Complete), Ok(pieces)) => {
let vec = eval.result();
assert_eq!(vec.len(), pieces.len());
for i in 0..pieces.len() {
assert_eq!(vec[i], pieces[i]);
}
}
(Err(f1), Err(f2)) => {
assert_eq!(f1, f2);
}
otherwise => panic!("Unexpected result: {:?}", otherwise),
}
}
fn check_eval(
program: &[AssemblerEntry],
expect: Result<&[Piece<EndianSlice<LittleEndian>>]>,
encoding: Encoding,
) {
check_eval_with_args(program, expect, encoding, None, None, None, |_, result| {
Ok(result)
});
}
#[test]
fn test_eval_arith() {
use self::AssemblerEntry::*;
use crate::constants::*;
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),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_const1u), U8(1),
Op(DW_OP_plus),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_plus_uconst), Uleb(1),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0),
Op(DW_OP_const1u), U8(1),
Op(DW_OP_minus),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_abs),
Op(DW_OP_const1u), U8(1),
Op(DW_OP_minus),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const4u), U32(0xf078_fffe),
Op(DW_OP_const4u), U32(0x0f87_0001),
Op(DW_OP_and),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const4u), U32(0xf078_fffe),
Op(DW_OP_const4u), U32(0xf000_00fe),
Op(DW_OP_and),
Op(DW_OP_const4u), U32(0xf000_00fe),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xfe),
Op(DW_OP_const1s), U8(2),
Op(DW_OP_div),
Op(DW_OP_plus_uconst), Uleb(1),
Op(DW_OP_bra), Branch(fail),
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),
Op(DW_OP_const4u), U32(0x8000_0001),
Op(DW_OP_lit2),
Op(DW_OP_mul),
Op(DW_OP_lit2),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const4u), U32(0xf0f0_f0f0),
Op(DW_OP_const4u), U32(0xf0f0_f0f0),
Op(DW_OP_xor),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const4u), U32(0xf0f0_f0f0),
Op(DW_OP_const4u), U32(0x0f0f_0f0f),
Op(DW_OP_or),
Op(DW_OP_not),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const8u), U64(0xffff_ffff_0000_0000),
Op(DW_OP_lit2),
Op(DW_OP_div),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1u), U8(0xff),
Op(DW_OP_lit1),
Op(DW_OP_shl),
Op(DW_OP_const2u), U16(0x1fe),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1u), U8(0xff),
Op(DW_OP_const1u), U8(50),
Op(DW_OP_shl),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1u), U8(0xff),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_shl),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_lit1),
Op(DW_OP_shr),
Op(DW_OP_const4u), U32(0x7fff_ffff),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_const1u), U8(0xff),
Op(DW_OP_shr),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_lit1),
Op(DW_OP_shra),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_const1u), U8(0xff),
Op(DW_OP_shra),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit0),
Op(DW_OP_nop),
Op(DW_OP_skip), Branch(done),
Mark(fail),
Op(DW_OP_lit1),
Mark(done),
Op(DW_OP_stack_value),
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(0),
},
}];
check_eval(&program, Ok(&result), encoding4());
}
#[test]
fn test_eval_arith64() {
use self::AssemblerEntry::*;
use crate::constants::*;
let done = 0;
let fail = 1;
#[rustfmt::skip]
let program = [
Op(DW_OP_const8u), U64(0x1111_2222_3333_4444),
Op(DW_OP_const8s), U64((-0x1111_2222_3333_4444i64) as u64),
Op(DW_OP_plus),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_constu), Uleb(0x1111_2222_3333_4444),
Op(DW_OP_consts), Sleb((-0x1111_2222_3333_4444i64) as u64),
Op(DW_OP_plus),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit1),
Op(DW_OP_plus_uconst), Uleb(!0u64),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit1),
Op(DW_OP_neg),
Op(DW_OP_not),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const8u), U64(0x8000_0000_0000_0000),
Op(DW_OP_const1u), U8(63),
Op(DW_OP_shr),
Op(DW_OP_lit1),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const8u), U64(0x8000_0000_0000_0000),
Op(DW_OP_const1u), U8(62),
Op(DW_OP_shra),
Op(DW_OP_plus_uconst), Uleb(2),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit1),
Op(DW_OP_const1u), U8(63),
Op(DW_OP_shl),
Op(DW_OP_const8u), U64(0x8000_0000_0000_0000),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit0),
Op(DW_OP_nop),
Op(DW_OP_skip), Branch(done),
Mark(fail),
Op(DW_OP_lit1),
Mark(done),
Op(DW_OP_stack_value),
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(0),
},
}];
check_eval(&program, Ok(&result), encoding8());
}
#[test]
fn test_eval_compare() {
use self::AssemblerEntry::*;
use crate::constants::*;
let done = 0;
let fail = 1;
#[rustfmt::skip]
let program = [
Op(DW_OP_const1s), U8(1),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_lt),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_const1s), U8(1),
Op(DW_OP_gt),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(1),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_le),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_const1s), U8(1),
Op(DW_OP_ge),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const1s), U8(0xff),
Op(DW_OP_const1s), U8(1),
Op(DW_OP_eq),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_const4s), U32(1),
Op(DW_OP_const1s), U8(1),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit0),
Op(DW_OP_nop),
Op(DW_OP_skip), Branch(done),
Mark(fail),
Op(DW_OP_lit1),
Mark(done),
Op(DW_OP_stack_value),
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(0),
},
}];
check_eval(&program, Ok(&result), encoding4());
}
#[test]
fn test_eval_stack() {
use self::AssemblerEntry::*;
use crate::constants::*;
#[rustfmt::skip]
let program = [
Op(DW_OP_lit17),
Op(DW_OP_dup),
Op(DW_OP_over),
Op(DW_OP_minus),
Op(DW_OP_swap),
Op(DW_OP_dup),
Op(DW_OP_plus_uconst), Uleb(1),
Op(DW_OP_rot),
Op(DW_OP_pick), U8(2),
Op(DW_OP_pick), U8(3),
Op(DW_OP_minus),
Op(DW_OP_drop),
Op(DW_OP_swap),
Op(DW_OP_drop),
Op(DW_OP_minus),
Op(DW_OP_stack_value),
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(1),
},
}];
check_eval(&program, Ok(&result), encoding4());
}
#[test]
fn test_eval_lit_and_reg() {
use self::AssemblerEntry::*;
use crate::constants::*;
let mut program = Vec::new();
program.push(Op(DW_OP_lit0));
for i in 0..32 {
program.push(Op(DwOp(DW_OP_lit0.0 + i)));
program.push(Op(DwOp(DW_OP_breg0.0 + i)));
program.push(Sleb(u64::from(i)));
program.push(Op(DW_OP_plus));
program.push(Op(DW_OP_plus));
}
program.push(Op(DW_OP_bregx));
program.push(Uleb(0x1234));
program.push(Sleb(0x1234));
program.push(Op(DW_OP_plus));
program.push(Op(DW_OP_stack_value));
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(496),
},
}];
check_eval_with_args(
&program,
Ok(&result),
encoding4(),
None,
None,
None,
|eval, mut result| {
while result != EvaluationResult::Complete {
result = eval.resume_with_register(match result {
EvaluationResult::RequiresRegister {
register,
base_type,
} => {
assert_eq!(base_type, UnitOffset(0));
Value::Generic(u64::from(register.0).wrapping_neg())
}
_ => panic!(),
})?;
}
Ok(result)
},
);
}
#[test]
fn test_eval_memory() {
use self::AssemblerEntry::*;
use crate::constants::*;
let done = 0;
let fail = 1;
#[rustfmt::skip]
let program = [
Op(DW_OP_addr), U32(0x7fff_ffff),
Op(DW_OP_deref),
Op(DW_OP_const4u), U32(0xffff_fffc),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_addr), U32(0x7fff_ffff),
Op(DW_OP_deref_size), U8(2),
Op(DW_OP_const4u), U32(0xfffc),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit1),
Op(DW_OP_addr), U32(0x7fff_ffff),
Op(DW_OP_xderef),
Op(DW_OP_const4u), U32(0xffff_fffd),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit1),
Op(DW_OP_addr), U32(0x7fff_ffff),
Op(DW_OP_xderef_size), U8(2),
Op(DW_OP_const4u), U32(0xfffd),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit17),
Op(DW_OP_form_tls_address),
Op(DW_OP_constu), Uleb(!17),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit17),
Op(DW_OP_GNU_push_tls_address),
Op(DW_OP_constu), Uleb(!17),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_addrx), Uleb(0x10),
Op(DW_OP_deref),
Op(DW_OP_const4u), U32(0x4040),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_constx), Uleb(17),
Op(DW_OP_form_tls_address),
Op(DW_OP_constu), Uleb(!27),
Op(DW_OP_ne),
Op(DW_OP_bra), Branch(fail),
Op(DW_OP_lit0),
Op(DW_OP_nop),
Op(DW_OP_skip), Branch(done),
Mark(fail),
Op(DW_OP_lit1),
Mark(done),
Op(DW_OP_stack_value),
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(0),
},
}];
check_eval_with_args(
&program,
Ok(&result),
encoding4(),
None,
None,
None,
|eval, mut result| {
while result != EvaluationResult::Complete {
result = match result {
EvaluationResult::RequiresMemory {
address,
size,
space,
base_type,
} => {
assert_eq!(base_type, UnitOffset(0));
let mut v = address << 2;
if let Some(value) = space {
v += value;
}
v &= (1u64 << (8 * size)) - 1;
eval.resume_with_memory(Value::Generic(v))?
}
EvaluationResult::RequiresTls(slot) => eval.resume_with_tls(!slot)?,
EvaluationResult::RequiresRelocatedAddress(address) => {
eval.resume_with_relocated_address(address)?
}
EvaluationResult::RequiresIndexedAddress { index, relocate } => {
if relocate {
eval.resume_with_indexed_address(0x1000 + index.0 as u64)?
} else {
eval.resume_with_indexed_address(10 + index.0 as u64)?
}
}
_ => panic!(),
};
}
Ok(result)
},
);
}
#[test]
fn test_eval_register() {
use self::AssemblerEntry::*;
use crate::constants::*;
for i in 0..32 {
#[rustfmt::skip]
let program = [
Op(DwOp(DW_OP_reg0.0 + i)),
Op(DW_OP_lit23),
];
let ok_result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Register {
register: Register(i.into()),
},
}];
check_eval(&program[..1], Ok(&ok_result), encoding4());
check_eval(
&program,
Err(Error::InvalidExpressionTerminator(1)),
encoding4(),
);
}
#[rustfmt::skip]
let program = [
Op(DW_OP_regx), Uleb(0x1234)
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Register {
register: Register(0x1234),
},
}];
check_eval(&program, Ok(&result), encoding4());
}
#[test]
fn test_eval_context() {
use self::AssemblerEntry::*;
use crate::constants::*;
#[rustfmt::skip]
let program = [
Op(DW_OP_fbreg), Sleb((-8i8) as u64),
Op(DW_OP_call_frame_cfa),
Op(DW_OP_plus),
Op(DW_OP_neg),
Op(DW_OP_stack_value)
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(9),
},
}];
check_eval_with_args(
&program,
Ok(&result),
encoding8(),
None,
None,
None,
|eval, result| {
match result {
EvaluationResult::RequiresFrameBase => {}
_ => panic!(),
};
match eval.resume_with_frame_base(0x0123_4567_89ab_cdef)? {
EvaluationResult::RequiresCallFrameCfa => {}
_ => panic!(),
};
eval.resume_with_call_frame_cfa(0xfedc_ba98_7654_3210)
},
);
#[rustfmt::skip]
let program = [
Op(DW_OP_entry_value), Uleb(8), U64(0x1234_5678),
Op(DW_OP_stack_value)
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(0x1234_5678),
},
}];
check_eval_with_args(
&program,
Ok(&result),
encoding8(),
None,
None,
None,
|eval, result| {
let entry_value = match result {
EvaluationResult::RequiresEntryValue(mut expression) => {
expression.0.read_u64()?
}
_ => panic!(),
};
eval.resume_with_entry_value(Value::Generic(entry_value))
},
);
#[rustfmt::skip]
let program = [
Op(DW_OP_push_object_address),
];
check_eval_with_args(
&program,
Err(Error::InvalidPushObjectAddress),
encoding4(),
None,
None,
None,
|_, _| panic!(),
);
#[rustfmt::skip]
let program = [
Op(DW_OP_push_object_address),
Op(DW_OP_stack_value),
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(0xff),
},
}];
check_eval_with_args(
&program,
Ok(&result),
encoding8(),
Some(0xff),
None,
None,
|_, result| Ok(result),
);
#[rustfmt::skip]
let program = [
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Address {
address: 0x1234_5678,
},
}];
check_eval_with_args(
&program,
Ok(&result),
encoding8(),
None,
Some(0x1234_5678),
None,
|_, result| Ok(result),
);
}
#[test]
fn test_eval_empty_stack() {
use self::AssemblerEntry::*;
use crate::constants::*;
#[rustfmt::skip]
let program = [
Op(DW_OP_stack_value)
];
check_eval(&program, Err(Error::NotEnoughStackItems), encoding4());
}
#[test]
fn test_eval_call() {
use self::AssemblerEntry::*;
use crate::constants::*;
#[rustfmt::skip]
let program = [
Op(DW_OP_lit23),
Op(DW_OP_call2), U16(0x7755),
Op(DW_OP_call4), U32(0x7755_aaee),
Op(DW_OP_call_ref), U32(0x7755_aaee),
Op(DW_OP_stack_value)
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(23),
},
}];
check_eval_with_args(
&program,
Ok(&result),
encoding4(),
None,
None,
None,
|eval, result| {
let buf = EndianSlice::new(&[], LittleEndian);
match result {
EvaluationResult::RequiresAtLocation(_) => {}
_ => panic!(),
};
eval.resume_with_at_location(buf)?;
match result {
EvaluationResult::RequiresAtLocation(_) => {}
_ => panic!(),
};
eval.resume_with_at_location(buf)?;
match result {
EvaluationResult::RequiresAtLocation(_) => {}
_ => panic!(),
};
eval.resume_with_at_location(buf)
},
);
const SUBR: &[u8] = &[0x32, 0x1e];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value {
value: Value::Generic(184),
},
}];
check_eval_with_args(
&program,
Ok(&result),
encoding4(),
None,
None,
None,
|eval, result| {
let buf = EndianSlice::new(SUBR, LittleEndian);
match result {
EvaluationResult::RequiresAtLocation(_) => {}
_ => panic!(),
};
eval.resume_with_at_location(buf)?;
match result {
EvaluationResult::RequiresAtLocation(_) => {}
_ => panic!(),
};
eval.resume_with_at_location(buf)?;
match result {
EvaluationResult::RequiresAtLocation(_) => {}
_ => panic!(),
};
eval.resume_with_at_location(buf)
},
);
}
#[test]
fn test_eval_pieces() {
use self::AssemblerEntry::*;
use crate::constants::*;
#[rustfmt::skip]
let program = [
Op(DW_OP_reg3),
Op(DW_OP_piece), Uleb(4),
Op(DW_OP_reg4),
Op(DW_OP_piece), Uleb(2),
];
let result = [
Piece {
size_in_bits: Some(32),
bit_offset: None,
location: Location::Register {
register: Register(3),
},
},
Piece {
size_in_bits: Some(16),
bit_offset: None,
location: Location::Register {
register: Register(4),
},
},
];
check_eval(&program, Ok(&result), encoding4());
#[rustfmt::skip]
let program = [
Op(DW_OP_reg0),
Op(DW_OP_piece), Uleb(4),
Op(DW_OP_piece), Uleb(4),
Op(DW_OP_addr), U32(0x7fff_ffff),
Op(DW_OP_piece), Uleb(4),
];
let result = [
Piece {
size_in_bits: Some(32),
bit_offset: None,
location: Location::Register {
register: Register(0),
},
},
Piece {
size_in_bits: Some(32),
bit_offset: None,
location: Location::Empty,
},
Piece {
size_in_bits: Some(32),
bit_offset: None,
location: Location::Address {
address: 0x7fff_ffff,
},
},
];
check_eval_with_args(
&program,
Ok(&result),
encoding4(),
None,
None,
None,
|eval, mut result| {
while result != EvaluationResult::Complete {
result = match result {
EvaluationResult::RequiresRelocatedAddress(address) => {
eval.resume_with_relocated_address(address)?
}
_ => panic!(),
};
}
Ok(result)
},
);
#[rustfmt::skip]
let program = [
Op(DW_OP_implicit_value), Uleb(5),
U8(23), U8(24), U8(25), U8(26), U8(0),
];
const BYTES: &[u8] = &[23, 24, 25, 26, 0];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Bytes {
value: EndianSlice::new(BYTES, LittleEndian),
},
}];
check_eval(&program, Ok(&result), encoding4());
#[rustfmt::skip]
let program = [
Op(DW_OP_lit7),
Op(DW_OP_stack_value),
Op(DW_OP_bit_piece), Uleb(5), Uleb(0),
Op(DW_OP_bit_piece), Uleb(3), Uleb(0),
];
let result = [
Piece {
size_in_bits: Some(5),
bit_offset: Some(0),
location: Location::Value {
value: Value::Generic(7),
},
},
Piece {
size_in_bits: Some(3),
bit_offset: Some(0),
location: Location::Empty,
},
];
check_eval(&program, Ok(&result), encoding4());
#[rustfmt::skip]
let program = [
Op(DW_OP_lit7),
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Address { address: 7 },
}];
check_eval(&program, Ok(&result), encoding4());
#[rustfmt::skip]
let program = [
Op(DW_OP_implicit_pointer), U32(0x1234_5678), Sleb(0x123),
];
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::ImplicitPointer {
value: DebugInfoOffset(0x1234_5678),
byte_offset: 0x123,
},
}];
check_eval(&program, Ok(&result), encoding4());
#[rustfmt::skip]
let program = [
Op(DW_OP_reg3),
Op(DW_OP_piece), Uleb(4),
Op(DW_OP_reg4),
];
check_eval(&program, Err(Error::InvalidPiece), encoding4());
#[rustfmt::skip]
let program = [
Op(DW_OP_reg3),
Op(DW_OP_piece), Uleb(4),
Op(DW_OP_lit0),
];
check_eval(&program, Err(Error::InvalidPiece), encoding4());
}
#[test]
fn test_eval_max_iterations() {
use self::AssemblerEntry::*;
use crate::constants::*;
#[rustfmt::skip]
let program = [
Mark(1),
Op(DW_OP_skip), Branch(1),
];
check_eval_with_args(
&program,
Err(Error::TooManyIterations),
encoding4(),
None,
None,
Some(150),
|_, _| panic!(),
);
}
#[test]
fn test_eval_typed_stack() {
use self::AssemblerEntry::*;
use crate::constants::*;
let base_types = [
ValueType::Generic,
ValueType::U16,
ValueType::U32,
ValueType::F32,
];
#[rustfmt::skip]
let tests = [
(
&[
Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234),
Op(DW_OP_stack_value),
][..],
Value::U16(0x1234),
),
(
&[
Op(DW_OP_regval_type), Uleb(0x1234), Uleb(1),
Op(DW_OP_stack_value),
][..],
Value::U16(0x2340),
),
(
&[
Op(DW_OP_addr), U32(0x7fff_ffff),
Op(DW_OP_deref_type), U8(2), Uleb(1),
Op(DW_OP_stack_value),
][..],
Value::U16(0xfff0),
),
(
&[
Op(DW_OP_lit1),
Op(DW_OP_addr), U32(0x7fff_ffff),
Op(DW_OP_xderef_type), U8(2), Uleb(1),
Op(DW_OP_stack_value),
][..],
Value::U16(0xfff1),
),
(
&[
Op(DW_OP_const_type), Uleb(1), U8(2), U16(0x1234),
Op(DW_OP_convert), Uleb(2),
Op(DW_OP_stack_value),
][..],
Value::U32(0x1234),
),
(
&[
Op(DW_OP_const_type), Uleb(2), U8(4), U32(0x3f80_0000),
Op(DW_OP_reinterpret), Uleb(3),
Op(DW_OP_stack_value),
][..],
Value::F32(1.0),
),
];
for &(program, value) in &tests {
let result = [Piece {
size_in_bits: None,
bit_offset: None,
location: Location::Value { value },
}];
check_eval_with_args(
program,
Ok(&result),
encoding4(),
None,
None,
None,
|eval, mut result| {
while result != EvaluationResult::Complete {
result = match result {
EvaluationResult::RequiresMemory {
address,
size,
space,
base_type,
} => {
let mut v = address << 4;
if let Some(value) = space {
v += value;
}
v &= (1u64 << (8 * size)) - 1;
let v = Value::from_u64(base_types[base_type.0], v)?;
eval.resume_with_memory(v)?
}
EvaluationResult::RequiresRegister {
register,
base_type,
} => {
let v = Value::from_u64(
base_types[base_type.0],
u64::from(register.0) << 4,
)?;
eval.resume_with_register(v)?
}
EvaluationResult::RequiresBaseType(offset) => {
eval.resume_with_base_type(base_types[offset.0])?
}
EvaluationResult::RequiresRelocatedAddress(address) => {
eval.resume_with_relocated_address(address)?
}
_ => panic!("Unexpected result {:?}", result),
}
}
Ok(result)
},
);
}
}
}