use std::collections::{BTreeMap, HashMap};
pub const DW_OP_addr: u8 = 0x03;
pub const DW_OP_deref: u8 = 0x06;
pub const DW_OP_const1u: u8 = 0x08;
pub const DW_OP_const1s: u8 = 0x09;
pub const DW_OP_const2u: u8 = 0x0a;
pub const DW_OP_const2s: u8 = 0x0b;
pub const DW_OP_const4u: u8 = 0x0c;
pub const DW_OP_const4s: u8 = 0x0d;
pub const DW_OP_const8u: u8 = 0x0e;
pub const DW_OP_const8s: u8 = 0x0f;
pub const DW_OP_constu: u8 = 0x10;
pub const DW_OP_consts: u8 = 0x11;
pub const DW_OP_dup: u8 = 0x12;
pub const DW_OP_drop: u8 = 0x13;
pub const DW_OP_over: u8 = 0x14;
pub const DW_OP_pick: u8 = 0x15;
pub const DW_OP_swap: u8 = 0x16;
pub const DW_OP_rot: u8 = 0x17;
pub const DW_OP_xderef: u8 = 0x18;
pub const DW_OP_abs: u8 = 0x19;
pub const DW_OP_and: u8 = 0x1a;
pub const DW_OP_div: u8 = 0x1b;
pub const DW_OP_minus: u8 = 0x1c;
pub const DW_OP_mod: u8 = 0x1d;
pub const DW_OP_mul: u8 = 0x1e;
pub const DW_OP_neg: u8 = 0x1f;
pub const DW_OP_not: u8 = 0x20;
pub const DW_OP_or: u8 = 0x21;
pub const DW_OP_plus: u8 = 0x22;
pub const DW_OP_plus_uconst: u8 = 0x23;
pub const DW_OP_shl: u8 = 0x24;
pub const DW_OP_shr: u8 = 0x25;
pub const DW_OP_shra: u8 = 0x26;
pub const DW_OP_xor: u8 = 0x27;
pub const DW_OP_skip: u8 = 0x2f;
pub const DW_OP_bra: u8 = 0x28;
pub const DW_OP_eq: u8 = 0x29;
pub const DW_OP_ge: u8 = 0x2a;
pub const DW_OP_gt: u8 = 0x2b;
pub const DW_OP_le: u8 = 0x2c;
pub const DW_OP_lt: u8 = 0x2d;
pub const DW_OP_ne: u8 = 0x2e;
pub const DW_OP_lit0: u8 = 0x30;
pub const DW_OP_lit31: u8 = 0x4f;
pub const DW_OP_reg0: u8 = 0x50;
pub const DW_OP_reg31: u8 = 0x6f;
pub const DW_OP_breg0: u8 = 0x70;
pub const DW_OP_breg31: u8 = 0x8f;
pub const DW_OP_regx: u8 = 0x90;
pub const DW_OP_fbreg: u8 = 0x91;
pub const DW_OP_bregx: u8 = 0x92;
pub const DW_OP_piece: u8 = 0x93;
pub const DW_OP_deref_size: u8 = 0x94;
pub const DW_OP_xderef_size: u8 = 0x95;
pub const DW_OP_nop: u8 = 0x96;
pub const DW_OP_push_object_address: u8 = 0x97;
pub const DW_OP_call2: u8 = 0x98;
pub const DW_OP_call4: u8 = 0x99;
pub const DW_OP_call_ref: u8 = 0x9a;
pub const DW_OP_form_tls_address: u8 = 0x9b;
pub const DW_OP_call_frame_cfa: u8 = 0x9c;
pub const DW_OP_bit_piece: u8 = 0x9d;
pub const DW_OP_implicit_value: u8 = 0x9e;
pub const DW_OP_stack_value: u8 = 0x9f;
pub const DW_OP_implicit_pointer: u8 = 0xa0;
pub const DW_OP_addrx: u8 = 0xa1;
pub const DW_OP_constx: u8 = 0xa2;
pub const DW_OP_entry_value: u8 = 0xa3;
pub const DW_OP_const_type: u8 = 0xa4;
pub const DW_OP_regval_type: u8 = 0xa5;
pub const DW_OP_deref_type: u8 = 0xa6;
pub const DW_OP_xderef_type: u8 = 0xa7;
pub const DW_OP_convert: u8 = 0xa8;
pub const DW_OP_reinterpret: u8 = 0xa9;
pub const DW_OP_GNU_push_tls_address: u8 = 0xe0;
pub const DW_OP_GNU_uninit: u8 = 0xf0;
pub const DW_OP_GNU_encoded_addr: u8 = 0xf1;
pub const DW_OP_GNU_implicit_pointer: u8 = 0xf2;
pub const DW_OP_GNU_entry_value: u8 = 0xf3;
pub const DW_OP_GNU_const_type: u8 = 0xf4;
pub const DW_OP_GNU_regval_type: u8 = 0xf5;
pub const DW_OP_GNU_deref_type: u8 = 0xf6;
pub const DW_OP_GNU_convert: u8 = 0xf7;
pub const DW_OP_GNU_reinterpret: u8 = 0xf9;
pub const DW_OP_GNU_parameter_ref: u8 = 0xfa;
pub const DW_OP_GNU_addr_index: u8 = 0xfb;
pub const DW_OP_GNU_const_index: u8 = 0xfc;
pub const DW_OP_GNU_variable_value: u8 = 0xfd;
pub fn x86_dwarf_reg_name(reg: u64) -> &'static str {
match reg {
0 => "RAX",
1 => "RDX",
2 => "RCX",
3 => "RBX",
4 => "RSI",
5 => "RDI",
6 => "RBP",
7 => "RSP",
8 => "R8",
9 => "R9",
10 => "R10",
11 => "R11",
12 => "R12",
13 => "R13",
14 => "R14",
15 => "R15",
16 => "RA",
17 => "RD",
18 => "RC",
19 => "RB",
20 => "RS",
21 => "RDI (ret addr)",
22 => "RBP (frame)",
23 => "RSP (stack)",
24 => "RFLAGS",
25 => "RIP",
26 => "ES",
27 => "CS",
28 => "SS",
29 => "DS",
30 => "FS",
31 => "GS",
33..=39 => "reserved",
40 => "XMM0",
41 => "XMM1",
42 => "XMM2",
43 => "XMM3",
44 => "XMM4",
45 => "XMM5",
46 => "XMM6",
47 => "XMM7",
48 => "XMM8",
49 => "XMM9",
50 => "XMM10",
51 => "XMM11",
52 => "XMM12",
53 => "XMM13",
54 => "XMM14",
55 => "XMM15",
56 => "XMM16",
57 => "XMM17",
58 => "XMM18",
59 => "XMM19",
60 => "XMM20",
61 => "XMM21",
62 => "XMM22",
63 => "XMM23",
64 => "XMM24",
65 => "XMM25",
66 => "XMM26",
67 => "XMM27",
68 => "XMM28",
69 => "XMM29",
70 => "XMM30",
71 => "XMM31",
72 => "ST(0)",
73 => "ST(1)",
74 => "ST(2)",
75 => "ST(3)",
76 => "ST(4)",
77 => "ST(5)",
78 => "ST(6)",
79 => "ST(7)",
80 => "MM0",
81 => "MM1",
82 => "MM2",
83 => "MM3",
84 => "MM4",
85 => "MM5",
86 => "MM6",
87 => "MM7",
88 => "BND0",
89 => "BND1",
90 => "BND2",
91 => "BND3",
92 => "BNDCFGU",
93 => "BNDSTATUS",
94..=101 => {
let idx = reg - 94;
match idx {
0 => "K0",
1 => "K1",
2 => "K2",
3 => "K3",
4 => "K4",
5 => "K5",
6 => "K6",
7 => "K7",
_ => "K?",
}
}
102..=133 => {
let idx = reg - 102;
match idx {
0 => "ZMM0",
1 => "ZMM1",
2 => "ZMM2",
3 => "ZMM3",
4 => "ZMM4",
5 => "ZMM5",
6 => "ZMM6",
7 => "ZMM7",
8 => "ZMM8",
9 => "ZMM9",
10 => "ZMM10",
11 => "ZMM11",
12 => "ZMM12",
13 => "ZMM13",
14 => "ZMM14",
15 => "ZMM15",
16 => "ZMM16",
17 => "ZMM17",
18 => "ZMM18",
19 => "ZMM19",
20 => "ZMM20",
21 => "ZMM21",
22 => "ZMM22",
23 => "ZMM23",
24 => "ZMM24",
25 => "ZMM25",
26 => "ZMM26",
27 => "ZMM27",
28 => "ZMM28",
29 => "ZMM29",
30 => "ZMM30",
31 => "ZMM31",
_ => "ZMM?",
}
}
_ => "unknown",
}
}
pub fn x86_dwarf_reg_number(name: &str) -> Option<u64> {
match name.to_ascii_uppercase().as_str() {
"RAX" => Some(0),
"RDX" => Some(1),
"RCX" => Some(2),
"RBX" => Some(3),
"RSI" => Some(4),
"RDI" => Some(5),
"RBP" => Some(6),
"RSP" => Some(7),
"R8" => Some(8),
"R9" => Some(9),
"R10" => Some(10),
"R11" => Some(11),
"R12" => Some(12),
"R13" => Some(13),
"R14" => Some(14),
"R15" => Some(15),
"RIP" => Some(25),
"EFLAGS" | "RFLAGS" => Some(24),
_ => {
if let Some(num_str) = name.strip_prefix("XMM") {
if let Ok(n) = num_str.parse::<u64>() {
if n <= 31 {
return Some(40 + n);
}
}
}
if let Some(num_str) = name.strip_prefix("YMM") {
if let Ok(n) = num_str.parse::<u64>() {
if n <= 31 {
return Some(40 + n);
}
}
}
if let Some(num_str) = name.strip_prefix("ZMM") {
if let Ok(n) = num_str.parse::<u64>() {
if n <= 31 {
return Some(102 + n);
}
}
}
None
}
}
}
pub fn is_x86_gpr(reg: u64) -> bool {
reg <= 15
}
pub fn is_x86_xmm(reg: u64) -> bool {
(40..=71).contains(®)
}
pub fn is_x86_mmx(reg: u64) -> bool {
(80..=87).contains(®)
}
#[derive(Debug, Clone, PartialEq)]
pub enum DwarfExprValue {
Generic(u64),
Typed { value: u64, type_offset: u64 },
Incomplete,
}
impl DwarfExprValue {
pub fn as_generic(&self) -> Option<u64> {
match self {
DwarfExprValue::Generic(v) => Some(*v),
DwarfExprValue::Typed { value, .. } => Some(*value),
DwarfExprValue::Incomplete => None,
}
}
pub fn is_incomplete(&self) -> bool {
matches!(self, DwarfExprValue::Incomplete)
}
}
impl From<u64> for DwarfExprValue {
fn from(v: u64) -> Self {
DwarfExprValue::Generic(v)
}
}
impl From<i64> for DwarfExprValue {
fn from(v: i64) -> Self {
DwarfExprValue::Generic(v as u64)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum X86DwarfLocation {
Register { reg: u64, offset: i64 },
Memory { address: u64 },
FrameRelative { offset: i64 },
Value { value: u64 },
Implicit { data: Vec<u8> },
ImplicitPointer { die_offset: u64, byte_offset: i64 },
Composite { pieces: Vec<X86DwarfPiece> },
Undefined,
Unresolved { reason: String },
}
#[derive(Debug, Clone, PartialEq)]
pub struct X86DwarfPiece {
pub location: Box<X86DwarfLocation>,
pub size_in_bits: Option<u64>,
pub bit_offset: Option<u64>,
}
impl X86DwarfPiece {
pub fn new_register(reg: u64, offset: i64) -> Self {
X86DwarfPiece {
location: Box::new(X86DwarfLocation::Register { reg, offset }),
size_in_bits: None,
bit_offset: None,
}
}
pub fn new_memory(address: u64) -> Self {
X86DwarfPiece {
location: Box::new(X86DwarfLocation::Memory { address }),
size_in_bits: None,
bit_offset: None,
}
}
pub fn with_size(mut self, size_in_bits: u64) -> Self {
self.size_in_bits = Some(size_in_bits);
self
}
pub fn with_bit_offset(mut self, bit_offset: u64) -> Self {
self.bit_offset = Some(bit_offset);
self
}
}
#[derive(Debug, Clone)]
pub struct X86CfiEvaluation {
pub value: u64,
pub is_initial: bool,
}
impl X86CfiEvaluation {
pub fn new(value: u64, is_initial: bool) -> Self {
X86CfiEvaluation { value, is_initial }
}
}
#[derive(Debug, Clone)]
pub struct X86LocationListEntry {
pub low_pc: u64,
pub high_pc: u64,
pub expr: Vec<u8>,
pub is_base_address: bool,
pub evaluated: Option<X86DwarfLocation>,
}
impl X86LocationListEntry {
pub fn new(low_pc: u64, high_pc: u64, expr: Vec<u8>) -> Self {
X86LocationListEntry {
low_pc,
high_pc,
expr,
is_base_address: false,
evaluated: None,
}
}
pub fn new_base_address(base: u64) -> Self {
X86LocationListEntry {
low_pc: base,
high_pc: !0,
expr: Vec::new(),
is_base_address: true,
evaluated: None,
}
}
pub fn covers(&self, pc: u64) -> bool {
pc >= self.low_pc && pc < self.high_pc
}
}
#[derive(Debug)]
pub struct X86DwarfExpr {
pub stack: Vec<DwarfExprValue>,
pub object_address: Option<u64>,
pub cfa: Option<u64>,
pub frame_base: Option<u64>,
pub registers: HashMap<u64, u64>,
pub address_size: u8,
pub is_location: bool,
pub location: Option<X86DwarfLocation>,
pub pieces: Vec<X86DwarfPiece>,
pub error: Option<String>,
pub max_steps: usize,
pub expr_bytes: Vec<u8>,
pub pc: usize,
pub addr_table: Vec<u64>,
pub tls_base: Option<u64>,
}
impl X86DwarfExpr {
pub fn new() -> Self {
X86DwarfExpr {
stack: Vec::new(),
object_address: None,
cfa: None,
frame_base: None,
registers: HashMap::new(),
address_size: 8,
is_location: false,
location: None,
pieces: Vec::new(),
error: None,
max_steps: 1000,
expr_bytes: Vec::new(),
pc: 0,
addr_table: Vec::new(),
tls_base: None,
}
}
pub fn set_register(&mut self, reg: u64, value: u64) {
self.registers.insert(reg, value);
}
pub fn set_cfa(&mut self, cfa: u64) {
self.cfa = Some(cfa);
}
pub fn set_frame_base(&mut self, fb: u64) {
self.frame_base = Some(fb);
}
pub fn set_object_address(&mut self, addr: u64) {
self.object_address = Some(addr);
}
pub fn set_tls_base(&mut self, base: u64) {
self.tls_base = Some(base);
}
pub fn push(&mut self, value: u64) {
self.stack.push(DwarfExprValue::Generic(value));
}
pub fn push_signed(&mut self, value: i64) {
self.stack.push(DwarfExprValue::Generic(value as u64));
}
pub fn pop(&mut self) -> Option<u64> {
self.stack.pop().and_then(|v| v.as_generic())
}
pub fn clear(&mut self) {
self.stack.clear();
self.location = None;
self.pieces.clear();
self.error = None;
self.pc = 0;
}
pub fn evaluate(&mut self, expr: &[u8]) -> Result<X86DwarfLocation, String> {
self.clear();
self.expr_bytes = expr.to_vec();
self.pc = 0;
let mut steps = 0;
while self.pc < self.expr_bytes.len() && steps < self.max_steps {
steps += 1;
let op = self.expr_bytes[self.pc];
self.pc += 1;
match op {
DW_OP_lit0..=DW_OP_lit31 => {
let val = (op - DW_OP_lit0) as u64;
self.stack.push(DwarfExprValue::Generic(val));
}
DW_OP_reg0..=DW_OP_reg31 => {
let reg = (op - DW_OP_reg0) as u64;
if let Some(&val) = self.registers.get(®) {
self.location = Some(X86DwarfLocation::Register {
reg,
offset: val as i64,
});
} else {
self.location = Some(X86DwarfLocation::Register { reg, offset: 0 });
}
self.is_location = true;
}
DW_OP_breg0..=DW_OP_breg31 => {
let reg = (op - DW_OP_breg0) as u64;
let offset = self.read_sleb()?;
let base = self.registers.get(®).copied().unwrap_or(0);
let addr = (base as i64).wrapping_add(offset) as u64;
self.stack.push(DwarfExprValue::Generic(addr));
}
DW_OP_addr => {
let addr = self.read_address()?;
self.stack.push(DwarfExprValue::Generic(addr));
}
DW_OP_deref => {
self.eval_deref(self.address_size as u8)?;
}
DW_OP_const1u => {
let v = self.read_u8()? as u64;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_const1s => {
let v = self.read_u8()? as i8 as i64 as u64;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_const2u => {
let v = self.read_u16()? as u64;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_const2s => {
let v = self.read_u16()? as i16 as i64 as u64;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_const4u => {
let v = self.read_u32()? as u64;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_const4s => {
let v = self.read_u32()? as i32 as i64 as u64;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_const8u => {
let v = self.read_u64()?;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_const8s => {
let v = self.read_u64()? as i64 as u64;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_constu => {
let v = self.read_uleb()?;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_consts => {
let v = self.read_sleb()? as u64;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_regx => {
let reg = self.read_uleb()?;
if let Some(&val) = self.registers.get(®) {
self.location = Some(X86DwarfLocation::Register {
reg,
offset: val as i64,
});
} else {
self.location = Some(X86DwarfLocation::Register { reg, offset: 0 });
}
self.is_location = true;
}
DW_OP_bregx => {
let reg = self.read_uleb()?;
let offset = self.read_sleb()?;
let base = self.registers.get(®).copied().unwrap_or(0);
let addr = (base as i64).wrapping_add(offset) as u64;
self.stack.push(DwarfExprValue::Generic(addr));
}
DW_OP_fbreg => {
let offset = self.read_sleb()?;
let fb = self.frame_base.unwrap_or(0);
let addr = (fb as i64).wrapping_add(offset) as u64;
self.stack.push(DwarfExprValue::Generic(addr));
self.location = Some(X86DwarfLocation::FrameRelative { offset });
}
DW_OP_piece => {
let size = self.read_uleb()?;
if let Some(loc) = self.location.take() {
self.pieces.push(X86DwarfPiece {
location: Box::new(loc),
size_in_bits: Some(size * 8),
bit_offset: None,
});
}
}
DW_OP_bit_piece => {
let size_in_bits = self.read_uleb()?;
let bit_offset = self.read_uleb()?;
if let Some(loc) = self.location.take() {
self.pieces.push(X86DwarfPiece {
location: Box::new(loc),
size_in_bits: Some(size_in_bits),
bit_offset: Some(bit_offset),
});
}
}
DW_OP_deref_size => {
let sz = self.read_u8()?;
self.eval_deref(sz)?;
}
DW_OP_xderef_size => {
let _sz = self.read_u8()?;
self.eval_xderef()?;
}
DW_OP_nop => {
}
DW_OP_push_object_address => {
if let Some(addr) = self.object_address {
self.stack.push(DwarfExprValue::Generic(addr));
} else {
return Err("DW_OP_push_object_address: no object address set".to_string());
}
}
DW_OP_call2 => {
let _offset = self.read_u16()? as u64;
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_call4 => {
let _offset = self.read_u32()? as u64;
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_call_ref => {
let _offset = if self.address_size == 8 {
self.read_u64()?
} else {
self.read_u32()? as u64
};
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_form_tls_address | DW_OP_GNU_push_tls_address => {
if let Some(addr) = self.pop() {
let tls = self.tls_base.unwrap_or(0);
let resolved = tls.wrapping_add(addr);
self.stack.push(DwarfExprValue::Generic(resolved));
} else {
return Err("DW_OP_form_tls_address: stack underflow".to_string());
}
}
DW_OP_call_frame_cfa => {
if let Some(cfa) = self.cfa {
self.stack.push(DwarfExprValue::Generic(cfa));
} else {
return Err("DW_OP_call_frame_cfa: no CFA set".to_string());
}
}
DW_OP_implicit_value => {
let len = self.read_uleb()? as usize;
let data = self.read_bytes(len)?;
self.location = Some(X86DwarfLocation::Implicit { data });
}
DW_OP_stack_value => {
if let Some(val) = self.pop() {
self.location = Some(X86DwarfLocation::Value { value: val });
} else {
return Err("DW_OP_stack_value: stack underflow".to_string());
}
}
DW_OP_implicit_pointer | DW_OP_GNU_implicit_pointer => {
let die_offset = if self.address_size == 8 {
self.read_u64()?
} else {
self.read_u32()? as u64
};
let byte_offset = self.read_sleb()?;
self.location = Some(X86DwarfLocation::ImplicitPointer {
die_offset,
byte_offset,
});
}
DW_OP_addrx => {
let idx = self.read_uleb()? as usize;
if idx < self.addr_table.len() {
self.stack
.push(DwarfExprValue::Generic(self.addr_table[idx]));
}
}
DW_OP_constx => {
let idx = self.read_uleb()? as usize;
if idx < self.addr_table.len() {
self.stack
.push(DwarfExprValue::Generic(self.addr_table[idx]));
}
}
DW_OP_entry_value | DW_OP_GNU_entry_value => {
let _len = self.read_uleb()? as usize;
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_const_type | DW_OP_GNU_const_type => {
let _type_offset = self.read_uleb()?;
let _len = self.read_u8()? as usize;
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_regval_type | DW_OP_GNU_regval_type => {
let _reg = self.read_uleb()?;
let _type_offset = self.read_uleb()?;
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_deref_type | DW_OP_GNU_deref_type => {
let _sz = self.read_u8()?;
let _type_offset = self.read_uleb()?;
self.eval_deref(self.address_size as u8)?;
}
DW_OP_convert | DW_OP_GNU_convert => {
let _type_offset = self.read_uleb()?;
}
DW_OP_reinterpret | DW_OP_GNU_reinterpret => {
let _type_offset = self.read_uleb()?;
}
DW_OP_dup => {
if let Some(v) = self.stack.last().cloned() {
self.stack.push(v);
} else {
return Err("DW_OP_dup: stack underflow".to_string());
}
}
DW_OP_drop => {
if self.stack.pop().is_none() {
return Err("DW_OP_drop: stack underflow".to_string());
}
}
DW_OP_over => {
if self.stack.len() < 2 {
return Err("DW_OP_over: stack underflow".to_string());
}
let v = self.stack[self.stack.len() - 2].clone();
self.stack.push(v);
}
DW_OP_pick => {
let idx = self.read_u8()? as usize;
if idx >= self.stack.len() {
return Err("DW_OP_pick: index out of range".to_string());
}
let v = self.stack[self.stack.len() - 1 - idx].clone();
self.stack.push(v);
}
DW_OP_swap => {
if self.stack.len() < 2 {
return Err("DW_OP_swap: stack underflow".to_string());
}
let len = self.stack.len();
self.stack.swap(len - 1, len - 2);
}
DW_OP_rot => {
if self.stack.len() < 3 {
return Err("DW_OP_rot: stack underflow".to_string());
}
let len = self.stack.len();
let a = self.stack.remove(len - 3);
self.stack.push(a);
}
DW_OP_abs => {
let v = self.pop().ok_or("DW_OP_abs: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_and => {
let b = self.pop().ok_or("DW_OP_and: stack underflow")?;
let a = self.pop().ok_or("DW_OP_and: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a & b));
}
DW_OP_div => {
let b = self.pop().ok_or("DW_OP_div: stack underflow")?;
let a = self.pop().ok_or("DW_OP_div: stack underflow")?;
if b == 0 {
return Err("DW_OP_div: division by zero".to_string());
}
self.stack.push(DwarfExprValue::Generic(
(a as i64).wrapping_div(b as i64) as u64
));
}
DW_OP_minus => {
let b = self.pop().ok_or("DW_OP_minus: stack underflow")?;
let a = self.pop().ok_or("DW_OP_minus: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a.wrapping_sub(b)));
}
DW_OP_mod => {
let b = self.pop().ok_or("DW_OP_mod: stack underflow")?;
let a = self.pop().ok_or("DW_OP_mod: stack underflow")?;
if b == 0 {
return Err("DW_OP_mod: division by zero".to_string());
}
self.stack.push(DwarfExprValue::Generic(
(a as i64).wrapping_rem(b as i64) as u64
));
}
DW_OP_mul => {
let b = self.pop().ok_or("DW_OP_mul: stack underflow")?;
let a = self.pop().ok_or("DW_OP_mul: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a.wrapping_mul(b)));
}
DW_OP_neg => {
let v = self.pop().ok_or("DW_OP_neg: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic((-(v as i64)) as u64));
}
DW_OP_not => {
let v = self.pop().ok_or("DW_OP_not: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(!v));
}
DW_OP_or => {
let b = self.pop().ok_or("DW_OP_or: stack underflow")?;
let a = self.pop().ok_or("DW_OP_or: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a | b));
}
DW_OP_plus => {
let b = self.pop().ok_or("DW_OP_plus: stack underflow")?;
let a = self.pop().ok_or("DW_OP_plus: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a.wrapping_add(b)));
}
DW_OP_plus_uconst => {
let c = self.read_uleb()?;
let a = self.pop().ok_or("DW_OP_plus_uconst: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a.wrapping_add(c)));
}
DW_OP_shl => {
let b = self.pop().ok_or("DW_OP_shl: stack underflow")?;
let a = self.pop().ok_or("DW_OP_shl: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic(a.wrapping_shl(b as u32)));
}
DW_OP_shr => {
let b = self.pop().ok_or("DW_OP_shr: stack underflow")?;
let a = self.pop().ok_or("DW_OP_shr: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic(a.wrapping_shr(b as u32)));
}
DW_OP_shra => {
let b = self.pop().ok_or("DW_OP_shra: stack underflow")?;
let a = self.pop().ok_or("DW_OP_shra: stack underflow")?;
let result = (a as i64).wrapping_shr(b as u32) as u64;
self.stack.push(DwarfExprValue::Generic(result));
}
DW_OP_xor => {
let b = self.pop().ok_or("DW_OP_xor: stack underflow")?;
let a = self.pop().ok_or("DW_OP_xor: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a ^ b));
}
DW_OP_skip => {
let offset = self.read_i16()? as i32;
let new_pc = (self.pc as i32 + offset) as usize;
if new_pc > self.expr_bytes.len() {
return Err("DW_OP_skip: jump out of bounds".to_string());
}
self.pc = new_pc;
}
DW_OP_bra => {
let offset = self.read_i16()? as i32;
let cond = self.pop().ok_or("DW_OP_bra: stack underflow")?;
if cond != 0 {
let new_pc = (self.pc as i32 + offset) as usize;
if new_pc > self.expr_bytes.len() {
return Err("DW_OP_bra: jump out of bounds".to_string());
}
self.pc = new_pc;
}
}
DW_OP_eq => {
let b = self.pop().ok_or("DW_OP_eq: stack underflow")?;
let a = self.pop().ok_or("DW_OP_eq: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic(if a == b { 1 } else { 0 }));
}
DW_OP_ge => {
let b = self.pop().ok_or("DW_OP_ge: stack underflow")?;
let a = self.pop().ok_or("DW_OP_ge: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic(if (a as i64) >= (b as i64) {
1
} else {
0
}));
}
DW_OP_gt => {
let b = self.pop().ok_or("DW_OP_gt: stack underflow")?;
let a = self.pop().ok_or("DW_OP_gt: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic(if (a as i64) > (b as i64) {
1
} else {
0
}));
}
DW_OP_le => {
let b = self.pop().ok_or("DW_OP_le: stack underflow")?;
let a = self.pop().ok_or("DW_OP_le: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic(if (a as i64) <= (b as i64) {
1
} else {
0
}));
}
DW_OP_lt => {
let b = self.pop().ok_or("DW_OP_lt: stack underflow")?;
let a = self.pop().ok_or("DW_OP_lt: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic(if (a as i64) < (b as i64) {
1
} else {
0
}));
}
DW_OP_ne => {
let b = self.pop().ok_or("DW_OP_ne: stack underflow")?;
let a = self.pop().ok_or("DW_OP_ne: stack underflow")?;
self.stack
.push(DwarfExprValue::Generic(if a != b { 1 } else { 0 }));
}
DW_OP_xderef => {
self.eval_xderef()?;
}
DW_OP_xderef_type => {
let _sz = self.read_u8()?;
let _type_offset = self.read_uleb()?;
self.eval_xderef()?;
}
DW_OP_GNU_addr_index => {
let idx = self.read_uleb()? as usize;
if idx < self.addr_table.len() {
self.stack
.push(DwarfExprValue::Generic(self.addr_table[idx]));
}
}
DW_OP_GNU_const_index => {
let idx = self.read_uleb()? as usize;
if idx < self.addr_table.len() {
self.stack
.push(DwarfExprValue::Generic(self.addr_table[idx]));
}
}
DW_OP_GNU_parameter_ref => {
let _offset = self.read_uleb()?;
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_GNU_variable_value => {
let _idx = self.read_uleb()?;
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_GNU_uninit => {
self.stack.push(DwarfExprValue::Incomplete);
}
DW_OP_GNU_encoded_addr => {
let _addr = self.read_address()?;
self.stack.push(DwarfExprValue::Generic(0));
}
_ => {
return Err(format!("Unknown DWARF opcode: 0x{:02x}", op));
}
}
}
if steps >= self.max_steps {
return Err("Maximum evaluation steps exceeded".to_string());
}
self.build_result()
}
pub fn evaluate_cfi(&mut self, expr: &[u8]) -> Result<X86CfiEvaluation, String> {
self.clear();
self.expr_bytes = expr.to_vec();
self.pc = 0;
let mut steps = 0;
while self.pc < self.expr_bytes.len() && steps < self.max_steps {
steps += 1;
let op = self.expr_bytes[self.pc];
self.pc += 1;
match op {
DW_OP_lit0..=DW_OP_lit31 => {
let val = (op - DW_OP_lit0) as u64;
self.stack.push(DwarfExprValue::Generic(val));
}
DW_OP_breg0..=DW_OP_breg31 => {
let reg = (op - DW_OP_breg0) as u64;
let offset = self.read_sleb()?;
let base = self.registers.get(®).copied().unwrap_or(0);
self.stack.push(DwarfExprValue::Generic(
(base as i64).wrapping_add(offset) as u64
));
}
DW_OP_plus => {
let b = self.pop().ok_or("CFI DW_OP_plus: stack underflow")?;
let a = self.pop().ok_or("CFI DW_OP_plus: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a.wrapping_add(b)));
}
DW_OP_minus => {
let b = self.pop().ok_or("CFI DW_OP_minus: stack underflow")?;
let a = self.pop().ok_or("CFI DW_OP_minus: stack underflow")?;
self.stack.push(DwarfExprValue::Generic(a.wrapping_sub(b)));
}
DW_OP_constu => {
let v = self.read_uleb()?;
self.stack.push(DwarfExprValue::Generic(v));
}
DW_OP_consts => {
let v = self.read_sleb()?;
self.stack.push(DwarfExprValue::Generic(v as u64));
}
DW_OP_deref => {
self.eval_deref(self.address_size as u8)?;
}
DW_OP_deref_size => {
let sz = self.read_u8()?;
self.eval_deref(sz)?;
}
DW_OP_nop => {}
DW_OP_call_frame_cfa => {
if let Some(cfa) = self.cfa {
self.stack.push(DwarfExprValue::Generic(cfa));
}
}
DW_OP_regx => {
let reg = self.read_uleb()?;
let val = self.registers.get(®).copied().unwrap_or(0);
self.stack.push(DwarfExprValue::Generic(val));
}
DW_OP_bregx => {
let reg = self.read_uleb()?;
let offset = self.read_sleb()?;
let base = self.registers.get(®).copied().unwrap_or(0);
self.stack.push(DwarfExprValue::Generic(
(base as i64).wrapping_add(offset) as u64
));
}
_ => {
}
}
}
let value = self.pop().unwrap_or(0);
Ok(X86CfiEvaluation::new(value, false))
}
pub fn evaluate_location_list_entry(
&mut self,
entry: &X86LocationListEntry,
) -> Result<X86DwarfLocation, String> {
self.evaluate(&entry.expr)
}
pub fn find_location_for_pc<'a>(
&self,
entries: &'a [X86LocationListEntry],
pc: u64,
) -> Option<&'a X86LocationListEntry> {
entries.iter().find(|e| !e.is_base_address && e.covers(pc))
}
fn build_result(&self) -> Result<X86DwarfLocation, String> {
if !self.pieces.is_empty() {
return Ok(X86DwarfLocation::Composite {
pieces: self.pieces.clone(),
});
}
if let Some(ref loc) = self.location {
return Ok(loc.clone());
}
if let Some(val) = self.stack.last().and_then(|v| v.as_generic()) {
return Ok(X86DwarfLocation::Memory { address: val });
}
Ok(X86DwarfLocation::Undefined)
}
fn eval_deref(&mut self, size: u8) -> Result<(), String> {
Ok(())
}
fn eval_xderef(&mut self) -> Result<(), String> {
let _addr_space = self.pop().ok_or("DW_OP_xderef: stack underflow")?;
let _addr = self.pop().ok_or("DW_OP_xderef: stack underflow")?;
self.stack.push(DwarfExprValue::Incomplete);
Ok(())
}
fn read_u8(&mut self) -> Result<u8, String> {
if self.pc >= self.expr_bytes.len() {
return Err("unexpected end of expression".to_string());
}
let v = self.expr_bytes[self.pc];
self.pc += 1;
Ok(v)
}
fn read_u16(&mut self) -> Result<u16, String> {
if self.pc + 2 > self.expr_bytes.len() {
return Err("unexpected end of expression".to_string());
}
let v = u16::from_le_bytes([self.expr_bytes[self.pc], self.expr_bytes[self.pc + 1]]);
self.pc += 2;
Ok(v)
}
fn read_i16(&mut self) -> Result<i16, String> {
if self.pc + 2 > self.expr_bytes.len() {
return Err("unexpected end of expression".to_string());
}
let v = i16::from_le_bytes([self.expr_bytes[self.pc], self.expr_bytes[self.pc + 1]]);
self.pc += 2;
Ok(v)
}
fn read_u32(&mut self) -> Result<u32, String> {
if self.pc + 4 > self.expr_bytes.len() {
return Err("unexpected end of expression".to_string());
}
let v = u32::from_le_bytes([
self.expr_bytes[self.pc],
self.expr_bytes[self.pc + 1],
self.expr_bytes[self.pc + 2],
self.expr_bytes[self.pc + 3],
]);
self.pc += 4;
Ok(v)
}
fn read_u64(&mut self) -> Result<u64, String> {
if self.pc + 8 > self.expr_bytes.len() {
return Err("unexpected end of expression".to_string());
}
let v = u64::from_le_bytes([
self.expr_bytes[self.pc],
self.expr_bytes[self.pc + 1],
self.expr_bytes[self.pc + 2],
self.expr_bytes[self.pc + 3],
self.expr_bytes[self.pc + 4],
self.expr_bytes[self.pc + 5],
self.expr_bytes[self.pc + 6],
self.expr_bytes[self.pc + 7],
]);
self.pc += 8;
Ok(v)
}
fn read_address(&mut self) -> Result<u64, String> {
if self.address_size == 8 {
self.read_u64()
} else {
self.read_u32().map(|v| v as u64)
}
}
fn read_uleb(&mut self) -> Result<u64, String> {
let mut result: u64 = 0;
let mut shift = 0;
loop {
if self.pc >= self.expr_bytes.len() {
return Err("unterminated ULEB128".to_string());
}
let byte = self.expr_bytes[self.pc];
self.pc += 1;
result |= ((byte & 0x7f) as u64) << shift;
if byte & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 64 {
return Err("ULEB128 too large".to_string());
}
}
Ok(result)
}
fn read_sleb(&mut self) -> Result<i64, String> {
let mut result: i64 = 0;
let mut shift = 0;
let mut byte;
loop {
if self.pc >= self.expr_bytes.len() {
return Err("unterminated SLEB128".to_string());
}
byte = self.expr_bytes[self.pc];
self.pc += 1;
result |= ((byte & 0x7f) as i64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
if shift >= 64 {
return Err("SLEB128 too large".to_string());
}
}
if shift < 64 && (byte & 0x40) != 0 {
result |= -(1i64 << shift);
}
Ok(result)
}
fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>, String> {
if self.pc + len > self.expr_bytes.len() {
return Err("unexpected end of expression".to_string());
}
let bytes = self.expr_bytes[self.pc..self.pc + len].to_vec();
self.pc += len;
Ok(bytes)
}
}
impl Default for X86DwarfExpr {
fn default() -> Self {
Self::new()
}
}
pub fn make_reg_location(reg: u64) -> Vec<u8> {
if reg <= 31 {
vec![DW_OP_reg0 + reg as u8]
} else {
let mut expr = vec![DW_OP_regx];
expr.extend(encode_uleb128(reg));
expr
}
}
pub fn make_fbreg_location(offset: i64) -> Vec<u8> {
let mut expr = vec![DW_OP_fbreg];
expr.extend(encode_sleb128(offset));
expr
}
pub fn make_constant_value(value: u64) -> Vec<u8> {
let mut expr = Vec::new();
if value <= 31 {
expr.push(DW_OP_lit0 + value as u8);
} else if value <= 255 {
expr.push(DW_OP_const1u);
expr.push(value as u8);
} else if value <= 65535 {
expr.push(DW_OP_const2u);
expr.extend_from_slice(&(value as u16).to_le_bytes());
} else if value <= 0xFFFFFFFF {
expr.push(DW_OP_const4u);
expr.extend_from_slice(&(value as u32).to_le_bytes());
} else {
expr.push(DW_OP_const8u);
expr.extend_from_slice(&value.to_le_bytes());
}
expr.push(DW_OP_stack_value);
expr
}
pub fn encode_uleb128(mut value: u64) -> Vec<u8> {
let mut result = Vec::new();
loop {
let mut byte = (value & 0x7f) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
result.push(byte);
if value == 0 {
break;
}
}
result
}
pub fn encode_sleb128(mut value: i64) -> Vec<u8> {
let mut result = Vec::new();
loop {
let mut byte = (value as u8) & 0x7f;
value >>= 7;
if (value == 0 && (byte & 0x40) == 0) || (value == -1 && (byte & 0x40) != 0) {
result.push(byte);
break;
}
byte |= 0x80;
result.push(byte);
}
result
}
pub fn dwarf_op_name(op: u8) -> &'static str {
match op {
DW_OP_addr => "DW_OP_addr",
DW_OP_deref => "DW_OP_deref",
DW_OP_const1u => "DW_OP_const1u",
DW_OP_const1s => "DW_OP_const1s",
DW_OP_const2u => "DW_OP_const2u",
DW_OP_const2s => "DW_OP_const2s",
DW_OP_const4u => "DW_OP_const4u",
DW_OP_const4s => "DW_OP_const4s",
DW_OP_const8u => "DW_OP_const8u",
DW_OP_const8s => "DW_OP_const8s",
DW_OP_constu => "DW_OP_constu",
DW_OP_consts => "DW_OP_consts",
DW_OP_dup => "DW_OP_dup",
DW_OP_drop => "DW_OP_drop",
DW_OP_over => "DW_OP_over",
DW_OP_pick => "DW_OP_pick",
DW_OP_swap => "DW_OP_swap",
DW_OP_rot => "DW_OP_rot",
DW_OP_abs => "DW_OP_abs",
DW_OP_and => "DW_OP_and",
DW_OP_div => "DW_OP_div",
DW_OP_minus => "DW_OP_minus",
DW_OP_mod => "DW_OP_mod",
DW_OP_mul => "DW_OP_mul",
DW_OP_neg => "DW_OP_neg",
DW_OP_not => "DW_OP_not",
DW_OP_or => "DW_OP_or",
DW_OP_plus => "DW_OP_plus",
DW_OP_shl => "DW_OP_shl",
DW_OP_shr => "DW_OP_shr",
DW_OP_shra => "DW_OP_shra",
DW_OP_xor => "DW_OP_xor",
DW_OP_skip => "DW_OP_skip",
DW_OP_bra => "DW_OP_bra",
DW_OP_eq => "DW_OP_eq",
DW_OP_ge => "DW_OP_ge",
DW_OP_gt => "DW_OP_gt",
DW_OP_le => "DW_OP_le",
DW_OP_lt => "DW_OP_lt",
DW_OP_ne => "DW_OP_ne",
DW_OP_regx => "DW_OP_regx",
DW_OP_fbreg => "DW_OP_fbreg",
DW_OP_bregx => "DW_OP_bregx",
DW_OP_piece => "DW_OP_piece",
DW_OP_deref_size => "DW_OP_deref_size",
DW_OP_nop => "DW_OP_nop",
DW_OP_push_object_address => "DW_OP_push_object_address",
DW_OP_call2 => "DW_OP_call2",
DW_OP_call4 => "DW_OP_call4",
DW_OP_call_ref => "DW_OP_call_ref",
DW_OP_form_tls_address => "DW_OP_form_tls_address",
DW_OP_call_frame_cfa => "DW_OP_call_frame_cfa",
DW_OP_bit_piece => "DW_OP_bit_piece",
DW_OP_implicit_value => "DW_OP_implicit_value",
DW_OP_stack_value => "DW_OP_stack_value",
DW_OP_implicit_pointer => "DW_OP_implicit_pointer",
DW_OP_addrx => "DW_OP_addrx",
DW_OP_constx => "DW_OP_constx",
DW_OP_entry_value => "DW_OP_entry_value",
DW_OP_const_type => "DW_OP_const_type",
DW_OP_regval_type => "DW_OP_regval_type",
DW_OP_deref_type => "DW_OP_deref_type",
DW_OP_convert => "DW_OP_convert",
DW_OP_reinterpret => "DW_OP_reinterpret",
_ => {
if (DW_OP_lit0..=DW_OP_lit31).contains(&op) {
"DW_OP_litN"
} else if (DW_OP_reg0..=DW_OP_reg31).contains(&op) {
"DW_OP_regN"
} else if (DW_OP_breg0..=DW_OP_breg31).contains(&op) {
"DW_OP_bregN"
} else if op == DW_OP_GNU_push_tls_address {
"DW_OP_GNU_push_tls_address"
} else if op == DW_OP_GNU_entry_value {
"DW_OP_GNU_entry_value"
} else {
"UNKNOWN"
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reg_name_gpr() {
assert_eq!(x86_dwarf_reg_name(0), "RAX");
assert_eq!(x86_dwarf_reg_name(1), "RDX");
assert_eq!(x86_dwarf_reg_name(2), "RCX");
assert_eq!(x86_dwarf_reg_name(3), "RBX");
assert_eq!(x86_dwarf_reg_name(4), "RSI");
assert_eq!(x86_dwarf_reg_name(5), "RDI");
assert_eq!(x86_dwarf_reg_name(6), "RBP");
assert_eq!(x86_dwarf_reg_name(7), "RSP");
assert_eq!(x86_dwarf_reg_name(15), "R15");
}
#[test]
fn test_reg_name_xmm() {
assert_eq!(x86_dwarf_reg_name(40), "XMM0");
assert_eq!(x86_dwarf_reg_name(55), "XMM15");
assert_eq!(x86_dwarf_reg_name(56), "XMM16");
assert_eq!(x86_dwarf_reg_name(71), "XMM31");
}
#[test]
fn test_reg_name_st() {
assert_eq!(x86_dwarf_reg_name(72), "ST(0)");
assert_eq!(x86_dwarf_reg_name(79), "ST(7)");
}
#[test]
fn test_reg_name_mm() {
assert_eq!(x86_dwarf_reg_name(80), "MM0");
assert_eq!(x86_dwarf_reg_name(87), "MM7");
}
#[test]
fn test_reg_name_unknown() {
assert_eq!(x86_dwarf_reg_name(200), "unknown");
assert_eq!(x86_dwarf_reg_name(32), "reserved");
}
#[test]
fn test_reg_number_gpr() {
assert_eq!(x86_dwarf_reg_number("RAX"), Some(0));
assert_eq!(x86_dwarf_reg_number("rbx"), Some(3));
assert_eq!(x86_dwarf_reg_number("R15"), Some(15));
assert_eq!(x86_dwarf_reg_number("RIP"), Some(25));
}
#[test]
fn test_reg_number_xmm() {
assert_eq!(x86_dwarf_reg_number("XMM0"), Some(40));
assert_eq!(x86_dwarf_reg_number("xmm15"), Some(55));
assert_eq!(x86_dwarf_reg_number("YMM0"), Some(40));
assert_eq!(x86_dwarf_reg_number("YMM15"), Some(55));
}
#[test]
fn test_reg_number_zmm() {
assert_eq!(x86_dwarf_reg_number("ZMM0"), Some(102));
assert_eq!(x86_dwarf_reg_number("ZMM31"), Some(133));
}
#[test]
fn test_reg_number_unknown() {
assert_eq!(x86_dwarf_reg_number("FOO"), None);
assert_eq!(x86_dwarf_reg_number("XMM99"), None);
}
#[test]
fn test_is_x86_gpr() {
assert!(is_x86_gpr(0));
assert!(is_x86_gpr(15));
assert!(!is_x86_gpr(16));
assert!(!is_x86_gpr(40));
}
#[test]
fn test_is_x86_xmm() {
assert!(is_x86_xmm(40));
assert!(is_x86_xmm(71));
assert!(!is_x86_xmm(0));
assert!(!is_x86_xmm(80));
}
#[test]
fn test_is_x86_mmx() {
assert!(is_x86_mmx(80));
assert!(!is_x86_mmx(0));
}
#[test]
fn test_new_evaluator() {
let e = X86DwarfExpr::new();
assert_eq!(e.address_size, 8);
assert!(e.stack.is_empty());
assert!(e.error.is_none());
}
#[test]
fn test_evaluate_lit0() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_lit0 + 5]).unwrap();
assert_eq!(e.stack.len(), 1);
assert_eq!(e.stack[0].as_generic(), Some(5));
}
#[test]
fn test_evaluate_lit_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0]).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_evaluate_lit31() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit31]).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(31));
}
#[test]
fn test_evaluate_const1u() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_const1u, 42]).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_evaluate_const2u() {
let mut e = X86DwarfExpr::new();
let expr = [DW_OP_const2u, 0x34, 0x12];
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x1234));
}
#[test]
fn test_evaluate_const4u() {
let mut e = X86DwarfExpr::new();
let expr = [DW_OP_const4u, 0x78, 0x56, 0x34, 0x12];
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x12345678));
}
#[test]
fn test_evaluate_const8u() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_const8u];
expr.extend_from_slice(&0xDEADBEEFu64.to_le_bytes());
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0xDEADBEEF));
}
#[test]
fn test_evaluate_constu() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_constu];
expr.extend(encode_uleb128(100));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(100));
}
#[test]
fn test_evaluate_consts_positive() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_consts];
expr.extend(encode_sleb128(42));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_evaluate_consts_negative() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_consts];
expr.extend(encode_sleb128(-10));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), (-10i64 as u64));
}
#[test]
fn test_evaluate_plus() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 3, DW_OP_lit0 + 5, DW_OP_plus])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(8));
}
#[test]
fn test_evaluate_minus() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 10, DW_OP_lit0 + 3, DW_OP_minus])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(7));
}
#[test]
fn test_evaluate_mul() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 6, DW_OP_lit0 + 7, DW_OP_mul])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_evaluate_div() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 20, DW_OP_lit0 + 4, DW_OP_div])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(5));
}
#[test]
fn test_evaluate_mod() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 17, DW_OP_lit0 + 5, DW_OP_mod])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(2));
}
#[test]
fn test_evaluate_neg() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_neg]).unwrap();
assert_eq!(e.stack[0].as_generic(), (-5i64 as u64));
}
#[test]
fn test_evaluate_abs() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_const4s, 0xFC, 0xFF, 0xFF, 0xFF, DW_OP_abs];
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(4));
}
#[test]
fn test_evaluate_not() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0, DW_OP_not]).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(!0u64));
}
#[test]
fn test_evaluate_and() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 0b1100, DW_OP_lit0 + 0b1010, DW_OP_and])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0b1000));
}
#[test]
fn test_evaluate_or() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 0b1100, DW_OP_lit0 + 0b0011, DW_OP_or])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0b1111));
}
#[test]
fn test_evaluate_xor() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 0b1111, DW_OP_lit0 + 0b0101, DW_OP_xor])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0b1010));
}
#[test]
fn test_evaluate_shl() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_lit0 + 3, DW_OP_shl])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(8));
}
#[test]
fn test_evaluate_shr() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 16, DW_OP_lit0 + 2, DW_OP_shr])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(4));
}
#[test]
fn test_evaluate_shra_positive() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 16, DW_OP_lit0 + 2, DW_OP_shra])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(4));
}
#[test]
fn test_evaluate_eq_true() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0 + 5, DW_OP_eq])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_evaluate_eq_false() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0 + 4, DW_OP_eq])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_evaluate_ne() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0 + 4, DW_OP_ne])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_evaluate_lt() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 3, DW_OP_lit0 + 7, DW_OP_lt])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_evaluate_gt() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 10, DW_OP_lit0 + 5, DW_OP_gt])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_evaluate_le() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0 + 5, DW_OP_le])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_evaluate_ge() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 7, DW_OP_lit0 + 5, DW_OP_ge])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_dup() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 42, DW_OP_dup]).unwrap();
assert_eq!(e.stack.len(), 2);
assert_eq!(e.stack[0].as_generic(), Some(42));
assert_eq!(e.stack[1].as_generic(), Some(42));
}
#[test]
fn test_drop() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_lit0 + 2, DW_OP_drop])
.unwrap();
assert_eq!(e.stack.len(), 1);
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_over() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_lit0 + 2, DW_OP_over])
.unwrap();
assert_eq!(e.stack.len(), 3);
assert_eq!(e.stack[2].as_generic(), Some(1));
}
#[test]
fn test_swap() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_lit0 + 2, DW_OP_swap])
.unwrap();
assert_eq!(e.stack.len(), 2);
assert_eq!(e.stack[0].as_generic(), Some(2));
assert_eq!(e.stack[1].as_generic(), Some(1));
}
#[test]
fn test_rot() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_lit0 + 2, DW_OP_lit0 + 3, DW_OP_rot])
.unwrap();
assert_eq!(e.stack.len(), 3);
assert_eq!(e.stack[0].as_generic(), Some(2));
assert_eq!(e.stack[1].as_generic(), Some(3));
assert_eq!(e.stack[2].as_generic(), Some(1));
}
#[test]
fn test_pick() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 10,
DW_OP_lit0 + 20,
DW_OP_lit0 + 30,
DW_OP_pick,
1,
])
.unwrap();
assert_eq!(e.stack.len(), 4);
assert_eq!(e.stack[3].as_generic(), Some(20));
}
#[test]
fn test_skip() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 1,
DW_OP_skip,
3,
0, DW_OP_lit0 + 3,
DW_OP_nop,
])
.unwrap();
assert_eq!(e.stack.len(), 1);
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_bra_taken() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 1, DW_OP_bra,
4,
0, DW_OP_lit0 + 99, DW_OP_nop,
])
.unwrap();
assert!(e.stack.is_empty());
}
#[test]
fn test_bra_not_taken() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 0, DW_OP_bra,
4,
0, DW_OP_lit0 + 42, DW_OP_nop,
])
.unwrap();
assert_eq!(e.stack.len(), 1);
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_evaluate_reg0() {
let mut e = X86DwarfExpr::new();
e.set_register(0, 0x1234);
e.evaluate(&[DW_OP_reg0]).unwrap();
if let X86DwarfLocation::Register { reg, offset } = &e.location.unwrap() {
assert_eq!(*reg, 0);
assert_eq!(*offset, 0x1234);
} else {
panic!("Expected Register location");
}
}
#[test]
fn test_evaluate_breg0() {
let mut e = X86DwarfExpr::new();
e.set_register(7, 0x1000); let mut expr = vec![DW_OP_breg7];
expr.extend(encode_sleb128(8));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x1008));
}
#[test]
fn test_evaluate_fbreg() {
let mut e = X86DwarfExpr::new();
e.set_frame_base(0x2000);
let mut expr = vec![DW_OP_fbreg];
expr.extend(encode_sleb128(-16));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x1FF0));
}
#[test]
fn test_evaluate_regx() {
let mut e = X86DwarfExpr::new();
e.set_register(42, 0xDEAD);
let mut expr = vec![DW_OP_regx];
expr.extend(encode_uleb128(42));
e.evaluate(&expr).unwrap();
if let X86DwarfLocation::Register { reg, offset } = &e.location.unwrap() {
assert_eq!(*reg, 42);
assert_eq!(*offset, 0xDEAD);
} else {
panic!("Expected Register location");
}
}
#[test]
fn test_evaluate_bregx() {
let mut e = X86DwarfExpr::new();
e.set_register(10, 100);
let mut expr = vec![DW_OP_bregx];
expr.extend(encode_uleb128(10));
expr.extend(encode_sleb128(50));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(150));
}
#[test]
fn test_stack_value() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 99, DW_OP_stack_value]).unwrap();
if let X86DwarfLocation::Value { value } = &e.location.unwrap() {
assert_eq!(*value, 99);
} else {
panic!("Expected Value location");
}
}
#[test]
fn test_piece_single() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_reg0];
expr.push(DW_OP_piece);
expr.extend(encode_uleb128(4));
let result = e.evaluate(&expr).unwrap();
if let X86DwarfLocation::Composite { pieces } = &result {
assert_eq!(pieces.len(), 1);
assert_eq!(pieces[0].size_in_bits, Some(32));
} else {
panic!("Expected Composite location");
}
}
#[test]
fn test_bit_piece() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_reg3];
expr.push(DW_OP_bit_piece);
expr.extend(encode_uleb128(8)); expr.extend(encode_uleb128(4)); let result = e.evaluate(&expr).unwrap();
if let X86DwarfLocation::Composite { pieces } = &result {
assert_eq!(pieces.len(), 1);
assert_eq!(pieces[0].size_in_bits, Some(8));
assert_eq!(pieces[0].bit_offset, Some(4));
} else {
panic!("Expected Composite location");
}
}
#[test]
fn test_call_frame_cfa() {
let mut e = X86DwarfExpr::new();
e.set_cfa(0x7FFFFFFF0000);
e.evaluate(&[DW_OP_call_frame_cfa]).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x7FFFFFFF0000));
}
#[test]
fn test_push_object_address() {
let mut e = X86DwarfExpr::new();
e.set_object_address(0xDEAD);
e.evaluate(&[DW_OP_push_object_address]).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0xDEAD));
}
#[test]
fn test_form_tls_address() {
let mut e = X86DwarfExpr::new();
e.set_tls_base(0x10000);
e.evaluate(&[DW_OP_lit0 + 8, DW_OP_form_tls_address])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x10008));
}
#[test]
fn test_cfi_breg() {
let mut e = X86DwarfExpr::new();
e.set_register(7, 0x1000);
let mut expr = vec![DW_OP_breg7];
expr.extend(encode_sleb128(8));
let result = e.evaluate_cfi(&expr).unwrap();
assert_eq!(result.value, 0x1008);
}
#[test]
fn test_cfi_plus() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_lit0 + 10, DW_OP_lit0 + 20, DW_OP_plus];
let result = e.evaluate_cfi(&expr).unwrap();
assert_eq!(result.value, 30);
}
#[test]
fn test_cfi_minus() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_lit0 + 50, DW_OP_lit0 + 10, DW_OP_minus];
let result = e.evaluate_cfi(&expr).unwrap();
assert_eq!(result.value, 40);
}
#[test]
fn test_location_list_entry_covers() {
let entry = X86LocationListEntry::new(0x1000, 0x2000, vec![]);
assert!(entry.covers(0x1000));
assert!(entry.covers(0x1FFF));
assert!(!entry.covers(0x2000));
assert!(!entry.covers(0x0FFF));
}
#[test]
fn test_location_list_entry_base_address() {
let entry = X86LocationListEntry::new_base_address(0x5000);
assert!(entry.is_base_address);
assert!(!entry.covers(0x4000));
}
#[test]
fn test_find_location_for_pc() {
let eval = X86DwarfExpr::new();
let entries = vec![
X86LocationListEntry::new(0x1000, 0x2000, vec![DW_OP_lit0 + 1]),
X86LocationListEntry::new(0x3000, 0x4000, vec![DW_OP_lit0 + 2]),
];
assert!(eval.find_location_for_pc(&entries, 0x1500).is_some());
assert!(eval.find_location_for_pc(&entries, 0x2500).is_none());
assert!(eval.find_location_for_pc(&entries, 0x3500).is_some());
}
#[test]
fn test_encode_uleb128_zero() {
assert_eq!(encode_uleb128(0), vec![0]);
}
#[test]
fn test_encode_uleb128_small() {
assert_eq!(encode_uleb128(127), vec![127]);
}
#[test]
fn test_encode_uleb128_two_byte() {
assert_eq!(encode_uleb128(128), vec![0x80, 0x01]);
}
#[test]
fn test_encode_uleb128_large() {
let encoded = encode_uleb128(624485);
let mut e = X86DwarfExpr::new();
e.expr_bytes = encoded;
assert_eq!(e.read_uleb().unwrap(), 624485);
}
#[test]
fn test_encode_sleb128_zero() {
assert_eq!(encode_sleb128(0), vec![0]);
}
#[test]
fn test_encode_sleb128_positive() {
assert_eq!(encode_sleb128(64), vec![0x40]);
}
#[test]
fn test_encode_sleb128_negative() {
let encoded = encode_sleb128(-1);
let mut e = X86DwarfExpr::new();
e.expr_bytes = encoded;
assert_eq!(e.read_sleb().unwrap(), -1);
}
#[test]
fn test_encode_sleb128_negative_64() {
let encoded = encode_sleb128(-64);
let mut e = X86DwarfExpr::new();
e.expr_bytes = encoded;
assert_eq!(e.read_sleb().unwrap(), -64);
}
#[test]
fn test_make_reg_location_compact() {
let expr = make_reg_location(5);
assert_eq!(expr, vec![DW_OP_reg0 + 5]);
}
#[test]
fn test_make_reg_location_extended() {
let expr = make_reg_location(42);
assert_eq!(expr[0], DW_OP_regx);
let mut e = X86DwarfExpr::new();
e.expr_bytes = expr[1..].to_vec();
assert_eq!(e.read_uleb().unwrap(), 42);
}
#[test]
fn test_make_fbreg_location() {
let expr = make_fbreg_location(-8);
assert_eq!(expr[0], DW_OP_fbreg);
let mut e = X86DwarfExpr::new();
e.expr_bytes = expr[1..].to_vec();
assert_eq!(e.read_sleb().unwrap(), -8);
}
#[test]
fn test_make_constant_value_small() {
let expr = make_constant_value(5);
assert_eq!(expr[0], DW_OP_lit0 + 5);
assert_eq!(expr[1], DW_OP_stack_value);
}
#[test]
fn test_make_constant_value_large() {
let expr = make_constant_value(0x10000);
assert_eq!(expr[0], DW_OP_const4u);
assert_eq!(*expr.last().unwrap(), DW_OP_stack_value);
}
#[test]
fn test_dwarf_op_name_known() {
assert_eq!(dwarf_op_name(DW_OP_addr), "DW_OP_addr");
assert_eq!(dwarf_op_name(DW_OP_plus), "DW_OP_plus");
assert_eq!(dwarf_op_name(DW_OP_regx), "DW_OP_regx");
assert_eq!(dwarf_op_name(DW_OP_stack_value), "DW_OP_stack_value");
}
#[test]
fn test_dwarf_op_name_lit_range() {
assert_eq!(dwarf_op_name(DW_OP_lit0 + 5), "DW_OP_litN");
assert_eq!(dwarf_op_name(DW_OP_lit31), "DW_OP_litN");
}
#[test]
fn test_dwarf_op_name_reg_range() {
assert_eq!(dwarf_op_name(DW_OP_reg0), "DW_OP_regN");
assert_eq!(dwarf_op_name(DW_OP_reg31), "DW_OP_regN");
}
#[test]
fn test_dwarf_op_name_breg_range() {
assert_eq!(dwarf_op_name(DW_OP_breg0), "DW_OP_bregN");
assert_eq!(dwarf_op_name(DW_OP_breg31), "DW_OP_bregN");
}
#[test]
fn test_dwarf_op_name_unknown() {
assert_eq!(dwarf_op_name(0xFF), "UNKNOWN");
}
#[test]
fn test_complex_arithmetic() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 3,
DW_OP_lit0 + 5,
DW_OP_plus,
DW_OP_lit0 + 2,
DW_OP_mul,
])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(16));
}
#[test]
fn test_variable_location_fbreg_deref() {
let mut e = X86DwarfExpr::new();
e.set_frame_base(0x1000);
let mut expr = vec![DW_OP_fbreg];
expr.extend(encode_sleb128(-8));
expr.push(DW_OP_deref);
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0xFF8));
}
#[test]
fn test_div_by_zero_error() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_lit0 + 10, DW_OP_lit0, DW_OP_div]);
assert!(result.is_err());
assert!(result.unwrap_err().contains("division by zero"));
}
#[test]
fn test_max_steps() {
let mut e = X86DwarfExpr::new();
e.max_steps = 2;
let result = e.evaluate(&[
DW_OP_lit0 + 1,
DW_OP_lit0 + 2,
DW_OP_lit0 + 3,
DW_OP_lit0 + 4,
]);
assert!(result.is_err());
assert!(result.unwrap_err().contains("Maximum evaluation steps"));
}
#[test]
fn test_unknown_opcode() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[0xFF]);
assert!(result.is_err());
assert!(result.unwrap_err().contains("Unknown DWARF opcode"));
}
#[test]
fn test_stack_underflow_dup() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_dup]);
assert!(result.is_err());
}
#[test]
fn test_stack_underflow_drop() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_drop]);
assert!(result.is_err());
}
#[test]
fn test_stack_underflow_over() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_lit0 + 1, DW_OP_over]);
assert!(result.is_err());
}
#[test]
fn test_stack_underflow_swap() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_lit0 + 1, DW_OP_swap]);
assert!(result.is_err());
}
#[test]
fn test_default_evaluator() {
let e = X86DwarfExpr::default();
assert_eq!(e.address_size, 8);
assert!(e.stack.is_empty());
}
#[test]
fn test_clear_state() {
let mut e = X86DwarfExpr::new();
e.push(42);
e.clear();
assert!(e.stack.is_empty());
}
#[test]
fn test_evaluate_plus_uconst() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_lit0 + 100, DW_OP_plus_uconst];
expr.extend(encode_uleb128(50));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(150));
}
#[test]
fn test_evaluate_nop() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 42, DW_OP_nop]).unwrap();
assert_eq!(e.stack.len(), 1);
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_dwarf_expr_value_from_u64() {
let v: DwarfExprValue = 42u64.into();
assert_eq!(v.as_generic(), Some(42));
}
#[test]
fn test_dwarf_expr_value_from_i64() {
let v: DwarfExprValue = (-1i64).into();
assert_eq!(v.as_generic(), Some((-1i64) as u64));
}
#[test]
fn test_dwarf_expr_value_is_incomplete() {
assert!(DwarfExprValue::Incomplete.is_incomplete());
assert!(!DwarfExprValue::Generic(0).is_incomplete());
}
#[test]
fn test_x86_dwarf_piece_new_register() {
let p = X86DwarfPiece::new_register(0, 0);
if let X86DwarfLocation::Register { reg, .. } = *p.location {
assert_eq!(reg, 0);
} else {
panic!("Expected Register");
}
}
#[test]
fn test_x86_dwarf_piece_new_memory() {
let p = X86DwarfPiece::new_memory(0xDEAD);
if let X86DwarfLocation::Memory { address } = *p.location {
assert_eq!(address, 0xDEAD);
} else {
panic!("Expected Memory");
}
}
#[test]
fn test_x86_dwarf_piece_with_size() {
let p = X86DwarfPiece::new_register(0, 0).with_size(32);
assert_eq!(p.size_in_bits, Some(32));
}
#[test]
fn test_x86_dwarf_piece_with_bit_offset() {
let p = X86DwarfPiece::new_register(0, 0).with_bit_offset(4);
assert_eq!(p.bit_offset, Some(4));
}
#[test]
fn test_cfi_evaluation_new() {
let result = X86CfiEvaluation::new(100, true);
assert_eq!(result.value, 100);
assert!(result.is_initial);
}
#[test]
fn test_x86_location_list_entry_evaluate() {
let mut e = X86DwarfExpr::new();
let entry = X86LocationListEntry::new(0x1000, 0x2000, vec![DW_OP_lit0 + 42]);
let result = e.evaluate_location_list_entry(&entry).unwrap();
assert_eq!(result, X86DwarfLocation::Memory { address: 42 });
}
#[test]
fn test_evaluate_empty_expression() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[]).unwrap();
assert_eq!(result, X86DwarfLocation::Undefined);
}
#[test]
fn test_all_reg_names_for_gprs() {
for reg in 0..=15u64 {
let name = x86_dwarf_reg_name(reg);
assert!(!name.is_empty());
assert_ne!(name, "unknown");
assert_ne!(name, "reserved");
}
}
#[test]
fn test_all_reg_names_for_xmm() {
for reg in 40..=71u64 {
let name = x86_dwarf_reg_name(reg);
assert!(name.starts_with("XMM"));
}
}
#[test]
fn test_all_const_ops_defined() {
let const_ops = [
DW_OP_const1u,
DW_OP_const1s,
DW_OP_const2u,
DW_OP_const2s,
DW_OP_const4u,
DW_OP_const4s,
DW_OP_const8u,
DW_OP_const8s,
DW_OP_constu,
DW_OP_consts,
];
for &op in &const_ops {
assert!(op >= DW_OP_const1u && op <= DW_OP_consts);
}
}
#[test]
fn test_implicit_value() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_implicit_value];
expr.extend(encode_uleb128(4)); expr.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]);
let result = e.evaluate(&expr).unwrap();
if let X86DwarfLocation::Implicit { data } = &result {
assert_eq!(data, &[0xAA, 0xBB, 0xCC, 0xDD]);
} else {
panic!("Expected Implicit location");
}
}
#[test]
fn test_implicit_pointer() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_implicit_pointer];
expr.extend_from_slice(&0x1234u64.to_le_bytes());
expr.extend(encode_sleb128(8));
let result = e.evaluate(&expr).unwrap();
if let X86DwarfLocation::ImplicitPointer {
die_offset,
byte_offset,
} = &result
{
assert_eq!(*die_offset, 0x1234);
assert_eq!(*byte_offset, 8);
} else {
panic!("Expected ImplicitPointer location");
}
}
#[test]
fn test_complex_with_branch() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 1,
DW_OP_bra,
3,
0, DW_OP_lit0 + 99,
DW_OP_lit0 + 42,
])
.unwrap();
assert_eq!(e.stack.len(), 1);
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_reg_name_zmm_range() {
assert_eq!(x86_dwarf_reg_name(102), "ZMM0");
assert_eq!(x86_dwarf_reg_name(133), "ZMM31");
assert_eq!(x86_dwarf_reg_name(110), "ZMM8");
}
#[test]
fn test_reg_name_k_registers() {
assert_eq!(x86_dwarf_reg_name(94), "K0");
assert_eq!(x86_dwarf_reg_name(101), "K7");
}
#[test]
fn test_call2_handled_gracefully() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_call2, 0x10, 0x00]).unwrap();
assert_eq!(e.stack.len(), 1);
assert!(e.stack[0].is_incomplete());
}
#[test]
fn test_call4_handled_gracefully() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_call4, 0x00, 0x00, 0x00, 0x00]).unwrap();
assert_eq!(e.stack.len(), 1);
}
#[test]
fn test_entry_value_handled_gracefully() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_entry_value];
expr.extend(encode_uleb128(2));
expr.push(DW_OP_lit0 + 5);
expr.push(DW_OP_nop);
e.evaluate(&expr).unwrap();
assert_eq!(e.stack.len(), 1);
}
#[test]
fn test_convert_reinterpret_pass_through() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_lit0 + 42, DW_OP_convert];
expr.extend(encode_uleb128(0));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_reinterpret_pass_through() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_lit0 + 99, DW_OP_reinterpret];
expr.extend(encode_uleb128(0));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(99));
}
#[test]
fn test_gnu_push_tls_address() {
let mut e = X86DwarfExpr::new();
e.set_tls_base(0x1000);
e.evaluate(&[DW_OP_lit0 + 8, DW_OP_GNU_push_tls_address])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x1008));
}
#[test]
fn test_default_evaluator_values() {
let e = X86DwarfExpr::new();
assert!(e.object_address.is_none());
assert!(e.cfa.is_none());
assert!(e.frame_base.is_none());
assert!(e.tls_base.is_none());
assert_eq!(e.address_size, 8);
assert_eq!(e.max_steps, 1000);
}
#[test]
fn test_mul_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0, DW_OP_mul])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_div_by_one() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 42, DW_OP_lit0 + 1, DW_OP_div])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_mod_identity() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 10, DW_OP_lit0 + 3, DW_OP_mod])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_neg_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0, DW_OP_neg]).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_neg_double() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_neg, DW_OP_neg]).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(5));
}
#[test]
fn test_not_idempotent() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 0xFF, DW_OP_not, DW_OP_not])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0xFF));
}
#[test]
fn test_and_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 42, DW_OP_lit0, DW_OP_and])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_or_all_ones() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 0, DW_OP_lit0 + 0xFF, DW_OP_or])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0xFF));
}
#[test]
fn test_xor_self_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 42, DW_OP_lit0 + 42, DW_OP_xor])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_shl_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 8, DW_OP_lit0, DW_OP_shl])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(8));
}
#[test]
fn test_shr_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 8, DW_OP_lit0, DW_OP_shr])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(8));
}
#[test]
fn test_shl_large_shift() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_lit0 + 63, DW_OP_shl])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1u64 << 63));
}
#[test]
fn test_shr_large_shift() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 8, DW_OP_lit0 + 3, DW_OP_shr])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_shra_negative() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_const8u];
expr.extend_from_slice(&((-16i64) as u64).to_le_bytes());
expr.push(DW_OP_lit0 + 2);
expr.push(DW_OP_shra);
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some((-4i64) as u64));
}
#[test]
fn test_eq_self() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 7, DW_OP_lit0 + 7, DW_OP_eq])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_ne_self() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 7, DW_OP_lit0 + 7, DW_OP_ne])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_lt_self() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0 + 5, DW_OP_lt])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_gt_self() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0 + 5, DW_OP_gt])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_le_self() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0 + 5, DW_OP_le])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_ge_self() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 5, DW_OP_lit0 + 5, DW_OP_ge])
.unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_lt_negative() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_const8u];
expr.extend_from_slice(&((-5i64) as u64).to_le_bytes());
expr.push(DW_OP_lit0 + 10);
expr.push(DW_OP_lt);
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(1));
}
#[test]
fn test_gt_negative() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_const8u];
expr.extend_from_slice(&((-3i64) as u64).to_le_bytes());
expr.push(DW_OP_lit0 + 5);
expr.push(DW_OP_gt);
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0));
}
#[test]
fn test_dup_twice() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_dup, DW_OP_dup]).unwrap();
assert_eq!(e.stack.len(), 3);
}
#[test]
fn test_swap_twice_identity() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_lit0 + 2, DW_OP_swap, DW_OP_swap])
.unwrap();
assert_eq!(e.stack.len(), 2);
assert_eq!(e.stack[0].as_generic(), Some(1));
assert_eq!(e.stack[1].as_generic(), Some(2));
}
#[test]
fn test_rot_twice() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 1,
DW_OP_lit0 + 2,
DW_OP_lit0 + 3,
DW_OP_rot,
DW_OP_rot,
])
.unwrap();
assert_eq!(e.stack.len(), 3);
assert_eq!(e.stack[0].as_generic(), Some(1));
assert_eq!(e.stack[1].as_generic(), Some(2));
assert_eq!(e.stack[2].as_generic(), Some(3));
}
#[test]
fn test_pick_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 10, DW_OP_pick, 0]).unwrap();
assert_eq!(e.stack.len(), 2);
assert_eq!(e.stack[1].as_generic(), Some(10));
}
#[test]
fn test_pick_out_of_range() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_lit0 + 5, DW_OP_pick, 10]);
assert!(result.is_err());
}
#[test]
fn test_over_twice() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[DW_OP_lit0 + 1, DW_OP_lit0 + 2, DW_OP_over, DW_OP_over])
.unwrap();
assert_eq!(e.stack.len(), 4);
}
#[test]
fn test_skip_zero() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 5,
DW_OP_skip,
0,
0, DW_OP_lit0 + 10,
])
.unwrap();
assert_eq!(e.stack.len(), 2);
}
#[test]
fn test_skip_forward() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 1,
DW_OP_skip,
2,
0, DW_OP_lit0 + 99,
DW_OP_lit0 + 2,
DW_OP_plus,
])
.unwrap();
assert_eq!(e.stack.len(), 1);
assert_eq!(e.stack[0].as_generic(), Some(3));
}
#[test]
fn test_bra_then_skip() {
let mut e = X86DwarfExpr::new();
e.evaluate(&[
DW_OP_lit0 + 1,
DW_OP_bra,
4,
0, DW_OP_lit0 + 99,
DW_OP_lit0 + 42, ])
.unwrap();
assert_eq!(e.stack.len(), 1);
assert_eq!(e.stack[0].as_generic(), Some(42));
}
#[test]
fn test_all_gpr_reg_names() {
let gpr_names = ["RAX", "RDX", "RCX", "RBX", "RSI", "RDI", "RBP", "RSP"];
for (i, name) in gpr_names.iter().enumerate() {
assert_eq!(x86_dwarf_reg_name(i as u64), *name);
}
}
#[test]
fn test_breg_basic() {
let mut e = X86DwarfExpr::new();
e.set_register(7, 0x1000);
let mut expr = vec![DW_OP_breg7];
expr.extend(encode_sleb128(16));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x1010));
}
#[test]
fn test_breg_negative() {
let mut e = X86DwarfExpr::new();
e.set_register(6, 0x2000);
let mut expr = vec![DW_OP_breg6];
expr.extend(encode_sleb128(-32));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x1FE0));
}
#[test]
fn test_reg_with_value() {
let mut e = X86DwarfExpr::new();
e.set_register(0, 0x12345678);
e.evaluate(&[DW_OP_reg0]).unwrap();
if let X86DwarfLocation::Register { reg, offset } = e.location.unwrap() {
assert_eq!(reg, 0);
assert_eq!(offset, 0x12345678i64);
} else {
panic!("Expected Register");
}
}
#[test]
fn test_fbreg_zero() {
let mut e = X86DwarfExpr::new();
e.set_frame_base(0x5000);
let mut expr = vec![DW_OP_fbreg];
expr.extend(encode_sleb128(0));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x5000));
}
#[test]
fn test_multiple_pieces() {
let mut e = X86DwarfExpr::new();
let mut expr = Vec::new();
expr.push(DW_OP_reg0);
expr.push(DW_OP_piece);
expr.extend(encode_uleb128(4));
expr.push(DW_OP_reg1);
expr.push(DW_OP_piece);
expr.extend(encode_uleb128(4));
let result = e.evaluate(&expr).unwrap();
if let X86DwarfLocation::Composite { pieces } = &result {
assert_eq!(pieces.len(), 2);
assert_eq!(pieces[0].size_in_bits, Some(32));
assert_eq!(pieces[1].size_in_bits, Some(32));
} else {
panic!("Expected Composite");
}
}
#[test]
fn test_bit_piece_full_register() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_reg2];
expr.push(DW_OP_bit_piece);
expr.extend(encode_uleb128(64)); expr.extend(encode_uleb128(0)); let result = e.evaluate(&expr).unwrap();
if let X86DwarfLocation::Composite { pieces } = &result {
assert_eq!(pieces.len(), 1);
assert_eq!(pieces[0].size_in_bits, Some(64));
} else {
panic!("Expected Composite");
}
}
#[test]
fn test_cfi_constu() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_constu];
expr.extend(encode_uleb128(42));
let result = e.evaluate_cfi(&expr).unwrap();
assert_eq!(result.value, 42);
}
#[test]
fn test_cfi_consts_negative() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_consts];
expr.extend(encode_sleb128(-8));
let result = e.evaluate_cfi(&expr).unwrap();
assert_eq!(result.value, (-8i64 as u64));
}
#[test]
fn test_cfi_complex_expression() {
let mut e = X86DwarfExpr::new();
e.set_register(6, 0x1000); let mut expr = vec![DW_OP_breg6];
expr.extend(encode_sleb128(-16)); expr.push(DW_OP_lit0 + 8);
expr.push(DW_OP_plus);
let result = e.evaluate_cfi(&expr).unwrap();
assert_eq!(result.value, 0x1000 - 16 + 8);
}
#[test]
fn test_cfi_deref() {
let mut e = X86DwarfExpr::new();
e.set_register(7, 0x1000);
let mut expr = vec![DW_OP_breg7];
expr.extend(encode_sleb128(0));
expr.push(DW_OP_deref);
let result = e.evaluate_cfi(&expr).unwrap();
assert_eq!(result.value, 0x1000);
}
#[test]
fn test_location_list_entry_boundaries() {
let entry = X86LocationListEntry::new(0x100, 0x200, vec![]);
assert!(!entry.covers(0x0FF));
assert!(entry.covers(0x100));
assert!(entry.covers(0x1FF));
assert!(!entry.covers(0x200));
}
#[test]
fn test_location_list_empty() {
let eval = X86DwarfExpr::new();
let entries: Vec<X86LocationListEntry> = vec![];
assert!(eval.find_location_for_pc(&entries, 0x100).is_none());
}
#[test]
fn test_location_list_skips_base_address_entries() {
let eval = X86DwarfExpr::new();
let entries = vec![
X86LocationListEntry::new_base_address(0x1000),
X86LocationListEntry::new(0x1000, 0x2000, vec![]),
];
let found = eval.find_location_for_pc(&entries, 0x1500);
assert!(found.is_some());
assert!(!found.unwrap().is_base_address);
}
#[test]
fn test_empty_expression_with_registers_set() {
let mut e = X86DwarfExpr::new();
e.set_register(0, 42);
e.set_cfa(0x1000);
e.set_frame_base(0x2000);
let result = e.evaluate(&[]).unwrap();
assert_eq!(result, X86DwarfLocation::Undefined);
}
#[test]
fn test_plus_uconst_on_empty_stack() {
let mut e = X86DwarfExpr::new();
let mut expr = vec![DW_OP_plus_uconst];
expr.extend(encode_uleb128(5));
let result = e.evaluate(&expr);
assert!(result.is_err());
}
#[test]
fn test_stack_value_on_empty_stack() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_stack_value]);
assert!(result.is_err());
}
#[test]
fn test_form_tls_address_on_empty_stack() {
let mut e = X86DwarfExpr::new();
e.set_tls_base(0x1000);
let result = e.evaluate(&[DW_OP_form_tls_address]);
assert!(result.is_err());
}
#[test]
fn test_call_frame_cfa_not_set() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_call_frame_cfa]);
assert!(result.is_err());
}
#[test]
fn test_push_object_address_not_set() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[DW_OP_push_object_address]);
assert!(result.is_err());
}
#[test]
fn test_max_steps_boundary() {
let mut e = X86DwarfExpr::new();
e.max_steps = 3;
let result = e.evaluate(&[
DW_OP_lit0 + 1,
DW_OP_lit0 + 2,
DW_OP_lit0 + 3,
DW_OP_lit0 + 4,
]);
assert!(result.is_err());
}
#[test]
fn test_skip_negative() {
let mut e = X86DwarfExpr::new();
let result = e.evaluate(&[
DW_OP_skip,
0xFF,
0xFF, DW_OP_lit0 + 1,
]);
assert!(result.is_err());
}
#[test]
fn test_uleb128_multibyte() {
let data = vec![0xE5, 0x8E, 0x26]; let mut e = X86DwarfExpr::new();
e.expr_bytes = data;
assert_eq!(e.read_uleb().unwrap(), 624485);
}
#[test]
fn test_sleb128_minus_one() {
let data = vec![0x7F]; let mut e = X86DwarfExpr::new();
e.expr_bytes = data;
assert_eq!(e.read_sleb().unwrap(), -1);
}
#[test]
fn test_uleb128_truncated() {
let data = vec![0x80]; let mut e = X86DwarfExpr::new();
e.expr_bytes = data;
assert!(e.read_uleb().is_err());
}
#[test]
fn test_addr_table() {
let mut e = X86DwarfExpr::new();
e.addr_table = vec![0x1000, 0x2000, 0x3000];
let mut expr = vec![DW_OP_addrx];
expr.extend(encode_uleb128(1));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x2000));
}
#[test]
fn test_constx() {
let mut e = X86DwarfExpr::new();
e.addr_table = vec![0xAA, 0xBB];
let mut expr = vec![DW_OP_constx];
expr.extend(encode_uleb128(0));
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0xAA));
}
#[test]
fn test_addrx_out_of_bounds() {
let mut e = X86DwarfExpr::new();
e.addr_table = vec![0x1000];
let mut expr = vec![DW_OP_addrx];
expr.extend(encode_uleb128(99));
e.evaluate(&expr).unwrap();
assert!(e.stack.is_empty());
}
#[test]
fn test_encode_uleb128_127() {
assert_eq!(encode_uleb128(127), vec![127]);
}
#[test]
fn test_encode_uleb128_128() {
assert_eq!(encode_uleb128(128), vec![0x80, 0x01]);
}
#[test]
fn test_encode_sleb128_64() {
assert_eq!(encode_sleb128(64), vec![0x40]);
}
#[test]
fn test_encode_sleb128_neg_64() {
let bytes = encode_sleb128(-64);
let mut e = X86DwarfExpr::new();
e.expr_bytes = bytes;
assert_eq!(e.read_sleb().unwrap(), -64);
}
#[test]
fn test_read_address_32bit() {
let mut e = X86DwarfExpr::new();
e.address_size = 4;
e.expr_bytes = vec![0x78, 0x56, 0x34, 0x12];
assert_eq!(e.read_address().unwrap(), 0x12345678);
}
#[test]
fn test_read_address_64bit() {
let mut e = X86DwarfExpr::new();
e.address_size = 8;
e.expr_bytes = (0x1234567890ABCDEFu64).to_le_bytes().to_vec();
assert_eq!(e.read_address().unwrap(), 0x1234567890ABCDEF);
}
#[test]
fn test_write_and_evaluate_complex() {
let mut e = X86DwarfExpr::new();
e.set_frame_base(0x7FFF0000);
let mut expr = vec![DW_OP_fbreg];
expr.extend(encode_sleb128(-8));
expr.push(DW_OP_deref);
e.evaluate(&expr).unwrap();
assert_eq!(e.stack[0].as_generic(), Some(0x7FFEFFF8));
}
}