use crate::dwarf::dwarf_die::DwarfDIE;
use crate::dwarf::dwarf_expr::DwarfExpr;
use crate::dwarf::dwarf_frame::DwarfFrame;
use crate::dwarf::dwarf_line::{DwarfLineTable, LineEntry};
use crate::dwarf::dwarf_types::DwarfType;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
use std::fmt;
use std::io;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum DwarfOpcode {
Reg = 0x03,
BReg = 0x04,
FbReg = 0x91,
Addr = 0x06,
Const1U = 0x08,
Const1S = 0x09, Const1s = 0x0a,
Const2U = 0x0b,
Const2S = 0x0c,
Const4U = 0x0d,
Const4S = 0x0e,
Const8U = 0x0f,
Const8S = 0x10,
ConstU = 0x11,
ConstS = 0x07, Dup = 0x12,
Drop = 0x13,
Over = 0x14,
Pick = 0x15,
Swap = 0x16,
Rot = 0x17,
XDeref = 0x18,
Abs = 0x19,
And = 0x1a,
Div = 0x1b,
Minus = 0x1c,
Mod = 0x1d,
Mul = 0x1e,
Neg = 0x1f,
Not = 0x20,
Or = 0x21,
Plus = 0x22,
PlusUconst = 0x23,
Shl = 0x24,
Shr = 0x25,
Shra = 0x26,
Xor = 0x27,
Skip = 0x2f,
Bra = 0x28,
Eq = 0x29,
Ge = 0x2a,
Gt = 0x2b,
Le = 0x2c,
Lt = 0x2d,
Ne = 0x2e,
Lit0 = 0x30,
Lit1 = 0x31,
Lit2 = 0x32,
Lit3 = 0x33,
Lit4 = 0x34,
Lit5 = 0x35,
Lit6 = 0x36,
Lit7 = 0x37,
Lit8 = 0x38,
Lit9 = 0x39,
Lit10 = 0x3a,
Lit11 = 0x3b,
Lit12 = 0x3c,
Lit13 = 0x3d,
Lit14 = 0x3e,
Lit15 = 0x3f,
Lit16 = 0x40,
Lit17 = 0x41,
Lit18 = 0x42,
Lit19 = 0x43,
Lit20 = 0x44,
Lit21 = 0x45,
Lit22 = 0x46,
Lit23 = 0x47,
Lit24 = 0x48,
Lit25 = 0x49,
Lit26 = 0x4a,
Lit27 = 0x4b,
Lit28 = 0x4c,
Lit29 = 0x4d,
Lit30 = 0x4e,
Lit31 = 0x4f,
Reg0 = 0x50,
Reg1 = 0x51,
Reg2 = 0x52,
Reg3 = 0x53,
Reg4 = 0x54,
Reg5 = 0x55,
Reg6 = 0x56,
Reg7 = 0x57,
Reg8 = 0x58,
Reg9 = 0x59,
Reg10 = 0x5a,
Reg11 = 0x5b,
Reg12 = 0x5c,
Reg13 = 0x5d,
Reg14 = 0x5e,
Reg15 = 0x5f,
Reg16 = 0x60,
Reg17 = 0x61,
Reg18 = 0x62,
Reg19 = 0x63,
Reg20 = 0x64,
Reg21 = 0x65,
Reg22 = 0x66,
Reg23 = 0x67,
Reg24 = 0x68,
Reg25 = 0x69,
Reg26 = 0x6a,
Reg27 = 0x6b,
Reg28 = 0x6c,
Reg29 = 0x6d,
Reg30 = 0x6e,
Reg31 = 0x6f,
BReg0 = 0x70,
BReg1 = 0x71,
BReg2 = 0x72,
BReg3 = 0x73,
BReg4 = 0x74,
BReg5 = 0x75,
BReg6 = 0x76,
BReg7 = 0x77,
BReg8 = 0x78,
BReg9 = 0x79,
BReg10 = 0x7a,
BReg11 = 0x7b,
BReg12 = 0x7c,
BReg13 = 0x7d,
BReg14 = 0x7e,
BReg15 = 0x7f,
BReg16 = 0x80,
BReg17 = 0x81,
BReg18 = 0x82,
BReg19 = 0x83,
BReg20 = 0x84,
BReg21 = 0x85,
BReg22 = 0x86,
BReg23 = 0x87,
BReg24 = 0x88,
BReg25 = 0x89,
BReg26 = 0x8a,
BReg27 = 0x8b,
BReg28 = 0x8c,
BReg29 = 0x8d,
BReg30 = 0x8e,
BReg31 = 0x8f,
Piece = 0x93,
BitPiece = 0x94,
ImplicitValue = 0x9e,
StackValue = 0x9f,
ImplicitPointer = 0xa1,
EntryValue = 0xa2,
ConstType = 0xa4,
RegvalType = 0xa5,
DerefType = 0xa6,
XDerefType = 0xa7,
Convert = 0xa8,
Reinterpret = 0xa9,
XDerefSize = 0x90, Call2 = 0x98,
Call4 = 0x99,
CallRef = 0x9a,
FormTlsAddress = 0x9b, CallFrameCfa = 0x9c,
GnuPushTlsAddress = 0xe0,
GnuUninit = 0xf0,
GnuDerefType = 0xf2,
GnuConvert = 0xf3,
GnuReinterpret = 0xf4,
GnuParameterRef = 0xf5,
GnuAddrIndex = 0xf6,
GnuConstIndex = 0xf7,
GnuEntryValue = 0xf8,
Addrx = 0xa0,
Constx = 0xa3,
Unknown(u8) = 0x100,
}
impl DwarfOpcode {
pub fn from_byte(b: u8) -> Self {
match b {
0x03 => Self::Reg,
0x06 => Self::Addr,
0x08 => Self::Const1U,
0x09 => Self::Const1S,
0x0a => Self::Const1s,
0x0b => Self::Const2U,
0x0c => Self::Const2S,
0x0d => Self::Const4U,
0x0e => Self::Const4S,
0x0f => Self::Const8U,
0x10 => Self::Const8S,
0x11 => Self::ConstU,
0x07 => Self::ConstS,
0x12 => Self::Dup,
0x13 => Self::Drop,
0x14 => Self::Over,
0x15 => Self::Pick,
0x16 => Self::Swap,
0x17 => Self::Rot,
0x18 => Self::XDeref,
0x19 => Self::Abs,
0x1a => Self::And,
0x1b => Self::Div,
0x1c => Self::Minus,
0x1d => Self::Mod,
0x1e => Self::Mul,
0x1f => Self::Neg,
0x20 => Self::Not,
0x21 => Self::Or,
0x22 => Self::Plus,
0x23 => Self::PlusUconst,
0x24 => Self::Shl,
0x25 => Self::Shr,
0x26 => Self::Shra,
0x27 => Self::Xor,
0x28 => Self::Bra,
0x29 => Self::Eq,
0x2a => Self::Ge,
0x2b => Self::Gt,
0x2c => Self::Le,
0x2d => Self::Lt,
0x2e => Self::Ne,
0x2f => Self::Skip,
0x30..=0x4f => Self::Lit0, 0x50..=0x6f => Self::Reg0,
0x70..=0x8f => Self::BReg0,
0x93 => Self::Piece,
0x94 => Self::BitPiece,
0x98 => Self::Call2,
0x99 => Self::Call4,
0x9a => Self::CallRef,
0x9b => Self::FormTlsAddress,
0x9c => Self::CallFrameCfa,
0x9e => Self::ImplicitValue,
0x9f => Self::StackValue,
0xa0 => Self::Addrx,
0xa1 => Self::ImplicitPointer,
0xa2 => Self::EntryValue,
0xa3 => Self::Constx,
0xa4 => Self::ConstType,
0xa5 => Self::RegvalType,
0xa6 => Self::DerefType,
0xa7 => Self::XDerefType,
0xa8 => Self::Convert,
0xa9 => Self::Reinterpret,
0xe0 => Self::GnuPushTlsAddress,
0xf0 => Self::GnuUninit,
0xf2 => Self::GnuDerefType,
0xf3 => Self::GnuConvert,
0xf4 => Self::GnuReinterpret,
0xf5 => Self::GnuParameterRef,
0xf6 => Self::GnuAddrIndex,
0xf7 => Self::GnuConstIndex,
0xf8 => Self::GnuEntryValue,
other => Self::Unknown(other),
}
}
pub fn to_byte(self) -> u8 {
match self {
Self::Reg => 0x03,
Self::BReg => 0x04,
Self::Addr => 0x06,
Self::Const1U => 0x08,
Self::Const1S => 0x09,
Self::FbReg => 0x91,
Self::Const1s => 0x0a,
Self::Const2U => 0x0b,
Self::Const2S => 0x0c,
Self::Const4U => 0x0d,
Self::Const4S => 0x0e,
Self::Const8U => 0x0f,
Self::Const8S => 0x10,
Self::ConstU => 0x11,
Self::ConstS => 0x07,
Self::Dup => 0x12,
Self::Drop => 0x13,
Self::Over => 0x14,
Self::Pick => 0x15,
Self::Swap => 0x16,
Self::Rot => 0x17,
Self::XDeref => 0x18,
Self::Abs => 0x19,
Self::And => 0x1a,
Self::Div => 0x1b,
Self::Minus => 0x1c,
Self::Mod => 0x1d,
Self::Mul => 0x1e,
Self::Neg => 0x1f,
Self::Not => 0x20,
Self::Or => 0x21,
Self::Plus => 0x22,
Self::PlusUconst => 0x23,
Self::Shl => 0x24,
Self::Shr => 0x25,
Self::Shra => 0x26,
Self::Xor => 0x27,
Self::Bra => 0x28,
Self::Eq => 0x29,
Self::Ge => 0x2a,
Self::Gt => 0x2b,
Self::Le => 0x2c,
Self::Lt => 0x2d,
Self::Ne => 0x2e,
Self::Skip => 0x2f,
Self::Lit0 => 0x30,
Self::Lit1 => 0x31,
Self::Lit2 => 0x32,
Self::Lit3 => 0x33,
Self::Lit4 => 0x34,
Self::Lit5 => 0x35,
Self::Lit6 => 0x36,
Self::Lit7 => 0x37,
Self::Lit8 => 0x38,
Self::Lit9 => 0x39,
Self::Lit10 => 0x3a,
Self::Lit11 => 0x3b,
Self::Lit12 => 0x3c,
Self::Lit13 => 0x3d,
Self::Lit14 => 0x3e,
Self::Lit15 => 0x3f,
Self::Lit16 => 0x40,
Self::Lit17 => 0x41,
Self::Lit18 => 0x42,
Self::Lit19 => 0x43,
Self::Lit20 => 0x44,
Self::Lit21 => 0x45,
Self::Lit22 => 0x46,
Self::Lit23 => 0x47,
Self::Lit24 => 0x48,
Self::Lit25 => 0x49,
Self::Lit26 => 0x4a,
Self::Lit27 => 0x4b,
Self::Lit28 => 0x4c,
Self::Lit29 => 0x4d,
Self::Lit30 => 0x4e,
Self::Lit31 => 0x4f,
Self::Reg0 => 0x50,
Self::Reg1 => 0x51,
Self::Reg2 => 0x52,
Self::Reg3 => 0x53,
Self::Reg4 => 0x54,
Self::Reg5 => 0x55,
Self::Reg6 => 0x56,
Self::Reg7 => 0x57,
Self::Reg8 => 0x58,
Self::Reg9 => 0x59,
Self::Reg10 => 0x5a,
Self::Reg11 => 0x5b,
Self::Reg12 => 0x5c,
Self::Reg13 => 0x5d,
Self::Reg14 => 0x5e,
Self::Reg15 => 0x5f,
Self::Reg16 => 0x60,
Self::Reg17 => 0x61,
Self::Reg18 => 0x62,
Self::Reg19 => 0x63,
Self::Reg20 => 0x64,
Self::Reg21 => 0x65,
Self::Reg22 => 0x66,
Self::Reg23 => 0x67,
Self::Reg24 => 0x68,
Self::Reg25 => 0x69,
Self::Reg26 => 0x6a,
Self::Reg27 => 0x6b,
Self::Reg28 => 0x6c,
Self::Reg29 => 0x6d,
Self::Reg30 => 0x6e,
Self::Reg31 => 0x6f,
Self::BReg0 => 0x70,
Self::BReg1 => 0x71,
Self::BReg2 => 0x72,
Self::BReg3 => 0x73,
Self::BReg4 => 0x74,
Self::BReg5 => 0x75,
Self::BReg6 => 0x76,
Self::BReg7 => 0x77,
Self::BReg8 => 0x78,
Self::BReg9 => 0x79,
Self::BReg10 => 0x7a,
Self::BReg11 => 0x7b,
Self::BReg12 => 0x7c,
Self::BReg13 => 0x7d,
Self::BReg14 => 0x7e,
Self::BReg15 => 0x7f,
Self::BReg16 => 0x80,
Self::BReg17 => 0x81,
Self::BReg18 => 0x82,
Self::BReg19 => 0x83,
Self::BReg20 => 0x84,
Self::BReg21 => 0x85,
Self::BReg22 => 0x86,
Self::BReg23 => 0x87,
Self::BReg24 => 0x88,
Self::BReg25 => 0x89,
Self::BReg26 => 0x8a,
Self::BReg27 => 0x8b,
Self::BReg28 => 0x8c,
Self::BReg29 => 0x8d,
Self::BReg30 => 0x8e,
Self::BReg31 => 0x8f,
Self::Piece => 0x93,
Self::BitPiece => 0x94,
Self::Call2 => 0x98,
Self::Call4 => 0x99,
Self::CallRef => 0x9a,
Self::FormTlsAddress => 0x9b,
Self::CallFrameCfa => 0x9c,
Self::ImplicitValue => 0x9e,
Self::StackValue => 0x9f,
Self::Addrx => 0xa0,
Self::ImplicitPointer => 0xa1,
Self::EntryValue => 0xa2,
Self::Constx => 0xa3,
Self::ConstType => 0xa4,
Self::RegvalType => 0xa5,
Self::DerefType => 0xa6,
Self::XDerefType => 0xa7,
Self::XDerefSize => 0x90,
Self::Convert => 0xa8,
Self::Reinterpret => 0xa9,
Self::GnuPushTlsAddress => 0xe0,
Self::GnuUninit => 0xf0,
Self::GnuDerefType => 0xf2,
Self::GnuConvert => 0xf3,
Self::GnuReinterpret => 0xf4,
Self::GnuParameterRef => 0xf5,
Self::GnuAddrIndex => 0xf6,
Self::GnuConstIndex => 0xf7,
Self::GnuEntryValue => 0xf8,
Self::Unknown(b) => b,
}
}
pub fn name(&self) -> &'static str {
match self {
Self::Reg => "DW_OP_reg",
Self::BReg => "DW_OP_breg",
Self::FbReg => "DW_OP_fbreg",
Self::Addr => "DW_OP_addr",
Self::Const1U => "DW_OP_const1u",
Self::Const1S => "DW_OP_const1s",
Self::Const1s => "DW_OP_const1s",
Self::Const2U => "DW_OP_const2u",
Self::Const2S => "DW_OP_const2s",
Self::Const4U => "DW_OP_const4u",
Self::Const4S => "DW_OP_const4s",
Self::Const8U => "DW_OP_const8u",
Self::Const8S => "DW_OP_const8s",
Self::ConstU => "DW_OP_constu",
Self::ConstS => "DW_OP_consts",
Self::Dup => "DW_OP_dup",
Self::Drop => "DW_OP_drop",
Self::Over => "DW_OP_over",
Self::Pick => "DW_OP_pick",
Self::Swap => "DW_OP_swap",
Self::Rot => "DW_OP_rot",
Self::XDeref => "DW_OP_xderef",
Self::Abs => "DW_OP_abs",
Self::And => "DW_OP_and",
Self::Div => "DW_OP_div",
Self::Minus => "DW_OP_minus",
Self::Mod => "DW_OP_mod",
Self::Mul => "DW_OP_mul",
Self::Neg => "DW_OP_neg",
Self::Not => "DW_OP_not",
Self::Or => "DW_OP_or",
Self::Plus => "DW_OP_plus",
Self::PlusUconst => "DW_OP_plus_uconst",
Self::Shl => "DW_OP_shl",
Self::Shr => "DW_OP_shr",
Self::Shra => "DW_OP_shra",
Self::Xor => "DW_OP_xor",
Self::Skip => "DW_OP_skip",
Self::Bra => "DW_OP_bra",
Self::Eq => "DW_OP_eq",
Self::Ge => "DW_OP_ge",
Self::Gt => "DW_OP_gt",
Self::Le => "DW_OP_le",
Self::Lt => "DW_OP_lt",
Self::Ne => "DW_OP_ne",
Self::Lit0 => "DW_OP_lit0",
Self::Piece => "DW_OP_piece",
Self::BitPiece => "DW_OP_bit_piece",
Self::ImplicitValue => "DW_OP_implicit_value",
Self::StackValue => "DW_OP_stack_value",
Self::ImplicitPointer => "DW_OP_implicit_pointer",
Self::EntryValue => "DW_OP_entry_value",
Self::ConstType => "DW_OP_const_type",
Self::RegvalType => "DW_OP_regval_type",
Self::DerefType => "DW_OP_deref_type",
Self::XDerefType => "DW_OP_xderef_type",
Self::XDerefSize => "DW_OP_xderef_size",
Self::Convert => "DW_OP_convert",
Self::Reinterpret => "DW_OP_reinterpret",
Self::Call2 => "DW_OP_call2",
Self::Call4 => "DW_OP_call4",
Self::CallRef => "DW_OP_call_ref",
Self::FormTlsAddress => "DW_OP_form_tls_address",
Self::CallFrameCfa => "DW_OP_call_frame_cfa",
Self::Addrx => "DW_OP_addrx",
Self::Constx => "DW_OP_constx",
Self::GnuPushTlsAddress => "DW_OP_GNU_push_tls_address",
Self::GnuUninit => "DW_OP_GNU_uninit",
Self::GnuDerefType => "DW_OP_GNU_deref_type",
Self::GnuConvert => "DW_OP_GNU_convert",
Self::GnuReinterpret => "DW_OP_GNU_reinterpret",
Self::GnuParameterRef => "DW_OP_GNU_parameter_ref",
Self::GnuAddrIndex => "DW_OP_GNU_addr_index",
Self::GnuConstIndex => "DW_OP_GNU_const_index",
Self::GnuEntryValue => "DW_OP_GNU_entry_value",
_ => "DW_OP_unknown",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExprValue {
Generic { bits: Vec<u8>, bit_size: usize },
Address {
addr: u64,
bit_size: usize,
address_space: u64,
},
Reg { reg_num: u64, bit_size: usize },
BReg {
reg_num: u64,
offset: i64,
bit_size: usize,
},
Value { value: u64, bit_size: usize },
SignedValue { value: i64, bit_size: usize },
Flag { present: bool },
}
impl ExprValue {
pub fn generic<T: AsRef<[u8]>>(data: T, bit_size: usize) -> Self {
Self::Generic {
bits: data.as_ref().to_vec(),
bit_size,
}
}
pub fn address(addr: u64, bit_size: usize, address_space: u64) -> Self {
Self::Address {
addr,
bit_size,
address_space,
}
}
pub fn reg(reg_num: u64, bit_size: usize) -> Self {
Self::Reg { reg_num, bit_size }
}
pub fn breg(reg_num: u64, offset: i64, bit_size: usize) -> Self {
Self::BReg {
reg_num,
offset,
bit_size,
}
}
pub fn value(val: u64, bit_size: usize) -> Self {
Self::Value {
value: val,
bit_size,
}
}
pub fn signed_value(val: i64, bit_size: usize) -> Self {
Self::SignedValue {
value: val,
bit_size,
}
}
pub fn as_u64(&self) -> Option<u64> {
match self {
Self::Value { value, .. } => Some(*value),
Self::SignedValue { value, .. } => Some(*value as u64),
Self::Address { addr, .. } => Some(*addr),
Self::Generic { bits, .. } => {
let mut buf = [0u8; 8];
let len = bits.len().min(8);
buf[..len].copy_from_slice(&bits[..len]);
Some(u64::from_le_bytes(buf))
}
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Self::SignedValue { value, .. } => Some(*value),
Self::Value { value, .. } => Some(*value as i64),
Self::Generic { bits, .. } => {
let mut buf = [0u8; 8];
let len = bits.len().min(8);
buf[..len].copy_from_slice(&bits[..len]);
Some(i64::from_le_bytes(buf))
}
_ => None,
}
}
pub fn bit_size(&self) -> usize {
match self {
Self::Generic { bit_size, .. }
| Self::Address { bit_size, .. }
| Self::Reg { bit_size, .. }
| Self::BReg { bit_size, .. }
| Self::Value { bit_size, .. }
| Self::SignedValue { bit_size, .. } => *bit_size,
Self::Flag { .. } => 1,
}
}
pub fn is_composite(&self) -> bool {
matches!(
self,
Self::Generic { .. } | Self::Address { .. } | Self::BReg { .. }
)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DwarfPiece {
pub expr: Vec<u8>,
pub size_in_bits: usize,
pub offset_in_bits: usize,
pub is_bit_piece: bool,
}
impl DwarfPiece {
pub fn byte_piece(expr: Vec<u8>, size_in_bytes: usize) -> Self {
Self {
expr,
size_in_bits: size_in_bytes * 8,
offset_in_bits: 0,
is_bit_piece: false,
}
}
pub fn bit_piece(expr: Vec<u8>, size_in_bits: usize, offset_in_bits: usize) -> Self {
Self {
expr,
size_in_bits,
offset_in_bits,
is_bit_piece: true,
}
}
pub fn size_in_bytes(&self) -> usize {
(self.size_in_bits + 7) / 8
}
}
#[derive(Debug, Clone)]
pub struct EvalResult {
pub value: Option<ExprValue>,
pub pieces: Vec<DwarfPiece>,
pub is_location: bool,
pub is_composite: bool,
pub is_stack_value: bool,
pub success: bool,
pub error: Option<String>,
pub warnings: Vec<String>,
}
impl Default for EvalResult {
fn default() -> Self {
Self {
value: None,
pieces: Vec::new(),
is_location: false,
is_composite: false,
is_stack_value: false,
success: true,
error: None,
warnings: Vec::new(),
}
}
}
impl EvalResult {
pub fn error(msg: impl Into<String>) -> Self {
Self {
value: None,
pieces: Vec::new(),
is_location: false,
is_composite: false,
is_stack_value: false,
success: false,
error: Some(msg.into()),
warnings: Vec::new(),
}
}
}
pub trait RegisterState {
fn get_register(&self, reg_num: u64) -> Option<u64>;
fn register_bit_size(&self, reg_num: u64) -> usize;
}
pub trait MemoryReader {
fn read_memory(&self, addr: u64, size: usize) -> Option<Vec<u8>>;
fn get_frame_base(&self) -> Option<u64>;
fn get_cfa(&self) -> Option<u64>;
fn get_tls_base(&self) -> Option<u64>;
fn resolve_addr_index(&self, index: u64) -> Option<u64>;
}
pub struct DefaultRegisterState;
impl RegisterState for DefaultRegisterState {
fn get_register(&self, _reg_num: u64) -> Option<u64> {
None
}
fn register_bit_size(&self, _reg_num: u64) -> usize {
64
}
}
pub struct DefaultMemoryReader;
impl MemoryReader for DefaultMemoryReader {
fn read_memory(&self, _addr: u64, _size: usize) -> Option<Vec<u8>> {
None
}
fn get_frame_base(&self) -> Option<u64> {
None
}
fn get_cfa(&self) -> Option<u64> {
None
}
fn get_tls_base(&self) -> Option<u64> {
None
}
fn resolve_addr_index(&self, _index: u64) -> Option<u64> {
None
}
}
pub struct DwarfDeep {
stack: Vec<ExprValue>,
expr: Vec<u8>,
pc: usize,
max_steps: usize,
step_count: usize,
address_size: usize,
result_size: usize,
pieces: Vec<DwarfPiece>,
stack_value_seen: bool,
implicit_value: Option<Vec<u8>>,
warnings: Vec<String>,
error: Option<String>,
in_entry_value: bool,
entry_value_stack: Vec<u8>,
entry_value_depth: usize,
max_entry_value_depth: usize,
}
impl DwarfDeep {
pub const DEFAULT_MAX_STEPS: usize = 10000;
pub const DEFAULT_MAX_ENTRY_VALUE_DEPTH: usize = 2;
pub fn new(expr: Vec<u8>, address_size: usize) -> Self {
Self {
stack: Vec::with_capacity(32),
expr,
pc: 0,
max_steps: Self::DEFAULT_MAX_STEPS,
step_count: 0,
address_size,
result_size: 0,
pieces: Vec::new(),
stack_value_seen: false,
implicit_value: None,
warnings: Vec::new(),
error: None,
in_entry_value: false,
entry_value_stack: Vec::new(),
entry_value_depth: 0,
max_entry_value_depth: Self::DEFAULT_MAX_ENTRY_VALUE_DEPTH,
}
}
pub fn set_max_steps(&mut self, max: usize) {
self.max_steps = max;
}
pub fn set_max_entry_value_depth(&mut self, depth: usize) {
self.max_entry_value_depth = depth;
}
pub fn set_result_size(&mut self, size: usize) {
self.result_size = size;
}
fn push(&mut self, val: ExprValue) {
self.stack.push(val);
}
fn pop(&mut self) -> Option<ExprValue> {
self.stack.pop()
}
fn read_u8(&mut self) -> Option<u8> {
if self.pc < self.expr.len() {
let b = self.expr[self.pc];
self.pc += 1;
Some(b)
} else {
self.error = Some("unexpected end of expression".into());
None
}
}
fn read_uleb128(&mut self) -> Option<u64> {
let mut result: u64 = 0;
let mut shift = 0;
loop {
if self.pc >= self.expr.len() {
self.error = Some("unexpected end of expression in LEB128".into());
return None;
}
let byte = self.expr[self.pc];
self.pc += 1;
result |= ((byte & 0x7f) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 64 {
self.error = Some("LEB128 too large".into());
return None;
}
}
Some(result)
}
fn read_sleb128(&mut self) -> Option<i64> {
let mut result: i64 = 0;
let mut shift = 0;
let mut byte;
loop {
if self.pc >= self.expr.len() {
self.error = Some("unexpected end of expression in SLEB128".into());
return None;
}
byte = self.expr[self.pc];
self.pc += 1;
result |= (((byte & 0x7f) as i64) << shift);
shift += 7;
if byte & 0x80 == 0 {
break;
}
if shift >= 64 {
self.error = Some("SLEB128 too large".into());
return None;
}
}
if shift < 64 && (byte & 0x40) != 0 {
result |= !0i64 << shift;
}
Some(result)
}
fn read_bytes(&mut self, n: usize) -> Option<Vec<u8>> {
if self.pc + n <= self.expr.len() {
let bytes = self.expr[self.pc..self.pc + n].to_vec();
self.pc += n;
Some(bytes)
} else {
self.error = Some("unexpected end of expression reading bytes".into());
None
}
}
fn read_u16(&mut self) -> Option<u16> {
let bytes = self.read_bytes(2)?;
Some(u16::from_le_bytes([bytes[0], bytes[1]]))
}
fn read_u32(&mut self) -> Option<u32> {
let bytes = self.read_bytes(4)?;
Some(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
fn read_u64(&mut self) -> Option<u64> {
let bytes = self.read_bytes(8)?;
Some(u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]))
}
fn read_address(&mut self) -> Option<u64> {
match self.address_size {
4 => self.read_u32().map(|v| v as u64),
8 => self.read_u64(),
_ => {
let bytes = self.read_bytes(self.address_size)?;
let mut buf = [0u8; 8];
let len = bytes.len().min(8);
buf[..len].copy_from_slice(&bytes[..len]);
Some(u64::from_le_bytes(buf))
}
}
}
fn skip(&mut self, n: i16) {
let new_pc = self.pc as i64 + n as i64;
if new_pc >= 0 && (new_pc as usize) <= self.expr.len() {
self.pc = new_pc as usize;
} else {
self.pc = self.expr.len();
}
}
fn execute_one<R: RegisterState, M: MemoryReader>(
&mut self,
regs: &R,
mem: &M,
op: u8,
) -> bool {
if self.step_count >= self.max_steps {
self.error = Some(format!(
"exceeded max evaluation steps ({})",
self.max_steps
));
return false;
}
self.step_count += 1;
match op {
b @ 0x30..=0x4f => {
let lit = (b - 0x30) as u64;
self.push(ExprValue::value(lit, self.address_size * 8));
true
}
b @ 0x50..=0x6f => {
let reg_num = (b - 0x50) as u64;
let bit_size = regs.register_bit_size(reg_num);
if let Some(val) = regs.get_register(reg_num) {
self.push(ExprValue::value(val, bit_size));
true
} else {
self.warnings
.push(format!("register {} unavailable at eval time", reg_num));
self.push(ExprValue::value(0, bit_size)); true
}
}
b @ 0x70..=0x8f => {
let reg_num = (b - 0x70) as u64;
if let Some(offset) = self.read_sleb128() {
let bit_size = regs.register_bit_size(reg_num);
self.push(ExprValue::breg(reg_num, offset, bit_size));
true
} else {
false
}
}
0x03 => {
if let Some(reg_num) = self.read_uleb128() {
let bit_size = regs.register_bit_size(reg_num);
if let Some(val) = regs.get_register(reg_num) {
self.push(ExprValue::value(val, bit_size));
} else {
self.warnings
.push(format!("register {} unavailable", reg_num));
self.push(ExprValue::value(0, bit_size));
}
true
} else {
false
}
}
0x04 => {
if let Some(reg_num) = self.read_uleb128() {
if let Some(offset) = self.read_sleb128() {
let bit_size = regs.register_bit_size(reg_num);
self.push(ExprValue::breg(reg_num, offset, bit_size));
return true;
}
}
false
}
0x06 => {
if let Some(addr) = self.read_address() {
self.push(ExprValue::address(addr, self.address_size * 8, 0));
true
} else {
false
}
}
0x08 => {
if let Some(v) = self.read_u8() {
self.push(ExprValue::value(v as u64, 8));
true
} else {
false
}
}
0x09 => {
if let Some(offset) = self.read_sleb128() {
if let Some(fb) = mem.get_frame_base() {
let addr = (fb as i64 + offset) as u64;
self.push(ExprValue::address(addr, self.address_size * 8, 0));
} else {
self.push(ExprValue::value(0, self.address_size * 8));
self.warnings.push("frame base not available".into());
}
true
} else {
false
}
}
0x0a => {
if self.pc < self.expr.len() {
let v = self.expr[self.pc] as i8;
self.pc += 1;
self.push(ExprValue::signed_value(v as i64, 8));
true
} else {
self.error = Some("unexpected end reading const1s".into());
false
}
}
0x0b => {
if let Some(v) = self.read_u16() {
self.push(ExprValue::value(v as u64, 16));
true
} else {
false
}
}
0x0c => {
if let Some(v) = self.read_u16() {
self.push(ExprValue::signed_value(v as i16 as i64, 16));
true
} else {
false
}
}
0x0d => {
if let Some(v) = self.read_u32() {
self.push(ExprValue::value(v as u64, 32));
true
} else {
false
}
}
0x0e => {
if let Some(v) = self.read_u32() {
self.push(ExprValue::signed_value(v as i32 as i64, 32));
true
} else {
false
}
}
0x0f => {
if let Some(v) = self.read_u64() {
self.push(ExprValue::value(v, 64));
true
} else {
false
}
}
0x10 => {
if let Some(v) = self.read_u64() {
self.push(ExprValue::signed_value(v as i64, 64));
true
} else {
false
}
}
0x11 => {
if let Some(v) = self.read_uleb128() {
self.push(ExprValue::value(v, self.address_size * 8));
true
} else {
false
}
}
0x12 => {
if let Some(v) = self.stack.last().cloned() {
self.push(v);
true
} else {
self.error = Some("DW_OP_dup on empty stack".into());
false
}
}
0x13 => {
if self.stack.pop().is_some() {
true
} else {
self.error = Some("DW_OP_drop on empty stack".into());
false
}
}
0x14 => {
if self.stack.len() >= 2 {
let v = self.stack[self.stack.len() - 2].clone();
self.push(v);
true
} else {
self.error = Some("DW_OP_over requires 2 stack elements".into());
false
}
}
0x15 => {
if let Some(idx) = self.read_u8() {
let idx = idx as usize;
if idx < self.stack.len() {
let v = self.stack[self.stack.len() - 1 - idx].clone();
self.push(v);
true
} else {
self.error = Some(format!("DW_OP_pick index {} out of range", idx));
false
}
} else {
false
}
}
0x16 => {
if self.stack.len() >= 2 {
let len = self.stack.len();
self.stack.swap(len - 1, len - 2);
true
} else {
self.error = Some("DW_OP_swap requires 2 elements".into());
false
}
}
0x17 => {
if self.stack.len() >= 3 {
let len = self.stack.len();
let a = self.stack.remove(len - 3);
self.stack.push(a);
true
} else {
self.error = Some("DW_OP_rot requires 3 elements".into());
false
}
}
0x18 => {
if self.stack.len() >= 2 {
let _addr_space = self.stack.pop();
let addr_val = self.stack.pop();
if let Some(addr) = addr_val.and_then(|v| v.as_u64()) {
let bit_size = self.result_size.max(self.address_size * 8);
let byte_size = (bit_size + 7) / 8;
if let Some(bytes) = mem.read_memory(addr, byte_size) {
self.push(ExprValue::generic(bytes, bit_size));
true
} else {
self.warnings
.push(format!("cannot deref memory at {:#x}", addr));
self.push(ExprValue::value(0, bit_size));
true
}
} else {
self.error = Some("DW_OP_xderef requires address".into());
false
}
} else {
self.error = Some("DW_OP_xderef requires 2 elements".into());
false
}
}
0x19 => {
if let Some(val) = self.pop() {
if let Some(v) = val.as_i64() {
self.push(ExprValue::signed_value(v.abs(), val.bit_size()));
true
} else {
self.error = Some("DW_OP_abs requires integer".into());
false
}
} else {
self.error = Some("DW_OP_abs on empty stack".into());
false
}
}
0x1a => self.binary_op(|a, b| a & b, "DW_OP_and"),
0x1b => self.binary_op_signed(
|a, b| {
if b == 0 {
None
} else {
Some(a / b)
}
},
"DW_OP_div",
),
0x1c => self.binary_op(|a, b| a.wrapping_sub(b), "DW_OP_minus"),
0x1d => self.binary_op_signed(
|a, b| {
if b == 0 {
None
} else {
Some(a % b)
}
},
"DW_OP_mod",
),
0x1e => self.binary_op(|a, b| a.wrapping_mul(b), "DW_OP_mul"),
0x1f => self.unary_op(|a| (!a).wrapping_add(1), "DW_OP_neg"),
0x20 => self.unary_op(|a| !a, "DW_OP_not"),
0x21 => self.binary_op(|a, b| a | b, "DW_OP_or"),
0x22 => self.binary_op(|a, b| a.wrapping_add(b), "DW_OP_plus"),
0x23 => {
if let Some(offset) = self.read_uleb128() {
if let Some(val) = self.pop() {
if let Some(v) = val.as_u64() {
self.push(ExprValue::value(v.wrapping_add(offset), val.bit_size()));
true
} else {
self.error = Some("DW_OP_plus_uconst requires integer".into());
false
}
} else {
self.error = Some("DW_OP_plus_uconst on empty stack".into());
false
}
} else {
false
}
}
0x24 => self.binary_op(|a, b| a.wrapping_shl(b as u32), "DW_OP_shl"),
0x25 => self.binary_op(|a, b| a.wrapping_shr(b as u32), "DW_OP_shr"),
0x26 => self.binary_op_signed(
|a, b| {
if b >= 64 {
if a < 0 {
Some(-1)
} else {
Some(0)
}
} else {
Some(a >> b)
}
},
"DW_OP_shra",
),
0x27 => self.binary_op(|a, b| a ^ b, "DW_OP_xor"),
0x28 => {
if let Some(offset) = self.read_u16() {
if let Some(val) = self.pop() {
if let Some(v) = val.as_u64() {
if v != 0 {
self.skip(offset as i16);
}
true
} else {
self.error = Some("DW_OP_bra requires integer".into());
false
}
} else {
self.error = Some("DW_OP_bra on empty stack".into());
false
}
} else {
false
}
}
0x29 => self.binary_op_bool(|a, b| a == b, "DW_OP_eq"),
0x2a => self.binary_op_bool_signed(|a, b| a >= b, "DW_OP_ge"),
0x2b => self.binary_op_bool_signed(|a, b| a > b, "DW_OP_gt"),
0x2c => self.binary_op_bool_signed(|a, b| a <= b, "DW_OP_le"),
0x2d => self.binary_op_bool_signed(|a, b| a < b, "DW_OP_lt"),
0x2e => self.binary_op_bool(|a, b| a != b, "DW_OP_ne"),
0x2f => {
if let Some(offset) = self.read_u16() {
self.skip(offset as i16);
true
} else {
false
}
}
0x93 => {
if let Some(size) = self.read_uleb128() {
if let Some(val) = self.pop() {
let expr_bytes = self.encode_value_as_expr(&val);
self.pieces
.push(DwarfPiece::byte_piece(expr_bytes, size as usize));
true
} else {
self.error = Some("DW_OP_piece on empty stack".into());
false
}
} else {
false
}
}
0x94 => {
if let Some(size_in_bits) = self.read_uleb128() {
if let Some(offset_in_bits) = self.read_uleb128() {
if let Some(val) = self.pop() {
let expr_bytes = self.encode_value_as_expr(&val);
self.pieces.push(DwarfPiece::bit_piece(
expr_bytes,
size_in_bits as usize,
offset_in_bits as usize,
));
true
} else {
self.error = Some("DW_OP_bit_piece on empty stack".into());
false
}
} else {
false
}
} else {
false
}
}
0x9e => {
if let Some(len) = self.read_uleb128() {
if let Some(bytes) = self.read_bytes(len as usize) {
self.implicit_value = Some(bytes);
true
} else {
false
}
} else {
false
}
}
0x9f => {
self.stack_value_seen = true;
true
}
0xa1 => {
if let Some(_offset) = self.read_sleb128() {
if self.address_size == 8 {
self.read_u64().map(|_| ());
} else {
self.read_u32().map(|_| ());
}
self.push(ExprValue::value(0, self.address_size * 8));
self.warnings
.push("DW_OP_implicit_pointer not fully resolved".into());
true
} else {
false
}
}
0xa2 | 0xf8 => {
if self.entry_value_depth >= self.max_entry_value_depth {
self.error = Some("max entry value nesting exceeded".into());
false
} else if let Some(len) = self.read_uleb128() {
let len = len as usize;
if self.pc + len <= self.expr.len() {
let _saved_stack = self.entry_value_stack.clone();
self.entry_value_depth += 1;
self.pc += len;
self.entry_value_depth -= 1;
self.warnings.push("DW_OP_entry_value noted".into());
self.push(ExprValue::value(0, self.address_size * 8));
true
} else {
self.error = Some("entry value length exceeds expression".into());
false
}
} else {
false
}
}
0xa0 => {
if let Some(index) = self.read_uleb128() {
if let Some(addr) = mem.resolve_addr_index(index) {
self.push(ExprValue::address(addr, self.address_size * 8, 0));
} else {
self.warnings
.push(format!("addr index {} unresolved", index));
self.push(ExprValue::value(0, self.address_size * 8));
}
true
} else {
false
}
}
0xa3 => {
if let Some(_index) = self.read_uleb128() {
self.push(ExprValue::value(0, self.address_size * 8));
self.warnings.push("DW_OP_constx unresolved".into());
true
} else {
false
}
}
0xa4 => {
if self.read_uleb128().is_some() {
if let Some(block_len) = self.read_u8() {
if let Some(bytes) = self.read_bytes(block_len as usize) {
let bit_size = block_len as usize * 8;
self.push(ExprValue::generic(bytes, bit_size));
return true;
}
}
}
false
}
0xa5 => {
if self.read_uleb128().is_some() {
if let Some(reg_num) = self.read_uleb128() {
let bit_size = regs.register_bit_size(reg_num);
if let Some(val) = regs.get_register(reg_num) {
self.push(ExprValue::value(val, bit_size));
} else {
self.push(ExprValue::value(0, bit_size));
}
return true;
}
}
false
}
0xa6 => {
if self.read_uleb128().is_some() && self.read_u8().is_some() {
if let Some(val) = self.pop() {
if let Some(addr) = val.as_u64() {
let size = self.address_size;
if let Some(bytes) = mem.read_memory(addr, size) {
self.push(ExprValue::generic(bytes, size * 8));
} else {
self.push(ExprValue::value(0, size * 8));
}
return true;
}
}
self.error = Some("DW_OP_deref_type requires address".into());
return false;
}
false
}
0xa7 => {
if self.read_uleb128().is_some() && self.read_u8().is_some() {
if self.stack.len() >= 2 {
let _as_val = self.pop();
let addr_val = self.pop();
if let Some(addr) = addr_val.and_then(|v| v.as_u64()) {
let size = self.address_size;
if let Some(bytes) = mem.read_memory(addr, size) {
self.push(ExprValue::generic(bytes, size * 8));
} else {
self.push(ExprValue::value(0, size * 8));
}
return true;
}
}
self.error = Some("DW_OP_xderef_type requires 2 elements".into());
return false;
}
false
}
0xa8 | 0xf3 => {
if self.read_uleb128().is_some() {
true
} else {
false
}
}
0xa9 | 0xf4 => {
if self.read_uleb128().is_some() {
true
} else {
false
}
}
0x98 => {
if self.read_u16().is_some() {
self.warnings.push("DW_OP_call2 skipped".into());
true
} else {
false
}
}
0x99 => {
if self.read_u32().is_some() {
self.warnings.push("DW_OP_call4 skipped".into());
true
} else {
false
}
}
0x9a => {
if self.address_size == 8 {
if self.read_u64().is_some() {
self.warnings.push("DW_OP_call_ref skipped".into());
true
} else {
false
}
} else if self.read_u32().is_some() {
self.warnings.push("DW_OP_call_ref skipped".into());
true
} else {
false
}
}
0x9b | 0xe0 => {
if let Some(tls_base) = mem.get_tls_base() {
if let Some(val) = self.pop() {
if let Some(offset) = val.as_u64() {
self.push(ExprValue::address(
tls_base.wrapping_add(offset),
self.address_size * 8,
0,
));
return true;
}
}
self.error = Some("DW_OP_form_tls_address requires offset on stack".into());
false
} else {
self.warnings.push("TLS base not available".into());
let _ = self.pop();
self.push(ExprValue::value(0, self.address_size * 8));
true
}
}
0x9c => {
if let Some(cfa) = mem.get_cfa() {
self.push(ExprValue::address(cfa, self.address_size * 8, 0));
} else {
self.push(ExprValue::value(0, self.address_size * 8));
self.warnings.push("CFA not available".into());
}
true
}
0xf0 => {
self.warnings.push("DW_OP_GNU_uninit skipped".into());
true
}
0xf2 => {
if self.read_uleb128().is_some() {
true
} else {
false
}
}
0xf5 => {
if self.read_uleb128().is_some() {
self.push(ExprValue::value(0, self.address_size * 8));
true
} else {
false
}
}
0xf6 | 0xf7 => {
if self.read_uleb128().is_some() {
self.push(ExprValue::value(0, self.address_size * 8));
true
} else {
false
}
}
other => {
self.warnings
.push(format!("unknown DW_OP opcode: {:#x}", other));
true }
}
}
fn binary_op<F>(&mut self, f: F, name: &str) -> bool
where
F: Fn(u64, u64) -> u64,
{
if self.stack.len() >= 2 {
let b = self.pop();
let a = self.pop();
if let (Some(av), Some(bv)) = (
a.as_ref().and_then(|x| x.as_u64()),
b.as_ref().and_then(|x| x.as_u64()),
) {
let bit_size = a.as_ref().map(|x| x.bit_size()).unwrap_or(64);
self.push(ExprValue::value(f(av, bv), bit_size));
true
} else {
self.error = Some(format!("{} requires integers", name));
false
}
} else {
self.error = Some(format!("{} requires 2 elements", name));
false
}
}
fn binary_op_signed<F>(&mut self, f: F, name: &str) -> bool
where
F: Fn(i64, i64) -> Option<i64>,
{
if self.stack.len() >= 2 {
let b = self.pop();
let a = self.pop();
if let (Some(av), Some(bv)) = (
a.as_ref().and_then(|x| x.as_i64()),
b.as_ref().and_then(|x| x.as_i64()),
) {
let bit_size = a.as_ref().map(|x| x.bit_size()).unwrap_or(64);
match f(av, bv) {
Some(v) => {
self.push(ExprValue::signed_value(v, bit_size));
true
}
None => {
self.error = Some(format!("{} division by zero", name));
false
}
}
} else {
self.error = Some(format!("{} requires integers", name));
false
}
} else {
self.error = Some(format!("{} requires 2 elements", name));
false
}
}
fn unary_op<F>(&mut self, f: F, name: &str) -> bool
where
F: Fn(u64) -> u64,
{
if let Some(val) = self.pop() {
if let Some(v) = val.as_u64() {
let bit_size = val.bit_size();
self.push(ExprValue::value(f(v), bit_size));
true
} else {
self.error = Some(format!("{} requires integer", name));
false
}
} else {
self.error = Some(format!("{} on empty stack", name));
false
}
}
fn binary_op_bool<F>(&mut self, f: F, name: &str) -> bool
where
F: Fn(u64, u64) -> bool,
{
if self.stack.len() >= 2 {
let b = self.pop();
let a = self.pop();
if let (Some(av), Some(bv)) = (
a.as_ref().and_then(|x| x.as_u64()),
b.as_ref().and_then(|x| x.as_u64()),
) {
let result = if f(av, bv) { 1u64 } else { 0u64 };
self.push(ExprValue::value(result, self.address_size * 8));
true
} else {
self.error = Some(format!("{} requires integers", name));
false
}
} else {
self.error = Some(format!("{} requires 2 elements", name));
false
}
}
fn binary_op_bool_signed<F>(&mut self, f: F, name: &str) -> bool
where
F: Fn(i64, i64) -> bool,
{
if self.stack.len() >= 2 {
let b = self.pop();
let a = self.pop();
if let (Some(av), Some(bv)) = (
a.as_ref().and_then(|x| x.as_i64()),
b.as_ref().and_then(|x| x.as_i64()),
) {
let result = if f(av, bv) { 1u64 } else { 0u64 };
self.push(ExprValue::value(result, self.address_size * 8));
true
} else {
self.error = Some(format!("{} requires integers", name));
false
}
} else {
self.error = Some(format!("{} requires 2 elements", name));
false
}
}
fn encode_value_as_expr(&self, val: &ExprValue) -> Vec<u8> {
match val {
ExprValue::Reg { reg_num, .. } => {
let mut v = vec![0x03u8];
let mut n = *reg_num;
loop {
let mut byte = (n & 0x7f) as u8;
n >>= 7;
if n != 0 {
byte |= 0x80;
}
v.push(byte);
if n == 0 {
break;
}
}
v
}
ExprValue::BReg {
reg_num, offset, ..
} => {
let mut v = vec![0x04u8];
let mut n = *reg_num;
loop {
let mut byte = (n & 0x7f) as u8;
n >>= 7;
if n != 0 {
byte |= 0x80;
}
v.push(byte);
if n == 0 {
break;
}
}
let mut off = *offset;
loop {
let mut byte = (off & 0x7f) as u8;
off >>= 7;
if !((off == 0 && (byte & 0x40) == 0) || (off == -1 && (byte & 0x40) != 0)) {
byte |= 0x80;
}
v.push(byte);
if (off == 0 && (byte & 0x40) == 0) || (off == -1 && (byte & 0x40) != 0) {
break;
}
}
v
}
ExprValue::Address { addr, .. } => {
let mut v = vec![0x06u8];
v.extend_from_slice(&addr.to_le_bytes()[..self.address_size]);
v
}
ExprValue::Value { value, .. } => {
let mut v = vec![0x11u8];
let mut n = *value;
loop {
let mut byte = (n & 0x7f) as u8;
n >>= 7;
if n != 0 {
byte |= 0x80;
}
v.push(byte);
if n == 0 {
break;
}
}
v
}
_ => vec![0x11, 0], }
}
pub fn evaluate<R: RegisterState, M: MemoryReader>(&mut self, regs: &R, mem: &M) -> EvalResult {
self.step_count = 0;
self.stack.clear();
self.pieces.clear();
self.stack_value_seen = false;
self.implicit_value = None;
self.warnings.clear();
self.error = None;
self.pc = 0;
while self.pc < self.expr.len() && self.error.is_none() {
if let Some(op) = self.read_u8() {
if !self.execute_one(regs, mem, op) {
break;
}
} else {
break;
}
}
let mut result = EvalResult::default();
if let Some(ref err) = self.error {
result.success = false;
result.error = Some(err.clone());
result.warnings = std::mem::take(&mut self.warnings);
return result;
}
if let Some(ref iv) = self.implicit_value {
result.value = Some(ExprValue::generic(iv, iv.len() * 8));
result.success = true;
result.warnings = std::mem::take(&mut self.warnings);
return result;
}
if !self.pieces.is_empty() {
result.is_composite = true;
result.pieces = std::mem::take(&mut self.pieces);
if let Some(val) = self.stack.pop() {
result.value = Some(val);
}
result.success = true;
result.warnings = std::mem::take(&mut self.warnings);
return result;
}
if self.stack_value_seen {
result.is_stack_value = true;
result.value = self.stack.pop();
result.success = true;
result.warnings = std::mem::take(&mut self.warnings);
return result;
}
if let Some(val) = self.stack.pop() {
result.value = Some(val);
result.is_location = matches!(
&result.value,
Some(ExprValue::Address { .. })
| Some(ExprValue::Reg { .. })
| Some(ExprValue::BReg { .. })
);
}
result.success = true;
result.warnings = std::mem::take(&mut self.warnings);
result
}
pub fn evaluate_as_u64<R: RegisterState, M: MemoryReader>(
&mut self,
regs: &R,
mem: &M,
) -> Option<u64> {
let result = self.evaluate(regs, mem);
result.value.and_then(|v| v.as_u64())
}
pub fn evaluate_as_i64<R: RegisterState, M: MemoryReader>(
&mut self,
regs: &R,
mem: &M,
) -> Option<i64> {
let result = self.evaluate(regs, mem);
result.value.and_then(|v| v.as_i64())
}
pub fn evaluate_as_address<R: RegisterState, M: MemoryReader>(
&mut self,
regs: &R,
mem: &M,
) -> Option<u64> {
let result = self.evaluate(regs, mem);
match result.value {
Some(ExprValue::Address { addr, .. }) => Some(addr),
Some(v) => v.as_u64(),
None => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LineStdOpcode {
Copy = 1,
AdvancePc = 2,
AdvanceLine = 3,
SetFile = 4,
SetColumn = 5,
NegateStmt = 6,
SetBasicBlock = 7,
ConstAddPc = 8,
FixedAdvancePc = 9,
SetPrologueEnd = 10,
SetEpilogueBegin = 11,
SetIsa = 12,
}
impl LineStdOpcode {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
1 => Some(Self::Copy),
2 => Some(Self::AdvancePc),
3 => Some(Self::AdvanceLine),
4 => Some(Self::SetFile),
5 => Some(Self::SetColumn),
6 => Some(Self::NegateStmt),
7 => Some(Self::SetBasicBlock),
8 => Some(Self::ConstAddPc),
9 => Some(Self::FixedAdvancePc),
10 => Some(Self::SetPrologueEnd),
11 => Some(Self::SetEpilogueBegin),
12 => Some(Self::SetIsa),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LineExtOpcode {
EndSequence = 1,
SetAddress = 2,
DefineFile = 3,
SetDiscriminator = 4,
}
impl LineExtOpcode {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
1 => Some(Self::EndSequence),
2 => Some(Self::SetAddress),
3 => Some(Self::DefineFile),
4 => Some(Self::SetDiscriminator),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DwarfFileEntry {
pub name: String,
pub directory_index: usize,
pub mod_time: u64,
pub file_length: u64,
}
impl DwarfFileEntry {
pub fn new(name: String, dir_index: usize) -> Self {
Self {
name,
directory_index: dir_index,
mod_time: 0,
file_length: 0,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DwarfDirectoryEntry {
pub path: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineTableRow {
pub address: u64,
pub op_index: usize,
pub file: usize,
pub line: usize,
pub column: usize,
pub is_stmt: bool,
pub basic_block: bool,
pub end_sequence: bool,
pub prologue_end: bool,
pub epilogue_begin: bool,
pub isa: usize,
pub discriminator: usize,
}
impl Default for LineTableRow {
fn default() -> Self {
Self {
address: 0,
op_index: 0,
file: 1,
line: 1,
column: 0,
is_stmt: false,
basic_block: false,
end_sequence: false,
prologue_end: false,
epilogue_begin: false,
isa: 0,
discriminator: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct LineTableHeader {
pub unit_length: u64,
pub format: DwarfFormat,
pub version: u16,
pub header_length: u64,
pub minimum_instruction_length: u8,
pub maximum_operations_per_instruction: u8,
pub default_is_stmt: bool,
pub line_base: i8,
pub line_range: u8,
pub opcode_base: u8,
pub standard_opcode_lengths: Vec<u8>,
pub directories: Vec<DwarfDirectoryEntry>,
pub file_names: Vec<DwarfFileEntry>,
}
impl LineTableHeader {
pub fn new() -> Self {
Self {
unit_length: 0,
format: DwarfFormat::Dwarf32,
version: 5,
header_length: 0,
minimum_instruction_length: 1,
maximum_operations_per_instruction: 1,
default_is_stmt: true,
line_base: -5,
line_range: 14,
opcode_base: 13,
standard_opcode_lengths: vec![0; 12],
directories: Vec::new(),
file_names: Vec::new(),
}
}
}
impl Default for LineTableHeader {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DwarfFormat {
Dwarf32,
Dwarf64,
}
impl DwarfFormat {
pub fn initial_length_size(&self) -> usize {
match self {
Self::Dwarf32 => 4,
Self::Dwarf64 => 12,
}
}
}
#[derive(Debug, Clone)]
pub struct FullLineTable {
pub header: LineTableHeader,
pub rows: Vec<LineTableRow>,
}
impl FullLineTable {
pub fn new() -> Self {
Self {
header: LineTableHeader::new(),
rows: Vec::new(),
}
}
pub fn parse(data: &[u8]) -> Result<Self, String> {
let mut table = Self::new();
table.parse_internal(data)?;
Ok(table)
}
fn parse_internal(&mut self, data: &[u8]) -> Result<(), String> {
if data.len() < 4 {
return Err("line table too short".into());
}
let mut offset = 0usize;
let len = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as u64;
offset += 4;
if len == 0xffffffff {
if data.len() < 16 {
return Err("DWARF-64 line table header too short".into());
}
let len64 = u64::from_le_bytes([
data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11],
]);
self.header.format = DwarfFormat::Dwarf64;
self.header.unit_length = len64 + 12;
offset += 8;
} else {
self.header.format = DwarfFormat::Dwarf32;
self.header.unit_length = len + 4;
}
if offset + 2 > data.len() {
return Err("truncated line table: no version".into());
}
self.header.version = u16::from_le_bytes([data[offset], data[offset + 1]]);
offset += 2;
if self.header.version < 2 || self.header.version > 5 {
return Err(format!(
"unsupported line table version {}",
self.header.version
));
}
if self.header.format == DwarfFormat::Dwarf64 {
if offset + 8 > data.len() {
return Err("truncated line table: no header length".into());
}
self.header.header_length = u64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
offset += 8;
} else {
if offset + 4 > data.len() {
return Err("truncated line table: no header length".into());
}
self.header.header_length = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as u64;
offset += 4;
}
let header_end = offset + self.header.header_length as usize;
if header_end > data.len() {
return Err("line table header length exceeds data".into());
}
self.header.minimum_instruction_length = data[offset];
offset += 1;
if self.header.version >= 4 {
self.header.maximum_operations_per_instruction = data[offset];
} else {
self.header.maximum_operations_per_instruction = 1;
}
offset += 1;
self.header.default_is_stmt = data[offset] != 0;
offset += 1;
self.header.line_base = data[offset] as i8;
offset += 1;
self.header.line_range = data[offset];
offset += 1;
self.header.opcode_base = data[offset];
offset += 1;
let num_std_opcodes = self.header.opcode_base as usize - 1;
self.header.standard_opcode_lengths.clear();
for _ in 0..num_std_opcodes {
if offset >= data.len() {
return Err("truncated line table: standard opcode lengths".into());
}
self.header.standard_opcode_lengths.push(data[offset]);
offset += 1;
}
self.header.directories.clear();
while offset < data.len() && data[offset] != 0 {
let dir = Self::read_null_terminated(data, &mut offset)?;
self.header
.directories
.push(DwarfDirectoryEntry { path: dir });
}
offset += 1;
self.header.file_names.clear();
while offset < data.len() && data[offset] != 0 {
let name = Self::read_null_terminated(data, &mut offset)?;
let dir_index = Self::read_uleb128_from(data, &mut offset)? as usize;
let mod_time = if self.header.version >= 5 {
Self::read_uleb128_from(data, &mut offset)?
} else {
0
};
let file_length = if self.header.version >= 5 {
Self::read_uleb128_from(data, &mut offset)?
} else {
0
};
self.header.file_names.push(DwarfFileEntry {
name,
directory_index: dir_index,
mod_time,
file_length,
});
}
offset += 1;
let mut row = LineTableRow::default();
row.is_stmt = self.header.default_is_stmt;
self.rows.clear();
while offset < header_end {
if data[offset] == 0 {
offset += 1;
if offset >= header_end {
break;
}
let ext_len = Self::read_uleb128_from(data, &mut offset)? as usize;
if offset >= header_end || offset + ext_len > header_end {
break;
}
let ext_opcode = data[offset];
offset += 1;
let ext_data_start = offset;
let ext_data_end = offset + ext_len - 1;
match LineExtOpcode::from_u8(ext_opcode) {
Some(LineExtOpcode::EndSequence) => {
row.end_sequence = true;
self.rows.push(row.clone());
row = LineTableRow::default();
row.is_stmt = self.header.default_is_stmt;
}
Some(LineExtOpcode::SetAddress) => {
let addr_size = self.header.format.initial_length_size();
if ext_data_end - ext_data_start >= addr_size {
row.address =
Self::read_target_address(&data[ext_data_start..], addr_size);
}
}
Some(LineExtOpcode::DefineFile) => {
let mut foff = ext_data_start;
let fname = Self::read_null_terminated(data, &mut foff)?;
let fdir = Self::read_uleb128_from(data, &mut foff)? as usize;
let fmod = Self::read_uleb128_from(data, &mut foff)?;
let flen = Self::read_uleb128_from(data, &mut foff)?;
self.header.file_names.push(DwarfFileEntry {
name: fname,
directory_index: fdir,
mod_time: fmod,
file_length: flen,
});
}
Some(LineExtOpcode::SetDiscriminator) => {
row.discriminator =
Self::read_uleb128_from(data, &mut offset.min(ext_data_start))?
as usize;
}
None => {
}
}
offset = ext_data_end;
} else if data[offset] < self.header.opcode_base {
let op = data[offset];
offset += 1;
match LineStdOpcode::from_u8(op) {
Some(LineStdOpcode::Copy) => {
self.rows.push(row.clone());
row.basic_block = false;
row.prologue_end = false;
row.epilogue_begin = false;
row.discriminator = 0;
}
Some(LineStdOpcode::AdvancePc) => {
let adv = Self::read_uleb128_from(data, &mut offset)?;
row.address += adv * self.header.minimum_instruction_length as u64;
}
Some(LineStdOpcode::AdvanceLine) => {
let adv = Self::read_sleb128_from(data, &mut offset)?;
row.line = (row.line as i64 + adv) as usize;
}
Some(LineStdOpcode::SetFile) => {
row.file = Self::read_uleb128_from(data, &mut offset)? as usize;
}
Some(LineStdOpcode::SetColumn) => {
row.column = Self::read_uleb128_from(data, &mut offset)? as usize;
}
Some(LineStdOpcode::NegateStmt) => {
row.is_stmt = !row.is_stmt;
}
Some(LineStdOpcode::SetBasicBlock) => {
row.basic_block = true;
}
Some(LineStdOpcode::ConstAddPc) => {
let adjusted =
(255 - self.header.opcode_base) as u64 / self.header.line_range as u64;
row.address += adjusted * self.header.minimum_instruction_length as u64;
}
Some(LineStdOpcode::FixedAdvancePc) => {
if offset < header_end {
let adv = data[offset] as u16 as u64;
offset += 1;
row.address += adv;
}
}
Some(LineStdOpcode::SetPrologueEnd) => {
row.prologue_end = true;
}
Some(LineStdOpcode::SetEpilogueBegin) => {
row.epilogue_begin = true;
}
Some(LineStdOpcode::SetIsa) => {
row.isa = Self::read_uleb128_from(data, &mut offset)? as usize;
}
None => {
let op_idx = (op - 1) as usize;
if op_idx < self.header.standard_opcode_lengths.len() {
let nargs = self.header.standard_opcode_lengths[op_idx] as usize;
offset += nargs;
}
}
}
} else {
let adjusted_opcode = data[offset] as i64 - self.header.opcode_base as i64;
offset += 1;
let line_inc = self.header.line_base as i64
+ (adjusted_opcode % self.header.line_range as i64);
let addr_inc = (adjusted_opcode / self.header.line_range as i64) as u64
* self.header.minimum_instruction_length as u64;
row.line = (row.line as i64 + line_inc) as usize;
row.address += addr_inc;
self.rows.push(row.clone());
row.basic_block = false;
row.prologue_end = false;
row.epilogue_begin = false;
row.discriminator = 0;
}
}
Ok(())
}
fn read_null_terminated(data: &[u8], offset: &mut usize) -> Result<String, String> {
let start = *offset;
while *offset < data.len() && data[*offset] != 0 {
*offset += 1;
}
let s = std::str::from_utf8(&data[start..*offset])
.map_err(|e| format!("invalid UTF-8 in line table: {}", e))?;
*offset += 1; Ok(s.to_string())
}
fn read_uleb128_from(data: &[u8], offset: &mut usize) -> Result<u64, String> {
let mut result: u64 = 0;
let mut shift = 0;
loop {
if *offset >= data.len() {
return Err("truncated ULEB128".into());
}
let byte = data[*offset];
*offset += 1;
result |= ((byte & 0x7f) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 64 {
return Err("ULEB128 too large".into());
}
}
Ok(result)
}
fn read_sleb128_from(data: &[u8], offset: &mut usize) -> Result<i64, String> {
let mut result: i64 = 0;
let mut shift = 0;
let mut byte;
loop {
if *offset >= data.len() {
return Err("truncated SLEB128".into());
}
byte = data[*offset];
*offset += 1;
result |= (((byte & 0x7f) as i64) << shift);
shift += 7;
if byte & 0x80 == 0 {
break;
}
if shift >= 64 {
return Err("SLEB128 too large".into());
}
}
if shift < 64 && (byte & 0x40) != 0 {
result |= !0i64 << shift;
}
Ok(result)
}
fn read_target_address(data: &[u8], addr_size: usize) -> u64 {
let mut buf = [0u8; 8];
let len = data.len().min(addr_size).min(8);
buf[..len].copy_from_slice(&data[..len]);
u64::from_le_bytes(buf)
}
pub fn lookup_address(&self, address: u64) -> Option<&LineTableRow> {
let mut best: Option<&LineTableRow> = None;
for row in &self.rows {
if row.address <= address && !row.end_sequence {
best = Some(row);
}
if row.address > address {
break;
}
}
best
}
pub fn entries_for_file(&self, file_index: usize) -> Vec<&LineTableRow> {
self.rows
.iter()
.filter(|r| r.file == file_index && !r.end_sequence)
.collect()
}
pub fn all_entries(&self) -> &[LineTableRow] {
&self.rows
}
pub fn file_name(&self, file_index: usize) -> Option<&str> {
if file_index == 0 {
None
} else if file_index <= self.header.file_names.len() {
Some(&self.header.file_names[file_index - 1].name)
} else {
None
}
}
pub fn directory_for_file(&self, file_index: usize) -> Option<&str> {
if file_index == 0 || file_index > self.header.file_names.len() {
return None;
}
let dir_idx = self.header.file_names[file_index - 1].directory_index;
if dir_idx == 0 {
None
} else if dir_idx <= self.header.directories.len() {
Some(&self.header.directories[dir_idx - 1].path)
} else {
None
}
}
}
impl Default for FullLineTable {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DecodedType {
Base(BaseType),
Pointer(PointerType),
Reference(ReferenceType),
Array(ArrayType),
Structure(StructureType),
Enumeration(EnumerationType),
Typedef(TypedefType),
Const(ConstType),
Volatile(VolatileType),
Restrict(RestrictType),
Function(FunctionType),
Subroutine(SubroutineType),
Unspecified,
Unknown(String),
}
impl fmt::Display for DecodedType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Base(t) => write!(f, "{}", t.name),
Self::Pointer(t) => {
write!(f, "{}*", t.pointee)
}
Self::Reference(t) => {
write!(f, "{}&", t.referent)
}
Self::Array(t) => {
write!(f, "{}[]", t.element_type)
}
Self::Structure(t) => match t.kind {
StructKind::Struct => write!(f, "struct {}", t.name),
StructKind::Class => write!(f, "class {}", t.name),
StructKind::Union => write!(f, "union {}", t.name),
},
Self::Enumeration(t) => write!(f, "enum {}", t.name),
Self::Typedef(t) => write!(f, "{}", t.name),
Self::Const(t) => write!(f, "const {}", t.inner),
Self::Volatile(t) => write!(f, "volatile {}", t.inner),
Self::Restrict(t) => write!(f, "restrict {}", t.inner),
Self::Function(t) => {
write!(f, "{}(", t.return_type)?;
for (i, p) in t.parameters.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", p)?;
}
write!(f, ")")
}
Self::Subroutine(t) => write!(f, "subroutine({})", t.return_type),
Self::Unspecified => write!(f, "void"),
Self::Unknown(s) => write!(f, "<unknown: {}>", s),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BaseTypeEncoding {
Address,
Boolean,
ComplexFloat,
Float,
Signed,
SignedChar,
Unsigned,
UnsignedChar,
ImaginaryFloat,
PackedDecimal,
NumericString,
Edited,
SignedFixed,
UnsignedFixed,
DecimalFloat,
Utf,
Ucs,
Ascii,
Unknown(u8),
}
impl BaseTypeEncoding {
pub fn from_u8(v: u8) -> Self {
match v {
0x01 => Self::Address,
0x02 => Self::Boolean,
0x03 => Self::ComplexFloat,
0x04 => Self::Float,
0x05 => Self::Signed,
0x06 => Self::SignedChar,
0x07 => Self::Unsigned,
0x08 => Self::UnsignedChar,
0x09 => Self::ImaginaryFloat,
0x0a => Self::PackedDecimal,
0x0b => Self::NumericString,
0x0c => Self::Edited,
0x0d => Self::SignedFixed,
0x0e => Self::UnsignedFixed,
0x0f => Self::DecimalFloat,
0x10 => Self::Utf,
0x11 => Self::Ucs,
0x12 => Self::Ascii,
_ => Self::Unknown(v),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BaseType {
pub name: String,
pub encoding: BaseTypeEncoding,
pub byte_size: usize,
pub bit_size: usize,
pub bit_offset: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PointerType {
pub pointee: Box<DecodedType>,
pub byte_size: usize,
pub address_class: Option<u64>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReferenceType {
pub referent: Box<DecodedType>,
pub byte_size: usize,
pub is_rvalue: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ArrayType {
pub element_type: Box<DecodedType>,
pub dimensions: Vec<ArrayDimension>,
pub byte_stride: Option<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArrayDimension {
pub lower_bound: i64,
pub count: Option<usize>,
pub upper_bound: Option<i64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StructKind {
Struct,
Class,
Union,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StructureType {
pub name: String,
pub kind: StructKind,
pub byte_size: usize,
pub members: Vec<StructMember>,
pub base_classes: Vec<BaseClass>,
pub is_declaration: bool,
pub has_vtable: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StructMember {
pub name: String,
pub member_type: Box<DecodedType>,
pub byte_offset: usize,
pub bit_offset: usize,
pub bit_size: usize,
pub accessibility: MemberAccess,
pub is_static: bool,
pub is_virtual: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemberAccess {
Public,
Protected,
Private,
None,
}
#[derive(Debug, Clone, PartialEq)]
pub struct BaseClass {
pub base_type: Box<DecodedType>,
pub byte_offset: usize,
pub accessibility: MemberAccess,
pub is_virtual: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EnumerationType {
pub name: String,
pub underlying_type: Box<DecodedType>,
pub byte_size: usize,
pub enumerators: Vec<Enumerator>,
pub is_scoped: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Enumerator {
pub name: String,
pub value: i64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypedefType {
pub name: String,
pub aliased_type: Box<DecodedType>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ConstType {
pub inner: Box<DecodedType>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct VolatileType {
pub inner: Box<DecodedType>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RestrictType {
pub inner: Box<DecodedType>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionType {
pub name: String,
pub return_type: Box<DecodedType>,
pub parameters: Vec<DecodedType>,
pub is_variadic: bool,
pub calling_convention: u8,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SubroutineType {
pub return_type: Box<DecodedType>,
pub parameters: Vec<DecodedType>,
}
pub struct DwarfTypeDecoder {
type_cache: HashMap<u64, DecodedType>,
address_size: usize,
}
impl DwarfTypeDecoder {
pub fn new(address_size: usize) -> Self {
Self {
type_cache: HashMap::new(),
address_size,
}
}
pub fn decode_die(
&mut self,
tag: u16,
attributes: &HashMap<String, DwarfAttributeValue>,
children: &[HashMap<String, DwarfAttributeValue>],
die_offset: u64,
) -> DecodedType {
use crate::dwarf::dwarf_tags::*;
match tag {
DW_TAG_base_type => self.decode_base_type(attributes),
DW_TAG_pointer_type => self.decode_pointer_type(attributes, die_offset),
DW_TAG_reference_type | DW_TAG_rvalue_reference_type => {
self.decode_reference_type(attributes, die_offset)
}
DW_TAG_array_type => self.decode_array_type(attributes, children),
DW_TAG_structure_type | DW_TAG_class_type | DW_TAG_union_type => {
self.decode_structure_type(tag, attributes, children, die_offset)
}
DW_TAG_enumeration_type => self.decode_enumeration_type(attributes, children),
DW_TAG_typedef => self.decode_typedef(attributes, die_offset),
DW_TAG_const_type => self.decode_const_type(attributes, die_offset),
DW_TAG_volatile_type => self.decode_volatile_type(attributes, die_offset),
DW_TAG_restrict_type => self.decode_restrict_type(attributes, die_offset),
DW_TAG_subroutine_type => self.decode_subroutine_type(attributes, children),
DW_TAG_subprogram => self.decode_function_type(attributes, children, die_offset),
DW_TAG_unspecified_type => DecodedType::Unspecified,
_ => DecodedType::Unknown(format!("unknown type tag: {}", tag)),
}
}
fn decode_base_type(&mut self, attrs: &HashMap<String, DwarfAttributeValue>) -> DecodedType {
let name = attrs
.get("DW_AT_name")
.and_then(|v| v.as_string())
.unwrap_or_else(|| "<anonymous>".to_string());
let encoding = attrs
.get("DW_AT_encoding")
.and_then(|v| v.as_u64())
.map(|v| BaseTypeEncoding::from_u8(v as u8))
.unwrap_or(BaseTypeEncoding::Unknown(0));
let byte_size = attrs
.get("DW_AT_byte_size")
.and_then(|v| v.as_u64())
.unwrap_or(0) as usize;
let bit_size = attrs
.get("DW_AT_bit_size")
.and_then(|v| v.as_u64())
.unwrap_or(byte_size as u64 * 8) as usize;
let bit_offset = attrs
.get("DW_AT_bit_offset")
.and_then(|v| v.as_u64())
.unwrap_or(0) as usize;
DecodedType::Base(BaseType {
name,
encoding,
byte_size,
bit_size,
bit_offset,
})
}
fn decode_pointer_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
_die_offset: u64,
) -> DecodedType {
let byte_size = attrs
.get("DW_AT_byte_size")
.and_then(|v| v.as_u64())
.unwrap_or(self.address_size as u64) as usize;
let address_class = attrs.get("DW_AT_address_class").and_then(|v| v.as_u64());
let pointee = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
DecodedType::Pointer(PointerType {
pointee: Box::new(pointee),
byte_size,
address_class,
})
}
fn decode_reference_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
_die_offset: u64,
) -> DecodedType {
let byte_size = attrs
.get("DW_AT_byte_size")
.and_then(|v| v.as_u64())
.unwrap_or(self.address_size as u64) as usize;
let referent = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
DecodedType::Reference(ReferenceType {
referent: Box::new(referent),
byte_size,
is_rvalue: false,
})
}
fn decode_array_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
children: &[HashMap<String, DwarfAttributeValue>],
) -> DecodedType {
let element_type = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
let byte_stride = attrs
.get("DW_AT_byte_stride")
.and_then(|v| v.as_u64())
.map(|s| s as usize);
let mut dimensions = Vec::new();
for child in children {
let lb = child
.get("DW_AT_lower_bound")
.and_then(|v| v.as_i64())
.unwrap_or(0);
let ub = child.get("DW_AT_upper_bound").and_then(|v| v.as_i64());
let count = child
.get("DW_AT_count")
.and_then(|v| v.as_u64())
.map(|c| c as usize);
dimensions.push(ArrayDimension {
lower_bound: lb,
count,
upper_bound: ub,
});
}
DecodedType::Array(ArrayType {
element_type: Box::new(element_type),
dimensions,
byte_stride,
})
}
fn decode_structure_type(
&mut self,
tag: u16,
attrs: &HashMap<String, DwarfAttributeValue>,
children: &[HashMap<String, DwarfAttributeValue>],
_die_offset: u64,
) -> DecodedType {
use crate::dwarf::dwarf_tags::*;
let name = attrs
.get("DW_AT_name")
.and_then(|v| v.as_string())
.unwrap_or_else(|| "<anonymous>".to_string());
let kind = match tag {
DW_TAG_class_type => StructKind::Class,
DW_TAG_union_type => StructKind::Union,
_ => StructKind::Struct,
};
let byte_size = attrs
.get("DW_AT_byte_size")
.and_then(|v| v.as_u64())
.unwrap_or(0) as usize;
let is_declaration = attrs
.get("DW_AT_declaration")
.map(|v| v.as_bool())
.unwrap_or(false);
let has_vtable = children
.iter()
.any(|c| c.get("DW_AT_vtable_elem_location").is_some());
let mut members = Vec::new();
let mut base_classes = Vec::new();
for child in children {
let child_tag = child.get("DW_AT_tag").and_then(|v| v.as_u64()).unwrap_or(0) as u16;
match child_tag {
DW_TAG_member => {
let mname = child
.get("DW_AT_name")
.and_then(|v| v.as_string())
.unwrap_or_else(|| "<anonymous>".to_string());
let mtype = child
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
let byte_offset = child
.get("DW_AT_data_member_location")
.and_then(|v| v.as_u64())
.unwrap_or(0) as usize;
let bit_offset = child
.get("DW_AT_bit_offset")
.and_then(|v| v.as_u64())
.unwrap_or(0) as usize;
let bit_size = child
.get("DW_AT_bit_size")
.and_then(|v| v.as_u64())
.unwrap_or(0) as usize;
let accessibility = child
.get("DW_AT_accessibility")
.and_then(|v| v.as_u64())
.map(|v| match v {
1 => MemberAccess::Public,
2 => MemberAccess::Protected,
3 => MemberAccess::Private,
_ => MemberAccess::None,
})
.unwrap_or(MemberAccess::None);
let is_static = child
.get("DW_AT_external")
.map(|v| v.as_bool())
.unwrap_or(false);
members.push(StructMember {
name: mname,
member_type: Box::new(mtype),
byte_offset,
bit_offset,
bit_size,
accessibility,
is_static,
is_virtual: false,
});
}
DW_TAG_inheritance => {
let btype = child
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
let byte_offset = child
.get("DW_AT_data_member_location")
.and_then(|v| v.as_u64())
.unwrap_or(0) as usize;
let accessibility = child
.get("DW_AT_accessibility")
.and_then(|v| v.as_u64())
.map(|v| match v {
1 => MemberAccess::Public,
2 => MemberAccess::Protected,
3 => MemberAccess::Private,
_ => MemberAccess::None,
})
.unwrap_or(MemberAccess::None);
let is_virtual = child
.get("DW_AT_virtuality")
.map(|v| v.as_u64() == Some(1))
.unwrap_or(false);
base_classes.push(BaseClass {
base_type: Box::new(btype),
byte_offset,
accessibility,
is_virtual,
});
}
_ => {}
}
}
DecodedType::Structure(StructureType {
name,
kind,
byte_size,
members,
base_classes,
is_declaration,
has_vtable,
})
}
fn decode_enumeration_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
children: &[HashMap<String, DwarfAttributeValue>],
) -> DecodedType {
let name = attrs
.get("DW_AT_name")
.and_then(|v| v.as_string())
.unwrap_or_else(|| "<anonymous>".to_string());
let byte_size = attrs
.get("DW_AT_byte_size")
.and_then(|v| v.as_u64())
.unwrap_or(4) as usize;
let underlying_type = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Base(BaseType {
name: "int".into(),
encoding: BaseTypeEncoding::Signed,
byte_size,
bit_size: byte_size * 8,
bit_offset: 0,
}));
let is_scoped = attrs
.get("DW_AT_enum_class")
.map(|v| v.as_bool())
.unwrap_or(false);
let mut enumerators = Vec::new();
for child in children {
let ename = child
.get("DW_AT_name")
.and_then(|v| v.as_string())
.unwrap_or_else(|| "<anonymous>".to_string());
let evalue = child
.get("DW_AT_const_value")
.and_then(|v| v.as_i64())
.unwrap_or(0);
enumerators.push(Enumerator {
name: ename,
value: evalue,
});
}
DecodedType::Enumeration(EnumerationType {
name,
underlying_type: Box::new(underlying_type),
byte_size,
enumerators,
is_scoped,
})
}
fn decode_typedef(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
_die_offset: u64,
) -> DecodedType {
let name = attrs
.get("DW_AT_name")
.and_then(|v| v.as_string())
.unwrap_or_else(|| "<anonymous>".to_string());
let aliased = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
DecodedType::Typedef(TypedefType {
name,
aliased_type: Box::new(aliased),
})
}
fn decode_const_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
_die_offset: u64,
) -> DecodedType {
let inner = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
DecodedType::Const(ConstType {
inner: Box::new(inner),
})
}
fn decode_volatile_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
_die_offset: u64,
) -> DecodedType {
let inner = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
DecodedType::Volatile(VolatileType {
inner: Box::new(inner),
})
}
fn decode_restrict_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
_die_offset: u64,
) -> DecodedType {
let inner = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unknown("<unresolved>".into()));
DecodedType::Restrict(RestrictType {
inner: Box::new(inner),
})
}
fn decode_subroutine_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
children: &[HashMap<String, DwarfAttributeValue>],
) -> DecodedType {
let return_type = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unspecified);
let mut parameters = Vec::new();
for child in children {
let ptype = child
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unspecified);
parameters.push(ptype);
}
DecodedType::Subroutine(SubroutineType {
return_type: Box::new(return_type),
parameters,
})
}
fn decode_function_type(
&mut self,
attrs: &HashMap<String, DwarfAttributeValue>,
children: &[HashMap<String, DwarfAttributeValue>],
_die_offset: u64,
) -> DecodedType {
let name = attrs
.get("DW_AT_name")
.and_then(|v| v.as_string())
.unwrap_or_else(|| "<anonymous>".to_string());
let return_type = attrs
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unspecified);
let calling_convention = attrs
.get("DW_AT_calling_convention")
.and_then(|v| v.as_u64())
.unwrap_or(0) as u8;
let is_variadic = children
.iter()
.any(|c| c.get("DW_AT_unspecified_parameters").is_some());
let mut parameters = Vec::new();
for child in children {
let ptype = child
.get("DW_AT_type")
.and_then(|v| v.as_reference())
.and_then(|off| self.type_cache.get(&off))
.cloned()
.unwrap_or(DecodedType::Unspecified);
parameters.push(ptype);
}
DecodedType::Function(FunctionType {
name,
return_type: Box::new(return_type),
parameters,
is_variadic,
calling_convention,
})
}
pub fn cache_type(&mut self, offset: u64, dtype: DecodedType) {
self.type_cache.insert(offset, dtype);
}
pub fn get_type(&self, offset: u64) -> Option<&DecodedType> {
self.type_cache.get(&offset)
}
pub fn decode_type_string(
&mut self,
tag: u16,
attributes: &HashMap<String, DwarfAttributeValue>,
children: &[HashMap<String, DwarfAttributeValue>],
die_offset: u64,
) -> String {
let dtype = self.decode_die(tag, attributes, children, die_offset);
format!("{}", dtype)
}
}
#[derive(Debug, Clone)]
pub enum DwarfAttributeValue {
String(String),
U64(u64),
I64(i64),
Bool(bool),
Reference(u64),
Exprloc(Vec<u8>),
Block(Vec<u8>),
Address(u64),
FlagPresent,
None,
}
impl DwarfAttributeValue {
pub fn as_string(&self) -> Option<String> {
match self {
Self::String(s) => Some(s.clone()),
_ => None,
}
}
pub fn as_u64(&self) -> Option<u64> {
match self {
Self::U64(v) => Some(*v),
Self::Address(v) => Some(*v),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Self::I64(v) => Some(*v),
_ => None,
}
}
pub fn as_bool(&self) -> bool {
match self {
Self::Bool(b) => *b,
Self::FlagPresent => true,
Self::U64(v) => *v != 0,
_ => false,
}
}
pub fn as_reference(&self) -> Option<u64> {
match self {
Self::Reference(v) => Some(*v),
Self::U64(v) => Some(*v),
_ => None,
}
}
pub fn as_exprloc(&self) -> Option<&[u8]> {
match self {
Self::Exprloc(v) => Some(v),
Self::Block(v) => Some(v),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct LocationListEntry {
pub start_address: u64,
pub end_address: u64,
pub expr: Vec<u8>,
pub is_base_address: bool,
pub is_end_of_list: bool,
}
impl LocationListEntry {
pub fn new(start: u64, end: u64, expr: Vec<u8>) -> Self {
Self {
start_address: start,
end_address: end,
expr,
is_base_address: false,
is_end_of_list: false,
}
}
pub fn base_address(addr: u64) -> Self {
Self {
start_address: addr,
end_address: 0,
expr: Vec::new(),
is_base_address: true,
is_end_of_list: false,
}
}
pub fn end_of_list() -> Self {
Self {
start_address: 0,
end_address: 0,
expr: Vec::new(),
is_base_address: false,
is_end_of_list: true,
}
}
}
pub struct LocationListParser {
address_size: usize,
}
impl LocationListParser {
pub fn new(address_size: usize) -> Self {
Self { address_size }
}
pub fn parse_loclists_dwarf5(
&self,
data: &[u8],
base_address: u64,
) -> Result<Vec<LocationListEntry>, String> {
let mut entries = Vec::new();
let mut offset = 0usize;
let mut current_base = base_address;
while offset < data.len() {
if data[offset] == 0 {
break;
}
let entry_kind = data[offset];
offset += 1;
match entry_kind {
0x00 => {
entries.push(LocationListEntry::end_of_list());
break;
}
0x01 => {
let _idx = Self::read_uleb128(data, &mut offset)?;
continue;
}
0x02 => {
let _start_idx = Self::read_uleb128(data, &mut offset)?;
let _end_idx = Self::read_uleb128(data, &mut offset)?;
let expr_len = Self::read_uleb128(data, &mut offset)? as usize;
if offset + expr_len > data.len() {
return Err("location expression exceeds data".into());
}
let expr = data[offset..offset + expr_len].to_vec();
offset += expr_len;
entries.push(LocationListEntry::new(0, 0, expr));
}
0x03 => {
let _start_idx = Self::read_uleb128(data, &mut offset)?;
let _length = Self::read_uleb128(data, &mut offset)?;
let expr_len = Self::read_uleb128(data, &mut offset)? as usize;
if offset + expr_len > data.len() {
return Err("location expression exceeds data".into());
}
let expr = data[offset..offset + expr_len].to_vec();
offset += expr_len;
entries.push(LocationListEntry::new(0, 0, expr));
}
0x04 => {
let start_off = Self::read_uleb128(data, &mut offset)?;
let end_off = Self::read_uleb128(data, &mut offset)?;
let expr_len = Self::read_uleb128(data, &mut offset)? as usize;
if offset + expr_len > data.len() {
return Err("location expression exceeds data".into());
}
let expr = data[offset..offset + expr_len].to_vec();
offset += expr_len;
entries.push(LocationListEntry::new(
current_base.wrapping_add(start_off),
current_base.wrapping_add(end_off),
expr,
));
}
0x05 => {
let expr_len = Self::read_uleb128(data, &mut offset)? as usize;
if offset + expr_len > data.len() {
return Err("location expression exceeds data".into());
}
let expr = data[offset..offset + expr_len].to_vec();
offset += expr_len;
entries.push(LocationListEntry::new(0, !0u64, expr));
}
0x06 => {
current_base = Self::read_address(data, &mut offset, self.address_size)?;
entries.push(LocationListEntry::base_address(current_base));
}
0x07 => {
let start = Self::read_address(data, &mut offset, self.address_size)?;
let end = Self::read_address(data, &mut offset, self.address_size)?;
let expr_len = Self::read_uleb128(data, &mut offset)? as usize;
if offset + expr_len > data.len() {
return Err("location expression exceeds data".into());
}
let expr = data[offset..offset + expr_len].to_vec();
offset += expr_len;
entries.push(LocationListEntry::new(start, end, expr));
}
0x08 => {
let start = Self::read_address(data, &mut offset, self.address_size)?;
let length = Self::read_uleb128(data, &mut offset)?;
let expr_len = Self::read_uleb128(data, &mut offset)? as usize;
if offset + expr_len > data.len() {
return Err("location expression exceeds data".into());
}
let expr = data[offset..offset + expr_len].to_vec();
offset += expr_len;
entries.push(LocationListEntry::new(
start,
start.wrapping_add(length),
expr,
));
}
_ => {
break;
}
}
}
Ok(entries)
}
pub fn parse_loc_dwarf4(&self, data: &[u8]) -> Result<Vec<LocationListEntry>, String> {
let mut entries = Vec::new();
let mut offset = 0usize;
let addr_size = self.address_size;
while offset + 2 * addr_size + 2 <= data.len() {
let start = Self::read_address(data, &mut offset, addr_size)?;
let end = Self::read_address(data, &mut offset, addr_size)?;
if start == 0 && end == 0 {
entries.push(LocationListEntry::end_of_list());
break;
}
if start == !0u64 >> (64 - addr_size * 8) {
entries.push(LocationListEntry::base_address(end));
continue;
}
let expr_len = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
offset += 2;
if offset + expr_len > data.len() {
return Err("DWARF 4 location expression length exceeds data".into());
}
let expr = data[offset..offset + expr_len].to_vec();
offset += expr_len;
entries.push(LocationListEntry::new(start, end, expr));
}
Ok(entries)
}
fn read_uleb128(data: &[u8], offset: &mut usize) -> Result<u64, String> {
let mut result: u64 = 0;
let mut shift = 0;
loop {
if *offset >= data.len() {
return Err("truncated LEB128".into());
}
let byte = data[*offset];
*offset += 1;
result |= ((byte & 0x7f) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 64 {
return Err("LEB128 too large".into());
}
}
Ok(result)
}
fn read_address(data: &[u8], offset: &mut usize, addr_size: usize) -> Result<u64, String> {
if *offset + addr_size > data.len() {
return Err("truncated address".into());
}
let mut buf = [0u8; 8];
let len = addr_size.min(8);
buf[..len].copy_from_slice(&data[*offset..*offset + len]);
*offset += addr_size;
Ok(u64::from_le_bytes(buf))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RangeListEntry {
pub start: u64,
pub end: u64,
}
impl RangeListEntry {
pub fn new(start: u64, end: u64) -> Self {
Self { start, end }
}
}
pub struct RangeListParser {
address_size: usize,
}
impl RangeListParser {
pub fn new(address_size: usize) -> Self {
Self { address_size }
}
pub fn parse_rnglists_dwarf5(
&self,
data: &[u8],
base_address: u64,
) -> Result<Vec<RangeListEntry>, String> {
let mut ranges = Vec::new();
let mut offset = 0usize;
let mut current_base = base_address;
while offset < data.len() {
let entry_kind = data[offset];
offset += 1;
match entry_kind {
0x00 => break, 0x01 => {
let _idx = Self::read_uleb128(data, &mut offset)?;
continue;
}
0x02 => {
let _s = Self::read_uleb128(data, &mut offset)?;
let _e = Self::read_uleb128(data, &mut offset)?;
ranges.push(RangeListEntry::new(0, 0));
}
0x03 => {
let _s = Self::read_uleb128(data, &mut offset)?;
let _l = Self::read_uleb128(data, &mut offset)?;
ranges.push(RangeListEntry::new(0, 0));
}
0x04 => {
let s = Self::read_uleb128(data, &mut offset)?;
let e = Self::read_uleb128(data, &mut offset)?;
ranges.push(RangeListEntry::new(
current_base.wrapping_add(s),
current_base.wrapping_add(e),
));
}
0x05 => {
current_base = Self::read_address(data, &mut offset, self.address_size)?;
}
0x06 => {
let s = Self::read_address(data, &mut offset, self.address_size)?;
let e = Self::read_address(data, &mut offset, self.address_size)?;
ranges.push(RangeListEntry::new(s, e));
}
0x07 => {
let s = Self::read_address(data, &mut offset, self.address_size)?;
let l = Self::read_uleb128(data, &mut offset)?;
ranges.push(RangeListEntry::new(s, s.wrapping_add(l)));
}
_ => break,
}
}
Ok(ranges)
}
pub fn parse_ranges_dwarf4(&self, data: &[u8]) -> Result<Vec<RangeListEntry>, String> {
let mut ranges = Vec::new();
let mut offset = 0usize;
let addr_size = self.address_size;
let max_addr: u64 = if addr_size == 4 { !0u32 as u64 } else { !0u64 };
let base_marker = if addr_size == 4 { !0u32 as u64 } else { !0u64 };
while offset + 2 * addr_size <= data.len() {
let start = Self::read_address(data, &mut offset, addr_size)?;
let end = Self::read_address(data, &mut offset, addr_size)?;
if start == 0 && end == 0 {
break;
}
if start == base_marker {
continue;
}
ranges.push(RangeListEntry::new(start, end));
}
Ok(ranges)
}
fn read_uleb128(data: &[u8], offset: &mut usize) -> Result<u64, String> {
let mut result: u64 = 0;
let mut shift = 0;
loop {
if *offset >= data.len() {
return Err("truncated LEB128".into());
}
let byte = data[*offset];
*offset += 1;
result |= ((byte & 0x7f) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 64 {
return Err("LEB128 too large".into());
}
}
Ok(result)
}
fn read_address(data: &[u8], offset: &mut usize, addr_size: usize) -> Result<u64, String> {
if *offset + addr_size > data.len() {
return Err("truncated address".into());
}
let mut buf = [0u8; 8];
let len = addr_size.min(8);
buf[..len].copy_from_slice(&data[*offset..*offset + len]);
*offset += addr_size;
Ok(u64::from_le_bytes(buf))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MacroOpcode {
Define = 0x01,
Undef = 0x02,
StartFile = 0x03,
EndFile = 0x04,
DefineStrd = 0x05,
UndefStrd = 0x06,
Import = 0x07,
DefineSup = 0x08,
UndefSup = 0x09,
ImportSup = 0x0a,
DefineStrdx = 0x0b,
UndefStrdx = 0x0c,
TransparentInclude = 0x10, GnuDefine = 0x30, GnuUndef = 0x31,
GnuStartFile = 0x32,
GnuEndFile = 0x33,
GnuDefineIndirect = 0x34,
GnuUndefIndirect = 0x35,
GnuTransparentInclude = 0x36,
GnuDefineIndirectAlt = 0x37,
GnuUndefIndirectAlt = 0x38,
GnuTransparentIncludeAlt = 0x39,
}
impl MacroOpcode {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0x01 => Some(Self::Define),
0x02 => Some(Self::Undef),
0x03 => Some(Self::StartFile),
0x04 => Some(Self::EndFile),
0x05 => Some(Self::DefineStrd),
0x06 => Some(Self::UndefStrd),
0x07 => Some(Self::Import),
0x08 => Some(Self::DefineSup),
0x09 => Some(Self::UndefSup),
0x0a => Some(Self::ImportSup),
0x0b => Some(Self::DefineStrdx),
0x0c => Some(Self::UndefStrdx),
0x10 => Some(Self::TransparentInclude),
0x30 => Some(Self::GnuDefine),
0x31 => Some(Self::GnuUndef),
0x32 => Some(Self::GnuStartFile),
0x33 => Some(Self::GnuEndFile),
0x34 => Some(Self::GnuDefineIndirect),
0x35 => Some(Self::GnuUndefIndirect),
0x36 => Some(Self::GnuTransparentInclude),
0x37 => Some(Self::GnuDefineIndirectAlt),
0x38 => Some(Self::GnuUndefIndirectAlt),
0x39 => Some(Self::GnuTransparentIncludeAlt),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub enum MacroEntry {
Define {
line: u64,
name: String,
value: String,
},
Undef {
line: u64,
name: String,
},
StartFile {
line: u64,
file_index: u64,
},
EndFile,
Import {
offset: u64,
},
TransparentInclude {
offset: u64,
},
}
pub struct MacroParser {
data: Vec<u8>,
pos: usize,
version: u16,
flags: u8,
debug_line_offset: u64,
is_dwarf64: bool,
}
impl MacroParser {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
pos: 0,
version: 5,
flags: 0,
debug_line_offset: 0,
is_dwarf64: false,
}
}
pub fn parse(&mut self) -> Result<Vec<MacroEntry>, String> {
if self.data.len() < 4 {
return Err("macro section too short".into());
}
self.version = u16::from_le_bytes([self.data[0], self.data[1]]);
self.pos = 2;
if self.pos < self.data.len() {
self.flags = self.data[self.pos];
self.pos += 1;
}
let offset_size_flag = self.flags & 0x01;
let offset_size = if offset_size_flag != 0 { 8 } else { 4 };
if self.pos + offset_size > self.data.len() {
return Err("macro: truncated debug_line_offset".into());
}
self.debug_line_offset = Self::read_uint(&self.data, &mut self.pos, offset_size);
let mut entries = Vec::new();
while self.pos < self.data.len() {
let opcode_byte = self.data[self.pos];
self.pos += 1;
match opcode_byte {
0x00 => break,
b @ 0x01..=0x0c | b @ 0x10..=0x12 | b @ 0x30..=0x39 => {
let entry = self.parse_opcode(b)?;
entries.push(entry);
}
_ => {
break;
}
}
}
Ok(entries)
}
fn parse_opcode(&mut self, op: u8) -> Result<MacroEntry, String> {
match op {
0x01 | 0x08 => {
let line = Self::read_uleb128(&self.data, &mut self.pos)?;
let name = Self::read_null_terminated(&self.data, &mut self.pos)?;
let value = Self::read_null_terminated(&self.data, &mut self.pos)?;
Ok(MacroEntry::Define { line, name, value })
}
0x05 | 0x0b => {
let line = Self::read_uleb128(&self.data, &mut self.pos)?;
let offset = Self::read_uleb128(&self.data, &mut self.pos)?;
Ok(MacroEntry::Define {
line,
name: format!("<str[{}]>", offset),
value: String::new(),
})
}
0x02 | 0x09 => {
let line = Self::read_uleb128(&self.data, &mut self.pos)?;
let name = Self::read_null_terminated(&self.data, &mut self.pos)?;
Ok(MacroEntry::Undef { line, name })
}
0x06 | 0x0c => {
let line = Self::read_uleb128(&self.data, &mut self.pos)?;
let _offset = Self::read_uleb128(&self.data, &mut self.pos)?;
Ok(MacroEntry::Undef {
line,
name: "<str>".to_string(),
})
}
0x03 => {
let line = Self::read_uleb128(&self.data, &mut self.pos)?;
let file_index = Self::read_uleb128(&self.data, &mut self.pos)?;
Ok(MacroEntry::StartFile { line, file_index })
}
0x04 => Ok(MacroEntry::EndFile),
0x07 | 0x0a => {
let offset = Self::read_uleb128(&self.data, &mut self.pos)?;
Ok(MacroEntry::Import { offset })
}
0x10 => {
let offset = Self::read_uleb128(&self.data, &mut self.pos)?;
Ok(MacroEntry::Import { offset })
}
0x30 | 0x37 => {
let line = Self::read_uleb128(&self.data, &mut self.pos)?;
let name = Self::read_null_terminated(&self.data, &mut self.pos)?;
let value = Self::read_null_terminated(&self.data, &mut self.pos)?;
Ok(MacroEntry::Define { line, name, value })
}
0x31 | 0x38 => {
let line = Self::read_uleb128(&self.data, &mut self.pos)?;
let name = Self::read_null_terminated(&self.data, &mut self.pos)?;
Ok(MacroEntry::Undef { line, name })
}
0x32 => {
let line = Self::read_uleb128(&self.data, &mut self.pos)?;
let file_index = Self::read_uleb128(&self.data, &mut self.pos)?;
Ok(MacroEntry::StartFile { line, file_index })
}
0x33 => Ok(MacroEntry::EndFile),
0x34 | 0x35 | 0x36 | 0x39 => {
let _offset = Self::read_uleb128(&self.data, &mut self.pos)?;
Ok(MacroEntry::Import { offset: 0 })
}
_ => Err(format!("unknown macro opcode {:#x}", op)),
}
}
fn read_uleb128(data: &[u8], offset: &mut usize) -> Result<u64, String> {
let mut result: u64 = 0;
let mut shift = 0;
loop {
if *offset >= data.len() {
return Err("truncated ULEB128 in macro".into());
}
let byte = data[*offset];
*offset += 1;
result |= ((byte & 0x7f) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 64 {
return Err("ULEB128 too large in macro".into());
}
}
Ok(result)
}
fn read_null_terminated(data: &[u8], offset: &mut usize) -> Result<String, String> {
let start = *offset;
while *offset < data.len() && data[*offset] != 0 {
*offset += 1;
}
let s = std::str::from_utf8(&data[start..*offset])
.map_err(|e| format!("invalid UTF-8 in macro: {}", e))?;
*offset += 1;
Ok(s.to_string())
}
fn read_uint(data: &[u8], offset: &mut usize, size: usize) -> u64 {
let mut buf = [0u8; 8];
let end = (*offset + size).min(data.len());
let len = (end - *offset).min(8);
buf[..len].copy_from_slice(&data[*offset..*offset + len]);
*offset += size;
u64::from_le_bytes(buf)
}
}
#[derive(Debug, Clone)]
pub struct NameIndexEntry {
pub name: String,
pub die_offset: u64,
pub parent_offset: Option<u64>,
pub tag: u16,
pub hash: u64,
}
pub struct NameIndexParser {
data: Vec<u8>,
pos: usize,
}
impl NameIndexParser {
pub fn new(data: Vec<u8>) -> Self {
Self { data, pos: 0 }
}
pub fn parse(&mut self) -> Result<(u32, Vec<NameIndexEntry>), String> {
if self.data.len() < 12 {
return Err("name index too short".into());
}
let unit_length =
u32::from_le_bytes([self.data[0], self.data[1], self.data[2], self.data[3]]);
self.pos = 4;
if unit_length as usize + 4 > self.data.len() {
return Err("name index unit length exceeds data".into());
}
let version = u16::from_le_bytes([self.data[4], self.data[5]]);
self.pos = 6;
if version != 5 {
return Err(format!("unsupported .debug_names version {}", version));
}
let _padding = u16::from_le_bytes([self.data[6], self.data[7]]);
self.pos = 8;
let cu_count =
u32::from_le_bytes([self.data[8], self.data[9], self.data[10], self.data[11]]);
self.pos = 12;
let local_tu_count = u32::from_le_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
]);
self.pos += 4;
let foreign_tu_count = u32::from_le_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
]);
self.pos += 4;
let bucket_count = u32::from_le_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
]);
self.pos += 4;
let name_count = u32::from_le_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
]);
self.pos += 4;
let abbrev_table_size = u32::from_le_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
]);
self.pos += 4;
let _aug_size = self.data[self.pos] as usize;
self.pos += 1;
self.pos += _aug_size;
self.pos += cu_count as usize * 8;
self.pos += local_tu_count as usize * 8;
self.pos += foreign_tu_count as usize * 8;
self.pos += bucket_count as usize * 4;
let mut hashes = Vec::with_capacity(name_count as usize);
for _ in 0..name_count {
if self.pos + 8 > self.data.len() {
return Err("truncated hash array".into());
}
let hash = u64::from_le_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
self.data[self.pos + 4],
self.data[self.pos + 5],
self.data[self.pos + 6],
self.data[self.pos + 7],
]);
hashes.push(hash);
self.pos += 8;
}
let mut name_offsets = Vec::with_capacity(name_count as usize);
for _ in 0..name_count {
if self.pos + 4 > self.data.len() {
return Err("truncated name offsets".into());
}
let off = u32::from_le_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
]);
name_offsets.push(off);
self.pos += 4;
}
let mut entry_offsets = Vec::with_capacity(name_count as usize);
for _ in 0..name_count {
if self.pos + 4 > self.data.len() {
return Err("truncated entry offsets".into());
}
let off = u32::from_le_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
]);
entry_offsets.push(off);
self.pos += 4;
}
self.pos += abbrev_table_size as usize;
let mut names = Vec::with_capacity(name_count as usize);
for off in &name_offsets {
let string_pos = *off as usize;
if string_pos < self.data.len() {
let end = self.data[string_pos..]
.iter()
.position(|&b| b == 0)
.unwrap_or(self.data.len() - string_pos);
let s = std::str::from_utf8(&self.data[string_pos..string_pos + end])
.unwrap_or("<invalid>");
names.push(s.to_string());
} else {
names.push("<out of range>".to_string());
}
}
let mut entries = Vec::with_capacity(name_count as usize);
for i in 0..name_count as usize {
entries.push(NameIndexEntry {
name: names.get(i).cloned().unwrap_or_default(),
die_offset: 0, parent_offset: None,
tag: 0,
hash: hashes.get(i).copied().unwrap_or(0),
});
}
Ok((name_count, entries))
}
}
pub struct StringOffsetsTable {
offsets: Vec<u64>,
base_offset: u64,
format: DwarfFormat,
}
impl StringOffsetsTable {
pub fn parse(data: &[u8], base_offset: u64) -> Result<Self, String> {
if data.len() < 8 {
return Err("string offsets table too short".into());
}
let mut offset = 0usize;
let ulen = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
offset += 4;
let format = if ulen == 0xffffffff {
offset += 8; DwarfFormat::Dwarf64
} else {
DwarfFormat::Dwarf32
};
if offset + 2 > data.len() {
return Err("truncated string offsets header".into());
}
let _version = u16::from_le_bytes([data[offset], data[offset + 1]]);
offset += 2;
if offset + 2 > data.len() {
return Err("truncated string offsets header padding".into());
}
offset += 2;
let offset_size = match format {
DwarfFormat::Dwarf32 => 4usize,
DwarfFormat::Dwarf64 => 8usize,
};
let mut offsets = Vec::new();
while offset + offset_size <= data.len() {
let val = if offset_size == 8 {
u64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
])
} else {
u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]) as u64
};
offsets.push(val);
offset += offset_size;
}
Ok(Self {
offsets,
base_offset,
format,
})
}
pub fn get_offset(&self, index: usize) -> Option<u64> {
self.offsets.get(index).copied()
}
pub fn resolve_string(&self, index: usize, debug_str: &[u8]) -> Option<String> {
let str_offset = self.get_offset(index)? as usize;
if str_offset >= debug_str.len() {
return None;
}
let end = debug_str[str_offset..]
.iter()
.position(|&b| b == 0)
.unwrap_or(debug_str.len() - str_offset);
std::str::from_utf8(&debug_str[str_offset..str_offset + end])
.ok()
.map(|s| s.to_string())
}
pub fn len(&self) -> usize {
self.offsets.len()
}
pub fn is_empty(&self) -> bool {
self.offsets.is_empty()
}
}
pub struct DwarfDeepUtils {
pub address_size: usize,
pub dwarf_version: u16,
line_tables: HashMap<u64, FullLineTable>,
type_cache: HashMap<u64, DecodedType>,
}
impl DwarfDeepUtils {
pub fn new(address_size: usize, dwarf_version: u16) -> Self {
Self {
address_size,
dwarf_version,
line_tables: HashMap::new(),
type_cache: HashMap::new(),
}
}
pub fn eval_as_address(expr_bytes: &[u8], address_size: usize) -> Option<u64> {
let mut evaluator = DwarfDeep::new(expr_bytes.to_vec(), address_size);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = evaluator.evaluate(®s, &mem);
result.value.and_then(|v| v.as_u64())
}
pub fn eval_as_value(expr_bytes: &[u8], address_size: usize) -> Option<u64> {
Self::eval_as_address(expr_bytes, address_size)
}
pub fn parse_line_table(&mut self, data: &[u8], offset: u64) -> Result<&FullLineTable, String> {
use std::collections::hash_map::Entry;
match self.line_tables.entry(offset) {
Entry::Occupied(entry) => Ok(entry.into_mut()),
Entry::Vacant(entry) => {
let off = offset as usize;
if off >= data.len() {
return Err("line table offset out of range".into());
}
let table = FullLineTable::parse(&data[off..])?;
Ok(entry.insert(table))
}
}
}
pub fn lookup_source(
&mut self,
line_data: &[u8],
line_offset: u64,
address: u64,
) -> Option<(String, usize, usize)> {
let table = self.parse_line_table(line_data, line_offset).ok()?;
let row = table.lookup_address(address)?;
let file = table.file_name(row.file)?;
Some((file.to_string(), row.line, row.column))
}
pub fn decode_type(
&mut self,
tag: u16,
attrs: &HashMap<String, DwarfAttributeValue>,
children: &[HashMap<String, DwarfAttributeValue>],
offset: u64,
) -> DecodedType {
let mut decoder = DwarfTypeDecoder::new(self.address_size);
for (&off, t) in &self.type_cache {
decoder.cache_type(off, t.clone());
}
let result = decoder.decode_die(tag, attrs, children, offset);
self.type_cache.insert(offset, result.clone());
result
}
pub fn disassemble_expression(expr: &[u8]) -> String {
let mut result = String::new();
let mut pos = 0usize;
while pos < expr.len() {
let op = expr[pos];
pos += 1;
let opcode = DwarfOpcode::from_byte(op);
result.push_str(opcode.name());
match op {
0x03 | 0x04 | 0x09 | 0xa5 => {
if pos < expr.len() {
let (val, new_pos) = Self::read_uleb128_at(expr, pos);
result.push_str(&format!(" {}", val));
pos = new_pos;
}
}
0x06 => {
if pos + 8 <= expr.len() {
let addr = u64::from_le_bytes([
expr[pos],
expr[pos + 1],
expr[pos + 2],
expr[pos + 3],
expr[pos + 4],
expr[pos + 5],
expr[pos + 6],
expr[pos + 7],
]);
result.push_str(&format!(" {:#x}", addr));
pos += 8;
}
}
_ => {}
}
result.push('\n');
}
result
}
fn read_uleb128_at(data: &[u8], start: usize) -> (u64, usize) {
let mut result: u64 = 0;
let mut shift = 0;
let mut pos = start;
while pos < data.len() {
let byte = data[pos];
pos += 1;
result |= ((byte & 0x7f) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 64 {
break;
}
}
(result, pos)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dwarf_opcode_roundtrip() {
let opcodes = [
0x03, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
];
for &b in &opcodes {
let op = DwarfOpcode::from_byte(b);
let rb = op.to_byte();
if b >= 0x30 && b <= 0x4f {
continue;
}
if b >= 0x50 && b <= 0x6f {
continue;
}
if b >= 0x70 && b <= 0x8f {
continue;
}
assert_eq!(rb, b, "Failed roundtrip for {:#x}", b);
}
}
#[test]
fn test_expr_value_creation() {
let v = ExprValue::value(42, 64);
assert_eq!(v.as_u64(), Some(42));
assert_eq!(v.bit_size(), 64);
let v = ExprValue::signed_value(-10, 64);
assert_eq!(v.as_i64(), Some(-10));
let v = ExprValue::address(0x1000, 64, 0);
assert_eq!(v.as_u64(), Some(0x1000));
let v = ExprValue::reg(5, 64);
assert!(!v.is_composite());
}
#[test]
fn test_eval_literal_stack() {
let expr = vec![0x30, 0x31, 0x32]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(2));
}
#[test]
fn test_eval_plus() {
let expr = vec![0x33, 0x34, 0x22]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(7));
}
#[test]
fn test_eval_minus() {
let expr = vec![0x3a, 0x33, 0x1c]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(7));
}
#[test]
fn test_eval_mul() {
let expr = vec![0x34, 0x35, 0x1e]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(20));
}
#[test]
fn test_eval_div() {
let expr = vec![0x3a, 0x33, 0x1b]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(3));
}
#[test]
fn test_eval_and() {
let expr = vec![0x38, 0x3a, 0x1a]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(8));
}
#[test]
fn test_eval_or() {
let expr = vec![0x38, 0x32, 0x21]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(10));
}
#[test]
fn test_eval_xor() {
let expr = vec![0x39, 0x35, 0x27]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(12));
}
#[test]
fn test_eval_shl() {
let expr = vec![0x31, 0x33, 0x24]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(8));
}
#[test]
fn test_eval_shr() {
let expr = vec![0x38, 0x31, 0x25]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(4));
}
#[test]
fn test_eval_neg() {
let expr = vec![0x31, 0x1f]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(!1u64 + 1));
}
#[test]
fn test_eval_not() {
let expr = vec![0x30, 0x20]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(!0u64));
}
#[test]
fn test_eval_abs() {
let expr = vec![0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(1));
}
#[test]
fn test_eval_mod() {
let expr = vec![0x3a, 0x33, 0x1d]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(1));
}
#[test]
fn test_eval_eq_true() {
let expr = vec![0x33, 0x33, 0x29]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(1));
}
#[test]
fn test_eval_eq_false() {
let expr = vec![0x33, 0x34, 0x29]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(0));
}
#[test]
fn test_eval_ge() {
let expr = vec![0x34, 0x33, 0x2a]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(1));
}
#[test]
fn test_eval_gt() {
let expr = vec![0x33, 0x34, 0x2b]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(0));
}
#[test]
fn test_eval_le() {
let expr = vec![0x33, 0x34, 0x2c]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(1));
}
#[test]
fn test_eval_lt() {
let expr = vec![0x34, 0x33, 0x2d]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(0));
}
#[test]
fn test_eval_ne_true() {
let expr = vec![0x33, 0x34, 0x2e]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(1));
}
#[test]
fn test_eval_dup() {
let expr = vec![0x30, 0x12]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(eval.stack.len(), 0);
}
#[test]
fn test_eval_drop() {
let expr = vec![0x30, 0x31, 0x13]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(0));
}
#[test]
fn test_eval_over() {
let expr = vec![0x30, 0x31, 0x14, 0x13]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(0));
}
#[test]
fn test_eval_swap() {
let expr = vec![0x30, 0x31, 0x16]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(0));
}
#[test]
fn test_eval_plus_uconst() {
let expr = vec![0x33, 0x23, 0x05]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(8));
}
#[test]
fn test_eval_const1u() {
let expr = vec![0x08, 42]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(42));
}
#[test]
fn test_eval_const1s_positive() {
let expr = vec![0x0a, 42]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(42));
}
#[test]
fn test_eval_const1s_negative() {
let expr = vec![0x0a, 0xfe]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_i64()), Some(-2));
}
#[test]
fn test_eval_constu() {
let expr = vec![0x11, 0x80, 0x01]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(128));
}
#[test]
fn test_eval_const4u() {
let expr = {
let mut v = vec![0x0d];
v.extend_from_slice(&0xdeadbeefu32.to_le_bytes());
v
};
let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(0xdeadbeef));
}
#[test]
fn test_eval_const8u() {
let expr = {
let mut v = vec![0x0f];
v.extend_from_slice(&0x123456789abcdef0u64.to_le_bytes());
v
};
let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert_eq!(
result.value.and_then(|v| v.as_u64()),
Some(0x123456789abcdef0)
);
}
#[test]
fn test_eval_addr_8byte() {
let expr = {
let mut v = vec![0x06];
v.extend_from_slice(&0x400000u64.to_le_bytes());
v
};
let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
match result.value {
Some(ExprValue::Address { addr, .. }) => assert_eq!(addr, 0x400000),
_ => panic!("expected Address"),
}
}
#[test]
fn test_eval_cfa() {
let expr = vec![0x9c]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
}
#[test]
fn test_eval_stack_value() {
let expr = vec![0x33, 0x9f]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert!(result.is_stack_value);
assert_eq!(result.value.and_then(|v| v.as_u64()), Some(3));
}
#[test]
fn test_eval_max_steps() {
let mut expr = Vec::new();
for _ in 0..1000 {
expr.push(0x30); expr.push(0x12); }
let mut eval = DwarfDeep::new(expr, 8);
eval.set_max_steps(10);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(!result.success);
assert!(result.error.unwrap().contains("max"));
}
#[test]
fn test_eval_empty_expression() {
let expr = vec![];
let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(result.success);
assert!(result.value.is_none());
}
#[test]
fn test_eval_div_by_zero() {
let expr = vec![0x33, 0x30, 0x1b]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(!result.success);
assert!(result.error.unwrap().contains("zero"));
}
#[test]
fn test_eval_mod_by_zero() {
let expr = vec![0x33, 0x30, 0x1d]; let mut eval = DwarfDeep::new(expr, 8);
let regs = DefaultRegisterState;
let mem = DefaultMemoryReader;
let result = eval.evaluate(®s, &mem);
assert!(!result.success);
}
#[test]
fn test_line_table_empty_parse() {
let data = [0u8; 4]; let result = FullLineTable::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_line_table_minimal_header() {
let mut data = Vec::new();
data.extend_from_slice(&20u32.to_le_bytes());
data.extend_from_slice(&4u16.to_le_bytes());
data.extend_from_slice(&(4 + 10u32).to_le_bytes());
data.push(1);
data.push(1);
data.push(1);
data.push(0xfb);
data.push(14);
data.push(13);
for _ in 0..12 {
data.push(1);
}
data.push(0);
data.push(0);
let result = FullLineTable::parse(&data);
assert!(result.is_ok(), "Parse failed: {:?}", result.err());
let table = result.unwrap();
assert_eq!(table.header.version, 4);
assert_eq!(table.header.minimum_instruction_length, 1);
assert_eq!(table.header.default_is_stmt, true);
assert_eq!(table.header.line_base, -5);
assert_eq!(table.header.line_range, 14);
}
#[test]
fn test_line_table_lookup() {
let mut header = LineTableHeader::new();
header
.file_names
.push(DwarfFileEntry::new("test.c".into(), 0));
let mut table = FullLineTable::new();
table.header = header;
table.rows.push(LineTableRow {
address: 0x1000,
file: 1,
line: 10,
column: 5,
is_stmt: true,
..Default::default()
});
table.rows.push(LineTableRow {
address: 0x1100,
file: 1,
line: 20,
column: 3,
is_stmt: true,
..Default::default()
});
let row = table.lookup_address(0x1050);
assert!(row.is_some());
assert_eq!(row.unwrap().line, 10);
let row = table.lookup_address(0x2000);
assert!(row.is_none() || row.unwrap().line == 20);
}
#[test]
fn test_type_decoder_base_type() {
let mut decoder = DwarfTypeDecoder::new(8);
let mut attrs = HashMap::new();
attrs.insert(
"DW_AT_name".into(),
DwarfAttributeValue::String("int".into()),
);
attrs.insert("DW_AT_encoding".into(), DwarfAttributeValue::U64(5)); attrs.insert("DW_AT_byte_size".into(), DwarfAttributeValue::U64(4));
let result = decoder.decode_die(crate::dwarf::dwarf_tags::DW_TAG_base_type, &attrs, &[], 0);
match result {
DecodedType::Base(b) => {
assert_eq!(b.name, "int");
assert_eq!(b.byte_size, 4);
}
other => panic!("expected Base, got {:?}", other),
}
}
#[test]
fn test_type_decoder_pointer() {
let mut decoder = DwarfTypeDecoder::new(8);
let mut base_attrs = HashMap::new();
base_attrs.insert(
"DW_AT_name".into(),
DwarfAttributeValue::String("char".into()),
);
base_attrs.insert("DW_AT_encoding".into(), DwarfAttributeValue::U64(6)); base_attrs.insert("DW_AT_byte_size".into(), DwarfAttributeValue::U64(1));
let base = decoder.decode_die(
crate::dwarf::dwarf_tags::DW_TAG_base_type,
&base_attrs,
&[],
10,
);
decoder.cache_type(10, base);
let mut ptr_attrs = HashMap::new();
ptr_attrs.insert("DW_AT_type".into(), DwarfAttributeValue::Reference(10));
ptr_attrs.insert("DW_AT_byte_size".into(), DwarfAttributeValue::U64(8));
let result = decoder.decode_die(
crate::dwarf::dwarf_tags::DW_TAG_pointer_type,
&ptr_attrs,
&[],
20,
);
match result {
DecodedType::Pointer(p) => {
assert_eq!(p.byte_size, 8);
}
other => panic!("expected Pointer, got {:?}", other),
}
}
#[test]
fn test_type_decoder_const() {
let mut decoder = DwarfTypeDecoder::new(8);
let mut base_attrs = HashMap::new();
base_attrs.insert(
"DW_AT_name".into(),
DwarfAttributeValue::String("int".into()),
);
base_attrs.insert("DW_AT_encoding".into(), DwarfAttributeValue::U64(5));
base_attrs.insert("DW_AT_byte_size".into(), DwarfAttributeValue::U64(4));
let base = decoder.decode_die(
crate::dwarf::dwarf_tags::DW_TAG_base_type,
&base_attrs,
&[],
10,
);
decoder.cache_type(10, base);
let mut const_attrs = HashMap::new();
const_attrs.insert("DW_AT_type".into(), DwarfAttributeValue::Reference(10));
let result = decoder.decode_die(
crate::dwarf::dwarf_tags::DW_TAG_const_type,
&const_attrs,
&[],
30,
);
match result {
DecodedType::Const(_) => {}
other => panic!("expected Const, got {:?}", other),
}
}
#[test]
fn test_type_display() {
let t = DecodedType::Base(BaseType {
name: "int".into(),
encoding: BaseTypeEncoding::Signed,
byte_size: 4,
bit_size: 32,
bit_offset: 0,
});
assert_eq!(format!("{}", t), "int");
let t = DecodedType::Pointer(PointerType {
pointee: Box::new(DecodedType::Base(BaseType {
name: "char".into(),
encoding: BaseTypeEncoding::UnsignedChar,
byte_size: 1,
bit_size: 8,
bit_offset: 0,
})),
byte_size: 8,
address_class: None,
});
assert_eq!(format!("{}", t), "char*");
let t = DecodedType::Const(ConstType {
inner: Box::new(DecodedType::Base(BaseType {
name: "int".into(),
encoding: BaseTypeEncoding::Signed,
byte_size: 4,
bit_size: 32,
bit_offset: 0,
})),
});
assert_eq!(format!("{}", t), "const int");
let t = DecodedType::Unspecified;
assert_eq!(format!("{}", t), "void");
}
#[test]
fn test_type_decoder_struct() {
let mut decoder = DwarfTypeDecoder::new(8);
let mut attrs = HashMap::new();
attrs.insert(
"DW_AT_name".into(),
DwarfAttributeValue::String("Point".into()),
);
attrs.insert("DW_AT_byte_size".into(), DwarfAttributeValue::U64(8));
let children: Vec<HashMap<String, DwarfAttributeValue>> = vec![];
let result = decoder.decode_die(
crate::dwarf::dwarf_tags::DW_TAG_structure_type,
&attrs,
&children,
100,
);
match result {
DecodedType::Structure(s) => {
assert_eq!(s.name, "Point");
assert_eq!(s.byte_size, 8);
assert_eq!(s.kind, StructKind::Struct);
}
other => panic!("expected Structure, got {:?}", other),
}
}
#[test]
fn test_type_decoder_enum() {
let mut decoder = DwarfTypeDecoder::new(8);
let mut attrs = HashMap::new();
attrs.insert(
"DW_AT_name".into(),
DwarfAttributeValue::String("Color".into()),
);
attrs.insert("DW_AT_byte_size".into(), DwarfAttributeValue::U64(4));
let mut red = HashMap::new();
red.insert(
"DW_AT_name".into(),
DwarfAttributeValue::String("Red".into()),
);
red.insert("DW_AT_const_value".into(), DwarfAttributeValue::I64(0));
let mut green = HashMap::new();
green.insert(
"DW_AT_name".into(),
DwarfAttributeValue::String("Green".into()),
);
green.insert("DW_AT_const_value".into(), DwarfAttributeValue::I64(1));
let children = vec![red, green];
let result = decoder.decode_die(
crate::dwarf::dwarf_tags::DW_TAG_enumeration_type,
&attrs,
&children,
200,
);
match result {
DecodedType::Enumeration(e) => {
assert_eq!(e.name, "Color");
assert_eq!(e.byte_size, 4);
assert_eq!(e.enumerators.len(), 2);
assert_eq!(e.enumerators[0].value, 0);
assert_eq!(e.enumerators[1].value, 1);
}
other => panic!("expected Enumeration, got {:?}", other),
}
}
#[test]
fn test_loclist_dwarf5_offset_pair() {
let parser = LocationListParser::new(8);
let mut data = Vec::new();
data.push(0x04); data.push(0x00); data.push(0x10); data.push(0x01); data.push(0x9f); data.push(0x00);
let result = parser.parse_loclists_dwarf5(&data, 0x1000);
assert!(result.is_ok());
let entries = result.unwrap();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].start_address, 0x1000);
assert_eq!(entries[0].end_address, 0x1010);
assert!(entries[1].is_end_of_list);
}
#[test]
fn test_loclist_dwarf4_simple() {
let parser = LocationListParser::new(8);
let mut data = Vec::new();
data.extend_from_slice(&0x1000u64.to_le_bytes());
data.extend_from_slice(&0x2000u64.to_le_bytes());
data.extend_from_slice(&1u16.to_le_bytes()); data.push(0x9f);
data.extend_from_slice(&0u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
let result = parser.parse_loc_dwarf4(&data);
assert!(result.is_ok());
let entries = result.unwrap();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].start_address, 0x1000);
assert_eq!(entries[0].end_address, 0x2000);
assert!(entries[1].is_end_of_list);
}
#[test]
fn test_rnglist_dwarf5_start_end() {
let parser = RangeListParser::new(8);
let mut data = Vec::new();
data.push(0x06);
data.extend_from_slice(&0x1000u64.to_le_bytes());
data.extend_from_slice(&0x2000u64.to_le_bytes());
data.push(0x00);
let result = parser.parse_rnglists_dwarf5(&data, 0);
assert!(result.is_ok());
let ranges = result.unwrap();
assert_eq!(ranges.len(), 1);
assert_eq!(ranges[0].start, 0x1000);
assert_eq!(ranges[0].end, 0x2000);
}
#[test]
fn test_rangelist_dwarf4_simple() {
let parser = RangeListParser::new(8);
let mut data = Vec::new();
data.extend_from_slice(&0x1000u64.to_le_bytes());
data.extend_from_slice(&0x2000u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
let result = parser.parse_ranges_dwarf4(&data);
assert!(result.is_ok());
let ranges = result.unwrap();
assert_eq!(ranges.len(), 1);
assert_eq!(ranges[0].start, 0x1000);
assert_eq!(ranges[0].end, 0x2000);
}
#[test]
fn test_macro_parser_define() {
let mut data = vec![0x05, 0x00]; data.push(0x00); data.extend_from_slice(&0u32.to_le_bytes()); data.push(0x01); data.push(0x01); data.extend_from_slice(b"FOO");
data.push(0);
data.extend_from_slice(b"bar");
data.push(0);
data.push(0x00);
let mut parser = MacroParser::new(data);
let result = parser.parse();
assert!(result.is_ok());
let entries = result.unwrap();
assert_eq!(entries.len(), 1);
match &entries[0] {
MacroEntry::Define { line, name, value } => {
assert_eq!(*line, 1);
assert_eq!(name, "FOO");
assert_eq!(value, "bar");
}
_ => panic!("expected Define"),
}
}
#[test]
fn test_macro_parser_undef() {
let mut data = vec![0x05, 0x00];
data.push(0x00);
data.extend_from_slice(&0u32.to_le_bytes());
data.push(0x02);
data.push(0x02);
data.extend_from_slice(b"BAR");
data.push(0);
data.push(0x00);
let mut parser = MacroParser::new(data);
let result = parser.parse();
assert!(result.is_ok());
let entries = result.unwrap();
assert_eq!(entries.len(), 1);
match &entries[0] {
MacroEntry::Undef { line, name } => {
assert_eq!(*line, 2);
assert_eq!(name, "BAR");
}
_ => panic!("expected Undef"),
}
}
#[test]
fn test_macro_parser_start_end_file() {
let mut data = vec![0x05, 0x00];
data.push(0x00);
data.extend_from_slice(&0u32.to_le_bytes());
data.push(0x03);
data.push(0x01); data.push(0x00); data.push(0x04);
data.push(0x00);
let mut parser = MacroParser::new(data);
let result = parser.parse();
assert!(result.is_ok());
let entries = result.unwrap();
assert_eq!(entries.len(), 2);
assert!(matches!(entries[0], MacroEntry::StartFile { .. }));
assert!(matches!(entries[1], MacroEntry::EndFile));
}
#[test]
fn test_name_index_parse() {
let mut data = Vec::new();
let header_size = 12 + 4 + 4 + 4 + 4 + 4 + 4 + 1;
data.extend_from_slice(&(header_size as u32 - 4).to_le_bytes());
data.extend_from_slice(&5u16.to_le_bytes());
data.extend_from_slice(&0u16.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.push(0);
let mut parser = NameIndexParser::new(data);
let result = parser.parse();
assert!(result.is_ok());
let (name_count, entries) = result.unwrap();
assert_eq!(name_count, 0);
assert!(entries.is_empty());
}
#[test]
fn test_str_offsets_parse() {
let mut data = Vec::new();
data.extend_from_slice(&8u32.to_le_bytes());
data.extend_from_slice(&5u16.to_le_bytes());
data.extend_from_slice(&0u16.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&16u32.to_le_bytes());
let result = StringOffsetsTable::parse(&data, 0);
assert!(result.is_ok());
let table = result.unwrap();
assert_eq!(table.len(), 2);
assert_eq!(table.get_offset(0), Some(0));
assert_eq!(table.get_offset(1), Some(16));
}
#[test]
fn test_deep_utils_eval_as_address() {
let expr = vec![
0x06, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let result = DwarfDeepUtils::eval_as_address(&expr, 8);
assert_eq!(result, Some(0x400000));
}
#[test]
fn test_disassemble_expression() {
let expr = vec![0x30, 0x31, 0x22]; let disasm = DwarfDeepUtils::disassemble_expression(&expr);
assert!(disasm.contains("DW_OP_lit0"));
assert!(disasm.contains("DW_OP_lit1"));
assert!(disasm.contains("DW_OP_plus"));
}
#[test]
fn test_opcode_names_coverage() {
for b in 0u8..=255 {
let op = DwarfOpcode::from_byte(b);
let name = op.name();
assert!(!name.is_empty(), "empty name for opcode {:#x}", b);
}
}
#[test]
fn test_opcode_from_byte_known() {
assert_eq!(DwarfOpcode::from_byte(0x03), DwarfOpcode::Reg);
assert_eq!(DwarfOpcode::from_byte(0x06), DwarfOpcode::Addr);
assert_eq!(DwarfOpcode::from_byte(0x22), DwarfOpcode::Plus);
assert_eq!(DwarfOpcode::from_byte(0x93), DwarfOpcode::Piece);
assert_eq!(DwarfOpcode::from_byte(0x9c), DwarfOpcode::CallFrameCfa);
}
#[test]
fn test_deep_utils_new() {
let utils = DwarfDeepUtils::new(8, 5);
assert_eq!(utils.address_size, 8);
assert_eq!(utils.dwarf_version, 5);
}
#[test]
fn test_piece_creation() {
let p = DwarfPiece::byte_piece(vec![0x03, 0x00], 4);
assert_eq!(p.size_in_bits, 32);
assert_eq!(p.size_in_bytes(), 4);
assert!(!p.is_bit_piece);
let p = DwarfPiece::bit_piece(vec![0x03, 0x00], 1, 3);
assert_eq!(p.size_in_bits, 1);
assert_eq!(p.size_in_bytes(), 1);
assert_eq!(p.offset_in_bits, 3);
assert!(p.is_bit_piece);
}
#[test]
fn test_eval_result_error() {
let result = EvalResult::error("test error");
assert!(!result.success);
assert_eq!(result.error, Some("test error".to_string()));
assert!(result.value.is_none());
}
#[test]
fn test_eval_result_default() {
let result = EvalResult::default();
assert!(result.success);
assert!(result.value.is_none());
assert!(!result.is_composite);
assert!(!result.is_stack_value);
}
}