/// 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::{DebugInfoOffset, Format}; usecrate::read; usecrate::write::{AttributeValue, Dwarf, EndianVec, LineProgram, Sections, Unit}; usecrate::LittleEndian; use std::collections::HashMap;
#[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 dwarf = Dwarf::new(); let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); let unit = dwarf.units.get_mut(unit_id);
// Create an entry that can be referenced by the expression. let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type); let reference = Reference::Entry(unit_id, entry_id);
// The offsets for the above entry when reading back the expression. struct ReadState {
debug_info_offset: DebugInfoOffset,
entry_offset: read::UnitOffset,
}
// Create a single expression containing all operations. 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);
// Create an entry containing the expression. let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); let subprogram = unit.get_mut(subprogram_id);
subprogram.set(
constants::DW_AT_location,
AttributeValue::Exprloc(expression),
);
// Write the DWARF, then parse it. letmut sections = Sections::new(EndianVec::new(LittleEndian));
dwarf.write(&mut sections).unwrap();
let read_dwarf = sections.read(LittleEndian); letmut read_units = read_dwarf.units(); let read_unit_header = read_units.next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit_header).unwrap(); letmut read_entries = read_unit.entries(); let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap();
assert_eq!(read_entry.tag(), constants::DW_TAG_compile_unit);
// Determine the offset of the entry that can be referenced by the expression. let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap();
assert_eq!(read_entry.tag(), constants::DW_TAG_base_type); let read_state = ReadState {
debug_info_offset: read_entry
.offset()
.to_debug_info_offset(&read_unit.header)
.unwrap(),
entry_offset: read_entry.offset(),
};
// Get the expression. let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap();
assert_eq!(read_entry.tag(), constants::DW_TAG_subprogram); let read_attr = read_entry
.attr_value(constants::DW_AT_location)
.unwrap()
.unwrap(); let read_expression = read_attr.exprloc_value().unwrap(); letmut read_operations = read_expression.operations(encoding); for (_, _, operation) in operations {
assert_eq!(read_operations.next(), Ok(Some(operation(&read_state))));
}
#[test] #[allow(clippy::type_complexity)] fn test_expression_raw() { 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 dwarf = Dwarf::new(); let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); let unit = dwarf.units.get_mut(unit_id); let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram); let subprogram = unit.get_mut(subprogram_id); let expression = Expression::raw(vec![constants::DW_OP_constu.0, 23]);
subprogram.set(
constants::DW_AT_location,
AttributeValue::Exprloc(expression),
);
// Write the DWARF, then parse it. letmut sections = Sections::new(EndianVec::new(LittleEndian));
dwarf.write(&mut sections).unwrap();
let read_dwarf = sections.read(LittleEndian); letmut read_units = read_dwarf.units(); let read_unit_header = read_units.next().unwrap().unwrap(); let read_unit = read_dwarf.unit(read_unit_header).unwrap(); letmut read_entries = read_unit.entries(); let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap();
assert_eq!(read_entry.tag(), constants::DW_TAG_compile_unit); let (_, read_entry) = read_entries.next_dfs().unwrap().unwrap();
assert_eq!(read_entry.tag(), constants::DW_TAG_subprogram); let read_attr = read_entry
.attr_value(constants::DW_AT_location)
.unwrap()
.unwrap(); let read_expression = read_attr.exprloc_value().unwrap(); letmut read_operations = read_expression.operations(encoding);
assert_eq!(
read_operations.next(),
Ok(Some(read::Operation::UnsignedConstant { value: 23 }))
);
assert_eq!(read_operations.next(), Ok(None));
}
}
}
}
letmut dwarf = Dwarf::new(); let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); let unit = dwarf.units.get_mut(unit_id);
// Create the entry to be referenced by the expression. let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); // And delete it so that it is not available when writing the expression.
unit.get_mut(unit.root()).delete_child(entry_id);
// Create an expression containing the reference. letmut expression = Expression::new();
expression.op_deref_type(2, entry_id);
// Create an entry containing the expression. let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram);
unit.get_mut(subprogram_id).set(
constants::DW_AT_location,
AttributeValue::Exprloc(expression),
);
// Writing the DWARF should fail. letmut sections = Sections::new(EndianVec::new(LittleEndian));
assert_eq!(
dwarf.write(&mut sections),
Err(Error::UnsupportedExpressionForwardReference)
);
}
letmut dwarf = Dwarf::new(); let unit_id = dwarf.units.add(Unit::new(encoding, LineProgram::none())); let unit = dwarf.units.get_mut(unit_id);
// Create the entry to be referenced by the expression. let entry_id = unit.add(unit.root(), constants::DW_TAG_const_type); // And delete it so that it is not available when writing the expression.
unit.get_mut(unit.root()).delete_child(entry_id);
// Create an expression containing the reference. letmut expression = Expression::new();
expression.op_call_ref(Reference::Entry(unit_id, entry_id));
// Create an entry containing the expression. let subprogram_id = unit.add(unit.root(), constants::DW_TAG_subprogram);
unit.get_mut(subprogram_id).set(
constants::DW_AT_location,
AttributeValue::Exprloc(expression),
);
// Writing the DWARF should fail. letmut sections = Sections::new(EndianVec::new(LittleEndian));
assert_eq!(dwarf.write(&mut sections), Err(Error::InvalidReference));
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-08-27)
¤
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.