#![allow(non_upper_case_globals)]
pub mod dwarf_deep;
pub mod dwarf_die;
pub mod dwarf_emitter;
pub mod dwarf_expr;
pub mod dwarf_expr_x86;
pub mod dwarf_frame;
pub mod dwarf_line;
pub mod dwarf_types;
pub use dwarf_die::DwarfDIE;
pub use dwarf_expr::DwarfExpr;
pub use dwarf_frame::DwarfFrame;
pub use dwarf_line::{DwarfLineTable, LineEntry, LineTableHeader};
pub use dwarf_types::DwarfType;
#[allow(dead_code, non_upper_case_globals)]
pub mod dwarf_tags {
pub const DW_TAG_compile_unit: u16 = 0x11;
pub const DW_TAG_subprogram: u16 = 0x2E;
pub const DW_TAG_lexical_block: u16 = 0x0B;
pub const DW_TAG_inlined_subroutine: u16 = 0x1D;
pub const DW_TAG_variable: u16 = 0x34;
pub const DW_TAG_formal_parameter: u16 = 0x05;
pub const DW_TAG_base_type: u16 = 0x24;
pub const DW_TAG_pointer_type: u16 = 0x0F;
pub const DW_TAG_structure_type: u16 = 0x13;
pub const DW_TAG_enumeration_type: u16 = 0x04;
pub const DW_TAG_array_type: u16 = 0x01;
pub const DW_TAG_subroutine_type: u16 = 0x15;
pub const DW_TAG_typedef: u16 = 0x16;
pub const DW_TAG_const_type: u16 = 0x26;
pub const DW_TAG_volatile_type: u16 = 0x35;
pub const DW_TAG_unspecified_parameters: u16 = 0x2F;
pub const DW_TAG_namespace: u16 = 0x39;
pub const DW_TAG_member: u16 = 0x0D;
pub const DW_TAG_subrange_type: u16 = 0x21;
pub const DW_TAG_reference_type: u16 = 0x10;
pub const DW_TAG_rvalue_reference_type: u16 = 0x42;
pub const DW_TAG_class_type: u16 = 0x02;
pub const DW_TAG_union_type: u16 = 0x17;
pub const DW_TAG_restrict_type: u16 = 0x37;
pub const DW_TAG_unspecified_type: u16 = 0x3B;
}
#[allow(dead_code, non_upper_case_globals)]
pub mod dwarf_attributes {
pub const DW_AT_sibling: u16 = 0x01;
pub const DW_AT_name: u16 = 0x03;
pub const DW_AT_byte_size: u16 = 0x0B;
pub const DW_AT_bit_size: u16 = 0x0D;
pub const DW_AT_low_pc: u16 = 0x11;
pub const DW_AT_high_pc: u16 = 0x12;
pub const DW_AT_language: u16 = 0x13;
pub const DW_AT_comp_dir: u16 = 0x1B;
pub const DW_AT_producer: u16 = 0x25;
pub const DW_AT_stmt_list: u16 = 0x10;
pub const DW_AT_type: u16 = 0x49;
pub const DW_AT_data_member_location: u16 = 0x38;
pub const DW_AT_decl_file: u16 = 0x3A;
pub const DW_AT_decl_line: u16 = 0x3B;
pub const DW_AT_decl_column: u16 = 0x3C;
pub const DW_AT_external: u16 = 0x3F;
pub const DW_AT_frame_base: u16 = 0x40;
pub const DW_AT_inline: u16 = 0x20;
pub const DW_AT_linkage_name: u16 = 0x6E;
pub const DW_AT_encoding: u16 = 0x3E;
pub const DW_AT_const_value: u16 = 0x1C;
pub const DW_AT_location: u16 = 0x02;
pub const DW_AT_return_addr: u16 = 0x2A;
pub const DW_AT_call_line: u16 = 0x59;
pub const DW_AT_call_file: u16 = 0x58;
pub const DW_AT_abstract_origin: u16 = 0x31;
pub const DW_AT_specification: u16 = 0x47;
pub const DW_AT_artificial: u16 = 0x34;
pub const DW_AT_use_UTF8: u16 = 0x69;
pub const DW_AT_count: u16 = 0x5A;
}
#[allow(dead_code, non_upper_case_globals)]
pub mod dwarf_forms {
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_flag: u16 = 0x0C;
pub const DW_FORM_flag_present: u16 = 0x19;
pub const DW_FORM_strp: u16 = 0x0E;
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_block1: u16 = 0x0A;
pub const DW_FORM_block2: u16 = 0x03;
pub const DW_FORM_block4: u16 = 0x04;
pub const DW_FORM_udata: u16 = 0x0F;
pub const DW_FORM_sdata: u16 = 0x0D;
}
#[allow(dead_code)]
pub mod dwarf_languages {
pub const DW_LANG_C89: u16 = 0x01;
pub const DW_LANG_C: u16 = 0x02;
pub const DW_LANG_C_plus_plus: u16 = 0x04;
pub const DW_LANG_Rust: u16 = 0x1C;
pub const DW_LANG_C11: u16 = 0x1D;
pub const DW_LANG_C_plus_plus_11: u16 = 0x1A;
pub const DW_LANG_C_plus_plus_14: u16 = 0x21;
}
#[allow(dead_code)]
pub mod dwarf_encodings {
pub const DW_ATE_boolean: u8 = 0x02;
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;
}
#[allow(dead_code)]
pub mod 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;
}
#[allow(dead_code)]
pub mod dwarf_children {
pub const DW_CHILDREN_no: u8 = 0;
pub const DW_CHILDREN_yes: u8 = 1;
}
#[allow(dead_code)]
pub mod line_number_std {
pub const DW_LNS_copy: u8 = 1;
pub const DW_LNS_advance_pc: u8 = 2;
pub const DW_LNS_advance_line: u8 = 3;
pub const DW_LNS_set_file: u8 = 4;
pub const DW_LNS_set_column: u8 = 5;
pub const DW_LNS_negate_stmt: u8 = 6;
pub const DW_LNS_set_basic_block: u8 = 7;
pub const DW_LNS_const_add_pc: u8 = 8;
pub const DW_LNS_fixed_advance_pc: u8 = 9;
pub const DW_LNS_set_prologue_end: u8 = 10;
pub const DW_LNS_set_epilogue_begin: u8 = 11;
pub const DW_LNS_set_isa: u8 = 12;
}
#[allow(dead_code)]
pub mod line_number_ext {
pub const DW_LNE_end_sequence: u8 = 1;
pub const DW_LNE_set_address: u8 = 2;
pub const DW_LNE_define_file: u8 = 3;
pub const DW_LNE_set_discriminator: u8 = 4;
}
pub use dwarf_attributes::*;
pub use dwarf_children::*;
pub use dwarf_encodings::*;
pub use dwarf_forms::*;
pub use dwarf_inline::*;
pub use dwarf_languages::*;
pub use dwarf_tags::*;
pub use line_number_ext::*;
pub use line_number_std::*;
#[derive(Debug, Clone)]
pub struct DwarfAttribute {
pub attr: u16,
pub form: u16,
pub value: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct DIE {
pub tag: u16,
pub attributes: Vec<DwarfAttribute>,
pub children: Vec<DIE>,
pub offset: u64,
pub has_children: bool,
}
impl DIE {
pub fn new(tag: u16) -> Self {
Self {
tag,
attributes: Vec::new(),
children: Vec::new(),
offset: 0,
has_children: false,
}
}
pub fn add_string(&mut self, attr: u16, value: &str) {
self.attributes.push(DwarfAttribute {
attr,
form: dwarf_forms::DW_FORM_string,
value: value.as_bytes().to_vec(),
});
}
pub fn add_flag(&mut self, attr: u16, value: bool) {
self.attributes.push(DwarfAttribute {
attr,
form: if value {
dwarf_forms::DW_FORM_flag_present
} else {
dwarf_forms::DW_FORM_flag
},
value: if value { vec![] } else { vec![0] },
});
}
pub fn add_data1(&mut self, attr: u16, value: u8) {
self.attributes.push(DwarfAttribute {
attr,
form: dwarf_forms::DW_FORM_data1,
value: vec![value],
});
}
pub fn add_data2(&mut self, attr: u16, value: u16) {
self.attributes.push(DwarfAttribute {
attr,
form: dwarf_forms::DW_FORM_data2,
value: value.to_le_bytes().to_vec(),
});
}
pub fn add_data4(&mut self, attr: u16, value: u32) {
self.attributes.push(DwarfAttribute {
attr,
form: dwarf_forms::DW_FORM_data4,
value: value.to_le_bytes().to_vec(),
});
}
pub fn add_data8(&mut self, attr: u16, value: u64) {
self.attributes.push(DwarfAttribute {
attr,
form: dwarf_forms::DW_FORM_data8,
value: value.to_le_bytes().to_vec(),
});
}
pub fn add_addr(&mut self, attr: u16, value: u64) {
self.attributes.push(DwarfAttribute {
attr,
form: dwarf_forms::DW_FORM_addr,
value: value.to_le_bytes().to_vec(),
});
}
pub fn add_child(&mut self, child: DIE) {
self.has_children = true;
self.children.push(child);
}
}
#[derive(Debug, Clone)]
pub struct AbbrevDeclaration {
pub code: u64,
pub tag: u16,
pub has_children: u8,
pub attr_form_pairs: Vec<(u16, u16)>,
}
#[derive(Debug, Clone)]
pub struct AbbrevTable {
pub declarations: Vec<AbbrevDeclaration>,
code_map: std::collections::HashMap<String, u64>,
next_code: u64,
}
impl AbbrevTable {
pub fn new() -> Self {
Self {
declarations: Vec::new(),
code_map: std::collections::HashMap::new(),
next_code: 1,
}
}
pub fn add_abbrev(
&mut self,
tag: u16,
has_children: u8,
attr_form_pairs: Vec<(u16, u16)>,
) -> u64 {
let code = self.next_code;
self.next_code += 1;
self.declarations.push(AbbrevDeclaration {
code,
tag,
has_children,
attr_form_pairs,
});
code
}
pub fn get_or_create_code(
&mut self,
tag: u16,
has_children: u8,
attr_form_pairs: Vec<(u16, u16)>,
) -> u64 {
let key = format!("{}:{}:{:?}", tag, has_children, attr_form_pairs);
if let Some(&code) = self.code_map.get(&key) {
return code;
}
let code = self.add_abbrev(tag, has_children, attr_form_pairs);
self.code_map.insert(key, code);
code
}
pub fn emit(&self) -> Vec<u8> {
let mut out = Vec::new();
for decl in &self.declarations {
encode_uleb128(&mut out, decl.code);
encode_uleb128(&mut out, decl.tag as u64);
out.push(decl.has_children);
for &(attr, form) in &decl.attr_form_pairs {
encode_uleb128(&mut out, attr as u64);
encode_uleb128(&mut out, form as u64);
}
encode_uleb128(&mut out, 0);
encode_uleb128(&mut out, 0);
}
out.push(0);
out
}
}
impl Default for AbbrevTable {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct SourceFile {
pub name: String,
pub directory_index: u64,
pub mod_time: u64,
pub length: u64,
}
#[derive(Debug, Clone)]
pub struct SourceDirectory {
pub path: String,
}
#[derive(Debug, Clone)]
pub struct LineRow {
pub address: u64,
pub file_index: u64,
pub line: u64,
pub column: u64,
pub is_stmt: bool,
pub basic_block: bool,
pub prologue_end: bool,
pub epilogue_begin: bool,
pub isa: u64,
}
#[derive(Debug, Clone)]
pub struct LineProgram {
pub directories: Vec<SourceDirectory>,
pub files: Vec<SourceFile>,
pub rows: Vec<LineRow>,
pub standard_opcode_lengths: Vec<u8>,
pub minimum_instruction_length: u8,
pub maximum_operations_per_instruction: u8,
pub default_is_stmt: bool,
pub line_base: i8,
pub line_range: u8,
pub opcode_base: u8,
}
impl LineProgram {
pub fn new() -> Self {
Self {
directories: Vec::new(),
files: vec![SourceFile {
name: "unknown".to_string(),
directory_index: 0,
mod_time: 0,
length: 0,
}],
rows: Vec::new(),
standard_opcode_lengths: vec![0; 12],
minimum_instruction_length: 1,
maximum_operations_per_instruction: 1,
default_is_stmt: true,
line_base: -5,
line_range: 14,
opcode_base: 13,
}
}
pub fn add_file(&mut self, name: &str, dir_idx: u64) -> u64 {
let idx = self.files.len() as u64;
self.files.push(SourceFile {
name: name.to_string(),
directory_index: dir_idx,
mod_time: 0,
length: 0,
});
idx
}
pub fn add_directory(&mut self, path: &str) -> u64 {
let idx = self.directories.len() as u64;
self.directories.push(SourceDirectory {
path: path.to_string(),
});
idx
}
pub fn add_line(&mut self, address: u64, file_index: u64, line: u64, column: u64) {
self.rows.push(LineRow {
address,
file_index,
line,
column,
is_stmt: self.default_is_stmt,
basic_block: false,
prologue_end: false,
epilogue_begin: false,
isa: 0,
});
}
pub fn emit_header(&self) -> Vec<u8> {
let mut out = Vec::new();
let len_pos = out.len();
out.extend_from_slice(&[0u8; 4]);
out.extend_from_slice(&2u16.to_le_bytes());
let hdr_len_pos = out.len();
out.extend_from_slice(&[0u8; 4]);
out.push(self.minimum_instruction_length);
out.push(self.maximum_operations_per_instruction);
out.push(self.default_is_stmt as u8);
out.push(self.line_base as u8);
out.push(self.line_range);
out.push(self.opcode_base);
for i in 1..self.opcode_base as usize {
if i < self.standard_opcode_lengths.len() {
out.push(self.standard_opcode_lengths[i]);
} else {
out.push(0);
}
}
for dir in &self.directories {
out.extend_from_slice(dir.path.as_bytes());
out.push(0);
}
out.push(0);
for file in &self.files {
out.extend_from_slice(file.name.as_bytes());
out.push(0);
encode_uleb128(&mut out, file.directory_index);
encode_uleb128(&mut out, file.mod_time);
encode_uleb128(&mut out, file.length);
}
out.push(0);
let hdr_len = (out.len() - hdr_len_pos - 4) as u32;
out[hdr_len_pos..hdr_len_pos + 4].copy_from_slice(&hdr_len.to_le_bytes());
let unit_len = (out.len() - len_pos - 4) as u32;
out[len_pos..len_pos + 4].copy_from_slice(&unit_len.to_le_bytes());
out
}
pub fn emit_body(&self) -> Vec<u8> {
let mut out = Vec::new();
if self.rows.is_empty() {
out.push(0);
encode_uleb128(&mut out, 1); out.push(line_number_ext::DW_LNE_end_sequence);
return out;
}
let mut prev_address: u64 = 0;
let mut prev_file: u64 = 1;
let mut prev_line: u64 = 1;
let mut prev_column: u64 = 0;
for row in &self.rows {
let addr_delta = row.address.wrapping_sub(prev_address);
let line_delta = row.line as i64 - prev_line as i64;
let file_delta = row.file_index as i64 - prev_file as i64;
if file_delta != 0 {
out.push(line_number_std::DW_LNS_set_file);
encode_uleb128(&mut out, row.file_index);
}
if row.column != prev_column {
out.push(line_number_std::DW_LNS_set_column);
encode_uleb128(&mut out, row.column);
}
if addr_delta > 0 {
if addr_delta <= 0xFF {
out.push(line_number_std::DW_LNS_advance_pc);
encode_uleb128(&mut out, addr_delta);
} else {
let count = addr_delta / self.minimum_instruction_length as u64;
out.resize(
out.len() + count as usize,
line_number_std::DW_LNS_const_add_pc,
);
}
}
if line_delta >= self.line_base as i64
&& line_delta < (self.line_base as i64 + self.line_range as i64)
&& addr_delta <= self.line_range as u64
{
let adjusted_line = (line_delta - self.line_base as i64) as u64;
let opcode =
adjusted_line + (addr_delta * self.line_range as u64) + self.opcode_base as u64;
if opcode <= 255 {
out.push(opcode as u8);
} else {
if line_delta != 0 {
out.push(line_number_std::DW_LNS_advance_line);
encode_sleb128(&mut out, line_delta);
}
out.push(line_number_std::DW_LNS_copy);
}
} else {
if line_delta != 0 {
out.push(line_number_std::DW_LNS_advance_line);
encode_sleb128(&mut out, line_delta);
}
out.push(line_number_std::DW_LNS_copy);
}
prev_address = row.address;
prev_file = row.file_index;
prev_line = row.line;
prev_column = row.column;
}
out.push(0);
encode_uleb128(&mut out, 1);
out.push(line_number_ext::DW_LNE_end_sequence);
out
}
}
impl Default for LineProgram {
fn default() -> Self {
Self::new()
}
}
pub struct DwarfEmitter {
pub unit: DIE,
pub abbrev_table: AbbrevTable,
pub line_program: LineProgram,
pub string_table: Vec<String>,
pub address_size: u8,
pub producer: String,
pub compile_dir: String,
pub language: u16,
}
impl DwarfEmitter {
pub fn new(producer: &str, compile_dir: &str, address_size: u8) -> Self {
let unit = DIE::new(dwarf_tags::DW_TAG_compile_unit);
Self {
unit,
abbrev_table: AbbrevTable::new(),
line_program: LineProgram::new(),
string_table: Vec::new(),
address_size,
producer: producer.to_string(),
compile_dir: compile_dir.to_string(),
language: dwarf_languages::DW_LANG_C_plus_plus,
}
}
pub fn add_string(&mut self, s: &str) -> u64 {
let mut offset = 0u64;
for existing in &self.string_table {
offset += existing.len() as u64 + 1;
}
self.string_table.push(s.to_string());
offset
}
pub fn set_compile_unit_info(&mut self, name: &str, low_pc: u64, high_pc: u64) {
self.unit.add_string(dwarf_attributes::DW_AT_name, name);
self.unit
.add_string(dwarf_attributes::DW_AT_producer, &self.producer);
self.unit
.add_string(dwarf_attributes::DW_AT_comp_dir, &self.compile_dir);
self.unit
.add_data2(dwarf_attributes::DW_AT_language, self.language);
self.unit.add_addr(dwarf_attributes::DW_AT_low_pc, low_pc);
self.unit
.add_data8(dwarf_attributes::DW_AT_high_pc, high_pc);
}
pub fn add_subprogram(
&mut self,
name: &str,
low_pc: u64,
high_pc: u64,
decl_file: u64,
decl_line: u64,
) -> &mut DIE {
let mut subprog = DIE::new(dwarf_tags::DW_TAG_subprogram);
subprog.add_string(dwarf_attributes::DW_AT_name, name);
subprog.add_addr(dwarf_attributes::DW_AT_low_pc, low_pc);
subprog.add_data8(dwarf_attributes::DW_AT_high_pc, high_pc);
subprog.add_flag(dwarf_attributes::DW_AT_external, true);
subprog.add_data1(dwarf_attributes::DW_AT_decl_file, decl_file as u8);
subprog.add_data4(dwarf_attributes::DW_AT_decl_line, decl_line as u32);
self.unit.add_child(subprog);
self.unit.children.last_mut().unwrap()
}
pub fn emit_debug_info(&mut self) -> Vec<u8> {
let mut out = Vec::new();
let len_pos = out.len();
out.extend_from_slice(&[0u8; 4]);
out.extend_from_slice(&4u16.to_le_bytes());
out.extend_from_slice(&[0u8; 4]);
out.push(self.address_size);
self.emit_die(&mut out, &self.unit);
let unit_len = (out.len() - len_pos - 4) as u32;
out[len_pos..len_pos + 4].copy_from_slice(&unit_len.to_le_bytes());
out
}
fn emit_die(&self, out: &mut Vec<u8>, die: &DIE) {
let mut attr_forms: Vec<(u16, u16)> =
die.attributes.iter().map(|a| (a.attr, a.form)).collect();
attr_forms.sort_by_key(|&(a, _)| a);
let _ = attr_forms;
encode_uleb128(out, 1);
for attr in &die.attributes {
out.extend_from_slice(&attr.value);
}
for child in &die.children {
self.emit_die(out, child);
}
if die.has_children {
out.push(0);
}
}
pub fn emit_debug_abbrev(&self) -> Vec<u8> {
self.abbrev_table.emit()
}
pub fn emit_debug_line(&self) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&self.line_program.emit_header());
out.extend_from_slice(&self.line_program.emit_body());
out
}
pub fn emit_debug_str(&self) -> Vec<u8> {
let mut out = Vec::new();
out.push(0);
for s in &self.string_table {
out.extend_from_slice(s.as_bytes());
out.push(0);
}
out
}
pub fn emit_eh_frame(&self, _func_start: u64, func_size: u64) -> Vec<u8> {
let mut out = Vec::new();
let cie_len: u32 = 20; out.extend_from_slice(&cie_len.to_le_bytes());
out.extend_from_slice(&0u32.to_le_bytes());
out.push(1);
out.push(b'z');
out.push(b'R');
out.push(0);
encode_uleb128(&mut out, 1);
encode_sleb128(&mut out, -8);
encode_uleb128(&mut out, 16);
out.push(1);
out.push(0x1B);
out.push(0x0C); encode_uleb128(&mut out, 7); encode_uleb128(&mut out, 8); out.push(0x90); out.push(0x90);
out.push(0x90);
let fde_data_start = out.len();
let fde_len_pos = out.len();
out.extend_from_slice(&[0u8; 4]);
let cie_offset: u32 = 4 + cie_len + 4; out.extend_from_slice(&cie_offset.to_le_bytes());
out.extend_from_slice(&0u32.to_le_bytes());
out.extend_from_slice(&(func_size as u32).to_le_bytes());
encode_uleb128(&mut out, 0);
out.push(0x90);
let fde_len = (out.len() - fde_data_start) as u32 - 4;
out[fde_len_pos..fde_len_pos + 4].copy_from_slice(&fde_len.to_le_bytes());
out
}
}
pub fn encode_uleb128(out: &mut Vec<u8>, mut value: u64) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
out.push(byte);
if value == 0 {
break;
}
}
}
pub fn encode_sleb128(out: &mut Vec<u8>, mut value: i64) {
loop {
let mut byte = (value as u8) & 0x7F;
value >>= 7;
if (value == 0 && (byte & 0x40) == 0) || (value == -1 && (byte & 0x40) != 0) {
out.push(byte);
break;
}
byte |= 0x80;
out.push(byte);
}
}
pub type DebugSections = (Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>);
pub fn build_debug_sections(
cu_name: &str,
func_name: &str,
func_low_pc: u64,
func_size: u64,
source_file: &str,
) -> DebugSections {
let mut emitter = DwarfEmitter::new("llvm-native", "/tmp", 8);
emitter.language = dwarf_languages::DW_LANG_C;
emitter.set_compile_unit_info(cu_name, func_low_pc, func_size);
emitter.line_program.add_directory("/tmp");
let file_idx = emitter.line_program.add_file(source_file, 1);
emitter.line_program.add_line(func_low_pc, file_idx, 1, 0);
emitter.add_subprogram(func_name, func_low_pc, func_size, file_idx, 1);
let debug_info = emitter.emit_debug_info();
let debug_abbrev = emitter.emit_debug_abbrev();
let debug_line = emitter.emit_debug_line();
let debug_str = emitter.emit_debug_str();
let eh_frame = emitter.emit_eh_frame(func_low_pc, func_size);
(debug_info, debug_abbrev, debug_line, debug_str, eh_frame)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_uleb128_encode_small() {
let mut out = Vec::new();
encode_uleb128(&mut out, 42);
assert_eq!(out, vec![42]);
}
#[test]
fn test_uleb128_encode_large() {
let mut out = Vec::new();
encode_uleb128(&mut out, 624485);
assert_eq!(out.len(), 3);
}
#[test]
fn test_uleb128_encode_zero() {
let mut out = Vec::new();
encode_uleb128(&mut out, 0);
assert_eq!(out, vec![0]);
}
#[test]
fn test_uleb128_encode_max_u32() {
let mut out = Vec::new();
encode_uleb128(&mut out, u32::MAX as u64);
assert!(out.len() >= 1);
assert!(out.len() <= 5);
}
#[test]
fn test_sleb128_encode_positive() {
let mut out = Vec::new();
encode_sleb128(&mut out, 42);
assert_eq!(out[0], 42);
}
#[test]
fn test_sleb128_encode_negative() {
let mut out = Vec::new();
encode_sleb128(&mut out, -1);
assert_eq!(out, vec![0x7F]);
}
#[test]
fn test_sleb128_encode_zero() {
let mut out = Vec::new();
encode_sleb128(&mut out, 0);
assert_eq!(out, vec![0]);
}
#[test]
fn test_uleb128_sleb128_roundtrip() {
let values: Vec<i64> = vec![0, 1, -1, 42, -42, 127, -127, 128, -128, 1000, -1000];
for &v in &values {
let mut out = Vec::new();
encode_sleb128(&mut out, v);
assert!(out.len() >= 1, "sleb128({}) should have at least 1 byte", v);
assert!(out.len() <= 10, "sleb128({}) should fit in 10 bytes", v);
}
let _ = values; }
#[test]
fn test_die_create() {
let die = DIE::new(dwarf_tags::DW_TAG_compile_unit);
assert_eq!(die.tag, dwarf_tags::DW_TAG_compile_unit);
assert!(die.attributes.is_empty());
assert!(!die.has_children);
}
#[test]
fn test_die_add_string() {
let mut die = DIE::new(dwarf_tags::DW_TAG_subprogram);
die.add_string(dwarf_attributes::DW_AT_name, "my_function");
assert_eq!(die.attributes.len(), 1);
assert_eq!(die.attributes[0].attr, dwarf_attributes::DW_AT_name);
}
#[test]
fn test_die_add_data_types() {
let mut die = DIE::new(dwarf_tags::DW_TAG_variable);
die.add_data1(dwarf_attributes::DW_AT_decl_file, 3);
die.add_data2(dwarf_attributes::DW_AT_decl_line, 42);
die.add_data4(dwarf_attributes::DW_AT_byte_size, 8);
die.add_data8(dwarf_attributes::DW_AT_low_pc, 0x1000);
assert_eq!(die.attributes.len(), 4);
}
#[test]
fn test_die_add_addr() {
let mut die = DIE::new(dwarf_tags::DW_TAG_subprogram);
die.add_addr(dwarf_attributes::DW_AT_low_pc, 0x400000);
assert_eq!(die.attributes.len(), 1);
assert_eq!(die.attributes[0].form, dwarf_forms::DW_FORM_addr);
}
#[test]
fn test_die_add_flag() {
let mut die = DIE::new(dwarf_tags::DW_TAG_subprogram);
die.add_flag(dwarf_attributes::DW_AT_external, true);
assert_eq!(die.attributes.len(), 1);
assert!(die.attributes[0].value.is_empty());
}
#[test]
fn test_die_add_child() {
let mut parent = DIE::new(dwarf_tags::DW_TAG_compile_unit);
let child = DIE::new(dwarf_tags::DW_TAG_subprogram);
parent.add_child(child);
assert!(parent.has_children);
assert_eq!(parent.children.len(), 1);
assert_eq!(parent.children[0].tag, dwarf_tags::DW_TAG_subprogram);
}
#[test]
fn test_abbrev_table_create() {
let table = AbbrevTable::new();
assert!(table.declarations.is_empty());
}
#[test]
fn test_abbrev_table_add() {
let mut table = AbbrevTable::new();
let code = table.add_abbrev(
dwarf_tags::DW_TAG_compile_unit,
dwarf_children::DW_CHILDREN_yes,
vec![
(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string),
(dwarf_attributes::DW_AT_low_pc, dwarf_forms::DW_FORM_addr),
],
);
assert_eq!(code, 1);
assert_eq!(table.declarations.len(), 1);
}
#[test]
fn test_abbrev_table_emit() {
let mut table = AbbrevTable::new();
table.add_abbrev(
dwarf_tags::DW_TAG_compile_unit,
dwarf_children::DW_CHILDREN_no,
vec![(dwarf_attributes::DW_AT_name, dwarf_forms::DW_FORM_string)],
);
let data = table.emit();
assert!(!data.is_empty());
assert_eq!(data.last(), Some(&0));
}
#[test]
fn test_line_program_create() {
let lp = LineProgram::new();
assert_eq!(lp.files.len(), 1); assert_eq!(lp.files[0].name, "unknown");
}
#[test]
fn test_line_program_add_file() {
let mut lp = LineProgram::new();
let idx = lp.add_file("test.c", 0);
assert_eq!(idx, 1);
assert_eq!(lp.files[1].name, "test.c");
}
#[test]
fn test_line_program_add_line() {
let mut lp = LineProgram::new();
lp.add_line(0x1000, 1, 10, 0);
assert_eq!(lp.rows.len(), 1);
assert_eq!(lp.rows[0].address, 0x1000);
assert_eq!(lp.rows[0].line, 10);
}
#[test]
fn test_line_program_emit_header() {
let lp = LineProgram::new();
let header = lp.emit_header();
assert!(!header.is_empty());
let unit_len = u32::from_le_bytes(header[0..4].try_into().unwrap());
assert!(unit_len > 0);
}
#[test]
fn test_line_program_emit_body() {
let mut lp = LineProgram::new();
lp.add_line(0x1000, 1, 5, 0);
lp.add_line(0x1005, 1, 6, 0);
let body = lp.emit_body();
assert!(!body.is_empty());
assert_eq!(body[body.len() - 1], line_number_ext::DW_LNE_end_sequence);
}
#[test]
fn test_dwarf_emitter_create() {
let emitter = DwarfEmitter::new("test-compiler", "/src", 8);
assert_eq!(emitter.address_size, 8);
assert_eq!(emitter.producer, "test-compiler");
}
#[test]
fn test_dwarf_emitter_set_cu_info() {
let mut emitter = DwarfEmitter::new("test", "/tmp", 8);
emitter.set_compile_unit_info("test.cu", 0x1000, 0x50);
assert!(!emitter.unit.attributes.is_empty());
}
#[test]
fn test_dwarf_emitter_add_subprogram() {
let mut emitter = DwarfEmitter::new("test", "/tmp", 8);
emitter.add_subprogram("main", 0x1000, 0x20, 1, 10);
assert!(emitter.unit.has_children);
assert_eq!(emitter.unit.children.len(), 1);
}
#[test]
fn test_dwarf_emitter_emit_debug_info() {
let mut emitter = DwarfEmitter::new("test", "/tmp", 8);
emitter.set_compile_unit_info("test.cu", 0x1000, 0x20);
emitter.add_subprogram("main", 0x1000, 0x10, 1, 5);
let info = emitter.emit_debug_info();
assert!(!info.is_empty());
let unit_len = u32::from_le_bytes(info[0..4].try_into().unwrap());
assert!(unit_len > 0);
}
#[test]
fn test_dwarf_emitter_emit_debug_abbrev() {
let mut emitter = DwarfEmitter::new("test", "/tmp", 8);
emitter.set_compile_unit_info("cu", 0x1000, 0x20);
let abbrev = emitter.emit_debug_abbrev();
assert!(!abbrev.is_empty());
assert_eq!(abbrev.last(), Some(&0));
}
#[test]
fn test_dwarf_emitter_emit_debug_line() {
let mut emitter = DwarfEmitter::new("test", "/tmp", 8);
emitter.line_program.add_file("test.c", 0);
emitter.line_program.add_line(0x1000, 1, 1, 0);
let line_data = emitter.emit_debug_line();
assert!(!line_data.is_empty());
}
#[test]
fn test_dwarf_emitter_emit_debug_str() {
let mut emitter = DwarfEmitter::new("test", "/tmp", 8);
emitter.add_string("hello");
emitter.add_string("world");
let str_data = emitter.emit_debug_str();
assert!(!str_data.is_empty());
assert_eq!(str_data[0], 0);
let s = String::from_utf8_lossy(&str_data);
assert!(s.contains("hello"));
assert!(s.contains("world"));
}
#[test]
fn test_dwarf_emitter_emit_eh_frame() {
let emitter = DwarfEmitter::new("test", "/tmp", 8);
let eh = emitter.emit_eh_frame(0x1000, 0x30);
assert!(!eh.is_empty());
let cie_len = u32::from_le_bytes(eh[0..4].try_into().unwrap());
assert!(cie_len > 0);
}
#[test]
fn test_dwarf_emitter_string_table_offset() {
let mut emitter = DwarfEmitter::new("test", "/tmp", 8);
let off1 = emitter.add_string("abc");
let off2 = emitter.add_string("def");
assert_eq!(off1, 0); assert_eq!(off2, 4); }
#[test]
fn test_build_debug_sections_all_non_empty() {
let (info, abbrev, line, strs, eh) = build_debug_sections(
"test_compile_unit",
"test_function",
0x400000,
0x100,
"test.c",
);
assert!(!info.is_empty());
assert!(!abbrev.is_empty());
assert!(!line.is_empty());
assert!(!strs.is_empty());
assert!(!eh.is_empty());
}
#[test]
fn test_debug_info_has_valid_header() {
let (info, _, _, _, _) = build_debug_sections("cu", "fn", 0x1000, 0x50, "src.c");
assert!(info.len() >= 11);
let version = u16::from_le_bytes(info[4..6].try_into().unwrap());
assert_eq!(version, 4); }
#[test]
fn test_dwarf_constants_are_valid() {
assert_eq!(dwarf_tags::DW_TAG_compile_unit, 0x11);
assert_eq!(dwarf_tags::DW_TAG_subprogram, 0x2E);
assert_eq!(dwarf_attributes::DW_AT_name, 0x03);
assert_eq!(dwarf_attributes::DW_AT_low_pc, 0x11);
assert_eq!(dwarf_forms::DW_FORM_string, 0x08);
assert_eq!(dwarf_forms::DW_FORM_addr, 0x01);
}
}