Skip to content

Commit

Permalink
Merge pull request #546 from philipc/wasm_loc
Browse files Browse the repository at this point in the history
Implement read/write of DW_OP_WASM_location
  • Loading branch information
philipc authored Jan 29, 2021
2 parents 8c5a7ab + 10500f1 commit aa03ab8
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 21 deletions.
14 changes: 10 additions & 4 deletions examples/dwarfdump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,16 +1383,15 @@ fn dump_exprloc<R: Reader, W: Write>(
let mut pc = data.0.clone();
let mut space = false;
while pc.len() != 0 {
let mut op_pc = pc.clone();
let dwop = gimli::DwOp(op_pc.read_u8()?);
let pc_clone = pc.clone();
match gimli::Operation::parse(&mut pc, encoding) {
Ok(op) => {
if space {
write!(w, " ")?;
} else {
space = true;
}
dump_op(w, encoding, dwop, op)?;
dump_op(w, encoding, pc_clone, op)?;
}
Err(gimli::Error::InvalidExpression(op)) => {
writeln!(w, "WARNING: unsupported operation 0x{:02x}", op.0)?;
Expand All @@ -1418,9 +1417,10 @@ fn dump_exprloc<R: Reader, W: Write>(
fn dump_op<R: Reader, W: Write>(
w: &mut W,
encoding: gimli::Encoding,
dwop: gimli::DwOp,
mut pc: R,
op: gimli::Operation<R>,
) -> Result<()> {
let dwop = gimli::DwOp(pc.read_u8()?);
write!(w, "{}", dwop)?;
match op {
gimli::Operation::Deref {
Expand Down Expand Up @@ -1553,6 +1553,12 @@ fn dump_op<R: Reader, W: Write>(
gimli::Operation::Reinterpret { base_type } => {
write!(w, " type 0x{:08x}", base_type.0)?;
}
gimli::Operation::WasmLocal { index }
| gimli::Operation::WasmGlobal { index }
| gimli::Operation::WasmStack { index } => {
let wasmop = pc.read_u8()?;
write!(w, " 0x{:x} 0x{:x}", wasmop, index)?;
}
gimli::Operation::Drop
| gimli::Operation::Swap
| gimli::Operation::Rot
Expand Down
5 changes: 4 additions & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,7 @@ DwOp(u8) {
DW_OP_convert = 0xa8,
DW_OP_reinterpret = 0xa9,

// GNU extensions
// GNU extensions
DW_OP_GNU_push_tls_address = 0xe0,
DW_OP_GNU_implicit_pointer = 0xf2,
DW_OP_GNU_entry_value = 0xf3,
Expand All @@ -1221,6 +1221,9 @@ DwOp(u8) {
DW_OP_GNU_parameter_ref = 0xfa,
DW_OP_GNU_addr_index = 0xfb,
DW_OP_GNU_const_index = 0xfc,

// Wasm extensions
DW_OP_WASM_location = 0xed,
});

dw!(
Expand Down
3 changes: 3 additions & 0 deletions src/read/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@ pub enum Error {
/// An unrecognized operation was found while parsing a DWARF
/// expression.
InvalidExpression(constants::DwOp),
/// An unsupported operation was found while evaluating a DWARF expression.
UnsupportedEvaluation,
/// The expression had a piece followed by an expression
/// terminator without a piece.
InvalidPiece,
Expand Down Expand Up @@ -470,6 +472,7 @@ impl Error {
Error::NotEnoughStackItems => "Not enough items on stack when evaluating expression",
Error::TooManyIterations => "Too many iterations to evaluate DWARF expression",
Error::InvalidExpression(_) => "Invalid opcode in DWARF expression",
Error::UnsupportedEvaluation => "Unsupported operation when evaluating expression",
Error::InvalidPiece => {
"DWARF expression has piece followed by non-piece expression at end"
}
Expand Down
120 changes: 104 additions & 16 deletions src/read/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ where
value: i64,
},
/// Indicate that this piece's location is in the given register.
///
/// Completes the piece or expression.
Register {
/// The register number.
Expand Down Expand Up @@ -174,82 +175,119 @@ where
/// next byte boundary.
bit_offset: Option<u64>,
},
/// Represents `DW_OP_implicit_value`.
/// The object has no location, but has a known constant value.
///
/// Represents `DW_OP_implicit_value`.
/// Completes the piece or expression.
ImplicitValue {
/// The implicit value to use.
data: R,
},
/// Represents `DW_OP_stack_value`.
/// The object has no location, but its value is at the top of the stack.
///
/// Represents `DW_OP_stack_value`.
/// Completes the piece or expression.
StackValue,
/// Represents `DW_OP_implicit_pointer`. The object is a pointer to
/// a value which has no actual location, such as an implicit value or
/// a stack value.
/// The object is a pointer to a value which has no actual location,
/// such as an implicit value or a stack value.
///
/// Represents `DW_OP_implicit_pointer`.
/// Completes the piece or expression.
ImplicitPointer {
/// The `.debug_info` offset of the value that this is an implicit pointer into.
value: DebugInfoOffset<Offset>,
/// The byte offset into the value that the implicit pointer points to.
byte_offset: i64,
},
/// Represents `DW_OP_entry_value`. Evaluate an expression at the entry to
/// the current subprogram, and push it on the stack.
/// Evaluate an expression at the entry to the current subprogram, and push it on the stack.
///
/// Represents `DW_OP_entry_value`.
EntryValue {
/// The expression to be evaluated.
expression: R,
},
/// Represents `DW_OP_GNU_parameter_ref`. This represents a parameter that was
/// optimized out. The offset points to the definition of the parameter, and is
/// This represents a parameter that was optimized out.
///
/// The offset points to 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 {
/// The DIE to use.
offset: UnitOffset<Offset>,
},
/// Represents `DW_OP_addr`.
/// Relocate the address if needed, and push it on the stack.
///
/// Represents `DW_OP_addr`.
Address {
/// The offset to add.
address: u64,
},
/// Represents `DW_OP_addrx`.
/// 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 {
/// The index of the address in `.debug_addr`.
index: DebugAddrIndex<Offset>,
},
/// Represents `DW_OP_constx`.
/// 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 {
/// The index of the address in `.debug_addr`.
index: DebugAddrIndex<Offset>,
},
/// Represents `DW_OP_const_type`.
/// Interpret the value bytes as a constant of a given type, and push it on the stack.
///
/// Represents `DW_OP_const_type`.
TypedLiteral {
/// The DIE of the base type.
base_type: UnitOffset<Offset>,
/// The value bytes.
value: R,
},
/// Represents `DW_OP_convert`.
/// Pop the top stack entry, convert it to a different type, and push it on the stack.
///
/// Represents `DW_OP_convert`.
Convert {
/// The DIE of the base type.
base_type: UnitOffset<Offset>,
},
/// Represents `DW_OP_reinterpret`.
/// 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 {
/// The DIE of the base type.
base_type: UnitOffset<Offset>,
},
/// The index of a local in the currently executing function.
///
/// Represents `DW_OP_WASM_location 0x00`.
/// Completes the piece or expression.
WasmLocal {
/// The index of the local.
index: u32,
},
/// The index of a global.
///
/// Represents `DW_OP_WASM_location 0x01` or `DW_OP_WASM_location 0x03`.
/// Completes the piece or expression.
WasmGlobal {
/// The index of the global.
index: u32,
},
/// The index of an item on the operand stack.
///
/// Represents `DW_OP_WASM_location 0x02`.
/// Completes the piece or expression.
WasmStack {
/// The index of the stack item. 0 is the bottom of the operand stack.
index: u32,
},
}

#[derive(Debug)]
Expand Down Expand Up @@ -745,7 +783,25 @@ where
base_type: UnitOffset(base_type),
})
}

constants::DW_OP_WASM_location => match bytes.read_u8()? {
0x0 => {
let index = bytes.read_uleb128_u32()?;
Ok(Operation::WasmLocal { index })
}
0x1 => {
let index = bytes.read_uleb128_u32()?;
Ok(Operation::WasmGlobal { index })
}
0x2 => {
let index = bytes.read_uleb128_u32()?;
Ok(Operation::WasmStack { index })
}
0x3 => {
let index = bytes.read_u32()?;
Ok(Operation::WasmGlobal { index })
}
_ => Err(Error::InvalidExpression(name)),
},
_ => Err(Error::InvalidExpression(name)),
}
}
Expand Down Expand Up @@ -1431,6 +1487,11 @@ impl<R: Reader> Evaluation<R> {
EvaluationResult::RequiresBaseType(base_type),
));
}
Operation::WasmLocal { .. }
| Operation::WasmGlobal { .. }
| Operation::WasmStack { .. } => {
return Err(Error::UnsupportedEvaluation);
}
}

Ok(OperationEvaluationResult::Incomplete)
Expand Down Expand Up @@ -2611,6 +2672,33 @@ mod tests {
)
}

#[test]
fn test_op_wasm() {
// Doesn't matter for this test.
let encoding = encoding4();

check_op_parse(
|s| s.D8(constants::DW_OP_WASM_location.0).D8(0).uleb(1000),
&Operation::WasmLocal { index: 1000 },
encoding,
);
check_op_parse(
|s| s.D8(constants::DW_OP_WASM_location.0).D8(1).uleb(1000),
&Operation::WasmGlobal { index: 1000 },
encoding,
);
check_op_parse(
|s| s.D8(constants::DW_OP_WASM_location.0).D8(2).uleb(1000),
&Operation::WasmStack { index: 1000 },
encoding,
);
check_op_parse(
|s| s.D8(constants::DW_OP_WASM_location.0).D8(3).D32(1000),
&Operation::WasmGlobal { index: 1000 },
encoding,
);
}

enum AssemblerEntry {
Op(constants::DwOp),
Mark(u8),
Expand Down
8 changes: 8 additions & 0 deletions src/read/reader.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use alloc::borrow::Cow;
use core::convert::TryInto;
use core::fmt::Debug;
use core::hash::Hash;
use core::ops::{Add, AddAssign, Sub};
Expand Down Expand Up @@ -391,6 +392,13 @@ pub trait Reader: Debug + Clone {
leb128::read::unsigned(self)
}

/// Read an unsigned LEB128 encoded u32.
fn read_uleb128_u32(&mut self) -> Result<u32> {
leb128::read::unsigned(self)?
.try_into()
.map_err(|_| Error::BadUnsignedLeb128)
}

/// Read an unsigned LEB128 encoded u16.
fn read_uleb128_u16(&mut self) -> Result<u16> {
leb128::read::u16(self)
Expand Down
Loading

0 comments on commit aa03ab8

Please sign in to comment.