#![allow(non_upper_case_globals, non_camel_case_types, dead_code)]
use std::collections::HashMap;
pub mod dwarf_lang {
pub const DW_LANG_C89: u16 = 0x0001;
pub const DW_LANG_C: u16 = 0x0002;
pub const DW_LANG_C_plus_plus: u16 = 0x0004;
pub const DW_LANG_C99: u16 = 0x000c;
pub const DW_LANG_C11: u16 = 0x001d;
pub const DW_LANG_C17: u16 = 0x002a;
pub const DW_LANG_C23: u16 = 0x003c;
pub const DW_LANG_C_plus_plus_03: u16 = 0x0013;
pub const DW_LANG_C_plus_plus_11: u16 = 0x001a;
pub const DW_LANG_C_plus_plus_14: u16 = 0x0021;
pub const DW_LANG_C_plus_plus_17: u16 = 0x002e;
pub const DW_LANG_C_plus_plus_20: u16 = 0x0037;
pub const DW_LANG_Rust: u16 = 0x001c;
pub const DW_LANG_Go: u16 = 0x0016;
pub const DW_LANG_Swift: u16 = 0x001e;
pub const DW_LANG_Fortran95: u16 = 0x000e;
pub const DW_LANG_Ada95: u16 = 0x000d;
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_D: u16 = 0x0013;
pub const DW_LANG_Python: u16 = 0x0014;
pub const DW_LANG_OpenCL: u16 = 0x0015;
pub const DW_LANG_Haskell: u16 = 0x0018;
pub const DW_LANG_Modula3: u16 = 0x0019;
pub const DW_LANG_Julia: u16 = 0x0023;
pub const DW_LANG_Dylan: u16 = 0x0024;
pub const DW_LANG_Assembly: u16 = 0x8001;
}
pub mod dwarf_tag {
pub const DW_TAG_compile_unit: u16 = 0x11;
pub const DW_TAG_subprogram: u16 = 0x2e;
pub const DW_TAG_variable: u16 = 0x34;
pub const DW_TAG_formal_parameter: u16 = 0x05;
pub const DW_TAG_lexical_block: u16 = 0x0b;
pub const DW_TAG_inlined_subroutine: u16 = 0x1d;
pub const DW_TAG_base_type: u16 = 0x24;
pub const DW_TAG_pointer_type: u16 = 0x0f;
pub const DW_TAG_reference_type: u16 = 0x10;
pub const DW_TAG_rvalue_reference_type: u16 = 0x42;
pub const DW_TAG_const_type: u16 = 0x26;
pub const DW_TAG_volatile_type: u16 = 0x35;
pub const DW_TAG_restrict_type: u16 = 0x37;
pub const DW_TAG_typedef: u16 = 0x16;
pub const DW_TAG_structure_type: u16 = 0x13;
pub const DW_TAG_union_type: u16 = 0x17;
pub const DW_TAG_class_type: u16 = 0x02;
pub const DW_TAG_enumeration_type: u16 = 0x04;
pub const DW_TAG_enumerator: u16 = 0x28;
pub const DW_TAG_array_type: u16 = 0x01;
pub const DW_TAG_subrange_type: u16 = 0x21;
pub const DW_TAG_subroutine_type: u16 = 0x15;
pub const DW_TAG_member: u16 = 0x0d;
pub const DW_TAG_inheritance: u16 = 0x1c;
pub const DW_TAG_unspecified_type: u16 = 0x3b;
pub const DW_TAG_unspecified_parameters: u16 = 0x21;
}
pub mod dwarf_attr {
pub const DW_AT_name: u16 = 0x03;
pub const DW_AT_producer: u16 = 0x25;
pub const DW_AT_language: u16 = 0x13;
pub const DW_AT_comp_dir: u16 = 0x1b;
pub const DW_AT_low_pc: u16 = 0x11;
pub const DW_AT_high_pc: u16 = 0x12;
pub const DW_AT_location: u16 = 0x02;
pub const DW_AT_type: u16 = 0x31;
pub const DW_AT_decl_file: u16 = 0x3a;
pub const DW_AT_decl_line: u16 = 0x3b;
pub const DW_AT_decl_column: u16 = 0x39;
pub const DW_AT_external: u16 = 0x3f;
pub const DW_AT_artificial: u16 = 0x34;
pub const DW_AT_inline: u16 = 0x20;
pub const DW_AT_abstract_origin: u16 = 0x31;
pub const DW_AT_specification: u16 = 0x47;
pub const DW_AT_ranges: u16 = 0x55;
pub const DW_AT_stmt_list: u16 = 0x10;
pub const DW_AT_byte_size: u16 = 0x0b;
pub const DW_AT_bit_size: u16 = 0x0d;
pub const DW_AT_bit_offset: u16 = 0x0c;
pub const DW_AT_data_member_location: u16 = 0x38;
pub const DW_AT_accessibility: u16 = 0x32;
pub const DW_AT_linkage_name: u16 = 0x6e;
pub const DW_AT_frame_base: u16 = 0x40;
pub const DW_AT_encoding: u16 = 0x3e;
pub const DW_AT_const_value: u16 = 0x1c;
pub const DW_AT_upper_bound: u16 = 0x2f;
pub const DW_AT_lower_bound: u16 = 0x22;
pub const DW_AT_count: u16 = 0x0b;
pub const DW_AT_containing_type: u16 = 0x1d;
pub const DW_AT_virtuality: u16 = 0x4c;
pub const DW_AT_vtable_elem_location: u16 = 0x4d;
pub const DW_AT_enum_class: u16 = 0x4e;
pub const DW_AT_identifier_case: u16 = 0x42;
pub const DW_AT_return_addr: u16 = 0x2a;
pub const DW_AT_entry_pc: u16 = 0x52;
pub const DW_AT_use_UTF8: u16 = 0x53;
pub const DW_AT_call_file: u16 = 0x58;
pub const DW_AT_call_line: u16 = 0x59;
pub const DW_AT_call_column: u16 = 0x57;
pub const DW_AT_description: u16 = 0x5a;
pub const DW_AT_GNU_locviews: u16 = 0x2137;
}
pub mod dwarf_form {
pub const DW_FORM_addr: u16 = 0x01;
pub const DW_FORM_data1: u16 = 0x0b;
pub const DW_FORM_data2: u16 = 0x05;
pub const DW_FORM_data4: u16 = 0x06;
pub const DW_FORM_data8: u16 = 0x07;
pub const DW_FORM_string: u16 = 0x08;
pub const DW_FORM_block: u16 = 0x09;
pub const DW_FORM_block1: u16 = 0x0a;
pub const DW_FORM_block2: u16 = 0x03;
pub const DW_FORM_block4: u16 = 0x04;
pub const DW_FORM_flag: u16 = 0x0c;
pub const DW_FORM_flag_present: u16 = 0x19;
pub const DW_FORM_ref_addr: u16 = 0x10;
pub const DW_FORM_ref1: u16 = 0x11;
pub const DW_FORM_ref2: u16 = 0x12;
pub const DW_FORM_ref4: u16 = 0x13;
pub const DW_FORM_ref8: u16 = 0x14;
pub const DW_FORM_sec_offset: u16 = 0x17;
pub const DW_FORM_exprloc: u16 = 0x18;
pub const DW_FORM_strp: u16 = 0x0e;
pub const DW_FORM_udata: u16 = 0x0f;
pub const DW_FORM_sdata: u16 = 0x0d;
pub const DW_FORM_strx: u16 = 0x1a;
pub const DW_FORM_addrx: u16 = 0x1b;
pub const DW_FORM_line_strp: u16 = 0x1f;
}
pub mod dwarf_inline {
pub const DW_INL_not_inlined: u16 = 0;
pub const DW_INL_inlined: u16 = 1;
pub const DW_INL_declared_not_inlined: u16 = 2;
pub const DW_INL_declared_inlined: u16 = 3;
}
pub mod dwarf_op {
pub const DW_OP_addr: u8 = 0x03;
pub const DW_OP_deref: u8 = 0x06;
pub const DW_OP_const1u: u8 = 0x08;
pub const DW_OP_const1s: u8 = 0x09;
pub const DW_OP_const2u: u8 = 0x0a;
pub const DW_OP_const2s: u8 = 0x0b;
pub const DW_OP_const4u: u8 = 0x0c;
pub const DW_OP_const4s: u8 = 0x0d;
pub const DW_OP_const8u: u8 = 0x0e;
pub const DW_OP_const8s: u8 = 0x0f;
pub const DW_OP_constu: u8 = 0x10;
pub const DW_OP_consts: u8 = 0x11;
pub const DW_OP_dup: u8 = 0x12;
pub const DW_OP_drop: u8 = 0x13;
pub const DW_OP_over: u8 = 0x14;
pub const DW_OP_pick: u8 = 0x15;
pub const DW_OP_swap: u8 = 0x16;
pub const DW_OP_rot: u8 = 0x17;
pub const DW_OP_xderef: u8 = 0x18;
pub const DW_OP_abs: u8 = 0x19;
pub const DW_OP_and: u8 = 0x1a;
pub const DW_OP_div: u8 = 0x1b;
pub const DW_OP_minus: u8 = 0x1c;
pub const DW_OP_mod: u8 = 0x1d;
pub const DW_OP_mul: u8 = 0x1e;
pub const DW_OP_neg: u8 = 0x1f;
pub const DW_OP_not: u8 = 0x20;
pub const DW_OP_or: u8 = 0x21;
pub const DW_OP_plus: u8 = 0x22;
pub const DW_OP_plus_uconst: u8 = 0x23;
pub const DW_OP_shl: u8 = 0x24;
pub const DW_OP_shr: u8 = 0x25;
pub const DW_OP_shra: u8 = 0x26;
pub const DW_OP_xor: u8 = 0x27;
pub const DW_OP_bra: u8 = 0x28;
pub const DW_OP_eq: u8 = 0x29;
pub const DW_OP_ge: u8 = 0x2a;
pub const DW_OP_gt: u8 = 0x2b;
pub const DW_OP_le: u8 = 0x2c;
pub const DW_OP_lt: u8 = 0x2d;
pub const DW_OP_ne: u8 = 0x2e;
pub const DW_OP_skip: u8 = 0x2f;
pub const DW_OP_lit0: u8 = 0x30;
pub const DW_OP_lit31: u8 = 0x4f;
pub const DW_OP_reg0: u8 = 0x50;
pub const DW_OP_reg31: u8 = 0x6f;
pub const DW_OP_breg0: u8 = 0x70;
pub const DW_OP_breg31: u8 = 0x8f;
pub const DW_OP_regx: u8 = 0x90;
pub const DW_OP_fbreg: u8 = 0x91;
pub const DW_OP_bregx: u8 = 0x92;
pub const DW_OP_piece: u8 = 0x93;
pub const DW_OP_deref_size: u8 = 0x94;
pub const DW_OP_xderef_size: u8 = 0x95;
pub const DW_OP_nop: u8 = 0x96;
pub const DW_OP_push_object_address: u8 = 0x97;
pub const DW_OP_call2: u8 = 0x98;
pub const DW_OP_call4: u8 = 0x99;
pub const DW_OP_call_ref: u8 = 0x9a;
pub const DW_OP_form_tls_address: u8 = 0x9b;
pub const DW_OP_call_frame_cfa: u8 = 0x9c;
pub const DW_OP_bit_piece: u8 = 0x9d;
pub const DW_OP_implicit_value: u8 = 0x9e;
pub const DW_OP_stack_value: u8 = 0x9f;
pub const DW_OP_implicit_pointer: u8 = 0xa0;
pub const DW_OP_addrx: u8 = 0xa1;
pub const DW_OP_constx: u8 = 0xa2;
pub const DW_OP_entry_value: u8 = 0xa3;
pub const DW_OP_const_type: u8 = 0xa4;
pub const DW_OP_regval_type: u8 = 0xa5;
pub const DW_OP_deref_type: u8 = 0xa6;
pub const DW_OP_xderef_type: u8 = 0xa7;
pub const DW_OP_convert: u8 = 0xa8;
pub const DW_OP_reinterpret: u8 = 0xa9;
}
pub mod x86_64_dwarf_reg {
pub const RAX: u16 = 0;
pub const RDX: u16 = 1;
pub const RCX: u16 = 2;
pub const RBX: u16 = 3;
pub const RSI: u16 = 4;
pub const RDI: u16 = 5;
pub const RBP: u16 = 6;
pub const RSP: u16 = 7;
pub const R8: u16 = 8;
pub const R9: u16 = 9;
pub const R10: u16 = 10;
pub const R11: u16 = 11;
pub const R12: u16 = 12;
pub const R13: u16 = 13;
pub const R14: u16 = 14;
pub const R15: u16 = 15;
pub const RIP: u16 = 16;
pub const XMM0: u16 = 17;
pub const XMM15: u16 = 32;
pub const ST0: u16 = 33;
pub const ST7: u16 = 40;
pub const MM0: u16 = 41;
pub const MM7: u16 = 48;
pub const RFLAGS: u16 = 49;
pub const ES: u16 = 50;
pub const CS: u16 = 51;
pub const SS: u16 = 52;
pub const DS: u16 = 53;
pub const FS: u16 = 54;
pub const GS: u16 = 55;
pub const FS_BASE: u16 = 58;
pub const GS_BASE: u16 = 59;
pub const TR: u16 = 62;
pub const LDTR: u16 = 63;
pub const MXCSR: u16 = 64;
pub const FCW: u16 = 65;
pub const FSW: u16 = 66;
}
pub mod x86_32_dwarf_reg {
pub const EAX: u16 = 0;
pub const ECX: u16 = 1;
pub const EDX: u16 = 2;
pub const EBX: u16 = 3;
pub const ESP: u16 = 4;
pub const EBP: u16 = 5;
pub const ESI: u16 = 6;
pub const EDI: u16 = 7;
pub const EIP: u16 = 8;
pub const ST0: u16 = 11;
pub const ST7: u16 = 18;
pub const XMM0: u16 = 21;
pub const XMM7: u16 = 28;
pub const MM0: u16 = 29;
pub const MM7: u16 = 36;
pub const EFLAGS: u16 = 9;
}
pub fn x86_64_reg_to_dwarf(name: &str) -> Option<u16> {
match name.to_lowercase().as_str() {
"rax" => Some(x86_64_dwarf_reg::RAX),
"rdx" => Some(x86_64_dwarf_reg::RDX),
"rcx" => Some(x86_64_dwarf_reg::RCX),
"rbx" => Some(x86_64_dwarf_reg::RBX),
"rsi" => Some(x86_64_dwarf_reg::RSI),
"rdi" => Some(x86_64_dwarf_reg::RDI),
"rbp" => Some(x86_64_dwarf_reg::RBP),
"rsp" => Some(x86_64_dwarf_reg::RSP),
"r8" => Some(x86_64_dwarf_reg::R8),
"r9" => Some(x86_64_dwarf_reg::R9),
"r10" => Some(x86_64_dwarf_reg::R10),
"r11" => Some(x86_64_dwarf_reg::R11),
"r12" => Some(x86_64_dwarf_reg::R12),
"r13" => Some(x86_64_dwarf_reg::R13),
"r14" => Some(x86_64_dwarf_reg::R14),
"r15" => Some(x86_64_dwarf_reg::R15),
"rip" => Some(x86_64_dwarf_reg::RIP),
"xmm0" => Some(x86_64_dwarf_reg::XMM0),
"xmm1" => Some(18),
"xmm14" => Some(31),
"xmm15" => Some(x86_64_dwarf_reg::XMM15),
"rflags" => Some(x86_64_dwarf_reg::RFLAGS),
_ => None,
}
}
pub fn x86_32_reg_to_dwarf(name: &str) -> Option<u16> {
match name.to_lowercase().as_str() {
"eax" => Some(x86_32_dwarf_reg::EAX),
"ecx" => Some(x86_32_dwarf_reg::ECX),
"edx" => Some(x86_32_dwarf_reg::EDX),
"ebx" => Some(x86_32_dwarf_reg::EBX),
"esp" => Some(x86_32_dwarf_reg::ESP),
"ebp" => Some(x86_32_dwarf_reg::EBP),
"esi" => Some(x86_32_dwarf_reg::ESI),
"edi" => Some(x86_32_dwarf_reg::EDI),
"eip" => Some(x86_32_dwarf_reg::EIP),
_ => None,
}
}
#[derive(Debug, Clone)]
pub struct X86DebugLocationExpr {
pub bytes: Vec<u8>,
pub address_size: u8,
pub is_location: bool,
}
impl X86DebugLocationExpr {
pub fn new(address_size: u8) -> Self {
Self {
bytes: Vec::new(),
address_size,
is_location: true,
}
}
pub fn new_x86_64() -> Self {
Self::new(8)
}
pub fn new_x86_32() -> Self {
Self::new(4)
}
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();
}
fn emit_u8(&mut self, byte: u8) {
self.bytes.push(byte);
}
fn emit_u16(&mut self, val: u16) {
self.bytes.extend_from_slice(&val.to_le_bytes());
}
fn emit_u32(&mut self, val: u32) {
self.bytes.extend_from_slice(&val.to_le_bytes());
}
fn emit_u64(&mut self, val: u64) {
self.bytes.extend_from_slice(&val.to_le_bytes());
}
fn emit_addr(&mut self, addr: u64) {
match self.address_size {
4 => self.emit_u32(addr as u32),
8 => self.emit_u64(addr),
_ => self.emit_u64(addr),
}
}
fn emit_uleb128(&mut self, mut val: u64) {
loop {
let mut byte = (val & 0x7f) as u8;
val >>= 7;
if val != 0 {
byte |= 0x80;
}
self.bytes.push(byte);
if val == 0 {
break;
}
}
}
fn emit_sleb128(&mut self, val: i64) {
let mut more = true;
let mut v = val;
while more {
let mut byte = (v & 0x7f) as u8;
v >>= 7;
if (v == 0 && byte & 0x40 == 0) || (v == -1 && byte & 0x40 != 0) {
more = false;
} else {
byte |= 0x80;
}
self.bytes.push(byte);
}
}
pub fn addr(&mut self, address: u64) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_addr);
self.emit_addr(address);
self
}
pub fn deref(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_deref);
self
}
pub fn deref_size(&mut self, size: u8) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_deref_size);
self.emit_u8(size);
self
}
pub fn reg(&mut self, dwarf_reg: u16) -> &mut Self {
if dwarf_reg < 32 {
self.emit_u8(dwarf_op::DW_OP_reg0 + dwarf_reg as u8);
} else {
self.emit_u8(dwarf_op::DW_OP_regx);
self.emit_uleb128(dwarf_reg as u64);
}
self
}
pub fn breg(&mut self, dwarf_reg: u16, offset: i64) -> &mut Self {
if dwarf_reg < 32 {
self.emit_u8(dwarf_op::DW_OP_breg0 + dwarf_reg as u8);
} else {
self.emit_u8(dwarf_op::DW_OP_bregx);
self.emit_uleb128(dwarf_reg as u64);
}
self.emit_sleb128(offset);
self
}
pub fn fbreg(&mut self, offset: i64) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_fbreg);
self.emit_sleb128(offset);
self
}
pub fn rbp_offset_64(&mut self, offset: i64) -> &mut Self {
self.breg(x86_64_dwarf_reg::RBP, offset)
}
pub fn ebp_offset_32(&mut self, offset: i64) -> &mut Self {
self.breg(x86_32_dwarf_reg::EBP, offset)
}
pub fn in_rax(&mut self) -> &mut Self {
self.reg(x86_64_dwarf_reg::RAX)
}
pub fn in_xmm(&mut self, index: u16) -> &mut Self {
let dwarf_reg = x86_64_dwarf_reg::XMM0 + index;
self.reg(dwarf_reg)
}
pub fn piece(&mut self, size_in_bytes: u64) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_piece);
self.emit_uleb128(size_in_bytes);
self
}
pub fn bit_piece(&mut self, size_in_bits: u64, offset_in_bits: u64) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_bit_piece);
self.emit_uleb128(size_in_bits);
self.emit_uleb128(offset_in_bits);
self
}
pub fn stack_value(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_stack_value);
self.is_location = false;
self
}
pub fn implicit_value(&mut self, data: &[u8]) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_implicit_value);
self.emit_uleb128(data.len() as u64);
self.bytes.extend_from_slice(data);
self.is_location = false;
self
}
pub fn entry_value(&mut self, inner: &X86DebugLocationExpr) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_entry_value);
self.emit_uleb128(inner.len() as u64);
self.bytes.extend_from_slice(inner.as_bytes());
self
}
pub fn plus(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_plus);
self
}
pub fn minus(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_minus);
self
}
pub fn mul(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_mul);
self
}
pub fn div(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_div);
self
}
pub fn and(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_and);
self
}
pub fn or(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_or);
self
}
pub fn xor(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_xor);
self
}
pub fn shl(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_shl);
self
}
pub fn shr(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_shr);
self
}
pub fn plus_uconst(&mut self, val: u64) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_plus_uconst);
self.emit_uleb128(val);
self
}
pub fn dup(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_dup);
self
}
pub fn drop(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_drop);
self
}
pub fn over(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_over);
self
}
pub fn swap(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_swap);
self
}
pub fn rot(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_rot);
self
}
pub fn pick(&mut self, index: u8) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_pick);
self.emit_u8(index);
self
}
pub fn form_tls_address(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_form_tls_address);
self
}
pub fn call_frame_cfa(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_call_frame_cfa);
self
}
pub fn nop(&mut self) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_nop);
self
}
pub fn local_var_below_rbp(&mut self, offset: i64, is_64bit: bool) -> &mut Self {
if is_64bit {
self.rbpf(offset)
} else {
self.ebp_offset_32(offset)
}
}
pub fn param_in_register(&mut self, reg_name: &str) -> &mut Self {
if let Some(dwarf_reg) = x86_64_reg_to_dwarf(reg_name) {
self.reg(dwarf_reg)
} else {
self
}
}
pub fn param_on_stack(&mut self, offset_from_cfa: i64) -> &mut Self {
self.call_frame_cfa();
self.plus_uconst(offset_from_cfa as u64);
self
}
pub fn composite_location(&mut self, pieces: &[(u64, &X86DebugLocationExpr)]) -> &mut Self {
for (size, expr) in pieces {
self.bytes.extend_from_slice(expr.as_bytes());
self.piece(*size);
}
self
}
pub fn convert(&mut self, type_die_offset: u64) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_convert);
self.emit_uleb128(type_die_offset);
self
}
pub fn reinterpret(&mut self, type_die_offset: u64) -> &mut Self {
self.emit_u8(dwarf_op::DW_OP_reinterpret);
self.emit_uleb128(type_die_offset);
self
}
fn rbpf(&mut self, offset: i64) -> &mut Self {
self.breg(x86_64_dwarf_reg::RBP, offset)
}
}
impl Default for X86DebugLocationExpr {
fn default() -> Self {
Self::new_x86_64()
}
}
#[derive(Debug, Clone)]
pub struct X86DebugFileEntry {
pub name: String,
pub directory_index: u32,
pub mod_time: u32,
pub length: u32,
}
impl X86DebugFileEntry {
pub fn new(name: &str, dir_index: u32) -> Self {
Self {
name: name.to_string(),
directory_index: dir_index,
mod_time: 0,
length: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct X86DebugLineEntry {
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 X86DebugLineEntry {
fn default() -> Self {
Self {
line: 0,
column: 0,
file: 1,
address: 0,
is_stmt: true,
basic_block: false,
prologue_end: false,
epilogue_begin: false,
isa: 0,
discriminator: 0,
end_sequence: false,
}
}
}
#[derive(Debug, Clone)]
pub struct X86DebugLineTable {
pub comp_dir: String,
pub files: Vec<X86DebugFileEntry>,
pub directories: Vec<String>,
pub entries: Vec<X86DebugLineEntry>,
pub version: u16,
pub address_size: u8,
pub min_insn_length: u8,
pub max_ops_per_insn: u8,
pub default_is_stmt: bool,
pub line_base: i8,
pub line_range: u8,
pub opcode_base: u8,
pub standard_opcode_lengths: Vec<u8>,
}
impl X86DebugLineTable {
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: true,
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 tbl = Self::new_x86_64();
tbl.address_size = 4;
tbl
}
pub fn set_comp_dir(&mut self, dir: &str) {
self.comp_dir = dir.to_string();
}
pub fn add_file(&mut self, name: &str, dir_index: u32) -> u32 {
self.files.push(X86DebugFileEntry::new(name, dir_index));
self.files.len() as u32
}
pub fn add_directory(&mut self, path: &str) -> u32 {
self.directories.push(path.to_string());
self.directories.len() as u32
}
pub fn add_entry(&mut self, entry: X86DebugLineEntry) {
self.entries.push(entry);
}
pub fn add_line(&mut self, line: u32, column: u32, file: u32, address: u64) {
self.add_entry(X86DebugLineEntry {
line,
column,
file,
address,
is_stmt: true,
..Default::default()
});
}
pub fn add_end_sequence(&mut self, address: u64) {
self.add_entry(X86DebugLineEntry {
address,
end_sequence: true,
..Default::default()
});
}
pub fn add_prologue_end(&mut self, line: u32, address: u64) {
self.add_entry(X86DebugLineEntry {
line,
address,
prologue_end: true,
..Default::default()
});
}
pub fn add_epilogue_begin(&mut self, line: u32, address: u64) {
self.add_entry(X86DebugLineEntry {
line,
address,
epilogue_begin: true,
..Default::default()
});
}
pub fn emit(&self) -> Vec<u8> {
let mut data = Vec::new();
let length_offset = 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(if self.default_is_stmt { 1 } else { 0 });
data.push(self.line_base as u8);
data.push(self.line_range);
data.push(self.opcode_base);
data.extend_from_slice(&self.standard_opcode_lengths);
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);
data.extend_from_slice(&file.directory_index.to_le_bytes());
data.extend_from_slice(&file.mod_time.to_le_bytes());
data.extend_from_slice(&file.length.to_le_bytes());
}
data.push(0);
let program_start = data.len();
let mut prev_address: u64 = 0;
let mut prev_file: u32 = 1;
let mut prev_line: u32 = 1;
let mut prev_column: u32 = 0;
for entry in &self.entries {
if entry.end_sequence {
data.push(0); data.extend_from_slice(&entry.address.to_le_bytes());
data.push(0);
data.push(1);
data.push(1); prev_address = 0;
prev_file = 1;
prev_line = 1;
prev_column = 0;
continue;
}
if entry.file != prev_file {
data.push(4); data.extend_from_slice(&entry.file.to_le_bytes());
prev_file = entry.file;
}
if entry.column != prev_column {
data.push(5); data.extend_from_slice(&entry.column.to_le_bytes());
prev_column = entry.column;
}
if entry.address != prev_address {
let delta = entry.address - prev_address;
data.push(2); let mut val = delta;
loop {
let mut byte = (val & 0x7f) as u8;
val >>= 7;
if val != 0 {
byte |= 0x80;
}
data.push(byte);
if val == 0 {
break;
}
}
prev_address = entry.address;
}
if entry.line != prev_line {
let delta = entry.line as i32 - prev_line as i32;
data.push(3); let mut val = delta as i64;
loop {
let mut byte = (val & 0x7f) as u8;
val >>= 7;
if (val == 0 && byte & 0x40 == 0) || (val == -1 && byte & 0x40 != 0) {
data.push(byte);
break;
} else {
byte |= 0x80;
}
data.push(byte);
}
prev_line = entry.line;
}
data.push(1);
}
let header_size = (program_start - header_length_offset - 4) as u32;
let header_bytes = header_size.to_le_bytes();
for (i, b) in header_bytes.iter().enumerate() {
data[header_length_offset + i] = *b;
}
let unit_size = (data.len() - length_offset - 4) as u32;
let unit_bytes = unit_size.to_le_bytes();
for (i, b) in unit_bytes.iter().enumerate() {
data[length_offset + i] = *b;
}
data
}
pub fn file_count(&self) -> usize {
self.files.len()
}
pub fn entry_count(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
impl Default for X86DebugLineTable {
fn default() -> Self {
Self::new_x86_64()
}
}
#[derive(Debug, Clone)]
pub struct X86DebugAttribute {
pub name: u16,
pub form: u16,
pub value: X86DebugAttrValue,
}
#[derive(Debug, Clone)]
pub enum X86DebugAttrValue {
String(String),
Data1(u8),
Data2(u16),
Data4(u32),
Data8(u64),
Flag(bool),
Ref(u32), ExprLoc(Vec<u8>), Block(Vec<u8>), UData(u64),
SData(i64),
SecOffset(u32),
}
#[derive(Debug, Clone)]
pub struct X86DebugDIE {
pub tag: u16,
pub attributes: Vec<X86DebugAttribute>,
pub children: Vec<X86DebugDIE>,
}
impl X86DebugDIE {
pub fn new(tag: u16) -> Self {
Self {
tag,
attributes: Vec::new(),
children: Vec::new(),
}
}
pub fn add_attr_string(&mut self, name: u16, value: &str) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_string,
value: X86DebugAttrValue::String(value.to_string()),
});
}
pub fn add_attr_data4(&mut self, name: u16, value: u32) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_data4,
value: X86DebugAttrValue::Data4(value),
});
}
pub fn add_attr_data8(&mut self, name: u16, value: u64) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_data8,
value: X86DebugAttrValue::Data8(value),
});
}
pub fn add_attr_addr(&mut self, name: u16, value: u64) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_addr,
value: X86DebugAttrValue::Data8(value),
});
}
pub fn add_attr_flag(&mut self, name: u16, value: bool) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_flag,
value: X86DebugAttrValue::Flag(value),
});
}
pub fn add_attr_ref(&mut self, name: u16, die_offset: u32) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_ref4,
value: X86DebugAttrValue::Ref(die_offset),
});
}
pub fn add_attr_exprloc(&mut self, name: u16, expr: &X86DebugLocationExpr) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_exprloc,
value: X86DebugAttrValue::ExprLoc(expr.bytes.clone()),
});
}
pub fn add_attr_udata(&mut self, name: u16, value: u64) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_udata,
value: X86DebugAttrValue::UData(value),
});
}
pub fn add_attr_sdata(&mut self, name: u16, value: i64) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_sdata,
value: X86DebugAttrValue::SData(value),
});
}
pub fn add_attr_sec_offset(&mut self, name: u16, value: u32) {
self.attributes.push(X86DebugAttribute {
name,
form: dwarf_form::DW_FORM_sec_offset,
value: X86DebugAttrValue::SecOffset(value),
});
}
pub fn add_child(&mut self, child: X86DebugDIE) {
self.children.push(child);
}
}
#[derive(Debug, Clone)]
pub struct X86DebugCompileUnit {
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: X86DebugLineTable,
pub subprograms: Vec<X86DebugSubprogram>,
pub global_variables: Vec<X86DebugVariable>,
pub section_offset: u64,
pub low_pc: u64,
pub high_pc: u64,
pub use_dwarf5_line: bool,
}
impl X86DebugCompileUnit {
pub fn new_x86_64(file: &str, comp_dir: &str, producer: &str) -> Self {
Self {
comp_dir: comp_dir.to_string(),
file_name: file.to_string(),
producer: producer.to_string(),
language: dwarf_lang::DW_LANG_C_plus_plus_17,
address_size: 8,
dwarf_version: 4,
line_table: X86DebugLineTable::new_x86_64(),
subprograms: Vec::new(),
global_variables: Vec::new(),
section_offset: 0,
low_pc: 0,
high_pc: 0,
use_dwarf5_line: false,
}
}
pub fn new_x86_32(file: &str, comp_dir: &str, producer: &str) -> Self {
let mut cu = Self::new_x86_64(file, comp_dir, producer);
cu.address_size = 4;
cu.line_table = X86DebugLineTable::new_x86_32();
cu
}
pub fn set_language(&mut self, lang: u16) {
self.language = lang;
}
pub fn add_subprogram(&mut self, sp: X86DebugSubprogram) {
self.subprograms.push(sp);
}
pub fn add_global_variable(&mut self, var: X86DebugVariable) {
self.global_variables.push(var);
}
pub fn set_pc_range(&mut self, low_pc: u64, high_pc: u64) {
self.low_pc = low_pc;
self.high_pc = high_pc;
}
pub fn build_die(&self) -> X86DebugDIE {
let mut cu_die = X86DebugDIE::new(dwarf_tag::DW_TAG_compile_unit);
cu_die.add_attr_string(dwarf_attr::DW_AT_name, &self.file_name);
cu_die.add_attr_string(dwarf_attr::DW_AT_comp_dir, &self.comp_dir);
cu_die.add_attr_string(dwarf_attr::DW_AT_producer, &self.producer);
cu_die.add_attr_data4(dwarf_attr::DW_AT_language, self.language as u32);
cu_die.add_attr_addr(dwarf_attr::DW_AT_low_pc, self.low_pc);
cu_die.add_attr_data8(dwarf_attr::DW_AT_high_pc, self.high_pc - self.low_pc);
cu_die.add_attr_sec_offset(dwarf_attr::DW_AT_stmt_list, 0); cu_die
}
pub fn subprogram_count(&self) -> usize {
self.subprograms.len()
}
pub fn global_var_count(&self) -> usize {
self.global_variables.len()
}
}
#[derive(Debug, Clone)]
pub struct X86DebugSubprogram {
pub name: String,
pub linkage_name: Option<String>,
pub low_pc: u64,
pub high_pc: u64,
pub frame_base: Option<X86DebugLocationExpr>,
pub return_type: Option<u32>,
pub parameters: Vec<X86DebugVariable>,
pub variables: Vec<X86DebugVariable>,
pub lexical_blocks: Vec<X86DebugLexicalBlock>,
pub inlined_subroutines: Vec<X86DebugInlinedSubroutine>,
pub is_external: bool,
pub inline_code: u16,
pub decl_file: u32,
pub decl_line: u32,
pub decl_column: u32,
pub is_artificial: bool,
pub is_declaration: bool,
pub abstract_origin: Option<u32>,
}
impl X86DebugSubprogram {
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: false,
inline_code: dwarf_inline::DW_INL_not_inlined,
decl_file: 1,
decl_line: 1,
decl_column: 0,
is_artificial: false,
is_declaration: false,
abstract_origin: None,
}
}
pub fn set_frame_base(&mut self, expr: X86DebugLocationExpr) {
self.frame_base = Some(expr);
}
pub fn set_return_type(&mut self, offset: u32) {
self.return_type = Some(offset);
}
pub fn set_linkage_name(&mut self, name: &str) {
self.linkage_name = Some(name.to_string());
}
pub fn add_parameter(&mut self, param: X86DebugVariable) {
self.parameters.push(param);
}
pub fn add_variable(&mut self, var: X86DebugVariable) {
self.variables.push(var);
}
pub fn add_lexical_block(&mut self, block: X86DebugLexicalBlock) {
self.lexical_blocks.push(block);
}
pub fn add_inlined_subroutine(&mut self, inline: X86DebugInlinedSubroutine) {
self.inlined_subroutines.push(inline);
}
pub fn set_inlined(&mut self, code: u16) {
self.inline_code = code;
}
pub fn set_abstract_origin(&mut self, offset: u32) {
self.abstract_origin = Some(offset);
}
pub fn compute_x86_64_frame_base(&mut self) {
let mut fb = X86DebugLocationExpr::new_x86_64();
fb.call_frame_cfa();
self.frame_base = Some(fb);
}
pub fn compute_x86_32_frame_base(&mut self) {
let mut fb = X86DebugLocationExpr::new_x86_32();
fb.call_frame_cfa();
self.frame_base = Some(fb);
}
pub fn build_die(&self) -> X86DebugDIE {
let mut die = X86DebugDIE::new(dwarf_tag::DW_TAG_subprogram);
die.add_attr_string(dwarf_attr::DW_AT_name, &self.name);
die.add_attr_addr(dwarf_attr::DW_AT_low_pc, self.low_pc);
die.add_attr_data8(dwarf_attr::DW_AT_high_pc, self.high_pc - self.low_pc);
if let Some(ref ln) = self.linkage_name {
die.add_attr_string(dwarf_attr::DW_AT_linkage_name, ln);
}
if self.is_external {
die.add_attr_flag(dwarf_attr::DW_AT_external, true);
}
if let Some(ref fb) = self.frame_base {
die.add_attr_exprloc(dwarf_attr::DW_AT_frame_base, fb);
}
if let Some(ret) = self.return_type {
die.add_attr_ref(dwarf_attr::DW_AT_type, ret);
}
die
}
}
#[derive(Debug, Clone)]
pub struct X86DebugVariable {
pub name: String,
pub tag: u16,
pub type_name: Option<String>,
pub type_offset: Option<u32>,
pub location: Option<X86DebugLocationExpr>,
pub decl_file: u32,
pub decl_line: u32,
pub decl_column: u32,
pub is_external: bool,
pub is_artificial: bool,
}
impl X86DebugVariable {
pub fn new(name: &str, tag: u16) -> Self {
Self {
name: name.to_string(),
tag,
type_name: None,
type_offset: None,
location: None,
decl_file: 1,
decl_line: 0,
decl_column: 0,
is_external: false,
is_artificial: false,
}
}
pub fn new_parameter(name: &str) -> Self {
Self::new(name, dwarf_tag::DW_TAG_formal_parameter)
}
pub fn new_local(name: &str) -> Self {
Self::new(name, dwarf_tag::DW_TAG_variable)
}
pub fn set_type(&mut self, type_name: &str, type_offset: u32) {
self.type_name = Some(type_name.to_string());
self.type_offset = Some(type_offset);
}
pub fn set_location(&mut self, loc: X86DebugLocationExpr) {
self.location = Some(loc);
}
pub fn build_die(&self) -> X86DebugDIE {
let mut die = X86DebugDIE::new(self.tag);
die.add_attr_string(dwarf_attr::DW_AT_name, &self.name);
if let Some(off) = self.type_offset {
die.add_attr_ref(dwarf_attr::DW_AT_type, off);
}
if let Some(ref loc) = self.location {
die.add_attr_exprloc(dwarf_attr::DW_AT_location, loc);
}
if self.decl_file > 0 {
die.add_attr_data4(dwarf_attr::DW_AT_decl_file, self.decl_file);
}
if self.decl_line > 0 {
die.add_attr_data4(dwarf_attr::DW_AT_decl_line, self.decl_line);
}
if self.is_artificial {
die.add_attr_flag(dwarf_attr::DW_AT_artificial, true);
}
die
}
}
#[derive(Debug, Clone)]
pub struct X86DebugLexicalBlock {
pub low_pc: u64,
pub high_pc: u64,
pub variables: Vec<X86DebugVariable>,
pub nested_blocks: Vec<X86DebugLexicalBlock>,
}
impl X86DebugLexicalBlock {
pub fn new(low_pc: u64, high_pc: u64) -> Self {
Self {
low_pc,
high_pc,
variables: Vec::new(),
nested_blocks: Vec::new(),
}
}
pub fn add_variable(&mut self, var: X86DebugVariable) {
self.variables.push(var);
}
pub fn build_die(&self) -> X86DebugDIE {
let mut die = X86DebugDIE::new(dwarf_tag::DW_TAG_lexical_block);
die.add_attr_addr(dwarf_attr::DW_AT_low_pc, self.low_pc);
die.add_attr_data8(dwarf_attr::DW_AT_high_pc, self.high_pc - self.low_pc);
for var in &self.variables {
die.add_child(var.build_die());
}
die
}
}
#[derive(Debug, Clone)]
pub struct X86DebugInlinedSubroutine {
pub abstract_origin: u32,
pub low_pc: u64,
pub high_pc: u64,
pub call_file: u32,
pub call_line: u32,
pub call_column: u32,
pub variables: Vec<X86DebugVariable>,
pub lexical_blocks: Vec<X86DebugLexicalBlock>,
}
impl X86DebugInlinedSubroutine {
pub fn new(abstract_origin: u32, low_pc: u64, high_pc: u64) -> Self {
Self {
abstract_origin,
low_pc,
high_pc,
call_file: 1,
call_line: 0,
call_column: 0,
variables: Vec::new(),
lexical_blocks: Vec::new(),
}
}
pub fn set_call_site(&mut self, file: u32, line: u32, column: u32) {
self.call_file = file;
self.call_line = line;
self.call_column = column;
}
pub fn build_die(&self) -> X86DebugDIE {
let mut die = X86DebugDIE::new(dwarf_tag::DW_TAG_inlined_subroutine);
die.add_attr_ref(dwarf_attr::DW_AT_abstract_origin, self.abstract_origin);
die.add_attr_addr(dwarf_attr::DW_AT_low_pc, self.low_pc);
die.add_attr_data8(dwarf_attr::DW_AT_high_pc, self.high_pc - self.low_pc);
die.add_attr_data4(dwarf_attr::DW_AT_call_file, self.call_file);
die.add_attr_data4(dwarf_attr::DW_AT_call_line, self.call_line);
for var in &self.variables {
die.add_child(var.build_die());
}
for block in &self.lexical_blocks {
die.add_child(block.build_die());
}
die
}
}
#[derive(Debug, Clone)]
pub struct X86DbgValue {
pub variable_name: String,
pub location: X86DebugLocationExpr,
pub instruction_index: u32,
pub is_undef: bool,
}
impl X86DbgValue {
pub fn new(var_name: &str, location: X86DebugLocationExpr, inst_idx: u32) -> Self {
Self {
variable_name: var_name.to_string(),
location,
instruction_index: inst_idx,
is_undef: false,
}
}
pub fn undef(var_name: &str, inst_idx: u32) -> Self {
Self {
variable_name: var_name.to_string(),
location: X86DebugLocationExpr::default(),
instruction_index: inst_idx,
is_undef: true,
}
}
}
#[derive(Debug, Clone)]
pub struct X86DebugLocRange {
pub start_inst: u32,
pub end_inst: u32,
pub location: X86DebugLocationExpr,
}
#[derive(Debug, Clone)]
pub struct X86DebugVarLocTracker {
pub function_name: String,
pub var_ranges: HashMap<String, Vec<X86DebugLocRange>>,
pub dbg_values: Vec<X86DbgValue>,
}
impl X86DebugVarLocTracker {
pub fn new(func_name: &str) -> Self {
Self {
function_name: func_name.to_string(),
var_ranges: HashMap::new(),
dbg_values: Vec::new(),
}
}
pub fn record_dbg_value(&mut self, dbg: X86DbgValue) {
self.dbg_values.push(dbg.clone());
let ranges = self
.var_ranges
.entry(dbg.variable_name.clone())
.or_insert_with(Vec::new);
if let Some(last) = ranges.last_mut() {
last.end_inst = dbg.instruction_index;
}
ranges.push(X86DebugLocRange {
start_inst: dbg.instruction_index,
end_inst: dbg.instruction_index + 1,
location: dbg.location,
});
}
pub fn register_in_reg(
&mut self,
var_name: &str,
reg_name: &str,
start_inst: u32,
end_inst: u32,
is_64bit: bool,
) {
let mut loc = if is_64bit {
X86DebugLocationExpr::new_x86_64()
} else {
X86DebugLocationExpr::new_x86_32()
};
loc.param_in_register(reg_name);
let ranges = self
.var_ranges
.entry(var_name.to_string())
.or_insert_with(Vec::new);
ranges.push(X86DebugLocRange {
start_inst,
end_inst,
location: loc,
});
}
pub fn register_on_stack(
&mut self,
var_name: &str,
frame_offset: i32,
start_inst: u32,
end_inst: u32,
is_64bit: bool,
) {
let mut loc = if is_64bit {
X86DebugLocationExpr::new_x86_64()
} else {
X86DebugLocationExpr::new_x86_32()
};
loc.local_var_below_rbp(frame_offset as i64, is_64bit);
let ranges = self
.var_ranges
.entry(var_name.to_string())
.or_insert_with(Vec::new);
ranges.push(X86DebugLocRange {
start_inst,
end_inst,
location: loc,
});
}
pub fn get_locations(&self, var_name: &str) -> Option<&Vec<X86DebugLocRange>> {
self.var_ranges.get(var_name)
}
pub fn tracked_variables(&self) -> Vec<String> {
self.var_ranges.keys().cloned().collect()
}
pub fn dbg_value_count(&self) -> usize {
self.dbg_values.len()
}
}
impl Default for X86DebugVarLocTracker {
fn default() -> Self {
Self::new("unknown")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CVLeafType {
LF_POINTER = 0x1002,
LF_MODIFIER = 0x1001,
LF_PROCEDURE = 0x1008,
LF_MFUNCTION = 0x1009,
LF_ARGLIST = 0x1201,
LF_FIELDLIST = 0x1203,
LF_STRUCTURE = 0x1505,
LF_UNION = 0x1506,
LF_ENUM = 0x1507,
LF_ARRAY = 0x1508,
LF_CLASS = 0x1509,
LF_MEMBER = 0x150d,
LF_STMEMBER = 0x150e,
LF_METHOD = 0x150f,
LF_NESTTYPE = 0x1510,
LF_ONEMETHOD = 0x1511,
LF_ENUMERATE = 0x1502,
LF_BITFIELD = 0x1205,
LF_METHODLIST = 0x1206,
}
#[derive(Debug, Clone)]
pub struct X86CVTypeRecord {
pub leaf: u16,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CVSymbolKind {
S_COMPILE3 = 0x113c,
S_OBJNAME = 0x1101,
S_GPROC32 = 0x1110,
S_GPROC32_ID = 0x1146,
S_LPROC32 = 0x110f,
S_LPROC32_ID = 0x1147,
S_END = 0x0006,
S_BLOCK32 = 0x1103,
S_LABEL32 = 0x1105,
S_REGREL32 = 0x1111,
S_CONSTANT = 0x1107,
S_UDT = 0x1108,
S_LDATA32 = 0x110c,
S_GDATA32 = 0x110d,
S_PUB32 = 0x110e,
S_LTHREAD32 = 0x1112,
S_GTHREAD32 = 0x1113,
S_FRAMEPROC = 0x1012,
S_FRAMECOOKIE = 0x1135,
S_CALLSITEINFO = 0x1139,
S_HEAPALLOCSITE = 0x1140,
S_INLINESITE = 0x114d,
S_INLINESITE_END = 0x114e,
S_DEFRANGE_REGISTER = 0x1141,
S_DEFRANGE_FRAMEPOINTER_REL = 0x1142,
}
#[derive(Debug, Clone)]
pub struct X86CVSymbolRecord {
pub kind: u16,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct X86CVLineEntry {
pub offset: u32, pub line_start: u32, }
#[derive(Debug, Clone)]
pub struct X86CVLineTable {
pub section_index: u32,
pub entries: Vec<X86CVLineEntry>,
pub file_checksums: Vec<(String, Vec<u8>)>,
pub source_files: Vec<String>,
}
impl X86CVLineTable {
pub fn new(section_index: u32) -> Self {
Self {
section_index,
entries: Vec::new(),
file_checksums: Vec::new(),
source_files: Vec::new(),
}
}
pub fn add_line(&mut self, offset: u32, line: u32) {
self.entries.push(X86CVLineEntry {
offset,
line_start: line,
});
}
pub fn add_source_file(&mut self, path: &str) {
if !self.source_files.iter().any(|f| f == path) {
self.source_files.push(path.to_string());
}
}
pub fn entry_count(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
impl Default for X86CVLineTable {
fn default() -> Self {
Self::new(1)
}
}
#[derive(Debug, Clone)]
pub struct X86DebugCodeGen {
pub compile_units: Vec<X86DebugCompileUnit>,
pub current_cu: usize,
pub var_loc_trackers: HashMap<String, X86DebugVarLocTracker>,
pub cv_type_records: Vec<X86CVTypeRecord>,
pub cv_symbol_records: Vec<X86CVSymbolRecord>,
pub cv_line_tables: Vec<X86CVLineTable>,
pub target_triple: String,
pub is_64bit: bool,
pub use_codeview: bool,
pub stats: X86DebugCodeGenStats,
}
#[derive(Debug, Clone, Default)]
pub struct X86DebugCodeGenStats {
pub compile_units: u64,
pub subprograms: u64,
pub variables: u64,
pub line_entries: u64,
pub dbg_values: u64,
pub lexical_blocks: u64,
pub inlined_subroutines: u64,
pub cv_types: u64,
pub cv_symbols: u64,
}
impl X86DebugCodeGen {
pub fn new_x86_64() -> Self {
Self {
compile_units: Vec::new(),
current_cu: 0,
var_loc_trackers: HashMap::new(),
cv_type_records: Vec::new(),
cv_symbol_records: Vec::new(),
cv_line_tables: Vec::new(),
target_triple: "x86_64-unknown-linux-gnu".to_string(),
is_64bit: true,
use_codeview: false,
stats: X86DebugCodeGenStats::default(),
}
}
pub fn new_x86_32() -> Self {
Self {
target_triple: "i386-unknown-linux-gnu".to_string(),
is_64bit: false,
..Self::new_x86_64()
}
}
pub fn as_windows(&mut self) {
self.target_triple = "x86_64-pc-windows-msvc".to_string();
self.use_codeview = true;
}
pub fn create_compile_unit(
&mut self,
file: &str,
comp_dir: &str,
producer: &str,
) -> &mut X86DebugCompileUnit {
let cu = if self.is_64bit {
X86DebugCompileUnit::new_x86_64(file, comp_dir, producer)
} else {
X86DebugCompileUnit::new_x86_32(file, comp_dir, producer)
};
self.compile_units.push(cu);
self.current_cu = self.compile_units.len() - 1;
self.stats.compile_units += 1;
&mut self.compile_units[self.current_cu]
}
pub fn current_compile_unit(&self) -> Option<&X86DebugCompileUnit> {
self.compile_units.get(self.current_cu)
}
pub fn current_compile_unit_mut(&mut self) -> Option<&mut X86DebugCompileUnit> {
self.compile_units.get_mut(self.current_cu)
}
pub fn add_subprogram(&mut self, name: &str, low_pc: u64, high_pc: u64) {
let mut sp = X86DebugSubprogram::new(name, low_pc, high_pc);
if self.is_64bit {
sp.compute_x86_64_frame_base();
} else {
sp.compute_x86_32_frame_base();
}
sp.is_external = true;
if let Some(cu) = self.current_compile_unit_mut() {
cu.add_subprogram(sp);
self.stats.subprograms += 1;
}
}
pub fn add_local_variable(
&mut self,
var_name: &str,
frame_offset: i32,
type_name: &str,
_type_offset: u32,
) {
let mut var = X86DebugVariable::new_local(var_name);
var.set_type(type_name, _type_offset);
let mut loc = if self.is_64bit {
X86DebugLocationExpr::new_x86_64()
} else {
X86DebugLocationExpr::new_x86_32()
};
loc.local_var_below_rbp(frame_offset as i64, self.is_64bit);
var.set_location(loc);
if let Some(cu) = self.current_compile_unit_mut() {
if let Some(sp) = cu.subprograms.last_mut() {
sp.add_variable(var);
self.stats.variables += 1;
}
}
}
pub fn add_parameter(
&mut self,
param_name: &str,
reg_name: &str,
type_name: &str,
type_offset: u32,
) {
let mut var = X86DebugVariable::new_parameter(param_name);
var.set_type(type_name, type_offset);
let mut loc = if self.is_64bit {
X86DebugLocationExpr::new_x86_64()
} else {
X86DebugLocationExpr::new_x86_32()
};
loc.param_in_register(reg_name);
var.set_location(loc);
if let Some(cu) = self.current_compile_unit_mut() {
if let Some(sp) = cu.subprograms.last_mut() {
sp.add_parameter(var);
}
}
}
pub fn begin_function(&mut self, func_name: &str) {
self.var_loc_trackers
.insert(func_name.to_string(), X86DebugVarLocTracker::new(func_name));
}
pub fn record_dbg_value(
&mut self,
func_name: &str,
var_name: &str,
location: X86DebugLocationExpr,
inst_idx: u32,
) {
if let Some(tracker) = self.var_loc_trackers.get_mut(func_name) {
tracker.record_dbg_value(X86DbgValue::new(var_name, location, inst_idx));
self.stats.dbg_values += 1;
}
}
pub fn track_var_in_reg(
&mut self,
func_name: &str,
var_name: &str,
reg_name: &str,
start_inst: u32,
end_inst: u32,
) {
if let Some(tracker) = self.var_loc_trackers.get_mut(func_name) {
tracker.register_in_reg(var_name, reg_name, start_inst, end_inst, self.is_64bit);
}
}
pub fn track_var_on_stack(
&mut self,
func_name: &str,
var_name: &str,
frame_offset: i32,
start_inst: u32,
end_inst: u32,
) {
if let Some(tracker) = self.var_loc_trackers.get_mut(func_name) {
tracker.register_on_stack(var_name, frame_offset, start_inst, end_inst, self.is_64bit);
}
}
pub fn end_function(&mut self, func_name: &str) -> Option<X86DebugVarLocTracker> {
self.var_loc_trackers.remove(func_name)
}
pub fn add_line_entry(&mut self, line: u32, column: u32, file: u32, address: u64) {
if let Some(cu) = self.current_compile_unit_mut() {
cu.line_table.add_line(line, column, file, address);
self.stats.line_entries += 1;
}
}
pub fn add_line_file(&mut self, name: &str, dir_index: u32) -> u32 {
if let Some(cu) = self.current_compile_unit_mut() {
cu.line_table.add_file(name, dir_index)
} else {
0
}
}
pub fn add_line_directory(&mut self, path: &str) -> u32 {
if let Some(cu) = self.current_compile_unit_mut() {
cu.line_table.add_directory(path)
} else {
0
}
}
pub fn add_lexical_block(&mut self, low_pc: u64, high_pc: u64) {
let block = X86DebugLexicalBlock::new(low_pc, high_pc);
if let Some(cu) = self.current_compile_unit_mut() {
if let Some(sp) = cu.subprograms.last_mut() {
sp.add_lexical_block(block);
self.stats.lexical_blocks += 1;
}
}
}
pub fn add_inlined_subroutine(
&mut self,
abstract_origin: u32,
low_pc: u64,
high_pc: u64,
call_file: u32,
call_line: u32,
) {
let mut inline = X86DebugInlinedSubroutine::new(abstract_origin, low_pc, high_pc);
inline.set_call_site(call_file, call_line, 0);
if let Some(cu) = self.current_compile_unit_mut() {
if let Some(sp) = cu.subprograms.last_mut() {
sp.add_inlined_subroutine(inline);
self.stats.inlined_subroutines += 1;
}
}
}
pub fn cv_add_type_record(&mut self, leaf: u16, data: Vec<u8>) {
self.cv_type_records.push(X86CVTypeRecord { leaf, data });
self.stats.cv_types += 1;
}
pub fn cv_add_symbol_record(&mut self, kind: u16, data: Vec<u8>) {
self.cv_symbol_records
.push(X86CVSymbolRecord { kind, data });
self.stats.cv_symbols += 1;
}
pub fn cv_create_line_table(&mut self, section_index: u32) -> usize {
self.cv_line_tables.push(X86CVLineTable::new(section_index));
self.cv_line_tables.len() - 1
}
pub fn cv_add_line_entry(&mut self, table_idx: usize, offset: u32, line: u32) {
if let Some(table) = self.cv_line_tables.get_mut(table_idx) {
table.add_line(offset, line);
self.stats.line_entries += 1;
}
}
pub fn add_global_variable(
&mut self,
name: &str,
address: u64,
type_name: &str,
type_offset: u32,
) {
let mut var = X86DebugVariable::new(name, dwarf_tag::DW_TAG_variable);
var.is_external = true;
var.set_type(type_name, type_offset);
let mut loc = if self.is_64bit {
X86DebugLocationExpr::new_x86_64()
} else {
X86DebugLocationExpr::new_x86_32()
};
loc.addr(address);
var.set_location(loc);
if let Some(cu) = self.current_compile_unit_mut() {
cu.add_global_variable(var);
}
}
pub fn get_stats(&self) -> &X86DebugCodeGenStats {
&self.stats
}
pub fn reset(&mut self) {
self.compile_units.clear();
self.current_cu = 0;
self.var_loc_trackers.clear();
self.cv_type_records.clear();
self.cv_symbol_records.clear();
self.cv_line_tables.clear();
self.stats = X86DebugCodeGenStats::default();
}
}
impl Default for X86DebugCodeGen {
fn default() -> Self {
Self::new_x86_64()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_loc_expr_new_x86_64() {
let expr = X86DebugLocationExpr::new_x86_64();
assert_eq!(expr.address_size, 8);
assert!(expr.is_empty());
}
#[test]
fn test_loc_expr_new_x86_32() {
let expr = X86DebugLocationExpr::new_x86_32();
assert_eq!(expr.address_size, 4);
}
#[test]
fn test_loc_expr_addr() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.addr(0x401000);
assert!(!expr.is_empty());
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_addr);
}
#[test]
fn test_loc_expr_deref() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.deref();
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_deref);
}
#[test]
fn test_loc_expr_reg_compact() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.reg(5); assert_eq!(expr.bytes[0], dwarf_op::DW_OP_reg0 + 5);
}
#[test]
fn test_loc_expr_reg_extended() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.reg(50); assert_eq!(expr.bytes[0], dwarf_op::DW_OP_regx);
}
#[test]
fn test_loc_expr_breg() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.breg(6, -8); assert_eq!(expr.bytes[0], dwarf_op::DW_OP_breg0 + 6);
}
#[test]
fn test_loc_expr_fbreg() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.fbreg(-16);
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_fbreg);
}
#[test]
fn test_loc_expr_piece() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.reg(0).piece(4);
assert!(expr.as_bytes().contains(&dwarf_op::DW_OP_piece));
}
#[test]
fn test_loc_expr_bit_piece() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.reg(0).bit_piece(32, 0);
assert!(expr.as_bytes().contains(&dwarf_op::DW_OP_bit_piece));
}
#[test]
fn test_loc_expr_stack_value() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.stack_value();
assert!(!expr.is_location);
}
#[test]
fn test_loc_expr_implicit_value() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.implicit_value(&[0x42, 0x43]);
assert!(!expr.is_location);
assert!(expr.as_bytes().contains(&0x42));
assert!(expr.as_bytes().contains(&0x43));
}
#[test]
fn test_loc_expr_arithmetic() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.plus();
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_plus);
expr.minus();
expr.mul();
expr.div();
expr.and();
expr.or();
expr.xor();
expr.shl();
expr.shr();
assert!(!expr.is_empty());
}
#[test]
fn test_loc_expr_comparisons() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.reg(0);
expr.plus_uconst(42);
expr.dup();
expr.drop();
expr.over();
expr.swap();
expr.rot();
expr.pick(2);
expr.nop();
assert!(!expr.is_empty());
}
#[test]
fn test_loc_expr_clear() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.addr(0x1000);
expr.clear();
assert!(expr.is_empty());
}
#[test]
fn test_loc_expr_rbp_offset() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.rbp_offset_64(-8);
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_breg0 + 6);
}
#[test]
fn test_loc_expr_in_rax() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.in_rax();
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_reg0);
}
#[test]
fn test_loc_expr_in_xmm() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.in_xmm(5);
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_reg0 + 22); }
#[test]
fn test_x86_64_reg_to_dwarf() {
assert_eq!(x86_64_reg_to_dwarf("rax"), Some(0));
assert_eq!(x86_64_reg_to_dwarf("rbp"), Some(6));
assert_eq!(x86_64_reg_to_dwarf("rsp"), Some(7));
assert_eq!(x86_64_reg_to_dwarf("r15"), Some(15));
assert_eq!(x86_64_reg_to_dwarf("rip"), Some(16));
assert_eq!(x86_64_reg_to_dwarf("nonexistent"), None);
}
#[test]
fn test_x86_32_reg_to_dwarf() {
assert_eq!(x86_32_reg_to_dwarf("eax"), Some(0));
assert_eq!(x86_32_reg_to_dwarf("ebp"), Some(5));
assert_eq!(x86_32_reg_to_dwarf("ebx"), Some(3));
assert_eq!(x86_32_reg_to_dwarf("nonexistent"), None);
}
#[test]
fn test_line_table_new_64() {
let tbl = X86DebugLineTable::new_x86_64();
assert_eq!(tbl.address_size, 8);
assert_eq!(tbl.version, 4);
assert!(tbl.is_empty());
}
#[test]
fn test_line_table_new_32() {
let tbl = X86DebugLineTable::new_x86_32();
assert_eq!(tbl.address_size, 4);
}
#[test]
fn test_line_table_add_file() {
let mut tbl = X86DebugLineTable::new_x86_64();
let idx = tbl.add_file("test.cpp", 0);
assert_eq!(idx, 1);
assert_eq!(tbl.file_count(), 1);
}
#[test]
fn test_line_table_add_directory() {
let mut tbl = X86DebugLineTable::new_x86_64();
let idx = tbl.add_directory("/home/user/src");
assert_eq!(idx, 1);
}
#[test]
fn test_line_table_add_entry() {
let mut tbl = X86DebugLineTable::new_x86_64();
tbl.add_line(10, 5, 1, 0x401000);
tbl.add_line(11, 5, 1, 0x401004);
assert_eq!(tbl.entry_count(), 2);
}
#[test]
fn test_line_table_add_end_sequence() {
let mut tbl = X86DebugLineTable::new_x86_64();
tbl.add_end_sequence(0x401008);
assert_eq!(tbl.entry_count(), 1);
assert!(tbl.entries[0].end_sequence);
}
#[test]
fn test_line_table_emit_non_empty() {
let mut tbl = X86DebugLineTable::new_x86_64();
tbl.add_line(5, 0, 1, 0x1000);
tbl.add_line(6, 0, 1, 0x1004);
let data = tbl.emit();
assert!(!data.is_empty());
assert!(data.len() > 10);
}
#[test]
fn test_line_table_emit_empty() {
let tbl = X86DebugLineTable::new_x86_64();
let data = tbl.emit();
assert!(!data.is_empty());
}
#[test]
fn test_die_new() {
let die = X86DebugDIE::new(dwarf_tag::DW_TAG_compile_unit);
assert_eq!(die.tag, dwarf_tag::DW_TAG_compile_unit);
assert!(die.attributes.is_empty());
assert!(die.children.is_empty());
}
#[test]
fn test_die_add_attributes() {
let mut die = X86DebugDIE::new(dwarf_tag::DW_TAG_subprogram);
die.add_attr_string(dwarf_attr::DW_AT_name, "main");
die.add_attr_data4(dwarf_attr::DW_AT_decl_line, 42);
die.add_attr_flag(dwarf_attr::DW_AT_external, true);
assert_eq!(die.attributes.len(), 3);
}
#[test]
fn test_die_add_child() {
let mut parent = X86DebugDIE::new(dwarf_tag::DW_TAG_compile_unit);
let child = X86DebugDIE::new(dwarf_tag::DW_TAG_subprogram);
parent.add_child(child);
assert_eq!(parent.children.len(), 1);
}
#[test]
fn test_cu_new_x86_64() {
let cu = X86DebugCompileUnit::new_x86_64("main.cpp", "/src", "mycc");
assert_eq!(cu.file_name, "main.cpp");
assert_eq!(cu.comp_dir, "/src");
assert_eq!(cu.producer, "mycc");
assert_eq!(cu.address_size, 8);
}
#[test]
fn test_cu_new_x86_32() {
let cu = X86DebugCompileUnit::new_x86_32("test.c", "/src", "gcc");
assert_eq!(cu.address_size, 4);
}
#[test]
fn test_cu_set_language() {
let mut cu = X86DebugCompileUnit::new_x86_64("f.c", ".", "cc");
cu.set_language(dwarf_lang::DW_LANG_C11);
assert_eq!(cu.language, dwarf_lang::DW_LANG_C11);
}
#[test]
fn test_cu_add_subprogram() {
let mut cu = X86DebugCompileUnit::new_x86_64("f.c", ".", "cc");
cu.add_subprogram(X86DebugSubprogram::new("foo", 0x1000, 0x1100));
assert_eq!(cu.subprogram_count(), 1);
}
#[test]
fn test_cu_add_global_variable() {
let mut cu = X86DebugCompileUnit::new_x86_64("f.c", ".", "cc");
cu.add_global_variable(X86DebugVariable::new_local("g_var"));
assert_eq!(cu.global_var_count(), 1);
}
#[test]
fn test_cu_build_die() {
let mut cu = X86DebugCompileUnit::new_x86_64("test.c", "/tmp", "clang");
cu.set_pc_range(0x4000, 0x5000);
let die = cu.build_die();
assert_eq!(die.tag, dwarf_tag::DW_TAG_compile_unit);
assert!(die.attributes.len() >= 4);
}
#[test]
fn test_subprogram_new() {
let sp = X86DebugSubprogram::new("myfunc", 0x1000, 0x1200);
assert_eq!(sp.name, "myfunc");
assert_eq!(sp.low_pc, 0x1000);
assert_eq!(sp.high_pc, 0x1200);
}
#[test]
fn test_subprogram_with_variables() {
let mut sp = X86DebugSubprogram::new("calc", 0x2000, 0x2100);
sp.add_variable(X86DebugVariable::new_local("x"));
sp.add_parameter(X86DebugVariable::new_parameter("n"));
assert_eq!(sp.variables.len(), 1);
assert_eq!(sp.parameters.len(), 1);
}
#[test]
fn test_subprogram_set_frame_base() {
let mut sp = X86DebugSubprogram::new("fn", 0x1000, 0x1100);
let mut fb = X86DebugLocationExpr::new_x86_64();
fb.call_frame_cfa();
sp.set_frame_base(fb);
assert!(sp.frame_base.is_some());
}
#[test]
fn test_subprogram_compute_frame_base() {
let mut sp = X86DebugSubprogram::new("fn", 0x1000, 0x1100);
sp.compute_x86_64_frame_base();
assert!(sp.frame_base.is_some());
let fb = sp.frame_base.unwrap();
assert_eq!(fb.bytes[0], dwarf_op::DW_OP_call_frame_cfa);
}
#[test]
fn test_subprogram_set_linkage_name() {
let mut sp = X86DebugSubprogram::new("fn", 0x1000, 0x1100);
sp.set_linkage_name("_Z2fnv");
assert_eq!(sp.linkage_name, Some("_Z2fnv".to_string()));
}
#[test]
fn test_subprogram_build_die() {
let mut sp = X86DebugSubprogram::new("main", 0x4000, 0x4100);
sp.compute_x86_64_frame_base();
sp.set_linkage_name("_Z4mainv");
let die = sp.build_die();
assert_eq!(die.tag, dwarf_tag::DW_TAG_subprogram);
assert!(die
.attributes
.iter()
.any(|a| a.name == dwarf_attr::DW_AT_name));
}
#[test]
fn test_variable_new_local() {
let var = X86DebugVariable::new_local("counter");
assert_eq!(var.tag, dwarf_tag::DW_TAG_variable);
assert_eq!(var.name, "counter");
}
#[test]
fn test_variable_new_parameter() {
let var = X86DebugVariable::new_parameter("argc");
assert_eq!(var.tag, dwarf_tag::DW_TAG_formal_parameter);
}
#[test]
fn test_variable_set_type() {
let mut var = X86DebugVariable::new_local("x");
var.set_type("int", 42);
assert_eq!(var.type_name, Some("int".to_string()));
assert_eq!(var.type_offset, Some(42));
}
#[test]
fn test_variable_set_location() {
let mut var = X86DebugVariable::new_local("x");
let mut loc = X86DebugLocationExpr::new_x86_64();
loc.rbp_offset_64(-4);
var.set_location(loc);
assert!(var.location.is_some());
}
#[test]
fn test_variable_build_die() {
let mut var = X86DebugVariable::new_local("x");
var.set_type("int", 10);
let mut loc = X86DebugLocationExpr::new_x86_64();
loc.rbp_offset_64(-4);
var.set_location(loc);
let die = var.build_die();
assert_eq!(die.tag, dwarf_tag::DW_TAG_variable);
}
#[test]
fn test_lexical_block_new() {
let block = X86DebugLexicalBlock::new(0x4000, 0x4100);
assert_eq!(block.low_pc, 0x4000);
assert_eq!(block.high_pc, 0x4100);
assert!(block.variables.is_empty());
}
#[test]
fn test_lexical_block_add_variable() {
let mut block = X86DebugLexicalBlock::new(0x4000, 0x4100);
block.add_variable(X86DebugVariable::new_local("tmp"));
assert_eq!(block.variables.len(), 1);
}
#[test]
fn test_lexical_block_build_die() {
let mut block = X86DebugLexicalBlock::new(0x4000, 0x4100);
block.add_variable(X86DebugVariable::new_local("v"));
let die = block.build_die();
assert_eq!(die.tag, dwarf_tag::DW_TAG_lexical_block);
assert_eq!(die.children.len(), 1);
}
#[test]
fn test_inlined_subroutine_new() {
let inline = X86DebugInlinedSubroutine::new(42, 0x5000, 0x5100);
assert_eq!(inline.abstract_origin, 42);
assert_eq!(inline.low_pc, 0x5000);
}
#[test]
fn test_inlined_subroutine_set_call_site() {
let mut inline = X86DebugInlinedSubroutine::new(42, 0x5000, 0x5100);
inline.set_call_site(1, 100, 5);
assert_eq!(inline.call_file, 1);
assert_eq!(inline.call_line, 100);
}
#[test]
fn test_inlined_subroutine_build_die() {
let mut inline = X86DebugInlinedSubroutine::new(42, 0x5000, 0x5100);
inline.set_call_site(1, 100, 5);
let die = inline.build_die();
assert_eq!(die.tag, dwarf_tag::DW_TAG_inlined_subroutine);
}
#[test]
fn test_var_loc_tracker_new() {
let tracker = X86DebugVarLocTracker::new("test_func");
assert_eq!(tracker.function_name, "test_func");
assert!(tracker.var_ranges.is_empty());
}
#[test]
fn test_var_loc_tracker_record_dbg_value() {
let mut tracker = X86DebugVarLocTracker::new("f");
let loc = X86DebugLocationExpr::new_x86_64();
tracker.record_dbg_value(X86DbgValue::new("x", loc.clone(), 0));
tracker.record_dbg_value(X86DbgValue::new("x", loc, 1));
assert_eq!(tracker.dbg_value_count(), 2);
}
#[test]
fn test_var_loc_tracker_register_in_reg() {
let mut tracker = X86DebugVarLocTracker::new("f");
tracker.register_in_reg("y", "rax", 0, 10, true);
assert_eq!(tracker.tracked_variables(), vec!["y"]);
}
#[test]
fn test_var_loc_tracker_register_on_stack() {
let mut tracker = X86DebugVarLocTracker::new("f");
tracker.register_on_stack("z", -8, 0, 10, true);
assert!(tracker.get_locations("z").is_some());
}
#[test]
fn test_cv_line_table_new() {
let tbl = X86CVLineTable::new(1);
assert_eq!(tbl.section_index, 1);
assert!(tbl.is_empty());
}
#[test]
fn test_cv_line_table_add_line() {
let mut tbl = X86CVLineTable::new(1);
tbl.add_line(0, 10);
tbl.add_line(4, 12);
assert_eq!(tbl.entry_count(), 2);
}
#[test]
fn test_cv_line_table_add_source_file() {
let mut tbl = X86CVLineTable::new(1);
tbl.add_source_file("main.cpp");
tbl.add_source_file("main.cpp"); assert_eq!(tbl.source_files.len(), 1);
}
#[test]
fn test_codegen_new_x86_64() {
let dc = X86DebugCodeGen::new_x86_64();
assert!(dc.is_64bit);
assert!(!dc.use_codeview);
assert_eq!(dc.compile_units.len(), 0);
}
#[test]
fn test_codegen_new_x86_32() {
let dc = X86DebugCodeGen::new_x86_32();
assert!(!dc.is_64bit);
}
#[test]
fn test_codegen_as_windows() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.as_windows();
assert!(dc.use_codeview);
assert!(dc.target_triple.contains("windows"));
}
#[test]
fn test_codegen_create_compile_unit() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("test.c", "/build", "mycc");
assert_eq!(dc.compile_units.len(), 1);
assert_eq!(dc.stats.compile_units, 1);
}
#[test]
fn test_codegen_add_subprogram() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("test.c", ".", "cc");
dc.add_subprogram("main", 0x4000, 0x4100);
let cu = dc.current_compile_unit().unwrap();
assert_eq!(cu.subprogram_count(), 1);
assert_eq!(dc.stats.subprograms, 1);
}
#[test]
fn test_codegen_add_local_variable() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("t.c", ".", "cc");
dc.add_subprogram("f", 0x1000, 0x1100);
dc.add_local_variable("x", -4, "int", 10);
let cu = dc.current_compile_unit().unwrap();
let sp = &cu.subprograms[0];
assert_eq!(sp.variables.len(), 1);
assert_eq!(dc.stats.variables, 1);
}
#[test]
fn test_codegen_add_parameter() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("p.c", ".", "cc");
dc.add_subprogram("g", 0x2000, 0x2100);
dc.add_parameter("argc", "rdi", "int", 10);
let cu = dc.current_compile_unit().unwrap();
assert_eq!(cu.subprograms[0].parameters.len(), 1);
}
#[test]
fn test_codegen_add_line_entry() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("lines.c", ".", "cc");
dc.add_line_entry(10, 5, 1, 0x5000);
dc.add_line_entry(11, 5, 1, 0x5004);
let cu = dc.current_compile_unit().unwrap();
assert_eq!(cu.line_table.entry_count(), 2);
assert_eq!(dc.stats.line_entries, 2);
}
#[test]
fn test_codegen_add_line_file() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("f.c", ".", "cc");
let idx = dc.add_line_file("f.c", 0);
assert_eq!(idx, 1);
}
#[test]
fn test_codegen_add_line_directory() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("f.c", ".", "cc");
let idx = dc.add_line_directory("/include");
assert_eq!(idx, 1);
}
#[test]
fn test_codegen_add_lexical_block() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("b.c", ".", "cc");
dc.add_subprogram("loop", 0x3000, 0x3100);
dc.add_lexical_block(0x3020, 0x3080);
let cu = dc.current_compile_unit().unwrap();
assert_eq!(cu.subprograms[0].lexical_blocks.len(), 1);
assert_eq!(dc.stats.lexical_blocks, 1);
}
#[test]
fn test_codegen_add_inlined_subroutine() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("i.c", ".", "cc");
dc.add_subprogram("outer", 0x4000, 0x4200);
dc.add_inlined_subroutine(100, 0x4100, 0x4150, 1, 5);
let cu = dc.current_compile_unit().unwrap();
assert_eq!(cu.subprograms[0].inlined_subroutines.len(), 1);
assert_eq!(dc.stats.inlined_subroutines, 1);
}
#[test]
fn test_codegen_add_global_variable() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("g.c", ".", "cc");
dc.add_global_variable("global_counter", 0x600000, "int", 10);
let cu = dc.current_compile_unit().unwrap();
assert_eq!(cu.global_var_count(), 1);
}
#[test]
fn test_codegen_variable_location_tracking() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.begin_function("work");
let mut loc = X86DebugLocationExpr::new_x86_64();
loc.rbp_offset_64(-8);
dc.record_dbg_value("work", "result", loc.clone(), 0);
dc.track_var_in_reg("work", "tmp", "rax", 2, 8);
dc.track_var_on_stack("work", "buf", -16, 0, 10);
let tracker = dc.end_function("work").unwrap();
assert_eq!(tracker.tracked_variables().len(), 3);
assert_eq!(dc.stats.dbg_values, 1);
}
#[test]
fn test_codegen_codeview_types() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.as_windows();
dc.cv_add_type_record(0x1002, vec![0x01, 0x02]);
dc.cv_add_symbol_record(0x1110, vec![0x03]);
assert_eq!(dc.stats.cv_types, 1);
assert_eq!(dc.stats.cv_symbols, 1);
}
#[test]
fn test_codegen_codeview_line_table() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.as_windows();
let idx = dc.cv_create_line_table(1);
dc.cv_add_line_entry(idx, 0, 5);
dc.cv_add_line_entry(idx, 4, 6);
assert_eq!(dc.cv_line_tables[idx].entry_count(), 2);
}
#[test]
fn test_codegen_get_stats() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("s.c", ".", "cc");
dc.add_subprogram("f", 0x1000, 0x1100);
let stats = dc.get_stats();
assert_eq!(stats.compile_units, 1);
assert_eq!(stats.subprograms, 1);
}
#[test]
fn test_codegen_reset() {
let mut dc = X86DebugCodeGen::new_x86_64();
dc.create_compile_unit("t.c", ".", "cc");
dc.add_subprogram("f", 0x1000, 0x1100);
dc.reset();
assert_eq!(dc.compile_units.len(), 0);
assert_eq!(dc.stats.compile_units, 0);
assert_eq!(dc.stats.subprograms, 0);
}
#[test]
fn test_codegen_end_to_end() {
let mut dc = X86DebugCodeGen::new_x86_64();
let cu = dc.create_compile_unit("app.cpp", "/project", "X86DebugCompiler 1.0");
dc.add_line_directory("/usr/include");
dc.add_line_file("app.cpp", 0);
dc.add_line_file("header.h", 1);
dc.add_subprogram("main", 0x401000, 0x401200);
dc.add_parameter("argc", "rdi", "int", 1);
dc.add_parameter("argv", "rsi", "char**", 2);
dc.add_local_variable("result", -4, "int", 1);
dc.add_local_variable("buffer", -64, "char[64]", 3);
dc.add_line_entry(3, 1, 1, 0x401000);
dc.add_line_entry(4, 5, 1, 0x401010);
dc.add_line_entry(5, 5, 1, 0x401020);
dc.add_lexical_block(0x401010, 0x401050);
dc.add_inlined_subroutine(10, 0x401100, 0x401180, 1, 4);
cu.set_pc_range(0x401000, 0x401200);
assert_eq!(cu.subprogram_count(), 1);
assert_eq!(cu.line_table.entry_count(), 3);
assert_eq!(cu.line_table.file_count(), 2);
assert_eq!(cu.subprograms[0].inlined_subroutines.len(), 1);
}
#[test]
fn test_dwarf_constants() {
assert_eq!(dwarf_tag::DW_TAG_compile_unit, 0x11);
assert_eq!(dwarf_tag::DW_TAG_subprogram, 0x2e);
assert_eq!(dwarf_attr::DW_AT_name, 0x03);
assert_eq!(dwarf_op::DW_OP_addr, 0x03);
assert_eq!(dwarf_lang::DW_LANG_C_plus_plus_17, 0x002e);
assert_eq!(x86_64_dwarf_reg::RBP, 6);
assert_eq!(x86_64_dwarf_reg::XMM0, 17);
}
#[test]
fn test_loc_expr_entry_value() {
let mut inner = X86DebugLocationExpr::new_x86_64();
inner.reg(0);
let mut outer = X86DebugLocationExpr::new_x86_64();
outer.entry_value(&inner);
assert!(outer.bytes.contains(&dwarf_op::DW_OP_entry_value));
}
#[test]
fn test_loc_expr_form_tls_address() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.reg(0).form_tls_address();
assert!(expr.bytes.contains(&dwarf_op::DW_OP_form_tls_address));
}
#[test]
fn test_loc_expr_call_frame_cfa() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.call_frame_cfa();
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_call_frame_cfa);
}
#[test]
fn test_loc_expr_param_in_register() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.param_in_register("rdi");
assert_eq!(expr.bytes[0], dwarf_op::DW_OP_reg0 + 5); }
#[test]
fn test_loc_expr_param_on_stack() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.call_frame_cfa();
expr.plus_uconst(16);
}
#[test]
fn test_loc_expr_convert_reinterpret() {
let mut expr = X86DebugLocationExpr::new_x86_64();
expr.reg(0);
expr.convert(100);
expr.reinterpret(200);
assert!(expr.bytes.contains(&dwarf_op::DW_OP_convert));
assert!(expr.bytes.contains(&dwarf_op::DW_OP_reinterpret));
}
#[test]
fn test_loc_expr_default() {
let expr = X86DebugLocationExpr::default();
assert_eq!(expr.address_size, 8);
assert!(expr.is_empty());
}
#[test]
fn test_line_table_prologue_end() {
let mut tbl = X86DebugLineTable::new_x86_64();
tbl.add_prologue_end(2, 0x1000);
assert!(tbl.entries[0].prologue_end);
}
#[test]
fn test_line_table_epilogue_begin() {
let mut tbl = X86DebugLineTable::new_x86_64();
tbl.add_epilogue_begin(10, 0x2000);
assert!(tbl.entries[0].epilogue_begin);
}
#[test]
fn test_die_add_attr_exprloc() {
let mut die = X86DebugDIE::new(dwarf_tag::DW_TAG_variable);
let mut loc = X86DebugLocationExpr::new_x86_64();
loc.rbp_offset_64(-8);
die.add_attr_exprloc(dwarf_attr::DW_AT_location, &loc);
assert_eq!(die.attributes.len(), 1);
}
#[test]
fn test_die_add_attr_udata_sdata() {
let mut die = X86DebugDIE::new(dwarf_tag::DW_TAG_enumerator);
die.add_attr_udata(dwarf_attr::DW_AT_const_value, 42);
die.add_attr_sdata(dwarf_attr::DW_AT_const_value, -1);
assert_eq!(die.attributes.len(), 2);
}
#[test]
fn test_line_table_set_comp_dir() {
let mut tbl = X86DebugLineTable::new_x86_64();
tbl.set_comp_dir("/home/project");
assert_eq!(tbl.comp_dir, "/home/project");
}
#[test]
fn test_db_value_undef() {
let dbg = X86DbgValue::undef("opt_out", 5);
assert!(dbg.is_undef);
assert_eq!(dbg.variable_name, "opt_out");
assert_eq!(dbg.instruction_index, 5);
}
}