1618 lines
70 KiB
Rust
1618 lines
70 KiB
Rust
use alloc::boxed::Box;
|
|
use alloc::vec::Vec;
|
|
|
|
use crate::common::{Encoding, Register};
|
|
use crate::constants::{self, DwOp};
|
|
use crate::leb128::write::{sleb128_size, uleb128_size};
|
|
use crate::write::{
|
|
Address, DebugInfoReference, Error, Reference, Result, UnitEntryId, UnitOffsets, Writer,
|
|
};
|
|
|
|
/// The bytecode for a DWARF expression or location description.
|
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
|
|
pub struct Expression {
|
|
operations: Vec<Operation>,
|
|
}
|
|
|
|
impl Expression {
|
|
/// Create an empty expression.
|
|
#[inline]
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// Create an expression from raw bytecode.
|
|
///
|
|
/// This does not support operations that require references, such as `DW_OP_addr`.
|
|
#[inline]
|
|
pub fn raw(bytecode: Vec<u8>) -> Self {
|
|
Expression {
|
|
operations: vec![Operation::Raw(bytecode)],
|
|
}
|
|
}
|
|
|
|
/// Add an operation to the expression.
|
|
///
|
|
/// This should only be used for operations that have no explicit operands.
|
|
pub fn op(&mut self, opcode: DwOp) {
|
|
self.operations.push(Operation::Simple(opcode));
|
|
}
|
|
|
|
/// Add a `DW_OP_addr` operation to the expression.
|
|
pub fn op_addr(&mut self, address: Address) {
|
|
self.operations.push(Operation::Address(address));
|
|
}
|
|
|
|
/// Add a `DW_OP_constu` operation to the expression.
|
|
///
|
|
/// This may be emitted as a smaller equivalent operation.
|
|
pub fn op_constu(&mut self, value: u64) {
|
|
self.operations.push(Operation::UnsignedConstant(value));
|
|
}
|
|
|
|
/// Add a `DW_OP_consts` operation to the expression.
|
|
///
|
|
/// This may be emitted as a smaller equivalent operation.
|
|
pub fn op_consts(&mut self, value: i64) {
|
|
self.operations.push(Operation::SignedConstant(value));
|
|
}
|
|
|
|
/// Add a `DW_OP_const_type` or `DW_OP_GNU_const_type` operation to the expression.
|
|
pub fn op_const_type(&mut self, base: UnitEntryId, value: Box<[u8]>) {
|
|
self.operations.push(Operation::ConstantType(base, value));
|
|
}
|
|
|
|
/// Add a `DW_OP_fbreg` operation to the expression.
|
|
pub fn op_fbreg(&mut self, offset: i64) {
|
|
self.operations.push(Operation::FrameOffset(offset));
|
|
}
|
|
|
|
/// Add a `DW_OP_bregx` operation to the expression.
|
|
///
|
|
/// This may be emitted as a smaller equivalent operation.
|
|
pub fn op_breg(&mut self, register: Register, offset: i64) {
|
|
self.operations
|
|
.push(Operation::RegisterOffset(register, offset));
|
|
}
|
|
|
|
/// Add a `DW_OP_regval_type` or `DW_OP_GNU_regval_type` operation to the expression.
|
|
///
|
|
/// This may be emitted as a smaller equivalent operation.
|
|
pub fn op_regval_type(&mut self, register: Register, base: UnitEntryId) {
|
|
self.operations
|
|
.push(Operation::RegisterType(register, base));
|
|
}
|
|
|
|
/// Add a `DW_OP_pick` operation to the expression.
|
|
///
|
|
/// This may be emitted as a `DW_OP_dup` or `DW_OP_over` operation.
|
|
pub fn op_pick(&mut self, index: u8) {
|
|
self.operations.push(Operation::Pick(index));
|
|
}
|
|
|
|
/// Add a `DW_OP_deref` operation to the expression.
|
|
pub fn op_deref(&mut self) {
|
|
self.operations.push(Operation::Deref { space: false });
|
|
}
|
|
|
|
/// Add a `DW_OP_xderef` operation to the expression.
|
|
pub fn op_xderef(&mut self) {
|
|
self.operations.push(Operation::Deref { space: true });
|
|
}
|
|
|
|
/// Add a `DW_OP_deref_size` operation to the expression.
|
|
pub fn op_deref_size(&mut self, size: u8) {
|
|
self.operations
|
|
.push(Operation::DerefSize { size, space: false });
|
|
}
|
|
|
|
/// Add a `DW_OP_xderef_size` operation to the expression.
|
|
pub fn op_xderef_size(&mut self, size: u8) {
|
|
self.operations
|
|
.push(Operation::DerefSize { size, space: true });
|
|
}
|
|
|
|
/// Add a `DW_OP_deref_type` or `DW_OP_GNU_deref_type` operation to the expression.
|
|
pub fn op_deref_type(&mut self, size: u8, base: UnitEntryId) {
|
|
self.operations.push(Operation::DerefType {
|
|
size,
|
|
base,
|
|
space: false,
|
|
});
|
|
}
|
|
|
|
/// Add a `DW_OP_xderef_type` operation to the expression.
|
|
pub fn op_xderef_type(&mut self, size: u8, base: UnitEntryId) {
|
|
self.operations.push(Operation::DerefType {
|
|
size,
|
|
base,
|
|
space: true,
|
|
});
|
|
}
|
|
|
|
/// Add a `DW_OP_plus_uconst` operation to the expression.
|
|
pub fn op_plus_uconst(&mut self, value: u64) {
|
|
self.operations.push(Operation::PlusConstant(value));
|
|
}
|
|
|
|
/// Add a `DW_OP_skip` operation to the expression.
|
|
///
|
|
/// Returns the index of the operation. The caller must call `set_target` with
|
|
/// this index to set the target of the branch.
|
|
pub fn op_skip(&mut self) -> usize {
|
|
let index = self.next_index();
|
|
self.operations.push(Operation::Skip(!0));
|
|
index
|
|
}
|
|
|
|
/// Add a `DW_OP_bra` operation to the expression.
|
|
///
|
|
/// Returns the index of the operation. The caller must call `set_target` with
|
|
/// this index to set the target of the branch.
|
|
pub fn op_bra(&mut self) -> usize {
|
|
let index = self.next_index();
|
|
self.operations.push(Operation::Branch(!0));
|
|
index
|
|
}
|
|
|
|
/// Return the index that will be assigned to the next operation.
|
|
///
|
|
/// This can be passed to `set_target`.
|
|
#[inline]
|
|
pub fn next_index(&self) -> usize {
|
|
self.operations.len()
|
|
}
|
|
|
|
/// Set the target of a `DW_OP_skip` or `DW_OP_bra` operation .
|
|
pub fn set_target(&mut self, operation: usize, new_target: usize) {
|
|
debug_assert!(new_target <= self.next_index());
|
|
debug_assert_ne!(operation, new_target);
|
|
match self.operations[operation] {
|
|
Operation::Skip(ref mut target) | Operation::Branch(ref mut target) => {
|
|
*target = new_target;
|
|
}
|
|
_ => unimplemented!(),
|
|
}
|
|
}
|
|
|
|
/// Add a `DW_OP_call4` operation to the expression.
|
|
pub fn op_call(&mut self, entry: UnitEntryId) {
|
|
self.operations.push(Operation::Call(entry));
|
|
}
|
|
|
|
/// Add a `DW_OP_call_ref` operation to the expression.
|
|
pub fn op_call_ref(&mut self, entry: Reference) {
|
|
self.operations.push(Operation::CallRef(entry));
|
|
}
|
|
|
|
/// Add a `DW_OP_convert` or `DW_OP_GNU_convert` operation to the expression.
|
|
///
|
|
/// `base` is the DIE of the base type, or `None` for the generic type.
|
|
pub fn op_convert(&mut self, base: Option<UnitEntryId>) {
|
|
self.operations.push(Operation::Convert(base));
|
|
}
|
|
|
|
/// Add a `DW_OP_reinterpret` or `DW_OP_GNU_reinterpret` operation to the expression.
|
|
///
|
|
/// `base` is the DIE of the base type, or `None` for the generic type.
|
|
pub fn op_reinterpret(&mut self, base: Option<UnitEntryId>) {
|
|
self.operations.push(Operation::Reinterpret(base));
|
|
}
|
|
|
|
/// Add a `DW_OP_entry_value` or `DW_OP_GNU_entry_value` operation to the expression.
|
|
pub fn op_entry_value(&mut self, expression: Expression) {
|
|
self.operations.push(Operation::EntryValue(expression));
|
|
}
|
|
|
|
/// Add a `DW_OP_regx` operation to the expression.
|
|
///
|
|
/// This may be emitted as a smaller equivalent operation.
|
|
pub fn op_reg(&mut self, register: Register) {
|
|
self.operations.push(Operation::Register(register));
|
|
}
|
|
|
|
/// Add a `DW_OP_implicit_value` operation to the expression.
|
|
pub fn op_implicit_value(&mut self, data: Box<[u8]>) {
|
|
self.operations.push(Operation::ImplicitValue(data));
|
|
}
|
|
|
|
/// Add a `DW_OP_implicit_pointer` or `DW_OP_GNU_implicit_pointer` operation to the expression.
|
|
pub fn op_implicit_pointer(&mut self, entry: Reference, byte_offset: i64) {
|
|
self.operations
|
|
.push(Operation::ImplicitPointer { entry, byte_offset });
|
|
}
|
|
|
|
/// Add a `DW_OP_piece` operation to the expression.
|
|
pub fn op_piece(&mut self, size_in_bytes: u64) {
|
|
self.operations.push(Operation::Piece { size_in_bytes });
|
|
}
|
|
|
|
/// Add a `DW_OP_bit_piece` operation to the expression.
|
|
pub fn op_bit_piece(&mut self, size_in_bits: u64, bit_offset: u64) {
|
|
self.operations.push(Operation::BitPiece {
|
|
size_in_bits,
|
|
bit_offset,
|
|
});
|
|
}
|
|
|
|
/// Add a `DW_OP_GNU_parameter_ref` operation to the expression.
|
|
pub fn op_gnu_parameter_ref(&mut self, entry: UnitEntryId) {
|
|
self.operations.push(Operation::ParameterRef(entry));
|
|
}
|
|
|
|
/// Add a `DW_OP_WASM_location 0x0` operation to the expression.
|
|
pub fn op_wasm_local(&mut self, index: u32) {
|
|
self.operations.push(Operation::WasmLocal(index));
|
|
}
|
|
|
|
/// Add a `DW_OP_WASM_location 0x1` operation to the expression.
|
|
pub fn op_wasm_global(&mut self, index: u32) {
|
|
self.operations.push(Operation::WasmGlobal(index));
|
|
}
|
|
|
|
/// Add a `DW_OP_WASM_location 0x2` operation to the expression.
|
|
pub fn op_wasm_stack(&mut self, index: u32) {
|
|
self.operations.push(Operation::WasmStack(index));
|
|
}
|
|
|
|
pub(crate) fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize {
|
|
let mut size = 0;
|
|
for operation in &self.operations {
|
|
size += operation.size(encoding, unit_offsets);
|
|
}
|
|
size
|
|
}
|
|
|
|
pub(crate) fn write<W: Writer>(
|
|
&self,
|
|
w: &mut W,
|
|
mut refs: Option<&mut Vec<DebugInfoReference>>,
|
|
encoding: Encoding,
|
|
unit_offsets: Option<&UnitOffsets>,
|
|
) -> Result<()> {
|
|
// TODO: only calculate offsets if needed?
|
|
let mut offsets = Vec::with_capacity(self.operations.len());
|
|
let mut offset = w.len();
|
|
for operation in &self.operations {
|
|
offsets.push(offset);
|
|
offset += operation.size(encoding, unit_offsets);
|
|
}
|
|
offsets.push(offset);
|
|
for (operation, offset) in self.operations.iter().zip(offsets.iter().copied()) {
|
|
debug_assert_eq!(w.len(), offset);
|
|
operation.write(w, refs.as_deref_mut(), encoding, unit_offsets, &offsets)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// A single DWARF operation.
|
|
//
|
|
// This type is intentionally not public so that we can change the
|
|
// representation of expressions as needed.
|
|
//
|
|
// Variants are listed in the order they appear in Section 2.5.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
enum Operation {
|
|
/// Raw bytecode.
|
|
///
|
|
/// Does not support references.
|
|
Raw(Vec<u8>),
|
|
/// An operation that has no explicit operands.
|
|
///
|
|
/// Represents:
|
|
/// - `DW_OP_drop`, `DW_OP_swap`, `DW_OP_rot`
|
|
/// - `DW_OP_push_object_address`, `DW_OP_form_tls_address`, `DW_OP_call_frame_cfa`
|
|
/// - `DW_OP_abs`, `DW_OP_and`, `DW_OP_div`, `DW_OP_minus`, `DW_OP_mod`, `DW_OP_mul`,
|
|
/// `DW_OP_neg`, `DW_OP_not`, `DW_OP_or`, `DW_OP_plus`, `DW_OP_shl`, `DW_OP_shr`,
|
|
/// `DW_OP_shra`, `DW_OP_xor`
|
|
/// - `DW_OP_le`, `DW_OP_ge`, `DW_OP_eq`, `DW_OP_lt`, `DW_OP_gt`, `DW_OP_ne`
|
|
/// - `DW_OP_nop`
|
|
/// - `DW_OP_stack_value`
|
|
Simple(DwOp),
|
|
/// Relocate the address if needed, and push it on the stack.
|
|
///
|
|
/// Represents `DW_OP_addr`.
|
|
Address(Address),
|
|
/// Push an unsigned constant value on the stack.
|
|
///
|
|
/// Represents `DW_OP_constu`.
|
|
UnsignedConstant(u64),
|
|
/// Push a signed constant value on the stack.
|
|
///
|
|
/// Represents `DW_OP_consts`.
|
|
SignedConstant(i64),
|
|
/* TODO: requires .debug_addr write support
|
|
/// 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(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(DebugAddrIndex<Offset>),
|
|
*/
|
|
/// Interpret the value bytes as a constant of a given type, and push it on the stack.
|
|
///
|
|
/// Represents `DW_OP_const_type`.
|
|
ConstantType(UnitEntryId, Box<[u8]>),
|
|
/// Compute the frame base (using `DW_AT_frame_base`), add the
|
|
/// given offset, and then push the resulting sum on the stack.
|
|
///
|
|
/// Represents `DW_OP_fbreg`.
|
|
FrameOffset(i64),
|
|
/// Find the contents of the given register, add the offset, and then
|
|
/// push the resulting sum on the stack.
|
|
///
|
|
/// Represents `DW_OP_bregx`.
|
|
RegisterOffset(Register, i64),
|
|
/// Interpret the contents of the given register as a value of the given type,
|
|
/// and push it on the stack.
|
|
///
|
|
/// Represents `DW_OP_regval_type`.
|
|
RegisterType(Register, UnitEntryId),
|
|
/// Copy the item at a stack index and push it on top of the stack.
|
|
///
|
|
/// Represents `DW_OP_pick`, `DW_OP_dup`, and `DW_OP_over`.
|
|
Pick(u8),
|
|
/// Pop the topmost value of the stack, dereference it, and push the
|
|
/// resulting value.
|
|
///
|
|
/// Represents `DW_OP_deref` and `DW_OP_xderef`.
|
|
Deref {
|
|
/// True if the dereference operation takes an address space
|
|
/// argument from the stack; false otherwise.
|
|
space: bool,
|
|
},
|
|
/// Pop the topmost value of the stack, dereference it to obtain a value
|
|
/// of the given size, and push the resulting value.
|
|
///
|
|
/// Represents `DW_OP_deref_size` and `DW_OP_xderef_size`.
|
|
DerefSize {
|
|
/// True if the dereference operation takes an address space
|
|
/// argument from the stack; false otherwise.
|
|
space: bool,
|
|
/// The size of the data to dereference.
|
|
size: u8,
|
|
},
|
|
/// Pop the topmost value of the stack, dereference it to obtain a value
|
|
/// of the given type, and push the resulting value.
|
|
///
|
|
/// Represents `DW_OP_deref_type` and `DW_OP_xderef_type`.
|
|
DerefType {
|
|
/// True if the dereference operation takes an address space
|
|
/// argument from the stack; false otherwise.
|
|
space: bool,
|
|
/// The size of the data to dereference.
|
|
size: u8,
|
|
/// The DIE of the base type, or `None` for the generic type.
|
|
base: UnitEntryId,
|
|
},
|
|
/// Add an unsigned constant to the topmost value on the stack.
|
|
///
|
|
/// Represents `DW_OP_plus_uconst`.
|
|
PlusConstant(u64),
|
|
/// Unconditional branch to the target location.
|
|
///
|
|
/// The value is the index within the expression of the operation to branch to.
|
|
/// This will be converted to a relative offset when writing.
|
|
///
|
|
/// Represents `DW_OP_skip`.
|
|
Skip(usize),
|
|
/// Branch to the target location if the top of stack is nonzero.
|
|
///
|
|
/// The value is the index within the expression of the operation to branch to.
|
|
/// This will be converted to a relative offset when writing.
|
|
///
|
|
/// Represents `DW_OP_bra`.
|
|
Branch(usize),
|
|
/// Evaluate a DWARF expression as a subroutine.
|
|
///
|
|
/// The expression comes from the `DW_AT_location` attribute of the indicated DIE.
|
|
///
|
|
/// Represents `DW_OP_call4`.
|
|
Call(UnitEntryId),
|
|
/// Evaluate an external DWARF expression as a subroutine.
|
|
///
|
|
/// The expression comes from the `DW_AT_location` attribute of the indicated DIE,
|
|
/// which may be in another compilation unit or shared object.
|
|
///
|
|
/// Represents `DW_OP_call_ref`.
|
|
CallRef(Reference),
|
|
/// Pop the top stack entry, convert it to a different type, and push it on the stack.
|
|
///
|
|
/// Represents `DW_OP_convert`.
|
|
Convert(Option<UnitEntryId>),
|
|
/// 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(Option<UnitEntryId>),
|
|
/// Evaluate an expression at the entry to the current subprogram, and push it on the stack.
|
|
///
|
|
/// Represents `DW_OP_entry_value`.
|
|
EntryValue(Expression),
|
|
// FIXME: EntryRegister
|
|
/// Indicate that this piece's location is in the given register.
|
|
///
|
|
/// Completes the piece or expression.
|
|
///
|
|
/// Represents `DW_OP_regx`.
|
|
Register(Register),
|
|
/// The object has no location, but has a known constant value.
|
|
///
|
|
/// Completes the piece or expression.
|
|
///
|
|
/// Represents `DW_OP_implicit_value`.
|
|
ImplicitValue(Box<[u8]>),
|
|
/// The object is a pointer to a value which has no actual location, such as
|
|
/// an implicit value or a stack value.
|
|
///
|
|
/// Completes the piece or expression.
|
|
///
|
|
/// Represents `DW_OP_implicit_pointer`.
|
|
ImplicitPointer {
|
|
/// The DIE of the value that this is an implicit pointer into.
|
|
entry: Reference,
|
|
/// The byte offset into the value that the implicit pointer points to.
|
|
byte_offset: i64,
|
|
},
|
|
/// Terminate a piece.
|
|
///
|
|
/// Represents `DW_OP_piece`.
|
|
Piece {
|
|
/// The size of this piece in bytes.
|
|
size_in_bytes: u64,
|
|
},
|
|
/// Terminate a piece with a size in bits.
|
|
///
|
|
/// Represents `DW_OP_bit_piece`.
|
|
BitPiece {
|
|
/// The size of this piece in bits.
|
|
size_in_bits: u64,
|
|
/// The bit offset of this piece.
|
|
bit_offset: u64,
|
|
},
|
|
/// This represents a parameter that was optimized out.
|
|
///
|
|
/// The entry is 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(UnitEntryId),
|
|
/// The index of a local in the currently executing function.
|
|
///
|
|
/// Represents `DW_OP_WASM_location 0x00`.
|
|
WasmLocal(u32),
|
|
/// The index of a global.
|
|
///
|
|
/// Represents `DW_OP_WASM_location 0x01`.
|
|
WasmGlobal(u32),
|
|
/// The index of an item on the operand stack.
|
|
///
|
|
/// Represents `DW_OP_WASM_location 0x02`.
|
|
WasmStack(u32),
|
|
}
|
|
|
|
impl Operation {
|
|
fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize {
|
|
let base_size = |base| {
|
|
// Errors are handled during writes.
|
|
match unit_offsets {
|
|
Some(offsets) => uleb128_size(offsets.unit_offset(base)),
|
|
None => 0,
|
|
}
|
|
};
|
|
1 + match *self {
|
|
Operation::Raw(ref bytecode) => return bytecode.len(),
|
|
Operation::Simple(_) => 0,
|
|
Operation::Address(_) => encoding.address_size as usize,
|
|
Operation::UnsignedConstant(value) => {
|
|
if value < 32 {
|
|
0
|
|
} else {
|
|
uleb128_size(value)
|
|
}
|
|
}
|
|
Operation::SignedConstant(value) => sleb128_size(value),
|
|
Operation::ConstantType(base, ref value) => base_size(base) + 1 + value.len(),
|
|
Operation::FrameOffset(offset) => sleb128_size(offset),
|
|
Operation::RegisterOffset(register, offset) => {
|
|
if register.0 < 32 {
|
|
sleb128_size(offset)
|
|
} else {
|
|
uleb128_size(register.0.into()) + sleb128_size(offset)
|
|
}
|
|
}
|
|
Operation::RegisterType(register, base) => {
|
|
uleb128_size(register.0.into()) + base_size(base)
|
|
}
|
|
Operation::Pick(index) => {
|
|
if index > 1 {
|
|
1
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
Operation::Deref { .. } => 0,
|
|
Operation::DerefSize { .. } => 1,
|
|
Operation::DerefType { base, .. } => 1 + base_size(base),
|
|
Operation::PlusConstant(value) => uleb128_size(value),
|
|
Operation::Skip(_) => 2,
|
|
Operation::Branch(_) => 2,
|
|
Operation::Call(_) => 4,
|
|
Operation::CallRef(_) => encoding.format.word_size() as usize,
|
|
Operation::Convert(base) => match base {
|
|
Some(base) => base_size(base),
|
|
None => 1,
|
|
},
|
|
Operation::Reinterpret(base) => match base {
|
|
Some(base) => base_size(base),
|
|
None => 1,
|
|
},
|
|
Operation::EntryValue(ref expression) => {
|
|
let length = expression.size(encoding, unit_offsets);
|
|
uleb128_size(length as u64) + length
|
|
}
|
|
Operation::Register(register) => {
|
|
if register.0 < 32 {
|
|
0
|
|
} else {
|
|
uleb128_size(register.0.into())
|
|
}
|
|
}
|
|
Operation::ImplicitValue(ref data) => uleb128_size(data.len() as u64) + data.len(),
|
|
Operation::ImplicitPointer { byte_offset, .. } => {
|
|
encoding.format.word_size() as usize + sleb128_size(byte_offset)
|
|
}
|
|
Operation::Piece { size_in_bytes } => uleb128_size(size_in_bytes),
|
|
Operation::BitPiece {
|
|
size_in_bits,
|
|
bit_offset,
|
|
} => uleb128_size(size_in_bits) + uleb128_size(bit_offset),
|
|
Operation::ParameterRef(_) => 4,
|
|
Operation::WasmLocal(index)
|
|
| Operation::WasmGlobal(index)
|
|
| Operation::WasmStack(index) => 1 + uleb128_size(index.into()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn write<W: Writer>(
|
|
&self,
|
|
w: &mut W,
|
|
refs: Option<&mut Vec<DebugInfoReference>>,
|
|
encoding: Encoding,
|
|
unit_offsets: Option<&UnitOffsets>,
|
|
offsets: &[usize],
|
|
) -> Result<()> {
|
|
let entry_offset = |entry| match unit_offsets {
|
|
Some(offsets) => {
|
|
let offset = offsets.unit_offset(entry);
|
|
if offset == 0 {
|
|
Err(Error::UnsupportedExpressionForwardReference)
|
|
} else {
|
|
Ok(offset)
|
|
}
|
|
}
|
|
None => Err(Error::UnsupportedCfiExpressionReference),
|
|
};
|
|
match *self {
|
|
Operation::Raw(ref bytecode) => w.write(bytecode)?,
|
|
Operation::Simple(opcode) => w.write_u8(opcode.0)?,
|
|
Operation::Address(address) => {
|
|
w.write_u8(constants::DW_OP_addr.0)?;
|
|
w.write_address(address, encoding.address_size)?;
|
|
}
|
|
Operation::UnsignedConstant(value) => {
|
|
if value < 32 {
|
|
w.write_u8(constants::DW_OP_lit0.0 + value as u8)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_constu.0)?;
|
|
w.write_uleb128(value)?;
|
|
}
|
|
}
|
|
Operation::SignedConstant(value) => {
|
|
w.write_u8(constants::DW_OP_consts.0)?;
|
|
w.write_sleb128(value)?;
|
|
}
|
|
Operation::ConstantType(base, ref value) => {
|
|
if encoding.version >= 5 {
|
|
w.write_u8(constants::DW_OP_const_type.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_GNU_const_type.0)?;
|
|
}
|
|
w.write_uleb128(entry_offset(base)?)?;
|
|
w.write_udata(value.len() as u64, 1)?;
|
|
w.write(value)?;
|
|
}
|
|
Operation::FrameOffset(offset) => {
|
|
w.write_u8(constants::DW_OP_fbreg.0)?;
|
|
w.write_sleb128(offset)?;
|
|
}
|
|
Operation::RegisterOffset(register, offset) => {
|
|
if register.0 < 32 {
|
|
w.write_u8(constants::DW_OP_breg0.0 + register.0 as u8)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_bregx.0)?;
|
|
w.write_uleb128(register.0.into())?;
|
|
}
|
|
w.write_sleb128(offset)?;
|
|
}
|
|
Operation::RegisterType(register, base) => {
|
|
if encoding.version >= 5 {
|
|
w.write_u8(constants::DW_OP_regval_type.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_GNU_regval_type.0)?;
|
|
}
|
|
w.write_uleb128(register.0.into())?;
|
|
w.write_uleb128(entry_offset(base)?)?;
|
|
}
|
|
Operation::Pick(index) => match index {
|
|
0 => w.write_u8(constants::DW_OP_dup.0)?,
|
|
1 => w.write_u8(constants::DW_OP_over.0)?,
|
|
_ => {
|
|
w.write_u8(constants::DW_OP_pick.0)?;
|
|
w.write_u8(index)?;
|
|
}
|
|
},
|
|
Operation::Deref { space } => {
|
|
if space {
|
|
w.write_u8(constants::DW_OP_xderef.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_deref.0)?;
|
|
}
|
|
}
|
|
Operation::DerefSize { space, size } => {
|
|
if space {
|
|
w.write_u8(constants::DW_OP_xderef_size.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_deref_size.0)?;
|
|
}
|
|
w.write_u8(size)?;
|
|
}
|
|
Operation::DerefType { space, size, base } => {
|
|
if space {
|
|
w.write_u8(constants::DW_OP_xderef_type.0)?;
|
|
} else {
|
|
if encoding.version >= 5 {
|
|
w.write_u8(constants::DW_OP_deref_type.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_GNU_deref_type.0)?;
|
|
}
|
|
}
|
|
w.write_u8(size)?;
|
|
w.write_uleb128(entry_offset(base)?)?;
|
|
}
|
|
Operation::PlusConstant(value) => {
|
|
w.write_u8(constants::DW_OP_plus_uconst.0)?;
|
|
w.write_uleb128(value)?;
|
|
}
|
|
Operation::Skip(target) => {
|
|
w.write_u8(constants::DW_OP_skip.0)?;
|
|
let offset = offsets[target] as i64 - (w.len() as i64 + 2);
|
|
w.write_sdata(offset, 2)?;
|
|
}
|
|
Operation::Branch(target) => {
|
|
w.write_u8(constants::DW_OP_bra.0)?;
|
|
let offset = offsets[target] as i64 - (w.len() as i64 + 2);
|
|
w.write_sdata(offset, 2)?;
|
|
}
|
|
Operation::Call(entry) => {
|
|
w.write_u8(constants::DW_OP_call4.0)?;
|
|
// TODO: this probably won't work in practice, because we may
|
|
// only know the offsets of base type DIEs at this point.
|
|
w.write_udata(entry_offset(entry)?, 4)?;
|
|
}
|
|
Operation::CallRef(entry) => {
|
|
w.write_u8(constants::DW_OP_call_ref.0)?;
|
|
let size = encoding.format.word_size();
|
|
match entry {
|
|
Reference::Symbol(symbol) => w.write_reference(symbol, size)?,
|
|
Reference::Entry(unit, entry) => {
|
|
let refs = refs.ok_or(Error::InvalidReference)?;
|
|
refs.push(DebugInfoReference {
|
|
offset: w.len(),
|
|
unit,
|
|
entry,
|
|
size,
|
|
});
|
|
w.write_udata(0, size)?;
|
|
}
|
|
}
|
|
}
|
|
Operation::Convert(base) => {
|
|
if encoding.version >= 5 {
|
|
w.write_u8(constants::DW_OP_convert.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_GNU_convert.0)?;
|
|
}
|
|
match base {
|
|
Some(base) => w.write_uleb128(entry_offset(base)?)?,
|
|
None => w.write_u8(0)?,
|
|
}
|
|
}
|
|
Operation::Reinterpret(base) => {
|
|
if encoding.version >= 5 {
|
|
w.write_u8(constants::DW_OP_reinterpret.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_GNU_reinterpret.0)?;
|
|
}
|
|
match base {
|
|
Some(base) => w.write_uleb128(entry_offset(base)?)?,
|
|
None => w.write_u8(0)?,
|
|
}
|
|
}
|
|
Operation::EntryValue(ref expression) => {
|
|
if encoding.version >= 5 {
|
|
w.write_u8(constants::DW_OP_entry_value.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_GNU_entry_value.0)?;
|
|
}
|
|
let length = expression.size(encoding, unit_offsets);
|
|
w.write_uleb128(length as u64)?;
|
|
expression.write(w, refs, encoding, unit_offsets)?;
|
|
}
|
|
Operation::Register(register) => {
|
|
if register.0 < 32 {
|
|
w.write_u8(constants::DW_OP_reg0.0 + register.0 as u8)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_regx.0)?;
|
|
w.write_uleb128(register.0.into())?;
|
|
}
|
|
}
|
|
Operation::ImplicitValue(ref data) => {
|
|
w.write_u8(constants::DW_OP_implicit_value.0)?;
|
|
w.write_uleb128(data.len() as u64)?;
|
|
w.write(data)?;
|
|
}
|
|
Operation::ImplicitPointer { entry, byte_offset } => {
|
|
if encoding.version >= 5 {
|
|
w.write_u8(constants::DW_OP_implicit_pointer.0)?;
|
|
} else {
|
|
w.write_u8(constants::DW_OP_GNU_implicit_pointer.0)?;
|
|
}
|
|
let size = if encoding.version == 2 {
|
|
encoding.address_size
|
|
} else {
|
|
encoding.format.word_size()
|
|
};
|
|
match entry {
|
|
Reference::Symbol(symbol) => {
|
|
w.write_reference(symbol, size)?;
|
|
}
|
|
Reference::Entry(unit, entry) => {
|
|
let refs = refs.ok_or(Error::InvalidReference)?;
|
|
refs.push(DebugInfoReference {
|
|
offset: w.len(),
|
|
unit,
|
|
entry,
|
|
size,
|
|
});
|
|
w.write_udata(0, size)?;
|
|
}
|
|
}
|
|
w.write_sleb128(byte_offset)?;
|
|
}
|
|
Operation::Piece { size_in_bytes } => {
|
|
w.write_u8(constants::DW_OP_piece.0)?;
|
|
w.write_uleb128(size_in_bytes)?;
|
|
}
|
|
Operation::BitPiece {
|
|
size_in_bits,
|
|
bit_offset,
|
|
} => {
|
|
w.write_u8(constants::DW_OP_bit_piece.0)?;
|
|
w.write_uleb128(size_in_bits)?;
|
|
w.write_uleb128(bit_offset)?;
|
|
}
|
|
Operation::ParameterRef(entry) => {
|
|
w.write_u8(constants::DW_OP_GNU_parameter_ref.0)?;
|
|
w.write_udata(entry_offset(entry)?, 4)?;
|
|
}
|
|
Operation::WasmLocal(index) => {
|
|
w.write(&[constants::DW_OP_WASM_location.0, 0])?;
|
|
w.write_uleb128(index.into())?;
|
|
}
|
|
Operation::WasmGlobal(index) => {
|
|
w.write(&[constants::DW_OP_WASM_location.0, 1])?;
|
|
w.write_uleb128(index.into())?;
|
|
}
|
|
Operation::WasmStack(index) => {
|
|
w.write(&[constants::DW_OP_WASM_location.0, 2])?;
|
|
w.write_uleb128(index.into())?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "read")]
|
|
pub(crate) mod convert {
|
|
use super::*;
|
|
use crate::common::UnitSectionOffset;
|
|
use crate::read::{self, Reader};
|
|
use crate::write::{ConvertError, ConvertResult, UnitEntryId, UnitId};
|
|
use std::collections::HashMap;
|
|
|
|
impl Expression {
|
|
/// Create an expression from the input expression.
|
|
pub fn from<R: Reader<Offset = usize>>(
|
|
from_expression: read::Expression<R>,
|
|
encoding: Encoding,
|
|
dwarf: Option<&read::Dwarf<R>>,
|
|
unit: Option<&read::Unit<R>>,
|
|
entry_ids: Option<&HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>>,
|
|
convert_address: &dyn Fn(u64) -> Option<Address>,
|
|
) -> ConvertResult<Expression> {
|
|
let convert_unit_offset = |offset: read::UnitOffset| -> ConvertResult<_> {
|
|
let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?;
|
|
let unit = unit.ok_or(ConvertError::UnsupportedOperation)?;
|
|
let id = entry_ids
|
|
.get(&offset.to_unit_section_offset(unit))
|
|
.ok_or(ConvertError::InvalidUnitRef)?;
|
|
Ok(id.1)
|
|
};
|
|
let convert_debug_info_offset = |offset| -> ConvertResult<_> {
|
|
// TODO: support relocations
|
|
let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?;
|
|
let id = entry_ids
|
|
.get(&UnitSectionOffset::DebugInfoOffset(offset))
|
|
.ok_or(ConvertError::InvalidDebugInfoRef)?;
|
|
Ok(Reference::Entry(id.0, id.1))
|
|
};
|
|
|
|
// Calculate offsets for use in branch/skip operations.
|
|
let mut offsets = Vec::new();
|
|
let mut offset = 0;
|
|
let mut from_operations = from_expression.clone().operations(encoding);
|
|
while from_operations.next()?.is_some() {
|
|
offsets.push(offset);
|
|
offset = from_operations.offset_from(&from_expression);
|
|
}
|
|
offsets.push(from_expression.0.len());
|
|
|
|
let mut from_operations = from_expression.clone().operations(encoding);
|
|
let mut operations = Vec::new();
|
|
while let Some(from_operation) = from_operations.next()? {
|
|
let operation = match from_operation {
|
|
read::Operation::Deref {
|
|
base_type,
|
|
size,
|
|
space,
|
|
} => {
|
|
if base_type.0 != 0 {
|
|
let base = convert_unit_offset(base_type)?;
|
|
Operation::DerefType { space, size, base }
|
|
} else if size != encoding.address_size {
|
|
Operation::DerefSize { space, size }
|
|
} else {
|
|
Operation::Deref { space }
|
|
}
|
|
}
|
|
read::Operation::Drop => Operation::Simple(constants::DW_OP_drop),
|
|
read::Operation::Pick { index } => Operation::Pick(index),
|
|
read::Operation::Swap => Operation::Simple(constants::DW_OP_swap),
|
|
read::Operation::Rot => Operation::Simple(constants::DW_OP_rot),
|
|
read::Operation::Abs => Operation::Simple(constants::DW_OP_abs),
|
|
read::Operation::And => Operation::Simple(constants::DW_OP_and),
|
|
read::Operation::Div => Operation::Simple(constants::DW_OP_div),
|
|
read::Operation::Minus => Operation::Simple(constants::DW_OP_minus),
|
|
read::Operation::Mod => Operation::Simple(constants::DW_OP_mod),
|
|
read::Operation::Mul => Operation::Simple(constants::DW_OP_mul),
|
|
read::Operation::Neg => Operation::Simple(constants::DW_OP_neg),
|
|
read::Operation::Not => Operation::Simple(constants::DW_OP_not),
|
|
read::Operation::Or => Operation::Simple(constants::DW_OP_or),
|
|
read::Operation::Plus => Operation::Simple(constants::DW_OP_plus),
|
|
read::Operation::PlusConstant { value } => Operation::PlusConstant(value),
|
|
read::Operation::Shl => Operation::Simple(constants::DW_OP_shl),
|
|
read::Operation::Shr => Operation::Simple(constants::DW_OP_shr),
|
|
read::Operation::Shra => Operation::Simple(constants::DW_OP_shra),
|
|
read::Operation::Xor => Operation::Simple(constants::DW_OP_xor),
|
|
read::Operation::Eq => Operation::Simple(constants::DW_OP_eq),
|
|
read::Operation::Ge => Operation::Simple(constants::DW_OP_ge),
|
|
read::Operation::Gt => Operation::Simple(constants::DW_OP_gt),
|
|
read::Operation::Le => Operation::Simple(constants::DW_OP_le),
|
|
read::Operation::Lt => Operation::Simple(constants::DW_OP_lt),
|
|
read::Operation::Ne => Operation::Simple(constants::DW_OP_ne),
|
|
read::Operation::Bra { target } => {
|
|
let offset = from_operations
|
|
.offset_from(&from_expression)
|
|
.wrapping_add(i64::from(target) as usize);
|
|
let index = offsets
|
|
.binary_search(&offset)
|
|
.map_err(|_| ConvertError::InvalidBranchTarget)?;
|
|
Operation::Branch(index)
|
|
}
|
|
read::Operation::Skip { target } => {
|
|
let offset = from_operations
|
|
.offset_from(&from_expression)
|
|
.wrapping_add(i64::from(target) as usize);
|
|
let index = offsets
|
|
.binary_search(&offset)
|
|
.map_err(|_| ConvertError::InvalidBranchTarget)?;
|
|
Operation::Skip(index)
|
|
}
|
|
read::Operation::UnsignedConstant { value } => {
|
|
Operation::UnsignedConstant(value)
|
|
}
|
|
read::Operation::SignedConstant { value } => Operation::SignedConstant(value),
|
|
read::Operation::Register { register } => Operation::Register(register),
|
|
read::Operation::RegisterOffset {
|
|
register,
|
|
offset,
|
|
base_type,
|
|
} => {
|
|
if base_type.0 != 0 {
|
|
Operation::RegisterType(register, convert_unit_offset(base_type)?)
|
|
} else {
|
|
Operation::RegisterOffset(register, offset)
|
|
}
|
|
}
|
|
read::Operation::FrameOffset { offset } => Operation::FrameOffset(offset),
|
|
read::Operation::Nop => Operation::Simple(constants::DW_OP_nop),
|
|
read::Operation::PushObjectAddress => {
|
|
Operation::Simple(constants::DW_OP_push_object_address)
|
|
}
|
|
read::Operation::Call { offset } => match offset {
|
|
read::DieReference::UnitRef(offset) => {
|
|
Operation::Call(convert_unit_offset(offset)?)
|
|
}
|
|
read::DieReference::DebugInfoRef(offset) => {
|
|
Operation::CallRef(convert_debug_info_offset(offset)?)
|
|
}
|
|
},
|
|
read::Operation::TLS => Operation::Simple(constants::DW_OP_form_tls_address),
|
|
read::Operation::CallFrameCFA => {
|
|
Operation::Simple(constants::DW_OP_call_frame_cfa)
|
|
}
|
|
read::Operation::Piece {
|
|
size_in_bits,
|
|
bit_offset: None,
|
|
} => Operation::Piece {
|
|
size_in_bytes: size_in_bits / 8,
|
|
},
|
|
read::Operation::Piece {
|
|
size_in_bits,
|
|
bit_offset: Some(bit_offset),
|
|
} => Operation::BitPiece {
|
|
size_in_bits,
|
|
bit_offset,
|
|
},
|
|
read::Operation::ImplicitValue { data } => {
|
|
Operation::ImplicitValue(data.to_slice()?.into_owned().into())
|
|
}
|
|
read::Operation::StackValue => Operation::Simple(constants::DW_OP_stack_value),
|
|
read::Operation::ImplicitPointer { value, byte_offset } => {
|
|
let entry = convert_debug_info_offset(value)?;
|
|
Operation::ImplicitPointer { entry, byte_offset }
|
|
}
|
|
read::Operation::EntryValue { expression } => {
|
|
let expression = Expression::from(
|
|
read::Expression(expression),
|
|
encoding,
|
|
dwarf,
|
|
unit,
|
|
entry_ids,
|
|
convert_address,
|
|
)?;
|
|
Operation::EntryValue(expression)
|
|
}
|
|
read::Operation::ParameterRef { offset } => {
|
|
let entry = convert_unit_offset(offset)?;
|
|
Operation::ParameterRef(entry)
|
|
}
|
|
read::Operation::Address { address } => {
|
|
let address =
|
|
convert_address(address).ok_or(ConvertError::InvalidAddress)?;
|
|
Operation::Address(address)
|
|
}
|
|
read::Operation::AddressIndex { index } => {
|
|
let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?;
|
|
let unit = unit.ok_or(ConvertError::UnsupportedOperation)?;
|
|
let val = dwarf.address(unit, index)?;
|
|
let address = convert_address(val).ok_or(ConvertError::InvalidAddress)?;
|
|
Operation::Address(address)
|
|
}
|
|
read::Operation::ConstantIndex { index } => {
|
|
let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?;
|
|
let unit = unit.ok_or(ConvertError::UnsupportedOperation)?;
|
|
let val = dwarf.address(unit, index)?;
|
|
Operation::UnsignedConstant(val)
|
|
}
|
|
read::Operation::TypedLiteral { base_type, value } => {
|
|
let entry = convert_unit_offset(base_type)?;
|
|
Operation::ConstantType(entry, value.to_slice()?.into_owned().into())
|
|
}
|
|
read::Operation::Convert { base_type } => {
|
|
if base_type.0 == 0 {
|
|
Operation::Convert(None)
|
|
} else {
|
|
let entry = convert_unit_offset(base_type)?;
|
|
Operation::Convert(Some(entry))
|
|
}
|
|
}
|
|
read::Operation::Reinterpret { base_type } => {
|
|
if base_type.0 == 0 {
|
|
Operation::Reinterpret(None)
|
|
} else {
|
|
let entry = convert_unit_offset(base_type)?;
|
|
Operation::Reinterpret(Some(entry))
|
|
}
|
|
}
|
|
read::Operation::WasmLocal { index } => Operation::WasmLocal(index),
|
|
read::Operation::WasmGlobal { index } => Operation::WasmGlobal(index),
|
|
read::Operation::WasmStack { index } => Operation::WasmStack(index),
|
|
};
|
|
operations.push(operation);
|
|
}
|
|
Ok(Expression { operations })
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[cfg(feature = "read")]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::common::{
|
|
DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase,
|
|
DebugStrOffsetsBase, Format, SectionId,
|
|
};
|
|
use crate::read;
|
|
use crate::write::{
|
|
DebugLineStrOffsets, DebugStrOffsets, EndianVec, LineProgram, Sections, Unit, UnitTable,
|
|
};
|
|
use crate::LittleEndian;
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
|
|
#[test]
|
|
fn test_operation() {
|
|
for &version in &[3, 4, 5] {
|
|
for &address_size in &[4, 8] {
|
|
for &format in &[Format::Dwarf32, Format::Dwarf64] {
|
|
let encoding = Encoding {
|
|
format,
|
|
version,
|
|
address_size,
|
|
};
|
|
|
|
let mut units = UnitTable::default();
|
|
let unit_id = units.add(Unit::new(encoding, LineProgram::none()));
|
|
let unit = units.get_mut(unit_id);
|
|
let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type);
|
|
let reference = Reference::Entry(unit_id, entry_id);
|
|
|
|
let mut sections = Sections::new(EndianVec::new(LittleEndian));
|
|
let debug_line_str_offsets = DebugLineStrOffsets::none();
|
|
let debug_str_offsets = DebugStrOffsets::none();
|
|
let debug_info_offsets = units
|
|
.write(&mut sections, &debug_line_str_offsets, &debug_str_offsets)
|
|
.unwrap();
|
|
let unit_offsets = debug_info_offsets.unit_offsets(unit_id);
|
|
let debug_info_offset = unit_offsets.debug_info_offset(entry_id);
|
|
let entry_offset =
|
|
read::UnitOffset(unit_offsets.unit_offset(entry_id) as usize);
|
|
|
|
let mut reg_expression = Expression::new();
|
|
reg_expression.op_reg(Register(23));
|
|
|
|
let operations: &[(&dyn Fn(&mut Expression), Operation, read::Operation<_>)] =
|
|
&[
|
|
(
|
|
&|x| x.op_deref(),
|
|
Operation::Deref { space: false },
|
|
read::Operation::Deref {
|
|
base_type: read::UnitOffset(0),
|
|
size: address_size,
|
|
space: false,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_xderef(),
|
|
Operation::Deref { space: true },
|
|
read::Operation::Deref {
|
|
base_type: read::UnitOffset(0),
|
|
size: address_size,
|
|
space: true,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_deref_size(2),
|
|
Operation::DerefSize {
|
|
space: false,
|
|
size: 2,
|
|
},
|
|
read::Operation::Deref {
|
|
base_type: read::UnitOffset(0),
|
|
size: 2,
|
|
space: false,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_xderef_size(2),
|
|
Operation::DerefSize {
|
|
space: true,
|
|
size: 2,
|
|
},
|
|
read::Operation::Deref {
|
|
base_type: read::UnitOffset(0),
|
|
size: 2,
|
|
space: true,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_deref_type(2, entry_id),
|
|
Operation::DerefType {
|
|
space: false,
|
|
size: 2,
|
|
base: entry_id,
|
|
},
|
|
read::Operation::Deref {
|
|
base_type: entry_offset,
|
|
size: 2,
|
|
space: false,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_xderef_type(2, entry_id),
|
|
Operation::DerefType {
|
|
space: true,
|
|
size: 2,
|
|
base: entry_id,
|
|
},
|
|
read::Operation::Deref {
|
|
base_type: entry_offset,
|
|
size: 2,
|
|
space: true,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_drop),
|
|
Operation::Simple(constants::DW_OP_drop),
|
|
read::Operation::Drop,
|
|
),
|
|
(
|
|
&|x| x.op_pick(0),
|
|
Operation::Pick(0),
|
|
read::Operation::Pick { index: 0 },
|
|
),
|
|
(
|
|
&|x| x.op_pick(1),
|
|
Operation::Pick(1),
|
|
read::Operation::Pick { index: 1 },
|
|
),
|
|
(
|
|
&|x| x.op_pick(2),
|
|
Operation::Pick(2),
|
|
read::Operation::Pick { index: 2 },
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_swap),
|
|
Operation::Simple(constants::DW_OP_swap),
|
|
read::Operation::Swap,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_rot),
|
|
Operation::Simple(constants::DW_OP_rot),
|
|
read::Operation::Rot,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_abs),
|
|
Operation::Simple(constants::DW_OP_abs),
|
|
read::Operation::Abs,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_and),
|
|
Operation::Simple(constants::DW_OP_and),
|
|
read::Operation::And,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_div),
|
|
Operation::Simple(constants::DW_OP_div),
|
|
read::Operation::Div,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_minus),
|
|
Operation::Simple(constants::DW_OP_minus),
|
|
read::Operation::Minus,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_mod),
|
|
Operation::Simple(constants::DW_OP_mod),
|
|
read::Operation::Mod,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_mul),
|
|
Operation::Simple(constants::DW_OP_mul),
|
|
read::Operation::Mul,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_neg),
|
|
Operation::Simple(constants::DW_OP_neg),
|
|
read::Operation::Neg,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_not),
|
|
Operation::Simple(constants::DW_OP_not),
|
|
read::Operation::Not,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_or),
|
|
Operation::Simple(constants::DW_OP_or),
|
|
read::Operation::Or,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_plus),
|
|
Operation::Simple(constants::DW_OP_plus),
|
|
read::Operation::Plus,
|
|
),
|
|
(
|
|
&|x| x.op_plus_uconst(23),
|
|
Operation::PlusConstant(23),
|
|
read::Operation::PlusConstant { value: 23 },
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_shl),
|
|
Operation::Simple(constants::DW_OP_shl),
|
|
read::Operation::Shl,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_shr),
|
|
Operation::Simple(constants::DW_OP_shr),
|
|
read::Operation::Shr,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_shra),
|
|
Operation::Simple(constants::DW_OP_shra),
|
|
read::Operation::Shra,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_xor),
|
|
Operation::Simple(constants::DW_OP_xor),
|
|
read::Operation::Xor,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_eq),
|
|
Operation::Simple(constants::DW_OP_eq),
|
|
read::Operation::Eq,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_ge),
|
|
Operation::Simple(constants::DW_OP_ge),
|
|
read::Operation::Ge,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_gt),
|
|
Operation::Simple(constants::DW_OP_gt),
|
|
read::Operation::Gt,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_le),
|
|
Operation::Simple(constants::DW_OP_le),
|
|
read::Operation::Le,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_lt),
|
|
Operation::Simple(constants::DW_OP_lt),
|
|
read::Operation::Lt,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_ne),
|
|
Operation::Simple(constants::DW_OP_ne),
|
|
read::Operation::Ne,
|
|
),
|
|
(
|
|
&|x| x.op_constu(23),
|
|
Operation::UnsignedConstant(23),
|
|
read::Operation::UnsignedConstant { value: 23 },
|
|
),
|
|
(
|
|
&|x| x.op_consts(-23),
|
|
Operation::SignedConstant(-23),
|
|
read::Operation::SignedConstant { value: -23 },
|
|
),
|
|
(
|
|
&|x| x.op_reg(Register(23)),
|
|
Operation::Register(Register(23)),
|
|
read::Operation::Register {
|
|
register: Register(23),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_reg(Register(123)),
|
|
Operation::Register(Register(123)),
|
|
read::Operation::Register {
|
|
register: Register(123),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_breg(Register(23), 34),
|
|
Operation::RegisterOffset(Register(23), 34),
|
|
read::Operation::RegisterOffset {
|
|
register: Register(23),
|
|
offset: 34,
|
|
base_type: read::UnitOffset(0),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_breg(Register(123), 34),
|
|
Operation::RegisterOffset(Register(123), 34),
|
|
read::Operation::RegisterOffset {
|
|
register: Register(123),
|
|
offset: 34,
|
|
base_type: read::UnitOffset(0),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_regval_type(Register(23), entry_id),
|
|
Operation::RegisterType(Register(23), entry_id),
|
|
read::Operation::RegisterOffset {
|
|
register: Register(23),
|
|
offset: 0,
|
|
base_type: entry_offset,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_fbreg(34),
|
|
Operation::FrameOffset(34),
|
|
read::Operation::FrameOffset { offset: 34 },
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_nop),
|
|
Operation::Simple(constants::DW_OP_nop),
|
|
read::Operation::Nop,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_push_object_address),
|
|
Operation::Simple(constants::DW_OP_push_object_address),
|
|
read::Operation::PushObjectAddress,
|
|
),
|
|
(
|
|
&|x| x.op_call(entry_id),
|
|
Operation::Call(entry_id),
|
|
read::Operation::Call {
|
|
offset: read::DieReference::UnitRef(entry_offset),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_call_ref(reference),
|
|
Operation::CallRef(reference),
|
|
read::Operation::Call {
|
|
offset: read::DieReference::DebugInfoRef(debug_info_offset),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_form_tls_address),
|
|
Operation::Simple(constants::DW_OP_form_tls_address),
|
|
read::Operation::TLS,
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_call_frame_cfa),
|
|
Operation::Simple(constants::DW_OP_call_frame_cfa),
|
|
read::Operation::CallFrameCFA,
|
|
),
|
|
(
|
|
&|x| x.op_piece(23),
|
|
Operation::Piece { size_in_bytes: 23 },
|
|
read::Operation::Piece {
|
|
size_in_bits: 23 * 8,
|
|
bit_offset: None,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_bit_piece(23, 34),
|
|
Operation::BitPiece {
|
|
size_in_bits: 23,
|
|
bit_offset: 34,
|
|
},
|
|
read::Operation::Piece {
|
|
size_in_bits: 23,
|
|
bit_offset: Some(34),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_implicit_value(vec![23].into()),
|
|
Operation::ImplicitValue(vec![23].into()),
|
|
read::Operation::ImplicitValue {
|
|
data: read::EndianSlice::new(&[23], LittleEndian),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op(constants::DW_OP_stack_value),
|
|
Operation::Simple(constants::DW_OP_stack_value),
|
|
read::Operation::StackValue,
|
|
),
|
|
(
|
|
&|x| x.op_implicit_pointer(reference, 23),
|
|
Operation::ImplicitPointer {
|
|
entry: reference,
|
|
byte_offset: 23,
|
|
},
|
|
read::Operation::ImplicitPointer {
|
|
value: debug_info_offset,
|
|
byte_offset: 23,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_entry_value(reg_expression.clone()),
|
|
Operation::EntryValue(reg_expression.clone()),
|
|
read::Operation::EntryValue {
|
|
expression: read::EndianSlice::new(
|
|
&[constants::DW_OP_reg23.0],
|
|
LittleEndian,
|
|
),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_gnu_parameter_ref(entry_id),
|
|
Operation::ParameterRef(entry_id),
|
|
read::Operation::ParameterRef {
|
|
offset: entry_offset,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_addr(Address::Constant(23)),
|
|
Operation::Address(Address::Constant(23)),
|
|
read::Operation::Address { address: 23 },
|
|
),
|
|
(
|
|
&|x| x.op_const_type(entry_id, vec![23].into()),
|
|
Operation::ConstantType(entry_id, vec![23].into()),
|
|
read::Operation::TypedLiteral {
|
|
base_type: entry_offset,
|
|
value: read::EndianSlice::new(&[23], LittleEndian),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_convert(None),
|
|
Operation::Convert(None),
|
|
read::Operation::Convert {
|
|
base_type: read::UnitOffset(0),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_convert(Some(entry_id)),
|
|
Operation::Convert(Some(entry_id)),
|
|
read::Operation::Convert {
|
|
base_type: entry_offset,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_reinterpret(None),
|
|
Operation::Reinterpret(None),
|
|
read::Operation::Reinterpret {
|
|
base_type: read::UnitOffset(0),
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_reinterpret(Some(entry_id)),
|
|
Operation::Reinterpret(Some(entry_id)),
|
|
read::Operation::Reinterpret {
|
|
base_type: entry_offset,
|
|
},
|
|
),
|
|
(
|
|
&|x| x.op_wasm_local(1000),
|
|
Operation::WasmLocal(1000),
|
|
read::Operation::WasmLocal { index: 1000 },
|
|
),
|
|
(
|
|
&|x| x.op_wasm_global(1000),
|
|
Operation::WasmGlobal(1000),
|
|
read::Operation::WasmGlobal { index: 1000 },
|
|
),
|
|
(
|
|
&|x| x.op_wasm_stack(1000),
|
|
Operation::WasmStack(1000),
|
|
read::Operation::WasmStack { index: 1000 },
|
|
),
|
|
];
|
|
|
|
let mut expression = Expression::new();
|
|
let start_index = expression.next_index();
|
|
for (f, o, _) in operations {
|
|
f(&mut expression);
|
|
assert_eq!(expression.operations.last(), Some(o));
|
|
}
|
|
|
|
let bra_index = expression.op_bra();
|
|
let skip_index = expression.op_skip();
|
|
expression.op(constants::DW_OP_nop);
|
|
let end_index = expression.next_index();
|
|
expression.set_target(bra_index, start_index);
|
|
expression.set_target(skip_index, end_index);
|
|
|
|
let mut w = EndianVec::new(LittleEndian);
|
|
let mut refs = Vec::new();
|
|
expression
|
|
.write(&mut w, Some(&mut refs), encoding, Some(&unit_offsets))
|
|
.unwrap();
|
|
for r in &refs {
|
|
assert_eq!(r.unit, unit_id);
|
|
assert_eq!(r.entry, entry_id);
|
|
w.write_offset_at(
|
|
r.offset,
|
|
debug_info_offset.0,
|
|
SectionId::DebugInfo,
|
|
r.size,
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
let read_expression =
|
|
read::Expression(read::EndianSlice::new(w.slice(), LittleEndian));
|
|
let mut read_operations = read_expression.operations(encoding);
|
|
for (_, _, operation) in operations {
|
|
assert_eq!(read_operations.next(), Ok(Some(*operation)));
|
|
}
|
|
|
|
// 4 = DW_OP_skip + i16 + DW_OP_nop
|
|
assert_eq!(
|
|
read_operations.next(),
|
|
Ok(Some(read::Operation::Bra {
|
|
target: -(w.len() as i16) + 4
|
|
}))
|
|
);
|
|
// 1 = DW_OP_nop
|
|
assert_eq!(
|
|
read_operations.next(),
|
|
Ok(Some(read::Operation::Skip { target: 1 }))
|
|
);
|
|
assert_eq!(read_operations.next(), Ok(Some(read::Operation::Nop)));
|
|
assert_eq!(read_operations.next(), Ok(None));
|
|
|
|
// Fake the unit.
|
|
let unit = read::Unit {
|
|
header: read::UnitHeader::new(
|
|
encoding,
|
|
0,
|
|
read::UnitType::Compilation,
|
|
DebugAbbrevOffset(0),
|
|
DebugInfoOffset(0).into(),
|
|
read::EndianSlice::new(&[], LittleEndian),
|
|
),
|
|
abbreviations: Arc::new(read::Abbreviations::default()),
|
|
name: None,
|
|
comp_dir: None,
|
|
low_pc: 0,
|
|
str_offsets_base: DebugStrOffsetsBase(0),
|
|
addr_base: DebugAddrBase(0),
|
|
loclists_base: DebugLocListsBase(0),
|
|
rnglists_base: DebugRngListsBase(0),
|
|
line_program: None,
|
|
dwo_id: None,
|
|
};
|
|
|
|
let mut entry_ids = HashMap::new();
|
|
entry_ids.insert(debug_info_offset.into(), (unit_id, entry_id));
|
|
let convert_expression = Expression::from(
|
|
read_expression,
|
|
encoding,
|
|
None, /* dwarf */
|
|
Some(&unit),
|
|
Some(&entry_ids),
|
|
&|address| Some(Address::Constant(address)),
|
|
)
|
|
.unwrap();
|
|
let mut convert_operations = convert_expression.operations.iter();
|
|
for (_, operation, _) in operations {
|
|
assert_eq!(convert_operations.next(), Some(operation));
|
|
}
|
|
assert_eq!(
|
|
convert_operations.next(),
|
|
Some(&Operation::Branch(start_index))
|
|
);
|
|
assert_eq!(convert_operations.next(), Some(&Operation::Skip(end_index)));
|
|
assert_eq!(
|
|
convert_operations.next(),
|
|
Some(&Operation::Simple(constants::DW_OP_nop))
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|