#![allow(non_upper_case_globals, dead_code)]
use std::collections::HashMap;
use crate::dwarf::dwarf_die as die_ext;
use crate::dwarf::dwarf_expr::DwarfExpression as CoreDwarfExpression;
use crate::dwarf::dwarf_frame::cfa_ops;
use crate::dwarf::dwarf_types::dwarf_ops;
use crate::dwarf::{
dwarf_attributes, dwarf_children, dwarf_encodings, dwarf_forms, dwarf_languages, dwarf_tags,
AbbrevTable, DwarfAttribute, LineProgram, DIE,
};
use crate::dwarf::{encode_sleb128, encode_uleb128};
use crate::x86::x86_register_info::{
X86RegisterInfo,
BND0,
BND1,
BND2,
BND3,
CR0,
CR1,
CR2,
CR3,
CR4,
CR8,
CS,
DR0,
DR1,
DR2,
DR3,
DR6,
DR7,
DS,
EFLAGS,
EIP,
ES,
FS,
GS,
K0,
K1,
K2,
K3,
K4,
K5,
K6,
K7,
MM0,
MM1,
MM2,
MM3,
MM4,
MM5,
MM6,
MM7,
R10,
R11,
R12,
R13,
R14,
R15,
R8,
R9,
RAX,
RBP,
RBX,
RCX,
RDI,
RDX,
RFLAGS,
RIP,
RSI,
RSP,
SS,
ST0,
ST1,
ST2,
ST3,
ST4,
ST5,
ST6,
ST7,
TOTAL_REG_COUNT,
XMM0,
XMM1,
XMM10,
XMM11,
XMM12,
XMM13,
XMM14,
XMM15,
XMM16,
XMM17,
XMM18,
XMM19,
XMM2,
XMM20,
XMM21,
XMM22,
XMM23,
XMM24,
XMM25,
XMM26,
XMM27,
XMM28,
XMM29,
XMM3,
XMM30,
XMM31,
XMM4,
XMM5,
XMM6,
XMM7,
XMM8,
XMM9,
YMM0,
YMM1,
YMM10,
YMM11,
YMM12,
YMM13,
YMM14,
YMM15,
YMM16,
YMM17,
YMM18,
YMM19,
YMM2,
YMM20,
YMM21,
YMM22,
YMM23,
YMM24,
YMM25,
YMM26,
YMM27,
YMM28,
YMM29,
YMM3,
YMM30,
YMM31,
YMM4,
YMM5,
YMM6,
YMM7,
YMM8,
YMM9,
ZMM0,
ZMM1,
ZMM10,
ZMM11,
ZMM12,
ZMM13,
ZMM14,
ZMM15,
ZMM16,
ZMM17,
ZMM18,
ZMM19,
ZMM2,
ZMM20,
ZMM21,
ZMM22,
ZMM23,
ZMM24,
ZMM25,
ZMM26,
ZMM27,
ZMM28,
ZMM29,
ZMM3,
ZMM30,
ZMM31,
ZMM4,
ZMM5,
ZMM6,
ZMM7,
ZMM8,
ZMM9,
};
pub mod x86_dwarf_languages {
pub const DW_LANG_C89: u16 = 0x0001;
pub const DW_LANG_C: u16 = 0x0002;
pub const DW_LANG_Ada83: u16 = 0x0003;
pub const DW_LANG_C_plus_plus: u16 = 0x0004;
pub const DW_LANG_Cobol74: u16 = 0x0005;
pub const DW_LANG_Cobol85: u16 = 0x0006;
pub const DW_LANG_Fortran77: u16 = 0x0007;
pub const DW_LANG_Fortran90: u16 = 0x0008;
pub const DW_LANG_Pascal83: u16 = 0x0009;
pub const DW_LANG_Modula2: u16 = 0x000a;
pub const DW_LANG_Java: u16 = 0x000b;
pub const DW_LANG_C99: u16 = 0x000c;
pub const DW_LANG_Ada95: u16 = 0x000d;
pub const DW_LANG_Fortran95: u16 = 0x000e;
pub const DW_LANG_PLI: u16 = 0x000f;
pub const DW_LANG_ObjC: u16 = 0x0010;
pub const DW_LANG_ObjC_plus_plus: u16 = 0x0011;
pub const DW_LANG_UPC: u16 = 0x0012;
pub const DW_LANG_D: u16 = 0x0013;
pub const DW_LANG_Python: u16 = 0x0014;
pub const DW_LANG_OpenCL: u16 = 0x0015;
pub const DW_LANG_Go: u16 = 0x0016;
pub const DW_LANG_Modula3: u16 = 0x0017;
pub const DW_LANG_Haskell: u16 = 0x0018;
pub const DW_LANG_C_plus_plus_03: u16 = 0x0019;
pub const DW_LANG_C_plus_plus_11: u16 = 0x001a;
pub const DW_LANG_OCaml: u16 = 0x001b;
pub const DW_LANG_Rust: u16 = 0x001c;
pub const DW_LANG_C11: u16 = 0x001d;
pub const DW_LANG_Swift: u16 = 0x001e;
pub const DW_LANG_Julia: u16 = 0x001f;
pub const DW_LANG_Dylan: u16 = 0x0020;
pub const DW_LANG_C_plus_plus_14: u16 = 0x0021;
pub const DW_LANG_Fortran03: u16 = 0x0022;
pub const DW_LANG_Fortran08: u16 = 0x0023;
pub const DW_LANG_RenderScript: u16 = 0x0024;
pub const DW_LANG_BLISS: u16 = 0x0025;
pub const DW_LANG_C_plus_plus_17: u16 = 0x002a;
pub const DW_LANG_Ada2005: u16 = 0x002b;
pub const DW_LANG_Ada2012: u16 = 0x002c;
pub const DW_LANG_C_plus_plus_20: u16 = 0x0030;
pub const DW_LANG_C17: u16 = 0x0031;
pub const DW_LANG_Fortran18: u16 = 0x0032;
pub const DW_LANG_C23: u16 = 0x0033;
pub const DW_LANG_Assembly: u16 = 0x0034;
}
pub mod x86_dwarf_encodings {
pub const DW_ATE_address: u8 = 0x01;
pub const DW_ATE_boolean: u8 = 0x02;
pub const DW_ATE_complex_float: u8 = 0x03;
pub const DW_ATE_float: u8 = 0x04;
pub const DW_ATE_signed: u8 = 0x05;
pub const DW_ATE_signed_char: u8 = 0x06;
pub const DW_ATE_unsigned: u8 = 0x07;
pub const DW_ATE_unsigned_char: u8 = 0x08;
pub const DW_ATE_imaginary_float: u8 = 0x09;
pub const DW_ATE_packed_decimal: u8 = 0x0a;
pub const DW_ATE_numeric_string: u8 = 0x0b;
pub const DW_ATE_edited: u8 = 0x0c;
pub const DW_ATE_signed_fixed: u8 = 0x0d;
pub const DW_ATE_unsigned_fixed: u8 = 0x0e;
pub const DW_ATE_decimal_float: u8 = 0x0f;
pub const DW_ATE_UTF: u8 = 0x10;
pub const DW_ATE_UCS: u8 = 0x11;
pub const DW_ATE_ASCII: u8 = 0x12;
pub const DW_ATE_lo_user: u8 = 0x80;
pub const DW_ATE_hi_user: u8 = 0xff;
}
pub mod x86_dwarf_inline {
pub const DW_INL_not_inlined: u8 = 0;
pub const DW_INL_inlined: u8 = 1;
pub const DW_INL_declared_not_inlined: u8 = 2;
pub const DW_INL_declared_inlined: u8 = 3;
}
pub mod x86_dwarf_access {
pub const DW_ACCESS_public: u8 = 1;
pub const DW_ACCESS_protected: u8 = 2;
pub const DW_ACCESS_private: u8 = 3;
}
pub mod x86_dwarf_visibility {
pub const DW_VIS_local: u8 = 1;
pub const DW_VIS_exported: u8 = 2;
pub const DW_VIS_qualified: u8 = 3;
}
pub mod x86_dwarf_virtuality {
pub const DW_VIRTUALITY_none: u8 = 0;
pub const DW_VIRTUALITY_virtual: u8 = 1;
pub const DW_VIRTUALITY_pure_virtual: u8 = 2;
}
pub mod x86_dwarf_children {
pub const DW_CHILDREN_no: u8 = 0x00;
pub const DW_CHILDREN_yes: u8 = 0x01;
}
pub mod x86_dwarf_forms {
pub const DW_FORM_strx: u16 = 0x1a;
pub const DW_FORM_addrx: u16 = 0x1b;
pub const DW_FORM_ref_sig8: u16 = 0x20;
pub const DW_FORM_implicit_const: u16 = 0x21;
pub const DW_FORM_loclistx: u16 = 0x22;
pub const DW_FORM_rnglistx: u16 = 0x23;
pub const DW_FORM_strx1: u16 = 0x25;
pub const DW_FORM_strx2: u16 = 0x26;
pub const DW_FORM_strx3: u16 = 0x27;
pub const DW_FORM_strx4: u16 = 0x28;
pub const DW_FORM_addrx1: u16 = 0x29;
pub const DW_FORM_addrx2: u16 = 0x2a;
pub const DW_FORM_addrx3: u16 = 0x2b;
pub const DW_FORM_addrx4: u16 = 0x2c;
}
pub mod x86_dwarf_tags {
pub use crate::dwarf::dwarf_tags::*;
pub const DW_TAG_enumerator: u16 = 0x28;
pub const DW_TAG_inheritance: u16 = 0x1c;
pub const DW_TAG_union_type: u16 = 0x17;
pub const DW_TAG_class_type: u16 = 0x02;
pub const DW_TAG_reference_type: u16 = 0x10;
pub const DW_TAG_rvalue_reference_type: u16 = 0x42;
pub const DW_TAG_restrict_type: u16 = 0x37;
}
pub mod x86_dwarf_attributes {
pub use crate::dwarf::dwarf_attributes::*;
pub const DW_AT_ranges: u16 = 0x48;
pub const DW_AT_accessibility: u16 = 0x32;
pub const DW_AT_upper_bound: u16 = 0x2f;
}
pub mod x86_dwarf_ops_ext {
pub const DW_OP_GNU_push_tls_address: u8 = 0xe0;
pub const DW_OP_entry_value: u8 = 0xa3;
pub const DW_OP_implicit_pointer: u8 = 0xa0;
pub const DW_OP_GNU_entry_value: u8 = 0xf3;
}
fn uleb128(val: u64) -> Vec<u8> {
let mut buf = Vec::new();
encode_uleb128(&mut buf, val);
buf
}
fn sleb128(val: i64) -> Vec<u8> {
let mut buf = Vec::new();
encode_sleb128(&mut buf, val);
buf
}
#[derive(Debug, Clone)]
pub struct X86DwarfLocation {
pub bytes: Vec<u8>,
pub address_size: u8,
pub is_location: bool,
}
impl X86DwarfLocation {
pub fn new(address_size: u8) -> Self {
Self {
bytes: Vec::new(),
address_size,
is_location: true,
}
}
pub fn new_x86_32() -> Self {
Self::new(4)
}
pub fn new_x86_64() -> Self {
Self::new(8)
}
pub fn finish(self) -> Vec<u8> {
self.bytes
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn len(&self) -> usize {
self.bytes.len()
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
pub fn clear(&mut self) {
self.bytes.clear();
}
pub fn set_is_location(&mut self, is_loc: bool) {
self.is_location = is_loc;
}
fn emit_u8(&mut self, b: u8) {
self.bytes.push(b);
}
fn emit_u16(&mut self, v: u16) {
self.bytes.extend_from_slice(&v.to_le_bytes());
}
fn emit_u32(&mut self, v: u32) {
self.bytes.extend_from_slice(&v.to_le_bytes());
}
fn emit_u64(&mut self, v: u64) {
self.bytes.extend_from_slice(&v.to_le_bytes());
}
fn emit_addr(&mut self, v: u64) {
match self.address_size {
4 => self.emit_u32(v as u32),
_ => self.emit_u64(v),
}
}
fn emit_uleb(&mut self, v: u64) {
encode_uleb128(&mut self.bytes, v);
}
fn emit_sleb(&mut self, v: i64) {
encode_sleb128(&mut self.bytes, v);
}
fn op_uleb(&mut self, op: u8, v: u64) {
self.emit_u8(op);
self.emit_uleb(v);
}
fn op_sleb(&mut self, op: u8, v: i64) {
self.emit_u8(op);
self.emit_sleb(v);
}
pub fn addr(&mut self, val: u64) {
self.emit_u8(dwarf_ops::DW_OP_addr as u8);
self.emit_addr(val);
}
pub fn deref(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_deref as u8);
}
pub fn deref_size(&mut self, size: u8) {
self.emit_u8(dwarf_ops::DW_OP_deref_size as u8);
self.emit_u8(size);
}
pub fn push_object_address(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_push_object_address as u8);
}
pub fn form_tls_address(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_form_tls_address as u8);
}
pub fn call_frame_cfa(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_call_frame_cfa as u8);
}
pub fn gnu_push_tls_address(&mut self) {
self.emit_u8(x86_dwarf_ops_ext::DW_OP_GNU_push_tls_address);
}
pub fn const_u8(&mut self, val: u8) {
self.emit_u8(dwarf_ops::DW_OP_const1u as u8);
self.emit_u8(val);
}
pub fn const_i8(&mut self, val: i8) {
self.emit_u8(dwarf_ops::DW_OP_const1s as u8);
self.emit_u8(val as u8);
}
pub fn const_u16(&mut self, val: u16) {
self.emit_u8(dwarf_ops::DW_OP_const2u as u8);
self.emit_u16(val);
}
pub fn const_i16(&mut self, val: i16) {
self.emit_u8(dwarf_ops::DW_OP_const2s as u8);
self.emit_u16(val as u16);
}
pub fn const_u32(&mut self, val: u32) {
self.emit_u8(dwarf_ops::DW_OP_const4u as u8);
self.emit_u32(val);
}
pub fn const_i32(&mut self, val: i32) {
self.emit_u8(dwarf_ops::DW_OP_const4s as u8);
self.emit_u32(val as u32);
}
pub fn const_u64(&mut self, val: u64) {
self.emit_u8(dwarf_ops::DW_OP_const8u as u8);
self.emit_u64(val);
}
pub fn const_i64(&mut self, val: i64) {
self.emit_u8(dwarf_ops::DW_OP_const8s as u8);
self.emit_u64(val as u64);
}
pub fn const_uleb(&mut self, val: u64) {
self.op_uleb(dwarf_ops::DW_OP_constu as u8, val);
}
pub fn const_sleb(&mut self, val: i64) {
self.op_sleb(dwarf_ops::DW_OP_consts as u8, val);
}
pub fn lit(&mut self, n: u8) {
assert!(n <= 31, "DW_OP_lit only supports values 0-31");
self.emit_u8(dwarf_ops::DW_OP_lit0 as u8 + n);
}
fn x86_reg_to_dwarf(reg_id: u16) -> u16 {
let dwarf = X86RegisterInfo::get_dwarf_num(reg_id);
if dwarf >= 0 {
dwarf as u16
} else {
Self::compute_ext_dwarf_num(reg_id)
}
}
fn compute_ext_dwarf_num(reg_id: u16) -> u16 {
if reg_id >= YMM0 && reg_id <= YMM31 {
let base_xmm = XMM0 + (reg_id - YMM0);
return X86RegisterInfo::get_dwarf_num(base_xmm) as u16;
}
if reg_id >= ZMM0 && reg_id <= ZMM31 {
let base_xmm = XMM0 + (reg_id - ZMM0);
return X86RegisterInfo::get_dwarf_num(base_xmm) as u16;
}
if reg_id >= K0 && reg_id <= K7 {
return 118 + (reg_id - K0);
}
if reg_id >= BND0 && reg_id <= BND3 {
return 126 + (reg_id - BND0);
}
256 + reg_id
}
pub fn reg(&mut self, reg_id: u16) {
let dwarf_num = Self::x86_reg_to_dwarf(reg_id);
if dwarf_num <= 31 {
self.emit_u8(dwarf_ops::DW_OP_reg0 as u8 + dwarf_num as u8);
} else {
self.op_uleb(dwarf_ops::DW_OP_regx as u8, dwarf_num as u64);
}
}
pub fn breg(&mut self, reg_id: u16, offset: i64) {
let dwarf_num = Self::x86_reg_to_dwarf(reg_id);
if dwarf_num <= 31 {
self.emit_u8(dwarf_ops::DW_OP_breg0 as u8 + dwarf_num as u8);
} else {
self.emit_u8(dwarf_ops::DW_OP_bregx as u8);
self.emit_uleb(dwarf_num as u64);
}
self.emit_sleb(offset);
}
pub fn fbreg(&mut self, offset: i64) {
self.op_sleb(dwarf_ops::DW_OP_fbreg as u8, offset);
}
pub fn rbp_offset_32(&mut self, offset: i32) -> &mut Self {
self.breg(RBP, offset as i64);
self
}
pub fn rbp_offset_64(&mut self, offset: i32) -> &mut Self {
self.breg(RBP, offset as i64);
self
}
pub fn rsp_offset(&mut self, offset: i32) -> &mut Self {
self.breg(RSP, offset as i64);
self
}
pub fn in_rax(&mut self) -> &mut Self {
self.reg(RAX);
self
}
pub fn in_xmm(&mut self, n: u16) -> &mut Self {
let xmm_base = XMM0 + n;
self.reg(xmm_base);
self
}
pub fn plus(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_plus as u8);
}
pub fn minus(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_minus as u8);
}
pub fn mul(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_mul as u8);
}
pub fn div(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_div as u8);
}
pub fn modulo(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_mod as u8);
}
pub fn and(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_and as u8);
}
pub fn or(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_or as u8);
}
pub fn xor(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_xor as u8);
}
pub fn neg(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_neg as u8);
}
pub fn not(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_not as u8);
}
pub fn abs(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_abs as u8);
}
pub fn shl(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_shl as u8);
}
pub fn shr(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_shr as u8);
}
pub fn shra(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_shra as u8);
}
pub fn plus_uconst(&mut self, val: u64) {
self.op_uleb(dwarf_ops::DW_OP_plus_uconst as u8, val);
}
pub fn dup(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_dup as u8);
}
pub fn drop(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_drop as u8);
}
pub fn over(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_over as u8);
}
pub fn pick(&mut self, index: u8) {
self.emit_u8(dwarf_ops::DW_OP_pick as u8);
self.emit_u8(index);
}
pub fn swap(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_swap as u8);
}
pub fn rot(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_rot as u8);
}
pub fn eq(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_eq as u8);
}
pub fn ne(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_ne as u8);
}
pub fn lt(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_lt as u8);
}
pub fn le(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_le as u8);
}
pub fn gt(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_gt as u8);
}
pub fn ge(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_ge as u8);
}
pub fn piece(&mut self, size_in_bytes: u64) {
self.op_uleb(dwarf_ops::DW_OP_piece as u8, size_in_bytes);
}
pub fn bit_piece(&mut self, size_in_bits: u64, offset_in_bits: u64) {
self.emit_u8(dwarf_ops::DW_OP_bit_piece as u8);
self.emit_uleb(size_in_bits);
self.emit_uleb(offset_in_bits);
}
pub fn stack_value(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_stack_value as u8);
}
pub fn implicit_value(&mut self, data: &[u8]) {
self.emit_u8(dwarf_ops::DW_OP_implicit_value as u8);
self.emit_uleb(data.len() as u64);
self.bytes.extend_from_slice(data);
}
pub fn entry_value(&mut self, inner_expr: &[u8]) {
self.emit_u8(x86_dwarf_ops_ext::DW_OP_entry_value);
self.emit_uleb(inner_expr.len() as u64);
self.bytes.extend_from_slice(inner_expr);
}
pub fn gnu_entry_value(&mut self, inner_expr: &[u8]) {
self.emit_u8(x86_dwarf_ops_ext::DW_OP_GNU_entry_value);
self.emit_uleb(inner_expr.len() as u64);
self.bytes.extend_from_slice(inner_expr);
}
pub fn skip(&mut self, offset: i16) {
self.emit_u8(dwarf_ops::DW_OP_skip as u8);
self.emit_u16(offset as u16);
}
pub fn bra(&mut self, offset: i16) {
self.emit_u8(dwarf_ops::DW_OP_bra as u8);
self.emit_u16(offset as u16);
}
pub fn nop(&mut self) {
self.emit_u8(dwarf_ops::DW_OP_nop as u8);
}
pub fn convert(&mut self, type_die_offset: u64) {
self.emit_u8(dwarf_ops::DW_OP_convert as u8);
self.emit_uleb(type_die_offset);
}
pub fn reinterpret(&mut self, type_die_offset: u64) {
self.emit_u8(dwarf_ops::DW_OP_reinterpret as u8);
self.emit_uleb(type_die_offset);
}
pub fn sysv_int_param(&mut self, param_index: usize, arg_area_base: i32) -> &mut Self {
match param_index {
0 => {
self.reg(RDI);
}
1 => {
self.reg(RSI);
}
2 => {
self.reg(RDX);
}
3 => {
self.reg(RCX);
}
4 => {
self.reg(R8);
}
5 => {
self.reg(R9);
}
_ => {
let stack_offset = arg_area_base as i64 + ((param_index - 6) * 8) as i64;
self.fbreg(stack_offset);
}
}
self
}
pub fn sysv_sse_param(&mut self, param_index: usize) -> &mut Self {
if param_index < 8 {
self.reg(XMM0 + param_index as u16);
} else {
self.fbreg(16 + (param_index - 8) as i64 * 8);
}
self
}
pub fn win64_int_param(&mut self, param_index: usize) -> &mut Self {
match param_index {
0 => {
self.reg(RCX);
}
1 => {
self.reg(RDX);
}
2 => {
self.reg(R8);
}
3 => {
self.reg(R9);
}
_ => {
let stack_offset = ((param_index - 4) * 8 + 8) as i64; self.fbreg(stack_offset);
}
}
self
}
pub fn local_var_below_rbp(&mut self, offset: u32) -> &mut Self {
self.breg(RBP, -(offset as i64));
self
}
pub fn local_var_below_ebp(&mut self, offset: u32) -> &mut Self {
self.breg(RBP, -(offset as i64));
self
}
}
#[derive(Debug, Clone)]
pub struct X86DwarfExpression {
pub stack: Vec<i64>,
pub output: Vec<u8>,
pub address_size: u8,
pub read_register: Option<fn(u16) -> Option<u64>>,
pub read_memory: Option<fn(u64, usize) -> Option<Vec<u8>>>,
pub frame_base: Option<u64>,
pub tls_base: Option<u64>,
pub max_steps: usize,
pc: usize,
expr_to_eval: Vec<u8>,
steps: usize,
last_result: Option<i64>,
result_is_location: bool,
}
impl X86DwarfExpression {
pub fn new(address_size: u8) -> Self {
Self {
stack: Vec::new(),
output: Vec::new(),
address_size,
read_register: None,
read_memory: None,
frame_base: None,
tls_base: None,
max_steps: 10000,
pc: 0,
expr_to_eval: Vec::new(),
steps: 0,
last_result: None,
result_is_location: true,
}
}
pub fn new_x86_32() -> Self {
Self::new(4)
}
pub fn new_x86_64() -> Self {
Self::new(8)
}
pub fn set_read_register(&mut self, f: fn(u16) -> Option<u64>) {
self.read_register = Some(f);
}
pub fn set_read_memory(&mut self, f: fn(u64, usize) -> Option<Vec<u8>>) {
self.read_memory = Some(f);
}
pub fn set_frame_base(&mut self, fb: u64) {
self.frame_base = Some(fb);
}
pub fn reset(&mut self) {
self.stack.clear();
self.pc = 0;
self.steps = 0;
self.last_result = None;
}
pub fn push(&mut self, val: i64) {
self.stack.push(val);
}
pub fn pop(&mut self) -> Option<i64> {
self.stack.pop()
}
pub fn evaluate(&mut self, expr: &[u8]) -> Result<Option<i64>, String> {
self.reset();
self.expr_to_eval = expr.to_vec();
self.pc = 0;
self.steps = 0;
while self.pc < self.expr_to_eval.len() {
if self.steps >= self.max_steps {
return Err("DWARF expression evaluation exceeded maximum steps".to_string());
}
self.steps += 1;
let op = self.expr_to_eval[self.pc];
self.pc += 1;
match op {
x if x >= dwarf_ops::DW_OP_lit0 as u8 && x <= dwarf_ops::DW_OP_lit0 as u8 + 31 => {
self.push((x - dwarf_ops::DW_OP_lit0 as u8) as i64);
}
o if o == dwarf_ops::DW_OP_addr as u8 => {
let addr = self.read_addr()?;
self.push(addr as i64);
}
o if o == dwarf_ops::DW_OP_const1u as u8 => {
let v = self.read_u8()?;
self.push(v as i64);
}
o if o == dwarf_ops::DW_OP_const1s as u8 => {
let v = self.read_u8()?;
self.push(v as i8 as i64);
}
o if o == dwarf_ops::DW_OP_const2u as u8 => {
let v = self.read_u16()?;
self.push(v as i64);
}
o if o == dwarf_ops::DW_OP_const2s as u8 => {
let v = self.read_u16()?;
self.push(v as i16 as i64);
}
o if o == dwarf_ops::DW_OP_const4u as u8 => {
let v = self.read_u32()?;
self.push(v as i64);
}
o if o == dwarf_ops::DW_OP_const4s as u8 => {
let v = self.read_u32()?;
self.push(v as i32 as i64);
}
o if o == dwarf_ops::DW_OP_const8u as u8 => {
let v = self.read_u64()?;
self.push(v as i64);
}
o if o == dwarf_ops::DW_OP_const8s as u8 => {
let v = self.read_u64()?;
self.push(v as i64);
}
o if o == dwarf_ops::DW_OP_constu as u8 => {
let v = self.read_uleb()?;
self.push(v as i64);
}
o if o == dwarf_ops::DW_OP_consts as u8 => {
let v = self.read_sleb()?;
self.push(v);
}
x if x >= dwarf_ops::DW_OP_reg0 as u8 && x <= dwarf_ops::DW_OP_reg0 as u8 + 31 => {
let dwarf_reg = (x - dwarf_ops::DW_OP_reg0 as u8) as u16;
if let Some(reader) = self.read_register {
if let Some(val) = reader(dwarf_reg) {
self.push(val as i64);
} else {
self.push(0);
}
} else {
self.push(dwarf_reg as i64); }
}
o if o == dwarf_ops::DW_OP_regx as u8 => {
let dwarf_reg = self.read_uleb()? as u16;
if let Some(reader) = self.read_register {
if let Some(val) = reader(dwarf_reg) {
self.push(val as i64);
} else {
self.push(0);
}
} else {
self.push(dwarf_reg as i64);
}
}
x if x >= dwarf_ops::DW_OP_breg0 as u8
&& x <= dwarf_ops::DW_OP_breg0 as u8 + 31 =>
{
let dwarf_reg = (x - dwarf_ops::DW_OP_breg0 as u8) as u16;
let offset = self.read_sleb()?;
let base = if let Some(reader) = self.read_register {
reader(dwarf_reg).unwrap_or(0) as i64
} else {
dwarf_reg as i64
};
self.push(base + offset);
}
o if o == dwarf_ops::DW_OP_bregx as u8 => {
let dwarf_reg = self.read_uleb()? as u16;
let offset = self.read_sleb()?;
let base = if let Some(reader) = self.read_register {
reader(dwarf_reg).unwrap_or(0) as i64
} else {
dwarf_reg as i64
};
self.push(base + offset);
}
o if o == dwarf_ops::DW_OP_fbreg as u8 => {
let offset = self.read_sleb()?;
let fb = self.frame_base.unwrap_or(0) as i64;
self.push(fb + offset);
}
o if o == dwarf_ops::DW_OP_dup as u8 => {
if let Some(&v) = self.stack.last() {
self.push(v);
}
}
o if o == dwarf_ops::DW_OP_drop as u8 => {
self.pop();
}
o if o == dwarf_ops::DW_OP_over as u8 => {
if self.stack.len() >= 2 {
let v = self.stack[self.stack.len() - 2];
self.push(v);
}
}
o if o == dwarf_ops::DW_OP_pick as u8 => {
let idx = self.read_u8()? as usize;
if idx < self.stack.len() {
let v = self.stack[self.stack.len() - 1 - idx];
self.push(v);
}
}
o if o == dwarf_ops::DW_OP_swap as u8 => {
let a = self.pop();
let b = self.pop();
if let (Some(a), Some(b)) = (a, b) {
self.push(a);
self.push(b);
}
}
o if o == dwarf_ops::DW_OP_rot as u8 => {
if self.stack.len() >= 3 {
let c = self.stack.pop().unwrap();
let b = self.stack.pop().unwrap();
let a = self.stack.pop().unwrap();
self.push(b);
self.push(c);
self.push(a);
}
}
o if o == dwarf_ops::DW_OP_plus as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(a + b);
}
o if o == dwarf_ops::DW_OP_minus as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(a - b);
}
o if o == dwarf_ops::DW_OP_mul as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(a * b);
}
o if o == dwarf_ops::DW_OP_div as u8 => {
let b = self.pop().unwrap_or(1);
let a = self.pop().unwrap_or(0);
self.push(a / b);
}
o if o == dwarf_ops::DW_OP_mod as u8 => {
let b = self.pop().unwrap_or(1);
let a = self.pop().unwrap_or(0);
self.push(a % b);
}
o if o == dwarf_ops::DW_OP_abs as u8 => {
let a = self.pop().unwrap_or(0);
self.push(a.abs());
}
o if o == dwarf_ops::DW_OP_and as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(a & b);
}
o if o == dwarf_ops::DW_OP_or as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(a | b);
}
o if o == dwarf_ops::DW_OP_xor as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(a ^ b);
}
o if o == dwarf_ops::DW_OP_not as u8 => {
let a = self.pop().unwrap_or(0);
self.push(!a);
}
o if o == dwarf_ops::DW_OP_neg as u8 => {
let a = self.pop().unwrap_or(0);
self.push(-a);
}
o if o == dwarf_ops::DW_OP_plus_uconst as u8 => {
let c = self.read_uleb()? as i64;
let a = self.pop().unwrap_or(0);
self.push(a + c);
}
o if o == dwarf_ops::DW_OP_shl as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(a << b);
}
o if o == dwarf_ops::DW_OP_shr as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(((a as u64) >> b) as i64);
}
o if o == dwarf_ops::DW_OP_shra as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(a >> b);
}
o if o == dwarf_ops::DW_OP_eq as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(if a == b { 1 } else { 0 });
}
o if o == dwarf_ops::DW_OP_ne as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(if a != b { 1 } else { 0 });
}
o if o == dwarf_ops::DW_OP_lt as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(if a < b { 1 } else { 0 });
}
o if o == dwarf_ops::DW_OP_le as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(if a <= b { 1 } else { 0 });
}
o if o == dwarf_ops::DW_OP_gt as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(if a > b { 1 } else { 0 });
}
o if o == dwarf_ops::DW_OP_ge as u8 => {
let b = self.pop().unwrap_or(0);
let a = self.pop().unwrap_or(0);
self.push(if a >= b { 1 } else { 0 });
}
o if o == dwarf_ops::DW_OP_deref as u8 => {
let addr = self.pop().unwrap_or(0) as u64;
if let Some(reader) = self.read_memory {
if let Some(data) = reader(addr, self.address_size as usize) {
let mut val: u64 = 0;
for (i, &b) in data.iter().enumerate() {
val |= (b as u64) << (i * 8);
}
self.push(val as i64);
} else {
self.push(0);
}
}
}
o if o == dwarf_ops::DW_OP_deref_size as u8 => {
let size = self.read_u8()? as usize;
let addr = self.pop().unwrap_or(0) as u64;
if let Some(reader) = self.read_memory {
if let Some(data) = reader(addr, size) {
let mut val: u64 = 0;
for (i, &b) in data.iter().enumerate() {
val |= (b as u64) << (i * 8);
}
self.push(val as i64);
} else {
self.push(0);
}
}
}
o if o == dwarf_ops::DW_OP_form_tls_address as u8 => {
let addr = self.pop().unwrap_or(0);
let tls = self.tls_base.unwrap_or(0) as i64;
self.push(addr + tls);
}
o if o == x86_dwarf_ops_ext::DW_OP_GNU_push_tls_address => {
let addr = self.pop().unwrap_or(0);
let tls = self.tls_base.unwrap_or(0) as i64;
self.push(addr + tls);
}
o if o == dwarf_ops::DW_OP_call_frame_cfa as u8 => {
let cfa = self.frame_base.unwrap_or(0) as i64;
self.push(cfa);
}
o if o == dwarf_ops::DW_OP_push_object_address as u8 => {
self.push(0);
}
o if o == dwarf_ops::DW_OP_skip as u8 => {
let offset = self.read_u16()? as i16;
let new_pc = self.pc as i32 + offset as i32;
if new_pc < 0 || new_pc > self.expr_to_eval.len() as i32 {
return Err(format!("DW_OP_skip out of bounds at PC {}", self.pc));
}
self.pc = new_pc as usize;
}
o if o == dwarf_ops::DW_OP_bra as u8 => {
let offset = self.read_u16()? as i16;
let cond = self.pop().unwrap_or(0);
if cond != 0 {
let new_pc = self.pc as i32 + offset as i32;
if new_pc < 0 || new_pc > self.expr_to_eval.len() as i32 {
return Err(format!("DW_OP_bra out of bounds at PC {}", self.pc));
}
self.pc = new_pc as usize;
}
}
o if o == dwarf_ops::DW_OP_nop as u8 => {}
o if o == dwarf_ops::DW_OP_piece as u8 => {
let _size = self.read_uleb()?;
}
o if o == dwarf_ops::DW_OP_bit_piece as u8 => {
let _bits = self.read_uleb()?;
let _offset = self.read_uleb()?;
}
o if o == dwarf_ops::DW_OP_stack_value as u8 => {
self.result_is_location = false;
}
o if o == dwarf_ops::DW_OP_implicit_value as u8 => {
let len = self.read_uleb()? as usize;
if self.pc + len > self.expr_to_eval.len() {
return Err(
"DW_OP_implicit_value data exceeds expression bounds".to_string()
);
}
let data = &self.expr_to_eval[self.pc..self.pc + len];
let mut val: u64 = 0;
for (i, &b) in data.iter().enumerate().take(8) {
val |= (b as u64) << (i * 8);
}
self.push(val as i64);
self.pc += len;
}
o if o == x86_dwarf_ops_ext::DW_OP_entry_value
|| o == x86_dwarf_ops_ext::DW_OP_GNU_entry_value =>
{
let inner_len = self.read_uleb()? as usize;
if self.pc + inner_len <= self.expr_to_eval.len() {
self.pc += inner_len;
}
self.push(0); }
_ => {
}
}
}
self.last_result = self.pop();
Ok(self.last_result)
}
pub fn compute_frame_base(&mut self, base_reg: u16, offset: i64) -> u64 {
if let Some(reader) = self.read_register {
reader(base_reg).unwrap_or(0).wrapping_add(offset as u64)
} else {
offset as u64
}
}
fn read_u8(&mut self) -> Result<u8, String> {
if self.pc >= self.expr_to_eval.len() {
return Err("Unexpected end of expression".to_string());
}
let v = self.expr_to_eval[self.pc];
self.pc += 1;
Ok(v)
}
fn read_u16(&mut self) -> Result<u16, String> {
if self.pc + 2 > self.expr_to_eval.len() {
return Err("Unexpected end of expression".to_string());
}
let v = u16::from_le_bytes([self.expr_to_eval[self.pc], self.expr_to_eval[self.pc + 1]]);
self.pc += 2;
Ok(v)
}
fn read_u32(&mut self) -> Result<u32, String> {
if self.pc + 4 > self.expr_to_eval.len() {
return Err("Unexpected end of expression".to_string());
}
let bytes: [u8; 4] = [
self.expr_to_eval[self.pc],
self.expr_to_eval[self.pc + 1],
self.expr_to_eval[self.pc + 2],
self.expr_to_eval[self.pc + 3],
];
self.pc += 4;
Ok(u32::from_le_bytes(bytes))
}
fn read_u64(&mut self) -> Result<u64, String> {
if self.pc + 8 > self.expr_to_eval.len() {
return Err("Unexpected end of expression".to_string());
}
let bytes: [u8; 8] = [
self.expr_to_eval[self.pc],
self.expr_to_eval[self.pc + 1],
self.expr_to_eval[self.pc + 2],
self.expr_to_eval[self.pc + 3],
self.expr_to_eval[self.pc + 4],
self.expr_to_eval[self.pc + 5],
self.expr_to_eval[self.pc + 6],
self.expr_to_eval[self.pc + 7],
];
self.pc += 8;
Ok(u64::from_le_bytes(bytes))
}
fn read_addr(&mut self) -> Result<u64, String> {
match self.address_size {
4 => Ok(self.read_u32()? as u64),
_ => self.read_u64(),
}
}
fn read_uleb(&mut self) -> Result<u64, String> {
let mut result: u64 = 0;
let mut shift: u32 = 0;
loop {
if self.pc >= self.expr_to_eval.len() {
return Err("Unexpected end of expression in ULEB128".to_string());
}
let byte = self.expr_to_eval[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: u32 = 0;
let mut byte: u8;
loop {
if self.pc >= self.expr_to_eval.len() {
return Err("Unexpected end of expression in SLEB128".to_string());
}
byte = self.expr_to_eval[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 |= !0 << shift;
}
Ok(result)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AbbrevDecl {
pub tag: u16,
pub has_children: bool,
pub attributes: Vec<(u16, u16)>,
}
#[derive(Debug, Clone)]
pub struct X86DwarfAbbrev {
pub decls: Vec<AbbrevDecl>,
decl_map: HashMap<AbbrevDecl, u64>,
}
impl X86DwarfAbbrev {
pub fn new() -> Self {
Self {
decls: Vec::new(),
decl_map: HashMap::new(),
}
}
pub fn add(&mut self, decl: AbbrevDecl) -> u64 {
if let Some(&code) = self.decl_map.get(&decl) {
return code;
}
let code = self.decls.len() as u64 + 1;
self.decl_map.insert(decl.clone(), code);
self.decls.push(decl);
code
}
pub fn add_compile_unit_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_compile_unit,
has_children: true,
attributes: vec![
(
dwarf_attributes::DW_AT_producer,
dwarf_forms::DW_FORM_string,
),
(dwarf_attributes::DW_AT_language, dwarf_forms::DW_FORM_data2),
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(
dwarf_attributes::DW_AT_comp_dir,
dwarf_forms::DW_FORM_string,
),
(dwarf_attributes::DW_AT_low_pc, dwarf_forms::DW_FORM_addr),
(dwarf_attributes::DW_AT_high_pc, dwarf_forms::DW_FORM_data8),
(
dwarf_attributes::DW_AT_stmt_list,
dwarf_forms::DW_FORM_sec_offset,
),
],
})
}
pub fn add_compile_unit_ranges_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_compile_unit,
has_children: true,
attributes: vec![
(
dwarf_attributes::DW_AT_producer,
dwarf_forms::DW_FORM_string,
),
(dwarf_attributes::DW_AT_language, dwarf_forms::DW_FORM_data2),
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(
dwarf_attributes::DW_AT_comp_dir,
dwarf_forms::DW_FORM_string,
),
(dwarf_attributes::DW_AT_low_pc, dwarf_forms::DW_FORM_addr),
(
x86_dwarf_attributes::DW_AT_ranges,
dwarf_forms::DW_FORM_sec_offset,
),
(
dwarf_attributes::DW_AT_stmt_list,
dwarf_forms::DW_FORM_sec_offset,
),
],
})
}
pub fn add_subprogram_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_subprogram,
has_children: true,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_low_pc, dwarf_forms::DW_FORM_addr),
(dwarf_attributes::DW_AT_high_pc, dwarf_forms::DW_FORM_data8),
(
dwarf_attributes::DW_AT_frame_base,
dwarf_forms::DW_FORM_exprloc,
),
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
],
})
}
pub fn add_subprogram_external_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_subprogram,
has_children: true,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_low_pc, dwarf_forms::DW_FORM_addr),
(dwarf_attributes::DW_AT_high_pc, dwarf_forms::DW_FORM_data8),
(
dwarf_attributes::DW_AT_frame_base,
dwarf_forms::DW_FORM_exprloc,
),
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
dwarf_attributes::DW_AT_external,
dwarf_forms::DW_FORM_flag_present,
),
],
})
}
pub fn add_subprogram_inlined_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_subprogram,
has_children: true,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_inline, dwarf_forms::DW_FORM_data1),
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
],
})
}
pub fn add_base_type_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_base_type,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_encoding, dwarf_forms::DW_FORM_data1),
(
dwarf_attributes::DW_AT_byte_size,
dwarf_forms::DW_FORM_data1,
),
],
})
}
pub fn add_pointer_type_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_pointer_type,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
dwarf_attributes::DW_AT_byte_size,
dwarf_forms::DW_FORM_data1,
),
],
})
}
pub fn add_modifier_type_abbrev(&mut self, tag: u16) -> u64 {
self.add(AbbrevDecl {
tag,
has_children: false,
attributes: vec![(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4)],
})
}
pub fn add_typedef_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_typedef,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
],
})
}
pub fn add_struct_type_abbrev(&mut self, tag: u16) -> u64 {
self.add(AbbrevDecl {
tag,
has_children: true,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(
dwarf_attributes::DW_AT_byte_size,
dwarf_forms::DW_FORM_data4,
),
],
})
}
pub fn add_struct_decl_abbrev(&mut self, tag: u16) -> u64 {
self.add(AbbrevDecl {
tag,
has_children: true,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(
dwarf_attributes::DW_AT_byte_size,
dwarf_forms::DW_FORM_data4,
),
(
dwarf_attributes::DW_AT_decl_file,
dwarf_forms::DW_FORM_data1,
),
(
dwarf_attributes::DW_AT_decl_line,
dwarf_forms::DW_FORM_data4,
),
],
})
}
pub fn add_member_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_member,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
dwarf_attributes::DW_AT_data_member_location,
dwarf_forms::DW_FORM_data4,
),
],
})
}
pub fn add_enum_type_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_enumeration_type,
has_children: true,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(
dwarf_attributes::DW_AT_byte_size,
dwarf_forms::DW_FORM_data1,
),
(dwarf_attributes::DW_AT_encoding, dwarf_forms::DW_FORM_data1),
],
})
}
pub fn add_enumerator_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: x86_dwarf_tags::DW_TAG_enumerator,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(
dwarf_attributes::DW_AT_const_value,
dwarf_forms::DW_FORM_sdata,
),
],
})
}
pub fn add_array_type_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_array_type,
has_children: true,
attributes: vec![(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4)],
})
}
pub fn add_subrange_type_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_subrange_type,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
x86_dwarf_attributes::DW_AT_upper_bound,
dwarf_forms::DW_FORM_data4,
),
],
})
}
pub fn add_subroutine_type_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_subroutine_type,
has_children: true,
attributes: vec![(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4)],
})
}
pub fn add_formal_parameter_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_formal_parameter,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
dwarf_attributes::DW_AT_location,
dwarf_forms::DW_FORM_exprloc,
),
],
})
}
pub fn add_variable_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_variable,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
dwarf_attributes::DW_AT_location,
dwarf_forms::DW_FORM_exprloc,
),
],
})
}
pub fn add_lexical_block_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_lexical_block,
has_children: true,
attributes: vec![
(dwarf_attributes::DW_AT_low_pc, dwarf_forms::DW_FORM_addr),
(dwarf_attributes::DW_AT_high_pc, dwarf_forms::DW_FORM_data8),
],
})
}
pub fn add_inlined_subroutine_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_inlined_subroutine,
has_children: true,
attributes: vec![
(
dwarf_attributes::DW_AT_abstract_origin,
dwarf_forms::DW_FORM_ref4,
),
(dwarf_attributes::DW_AT_low_pc, dwarf_forms::DW_FORM_addr),
(dwarf_attributes::DW_AT_high_pc, dwarf_forms::DW_FORM_data8),
(
dwarf_attributes::DW_AT_call_file,
dwarf_forms::DW_FORM_data1,
),
(
dwarf_attributes::DW_AT_call_line,
dwarf_forms::DW_FORM_data4,
),
],
})
}
pub fn add_inheritance_abbrev(&mut self) -> u64 {
self.add(AbbrevDecl {
tag: x86_dwarf_tags::DW_TAG_inheritance,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
dwarf_attributes::DW_AT_data_member_location,
dwarf_forms::DW_FORM_data4,
),
(
x86_dwarf_attributes::DW_AT_accessibility,
dwarf_forms::DW_FORM_data1,
),
],
})
}
pub fn add_reference_type_abbrev(&mut self, tag: u16) -> u64 {
self.add(AbbrevDecl {
tag,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
dwarf_attributes::DW_AT_byte_size,
dwarf_forms::DW_FORM_data1,
),
],
})
}
pub fn emit(&self) -> Vec<u8> {
let mut data = Vec::new();
for decl in &self.decls {
let code = self.decl_map.get(decl).copied().unwrap_or(0);
encode_uleb128(&mut data, code);
encode_uleb128(&mut data, decl.tag as u64);
data.push(if decl.has_children {
x86_dwarf_children::DW_CHILDREN_yes
} else {
x86_dwarf_children::DW_CHILDREN_no
});
for &(attr, form) in &decl.attributes {
encode_uleb128(&mut data, attr as u64);
encode_uleb128(&mut data, form as u64);
}
encode_uleb128(&mut data, 0);
encode_uleb128(&mut data, 0);
}
data.push(0);
data
}
pub fn len(&self) -> usize {
self.decls.len()
}
pub fn is_empty(&self) -> bool {
self.decls.is_empty()
}
}
impl Default for X86DwarfAbbrev {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct StringEntry {
text: String,
offset: u64,
}
#[derive(Debug, Clone)]
pub struct X86DwarfStringTable {
strings: Vec<StringEntry>,
string_map: HashMap<String, u64>,
total_size: u64,
}
impl X86DwarfStringTable {
pub fn new() -> Self {
let mut table = Self {
strings: Vec::new(),
string_map: HashMap::new(),
total_size: 1, };
table.string_map.insert(String::new(), 0);
table
}
pub fn add(&mut self, s: &str) -> u64 {
if let Some(&offset) = self.string_map.get(s) {
return offset;
}
let offset = self.total_size;
self.string_map.insert(s.to_string(), offset);
self.strings.push(StringEntry {
text: s.to_string(),
offset,
});
self.total_size += s.len() as u64 + 1; offset
}
pub fn get_offset(&self, s: &str) -> Option<u64> {
self.string_map.get(s).copied()
}
pub fn contains(&self, s: &str) -> bool {
self.string_map.contains_key(s)
}
pub fn emit(&self) -> Vec<u8> {
let mut data = vec![0u8];
for entry in &self.strings {
data.extend_from_slice(entry.text.as_bytes());
data.push(0); }
data
}
pub fn emit_str_offsets(&self, offset_size: u8) -> Vec<u8> {
let mut data = Vec::new();
let unit_length_offset = data.len();
data.extend_from_slice(&[0u8; 4]);
data.extend_from_slice(&2u16.to_le_bytes());
data.extend_from_slice(&0u16.to_le_bytes());
match offset_size {
4 => data.extend_from_slice(&0u32.to_le_bytes()),
_ => data.extend_from_slice(&0u64.to_le_bytes()),
}
for entry in &self.strings {
match offset_size {
4 => data.extend_from_slice(&(entry.offset as u32).to_le_bytes()),
_ => data.extend_from_slice(&entry.offset.to_le_bytes()),
}
}
let total_len = (data.len() - unit_length_offset - 4) as u32;
data[unit_length_offset..unit_length_offset + 4].copy_from_slice(&total_len.to_le_bytes());
data
}
pub fn size(&self) -> u64 {
self.total_size
}
pub fn len(&self) -> usize {
self.strings.len()
}
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
}
impl Default for X86DwarfStringTable {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct AddrEntry {
pub address: u64,
pub index: u64,
}
#[derive(Debug, Clone)]
pub struct X86DwarfAddrTable {
entries: Vec<AddrEntry>,
addr_map: HashMap<u64, u64>,
pub address_size: u8,
next_index: u64,
}
impl X86DwarfAddrTable {
pub fn new(address_size: u8) -> Self {
Self {
entries: Vec::new(),
addr_map: HashMap::new(),
address_size,
next_index: 0,
}
}
pub fn add(&mut self, addr: u64) -> u64 {
if let Some(&idx) = self.addr_map.get(&addr) {
return idx;
}
let index = self.next_index;
self.next_index += 1;
self.addr_map.insert(addr, index);
self.entries.push(AddrEntry {
address: addr,
index,
});
index
}
pub fn get_index(&self, addr: u64) -> Option<u64> {
self.addr_map.get(&addr).copied()
}
pub fn emit(&self) -> Vec<u8> {
let mut data = Vec::new();
let unit_length_offset = data.len();
data.extend_from_slice(&[0u8; 4]);
data.extend_from_slice(&2u16.to_le_bytes());
data.push(self.address_size);
data.push(0);
for entry in &self.entries {
match self.address_size {
4 => data.extend_from_slice(&(entry.address as u32).to_le_bytes()),
_ => data.extend_from_slice(&entry.address.to_le_bytes()),
}
}
let total_len = (data.len() - unit_length_offset - 4) as u32;
data[unit_length_offset..unit_length_offset + 4].copy_from_slice(&total_len.to_le_bytes());
data
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddrRange {
pub low_pc: u64,
pub high_pc: u64,
}
#[derive(Debug, Clone)]
pub struct RangeListEntry {
pub id: u64,
pub ranges: Vec<AddrRange>,
pub offset: u64,
}
#[derive(Debug, Clone)]
pub struct X86DwarfRangeList {
entries: Vec<RangeListEntry>,
total_size: u64,
pub use_dwarf5: bool,
pub address_size: u8,
}
impl X86DwarfRangeList {
pub fn new(address_size: u8, use_dwarf5: bool) -> Self {
Self {
entries: Vec::new(),
total_size: 0,
use_dwarf5,
address_size,
}
}
pub fn add(&mut self, ranges: Vec<AddrRange>) -> u64 {
let id = self.entries.len() as u64;
let offset = self.total_size;
let entry_size = if self.use_dwarf5 {
ranges.len() as u64 * (1 + 2 * self.address_size as u64) + 1
} else {
(ranges.len() as u64 * 2 + 2) * self.address_size as u64
};
self.entries.push(RangeListEntry { id, ranges, offset });
self.total_size += entry_size;
offset
}
pub fn emit_ranges_dwarf4(&self) -> Vec<u8> {
let mut data = Vec::new();
for entry in &self.entries {
for range in &entry.ranges {
match self.address_size {
4 => {
data.extend_from_slice(&(range.low_pc as u32).to_le_bytes());
data.extend_from_slice(&(range.high_pc as u32).to_le_bytes());
}
_ => {
data.extend_from_slice(&range.low_pc.to_le_bytes());
data.extend_from_slice(&range.high_pc.to_le_bytes());
}
}
}
match self.address_size {
4 => {
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
}
_ => {
data.extend_from_slice(&0u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
}
}
}
data
}
pub fn emit_rnglists_dwarf5(&self) -> Vec<u8> {
let mut data = Vec::new();
let unit_length_offset = data.len();
data.extend_from_slice(&[0u8; 4]);
data.extend_from_slice(&5u16.to_le_bytes());
data.push(self.address_size);
data.push(0);
data.extend_from_slice(&(self.entries.len() as u32).to_le_bytes());
for entry in &self.entries {
data.extend_from_slice(&(entry.offset as u32).to_le_bytes());
}
for entry in &self.entries {
for range in &entry.ranges {
data.push(0x04); match self.address_size {
4 => {
data.extend_from_slice(&(range.low_pc as u32).to_le_bytes());
data.extend_from_slice(&(range.high_pc as u32).to_le_bytes());
}
_ => {
data.extend_from_slice(&range.low_pc.to_le_bytes());
data.extend_from_slice(&range.high_pc.to_le_bytes());
}
}
}
data.push(0x00);
}
let total_len = (data.len() - unit_length_offset - 4) as u32;
data[unit_length_offset..unit_length_offset + 4].copy_from_slice(&total_len.to_le_bytes());
data
}
pub fn emit(&self) -> Vec<u8> {
if self.use_dwarf5 {
self.emit_rnglists_dwarf5()
} else {
self.emit_ranges_dwarf4()
}
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct ARangeEntry {
pub cu_offset: u64,
pub address: u64,
pub length: u64,
}
#[derive(Debug, Clone)]
pub struct X86DwarfARange {
entries: Vec<(u64, Vec<ARangeEntry>)>,
pub address_size: u8,
}
impl X86DwarfARange {
pub fn new(address_size: u8) -> Self {
Self {
entries: Vec::new(),
address_size,
}
}
pub fn add_cu(&mut self, cu_offset: u64, ranges: Vec<ARangeEntry>) {
self.entries.push((cu_offset, ranges));
}
pub fn emit(&self) -> Vec<u8> {
let mut data = Vec::new();
for (cu_offset, ranges) in &self.entries {
let header_start = data.len();
data.extend_from_slice(&[0u8; 4]);
data.extend_from_slice(&2u16.to_le_bytes());
match self.address_size {
4 => data.extend_from_slice(&(*cu_offset as u32).to_le_bytes()),
_ => data.extend_from_slice(&cu_offset.to_le_bytes()),
}
data.push(self.address_size);
data.push(0);
let header_end = data.len();
let alignment = 2 * self.address_size as usize;
let pad = (alignment - (header_end % alignment)) % alignment;
for _ in 0..pad {
data.push(0);
}
for entry in ranges {
match self.address_size {
4 => data.extend_from_slice(&(entry.address as u32).to_le_bytes()),
_ => data.extend_from_slice(&entry.address.to_le_bytes()),
}
match self.address_size {
4 => data.extend_from_slice(&(entry.length as u32).to_le_bytes()),
_ => data.extend_from_slice(&entry.length.to_le_bytes()),
}
}
match self.address_size {
4 => {
data.extend_from_slice(&0u32.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
}
_ => {
data.extend_from_slice(&0u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
}
}
let unit_len = (data.len() - header_start - 4) as u32;
data[header_start..header_start + 4].copy_from_slice(&unit_len.to_le_bytes());
}
data
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct TypeDIE {
pub tag: u16,
pub offset: u64,
pub attributes: Vec<(u16, Vec<u8>)>,
pub children: Vec<TypeDIE>,
pub abbrev_code: u64,
}
impl TypeDIE {
pub fn new(tag: u16) -> Self {
Self {
tag,
offset: 0,
attributes: Vec::new(),
children: Vec::new(),
abbrev_code: 0,
}
}
pub fn add_attr_bytes(&mut self, attr: u16, form: u16, data: Vec<u8>) {
self.attributes.push((attr, data));
let last = self.attributes.last_mut().unwrap();
let _ = form;
let _ = last;
}
pub fn add_child(&mut self, child: TypeDIE) {
self.children.push(child);
}
}
#[derive(Debug, Clone)]
pub struct X86DwarfTypeUnit {
pub types: Vec<TypeDIE>,
pub offsets: HashMap<String, u64>,
pub x86_types: HashMap<String, TypeDIE>,
pub abbrev: X86DwarfAbbrev,
pub address_size: u8,
pub section_offset: u64,
pub type_signature: u64,
}
impl X86DwarfTypeUnit {
pub fn new(address_size: u8) -> Self {
let mut abbrev = X86DwarfAbbrev::new();
abbrev.add_base_type_abbrev();
abbrev.add_pointer_type_abbrev();
abbrev.add_modifier_type_abbrev(dwarf_tags::DW_TAG_const_type);
abbrev.add_modifier_type_abbrev(dwarf_tags::DW_TAG_volatile_type);
abbrev.add_modifier_type_abbrev(x86_dwarf_tags::DW_TAG_restrict_type);
abbrev.add_typedef_abbrev();
abbrev.add_struct_type_abbrev(dwarf_tags::DW_TAG_structure_type);
abbrev.add_struct_type_abbrev(x86_dwarf_tags::DW_TAG_union_type);
abbrev.add_struct_decl_abbrev(x86_dwarf_tags::DW_TAG_class_type);
abbrev.add_member_abbrev();
abbrev.add_enum_type_abbrev();
abbrev.add_enumerator_abbrev();
abbrev.add_array_type_abbrev();
abbrev.add_subrange_type_abbrev();
abbrev.add_subroutine_type_abbrev();
abbrev.add_formal_parameter_abbrev();
abbrev.add_reference_type_abbrev(x86_dwarf_tags::DW_TAG_reference_type);
abbrev.add_reference_type_abbrev(x86_dwarf_tags::DW_TAG_rvalue_reference_type);
abbrev.add_inheritance_abbrev();
Self {
types: Vec::new(),
offsets: HashMap::new(),
x86_types: HashMap::new(),
abbrev,
address_size,
section_offset: 0,
type_signature: 0,
}
}
pub fn build_base_type(name: &str, encoding: u8, byte_size: u8) -> TypeDIE {
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_base_type);
die.attributes
.push((dwarf_attributes::DW_AT_name, name.as_bytes().to_vec()));
die.attributes
.push((dwarf_attributes::DW_AT_encoding, vec![encoding]));
die.attributes
.push((dwarf_attributes::DW_AT_byte_size, vec![byte_size]));
die
}
pub fn register_standard_base_types(&mut self) {
let base_types = vec![
("int", x86_dwarf_encodings::DW_ATE_signed, 4u8),
("unsigned int", x86_dwarf_encodings::DW_ATE_unsigned, 4),
("short", x86_dwarf_encodings::DW_ATE_signed, 2),
("unsigned short", x86_dwarf_encodings::DW_ATE_unsigned, 2),
("char", x86_dwarf_encodings::DW_ATE_signed_char, 1),
("signed char", x86_dwarf_encodings::DW_ATE_signed_char, 1),
(
"unsigned char",
x86_dwarf_encodings::DW_ATE_unsigned_char,
1,
),
("long", x86_dwarf_encodings::DW_ATE_signed, 8),
("long long", x86_dwarf_encodings::DW_ATE_signed, 8),
("unsigned long", x86_dwarf_encodings::DW_ATE_unsigned, 8),
(
"unsigned long long",
x86_dwarf_encodings::DW_ATE_unsigned,
8,
),
("float", x86_dwarf_encodings::DW_ATE_float, 4),
("double", x86_dwarf_encodings::DW_ATE_float, 8),
("long double", x86_dwarf_encodings::DW_ATE_float, 16),
("bool", x86_dwarf_encodings::DW_ATE_boolean, 1),
("_Bool", x86_dwarf_encodings::DW_ATE_boolean, 1),
("void", x86_dwarf_encodings::DW_ATE_address, 0),
("wchar_t", x86_dwarf_encodings::DW_ATE_signed, 4),
];
for (name, encoding, byte_size) in base_types {
let die = Self::build_base_type(name, encoding, byte_size);
self.register_type(name, die);
}
}
pub fn register_x86_specific_types(&mut self) {
let x86_types = vec![
(
"__int128",
Self::build_base_type("__int128", x86_dwarf_encodings::DW_ATE_signed, 16),
),
(
"unsigned __int128",
Self::build_base_type(
"unsigned __int128",
x86_dwarf_encodings::DW_ATE_unsigned,
16,
),
),
(
"__float80",
Self::build_base_type("__float80", x86_dwarf_encodings::DW_ATE_float, 10),
),
(
"__float128",
Self::build_base_type("__float128", x86_dwarf_encodings::DW_ATE_float, 16),
),
(
"__m64",
Self::build_base_type("__m64", x86_dwarf_encodings::DW_ATE_unsigned, 8),
),
(
"__m128",
Self::build_base_type("__m128", x86_dwarf_encodings::DW_ATE_unsigned, 16),
),
(
"__m128d",
Self::build_base_type("__m128d", x86_dwarf_encodings::DW_ATE_float, 16),
),
(
"__m128i",
Self::build_base_type("__m128i", x86_dwarf_encodings::DW_ATE_signed, 16),
),
(
"__m256",
Self::build_base_type("__m256", x86_dwarf_encodings::DW_ATE_unsigned, 32),
),
(
"__m256d",
Self::build_base_type("__m256d", x86_dwarf_encodings::DW_ATE_float, 32),
),
(
"__m256i",
Self::build_base_type("__m256i", x86_dwarf_encodings::DW_ATE_signed, 32),
),
(
"__m512",
Self::build_base_type("__m512", x86_dwarf_encodings::DW_ATE_unsigned, 64),
),
(
"__m512d",
Self::build_base_type("__m512d", x86_dwarf_encodings::DW_ATE_float, 64),
),
(
"__m512i",
Self::build_base_type("__m512i", x86_dwarf_encodings::DW_ATE_signed, 64),
),
(
"__m128bh",
Self::build_base_type("__m128bh", x86_dwarf_encodings::DW_ATE_float, 16),
),
(
"__m256bh",
Self::build_base_type("__m256bh", x86_dwarf_encodings::DW_ATE_float, 32),
),
(
"__m512bh",
Self::build_base_type("__m512bh", x86_dwarf_encodings::DW_ATE_float, 64),
),
];
for (name, die) in x86_types {
self.register_type(name, die);
}
}
pub fn build_pointer_type(
&mut self,
target_type_name: &str,
ptr_name: &str,
) -> Option<TypeDIE> {
let target_offset = self.offsets.get(target_type_name).copied()?;
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_pointer_type);
die.attributes.push((
dwarf_attributes::DW_AT_type,
(target_offset as u32).to_le_bytes().to_vec(),
));
die.attributes
.push((dwarf_attributes::DW_AT_byte_size, vec![self.address_size]));
self.register_type(ptr_name, die.clone());
Some(die)
}
pub fn build_reference_type(
&mut self,
target_type_name: &str,
ref_name: &str,
) -> Option<TypeDIE> {
let target_offset = self.offsets.get(target_type_name).copied()?;
let mut die = TypeDIE::new(x86_dwarf_tags::DW_TAG_reference_type);
die.attributes.push((
dwarf_attributes::DW_AT_type,
(target_offset as u32).to_le_bytes().to_vec(),
));
die.attributes
.push((dwarf_attributes::DW_AT_byte_size, vec![self.address_size]));
self.register_type(ref_name, die.clone());
Some(die)
}
pub fn build_rvalue_reference_type(
&mut self,
target_type_name: &str,
ref_name: &str,
) -> Option<TypeDIE> {
let target_offset = self.offsets.get(target_type_name).copied()?;
let mut die = TypeDIE::new(x86_dwarf_tags::DW_TAG_rvalue_reference_type);
die.attributes.push((
dwarf_attributes::DW_AT_type,
(target_offset as u32).to_le_bytes().to_vec(),
));
die.attributes
.push((dwarf_attributes::DW_AT_byte_size, vec![self.address_size]));
self.register_type(ref_name, die.clone());
Some(die)
}
pub fn build_const_type(
&mut self,
target_type_name: &str,
const_name: &str,
) -> Option<TypeDIE> {
let target_offset = self.offsets.get(target_type_name).copied()?;
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_const_type);
die.attributes.push((
dwarf_attributes::DW_AT_type,
(target_offset as u32).to_le_bytes().to_vec(),
));
self.register_type(const_name, die.clone());
Some(die)
}
pub fn build_volatile_type(
&mut self,
target_type_name: &str,
vol_name: &str,
) -> Option<TypeDIE> {
let target_offset = self.offsets.get(target_type_name).copied()?;
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_volatile_type);
die.attributes.push((
dwarf_attributes::DW_AT_type,
(target_offset as u32).to_le_bytes().to_vec(),
));
self.register_type(vol_name, die.clone());
Some(die)
}
pub fn build_restrict_type(
&mut self,
target_type_name: &str,
rest_name: &str,
) -> Option<TypeDIE> {
let target_offset = self.offsets.get(target_type_name).copied()?;
let mut die = TypeDIE::new(x86_dwarf_tags::DW_TAG_restrict_type);
die.attributes.push((
dwarf_attributes::DW_AT_type,
(target_offset as u32).to_le_bytes().to_vec(),
));
self.register_type(rest_name, die.clone());
Some(die)
}
pub fn build_typedef(&mut self, name: &str, target_type_name: &str) -> Option<TypeDIE> {
let target_offset = self.offsets.get(target_type_name).copied()?;
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_typedef);
die.attributes
.push((dwarf_attributes::DW_AT_name, name.as_bytes().to_vec()));
die.attributes.push((
dwarf_attributes::DW_AT_type,
(target_offset as u32).to_le_bytes().to_vec(),
));
self.register_type(name, die.clone());
Some(die)
}
pub fn build_array_type(
&mut self,
element_type_name: &str,
array_name: &str,
element_count: u32,
) -> Option<TypeDIE> {
let element_offset = self.offsets.get(element_type_name).copied()?;
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_array_type);
die.attributes.push((
dwarf_attributes::DW_AT_type,
(element_offset as u32).to_le_bytes().to_vec(),
));
let mut subrange = TypeDIE::new(dwarf_tags::DW_TAG_subrange_type);
subrange.attributes.push((
dwarf_attributes::DW_AT_type,
(self.offsets.get("unsigned long").copied().unwrap_or(0) as u32)
.to_le_bytes()
.to_vec(),
));
subrange.attributes.push((
x86_dwarf_attributes::DW_AT_upper_bound,
(element_count.saturating_sub(1)).to_le_bytes().to_vec(),
));
die.children.push(subrange);
self.register_type(array_name, die.clone());
Some(die)
}
pub fn build_struct_type(
&mut self,
name: &str,
byte_size: u32,
members: Vec<(&str, &str, u32)>, ) -> Option<TypeDIE> {
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_structure_type);
die.attributes
.push((dwarf_attributes::DW_AT_name, name.as_bytes().to_vec()));
die.attributes.push((
dwarf_attributes::DW_AT_byte_size,
byte_size.to_le_bytes().to_vec(),
));
for (member_name, type_name, offset) in members {
if let Some(type_offset) = self.offsets.get(type_name).copied() {
let mut member = TypeDIE::new(dwarf_tags::DW_TAG_member);
member.attributes.push((
dwarf_attributes::DW_AT_name,
member_name.as_bytes().to_vec(),
));
member.attributes.push((
dwarf_attributes::DW_AT_type,
(type_offset as u32).to_le_bytes().to_vec(),
));
member.attributes.push((
dwarf_attributes::DW_AT_data_member_location,
offset.to_le_bytes().to_vec(),
));
die.children.push(member);
}
}
self.register_type(name, die.clone());
Some(die)
}
pub fn build_union_type(
&mut self,
name: &str,
byte_size: u32,
members: Vec<(&str, &str)>, ) -> Option<TypeDIE> {
let mut die = TypeDIE::new(x86_dwarf_tags::DW_TAG_union_type);
die.attributes
.push((dwarf_attributes::DW_AT_name, name.as_bytes().to_vec()));
die.attributes.push((
dwarf_attributes::DW_AT_byte_size,
byte_size.to_le_bytes().to_vec(),
));
for (member_name, type_name) in members {
if let Some(type_offset) = self.offsets.get(type_name).copied() {
let mut member = TypeDIE::new(dwarf_tags::DW_TAG_member);
member.attributes.push((
dwarf_attributes::DW_AT_name,
member_name.as_bytes().to_vec(),
));
member.attributes.push((
dwarf_attributes::DW_AT_type,
(type_offset as u32).to_le_bytes().to_vec(),
));
member.attributes.push((
dwarf_attributes::DW_AT_data_member_location,
0u32.to_le_bytes().to_vec(),
));
die.children.push(member);
}
}
self.register_type(name, die.clone());
Some(die)
}
pub fn build_class_type(
&mut self,
name: &str,
byte_size: u32,
base_classes: Vec<(&str, u32, u8)>, members: Vec<(&str, &str, u32)>, ) -> Option<TypeDIE> {
let mut die = TypeDIE::new(x86_dwarf_tags::DW_TAG_class_type);
die.attributes
.push((dwarf_attributes::DW_AT_name, name.as_bytes().to_vec()));
die.attributes.push((
dwarf_attributes::DW_AT_byte_size,
byte_size.to_le_bytes().to_vec(),
));
for (base_name, offset, accessibility) in base_classes {
if let Some(base_offset) = self.offsets.get(base_name).copied() {
let mut inherit = TypeDIE::new(x86_dwarf_tags::DW_TAG_inheritance);
inherit.attributes.push((
dwarf_attributes::DW_AT_type,
(base_offset as u32).to_le_bytes().to_vec(),
));
inherit.attributes.push((
dwarf_attributes::DW_AT_data_member_location,
offset.to_le_bytes().to_vec(),
));
inherit.attributes.push((
x86_dwarf_attributes::DW_AT_accessibility,
vec![accessibility],
));
die.children.push(inherit);
}
}
for (member_name, type_name, offset) in members {
if let Some(type_offset) = self.offsets.get(type_name).copied() {
let mut member = TypeDIE::new(dwarf_tags::DW_TAG_member);
member.attributes.push((
dwarf_attributes::DW_AT_name,
member_name.as_bytes().to_vec(),
));
member.attributes.push((
dwarf_attributes::DW_AT_type,
(type_offset as u32).to_le_bytes().to_vec(),
));
member.attributes.push((
dwarf_attributes::DW_AT_data_member_location,
offset.to_le_bytes().to_vec(),
));
die.children.push(member);
}
}
self.register_type(name, die.clone());
Some(die)
}
pub fn build_enum_type(
&mut self,
name: &str,
byte_size: u8,
enumerators: Vec<(&str, i64)>, ) -> Option<TypeDIE> {
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_enumeration_type);
die.attributes
.push((dwarf_attributes::DW_AT_name, name.as_bytes().to_vec()));
die.attributes
.push((dwarf_attributes::DW_AT_byte_size, vec![byte_size]));
die.attributes.push((
dwarf_attributes::DW_AT_encoding,
vec![x86_dwarf_encodings::DW_ATE_signed],
));
for (enum_name, value) in enumerators {
let mut enumerator = TypeDIE::new(x86_dwarf_tags::DW_TAG_enumerator);
enumerator
.attributes
.push((dwarf_attributes::DW_AT_name, enum_name.as_bytes().to_vec()));
let mut sleb = Vec::new();
encode_sleb128(&mut sleb, value);
enumerator
.attributes
.push((dwarf_attributes::DW_AT_const_value, sleb));
die.children.push(enumerator);
}
self.register_type(name, die.clone());
Some(die)
}
pub fn build_subroutine_type(
&mut self,
name: &str,
return_type_name: &str,
param_types: Vec<&str>, ) -> Option<TypeDIE> {
let return_offset = self.offsets.get(return_type_name).copied()?;
let mut die = TypeDIE::new(dwarf_tags::DW_TAG_subroutine_type);
die.attributes.push((
dwarf_attributes::DW_AT_type,
(return_offset as u32).to_le_bytes().to_vec(),
));
for param_name in param_types {
if let Some(param_offset) = self.offsets.get(param_name).copied() {
let mut param = TypeDIE::new(dwarf_tags::DW_TAG_formal_parameter);
param.attributes.push((
dwarf_attributes::DW_AT_type,
(param_offset as u32).to_le_bytes().to_vec(),
));
die.children.push(param);
}
}
self.register_type(name, die.clone());
Some(die)
}
pub fn register_type(&mut self, name: &str, die: TypeDIE) {
let offset = if self.types.is_empty() {
self.section_offset
} else {
self.section_offset
+ self.types.len() as u64 * 16
+ self
.types
.iter()
.map(|t| {
t.attributes
.iter()
.map(|(_, d)| d.len() as u64 + 2)
.sum::<u64>()
+ t.children
.iter()
.map(|c| {
c.attributes
.iter()
.map(|(_, d)| d.len() as u64 + 2)
.sum::<u64>()
})
.sum::<u64>()
})
.sum::<u64>()
};
self.offsets.insert(name.to_string(), offset);
self.types.push(die);
self.x86_types
.insert(name.to_string(), self.types.last().unwrap().clone());
}
pub fn get_type_offset(&self, name: &str) -> Option<u64> {
self.offsets.get(name).copied()
}
pub fn get_type_die(&self, name: &str) -> Option<&TypeDIE> {
self.x86_types.get(name)
}
}
#[derive(Debug, Clone)]
pub struct X86LineFileEntry {
pub name: String,
pub directory_index: u64,
pub mod_time: u64,
pub length: u64,
}
#[derive(Debug, Clone)]
pub struct X86LineEntry {
pub line: u32,
pub column: u32,
pub file: u32,
pub address: u64,
pub is_stmt: bool,
pub basic_block: bool,
pub prologue_end: bool,
pub epilogue_begin: bool,
pub isa: u32,
pub discriminator: u32,
pub end_sequence: bool,
}
impl Default for X86LineEntry {
fn default() -> Self {
Self {
line: 1,
column: 0,
file: 1,
address: 0,
is_stmt: false,
basic_block: false,
prologue_end: false,
epilogue_begin: false,
isa: 0,
discriminator: 0,
end_sequence: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86DwarfLineTable {
pub comp_dir: String,
pub files: Vec<X86LineFileEntry>,
pub directories: Vec<String>,
pub entries: Vec<X86LineEntry>,
pub version: u16,
pub address_size: u8,
pub min_insn_length: u8,
pub max_ops_per_insn: u8,
pub default_is_stmt: u8,
pub line_base: i8,
pub line_range: u8,
pub opcode_base: u8,
pub standard_opcode_lengths: Vec<u8>,
}
impl X86DwarfLineTable {
pub fn new_x86_64() -> Self {
Self {
comp_dir: String::new(),
files: Vec::new(),
directories: Vec::new(),
entries: Vec::new(),
version: 4,
address_size: 8,
min_insn_length: 1,
max_ops_per_insn: 1,
default_is_stmt: 1,
line_base: -5,
line_range: 14,
opcode_base: 13,
standard_opcode_lengths: vec![0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1],
}
}
pub fn new_x86_32() -> Self {
let mut table = Self::new_x86_64();
table.address_size = 4;
table
}
pub fn set_comp_dir(&mut self, dir: &str) {
self.comp_dir = dir.to_string();
if self.directories.is_empty() {
self.directories.push(dir.to_string());
}
}
pub fn add_file(&mut self, name: &str, directory_index: u64) {
self.files.push(X86LineFileEntry {
name: name.to_string(),
directory_index,
mod_time: 0,
length: 0,
});
}
pub fn add_directory(&mut self, dir: &str) -> u64 {
let idx = self.directories.len() as u64;
self.directories.push(dir.to_string());
idx
}
pub fn add_entry(&mut self, entry: X86LineEntry) {
self.entries.push(entry);
}
pub fn add_line(&mut self, address: u64, file: u32, line: u32, column: u32) {
self.entries.push(X86LineEntry {
line,
column,
file,
address,
is_stmt: true,
basic_block: false,
prologue_end: false,
epilogue_begin: false,
isa: 0,
discriminator: 0,
end_sequence: false,
});
}
pub fn add_end_sequence(&mut self, address: u64) {
self.entries.push(X86LineEntry {
line: 1,
column: 0,
file: 1,
address,
is_stmt: false,
basic_block: false,
prologue_end: false,
epilogue_begin: false,
isa: 0,
discriminator: 0,
end_sequence: true,
});
}
pub fn add_prologue_end(&mut self, address: u64, file: u32, line: u32) {
self.entries.push(X86LineEntry {
line,
column: 0,
file,
address,
is_stmt: true,
basic_block: false,
prologue_end: true,
epilogue_begin: false,
isa: 0,
discriminator: 0,
end_sequence: false,
});
}
pub fn add_epilogue_begin(&mut self, address: u64, file: u32, line: u32) {
self.entries.push(X86LineEntry {
line,
column: 0,
file,
address,
is_stmt: true,
basic_block: false,
prologue_end: false,
epilogue_begin: true,
isa: 0,
discriminator: 0,
end_sequence: false,
});
}
pub fn emit(&self) -> Vec<u8> {
let mut data = Vec::new();
let header_start = data.len();
data.extend_from_slice(&[0u8; 4]);
data.extend_from_slice(&self.version.to_le_bytes());
let header_length_offset = data.len();
data.extend_from_slice(&[0u8; 4]);
data.push(self.min_insn_length);
if self.version >= 4 {
data.push(self.max_ops_per_insn);
}
data.push(self.default_is_stmt);
data.push(self.line_base as u8);
data.push(self.line_range);
data.push(self.opcode_base);
for &len in &self.standard_opcode_lengths {
data.push(len);
}
data.push(0); for dir in &self.directories {
data.extend_from_slice(dir.as_bytes());
data.push(0);
}
data.push(0);
for file in &self.files {
data.extend_from_slice(file.name.as_bytes());
data.push(0); encode_uleb128(&mut data, file.directory_index);
encode_uleb128(&mut data, file.mod_time);
encode_uleb128(&mut data, file.length);
}
data.push(0);
let header_end = data.len();
let header_len = (header_end - header_length_offset - 4) as u32;
data[header_length_offset..header_length_offset + 4]
.copy_from_slice(&header_len.to_le_bytes());
if self.entries.is_empty() {
let total_len = (data.len() - header_start - 4) as u32;
data[header_start..header_start + 4].copy_from_slice(&total_len.to_le_bytes());
return data;
}
let mut current_address: u64 = 0;
let mut current_file: u32 = 1;
let mut current_line: u32 = 1;
let mut current_column: u32 = 0;
let mut current_is_stmt: bool = self.default_is_stmt != 0;
let mut current_basic_block: bool = false;
let mut current_prologue_end: bool = false;
let mut current_epilogue_begin: bool = false;
let mut current_isa: u32 = 0;
let mut current_discriminator: u32 = 0;
for entry in &self.entries {
if entry.end_sequence {
data.push(0); encode_uleb128(&mut data, 1); data.push(0x01); current_address = 0;
current_file = 1;
current_line = 1;
current_column = 0;
current_is_stmt = self.default_is_stmt != 0;
current_basic_block = false;
current_prologue_end = false;
current_epilogue_begin = false;
current_isa = 0;
current_discriminator = 0;
continue;
}
if entry.address != current_address {
data.push(0); encode_uleb128(&mut data, 1 + self.address_size as u64); data.push(0x02); match self.address_size {
4 => data.extend_from_slice(&(entry.address as u32).to_le_bytes()),
_ => data.extend_from_slice(&entry.address.to_le_bytes()),
}
current_address = entry.address;
}
if entry.file != current_file {
data.push(dwarf_line_std_opcodes::DW_LNS_set_file);
encode_uleb128(&mut data, entry.file as u64);
current_file = entry.file;
}
if entry.column != current_column {
data.push(dwarf_line_std_opcodes::DW_LNS_set_column);
encode_uleb128(&mut data, entry.column as u64);
current_column = entry.column;
}
if entry.basic_block && !current_basic_block {
data.push(dwarf_line_std_opcodes::DW_LNS_set_basic_block);
current_basic_block = true;
}
if entry.prologue_end && !current_prologue_end {
data.push(dwarf_line_std_opcodes::DW_LNS_set_prologue_end);
current_prologue_end = true;
}
if entry.epilogue_begin && !current_epilogue_begin {
data.push(dwarf_line_std_opcodes::DW_LNS_set_epilogue_begin);
current_epilogue_begin = true;
}
if entry.isa != current_isa {
data.push(dwarf_line_std_opcodes::DW_LNS_set_isa);
encode_uleb128(&mut data, entry.isa as u64);
current_isa = entry.isa;
}
if entry.discriminator != current_discriminator {
data.push(0); let disc_bytes = uleb128(entry.discriminator as u64);
encode_uleb128(&mut data, 1 + disc_bytes.len() as u64);
data.push(0x04); data.extend_from_slice(&disc_bytes);
current_discriminator = entry.discriminator;
}
if entry.is_stmt != current_is_stmt {
data.push(if entry.is_stmt {
dwarf_line_std_opcodes::DW_LNS_set_is_stmt
} else {
dwarf_line_std_opcodes::DW_LNS_negate_stmt
});
current_is_stmt = entry.is_stmt;
}
let line_delta: i32 = entry.line as i32 - current_line as i32;
let adjusted = line_delta - self.line_base as i32;
if adjusted >= 0 && adjusted < self.line_range as i32 {
let special_op = self.opcode_base + adjusted as u8;
data.push(special_op);
current_line = entry.line;
} else {
data.push(dwarf_line_std_opcodes::DW_LNS_advance_line);
encode_sleb128(&mut data, line_delta as i64);
current_line = entry.line;
let adj2 = 0i32 - self.line_base as i32;
if adj2 >= 0 && adj2 < self.line_range as i32 {
data.push(self.opcode_base + adj2 as u8);
}
}
current_basic_block = false;
current_prologue_end = false;
current_epilogue_begin = false;
}
let total_len = (data.len() - header_start - 4) as u32;
data[header_start..header_start + 4].copy_from_slice(&total_len.to_le_bytes());
data
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
mod dwarf_line_std_opcodes {
pub const DW_LNS_copy: u8 = 0x01;
pub const DW_LNS_advance_pc: u8 = 0x02;
pub const DW_LNS_advance_line: u8 = 0x03;
pub const DW_LNS_set_file: u8 = 0x04;
pub const DW_LNS_set_column: u8 = 0x05;
pub const DW_LNS_negate_stmt: u8 = 0x06;
pub const DW_LNS_set_basic_block: u8 = 0x07;
pub const DW_LNS_const_add_pc: u8 = 0x08;
pub const DW_LNS_fixed_advance_pc: u8 = 0x09;
pub const DW_LNS_set_prologue_end: u8 = 0x0a;
pub const DW_LNS_set_epilogue_begin: u8 = 0x0b;
pub const DW_LNS_set_isa: u8 = 0x0c;
pub const DW_LNS_set_is_stmt: u8 = 0x0d; }
#[derive(Debug, Clone)]
pub struct X86DwarfCompileUnit {
pub comp_dir: String,
pub file_name: String,
pub producer: String,
pub language: u16,
pub address_size: u8,
pub dwarf_version: u16,
pub line_table: X86DwarfLineTable,
pub subprograms: Vec<X86DwarfSubprogram>,
pub global_variables: Vec<X86DwarfVariable>,
pub type_unit: X86DwarfTypeUnit,
pub abbrev: X86DwarfAbbrev,
pub string_table: X86DwarfStringTable,
pub section_offset: u64,
pub low_pc: u64,
pub high_pc: u64,
pub use_dwarf5_line: bool,
}
impl X86DwarfCompileUnit {
pub fn new_x86_64(file_name: &str, comp_dir: &str, producer: &str, language: u16) -> Self {
let mut abbrev = X86DwarfAbbrev::new();
abbrev.add_compile_unit_abbrev();
abbrev.add_subprogram_abbrev();
abbrev.add_variable_abbrev();
abbrev.add_lexical_block_abbrev();
abbrev.add_inlined_subroutine_abbrev();
Self {
comp_dir: comp_dir.to_string(),
file_name: file_name.to_string(),
producer: producer.to_string(),
language,
address_size: 8,
dwarf_version: 4,
line_table: X86DwarfLineTable::new_x86_64(),
subprograms: Vec::new(),
global_variables: Vec::new(),
type_unit: X86DwarfTypeUnit::new(8),
abbrev,
string_table: X86DwarfStringTable::new(),
section_offset: 0,
low_pc: 0,
high_pc: 0,
use_dwarf5_line: false,
}
}
pub fn new_x86_32(file_name: &str, comp_dir: &str, producer: &str, language: u16) -> Self {
let mut cu = Self::new_x86_64(file_name, comp_dir, producer, language);
cu.address_size = 4;
cu.line_table = X86DwarfLineTable::new_x86_32();
cu.type_unit = X86DwarfTypeUnit::new(4);
cu
}
pub fn set_line_table(&mut self, table: X86DwarfLineTable) {
self.line_table = table;
}
pub fn add_subprogram(&mut self, sp: X86DwarfSubprogram) {
self.subprograms.push(sp);
}
pub fn add_global_variable(&mut self, var: X86DwarfVariable) {
self.global_variables.push(var);
}
pub fn emit_die_header(&self) -> Vec<u8> {
let mut data = Vec::new();
let abbrev_code = 1u64;
encode_uleb128(&mut data, abbrev_code);
data.extend_from_slice(self.producer.as_bytes());
data.push(0);
data.extend_from_slice(&self.language.to_le_bytes());
data.extend_from_slice(self.file_name.as_bytes());
data.push(0);
data.extend_from_slice(self.comp_dir.as_bytes());
data.push(0);
match self.address_size {
4 => data.extend_from_slice(&(self.low_pc as u32).to_le_bytes()),
_ => data.extend_from_slice(&self.low_pc.to_le_bytes()),
}
data.extend_from_slice(&self.high_pc.to_le_bytes());
match self.address_size {
4 => data.extend_from_slice(&0u32.to_le_bytes()),
_ => data.extend_from_slice(&0u64.to_le_bytes()),
}
data
}
pub fn compute_line_offset(&self, prefix_size: u64) -> u64 {
prefix_size }
}
#[derive(Debug, Clone)]
pub struct X86DwarfVariable {
pub name: String,
pub tag: u16,
pub type_name: String,
pub location: Vec<u8>,
pub decl_file: u32,
pub decl_line: u32,
pub decl_column: u32,
pub is_external: bool,
pub is_artificial: bool,
}
impl X86DwarfVariable {
pub fn new(name: &str, type_name: &str, location: Vec<u8>) -> Self {
Self {
name: name.to_string(),
tag: dwarf_tags::DW_TAG_variable,
type_name: type_name.to_string(),
location,
decl_file: 0,
decl_line: 0,
decl_column: 0,
is_external: false,
is_artificial: false,
}
}
pub fn new_parameter(name: &str, type_name: &str, location: Vec<u8>) -> Self {
Self {
name: name.to_string(),
tag: dwarf_tags::DW_TAG_formal_parameter,
type_name: type_name.to_string(),
location,
decl_file: 0,
decl_line: 0,
decl_column: 0,
is_external: false,
is_artificial: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86DwarfLexicalBlock {
pub low_pc: u64,
pub high_pc: u64,
pub variables: Vec<X86DwarfVariable>,
}
#[derive(Debug, Clone)]
pub struct X86DwarfSubprogram {
pub name: String,
pub linkage_name: Option<String>,
pub low_pc: u64,
pub high_pc: u64,
pub frame_base: Option<Vec<u8>>,
pub return_type: Option<String>,
pub parameters: Vec<X86DwarfVariable>,
pub variables: Vec<X86DwarfVariable>,
pub lexical_blocks: Vec<X86DwarfLexicalBlock>,
pub inlined_subroutines: Vec<X86DwarfInlinedSubroutine>,
pub is_external: bool,
pub inline_code: Option<u8>,
pub decl_file: u32,
pub decl_line: u32,
pub decl_column: u32,
pub is_artificial: bool,
pub is_declaration: bool,
pub abstract_origin: Option<u64>,
}
impl X86DwarfSubprogram {
pub fn new(name: &str, low_pc: u64, high_pc: u64) -> Self {
Self {
name: name.to_string(),
linkage_name: None,
low_pc,
high_pc,
frame_base: None,
return_type: None,
parameters: Vec::new(),
variables: Vec::new(),
lexical_blocks: Vec::new(),
inlined_subroutines: Vec::new(),
is_external: true,
inline_code: None,
decl_file: 1,
decl_line: 0,
decl_column: 0,
is_artificial: false,
is_declaration: false,
abstract_origin: None,
}
}
pub fn set_frame_base(&mut self, base_expr: Vec<u8>) {
self.frame_base = Some(base_expr);
}
pub fn set_return_type(&mut self, type_name: &str) {
self.return_type = Some(type_name.to_string());
}
pub fn set_linkage_name(&mut self, name: &str) {
self.linkage_name = Some(name.to_string());
}
pub fn add_parameter(&mut self, param: X86DwarfVariable) {
self.parameters.push(param);
}
pub fn add_variable(&mut self, var: X86DwarfVariable) {
self.variables.push(var);
}
pub fn add_lexical_block(&mut self, block: X86DwarfLexicalBlock) {
self.lexical_blocks.push(block);
}
pub fn add_inlined_subroutine(&mut self, inlined: X86DwarfInlinedSubroutine) {
self.inlined_subroutines.push(inlined);
}
pub fn set_inlined(&mut self, code: u8) {
self.inline_code = Some(code);
self.is_declaration = true;
}
pub fn set_abstract_origin(&mut self, origin_offset: u64) {
self.abstract_origin = Some(origin_offset);
}
pub fn compute_x86_64_frame_base(&self, local_area_size: u32) -> Vec<u8> {
let mut loc = X86DwarfLocation::new_x86_64();
loc.call_frame_cfa();
loc.finish()
}
pub fn compute_x86_32_frame_base(&self, local_area_size: u32) -> Vec<u8> {
let mut loc = X86DwarfLocation::new_x86_32();
loc.call_frame_cfa();
loc.finish()
}
}
#[derive(Debug, Clone)]
pub struct X86DwarfInlinedSubroutine {
pub abstract_origin: u64,
pub low_pc: u64,
pub high_pc: u64,
pub call_file: u32,
pub call_line: u32,
pub variables: Vec<X86DwarfVariable>,
pub lexical_blocks: Vec<X86DwarfLexicalBlock>,
}
impl X86DwarfInlinedSubroutine {
pub fn new(
abstract_origin: u64,
low_pc: u64,
high_pc: u64,
call_file: u32,
call_line: u32,
) -> Self {
Self {
abstract_origin,
low_pc,
high_pc,
call_file,
call_line,
variables: Vec::new(),
lexical_blocks: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CFIEntry {
CIE(CIEData),
FDE(FDEData),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CIEData {
pub cie_id: u64,
pub version: u8,
pub augmentation: String,
pub address_size: u8,
pub segment_size: u8,
pub code_alignment_factor: u64,
pub data_alignment_factor: i64,
pub return_address_register: u64,
pub initial_instructions: Vec<u8>,
pub pointer_encoding: Option<u8>,
pub lsda_encoding: Option<u8>,
pub personality_encoding: Option<u8>,
pub personality: Option<u64>,
pub fde_pointer_encoding: Option<u8>,
}
impl Default for CIEData {
fn default() -> Self {
Self {
cie_id: 0,
version: 4,
augmentation: String::new(),
address_size: 8,
segment_size: 0,
code_alignment_factor: 1,
data_alignment_factor: -8,
return_address_register: 16, initial_instructions: Vec::new(),
pointer_encoding: None,
lsda_encoding: None,
personality_encoding: None,
personality: None,
fde_pointer_encoding: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FDEData {
pub cie_pointer: u64,
pub initial_location: u64,
pub address_range: u64,
pub instructions: Vec<u8>,
pub lsda_pointer: Option<u64>,
pub personality: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct X86DwarfFrame {
pub entries: Vec<CFIEntry>,
pub address_size: u8,
pub version: u8,
pub is_eh_frame: bool,
}
impl X86DwarfFrame {
pub fn new_debug_frame_64() -> Self {
Self {
entries: Vec::new(),
address_size: 8,
version: 4,
is_eh_frame: false,
}
}
pub fn new_debug_frame_32() -> Self {
Self {
entries: Vec::new(),
address_size: 4,
version: 4,
is_eh_frame: false,
}
}
pub fn new_eh_frame_64() -> Self {
Self {
entries: Vec::new(),
address_size: 8,
version: 4,
is_eh_frame: true,
}
}
pub fn create_x86_64_cie() -> CIEData {
let mut cie = CIEData::default();
cie.version = 4;
cie.address_size = 8;
cie.code_alignment_factor = 1;
cie.data_alignment_factor = -8;
cie.return_address_register = 16; let mut init = Vec::new();
init.push(cfa_ops::DW_CFA_def_cfa);
encode_uleb128(&mut init, 7); encode_uleb128(&mut init, 8); init.push(cfa_ops::DW_CFA_offset + 16); encode_uleb128(&mut init, 1); cie.initial_instructions = init;
cie
}
pub fn create_x86_32_cie() -> CIEData {
let mut cie = CIEData::default();
cie.version = 4;
cie.address_size = 4;
cie.code_alignment_factor = 1;
cie.data_alignment_factor = -4;
cie.return_address_register = 8; let mut init = Vec::new();
init.push(cfa_ops::DW_CFA_def_cfa);
encode_uleb128(&mut init, 4); encode_uleb128(&mut init, 4); init.push(0x80 + 8); encode_uleb128(&mut init, 1); cie.initial_instructions = init;
cie
}
pub fn add_cie(&mut self, cie: CIEData) -> u64 {
let offset = self.compute_offset();
self.entries.push(CFIEntry::CIE(cie));
offset
}
pub fn add_fde(&mut self, fde: FDEData) -> u64 {
let offset = self.compute_offset();
self.entries.push(CFIEntry::FDE(fde));
offset
}
fn compute_offset(&self) -> u64 {
let mut offset: u64 = 0;
for entry in &self.entries {
offset += self.entry_size_estimate(entry);
}
offset
}
fn entry_size_estimate(&self, entry: &CFIEntry) -> u64 {
match entry {
CFIEntry::CIE(cie) => {
let aug_len = cie.augmentation.len() as u64 + 1; let init_len = cie.initial_instructions.len() as u64;
4 + 4 + 1 + aug_len + 2 + init_len + 4 }
CFIEntry::FDE(fde) => {
4 + 4
+ self.address_size as u64
+ self.address_size as u64
+ fde.instructions.len() as u64
}
}
}
pub fn emit(&self) -> Vec<u8> {
let mut data = Vec::new();
for entry in &self.entries {
match entry {
CFIEntry::CIE(cie) => {
self.emit_cie(&mut data, cie);
}
CFIEntry::FDE(fde) => {
self.emit_fde(&mut data, fde);
}
}
}
data.extend_from_slice(&0u32.to_le_bytes());
data
}
fn emit_cie(&self, data: &mut Vec<u8>, cie: &CIEData) {
let cie_id_size = if cie.cie_id == 0xFFFFFFFF { 8 } else { 4 };
let aug_len = cie.augmentation.len() as u64 + 1;
let content_len: u64 = cie_id_size as u64
+ 1 + aug_len
+ uleb128(cie.code_alignment_factor).len() as u64
+ sleb128(cie.data_alignment_factor).len() as u64
+ uleb128(cie.return_address_register).len() as u64
+ cie.initial_instructions.len() as u64;
data.extend_from_slice(&(content_len as u32).to_le_bytes());
data.extend_from_slice(&(cie.cie_id as u32).to_le_bytes());
data.push(cie.version);
data.extend_from_slice(cie.augmentation.as_bytes());
data.push(0);
data.extend_from_slice(&uleb128(cie.code_alignment_factor));
data.extend_from_slice(&sleb128(cie.data_alignment_factor));
data.extend_from_slice(&uleb128(cie.return_address_register));
data.extend_from_slice(&cie.initial_instructions);
}
fn emit_fde(&self, data: &mut Vec<u8>, fde: &FDEData) {
let addr_size = self.address_size as u64;
let content_len: u64 = 4 + addr_size + addr_size + fde.instructions.len() as u64;
data.extend_from_slice(&(content_len as u32).to_le_bytes());
data.extend_from_slice(&(fde.cie_pointer as u32).to_le_bytes());
match self.address_size {
4 => data.extend_from_slice(&(fde.initial_location as u32).to_le_bytes()),
_ => data.extend_from_slice(&fde.initial_location.to_le_bytes()),
}
match self.address_size {
4 => data.extend_from_slice(&(fde.address_range as u32).to_le_bytes()),
_ => data.extend_from_slice(&fde.address_range.to_le_bytes()),
}
data.extend_from_slice(&fde.instructions);
}
pub fn build_x86_64_prologue_cfi(local_area_size: u32) -> Vec<u8> {
let mut insns = Vec::new();
insns.push(cfa_ops::DW_CFA_advance_loc | 1);
insns.push(cfa_ops::DW_CFA_def_cfa_offset);
encode_uleb128(&mut insns, 16);
insns.push(0x80 | 6); encode_uleb128(&mut insns, 2);
if local_area_size > 0 {
insns.push(cfa_ops::DW_CFA_advance_loc | 4);
insns.push(cfa_ops::DW_CFA_def_cfa_offset);
encode_uleb128(&mut insns, 16 + local_area_size as u64);
}
insns
}
pub fn build_x86_64_epilogue_cfi() -> Vec<u8> {
let mut insns = Vec::new();
insns.push(cfa_ops::DW_CFA_advance_loc | 3);
insns.push(cfa_ops::DW_CFA_def_cfa_register);
encode_uleb128(&mut insns, 6);
insns.push(cfa_ops::DW_CFA_advance_loc | 1);
insns.push(cfa_ops::DW_CFA_def_cfa_offset);
encode_uleb128(&mut insns, 8);
insns.push(0xC0 | 6);
insns
}
pub fn build_x86_32_prologue_cfi(local_area_size: u32) -> Vec<u8> {
let mut insns = Vec::new();
insns.push(cfa_ops::DW_CFA_advance_loc | 1);
insns.push(cfa_ops::DW_CFA_def_cfa_offset);
encode_uleb128(&mut insns, 8);
insns.push(0x80 | 4);
encode_uleb128(&mut insns, 2);
if local_area_size > 0 {
insns.push(cfa_ops::DW_CFA_advance_loc | 4);
insns.push(cfa_ops::DW_CFA_def_cfa_offset);
encode_uleb128(&mut insns, 8 + local_area_size as u64);
}
insns
}
pub fn cfa_advance_loc(delta: u8) -> Vec<u8> {
if delta <= 0x3F {
vec![cfa_ops::DW_CFA_advance_loc | delta]
} else if delta <= 0xFF {
let mut v = vec![cfa_ops::DW_CFA_advance_loc1];
v.push(delta);
v
} else {
let mut v = vec![cfa_ops::DW_CFA_advance_loc2];
v.extend_from_slice(&(delta as u16).to_le_bytes());
v
}
}
pub fn cfa_def_cfa(register: u64, offset: u64) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_def_cfa];
encode_uleb128(&mut v, register);
encode_uleb128(&mut v, offset);
v
}
pub fn cfa_def_cfa_register(register: u64) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_def_cfa_register];
encode_uleb128(&mut v, register);
v
}
pub fn cfa_def_cfa_offset(offset: u64) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_def_cfa_offset];
encode_uleb128(&mut v, offset);
v
}
pub fn cfa_offset(register: u64, offset: u64) -> Vec<u8> {
if register <= 0x3F {
let mut v = vec![cfa_ops::DW_CFA_offset | register as u8];
encode_uleb128(&mut v, offset);
v
} else {
let mut v = vec![cfa_ops::DW_CFA_offset_extended];
encode_uleb128(&mut v, register);
encode_uleb128(&mut v, offset);
v
}
}
pub fn cfa_register(reg: u64, other_reg: u64) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_register];
encode_uleb128(&mut v, reg);
encode_uleb128(&mut v, other_reg);
v
}
pub fn cfa_restore(register: u64) -> Vec<u8> {
if register <= 0x3F {
vec![cfa_ops::DW_CFA_restore | register as u8]
} else {
let mut v = vec![cfa_ops::DW_CFA_restore_extended];
encode_uleb128(&mut v, register);
v
}
}
pub fn cfa_remember_state() -> Vec<u8> {
vec![cfa_ops::DW_CFA_remember_state]
}
pub fn cfa_restore_state() -> Vec<u8> {
vec![cfa_ops::DW_CFA_restore_state]
}
pub fn cfa_nop() -> Vec<u8> {
vec![cfa_ops::DW_CFA_nop]
}
pub fn cfa_gnu_args_size(size: u64) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_GNU_args_size];
encode_uleb128(&mut v, size);
v
}
pub fn cfa_val_offset(register: u64, offset: u64) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_val_offset];
encode_uleb128(&mut v, register);
encode_uleb128(&mut v, offset);
v
}
pub fn cfa_val_expression(register: u64, expression: &[u8]) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_val_expression];
encode_uleb128(&mut v, register);
encode_uleb128(&mut v, expression.len() as u64);
v.extend_from_slice(expression);
v
}
pub fn cfa_expression(register: u64, expression: &[u8]) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_expression];
encode_uleb128(&mut v, register);
encode_uleb128(&mut v, expression.len() as u64);
v.extend_from_slice(expression);
v
}
pub fn cfa_def_cfa_expression(expression: &[u8]) -> Vec<u8> {
let mut v = vec![cfa_ops::DW_CFA_def_cfa_expression];
encode_uleb128(&mut v, expression.len() as u64);
v.extend_from_slice(expression);
v
}
pub fn x86_64_save_callee_saved(reg: u64, cfa_offset: i64) -> Vec<u8> {
let mut insns = Vec::new();
insns.push(cfa_ops::DW_CFA_advance_loc | 1);
insns.push(cfa_ops::DW_CFA_offset | (reg as u8 & 0x3F));
encode_uleb128(&mut insns, ((-cfa_offset) / 8) as u64);
insns
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Debug, Clone, Default)]
pub struct SectionOffsets {
pub debug_info: u64,
pub debug_abbrev: u64,
pub debug_line: u64,
pub debug_str: u64,
pub debug_str_offsets: u64,
pub debug_addr: u64,
pub debug_ranges: u64,
pub debug_aranges: u64,
pub debug_frame: u64,
}
#[derive(Debug, Clone)]
pub struct X86DwarfGenerator {
pub compile_unit: X86DwarfCompileUnit,
pub type_unit: X86DwarfTypeUnit,
pub string_table: X86DwarfStringTable,
pub frame: X86DwarfFrame,
pub abbrev: X86DwarfAbbrev,
pub addr_table: X86DwarfAddrTable,
pub range_list: X86DwarfRangeList,
pub arange: X86DwarfARange,
pub target_triple: String,
pub dwarf_version: u16,
pub address_size: u8,
pub is_64bit: bool,
pub section_offsets: SectionOffsets,
}
impl X86DwarfGenerator {
pub fn new_x86_64() -> Self {
let file_name = "unknown.c";
let comp_dir = "/tmp";
let producer = "LLVM-Native 1.0 (X86-64 DWARF Generator)";
let cu = X86DwarfCompileUnit::new_x86_64(
file_name,
comp_dir,
producer,
x86_dwarf_languages::DW_LANG_C99,
);
let frame = X86DwarfFrame::new_debug_frame_64();
Self {
compile_unit: cu.clone(),
type_unit: cu.type_unit.clone(),
string_table: X86DwarfStringTable::new(),
frame,
abbrev: cu.abbrev.clone(),
addr_table: X86DwarfAddrTable::new(8),
range_list: X86DwarfRangeList::new(8, false),
arange: X86DwarfARange::new(8),
target_triple: "x86_64-unknown-linux-gnu".to_string(),
dwarf_version: 4,
address_size: 8,
is_64bit: true,
section_offsets: SectionOffsets::default(),
}
}
pub fn new_x86_32() -> Self {
let file_name = "unknown.c";
let comp_dir = "/tmp";
let producer = "LLVM-Native 1.0 (X86-32 DWARF Generator)";
let cu = X86DwarfCompileUnit::new_x86_32(
file_name,
comp_dir,
producer,
x86_dwarf_languages::DW_LANG_C99,
);
let frame = X86DwarfFrame::new_debug_frame_32();
Self {
compile_unit: cu.clone(),
type_unit: cu.type_unit.clone(),
string_table: X86DwarfStringTable::new(),
frame,
abbrev: cu.abbrev.clone(),
addr_table: X86DwarfAddrTable::new(4),
range_list: X86DwarfRangeList::new(4, false),
arange: X86DwarfARange::new(4),
target_triple: "i386-unknown-linux-gnu".to_string(),
dwarf_version: 4,
address_size: 4,
is_64bit: false,
section_offsets: SectionOffsets::default(),
}
}
pub fn set_producer(&mut self, producer: &str) {
self.compile_unit.producer = producer.to_string();
}
pub fn set_source_file(&mut self, file_name: &str, comp_dir: &str) {
self.compile_unit.file_name = file_name.to_string();
self.compile_unit.comp_dir = comp_dir.to_string();
self.compile_unit.line_table.set_comp_dir(comp_dir);
self.compile_unit.line_table.add_file(file_name, 0);
}
pub fn set_language(&mut self, lang: u16) {
self.compile_unit.language = lang;
}
pub fn add_subprogram(&mut self, sp: X86DwarfSubprogram) {
self.compile_unit.add_subprogram(sp);
}
pub fn add_global_variable(&mut self, var: X86DwarfVariable) {
self.compile_unit.add_global_variable(var);
}
pub fn add_line_entry(&mut self, entry: X86LineEntry) {
self.compile_unit.line_table.add_entry(entry);
}
pub fn register_base_types(&mut self) {
self.type_unit.register_standard_base_types();
}
pub fn register_x86_types(&mut self) {
self.type_unit.register_x86_specific_types();
}
pub fn emit_all(&mut self) -> HashMap<String, Vec<u8>> {
let mut sections = HashMap::new();
self.collect_strings();
sections.insert(".debug_abbrev".to_string(), self.abbrev.emit());
sections.insert(".debug_info".to_string(), self.emit_debug_info());
sections.insert(
".debug_line".to_string(),
self.compile_unit.line_table.emit(),
);
sections.insert(".debug_str".to_string(), self.string_table.emit());
let offset_size = if self.address_size == 8 { 4 } else { 4 };
sections.insert(
".debug_str_offsets".to_string(),
self.string_table.emit_str_offsets(offset_size),
);
sections.insert(".debug_frame".to_string(), self.frame.emit());
self.build_aranges();
sections.insert(".debug_aranges".to_string(), self.arange.emit());
if self.dwarf_version >= 5 {
sections.insert(
".debug_rnglists".to_string(),
self.range_list.emit_rnglists_dwarf5(),
);
} else {
sections.insert(
".debug_ranges".to_string(),
self.range_list.emit_ranges_dwarf4(),
);
}
sections.insert(".debug_addr".to_string(), self.addr_table.emit());
sections
}
pub fn emit_debug_info(&mut self) -> Vec<u8> {
let mut data = Vec::new();
let cu_start = data.len();
data.extend_from_slice(&[0u8; 4]);
data.extend_from_slice(&self.dwarf_version.to_le_bytes());
data.extend_from_slice(&0u32.to_le_bytes());
data.push(self.address_size);
data.extend_from_slice(&self.compile_unit.emit_die_header());
for sp in &self.compile_unit.subprograms {
data.extend_from_slice(&self.emit_subprogram_die(sp));
}
data.push(0);
let total_len = (data.len() - cu_start - 4) as u32;
data[cu_start..cu_start + 4].copy_from_slice(&total_len.to_le_bytes());
data
}
fn emit_subprogram_die(&self, sp: &X86DwarfSubprogram) -> Vec<u8> {
let mut data = Vec::new();
let abbrev_code = if sp.is_external { 3u64 } else { 2u64 };
encode_uleb128(&mut data, abbrev_code);
data.extend_from_slice(sp.name.as_bytes());
data.push(0);
match self.address_size {
4 => data.extend_from_slice(&(sp.low_pc as u32).to_le_bytes()),
_ => data.extend_from_slice(&sp.low_pc.to_le_bytes()),
}
data.extend_from_slice(&sp.high_pc.to_le_bytes());
if let Some(ref fb) = sp.frame_base {
encode_uleb128(&mut data, fb.len() as u64);
data.extend_from_slice(fb);
} else {
encode_uleb128(&mut data, 0);
}
data.extend_from_slice(&0u32.to_le_bytes());
if sp.is_external {
}
for param in &sp.parameters {
data.extend_from_slice(&self.emit_variable_die(param));
}
for var in &sp.variables {
data.extend_from_slice(&self.emit_variable_die(var));
}
for block in &sp.lexical_blocks {
data.extend_from_slice(&self.emit_lexical_block_die(block));
}
for inlined in &sp.inlined_subroutines {
data.extend_from_slice(&self.emit_inlined_die(inlined));
}
data.push(0);
data
}
fn emit_variable_die(&self, var: &X86DwarfVariable) -> Vec<u8> {
let mut data = Vec::new();
let abbrev_code = match var.tag {
x if x == dwarf_tags::DW_TAG_formal_parameter => 5u64,
_ => 4u64,
};
encode_uleb128(&mut data, abbrev_code);
data.extend_from_slice(var.name.as_bytes());
data.push(0);
data.extend_from_slice(&0u32.to_le_bytes());
encode_uleb128(&mut data, var.location.len() as u64);
data.extend_from_slice(&var.location);
data
}
fn emit_lexical_block_die(&self, block: &X86DwarfLexicalBlock) -> Vec<u8> {
let mut data = Vec::new();
encode_uleb128(&mut data, 6u64);
match self.address_size {
4 => data.extend_from_slice(&(block.low_pc as u32).to_le_bytes()),
_ => data.extend_from_slice(&block.low_pc.to_le_bytes()),
}
data.extend_from_slice(&block.high_pc.to_le_bytes());
for var in &block.variables {
data.extend_from_slice(&self.emit_variable_die(var));
}
data.push(0);
data
}
fn emit_inlined_die(&self, inlined: &X86DwarfInlinedSubroutine) -> Vec<u8> {
let mut data = Vec::new();
encode_uleb128(&mut data, 7u64);
data.extend_from_slice(&(inlined.abstract_origin as u32).to_le_bytes());
match self.address_size {
4 => data.extend_from_slice(&(inlined.low_pc as u32).to_le_bytes()),
_ => data.extend_from_slice(&inlined.low_pc.to_le_bytes()),
}
data.extend_from_slice(&inlined.high_pc.to_le_bytes());
data.push(inlined.call_file as u8);
data.extend_from_slice(&inlined.call_line.to_le_bytes());
for var in &inlined.variables {
data.extend_from_slice(&self.emit_variable_die(var));
}
for block in &inlined.lexical_blocks {
data.extend_from_slice(&self.emit_lexical_block_die(block));
}
data.push(0);
data
}
fn collect_strings(&mut self) {
self.string_table.add(&self.compile_unit.producer);
self.string_table.add(&self.compile_unit.file_name);
self.string_table.add(&self.compile_unit.comp_dir);
for sp in &self.compile_unit.subprograms {
self.string_table.add(&sp.name);
if let Some(ref link) = sp.linkage_name {
self.string_table.add(link);
}
for param in &sp.parameters {
self.string_table.add(¶m.name);
}
for var in &sp.variables {
self.string_table.add(&var.name);
}
}
for var in &self.compile_unit.global_variables {
self.string_table.add(&var.name);
}
for t in &self.type_unit.types {
for (_, data) in &t.attributes {
if data.last() == Some(&0) && data.len() > 1 {
if let Ok(s) = std::str::from_utf8(&data[..data.len() - 1]) {
self.string_table.add(s);
}
}
}
}
}
fn build_aranges(&mut self) {
let mut ranges = Vec::new();
for sp in &self.compile_unit.subprograms {
ranges.push(ARangeEntry {
cu_offset: self.compile_unit.section_offset,
address: sp.low_pc,
length: sp.high_pc,
});
}
self.arange.add_cu(self.compile_unit.section_offset, ranges);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_location_new_x86_64() {
let loc = X86DwarfLocation::new_x86_64();
assert_eq!(loc.address_size, 8);
assert!(loc.is_empty());
}
#[test]
fn test_location_new_x86_32() {
let loc = X86DwarfLocation::new_x86_32();
assert_eq!(loc.address_size, 4);
assert!(loc.is_empty());
}
#[test]
fn test_location_deref() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.deref();
assert_eq!(loc.len(), 1);
assert_eq!(loc.bytes[0], dwarf_ops::DW_OP_deref as u8);
}
#[test]
fn test_location_const_u32() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.const_u32(0xDEAD_BEEF);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_const4u as u8);
assert_eq!(&bytes[1..5], &0xDEAD_BEEFu32.to_le_bytes());
}
#[test]
fn test_location_addr() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.addr(0x4000_0000);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_addr as u8);
assert_eq!(&bytes[1..9], &0x4000_0000u64.to_le_bytes());
}
#[test]
fn test_location_addr_32() {
let mut loc = X86DwarfLocation::new_x86_32();
loc.addr(0x0804_8000);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_addr as u8);
assert_eq!(&bytes[1..5], &0x0804_8000u32.to_le_bytes());
}
#[test]
fn test_location_fbreg() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.fbreg(-8);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_fbreg as u8);
assert_eq!(bytes[1], 0x78);
}
#[test]
fn test_location_fbreg_positive() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.fbreg(16);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_fbreg as u8);
assert_eq!(bytes[1], 0x10); }
#[test]
fn test_location_plus_minus_mul_div() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.const_u32(10);
loc.const_u32(3);
loc.plus();
loc.minus();
loc.mul();
loc.div();
let bytes = loc.finish();
assert!(bytes.len() > 12);
}
#[test]
fn test_location_piece() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.piece(8);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_piece as u8);
assert_eq!(bytes[1], 0x08);
}
#[test]
fn test_location_stack_value() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.const_u32(42);
loc.stack_value();
let bytes = loc.finish();
let last = *bytes.last().unwrap();
assert_eq!(last, dwarf_ops::DW_OP_stack_value as u8);
}
#[test]
fn test_location_implicit_value() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.implicit_value(&[0x42, 0x00, 0x00, 0x00]);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_implicit_value as u8);
assert_eq!(bytes[1], 0x04);
assert_eq!(&bytes[2..6], &[0x42, 0x00, 0x00, 0x00]);
}
#[test]
fn test_location_entry_value() {
let inner = vec![0x50u8, 0x91, 0x78]; let mut loc = X86DwarfLocation::new_x86_64();
loc.entry_value(&inner);
let bytes = loc.finish();
assert_eq!(bytes[0], x86_dwarf_ops_ext::DW_OP_entry_value);
assert_eq!(bytes[1], inner.len() as u8);
assert_eq!(&bytes[2..], &inner[..]);
}
#[test]
fn test_location_sysv_param_reg() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.sysv_int_param(0, 16);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_reg0 as u8 + 5);
}
#[test]
fn test_location_sysv_param_stack() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.sysv_int_param(6, 16);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_fbreg as u8);
}
#[test]
fn test_location_win64_param() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.win64_int_param(0);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_reg0 as u8 + 2);
}
#[test]
fn test_location_all_arithmetic() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.const_u32(100);
loc.const_u32(50);
loc.plus();
loc.minus();
loc.mul();
loc.div();
loc.modulo();
loc.and();
loc.or();
loc.xor();
loc.shl();
loc.shr();
loc.shl();
loc.neg();
loc.not();
loc.abs();
let bytes = loc.finish();
assert!(bytes.len() > 30);
}
#[test]
fn test_location_all_comparisons() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.const_u32(10);
loc.const_u32(20);
loc.eq();
loc.ne();
loc.lt();
loc.le();
loc.gt();
loc.ge();
assert!(loc.len() > 10);
}
#[test]
fn test_location_rot_swap() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.const_u32(1);
loc.const_u32(2);
loc.const_u32(3);
loc.rot();
loc.swap();
assert!(loc.len() > 10);
}
#[test]
fn test_location_local_var_below_rbp() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.local_var_below_rbp(8);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_breg0 as u8 + 6);
assert_eq!(bytes[1], 0x78); }
#[test]
fn test_expression_eval_lit() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![dwarf_ops::DW_OP_lit0 as u8 + 5]; let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(5));
}
#[test]
fn test_expression_eval_plus() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 10);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 5);
program.push(dwarf_ops::DW_OP_plus as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(15));
}
#[test]
fn test_expression_eval_subtract() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 20);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 8);
program.push(dwarf_ops::DW_OP_minus as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(12));
}
#[test]
fn test_expression_eval_mul() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 6);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 7);
program.push(dwarf_ops::DW_OP_mul as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(42));
}
#[test]
fn test_expression_eval_div() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 20);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 4);
program.push(dwarf_ops::DW_OP_div as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(5));
}
#[test]
fn test_expression_eval_dup_drop() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 7);
program.push(dwarf_ops::DW_OP_dup as u8);
program.push(dwarf_ops::DW_OP_plus as u8); let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(14));
}
#[test]
fn test_expression_eval_fbreg() {
let mut expr = X86DwarfExpression::new_x86_64();
expr.set_frame_base(0x7FFF_FF00);
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_fbreg as u8);
program.push(0x78); let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0x7FFF_FF00 - 8));
}
#[test]
fn test_expression_eval_compare() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 5);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 10);
program.push(dwarf_ops::DW_OP_lt as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(1));
}
#[test]
fn test_expression_eval_const() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = vec![dwarf_ops::DW_OP_const4u as u8];
program.extend_from_slice(&0x1234_5678u32.to_le_bytes());
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0x1234_5678));
}
#[test]
fn test_expression_eval_consts() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = vec![dwarf_ops::DW_OP_const4s as u8];
program.extend_from_slice(&(-42i32).to_le_bytes());
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(-42));
}
#[test]
fn test_expression_eval_stack_value() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![
dwarf_ops::DW_OP_lit0 as u8 + 9,
dwarf_ops::DW_OP_stack_value as u8,
];
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(9));
assert!(!expr.result_is_location);
}
#[test]
fn test_abbrev_new_empty() {
let abbrev = X86DwarfAbbrev::new();
assert!(abbrev.is_empty());
assert_eq!(abbrev.len(), 0);
}
#[test]
fn test_abbrev_add_and_dedup() {
let mut abbrev = X86DwarfAbbrev::new();
let code1 = abbrev.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_base_type,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(
dwarf_attributes::DW_AT_byte_size,
dwarf_forms::DW_FORM_data1,
),
],
});
let code2 = abbrev.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_base_type,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(
dwarf_attributes::DW_AT_byte_size,
dwarf_forms::DW_FORM_data1,
),
],
});
assert_eq!(code1, code2);
assert_eq!(abbrev.len(), 1);
}
#[test]
fn test_abbrev_compile_unit() {
let mut abbrev = X86DwarfAbbrev::new();
let code = abbrev.add_compile_unit_abbrev();
assert_eq!(code, 1);
assert_eq!(abbrev.len(), 1);
}
#[test]
fn test_abbrev_subprogram() {
let mut abbrev = X86DwarfAbbrev::new();
let code = abbrev.add_subprogram_abbrev();
assert!(code > 0);
assert_eq!(abbrev.len(), 1);
}
#[test]
fn test_abbrev_emit() {
let mut abbrev = X86DwarfAbbrev::new();
abbrev.add_base_type_abbrev();
abbrev.add_pointer_type_abbrev();
let data = abbrev.emit();
assert!(!data.is_empty());
assert_eq!(*data.last().unwrap(), 0);
}
#[test]
fn test_abbrev_emit_multiple() {
let mut abbrev = X86DwarfAbbrev::new();
abbrev.add_compile_unit_abbrev();
abbrev.add_subprogram_abbrev();
abbrev.add_base_type_abbrev();
abbrev.add_pointer_type_abbrev();
abbrev.add_member_abbrev();
let data = abbrev.emit();
assert!(data.len() > 10);
}
#[test]
fn test_abbrev_all_types() {
let mut abbrev = X86DwarfAbbrev::new();
abbrev.add_base_type_abbrev();
abbrev.add_pointer_type_abbrev();
abbrev.add_modifier_type_abbrev(dwarf_tags::DW_TAG_const_type);
abbrev.add_modifier_type_abbrev(dwarf_tags::DW_TAG_volatile_type);
abbrev.add_modifier_type_abbrev(x86_dwarf_tags::DW_TAG_restrict_type);
abbrev.add_typedef_abbrev();
abbrev.add_struct_type_abbrev(dwarf_tags::DW_TAG_structure_type);
abbrev.add_struct_type_abbrev(x86_dwarf_tags::DW_TAG_union_type);
abbrev.add_struct_decl_abbrev(x86_dwarf_tags::DW_TAG_class_type);
abbrev.add_member_abbrev();
abbrev.add_enum_type_abbrev();
abbrev.add_enumerator_abbrev();
abbrev.add_array_type_abbrev();
abbrev.add_subrange_type_abbrev();
abbrev.add_subroutine_type_abbrev();
abbrev.add_formal_parameter_abbrev();
abbrev.add_reference_type_abbrev(x86_dwarf_tags::DW_TAG_reference_type);
abbrev.add_reference_type_abbrev(x86_dwarf_tags::DW_TAG_rvalue_reference_type);
abbrev.add_inheritance_abbrev();
assert_eq!(abbrev.len(), 20);
}
#[test]
fn test_string_table_new() {
let table = X86DwarfStringTable::new();
assert_eq!(table.len(), 0);
assert!(table.is_empty());
assert!(table.contains("")); assert_eq!(table.size(), 1); }
#[test]
fn test_string_table_add() {
let mut table = X86DwarfStringTable::new();
let offset = table.add("hello");
assert_eq!(offset, 1); assert_eq!(table.len(), 1);
}
#[test]
fn test_string_table_dedup() {
let mut table = X86DwarfStringTable::new();
let o1 = table.add("test");
let o2 = table.add("test");
assert_eq!(o1, o2);
assert_eq!(table.len(), 1);
}
#[test]
fn test_string_table_emit() {
let mut table = X86DwarfStringTable::new();
table.add("abc");
table.add("def");
let data = table.emit();
assert_eq!(data[0], 0);
assert_eq!(&data[1..4], b"abc");
assert_eq!(data[4], 0);
assert_eq!(&data[5..8], b"def");
assert_eq!(data[8], 0);
}
#[test]
fn test_string_table_get_offset() {
let mut table = X86DwarfStringTable::new();
let o = table.add("var_name");
assert_eq!(table.get_offset("var_name"), Some(o));
assert_eq!(table.get_offset("nonexistent"), None);
}
#[test]
fn test_str_offsets_emit() {
let mut table = X86DwarfStringTable::new();
table.add("type1");
table.add("type2");
let data = table.emit_str_offsets(4);
assert!(data.len() > 12); }
#[test]
fn test_addr_table_new() {
let table = X86DwarfAddrTable::new(8);
assert!(table.is_empty());
assert_eq!(table.len(), 0);
}
#[test]
fn test_addr_table_add() {
let mut table = X86DwarfAddrTable::new(8);
let idx = table.add(0x4000_0000);
assert_eq!(idx, 0);
assert_eq!(table.len(), 1);
}
#[test]
fn test_addr_table_dedup() {
let mut table = X86DwarfAddrTable::new(4);
let i1 = table.add(0x0804_8000);
let i2 = table.add(0x0804_8000);
assert_eq!(i1, i2);
assert_eq!(table.len(), 1);
}
#[test]
fn test_addr_table_emit_64() {
let mut table = X86DwarfAddrTable::new(8);
table.add(0x4010_0000);
table.add(0x4020_0000);
let data = table.emit();
assert!(data.len() >= 24);
}
#[test]
fn test_addr_table_emit_32() {
let mut table = X86DwarfAddrTable::new(4);
table.add(0x0804_8000);
let data = table.emit();
assert!(data.len() >= 12);
}
#[test]
fn test_range_list_new() {
let rl = X86DwarfRangeList::new(8, false);
assert!(rl.is_empty());
assert_eq!(rl.len(), 0);
}
#[test]
fn test_range_list_add() {
let mut rl = X86DwarfRangeList::new(8, false);
let ranges = vec![AddrRange {
low_pc: 0x1000,
high_pc: 0x2000,
}];
let offset = rl.add(ranges);
assert_eq!(offset, 0);
assert_eq!(rl.len(), 1);
}
#[test]
fn test_range_list_emit_dwarf4() {
let mut rl = X86DwarfRangeList::new(8, false);
rl.add(vec![AddrRange {
low_pc: 0x1000,
high_pc: 0x2000,
}]);
let data = rl.emit_ranges_dwarf4();
assert_eq!(data.len(), 32);
}
#[test]
fn test_range_list_emit_dwarf5() {
let mut rl = X86DwarfRangeList::new(8, true);
rl.add(vec![AddrRange {
low_pc: 0x1000,
high_pc: 0x2000,
}]);
let data = rl.emit_rnglists_dwarf5();
assert!(data.len() > 20);
}
#[test]
fn test_range_list_multiple_ranges() {
let mut rl = X86DwarfRangeList::new(8, false);
rl.add(vec![
AddrRange {
low_pc: 0x1000,
high_pc: 0x1500,
},
AddrRange {
low_pc: 0x2000,
high_pc: 0x2500,
},
AddrRange {
low_pc: 0x3000,
high_pc: 0x3500,
},
]);
let data = rl.emit_ranges_dwarf4();
assert_eq!(data.len(), 8 * 8);
}
#[test]
fn test_arange_new() {
let ar = X86DwarfARange::new(8);
assert!(ar.is_empty());
}
#[test]
fn test_arange_add_cu() {
let mut ar = X86DwarfARange::new(8);
let ranges = vec![ARangeEntry {
cu_offset: 0,
address: 0x4000_0000,
length: 0x1000,
}];
ar.add_cu(0, ranges);
assert_eq!(ar.len(), 1);
}
#[test]
fn test_arange_emit() {
let mut ar = X86DwarfARange::new(8);
ar.add_cu(
0,
vec![ARangeEntry {
cu_offset: 0,
address: 0x4000_0000,
length: 0x500,
}],
);
let data = ar.emit();
assert!(data.len() > 20);
}
#[test]
fn test_arange_emit_32() {
let mut ar = X86DwarfARange::new(4);
ar.add_cu(
0,
vec![ARangeEntry {
cu_offset: 0,
address: 0x0804_8000,
length: 0x1000,
}],
);
let data = ar.emit();
assert!(data.len() > 10);
}
#[test]
fn test_type_unit_new() {
let tu = X86DwarfTypeUnit::new(8);
assert!(tu.types.is_empty());
assert!(tu.offsets.is_empty());
}
#[test]
fn test_type_unit_register_base_types() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
assert!(tu.offsets.contains_key("int"));
assert!(tu.offsets.contains_key("float"));
assert!(tu.offsets.contains_key("double"));
assert!(tu.offsets.contains_key("char"));
assert!(tu.offsets.contains_key("void"));
assert!(tu.offsets.contains_key("bool"));
}
#[test]
fn test_type_unit_register_x86_types() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_x86_specific_types();
assert!(tu.offsets.contains_key("__int128"));
assert!(tu.offsets.contains_key("__float80"));
assert!(tu.offsets.contains_key("__float128"));
assert!(tu.offsets.contains_key("__m64"));
assert!(tu.offsets.contains_key("__m128"));
assert!(tu.offsets.contains_key("__m128d"));
assert!(tu.offsets.contains_key("__m128i"));
assert!(tu.offsets.contains_key("__m256"));
assert!(tu.offsets.contains_key("__m256d"));
assert!(tu.offsets.contains_key("__m256i"));
assert!(tu.offsets.contains_key("__m512"));
assert!(tu.offsets.contains_key("__m512d"));
assert!(tu.offsets.contains_key("__m512i"));
}
#[test]
fn test_type_unit_base_type_die() {
let die = X86DwarfTypeUnit::build_base_type("int", x86_dwarf_encodings::DW_ATE_signed, 4);
assert_eq!(die.tag, dwarf_tags::DW_TAG_base_type);
assert_eq!(die.attributes.len(), 3);
assert!(die.children.is_empty());
}
#[test]
fn test_type_unit_pointer_type() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let ptr = tu.build_pointer_type("int", "int*");
assert!(ptr.is_some());
assert!(tu.offsets.contains_key("int*"));
}
#[test]
fn test_type_unit_reference_type() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let rref = tu.build_reference_type("int", "int&");
assert!(rref.is_some());
}
#[test]
fn test_type_unit_rvalue_ref_type() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let rr = tu.build_rvalue_reference_type("int", "int&&");
assert!(rr.is_some());
}
#[test]
fn test_type_unit_const_volatile_restrict() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let c = tu.build_const_type("int", "const int");
assert!(c.is_some());
let v = tu.build_volatile_type("int", "volatile int");
assert!(v.is_some());
let r = tu.build_restrict_type("int", "restrict int");
assert!(r.is_some());
}
#[test]
fn test_type_unit_typedef() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let td = tu.build_typedef("size_t", "unsigned long");
assert!(td.is_some());
}
#[test]
fn test_type_unit_struct() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let s = tu.build_struct_type("Point", 8, vec![("x", "int", 0), ("y", "int", 4)]);
assert!(s.is_some());
let die = s.unwrap();
assert_eq!(die.children.len(), 2);
}
#[test]
fn test_type_unit_union() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let u = tu.build_union_type("Variant", 8, vec![("as_int", "int"), ("as_float", "float")]);
assert!(u.is_some());
}
#[test]
fn test_type_unit_class_with_inheritance() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
tu.build_struct_type("Base", 4, vec![("b", "int", 0)]);
let cls = tu.build_class_type(
"Derived",
8,
vec![("Base", 0, x86_dwarf_access::DW_ACCESS_public)],
vec![("d", "int", 4)],
);
assert!(cls.is_some());
}
#[test]
fn test_type_unit_enum() {
let mut tu = X86DwarfTypeUnit::new(8);
let e = tu.build_enum_type("Color", 4, vec![("RED", 0), ("GREEN", 1), ("BLUE", 2)]);
assert!(e.is_some());
let die = e.unwrap();
assert_eq!(die.children.len(), 3);
}
#[test]
fn test_type_unit_array() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let a = tu.build_array_type("int", "int[10]", 10);
assert!(a.is_some());
let die = a.unwrap();
assert_eq!(die.children.len(), 1);
}
#[test]
fn test_type_unit_subroutine_type() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let fn_type = tu.build_subroutine_type("int(*)(int,float)", "int", vec!["int", "float"]);
assert!(fn_type.is_some());
}
#[test]
fn test_type_unit_unknown_type() {
let mut tu = X86DwarfTypeUnit::new(8);
let ptr = tu.build_pointer_type("nonexistent", "not_built");
assert!(ptr.is_none());
}
#[test]
fn test_line_table_new_64() {
let table = X86DwarfLineTable::new_x86_64();
assert_eq!(table.address_size, 8);
assert!(table.entries.is_empty());
assert_eq!(table.version, 4);
}
#[test]
fn test_line_table_new_32() {
let table = X86DwarfLineTable::new_x86_32();
assert_eq!(table.address_size, 4);
}
#[test]
fn test_line_table_add_file() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/home/user/project");
table.add_file("main.c", 0);
assert_eq!(table.files.len(), 1);
assert_eq!(table.files[0].name, "main.c");
}
#[test]
fn test_line_table_add_entry() {
let mut table = X86DwarfLineTable::new_x86_64();
table.add_line(0x4000_1000, 1, 10, 5);
assert_eq!(table.entries.len(), 1);
assert_eq!(table.entries[0].line, 10);
assert_eq!(table.entries[0].column, 5);
assert_eq!(table.entries[0].address, 0x4000_1000);
}
#[test]
fn test_line_table_end_sequence() {
let mut table = X86DwarfLineTable::new_x86_64();
table.add_end_sequence(0x4000_2000);
assert!(table.entries[0].end_sequence);
}
#[test]
fn test_line_table_prologue_epilogue() {
let mut table = X86DwarfLineTable::new_x86_64();
table.add_prologue_end(0x4000_1000, 1, 5);
assert!(table.entries[0].prologue_end);
table.add_epilogue_begin(0x4000_1500, 1, 50);
assert!(table.entries[1].epilogue_begin);
}
#[test]
fn test_line_table_emit_empty() {
let table = X86DwarfLineTable::new_x86_64();
let data = table.emit();
assert!(data.len() > 10);
}
#[test]
fn test_line_table_emit_with_entries() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/src");
table.add_file("test.c", 0);
table.add_line(0x1000, 1, 1, 0);
table.add_line(0x1004, 1, 2, 0);
table.add_line(0x1008, 1, 3, 0);
table.add_end_sequence(0x1010);
let data = table.emit();
assert!(data.len() > 30);
}
#[test]
fn test_line_table_multiple_files() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/proj");
table.add_file("a.c", 0);
table.add_file("b.h", 0);
table.add_line(0x1000, 1, 10, 0);
table.add_line(0x1004, 2, 20, 0);
let data = table.emit();
assert!(data.len() > 20);
}
#[test]
fn test_cu_new_x86_64() {
let cu = X86DwarfCompileUnit::new_x86_64(
"test.c",
"/build",
"TestCompiler 1.0",
x86_dwarf_languages::DW_LANG_C99,
);
assert_eq!(cu.address_size, 8);
assert_eq!(cu.file_name, "test.c");
assert_eq!(cu.language, x86_dwarf_languages::DW_LANG_C99);
}
#[test]
fn test_cu_new_x86_32() {
let cu = X86DwarfCompileUnit::new_x86_32(
"app.c",
"/build32",
"GCC Compat 1.0",
x86_dwarf_languages::DW_LANG_C11,
);
assert_eq!(cu.address_size, 4);
}
#[test]
fn test_cu_emit_die_header() {
let cu = X86DwarfCompileUnit::new_x86_64(
"main.c",
"/tmp",
"Clang 15.0.0",
x86_dwarf_languages::DW_LANG_C_plus_plus_17,
);
let data = cu.emit_die_header();
assert!(!data.is_empty());
assert!(data.len() > 20);
}
#[test]
fn test_cu_add_subprogram() {
let mut cu = X86DwarfCompileUnit::new_x86_64("f.c", "/d", "cc", 2);
let sp = X86DwarfSubprogram::new("main", 0x4000, 0x100);
cu.add_subprogram(sp);
assert_eq!(cu.subprograms.len(), 1);
}
#[test]
fn test_cu_add_global_var() {
let mut cu = X86DwarfCompileUnit::new_x86_64("g.c", "/d", "cc", 2);
let var = X86DwarfVariable::new("g_counter", "int", Vec::new());
cu.add_global_variable(var);
assert_eq!(cu.global_variables.len(), 1);
}
#[test]
fn test_cu_all_languages() {
let langs = vec![
x86_dwarf_languages::DW_LANG_C89,
x86_dwarf_languages::DW_LANG_C,
x86_dwarf_languages::DW_LANG_C99,
x86_dwarf_languages::DW_LANG_C11,
x86_dwarf_languages::DW_LANG_C17,
x86_dwarf_languages::DW_LANG_C23,
x86_dwarf_languages::DW_LANG_C_plus_plus,
x86_dwarf_languages::DW_LANG_C_plus_plus_03,
x86_dwarf_languages::DW_LANG_C_plus_plus_11,
x86_dwarf_languages::DW_LANG_C_plus_plus_14,
x86_dwarf_languages::DW_LANG_C_plus_plus_17,
x86_dwarf_languages::DW_LANG_C_plus_plus_20,
x86_dwarf_languages::DW_LANG_Rust,
x86_dwarf_languages::DW_LANG_Swift,
x86_dwarf_languages::DW_LANG_Assembly,
];
for lang in langs {
let cu = X86DwarfCompileUnit::new_x86_64("t.c", "/d", "cc", lang);
assert_eq!(cu.language, lang);
}
}
#[test]
fn test_subprogram_new() {
let sp = X86DwarfSubprogram::new("foo", 0x4010_0000, 0x80);
assert_eq!(sp.name, "foo");
assert_eq!(sp.low_pc, 0x4010_0000);
assert_eq!(sp.high_pc, 0x80);
assert!(sp.parameters.is_empty());
assert!(sp.variables.is_empty());
}
#[test]
fn test_subprogram_with_params() {
let mut sp = X86DwarfSubprogram::new("bar", 0x5000, 0x200);
let param = X86DwarfVariable::new_parameter("x", "int", vec![0x55]);
sp.add_parameter(param);
assert_eq!(sp.parameters.len(), 1);
}
#[test]
fn test_subprogram_with_variables() {
let mut sp = X86DwarfSubprogram::new("baz", 0x6000, 0x100);
let var = X86DwarfVariable::new("local", "float", vec![0x91, 0x78]);
sp.add_variable(var);
assert_eq!(sp.variables.len(), 1);
}
#[test]
fn test_subprogram_with_lexical_block() {
let mut sp = X86DwarfSubprogram::new("block_fn", 0x7000, 0x300);
let block = X86DwarfLexicalBlock {
low_pc: 0x7100,
high_pc: 0x100,
variables: vec![X86DwarfVariable::new("inner", "int", vec![0x91, 0x70])],
};
sp.add_lexical_block(block);
assert_eq!(sp.lexical_blocks.len(), 1);
}
#[test]
fn test_subprogram_frame_base() {
let mut sp = X86DwarfSubprogram::new("fb_test", 0x8000, 0x50);
let fb = vec![0x91, 0x68]; sp.set_frame_base(fb.clone());
assert_eq!(sp.frame_base, Some(fb));
}
#[test]
fn test_subprogram_return_type() {
let mut sp = X86DwarfSubprogram::new("ret_test", 0x9000, 0x40);
sp.set_return_type("int");
assert_eq!(sp.return_type, Some("int".to_string()));
}
#[test]
fn test_subprogram_linkage_name() {
let mut sp = X86DwarfSubprogram::new("cpp_fn", 0xA000, 0x30);
sp.set_linkage_name("_Z6cpp_fnv");
assert_eq!(sp.linkage_name, Some("_Z6cpp_fnv".to_string()));
}
#[test]
fn test_subprogram_inlined() {
let mut sp = X86DwarfSubprogram::new("inline_me", 0xB000, 0x20);
sp.set_inlined(x86_dwarf_inline::DW_INL_inlined);
assert_eq!(sp.inline_code, Some(x86_dwarf_inline::DW_INL_inlined));
assert!(sp.is_declaration);
}
#[test]
fn test_subprogram_compute_frame_base_64() {
let sp = X86DwarfSubprogram::new("f", 0x1000, 0x50);
let fb = sp.compute_x86_64_frame_base(32);
assert_eq!(fb[0], dwarf_ops::DW_OP_call_frame_cfa as u8);
}
#[test]
fn test_subprogram_compute_frame_base_32() {
let sp = X86DwarfSubprogram::new("f", 0x1000, 0x50);
let fb = sp.compute_x86_32_frame_base(16);
assert_eq!(fb[0], dwarf_ops::DW_OP_call_frame_cfa as u8);
}
#[test]
fn test_inlined_subroutine_new() {
let inlined = X86DwarfInlinedSubroutine::new(0x500, 0x6000, 0x50, 1, 42);
assert_eq!(inlined.abstract_origin, 0x500);
assert_eq!(inlined.low_pc, 0x6000);
assert_eq!(inlined.call_line, 42);
}
#[test]
fn test_inlined_with_variables() {
let mut inlined = X86DwarfInlinedSubroutine::new(0x100, 0x2000, 0x30, 1, 10);
let var = X86DwarfVariable::new("inline_var", "int", vec![0x91, 0x60]);
inlined.variables.push(var);
assert_eq!(inlined.variables.len(), 1);
}
#[test]
fn test_frame_new_64() {
let frame = X86DwarfFrame::new_debug_frame_64();
assert!(frame.is_empty());
assert_eq!(frame.address_size, 8);
assert!(!frame.is_eh_frame);
}
#[test]
fn test_frame_new_32() {
let frame = X86DwarfFrame::new_debug_frame_32();
assert_eq!(frame.address_size, 4);
}
#[test]
fn test_frame_create_cie_64() {
let cie = X86DwarfFrame::create_x86_64_cie();
assert_eq!(cie.version, 4);
assert_eq!(cie.address_size, 8);
assert_eq!(cie.return_address_register, 16); assert_eq!(cie.code_alignment_factor, 1);
assert_eq!(cie.data_alignment_factor, -8);
}
#[test]
fn test_frame_create_cie_32() {
let cie = X86DwarfFrame::create_x86_32_cie();
assert_eq!(cie.address_size, 4);
assert_eq!(cie.return_address_register, 8);
}
#[test]
fn test_frame_add_cie() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
let cie = X86DwarfFrame::create_x86_64_cie();
let offset = frame.add_cie(cie);
assert_eq!(offset, 0);
assert_eq!(frame.len(), 1);
}
#[test]
fn test_frame_add_fde() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
let fde = FDEData {
cie_pointer: 0,
initial_location: 0x4000_0000,
address_range: 0x100,
instructions: Vec::new(),
lsda_pointer: None,
personality: None,
};
frame.add_fde(fde);
assert_eq!(frame.len(), 1);
}
#[test]
fn test_frame_emit_cie_only() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
frame.add_cie(X86DwarfFrame::create_x86_64_cie());
let data = frame.emit();
assert!(data.len() > 10);
assert_eq!(&data[data.len() - 4..], &[0, 0, 0, 0]); }
#[test]
fn test_frame_emit_cie_and_fde() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
frame.add_cie(X86DwarfFrame::create_x86_64_cie());
let fde = FDEData {
cie_pointer: 0,
initial_location: 0x4000_0000,
address_range: 0x80,
instructions: vec![cfa_ops::DW_CFA_nop],
lsda_pointer: None,
personality: None,
};
frame.add_fde(fde);
let data = frame.emit();
assert!(data.len() > 30);
}
#[test]
fn test_cfa_advance_loc_compact() {
let insn = X86DwarfFrame::cfa_advance_loc(1);
assert_eq!(insn[0], cfa_ops::DW_CFA_advance_loc | 1);
}
#[test]
fn test_cfa_def_cfa() {
let insn = X86DwarfFrame::cfa_def_cfa(7, 16);
assert_eq!(insn[0], cfa_ops::DW_CFA_def_cfa);
}
#[test]
fn test_cfa_def_cfa_offset() {
let insn = X86DwarfFrame::cfa_def_cfa_offset(32);
assert_eq!(insn[0], cfa_ops::DW_CFA_def_cfa_offset);
}
#[test]
fn test_cfa_offset() {
let insn = X86DwarfFrame::cfa_offset(6, 2);
assert_eq!(insn[0], cfa_ops::DW_CFA_offset | 6);
}
#[test]
fn test_cfa_restore() {
let insn = X86DwarfFrame::cfa_restore(3);
assert_eq!(insn[0], cfa_ops::DW_CFA_restore | 3);
}
#[test]
fn test_cfa_remember_restore_state() {
let r = X86DwarfFrame::cfa_remember_state();
assert_eq!(r[0], cfa_ops::DW_CFA_remember_state);
let s = X86DwarfFrame::cfa_restore_state();
assert_eq!(s[0], cfa_ops::DW_CFA_restore_state);
}
#[test]
fn test_cfa_nop() {
let n = X86DwarfFrame::cfa_nop();
assert_eq!(n[0], cfa_ops::DW_CFA_nop);
}
#[test]
fn test_cfa_gnu_args_size() {
let insn = X86DwarfFrame::cfa_gnu_args_size(8);
assert_eq!(insn[0], cfa_ops::DW_CFA_GNU_args_size);
}
#[test]
fn test_cfa_val_offset() {
let insn = X86DwarfFrame::cfa_val_offset(5, 3);
assert_eq!(insn[0], cfa_ops::DW_CFA_val_offset);
}
#[test]
fn test_cfa_val_expression() {
let expr = vec![0x50, 0x91, 0x78];
let insn = X86DwarfFrame::cfa_val_expression(3, &expr);
assert_eq!(insn[0], cfa_ops::DW_CFA_val_expression);
}
#[test]
fn test_cfa_expression() {
let expr = vec![0x70, 0x00, 0x08];
let insn = X86DwarfFrame::cfa_expression(16, &expr);
assert_eq!(insn[0], cfa_ops::DW_CFA_expression);
}
#[test]
fn test_cfa_def_cfa_expression() {
let expr = vec![0x74, 0x00];
let insn = X86DwarfFrame::cfa_def_cfa_expression(&expr);
assert_eq!(insn[0], cfa_ops::DW_CFA_def_cfa_expression);
}
#[test]
fn test_prologue_cfi_64() {
let insns = X86DwarfFrame::build_x86_64_prologue_cfi(32);
assert!(!insns.is_empty());
assert!(insns.len() > 3);
}
#[test]
fn test_epilogue_cfi_64() {
let insns = X86DwarfFrame::build_x86_64_epilogue_cfi();
assert!(!insns.is_empty());
}
#[test]
fn test_prologue_cfi_32() {
let insns = X86DwarfFrame::build_x86_32_prologue_cfi(16);
assert!(!insns.is_empty());
}
#[test]
fn test_generator_new_64() {
let r#gen = X86DwarfGenerator::new_x86_64();
assert!(r#gen.is_64bit);
assert_eq!(r#gen.address_size, 8);
assert_eq!(r#gen.dwarf_version, 4);
}
#[test]
fn test_generator_new_32() {
let r#gen = X86DwarfGenerator::new_x86_32();
assert!(!r#gen.is_64bit);
assert_eq!(r#gen.address_size, 4);
}
#[test]
fn test_generator_set_producer() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_producer("My Custom Compiler v2.0");
assert_eq!(r#gen.compile_unit.producer, "My Custom Compiler v2.0");
}
#[test]
fn test_generator_set_source_file() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("app.c", "/home/dev/project");
assert_eq!(r#gen.compile_unit.file_name, "app.c");
assert_eq!(r#gen.compile_unit.comp_dir, "/home/dev/project");
}
#[test]
fn test_generator_set_language() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_language(x86_dwarf_languages::DW_LANG_Rust);
assert_eq!(r#gen.compile_unit.language, x86_dwarf_languages::DW_LANG_Rust);
}
#[test]
fn test_generator_add_subprogram() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
let sp = X86DwarfSubprogram::new("test_fn", 0x4000_0000, 0x50);
r#gen.add_subprogram(sp);
assert_eq!(r#gen.compile_unit.subprograms.len(), 1);
}
#[test]
fn test_generator_register_types() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.register_base_types();
r#gen.register_x86_types();
assert!(r#gen.type_unit.offsets.contains_key("int"));
assert!(r#gen.type_unit.offsets.contains_key("__m128"));
}
#[test]
fn test_generator_emit_all_basic() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("hello.c", "/tmp");
r#gen.register_base_types();
let sp = {
let mut s = X86DwarfSubprogram::new("main", 0x4000_0000, 0x80);
s.set_frame_base(s.compute_x86_64_frame_base(32));
s.set_return_type("int");
s
};
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0000,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0080,
end_sequence: true,
..Default::default()
});
let sections = r#gen.emit_all();
assert!(sections.contains_key(".debug_info"));
assert!(sections.contains_key(".debug_abbrev"));
assert!(sections.contains_key(".debug_line"));
assert!(sections.contains_key(".debug_str"));
assert!(sections.contains_key(".debug_frame"));
assert!(sections.contains_key(".debug_aranges"));
for (name, data) in §ions {
assert!(!data.is_empty(), "Section {} is empty", name);
}
}
#[test]
fn test_generator_emit_32() {
let mut r#gen = X86DwarfGenerator::new_x86_32();
r#gen.set_source_file("small.c", "/build");
r#gen.register_base_types();
let sp = {
let mut s = X86DwarfSubprogram::new("_start", 0x0804_8000, 0x40);
s.set_frame_base(s.compute_x86_32_frame_base(16));
s
};
r#gen.add_subprogram(sp);
let sections = r#gen.emit_all();
assert!(sections.contains_key(".debug_info"));
let debug_info = §ions[".debug_info"];
let addr_size_byte = debug_info[11]; assert_eq!(addr_size_byte, 4);
}
#[test]
fn test_generator_complex_function() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("complex.c", "/proj");
r#gen.register_base_types();
let mut sp = X86DwarfSubprogram::new("compute", 0x4010_0000, 0x200);
sp.set_frame_base(vec![0x9c]); sp.set_return_type("double");
sp.set_linkage_name("_Z7computev");
sp.add_parameter(X86DwarfVariable::new_parameter("a", "int", vec![0x55])); sp.add_parameter(X86DwarfVariable::new_parameter("b", "double", vec![0x61]));
let mut loc = X86DwarfLocation::new_x86_64();
loc.local_var_below_rbp(8);
sp.add_variable(X86DwarfVariable::new("result", "double", loc.finish()));
let mut inner_loc = X86DwarfLocation::new_x86_64();
inner_loc.local_var_below_rbp(16);
sp.add_lexical_block(X86DwarfLexicalBlock {
low_pc: 0x4010_0040,
high_pc: 0x80,
variables: vec![X86DwarfVariable::new("temp", "int", inner_loc.finish())],
});
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: 10,
column: 0,
file: 1,
address: 0x4010_0000,
is_stmt: true,
prologue_end: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 11,
column: 5,
file: 1,
address: 0x4010_0010,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 12,
column: 5,
file: 1,
address: 0x4010_0040,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 15,
column: 1,
file: 1,
address: 0x4010_0200,
is_stmt: true,
end_sequence: true,
..Default::default()
});
let sections = r#gen.emit_all();
for section in &[
".debug_info",
".debug_abbrev",
".debug_line",
".debug_str",
".debug_frame",
".debug_aranges",
".debug_ranges",
] {
assert!(
sections.contains_key(*section),
"Missing section: {}",
section
);
}
}
#[test]
fn test_generator_multiple_functions() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("multi.c", "/src");
r#gen.register_base_types();
for i in 0..5 {
let base_addr = 0x4000_0000 + i * 0x1000;
let mut sp = X86DwarfSubprogram::new(&format!("fn{}", i), base_addr, 0x100);
sp.set_frame_base(vec![0x9c]);
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: (i + 1) as u32,
column: 0,
file: 1,
address: base_addr,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: (i + 1) as u32,
column: 0,
file: 1,
address: base_addr + 0x100,
end_sequence: true,
..Default::default()
});
}
let sections = r#gen.emit_all();
assert!(!sections[".debug_info"].is_empty());
}
#[test]
fn test_generator_with_global_variables() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("global.c", "/src");
r#gen.register_base_types();
let mut loc = X86DwarfLocation::new_x86_64();
loc.addr(0x6000_0000);
let var = X86DwarfVariable::new("global_var", "int", loc.finish());
r#gen.add_global_variable(var);
let sections = r#gen.emit_all();
assert!(sections.contains_key(".debug_info"));
}
#[test]
fn test_generator_eh_frame() {
let mut frame = X86DwarfFrame::new_eh_frame_64();
assert!(frame.is_eh_frame);
let cie = X86DwarfFrame::create_x86_64_cie();
frame.add_cie(cie);
let data = frame.emit();
assert!(data.len() > 10);
}
#[test]
fn test_location_convert_reinterpret() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.convert(0x100);
loc.reinterpret(0x200);
let bytes = loc.finish();
assert!(bytes.len() > 4);
}
#[test]
fn test_location_x86_reg_mapping() {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(RAX);
assert_eq!(dwarf, 0);
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(RBP);
assert_eq!(dwarf, 6);
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(RSP);
assert_eq!(dwarf, 7);
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(XMM0);
assert_eq!(dwarf, 17);
}
#[test]
fn test_location_gnu_push_tls() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.gnu_push_tls_address();
assert_eq!(loc.bytes[0], x86_dwarf_ops_ext::DW_OP_GNU_push_tls_address);
}
#[test]
fn test_expression_max_steps() {
let mut expr = X86DwarfExpression::new_x86_64();
expr.max_steps = 2;
let program = vec![
dwarf_ops::DW_OP_lit0 as u8 + 1,
dwarf_ops::DW_OP_lit0 as u8 + 2,
dwarf_ops::DW_OP_plus as u8,
dwarf_ops::DW_OP_lit0 as u8 + 3,
dwarf_ops::DW_OP_plus as u8,
];
let result = expr.evaluate(&program);
assert!(result.is_err());
}
#[test]
fn test_frame_multiple_functions() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
let cie = X86DwarfFrame::create_x86_64_cie();
frame.add_cie(cie);
for i in 0..3 {
frame.add_fde(FDEData {
cie_pointer: 0,
initial_location: 0x4000_0000 + i * 0x1000,
address_range: 0x100,
instructions: vec![cfa_ops::DW_CFA_nop],
lsda_pointer: None,
personality: None,
});
}
let data = frame.emit();
assert!(data.len() > 50);
}
#[test]
fn test_abbrev_compile_unit_ranges() {
let mut abbrev = X86DwarfAbbrev::new();
let code = abbrev.add_compile_unit_ranges_abbrev();
assert_eq!(code, 1);
assert_eq!(abbrev.len(), 1);
}
#[test]
fn test_abbrev_subprogram_external() {
let mut abbrev = X86DwarfAbbrev::new();
let code = abbrev.add_subprogram_external_abbrev();
assert!(code > 0);
}
#[test]
fn test_abbrev_subprogram_inlined() {
let mut abbrev = X86DwarfAbbrev::new();
let code = abbrev.add_subprogram_inlined_abbrev();
assert!(code > 0);
}
#[test]
fn test_arange_multiple_cus() {
let mut ar = X86DwarfARange::new(8);
ar.add_cu(
0,
vec![ARangeEntry {
cu_offset: 0,
address: 0x1000,
length: 0x100,
}],
);
ar.add_cu(
0x100,
vec![ARangeEntry {
cu_offset: 0x100,
address: 0x2000,
length: 0x200,
}],
);
let data = ar.emit();
assert!(data.len() > 40);
}
#[test]
fn test_range_list_empty() {
let rl = X86DwarfRangeList::new(8, true);
let data = rl.emit_rnglists_dwarf5();
assert!(data.len() >= 12);
}
#[test]
fn test_expression_eval_const_u8() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![dwarf_ops::DW_OP_const1u as u8, 42];
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(42));
}
#[test]
fn test_expression_eval_const_i8() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![dwarf_ops::DW_OP_const1s as u8, 0xFEu8]; let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(-2));
}
#[test]
fn test_expression_eval_const_u16() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = vec![dwarf_ops::DW_OP_const2u as u8];
program.extend_from_slice(&0x1234u16.to_le_bytes());
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0x1234));
}
#[test]
fn test_expression_eval_const_u64() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = vec![dwarf_ops::DW_OP_const8u as u8];
program.extend_from_slice(&0xDEAD_BEEF_CAFE_BABEu64.to_le_bytes());
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0xDEAD_BEEF_CAFE_BABE_i64 as i64));
}
#[test]
fn test_expression_eval_eq_ne() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 5);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 5);
program.push(dwarf_ops::DW_OP_eq as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(1));
}
#[test]
fn test_expression_eval_le_ge() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 3);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 5);
program.push(dwarf_ops::DW_OP_le as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(1));
let mut program2 = Vec::new();
program2.push(dwarf_ops::DW_OP_lit0 as u8 + 5);
program2.push(dwarf_ops::DW_OP_lit0 as u8 + 3);
program2.push(dwarf_ops::DW_OP_ge as u8);
let result2 = expr.evaluate(&program2).unwrap();
assert_eq!(result2, Some(1));
}
#[test]
fn test_expression_eval_and_or_xor() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 15);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 15); program.clear();
program.push(dwarf_ops::DW_OP_const1u as u8);
program.push(0xFF);
program.push(dwarf_ops::DW_OP_const1u as u8);
program.push(0x0F);
program.push(dwarf_ops::DW_OP_and as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0x0F));
}
#[test]
fn test_expression_eval_shl_shr() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 1);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 3);
program.push(dwarf_ops::DW_OP_shl as u8); let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(8));
let mut program2 = Vec::new();
program2.push(dwarf_ops::DW_OP_lit0 as u8 + 8);
program2.push(dwarf_ops::DW_OP_lit0 as u8 + 2);
program2.push(dwarf_ops::DW_OP_shr as u8);
let result2 = expr.evaluate(&program2).unwrap();
assert_eq!(result2, Some(2));
}
#[test]
fn test_expression_eval_neg_abs_not() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = vec![dwarf_ops::DW_OP_const1s as u8, 42];
program.push(dwarf_ops::DW_OP_neg as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(-42));
let mut program2 = vec![dwarf_ops::DW_OP_const1s as u8, 0xFBu8]; program2.push(dwarf_ops::DW_OP_abs as u8);
let result2 = expr.evaluate(&program2).unwrap();
assert_eq!(result2, Some(5));
let mut program3 = vec![dwarf_ops::DW_OP_lit0 as u8];
program3.push(dwarf_ops::DW_OP_not as u8);
let result3 = expr.evaluate(&program3).unwrap();
assert_eq!(result3, Some(!0i64));
}
#[test]
fn test_expression_eval_implicit_value() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = vec![dwarf_ops::DW_OP_implicit_value as u8];
let data: [u8; 4] = [0x78, 0x56, 0x34, 0x12];
program.push(4); program.extend_from_slice(&data);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0x1234_5678));
}
#[test]
fn test_expression_eval_skip_bra() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = vec![
dwarf_ops::DW_OP_skip as u8,
3,
0, dwarf_ops::DW_OP_lit0 as u8 + 3, dwarf_ops::DW_OP_lit0 as u8 + 5, ];
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(5));
}
#[test]
fn test_expression_eval_piece_operations() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
program.push(dwarf_ops::DW_OP_lit0 as u8 + 10);
program.push(dwarf_ops::DW_OP_piece as u8);
program.push(4); program.push(dwarf_ops::DW_OP_lit0 as u8 + 20);
program.push(dwarf_ops::DW_OP_piece as u8);
program.push(4);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(20));
}
#[test]
fn test_expression_compute_frame_base() {
let mut expr = X86DwarfExpression::new_x86_64();
expr.read_register = Some(|reg| if reg == 6 { Some(0x7FFF_FF00) } else { None });
let fb = expr.compute_frame_base(6, 16);
assert_eq!(fb, 0x7FFF_FF10);
}
#[test]
fn test_expression_tls_address() {
let mut expr = X86DwarfExpression::new_x86_64();
expr.tls_base = Some(0x7F00_0000_0000);
let program = vec![
dwarf_ops::DW_OP_lit0 as u8 + 0x10,
dwarf_ops::DW_OP_form_tls_address as u8,
];
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0x7F00_0000_0010));
}
#[test]
fn test_location_empty_expression() {
let loc = X86DwarfLocation::new_x86_64();
assert!(loc.is_empty());
assert_eq!(loc.len(), 0);
assert!(loc.finish().is_empty());
}
#[test]
fn test_location_max_values() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.const_u64(u64::MAX);
let bytes = loc.finish();
assert_eq!(bytes.len(), 9);
let mut loc2 = X86DwarfLocation::new_x86_64();
loc2.const_i64(i64::MIN);
let bytes2 = loc2.finish();
assert_eq!(bytes2.len(), 9);
}
#[test]
fn test_location_fbreg_zero() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.fbreg(0);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_fbreg as u8);
assert_eq!(bytes[1], 0); }
#[test]
fn test_string_table_large() {
let mut table = X86DwarfStringTable::new();
for i in 0..100 {
table.add(&format!("variable_name_{}", i));
}
assert_eq!(table.len(), 100);
let data = table.emit();
assert!(data.len() > 1000);
}
#[test]
fn test_string_table_empty_string() {
let table = X86DwarfStringTable::new();
assert_eq!(table.get_offset(""), Some(0));
}
#[test]
fn test_addr_table_multiple_addresses() {
let mut table = X86DwarfAddrTable::new(8);
for i in 0..50 {
table.add(0x4000_0000 + i * 0x1000);
}
assert_eq!(table.len(), 50);
let data = table.emit();
assert!(data.len() > 400);
}
#[test]
fn test_range_list_large() {
let mut rl = X86DwarfRangeList::new(8, false);
let mut ranges = Vec::new();
for i in 0..20 {
ranges.push(AddrRange {
low_pc: 0x1000 + i * 0x100,
high_pc: 0x1000 + (i + 1) * 0x100,
});
}
rl.add(ranges);
let data = rl.emit_ranges_dwarf4();
assert_eq!(data.len(), 42 * 8);
}
#[test]
fn test_line_table_special_opcodes() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/src");
table.add_file("test.c", 0);
for i in 0..10 {
table.add_line(0x1000 + i * 4, 1, (i + 1) as u32, 0);
}
table.add_end_sequence(0x1030);
let data = table.emit();
assert!(data.len() > 20);
}
#[test]
fn test_line_table_column_info() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/proj");
table.add_file("col.c", 0);
table.add_line(0x1000, 1, 10, 5);
table.add_line(0x1004, 1, 10, 20);
table.add_line(0x1008, 1, 11, 3);
let data = table.emit();
assert!(data.len() > 20);
}
#[test]
fn test_cu_string_integration() {
let mut cu = X86DwarfCompileUnit::new_x86_64(
"integration_test.c",
"/home/dev/integration",
"LLVM-Native Integration Test",
x86_dwarf_languages::DW_LANG_C_plus_plus_20,
);
let die_data = cu.emit_die_header();
let producer_bytes = b"LLVM-Native Integration Test";
let found = die_data
.windows(producer_bytes.len())
.any(|w| w == producer_bytes);
assert!(found, "Producer string not found in DIE header");
}
#[test]
fn test_subprogram_nested_blocks() {
let mut sp = X86DwarfSubprogram::new("nested", 0x5000, 0x400);
let inner_var = X86DwarfVariable::new("inner_x", "int", vec![0x91, 0x70]);
let inner_block = X86DwarfLexicalBlock {
low_pc: 0x5200,
high_pc: 0x80,
variables: vec![inner_var],
};
let outer_var = X86DwarfVariable::new("outer_y", "double", vec![0x91, 0x60]);
sp.add_lexical_block(X86DwarfLexicalBlock {
low_pc: 0x5100,
high_pc: 0x200,
variables: vec![outer_var],
});
sp.add_lexical_block(inner_block);
assert_eq!(sp.lexical_blocks.len(), 2);
}
#[test]
fn test_frame_x86_64_callee_saved() {
let insns = X86DwarfFrame::x86_64_save_callee_saved(3, -24);
assert!(!insns.is_empty());
assert_eq!(insns[0], cfa_ops::DW_CFA_advance_loc | 1);
}
#[test]
fn test_generator_section_offsets() {
let r#gen = X86DwarfGenerator::new_x86_64();
assert_eq!(r#gen.section_offsets.debug_info, 0);
assert_eq!(r#gen.section_offsets.debug_abbrev, 0);
assert_eq!(r#gen.section_offsets.debug_line, 0);
assert_eq!(r#gen.section_offsets.debug_str, 0);
}
#[test]
fn test_location_all_x86_regs_mappable() {
let gprs = [
RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15,
];
for ® in &gprs {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(reg);
assert!(
dwarf < 256,
"Register {} maps to invalid DWARF {}",
reg,
dwarf
);
}
}
#[test]
fn test_location_ymm_zmm_aliasing() {
let xmm0_dwarf = X86DwarfLocation::x86_reg_to_dwarf(XMM0);
let ymm0_dwarf = X86DwarfLocation::x86_reg_to_dwarf(YMM0);
let zmm0_dwarf = X86DwarfLocation::x86_reg_to_dwarf(ZMM0);
assert_eq!(xmm0_dwarf, ymm0_dwarf);
assert_eq!(xmm0_dwarf, zmm0_dwarf);
}
#[test]
fn test_type_unit_get_offset() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let int_off = tu.get_type_offset("int");
assert!(int_off.is_some());
let void_off = tu.get_type_offset("void");
assert!(void_off.is_some());
let nonexistent = tu.get_type_offset("nonexistent_type");
assert!(nonexistent.is_none());
}
#[test]
fn test_type_unit_build_base_type_die_content() {
let die =
X86DwarfTypeUnit::build_base_type("long double", x86_dwarf_encodings::DW_ATE_float, 16);
assert_eq!(die.tag, dwarf_tags::DW_TAG_base_type);
let has_name = die
.attributes
.iter()
.any(|(attr, _)| *attr == dwarf_attributes::DW_AT_name);
let has_encoding = die
.attributes
.iter()
.any(|(attr, _)| *attr == dwarf_attributes::DW_AT_encoding);
let has_size = die
.attributes
.iter()
.any(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert!(has_name);
assert!(has_encoding);
assert!(has_size);
}
#[test]
fn test_abbrev_children_flag() {
let mut abbrev = X86DwarfAbbrev::new();
abbrev.add(AbbrevDecl {
tag: dwarf_tags::DW_TAG_structure_type,
has_children: true,
attributes: vec![(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string)],
});
let data = abbrev.emit();
assert!(data.len() >= 6);
}
#[test]
fn test_type_unit_signature_default() {
let tu = X86DwarfTypeUnit::new(8);
assert_eq!(tu.type_signature, 0);
}
#[test]
fn test_uleb128_roundtrip() {
let values = [
0u64,
1,
127,
128,
255,
256,
16383,
16384,
u32::MAX as u64,
u64::MAX,
];
for &val in &values {
let encoded = uleb128(val);
let mut test_buf = Vec::new();
encode_uleb128(&mut test_buf, val);
assert_eq!(encoded, test_buf, "ULEB128 mismatch for {}", val);
}
}
#[test]
fn test_sleb128_roundtrip() {
let values = [
0i64,
1,
-1,
127,
-127,
128,
-128,
16383,
-16383,
i32::MAX as i64,
i32::MIN as i64,
i64::MAX,
i64::MIN,
];
for &val in &values {
let encoded = sleb128(val);
let mut test_buf = Vec::new();
encode_sleb128(&mut test_buf, val);
assert_eq!(encoded, test_buf, "SLEB128 mismatch for {}", val);
}
}
#[test]
fn test_full_dwarf_generation_roundtrip() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("roundtrip.c", "/test");
r#gen.set_producer("Roundtrip Test Compiler");
r#gen.set_language(x86_dwarf_languages::DW_LANG_C11);
r#gen.register_base_types();
r#gen.register_x86_types();
let mut sp = X86DwarfSubprogram::new("roundtrip_fn", 0x4000_0000, 0x100);
sp.set_frame_base(vec![0x9c]);
sp.set_return_type("int");
sp.set_linkage_name("_Z12roundtrip_fnv");
sp.add_parameter(X86DwarfVariable::new_parameter("x", "int", vec![0x55]));
sp.add_variable(X86DwarfVariable::new("local", "double", vec![0x91, 0x78]));
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0000,
is_stmt: true,
prologue_end: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 2,
column: 5,
file: 1,
address: 0x4000_0050,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 3,
column: 5,
file: 1,
address: 0x4000_00a0,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 4,
column: 1,
file: 1,
address: 0x4000_0100,
is_stmt: true,
end_sequence: true,
..Default::default()
});
let sections = r#gen.emit_all();
let expected_sections = [
".debug_info",
".debug_abbrev",
".debug_line",
".debug_str",
".debug_str_offsets",
".debug_frame",
".debug_aranges",
".debug_ranges",
".debug_addr",
];
for section in &expected_sections {
assert!(
sections.contains_key(*section),
"Missing section: {}",
section
);
assert!(!sections[*section].is_empty(), "Empty section: {}", section);
}
let info = §ions[".debug_info"];
let producer = b"Roundtrip Test Compiler";
assert!(info.windows(producer.len()).any(|w| w == producer));
let fn_name = b"roundtrip_fn";
assert!(info.windows(fn_name.len()).any(|w| w == fn_name));
}
#[test]
fn test_full_dwarf_32bit() {
let mut r#gen = X86DwarfGenerator::new_x86_32();
r#gen.set_source_file("i386_test.c", "/build32");
r#gen.set_language(x86_dwarf_languages::DW_LANG_C99);
r#gen.register_base_types();
let mut sp = X86DwarfSubprogram::new("_i386_fn", 0x0804_8000, 0x80);
sp.set_frame_base(vec![0x9c]);
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x0804_8000,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x0804_8080,
end_sequence: true,
..Default::default()
});
let sections = r#gen.emit_all();
let info = §ions[".debug_info"];
let addr_size_byte = info[11];
assert_eq!(addr_size_byte, 4, "Expected 32-bit address size");
}
#[test]
fn test_location_complex_composite() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.reg(RAX); loc.piece(8); loc.fbreg(-16); loc.piece(8); let bytes = loc.finish();
assert!(bytes.len() > 5);
assert!(bytes.contains(&(dwarf_ops::DW_OP_reg0 as u8)));
assert!(bytes.contains(&dwarf_ops::DW_OP_fbreg));
assert!(bytes.contains(&dwarf_ops::DW_OP_piece));
}
#[test]
fn test_frame_advance_loc_large_delta() {
let insns = X86DwarfFrame::cfa_advance_loc(0x80);
assert_eq!(insns[0], cfa_ops::DW_CFA_advance_loc1);
assert_eq!(insns[1], 0x80);
}
#[test]
fn test_frame_advance_loc_huge_delta() {
let insns = X86DwarfFrame::cfa_advance_loc(0x400);
assert_eq!(insns[0], cfa_ops::DW_CFA_advance_loc2);
}
#[test]
fn test_frame_offset_extended_reg() {
let insns = X86DwarfFrame::cfa_offset(64, 5);
assert_eq!(insns[0], cfa_ops::DW_CFA_offset_extended);
}
#[test]
fn test_frame_restore_extended_reg() {
let insns = X86DwarfFrame::cfa_restore(64);
assert_eq!(insns[0], cfa_ops::DW_CFA_restore_extended);
}
#[test]
fn test_frame_def_cfa_register() {
let insns = X86DwarfFrame::cfa_def_cfa_register(6); assert_eq!(insns[0], cfa_ops::DW_CFA_def_cfa_register);
assert_eq!(insns[1], 0x06);
}
#[test]
fn test_range_list_dwarf5_specific() {
let mut rl = X86DwarfRangeList::new(8, true);
rl.add(vec![
AddrRange {
low_pc: 0x4000,
high_pc: 0x5000,
},
AddrRange {
low_pc: 0x6000,
high_pc: 0x7000,
},
]);
let data = rl.emit_rnglists_dwarf5();
let has_rle = data.windows(1).any(|w| w[0] == 0x04);
assert!(has_rle);
}
#[test]
fn test_mixed_locations_x86() {
let patterns = [
("reg", vec![0x55u8]), ("breg", vec![0x76, 0x08]), ("fbreg", vec![0x91, 0x70]), ("addr", {
let mut v = vec![dwarf_ops::DW_OP_addr as u8];
v.extend_from_slice(&0x6000_0000u64.to_le_bytes());
v
}),
];
for (name, expected_prefix) in &patterns {
match name.as_ref() {
"reg" => {
let mut loc = X86DwarfLocation::new_x86_64();
loc.reg(RDI);
let bytes = loc.finish();
assert_eq!(&bytes[..expected_prefix.len()], expected_prefix.as_slice());
}
"breg" => {
let mut loc = X86DwarfLocation::new_x86_64();
loc.breg(RBP, 8);
let bytes = loc.finish();
assert_eq!(bytes[0], 0x76);
assert_eq!(bytes[1], 0x08);
}
"fbreg" => {
let mut loc = X86DwarfLocation::new_x86_64();
loc.fbreg(-16);
let bytes = loc.finish();
assert_eq!(bytes[0], 0x91);
assert_eq!(bytes[1], 0x70);
}
"addr" => {
let mut loc = X86DwarfLocation::new_x86_64();
loc.addr(0x6000_0000);
let bytes = loc.finish();
assert_eq!(&bytes[..expected_prefix.len()], expected_prefix.as_slice());
}
_ => {}
}
}
}
#[test]
fn test_string_table_unicode() {
let mut table = X86DwarfStringTable::new();
table.add("日本語変数");
table.add("café");
table.add("✨emoji");
let data = table.emit();
let found_japanese = data.windows(3).any(|w| w == &[0xE6, 0x97, 0xA5]);
assert!(found_japanese);
}
#[test]
fn test_cu_producer_with_special_chars() {
let cu = X86DwarfCompileUnit::new_x86_64(
"test.c",
"/path/with spaces/",
"Compiler v1.0 (beta)",
x86_dwarf_languages::DW_LANG_C_plus_plus,
);
let data = cu.emit_die_header();
let expected = b"Compiler v1.0 (beta)";
assert!(data.windows(expected.len()).any(|w| w == expected));
}
#[test]
fn test_multiple_compile_units_different_languages() {
let cu_c = X86DwarfCompileUnit::new_x86_64(
"c_file.c",
"/src",
"cc",
x86_dwarf_languages::DW_LANG_C17,
);
let cu_cpp = X86DwarfCompileUnit::new_x86_64(
"cpp_file.cpp",
"/src",
"c++",
x86_dwarf_languages::DW_LANG_C_plus_plus_20,
);
let cu_rust = X86DwarfCompileUnit::new_x86_64(
"rust_file.rs",
"/src",
"rustc",
x86_dwarf_languages::DW_LANG_Rust,
);
assert_ne!(cu_c.language, cu_cpp.language);
assert_ne!(cu_cpp.language, cu_rust.language);
assert_ne!(cu_c.language, cu_rust.language);
}
#[test]
fn test_string_table_dedup_performance() {
let mut table = X86DwarfStringTable::new();
for _ in 0..1000 {
table.add("repeated_string");
}
assert_eq!(table.len(), 1);
assert_eq!(table.get_offset("repeated_string"), Some(1));
}
#[test]
fn test_abbrev_dedup_performance() {
let mut abbrev = X86DwarfAbbrev::new();
for _ in 0..100 {
abbrev.add_base_type_abbrev();
abbrev.add_pointer_type_abbrev();
abbrev.add_member_abbrev();
}
assert_eq!(abbrev.len(), 3);
}
#[test]
#[should_panic]
fn test_location_lit_out_of_range() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.lit(32);
}
#[test]
fn test_location_lit_at_boundary() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.lit(0);
assert_eq!(loc.bytes[0], dwarf_ops::DW_OP_lit0 as u8);
let mut loc2 = X86DwarfLocation::new_x86_64();
loc2.lit(31);
assert_eq!(loc2.bytes[0], dwarf_ops::DW_OP_lit0 as u8 + 31);
}
#[test]
fn test_expression_eval_empty_program() {
let mut expr = X86DwarfExpression::new_x86_64();
let result = expr.evaluate(&[]).unwrap();
assert_eq!(result, None);
}
#[test]
fn test_frame_empty_instructions() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
let cie = X86DwarfFrame::create_x86_64_cie();
frame.add_cie(cie);
let fde = FDEData {
cie_pointer: 0,
initial_location: 0x4000_0000,
address_range: 0,
instructions: Vec::new(),
lsda_pointer: None,
personality: None,
};
frame.add_fde(fde);
let data = frame.emit();
assert!(data.len() > 10);
}
#[test]
fn test_type_unit_bool_encoding() {
let die = X86DwarfTypeUnit::build_base_type("bool", x86_dwarf_encodings::DW_ATE_boolean, 1);
let encoding_attr = die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_encoding);
assert!(encoding_attr.is_some());
if let Some((_, data)) = encoding_attr {
assert_eq!(data[0], x86_dwarf_encodings::DW_ATE_boolean);
}
}
#[test]
fn test_expression_eval_overflow_handling() {
let mut expr = X86DwarfExpression::new_x86_64();
let mut program = Vec::new();
let max_bytes = (i64::MAX as u64).to_le_bytes().to_vec();
program.push(dwarf_ops::DW_OP_const8u as u8);
program.extend_from_slice(&max_bytes);
program.push(dwarf_ops::DW_OP_lit0 as u8 + 1);
program.push(dwarf_ops::DW_OP_plus as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(i64::MIN));
}
#[test]
fn test_generator_elf_section_names() {
let r#gen = X86DwarfGenerator::new_x86_64();
let section_names = [
".debug_info",
".debug_abbrev",
".debug_line",
".debug_str",
".debug_frame",
".debug_aranges",
".debug_ranges",
".debug_addr",
];
for name in §ion_names {
assert!(name.starts_with(".debug_"), "Not a debug section: {}", name);
}
}
#[test]
fn test_reg_info_dwarf_num_consistency() {
assert_eq!(
X86DwarfLocation::x86_reg_to_dwarf(RAX),
X86RegisterInfo::get_dwarf_num(RAX) as u16
);
assert_eq!(
X86DwarfLocation::x86_reg_to_dwarf(RBP),
X86RegisterInfo::get_dwarf_num(RBP) as u16
);
assert_eq!(
X86DwarfLocation::x86_reg_to_dwarf(RSP),
X86RegisterInfo::get_dwarf_num(RSP) as u16
);
assert_eq!(
X86DwarfLocation::x86_reg_to_dwarf(XMM0),
X86RegisterInfo::get_dwarf_num(XMM0) as u16
);
assert_eq!(
X86DwarfLocation::x86_reg_to_dwarf(XMM15),
X86RegisterInfo::get_dwarf_num(XMM15) as u16
);
}
#[test]
fn test_all_base_types_registered() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let expected_types = [
"int",
"unsigned int",
"short",
"unsigned short",
"char",
"signed char",
"unsigned char",
"long",
"long long",
"unsigned long",
"unsigned long long",
"float",
"double",
"long double",
"bool",
"_Bool",
"void",
"wchar_t",
];
for t in &expected_types {
assert!(tu.offsets.contains_key(*t), "Missing type: {}", t);
}
}
#[test]
fn test_all_x86_types_registered() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_x86_specific_types();
let expected_x86_types = [
"__int128",
"unsigned __int128",
"__float80",
"__float128",
"__m64",
"__m128",
"__m128d",
"__m128i",
"__m256",
"__m256d",
"__m256i",
"__m512",
"__m512d",
"__m512i",
"__m128bh",
"__m256bh",
"__m512bh",
];
for t in &expected_x86_types {
assert!(tu.offsets.contains_key(*t), "Missing X86 type: {}", t);
}
}
#[test]
fn test_struct_with_all_basic_member_types() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let members = vec![
("a", "char", 0),
("b", "short", 2),
("c", "int", 4),
("d", "long", 8),
("e", "float", 16),
("f", "double", 24),
];
let s = tu.build_struct_type("AllTypes", 32, members);
assert!(s.is_some());
let die = s.unwrap();
assert_eq!(die.children.len(), 6);
}
#[test]
fn test_enum_all_values() {
let mut tu = X86DwarfTypeUnit::new(8);
let enumerators = vec![
("ZERO", 0i64),
("ONE", 1),
("NEG", -1),
("BIG", 0x7FFF_FFFF),
("NEG_BIG", -0x8000_0000i64),
];
let e = tu.build_enum_type("WideEnum", 4, enumerators);
assert!(e.is_some());
let die = e.unwrap();
assert_eq!(die.children.len(), 5);
}
#[test]
fn test_line_table_version_5_flag() {
let mut cu = X86DwarfCompileUnit::new_x86_64("v5.c", "/src", "cc", 2);
assert!(!cu.use_dwarf5_line);
cu.use_dwarf5_line = true;
assert!(cu.use_dwarf5_line);
}
#[test]
fn test_range_list_dwarf4_vs_5() {
let mut rl4 = X86DwarfRangeList::new(8, false);
let mut rl5 = X86DwarfRangeList::new(8, true);
let ranges = vec![AddrRange {
low_pc: 0x1000,
high_pc: 0x2000,
}];
rl4.add(ranges.clone());
rl5.add(ranges);
let d4 = rl4.emit();
let d5 = rl5.emit();
assert_ne!(d4, d5);
}
#[test]
fn test_str_offsets_header_format() {
let mut table = X86DwarfStringTable::new();
table.add("test_str");
let data = table.emit_str_offsets(4);
assert!(data.len() >= 12);
let version = u16::from_le_bytes([data[4], data[5]]);
assert_eq!(version, 2);
}
#[test]
fn test_access_constants() {
assert_eq!(x86_dwarf_access::DW_ACCESS_public, 1);
assert_eq!(x86_dwarf_access::DW_ACCESS_protected, 2);
assert_eq!(x86_dwarf_access::DW_ACCESS_private, 3);
}
#[test]
fn test_visibility_constants() {
assert_eq!(x86_dwarf_visibility::DW_VIS_local, 1);
assert_eq!(x86_dwarf_visibility::DW_VIS_exported, 2);
assert_eq!(x86_dwarf_visibility::DW_VIS_qualified, 3);
}
#[test]
fn test_virtuality_constants() {
assert_eq!(x86_dwarf_virtuality::DW_VIRTUALITY_none, 0);
assert_eq!(x86_dwarf_virtuality::DW_VIRTUALITY_virtual, 1);
assert_eq!(x86_dwarf_virtuality::DW_VIRTUALITY_pure_virtual, 2);
}
#[test]
fn test_inline_constants() {
assert_eq!(x86_dwarf_inline::DW_INL_not_inlined, 0);
assert_eq!(x86_dwarf_inline::DW_INL_inlined, 1);
assert_eq!(x86_dwarf_inline::DW_INL_declared_not_inlined, 2);
assert_eq!(x86_dwarf_inline::DW_INL_declared_inlined, 3);
}
#[test]
fn test_class_multiple_inheritance() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
tu.build_struct_type("Base1", 4, vec![("b1", "int", 0)]);
tu.build_struct_type("Base2", 4, vec![("b2", "int", 0)]);
let cls = tu.build_class_type(
"MultiDerived",
12,
vec![
("Base1", 0, x86_dwarf_access::DW_ACCESS_public),
("Base2", 4, x86_dwarf_access::DW_ACCESS_public),
],
vec![("d", "int", 8)],
);
assert!(cls.is_some());
let die = cls.unwrap();
assert_eq!(die.children.len(), 3);
}
#[test]
fn test_class_protected_private_inheritance() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
tu.build_struct_type("Base", 4, vec![("b", "int", 0)]);
let protected = tu.build_class_type(
"ProtectedDerived",
8,
vec![("Base", 0, x86_dwarf_access::DW_ACCESS_protected)],
vec![("d", "int", 4)],
);
assert!(protected.is_some());
let private = tu.build_class_type(
"PrivateDerived",
8,
vec![("Base", 0, x86_dwarf_access::DW_ACCESS_private)],
vec![("d", "int", 4)],
);
assert!(private.is_some());
}
#[test]
fn test_array_zero_elements() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let a = tu.build_array_type("int", "int[0]", 0);
assert!(a.is_some());
}
#[test]
fn test_array_multi_dimensional() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let inner = tu.build_array_type("int", "int[4]", 4);
assert!(inner.is_some());
let outer = tu.build_array_type("int[4]", "int[3][4]", 3);
assert!(outer.is_some());
}
#[test]
fn test_subroutine_type_variadic() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let fn_type = tu.build_subroutine_type("void(*)(int,...)", "void", vec!["int"]);
assert!(fn_type.is_some());
}
#[test]
fn test_subroutine_type_no_params() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let fn_type = tu.build_subroutine_type("int(*)(void)", "int", vec![]);
assert!(fn_type.is_some());
let die = fn_type.unwrap();
assert!(die.children.is_empty());
}
#[test]
fn test_line_table_discriminator() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/src");
table.add_file("loop.c", 0);
table.entries.push(X86LineEntry {
line: 10,
column: 5,
file: 1,
address: 0x1000,
is_stmt: true,
discriminator: 1,
..Default::default()
});
table.entries.push(X86LineEntry {
line: 10,
column: 5,
file: 1,
address: 0x1004,
is_stmt: true,
discriminator: 2,
..Default::default()
});
let data = table.emit();
assert!(data.len() > 20);
}
#[test]
fn test_line_table_isa_change() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/src");
table.add_file("mixed.s", 0);
table.entries.push(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x1000,
is_stmt: true,
isa: 0,
..Default::default()
});
table.entries.push(X86LineEntry {
line: 2,
column: 0,
file: 1,
address: 0x1004,
is_stmt: true,
isa: 1,
..Default::default()
});
let data = table.emit();
assert!(data.len() > 20);
}
#[test]
fn test_line_table_basic_blocks() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/src");
table.add_file("branch.c", 0);
table.entries.push(X86LineEntry {
line: 5,
column: 0,
file: 1,
address: 0x1000,
is_stmt: true,
basic_block: true,
..Default::default()
});
table.entries.push(X86LineEntry {
line: 10,
column: 0,
file: 1,
address: 0x1010,
is_stmt: true,
basic_block: true,
..Default::default()
});
table.add_end_sequence(0x1020);
let data = table.emit();
assert!(data.len() > 20);
}
#[test]
fn test_variable_with_no_location() {
let var = X86DwarfVariable::new("unlocated", "int", Vec::new());
assert!(var.location.is_empty());
}
#[test]
fn test_variable_artificial_flag() {
let mut var = X86DwarfVariable::new("__temp", "int", vec![0x50]);
var.is_artificial = true;
assert!(var.is_artificial);
}
#[test]
fn test_variable_external_flag() {
let mut var = X86DwarfVariable::new(
"global_x",
"float",
vec![0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x40],
);
var.is_external = true;
assert!(var.is_external);
}
#[test]
fn test_frame_x86_64_with_augmentation() {
let mut cie = X86DwarfFrame::create_x86_64_cie();
cie.augmentation = "zR".to_string();
cie.pointer_encoding = Some(0x1B); assert_eq!(cie.augmentation, "zR");
}
#[test]
fn test_frame_cfa_register_swap() {
let mut insns = Vec::new();
insns.extend_from_slice(&X86DwarfFrame::cfa_def_cfa_register(6)); assert_eq!(insns[0], cfa_ops::DW_CFA_def_cfa_register);
}
#[test]
fn test_location_large_fbreg() {
let large_offsets = [0i64, 64, -64, 128, -128, 8192, -8192, 1048576, -1048576];
for &offset in &large_offsets {
let mut loc = X86DwarfLocation::new_x86_64();
loc.fbreg(offset);
let bytes = loc.finish();
assert!(!bytes.is_empty(), "Empty expression for fbreg({})", offset);
}
}
#[test]
fn test_generator_type_dependency_chain() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.register_base_types();
let _ptr1 = r#gen.type_unit.build_pointer_type("int", "int*");
let _ptr2 = r#gen.type_unit.build_pointer_type("int*", "int**");
let _cptr = r#gen.type_unit.build_const_type("int**", "const int**");
let _ptr3 = r#gen.type_unit.build_pointer_type("const int**", "int***");
let _s = r#gen.type_unit.build_struct_type(
"ComplexStruct",
16,
vec![("p", "int***", 0), ("f", "float", 8)],
);
assert!(r#gen.type_unit.get_type_offset("int*").is_some());
assert!(r#gen.type_unit.get_type_offset("int**").is_some());
assert!(r#gen.type_unit.get_type_offset("ComplexStruct").is_some());
}
#[test]
fn test_generator_typedef_chain() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.register_base_types();
let _size_t = r#gen.type_unit.build_typedef("size_t", "unsigned long");
let _ssize_t = r#gen.type_unit.build_typedef("ssize_t", "long");
let _ptrdiff_t = r#gen.type_unit.build_typedef("ptrdiff_t", "long");
let _intptr_t = r#gen.type_unit.build_typedef("intptr_t", "long");
let _uintptr_t = r#gen.type_unit.build_typedef("uintptr_t", "unsigned long");
assert!(r#gen.type_unit.get_type_offset("size_t").is_some());
assert!(r#gen.type_unit.get_type_offset("ssize_t").is_some());
assert!(r#gen.type_unit.get_type_offset("ptrdiff_t").is_some());
assert!(r#gen.type_unit.get_type_offset("intptr_t").is_some());
assert!(r#gen.type_unit.get_type_offset("uintptr_t").is_some());
}
#[test]
fn test_generator_stress_many_functions() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("stress.c", "/stress");
r#gen.register_base_types();
for i in 0..100 {
let addr = 0x4000_0000 + i * 0x1000;
let mut sp = X86DwarfSubprogram::new(&format!("fn_{}", i), addr, 0x100);
sp.set_frame_base(vec![0x9c]);
sp.set_return_type("int");
sp.add_parameter(X86DwarfVariable::new_parameter("x", "int", vec![0x55]));
sp.add_variable(X86DwarfVariable::new("local", "int", vec![0x91, 0x78]));
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: (i + 1) as u32,
column: 0,
file: 1,
address: addr,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: (i + 1) as u32,
column: 0,
file: 1,
address: addr + 0x100,
end_sequence: true,
..Default::default()
});
}
let sections = r#gen.emit_all();
assert!(sections[".debug_info"].len() > 5000);
assert!(sections[".debug_line"].len() > 1000);
}
#[test]
fn test_generator_stress_many_variables() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("vars.c", "/vars");
r#gen.register_base_types();
let mut sp = X86DwarfSubprogram::new("big_fn", 0x4000_0000, 0x1000);
sp.set_frame_base(vec![0x9c]);
for i in 0..50 {
let mut loc = X86DwarfLocation::new_x86_64();
loc.local_var_below_rbp((i * 8 + 8) as u32);
sp.add_variable(X86DwarfVariable::new(
&format!("var_{}", i),
if i % 2 == 0 { "int" } else { "float" },
loc.finish(),
));
}
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0000,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_1000,
end_sequence: true,
..Default::default()
});
let sections = r#gen.emit_all();
assert!(!sections[".debug_info"].is_empty());
}
#[test]
fn test_fde_references_cie() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
let cie_offset = frame.add_cie(X86DwarfFrame::create_x86_64_cie());
frame.add_fde(FDEData {
cie_pointer: cie_offset,
initial_location: 0x4000_0000,
address_range: 0x200,
instructions: vec![cfa_ops::DW_CFA_nop],
lsda_pointer: None,
personality: None,
});
let data = frame.emit();
assert!(data.len() > 20);
}
#[test]
fn test_multiple_fde_same_cie() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
let cie_offset = frame.add_cie(X86DwarfFrame::create_x86_64_cie());
for i in 0..10 {
frame.add_fde(FDEData {
cie_pointer: cie_offset,
initial_location: 0x4000_0000 + i * 0x1000,
address_range: 0x500,
instructions: vec![cfa_ops::DW_CFA_nop],
lsda_pointer: None,
personality: None,
});
}
let data = frame.emit();
assert!(data.len() > 200);
}
#[test]
fn test_address_and_arange_consistency() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("addr.c", "/addr");
r#gen.register_base_types();
let addr = 0x4000_1000;
let len = 0x500;
let mut sp = X86DwarfSubprogram::new("addr_fn", addr, len);
sp.set_frame_base(vec![0x9c]);
r#gen.add_subprogram(sp);
let sections = r#gen.emit_all();
let arange_data = §ions[".debug_aranges"];
let addr_bytes = addr.to_le_bytes();
let found = arange_data.windows(8).any(|w| w == addr_bytes);
assert!(found, "Address not found in .debug_aranges");
}
#[test]
fn test_line_info_addresses() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("lines.c", "/lines");
r#gen.register_base_types();
let sp = X86DwarfSubprogram::new("lines_fn", 0x4000_0000, 0x100);
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0000,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0100,
end_sequence: true,
..Default::default()
});
let sections = r#gen.emit_all();
let line_data = §ions[".debug_line"];
let set_addr_marker = [0x00, 0x09, 0x02]; let addr_bytes = 0x4000_0000u64.to_le_bytes();
let found = line_data.windows(8).any(|w| w == addr_bytes);
assert!(found, "Address 0x4000_0000 not found in .debug_line");
}
#[test]
fn test_line_entry_default() {
let entry = X86LineEntry::default();
assert_eq!(entry.line, 1);
assert_eq!(entry.column, 0);
assert_eq!(entry.file, 1);
assert_eq!(entry.address, 0);
assert!(!entry.is_stmt);
assert!(!entry.basic_block);
assert!(!entry.prologue_end);
assert!(!entry.epilogue_begin);
assert_eq!(entry.isa, 0);
assert_eq!(entry.discriminator, 0);
assert!(!entry.end_sequence);
}
#[test]
fn test_cie_default() {
let cie = CIEData::default();
assert_eq!(cie.cie_id, 0);
assert_eq!(cie.version, 4);
assert!(cie.augmentation.is_empty());
assert_eq!(cie.address_size, 8);
assert_eq!(cie.code_alignment_factor, 1);
assert_eq!(cie.data_alignment_factor, -8);
assert_eq!(cie.return_address_register, 16);
}
#[test]
fn test_section_offsets_default() {
let offsets = SectionOffsets::default();
assert_eq!(offsets.debug_info, 0);
assert_eq!(offsets.debug_abbrev, 0);
assert_eq!(offsets.debug_line, 0);
assert_eq!(offsets.debug_str, 0);
assert_eq!(offsets.debug_str_offsets, 0);
assert_eq!(offsets.debug_addr, 0);
assert_eq!(offsets.debug_ranges, 0);
assert_eq!(offsets.debug_aranges, 0);
assert_eq!(offsets.debug_frame, 0);
}
#[test]
fn test_abbrev_default() {
let abbrev = X86DwarfAbbrev::default();
assert!(abbrev.is_empty());
}
#[test]
fn test_string_table_default() {
let table = X86DwarfStringTable::default();
assert_eq!(table.size(), 1);
assert!(table.is_empty());
}
#[test]
fn test_location_clone() {
let loc = X86DwarfLocation::new_x86_64();
let _cloned = loc.clone();
let _dbg = format!("{:?}", loc);
}
#[test]
fn test_expression_clone() {
let expr = X86DwarfExpression::new_x86_64();
let _cloned = expr.clone();
let _dbg = format!("{:?}", expr);
}
#[test]
fn test_frame_clone() {
let frame = X86DwarfFrame::new_debug_frame_64();
let _cloned = frame.clone();
let _dbg = format!("{:?}", frame);
}
#[test]
fn test_generator_clone() {
let r#gen = X86DwarfGenerator::new_x86_64();
let _cloned = r#gen.clone();
let _dbg = format!("{:?}", r#gen);
}
#[test]
fn test_location_addr_zero() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.addr(0);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_addr as u8);
assert_eq!(&bytes[1..9], &[0u8; 8]);
}
#[test]
fn test_location_addr_max() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.addr(u64::MAX);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_addr as u8);
assert_eq!(&bytes[1..9], &[0xFFu8; 8]);
}
#[test]
fn test_range_list_zero_range() {
let mut rl = X86DwarfRangeList::new(8, false);
rl.add(vec![AddrRange {
low_pc: 0,
high_pc: 0,
}]);
let data = rl.emit_ranges_dwarf4();
assert_eq!(data.len(), 32); }
#[test]
fn test_range_list_max_addresses() {
let mut rl = X86DwarfRangeList::new(8, false);
rl.add(vec![AddrRange {
low_pc: 0,
high_pc: u64::MAX,
}]);
let data = rl.emit_ranges_dwarf4();
let high_bytes = &data[8..16];
assert_eq!(high_bytes, &[0xFFu8; 8]);
}
#[test]
fn test_32bit_address_range() {
let mut ar = X86DwarfARange::new(4);
ar.add_cu(
0,
vec![ARangeEntry {
cu_offset: 0,
address: 0xFFFFFFFF,
length: 0x1000,
}],
);
let data = ar.emit();
assert!(!data.is_empty());
}
#[test]
fn test_64bit_address_range_max() {
let mut ar = X86DwarfARange::new(8);
ar.add_cu(
0,
vec![ARangeEntry {
cu_offset: 0,
address: u64::MAX,
length: 1,
}],
);
let data = ar.emit();
assert!(!data.is_empty());
}
#[test]
fn test_frame_32bit_cie() {
let mut frame = X86DwarfFrame::new_debug_frame_32();
let cie = X86DwarfFrame::create_x86_32_cie();
frame.add_cie(cie);
let data = frame.emit();
assert!(data.len() > 10);
}
#[test]
fn test_frame_eh_frame_compatibility() {
let mut frame = X86DwarfFrame::new_eh_frame_64();
assert!(frame.is_eh_frame);
let cie = X86DwarfFrame::create_x86_64_cie();
frame.add_cie(cie);
let data = frame.emit();
assert!(!data.is_empty());
}
#[test]
fn test_expression_truncated_u8() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![dwarf_ops::DW_OP_const1u as u8];
let result = expr.evaluate(&program);
assert!(result.is_err());
}
#[test]
fn test_expression_truncated_u16() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![dwarf_ops::DW_OP_const2u as u8, 0x42];
let result = expr.evaluate(&program);
assert!(result.is_err());
}
#[test]
fn test_expression_truncated_u32() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![dwarf_ops::DW_OP_const4u as u8, 0x01, 0x02];
let result = expr.evaluate(&program);
assert!(result.is_err());
}
#[test]
fn test_expression_truncated_uleb() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![dwarf_ops::DW_OP_constu as u8, 0x80]; let result = expr.evaluate(&program);
assert!(result.is_err());
}
#[test]
fn test_x86_type_sizes() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_x86_specific_types();
let m128_die = tu.get_type_die("__m128").unwrap();
let size_attr = m128_die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert!(size_attr.is_some());
assert_eq!(size_attr.unwrap().1[0], 16);
let m256_die = tu.get_type_die("__m256").unwrap();
let size_attr = m256_die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert!(size_attr.is_some());
assert_eq!(size_attr.unwrap().1[0], 32);
let m512_die = tu.get_type_die("__m512").unwrap();
let size_attr = m512_die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert!(size_attr.is_some());
assert_eq!(size_attr.unwrap().1[0], 64);
let i128_die = tu.get_type_die("__int128").unwrap();
let size_attr = i128_die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert!(size_attr.is_some());
assert_eq!(size_attr.unwrap().1[0], 16);
}
#[test]
fn test_float_type_encodings() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let float_die = tu.get_type_die("float").unwrap();
let enc = float_die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_encoding);
assert_eq!(enc.unwrap().1[0], x86_dwarf_encodings::DW_ATE_float);
let double_die = tu.get_type_die("double").unwrap();
let enc = double_die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_encoding);
assert_eq!(enc.unwrap().1[0], x86_dwarf_encodings::DW_ATE_float);
let bool_die = tu.get_type_die("bool").unwrap();
let enc = bool_die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_encoding);
assert_eq!(enc.unwrap().1[0], x86_dwarf_encodings::DW_ATE_boolean);
}
#[test]
fn test_location_reg_compact_vs_extended() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.reg(RAX); let bytes = loc.finish();
assert_eq!(bytes.len(), 1);
assert_eq!(bytes[0], dwarf_ops::DW_OP_reg0 as u8);
let mut loc2 = X86DwarfLocation::new_x86_64();
let high_reg = 256 + 100; loc2.reg(high_reg);
let bytes2 = loc2.finish();
assert!(bytes2.len() > 1);
}
#[test]
fn test_location_breg_compact_vs_extended() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.breg(RSP, 0); let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_breg0 as u8 + 7);
let mut loc2 = X86DwarfLocation::new_x86_64();
loc2.breg(256 + 100, 0);
let bytes2 = loc2.finish();
assert_eq!(bytes2[0], dwarf_ops::DW_OP_bregx as u8);
}
#[test]
fn test_string_table_interleaved_dedup() {
let mut table = X86DwarfStringTable::new();
let strings = ["a", "b", "a", "c", "b", "d", "a", "e"];
for s in &strings {
table.add(s);
}
assert_eq!(table.len(), 5);
}
#[test]
fn test_string_table_size_calculation() {
let mut table = X86DwarfStringTable::new();
table.add("abc"); table.add("de"); table.add("fghi"); assert_eq!(table.size(), 13);
}
#[test]
fn test_abbrev_attribute_order_preserved() {
let mut abbrev = X86DwarfAbbrev::new();
let decl = AbbrevDecl {
tag: dwarf_tags::DW_TAG_variable,
has_children: false,
attributes: vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_type, dwarf_forms::DW_FORM_ref4),
(
dwarf_attributes::DW_AT_location,
dwarf_forms::DW_FORM_exprloc,
),
],
};
let code = abbrev.add(decl);
assert_eq!(code, 1);
let data = abbrev.emit();
assert!(data.len() > 5);
}
#[test]
fn test_generator_incremental_build() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("incremental.c", "/inc");
r#gen.register_base_types();
let sections1 = r#gen.emit_all();
assert!(!sections1[".debug_info"].is_empty());
let sp = X86DwarfSubprogram::new("added_fn", 0x5000, 0x100);
r#gen.add_subprogram(sp);
let sections2 = r#gen.emit_all();
assert!(sections2[".debug_info"].len() > sections1[".debug_info"].len());
}
#[test]
fn test_generator_type_unit_before_after() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
let before_count = r#gen.type_unit.types.len();
r#gen.register_base_types();
let after_count = r#gen.type_unit.types.len();
assert!(after_count > before_count);
}
#[test]
fn test_generator_x86_types_before_after() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
let before_count = r#gen.type_unit.x86_types.len();
r#gen.register_x86_types();
let after_count = r#gen.type_unit.x86_types.len();
assert!(after_count > before_count);
}
#[test]
fn test_segment_register_dwarf_mapping() {
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(CS), 51);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(DS), 53);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(SS), 52);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(ES), 50);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(FS), 54);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(GS), 55);
}
#[test]
fn test_flags_register_dwarf() {
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(RFLAGS), 49);
}
#[test]
fn test_rip_dwarf_mapping() {
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(RIP), 16);
}
#[test]
fn test_expression_memory_read() {
let mut expr = X86DwarfExpression::new_x86_64();
expr.read_memory = Some(|addr, size| {
if addr == 0x1000 && size == 8 {
Some(vec![0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00])
} else {
None
}
});
let mut program = vec![dwarf_ops::DW_OP_const4u as u8];
program.extend_from_slice(&0x1000u32.to_le_bytes());
program.push(dwarf_ops::DW_OP_deref as u8);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0x1234_5678));
}
#[test]
fn test_expression_deref_size() {
let mut expr = X86DwarfExpression::new_x86_64();
expr.read_memory = Some(|addr, size| {
if addr == 0x2000 {
Some(vec![0x42, 0x00, 0x00, 0x00])
} else {
None
}
});
let mut program = vec![dwarf_ops::DW_OP_const4u as u8];
program.extend_from_slice(&0x2000u32.to_le_bytes());
program.push(dwarf_ops::DW_OP_deref_size as u8);
program.push(4);
let result = expr.evaluate(&program).unwrap();
assert_eq!(result, Some(0x42));
}
#[test]
fn test_full_type_system() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
tu.register_x86_specific_types();
let _ptr = tu.build_pointer_type("int", "int*");
let _const_ptr = tu.build_const_type("int*", "const int*");
let _vol_ptr = tu.build_volatile_type("int*", "volatile int*");
let _ref = tu.build_reference_type("int", "int&");
let _rref = tu.build_rvalue_reference_type("int", "int&&");
let _td = tu.build_typedef("my_int", "int");
let _arr = tu.build_array_type("int", "int[100]", 100);
let _s = tu.build_struct_type(
"Vec3",
12,
vec![("x", "float", 0), ("y", "float", 4), ("z", "float", 8)],
);
let _u = tu.build_union_type("Value", 8, vec![("as_int", "int"), ("as_double", "double")]);
let _e = tu.build_enum_type("Option", 4, vec![("None", 0), ("Some", 1)]);
let _fn = tu.build_subroutine_type("callback_t", "void", vec!["int", "float"]);
assert!(tu.offsets.contains_key("int*"));
assert!(tu.offsets.contains_key("const int*"));
assert!(tu.offsets.contains_key("int&"));
assert!(tu.offsets.contains_key("int&&"));
assert!(tu.offsets.contains_key("my_int"));
assert!(tu.offsets.contains_key("int[100]"));
assert!(tu.offsets.contains_key("Vec3"));
assert!(tu.offsets.contains_key("Value"));
assert!(tu.offsets.contains_key("Option"));
assert!(tu.offsets.contains_key("callback_t"));
}
#[test]
fn test_independent_generators() {
let mut gen1 = X86DwarfGenerator::new_x86_64();
gen1.set_source_file("a.c", "/a");
gen1.register_base_types();
gen1.add_subprogram(X86DwarfSubprogram::new("fn_a", 0x1000, 0x100));
let mut gen2 = X86DwarfGenerator::new_x86_64();
gen2.set_source_file("b.c", "/b");
gen2.register_base_types();
gen2.add_subprogram(X86DwarfSubprogram::new("fn_b", 0x2000, 0x200));
let s1 = gen1.emit_all();
let s2 = gen2.emit_all();
assert!(!s1[".debug_info"].is_empty());
assert!(!s2[".debug_info"].is_empty());
assert_ne!(s1[".debug_info"], s2[".debug_info"]);
}
#[test]
fn test_location_all_sse_regs() {
let xmm_regs = [
XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13,
XMM14, XMM15,
];
for ® in &xmm_regs {
let mut loc = X86DwarfLocation::new_x86_64();
loc.reg(reg);
let bytes = loc.finish();
assert!(
!bytes.is_empty(),
"Empty expression for XMM register {}",
reg
);
}
}
#[test]
fn test_location_all_gpr_regs() {
let gprs = [
RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8, R9, R10, R11, R12, R13, R14, R15,
];
for ® in &gprs {
let mut loc = X86DwarfLocation::new_x86_64();
loc.reg(reg);
let bytes = loc.finish();
assert!(
!bytes.is_empty(),
"Empty expression for GPR register {}",
reg
);
}
}
#[test]
fn test_doc_example_sysv_int_params() {
for param_idx in 0..12 {
let mut loc = X86DwarfLocation::new_x86_64();
loc.sysv_int_param(param_idx, 16);
let bytes = loc.finish();
assert!(!bytes.is_empty(), "Empty for param {}", param_idx);
}
}
#[test]
fn test_doc_example_local_var_below_rbp() {
for offset in &[0u32, 4, 8, 16, 32, 64, 128, 256] {
let mut loc = X86DwarfLocation::new_x86_64();
loc.local_var_below_rbp(*offset);
let bytes = loc.finish();
assert!(!bytes.is_empty(), "Empty for offset {}", offset);
}
}
#[test]
fn test_doc_example_win64_int_params() {
for param_idx in 0..8 {
let mut loc = X86DwarfLocation::new_x86_64();
loc.win64_int_param(param_idx);
let bytes = loc.finish();
assert!(!bytes.is_empty(), "Empty for param {}", param_idx);
}
}
#[test]
fn test_line_table_no_files() {
let table = X86DwarfLineTable::new_x86_64();
let data = table.emit();
assert!(data.len() > 10);
}
#[test]
fn test_line_table_single_entry_no_end_seq() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/tmp");
table.add_file("lone.c", 0);
table.add_line(0x1000, 1, 1, 0);
let data = table.emit();
assert!(data.len() > 20);
}
#[test]
fn test_frame_no_entries() {
let frame = X86DwarfFrame::new_debug_frame_64();
let data = frame.emit();
assert_eq!(data, vec![0u8, 0, 0, 0]);
}
#[test]
fn test_arange_no_entries() {
let ar = X86DwarfARange::new(8);
let data = ar.emit();
assert!(data.is_empty());
}
#[test]
fn test_range_list_no_entries() {
let rl = X86DwarfRangeList::new(8, false);
let data = rl.emit();
assert!(data.is_empty());
}
#[test]
fn test_pointer_size_64bit() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let ptr = tu.build_pointer_type("int", "int*").unwrap();
let size_attr = ptr
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert_eq!(size_attr.unwrap().1[0], 8);
}
#[test]
fn test_pointer_size_32bit() {
let mut tu = X86DwarfTypeUnit::new(4);
tu.register_standard_base_types();
let ptr = tu.build_pointer_type("int", "int*").unwrap();
let size_attr = ptr
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert_eq!(size_attr.unwrap().1[0], 4);
}
#[test]
fn test_reference_size_64bit() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_standard_base_types();
let rref = tu.build_reference_type("int", "int&").unwrap();
let size_attr = rref
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert_eq!(size_attr.unwrap().1[0], 8);
}
#[test]
fn test_subprogram_fully_populated() {
let mut sp = X86DwarfSubprogram::new("full_fn", 0x4000_0100, 0x300);
sp.set_frame_base(vec![0x9c]);
sp.set_return_type("int");
sp.set_linkage_name("_Z7full_fnv");
sp.is_external = true;
sp.inline_code = Some(x86_dwarf_inline::DW_INL_not_inlined);
sp.decl_file = 1;
sp.decl_line = 42;
sp.decl_column = 10;
sp.is_artificial = false;
sp.is_declaration = false;
sp.abstract_origin = Some(0x500);
sp.add_parameter(X86DwarfVariable::new_parameter("p1", "int", vec![0x55]));
sp.add_parameter(X86DwarfVariable::new_parameter("p2", "float", vec![0x61]));
sp.add_variable(X86DwarfVariable::new("v1", "double", vec![0x91, 0x78]));
sp.add_lexical_block(X86DwarfLexicalBlock {
low_pc: 0x4000_0200,
high_pc: 0x80,
variables: vec![X86DwarfVariable::new("inner", "int", vec![0x91, 0x70])],
});
sp.add_inlined_subroutine(X86DwarfInlinedSubroutine::new(
0x600,
0x4000_0280,
0x40,
1,
50,
));
assert_eq!(sp.name, "full_fn");
assert_eq!(sp.parameters.len(), 2);
assert_eq!(sp.variables.len(), 1);
assert_eq!(sp.lexical_blocks.len(), 1);
assert_eq!(sp.inlined_subroutines.len(), 1);
}
#[test]
fn test_debug_info_size_nonzero() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("size_test.c", "/size");
r#gen.register_base_types();
let sp = X86DwarfSubprogram::new("size_fn", 0x4000_0000, 0x50);
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0000,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0050,
end_sequence: true,
..Default::default()
});
let sections = r#gen.emit_all();
assert!(sections[".debug_info"].len() >= 30, "debug_info too small");
assert!(
sections[".debug_abbrev"].len() >= 5,
"debug_abbrev too small"
);
assert!(sections[".debug_line"].len() >= 20, "debug_line too small");
}
#[test]
fn test_debug_str_size_calculation() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("str_size.c", "/ss");
r#gen.set_producer("Super Long Producer Name v2024.06.15-beta");
r#gen.register_base_types();
r#gen.add_subprogram(X86DwarfSubprogram::new("fn", 0x1000, 0x50));
let sections = r#gen.emit_all();
let str_data = §ions[".debug_str"];
assert!(str_data.len() > 20);
}
#[test]
fn test_abbrev_terminates_with_null() {
let mut abbrev = X86DwarfAbbrev::new();
abbrev.add_base_type_abbrev();
let data = abbrev.emit();
assert_eq!(data.last(), Some(&0u8));
}
#[test]
fn test_debug_info_terminates_with_null_die() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("null.c", "/null");
r#gen.register_base_types();
let sp = X86DwarfSubprogram::new("fn", 0x1000, 0x50);
r#gen.add_subprogram(sp);
let sections = r#gen.emit_all();
let info = §ions[".debug_info"];
assert_eq!(info.last(), Some(&0u8));
}
#[test]
fn test_debug_frame_terminates_with_null_cie() {
let mut frame = X86DwarfFrame::new_debug_frame_64();
frame.add_cie(X86DwarfFrame::create_x86_64_cie());
let data = frame.emit();
let len = data.len();
assert_eq!(&data[len - 4..], &[0u8, 0, 0, 0]);
}
#[test]
fn test_generator_no_functions() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("nofn.c", "/empty");
r#gen.register_base_types();
let sections = r#gen.emit_all();
assert!(sections.contains_key(".debug_info"));
}
#[test]
fn test_generator_no_line_table() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("noline.c", "/nl");
r#gen.register_base_types();
r#gen.add_subprogram(X86DwarfSubprogram::new("fn", 0x1000, 0x50));
let sections = r#gen.emit_all();
assert!(sections.contains_key(".debug_line"));
}
#[test]
fn test_generator_emit_twice_idempotent() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("idem.c", "/id");
r#gen.register_base_types();
let sp = X86DwarfSubprogram::new("f", 0x1000, 0x50);
r#gen.add_subprogram(sp);
let s1 = r#gen.emit_all();
let s2 = r#gen.emit_all();
assert_eq!(s1[".debug_info"], s2[".debug_info"]);
assert_eq!(s1[".debug_abbrev"], s2[".debug_abbrev"]);
assert_eq!(s1[".debug_line"], s2[".debug_line"]);
assert_eq!(s1[".debug_str"], s2[".debug_str"]);
}
#[test]
fn test_language_code_roundtrip() {
use x86_dwarf_languages::*;
let langs = vec![
("C89", DW_LANG_C89),
("C", DW_LANG_C),
("C++", DW_LANG_C_plus_plus),
("Rust", DW_LANG_Rust),
("Swift", DW_LANG_Swift),
("Go", DW_LANG_Go),
("Python", DW_LANG_Python),
("Assembly", DW_LANG_Assembly),
];
for (name, code) in langs {
let cu = X86DwarfCompileUnit::new_x86_64("t.c", "/d", "cc", code);
assert_eq!(cu.language, code, "Language {} mismatch", name);
}
}
#[test]
fn test_gpr_register_widths() {
let gpr64 = [
RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15,
];
for ® in &gpr64 {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(reg);
assert!(
dwarf <= 15,
"GPR64 register {} maps to DWARF {} > 15",
reg,
dwarf
);
}
}
#[test]
fn test_xmm_register_widths() {
let xmm_lo = [
XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13,
XMM14, XMM15,
];
for (i, ®) in xmm_lo.iter().enumerate() {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(reg);
assert_eq!(dwarf, 17 + i as u16, "XMM{} maps to wrong DWARF number", i);
}
}
#[test]
fn test_compute_ext_dwarf_num_for_ymm() {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(YMM0);
assert_eq!(dwarf, 17);
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(YMM15);
assert_eq!(dwarf, 32); }
#[test]
fn test_compute_ext_dwarf_num_for_zmm() {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(ZMM0);
assert_eq!(dwarf, 17);
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(ZMM31);
assert_eq!(dwarf, 82); }
#[test]
fn test_compute_ext_dwarf_num_for_k_regs() {
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(K0), 118);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(K7), 125);
}
#[test]
fn test_compute_ext_dwarf_num_for_bnd_regs() {
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(BND0), 126);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(BND3), 129);
}
#[test]
fn test_composite_location_mixed_reg_memory() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.reg(RAX);
loc.piece(8);
loc.reg(RDX);
loc.piece(8);
let bytes = loc.finish();
assert_eq!(bytes[0], dwarf_ops::DW_OP_reg0 as u8);
assert!(bytes.contains(&dwarf_ops::DW_OP_piece));
}
#[test]
fn test_bitfield_location() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.breg(RBP, -8);
loc.bit_piece(5, 3);
let bytes = loc.finish();
assert!(bytes.contains(&dwarf_ops::DW_OP_bit_piece));
}
#[test]
fn test_large_bit_piece() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.breg(RBP, -16);
loc.bit_piece(64, 0);
let bytes = loc.finish();
assert!(!bytes.is_empty());
}
#[test]
fn test_location_mode_switch() {
let mut loc = X86DwarfLocation::new_x86_64();
assert!(loc.is_location);
loc.set_is_location(false);
assert!(!loc.is_location);
loc.set_is_location(true);
assert!(loc.is_location);
}
#[test]
fn test_full_build_all_nine_sections() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("complete.c", "/complete");
r#gen.set_producer("Full Build Test Compiler");
r#gen.set_language(x86_dwarf_languages::DW_LANG_C_plus_plus_17);
r#gen.register_base_types();
r#gen.register_x86_types();
let mut sp = X86DwarfSubprogram::new("complete_fn", 0x4000_0000, 0x200);
sp.set_frame_base(vec![0x9c]);
sp.set_return_type("int");
sp.set_linkage_name("_Z11complete_fnv");
sp.add_parameter(X86DwarfVariable::new_parameter("argc", "int", vec![0x55]));
sp.add_variable(X86DwarfVariable::new("local", "double", vec![0x91, 0x78]));
sp.add_lexical_block(X86DwarfLexicalBlock {
low_pc: 0x4000_0100,
high_pc: 0x80,
variables: vec![X86DwarfVariable::new("temp", "float", vec![0x91, 0x70])],
});
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: 1,
column: 0,
file: 1,
address: 0x4000_0000,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 2,
column: 5,
file: 1,
address: 0x4000_0050,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 2,
column: 10,
file: 1,
address: 0x4000_0100,
is_stmt: true,
prologue_end: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: 3,
column: 1,
file: 1,
address: 0x4000_0200,
is_stmt: true,
end_sequence: true,
..Default::default()
});
let sections = r#gen.emit_all();
let expected = vec![
".debug_info",
".debug_abbrev",
".debug_line",
".debug_str",
".debug_str_offsets",
".debug_frame",
".debug_aranges",
".debug_ranges",
".debug_addr",
];
for section_name in &expected {
assert!(
sections.contains_key(*section_name),
"Missing DWARF section: {}",
section_name
);
assert!(
!sections[*section_name].is_empty(),
"Empty DWARF section: {}",
section_name
);
}
let info = §ions[".debug_info"];
let version = u16::from_le_bytes([info[4], info[5]]);
assert_eq!(version, 4, "DWARF version should be 4");
let line = §ions[".debug_line"];
let line_version = u16::from_le_bytes([line[4], line[5]]);
assert_eq!(line_version, 4, "Line table version should be 4");
}
#[test]
fn test_sysv_amd64_arg_registers() {
let arg_regs = [RDI, RSI, RDX, RCX, R8, R9];
let expected_dwarf = [5u16, 4, 1, 2, 8, 9];
for (i, (®, &exp)) in arg_regs.iter().zip(expected_dwarf.iter()).enumerate() {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(reg);
assert_eq!(
dwarf, exp,
"SysV arg register {} (reg_id={}) maps to DWARF {} instead of {}",
i, reg, dwarf, exp
);
}
}
#[test]
fn test_sysv_amd64_return_register() {
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(RAX), 0);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(RDX), 1);
}
#[test]
fn test_sysv_amd64_sse_return() {
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(XMM0), 17);
assert_eq!(X86DwarfLocation::x86_reg_to_dwarf(XMM1), 18);
}
#[test]
fn test_win64_arg_registers() {
let arg_regs = [RCX, RDX, R8, R9];
let expected_dwarf = [2u16, 1, 8, 9];
for (i, (®, &exp)) in arg_regs.iter().zip(expected_dwarf.iter()).enumerate() {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(reg);
assert_eq!(dwarf, exp, "Win64 arg register {} maps to wrong DWARF", i);
}
}
#[test]
fn test_sysv_callee_saved_registers() {
let callee_saved = [RBX, RBP, R12, R13, R14, R15];
for ® in &callee_saved {
let dwarf = X86DwarfLocation::x86_reg_to_dwarf(reg);
assert!(
dwarf < 256,
"Callee-saved register {} maps to invalid DWARF {}",
reg,
dwarf
);
}
}
#[test]
fn test_location_clear_and_rebuild() {
let mut loc = X86DwarfLocation::new_x86_64();
loc.const_u32(42);
assert!(!loc.is_empty());
loc.clear();
assert!(loc.is_empty());
loc.const_u32(99);
assert!(!loc.is_empty());
}
#[test]
fn test_expression_clear_and_reset() {
let mut expr = X86DwarfExpression::new_x86_64();
let program = vec![dwarf_ops::DW_OP_lit0 as u8 + 5];
let _ = expr.evaluate(&program);
expr.reset();
assert!(expr.stack.is_empty());
}
#[test]
fn test_e2e_structural_invariants() {
let mut r#gen = X86DwarfGenerator::new_x86_64();
r#gen.set_source_file("invariant.c", "/inv");
r#gen.set_producer("Invariant Test Compiler");
r#gen.register_base_types();
for i in 0..2 {
let mut sp =
X86DwarfSubprogram::new(&format!("fn{}", i), 0x4000_0000 + i * 0x1000, 0x50);
sp.set_frame_base(vec![0x9c]);
r#gen.add_subprogram(sp);
r#gen.add_line_entry(X86LineEntry {
line: (i + 1) as u32,
column: 0,
file: 1,
address: 0x4000_0000 + i * 0x1000,
is_stmt: true,
..Default::default()
});
r#gen.add_line_entry(X86LineEntry {
line: (i + 1) as u32,
column: 0,
file: 1,
address: 0x4000_0000 + i * 0x1000 + 0x50,
end_sequence: true,
..Default::default()
});
}
let sections = r#gen.emit_all();
assert!(sections[".debug_info"].len() > sections[".debug_abbrev"].len());
let line_data = §ions[".debug_line"];
let addr1 = 0x4000_0000u64.to_le_bytes();
let addr2 = 0x4000_1000u64.to_le_bytes();
assert!(line_data.windows(8).any(|w| w == addr1));
assert!(line_data.windows(8).any(|w| w == addr2));
let str_data = §ions[".debug_str"];
let producer = b"Invariant Test Compiler";
assert!(str_data.windows(producer.len()).any(|w| w == producer));
let abbrev_data = §ions[".debug_abbrev"];
assert_eq!(abbrev_data.last(), Some(&0u8));
for (name, data) in §ions {
assert!(!data.is_empty(), "Section {} is empty", name);
}
}
#[test]
fn test_fuzz_random_line_order() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/fuzz");
table.add_file("fuzz.c", 0);
let entries = vec![
(0x1000u64, 1u32, 1u32),
(0x1008, 3, 2),
(0x1004, 2, 2),
(0x1010, 5, 3),
(0x100c, 4, 3),
];
for (addr, line, col) in entries {
table.add_line(addr, 1, line, col);
}
table.add_end_sequence(0x1020);
let data = table.emit();
assert!(data.len() > 30);
}
#[test]
fn test_line_table_sparse_addresses() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/sparse");
table.add_file("sparse.c", 0);
table.add_line(0x1000, 1, 1, 0);
table.add_line(0xFFFF_0000, 1, 2, 0);
table.add_end_sequence(0x1_0000_0000);
let data = table.emit();
assert!(data.len() > 20);
}
#[test]
fn test_line_table_dense_lines() {
let mut table = X86DwarfLineTable::new_x86_64();
table.set_comp_dir("/dense");
table.add_file("dense.c", 0);
for i in 0..50 {
table.add_line(0x1000 + i * 4, 1, (i + 1) as u32, 0);
}
table.add_end_sequence(0x1000 + 200);
let data = table.emit();
assert!(data.len() > 40);
}
#[test]
fn test_abbrev_table_always_starts_with_code_one() {
let mut abbrev = X86DwarfAbbrev::new();
assert_eq!(abbrev.add_base_type_abbrev(), 1);
assert_eq!(abbrev.add_pointer_type_abbrev(), 2);
assert_eq!(
abbrev.add_struct_type_abbrev(dwarf_tags::DW_TAG_structure_type),
3
);
}
#[test]
fn test_string_table_offset_zero_is_always_empty_string() {
let table = X86DwarfStringTable::new();
assert_eq!(table.get_offset(""), Some(0));
let mut table2 = X86DwarfStringTable::new();
table2.add("first_real_string");
assert_eq!(table2.get_offset(""), Some(0));
assert_eq!(table2.get_offset("first_real_string"), Some(1));
}
#[test]
fn test_cu_producer_always_first_attribute() {
let cu = X86DwarfCompileUnit::new_x86_64("t.c", "/d", "CustomCC", 2);
let data = cu.emit_die_header();
let producer_idx = data.windows(8).position(|w| w == b"CustomCC");
assert!(producer_idx.is_some());
assert!(producer_idx.unwrap() < 20);
}
#[test]
fn test_no_panic_on_empty_line_table_emit() {
let table = X86DwarfLineTable::new_x86_64();
let _data = table.emit();
}
#[test]
fn test_no_panic_on_empty_frame_emit() {
let frame = X86DwarfFrame::new_debug_frame_64();
let _data = frame.emit();
}
#[test]
fn test_no_panic_on_empty_arange_emit() {
let ar = X86DwarfARange::new(8);
let _data = ar.emit();
}
#[test]
fn test_no_panic_on_empty_range_list_emit() {
let rl = X86DwarfRangeList::new(8, false);
let _data = rl.emit();
}
#[test]
fn test_no_panic_on_empty_addr_table_emit() {
let table = X86DwarfAddrTable::new(8);
let _data = table.emit();
}
#[test]
fn test_all_x86_types_have_nonzero_size() {
let mut tu = X86DwarfTypeUnit::new(8);
tu.register_x86_specific_types();
for (name, die) in &tu.x86_types {
let size_attr = die
.attributes
.iter()
.find(|(attr, _)| *attr == dwarf_attributes::DW_AT_byte_size);
assert!(
size_attr.is_some(),
"X86 type '{}' has no size attribute",
name
);
}
}
#[test]
fn test_generator_respects_x86_target_triple() {
let r#gen = X86DwarfGenerator::new_x86_64();
assert!(r#gen.target_triple.contains("x86_64"));
let gen32 = X86DwarfGenerator::new_x86_32();
assert!(gen32.target_triple.contains("i386"));
}
#[test]
fn test_compile_unit_address_size_byte_in_header() {
let cu64 = X86DwarfCompileUnit::new_x86_64("a.c", "/d", "cc", 2);
assert_eq!(cu64.address_size, 8);
let cu32 = X86DwarfCompileUnit::new_x86_32("a.c", "/d", "cc", 2);
assert_eq!(cu32.address_size, 4);
}
}