| 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()) { |
| let refs = match refs { |
| Some(ref mut refs) => Some(&mut **refs), |
| None => None, |
| }; |
| debug_assert_eq!(w.len(), offset); |
| operation.write(w, refs, 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 = 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 let Some(_) = from_operations.next()? { |
| 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; |
| |
| #[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: 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)) |
| ); |
| } |
| } |
| } |
| } |
| } |