/// Create an expression from raw bytecode. /// /// This does not support operations that require references, such as `DW_OP_addr`. #[inline] pubfn 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. pubfn op(&mutself, opcode: DwOp) { self.operations.push(Operation::Simple(opcode));
}
/// Add a `DW_OP_addr` operation to the expression. pubfn op_addr(&mutself, 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. pubfn op_constu(&mutself, 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. pubfn op_consts(&mutself, value: i64) { self.operations.push(Operation::SignedConstant(value));
}
/// Add a `DW_OP_const_type` or `DW_OP_GNU_const_type` operation to the expression. pubfn op_const_type(&mutself, base: UnitEntryId, value: Box<[u8]>) { self.operations.push(Operation::ConstantType(base, value));
}
/// Add a `DW_OP_fbreg` operation to the expression. pubfn op_fbreg(&mutself, 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. pubfn op_breg(&mutself, 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. pubfn op_regval_type(&mutself, 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. pubfn op_pick(&mutself, index: u8) { self.operations.push(Operation::Pick(index));
}
/// Add a `DW_OP_deref` operation to the expression. pubfn op_deref(&mutself) { self.operations.push(Operation::Deref { space: false });
}
/// Add a `DW_OP_xderef` operation to the expression. pubfn op_xderef(&mutself) { self.operations.push(Operation::Deref { space: true });
}
/// Add a `DW_OP_deref_size` operation to the expression. pubfn op_deref_size(&mutself, size: u8) { self.operations
.push(Operation::DerefSize { size, space: false });
}
/// Add a `DW_OP_xderef_size` operation to the expression. pubfn op_xderef_size(&mutself, 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. pubfn op_deref_type(&mutself, size: u8, base: UnitEntryId) { self.operations.push(Operation::DerefType {
size,
base,
space: false,
});
}
/// Add a `DW_OP_xderef_type` operation to the expression. pubfn op_xderef_type(&mutself, size: u8, base: UnitEntryId) { self.operations.push(Operation::DerefType {
size,
base,
space: true,
});
}
/// Add a `DW_OP_plus_uconst` operation to the expression. pubfn op_plus_uconst(&mutself, 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. pubfn op_skip(&mutself) -> 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. pubfn op_bra(&mutself) -> 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] pubfn next_index(&self) -> usize { self.operations.len()
}
/// Set the target of a `DW_OP_skip` or `DW_OP_bra` operation . pubfn set_target(&mutself, operation: usize, new_target: usize) {
debug_assert!(new_target <= self.next_index());
debug_assert_ne!(operation, new_target); matchself.operations[operation] {
Operation::Skip(refmut target) | Operation::Branch(refmut target) => {
*target = new_target;
}
_ => unimplemented!(),
}
}
/// Add a `DW_OP_call4` operation to the expression. pubfn op_call(&mutself, entry: UnitEntryId) { self.operations.push(Operation::Call(entry));
}
/// Add a `DW_OP_call_ref` operation to the expression. pubfn op_call_ref(&mutself, 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. pubfn op_convert(&mutself, 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. pubfn op_reinterpret(&mutself, 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. pubfn op_entry_value(&mutself, 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. pubfn op_reg(&mutself, register: Register) { self.operations.push(Operation::Register(register));
}
/// Add a `DW_OP_implicit_value` operation to the expression. pubfn op_implicit_value(&mutself, 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. pubfn op_implicit_pointer(&mutself, entry: Reference, byte_offset: i64) { self.operations
.push(Operation::ImplicitPointer { entry, byte_offset });
}
/// Add a `DW_OP_piece` operation to the expression. pubfn op_piece(&mutself, size_in_bytes: u64) { self.operations.push(Operation::Piece { size_in_bytes });
}
/// Add a `DW_OP_bit_piece` operation to the expression. pubfn op_bit_piece(&mutself, 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. pubfn op_gnu_parameter_ref(&mutself, entry: UnitEntryId) { self.operations.push(Operation::ParameterRef(entry));
}
/// Add a `DW_OP_WASM_location 0x0` operation to the expression. pubfn op_wasm_local(&mutself, index: u32) { self.operations.push(Operation::WasmLocal(index));
}
/// Add a `DW_OP_WASM_location 0x1` operation to the expression. pubfn op_wasm_global(&mutself, index: u32) { self.operations.push(Operation::WasmGlobal(index));
}
/// Add a `DW_OP_WASM_location 0x2` operation to the expression. pubfn op_wasm_stack(&mutself, index: u32) { self.operations.push(Operation::WasmStack(index));
}
/// 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),
}
#[cfg(test)] #[cfg(feature = "read")] mod tests { usesuper::*; usecrate::common::{
DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase,
DebugStrOffsetsBase, Format, SectionId,
}; usecrate::read; usecrate::write::{
DebugLineStrOffsets, DebugStrOffsets, EndianVec, LineProgram, Sections, Unit, UnitTable,
}; usecrate::LittleEndian; use std::collections::HashMap; use std::sync::Arc;
#[test] #[allow(clippy::type_complexity)] fn test_operation() { for version in [2, 3, 4, 5] { for address_size in [4, 8] { for format in [Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding {
format,
version,
address_size,
};
letmut 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);
letmut 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);
letmut 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);
letmut w = EndianVec::new(LittleEndian); letmut 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)); letmut read_operations = read_expression.operations(encoding); for (_, _, operation) in operations {
assert_eq!(read_operations.next(), Ok(Some(*operation)));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.