#![allow(dead_code)]
use std::cell::Cell;
use super::dwarf_attributes;
use super::dwarf_children;
use super::dwarf_encodings;
use super::dwarf_forms;
use super::dwarf_languages;
use super::dwarf_tags;
use super::encode_uleb128;
use super::AbbrevTable;
use super::LineProgram;
use super::DIE;
use crate::module::Module;
#[derive(Debug, Clone)]
pub struct CompileUnit {
pub die: DIE,
pub subprograms: Vec<Subprogram>,
pub global_variables: Vec<Variable>,
pub string_offsets: std::collections::HashMap<String, u64>,
pub offset: u64,
pub line_program: LineProgram,
}
impl CompileUnit {
pub fn new() -> Self {
Self {
die: DIE::new(dwarf_tags::DW_TAG_compile_unit),
subprograms: Vec::new(),
global_variables: Vec::new(),
string_offsets: std::collections::HashMap::new(),
offset: 0,
line_program: LineProgram::new(),
}
}
pub fn add_string(&mut self, s: &str) -> u64 {
if let Some(&offset) = self.string_offsets.get(s) {
return offset;
}
let offset = self
.string_offsets
.values()
.map(|&o| {
let existing_len = self
.string_offsets
.iter()
.find(|&(_, &off)| off == o)
.map(|(k, _)| k.len() as u64 + 1)
.unwrap_or(1);
existing_len
})
.sum::<u64>()
+ if self.string_offsets.is_empty() { 1 } else { 0 };
self.string_offsets.insert(s.to_string(), offset);
offset
}
pub fn get_string_offset(&self, s: &str) -> Option<u64> {
self.string_offsets.get(s).copied()
}
}
impl Default for CompileUnit {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Subprogram {
pub die: DIE,
pub name: String,
pub low_pc: u64,
pub high_pc: u64,
pub frame_base: Option<Vec<u8>>,
pub variables: Vec<Variable>,
pub lexical_blocks: Vec<LexicalBlock>,
pub inlined_subroutines: Vec<InlinedSubroutine>,
}
impl Subprogram {
pub fn new(name: &str, low_pc: u64, high_pc: u64) -> Self {
Self {
die: DIE::new(dwarf_tags::DW_TAG_subprogram),
name: name.to_string(),
low_pc,
high_pc,
frame_base: None,
variables: Vec::new(),
lexical_blocks: Vec::new(),
inlined_subroutines: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct Variable {
pub die: DIE,
pub name: String,
pub var_type: Option<u64>,
pub location: Option<Vec<u8>>,
pub is_parameter: bool,
}
impl Variable {
pub fn new(name: &str) -> Self {
Self {
die: DIE::new(dwarf_tags::DW_TAG_variable),
name: name.to_string(),
var_type: None,
location: None,
is_parameter: false,
}
}
pub fn new_parameter(name: &str) -> Self {
Self {
die: DIE::new(dwarf_tags::DW_TAG_formal_parameter),
name: name.to_string(),
var_type: None,
location: None,
is_parameter: true,
}
}
}
#[derive(Debug, Clone)]
pub struct LexicalBlock {
pub die: DIE,
pub low_pc: u64,
pub high_pc: u64,
pub variables: Vec<Variable>,
}
impl LexicalBlock {
pub fn new(low_pc: u64) -> Self {
Self {
die: DIE::new(dwarf_tags::DW_TAG_lexical_block),
low_pc,
high_pc: 0,
variables: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct InlinedSubroutine {
pub die: DIE,
pub low_pc: u64,
pub high_pc: u64,
pub abstract_origin: u64,
}
impl InlinedSubroutine {
pub fn new(low_pc: u64, high_pc: u64, abstract_origin: u64) -> Self {
Self {
die: DIE::new(dwarf_tags::DW_TAG_inlined_subroutine),
low_pc,
high_pc,
abstract_origin,
}
}
}
pub struct DebugInfoEmitter {
pub compile_units: Vec<CompileUnit>,
pub abbrev_table: AbbrevTable,
pub string_table: Vec<u8>,
pub line_table: LineProgram,
pub address_size: u8,
pub producer: String,
pub is_dwarf5: bool,
next_die_offset: Cell<u64>,
}
impl DebugInfoEmitter {
pub fn new(producer: &str, is_dwarf5: bool, address_size: u8) -> Self {
let mut string_table = Vec::new();
string_table.push(0);
Self {
compile_units: Vec::new(),
abbrev_table: AbbrevTable::new(),
string_table,
line_table: LineProgram::new(),
address_size,
producer: producer.to_string(),
is_dwarf5,
next_die_offset: Cell::new(0),
}
}
pub fn create_compile_unit(
&mut self,
name: &str,
comp_dir: &str,
language: u16,
low_pc: u64,
) -> usize {
let mut cu = CompileUnit::new();
let name_off = cu.add_string(name);
let prod_off = cu.add_string(&self.producer);
let dir_off = cu.add_string(comp_dir);
cu.die.add_strp(dwarf_attributes::DW_AT_name, name_off, 4);
cu.die
.add_strp(dwarf_attributes::DW_AT_producer, prod_off, 4);
cu.die
.add_strp(dwarf_attributes::DW_AT_comp_dir, dir_off, 4);
cu.die.add_data2(dwarf_attributes::DW_AT_language, language);
cu.die.add_addr(dwarf_attributes::DW_AT_low_pc, low_pc);
if self.is_dwarf5 {
cu.die.add_data1(dwarf_attributes::DW_AT_use_UTF8, 1);
}
self.compile_units.push(cu);
self.compile_units.len() - 1
}
pub fn set_cu_high_pc(cu: &mut CompileUnit, high_pc: u64) {
cu.die
.attributes
.retain(|a| a.attr != dwarf_attributes::DW_AT_high_pc);
cu.die.add_data8(dwarf_attributes::DW_AT_high_pc, high_pc);
}
pub fn add_subprogram<'a>(
cu: &'a mut CompileUnit,
name: &str,
low_pc: u64,
high_pc: u64,
frame_base: Option<Vec<u8>>,
) -> &'a mut Subprogram {
let mut sp = Subprogram::new(name, low_pc, high_pc);
sp.frame_base = frame_base.clone();
let name_off = cu.add_string(name);
sp.die.add_strp(dwarf_attributes::DW_AT_name, name_off, 4);
sp.die.add_addr(dwarf_attributes::DW_AT_low_pc, low_pc);
sp.die.add_data8(dwarf_attributes::DW_AT_high_pc, high_pc);
sp.die.add_flag_present(dwarf_attributes::DW_AT_external);
if let Some(ref fb) = frame_base {
sp.die.add_exprloc(dwarf_attributes::DW_AT_frame_base, fb);
}
cu.subprograms.push(sp);
cu.subprograms.last_mut().unwrap()
}
pub fn add_variable<'a>(
sp: &'a mut Subprogram,
name: &str,
name_off: u64,
location: Vec<u8>,
) -> &'a mut Variable {
let mut var = Variable::new(name);
var.location = Some(location.clone());
var.die.add_strp(dwarf_attributes::DW_AT_name, name_off, 4);
var.die
.add_exprloc(dwarf_attributes::DW_AT_location, &location);
if let Some(type_offset) = var.var_type {
var.die.add_ref4(
dwarf_attributes::DW_AT_type,
dwarf_forms::DW_FORM_ref4,
type_offset as u32,
);
}
sp.variables.push(var);
sp.variables.last_mut().unwrap()
}
pub fn add_formal_parameter<'a>(
sp: &'a mut Subprogram,
name: &str,
name_off: u64,
arg_index: u32,
location: Vec<u8>,
) -> &'a mut Variable {
let mut var = Variable::new_parameter(name);
var.location = Some(location.clone());
var.die.add_strp(dwarf_attributes::DW_AT_name, name_off, 4);
var.die
.add_exprloc(dwarf_attributes::DW_AT_location, &location);
var.die
.add_data4(dwarf_attributes::DW_AT_decl_line, arg_index);
if let Some(type_offset) = var.var_type {
var.die.add_ref4(
dwarf_attributes::DW_AT_type,
dwarf_forms::DW_FORM_ref4,
type_offset as u32,
);
}
sp.variables.push(var);
sp.variables.last_mut().unwrap()
}
pub fn add_type(
next_die_offset: &Cell<u64>,
cu: &mut CompileUnit,
name: &str,
byte_size: u64,
encoding: u8,
) -> u64 {
let offset = next_die_offset.get();
next_die_offset.set(offset + 1);
let mut type_die = DIE::new(dwarf_tags::DW_TAG_base_type);
let name_off = cu.add_string(name);
type_die.add_strp(dwarf_attributes::DW_AT_name, name_off, 4);
type_die.add_data8(dwarf_attributes::DW_AT_byte_size, byte_size);
type_die.add_data1(dwarf_attributes::DW_AT_encoding, encoding);
type_die.set_offset(offset);
cu.die.add_child(type_die);
offset
}
pub fn add_pointer_type(
next_die_offset: &Cell<u64>,
cu: &mut CompileUnit,
pointee_offset: u64,
) -> u64 {
let offset = next_die_offset.get();
next_die_offset.set(offset + 1);
let mut ptr_die = DIE::new(dwarf_tags::DW_TAG_pointer_type);
ptr_die.add_ref4(
dwarf_attributes::DW_AT_type,
dwarf_forms::DW_FORM_ref4,
pointee_offset as u32,
);
ptr_die.set_offset(offset);
cu.die.add_child(ptr_die);
offset
}
pub fn add_struct_type(
next_die_offset: &Cell<u64>,
cu: &mut CompileUnit,
name: &str,
byte_size: u64,
members: Vec<(String, u64, u64)>,
) -> u64 {
let offset = next_die_offset.get();
next_die_offset.set(offset + 1);
let mut struct_die = DIE::new(dwarf_tags::DW_TAG_structure_type);
let name_off = cu.add_string(name);
struct_die.add_strp(dwarf_attributes::DW_AT_name, name_off, 4);
struct_die.add_data8(dwarf_attributes::DW_AT_byte_size, byte_size);
for (member_name, member_type_offset, member_offset) in &members {
let mut member_die = DIE::new(dwarf_tags::DW_TAG_member);
let mem_name_off = cu.add_string(member_name);
member_die.add_strp(dwarf_attributes::DW_AT_name, mem_name_off, 4);
member_die.add_ref4(
dwarf_attributes::DW_AT_type,
dwarf_forms::DW_FORM_ref4,
*member_type_offset as u32,
);
member_die.add_data8(dwarf_attributes::DW_AT_data_member_location, *member_offset);
struct_die.add_child(member_die);
}
struct_die.set_offset(offset);
cu.die.add_child(struct_die);
offset
}
pub fn add_array_type(
next_die_offset: &Cell<u64>,
cu: &mut CompileUnit,
elem_type_offset: u64,
count: u64,
) -> u64 {
let offset = next_die_offset.get();
next_die_offset.set(offset + 1);
let mut array_die = DIE::new(dwarf_tags::DW_TAG_array_type);
array_die.add_ref4(
dwarf_attributes::DW_AT_type,
dwarf_forms::DW_FORM_ref4,
elem_type_offset as u32,
);
let mut subrange = DIE::new(dwarf_tags::DW_TAG_subrange_type);
subrange.add_udata(dwarf_attributes::DW_AT_count, count);
array_die.add_child(subrange);
array_die.set_offset(offset);
cu.die.add_child(array_die);
offset
}
pub fn add_subroutine_type(next_die_offset: &Cell<u64>, cu: &mut CompileUnit) -> u64 {
let offset = next_die_offset.get();
next_die_offset.set(offset + 1);
let mut subr_die = DIE::new(dwarf_tags::DW_TAG_subroutine_type);
let mut unspec = DIE::new(dwarf_tags::DW_TAG_unspecified_parameters);
subr_die.add_child(unspec);
subr_die.set_offset(offset);
cu.die.add_child(subr_die);
offset
}
pub fn begin_lexical_block<'a>(sp: &'a mut Subprogram, low_pc: u64) -> &'a mut LexicalBlock {
let lb = LexicalBlock::new(low_pc);
sp.lexical_blocks.push(lb);
sp.lexical_blocks.last_mut().unwrap()
}
pub fn end_lexical_block(lb: &mut LexicalBlock, high_pc: u64) {
lb.high_pc = high_pc;
}
pub fn emit_debug_info(&mut self) -> Vec<u8> {
let mut out = Vec::new();
let dies: Vec<DIE> = self.compile_units.iter().map(|cu| cu.die.clone()).collect();
for (i, die) in dies.iter().enumerate() {
let cu_start = out.len();
if self.is_dwarf5 {
let len_pos = out.len();
out.extend_from_slice(&[0u8; 4]);
out.extend_from_slice(&5u16.to_le_bytes());
out.push(0x01);
out.push(self.address_size);
out.extend_from_slice(&0u32.to_le_bytes());
self.emit_die_tree(&mut out, die);
let unit_len = (out.len() - len_pos - 4) as u32;
out[len_pos..len_pos + 4].copy_from_slice(&unit_len.to_le_bytes());
} else {
let len_pos = out.len();
out.extend_from_slice(&[0u8; 4]);
out.extend_from_slice(&4u16.to_le_bytes());
out.extend_from_slice(&0u32.to_le_bytes());
out.push(self.address_size);
self.emit_die_tree(&mut out, die);
let unit_len = (out.len() - len_pos - 4) as u32;
out[len_pos..len_pos + 4].copy_from_slice(&unit_len.to_le_bytes());
}
let _ = cu_start;
}
out
}
fn emit_die_tree(&mut self, out: &mut Vec<u8>, die: &DIE) {
let children_flag = if die.has_children {
dwarf_children::DW_CHILDREN_yes
} else {
dwarf_children::DW_CHILDREN_no
};
let pairs: Vec<(u16, u16)> = die.attributes.iter().map(|a| (a.attr, a.form)).collect();
let code = self
.abbrev_table
.get_or_create_code(die.tag, children_flag, pairs);
encode_uleb128(out, code);
for attr in &die.attributes {
out.extend_from_slice(&attr.value);
}
for child in &die.children {
self.emit_die_tree(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 lp = self
.compile_units
.first()
.map(|cu| &cu.line_program)
.unwrap_or(&self.line_table);
let mut out = Vec::new();
out.extend_from_slice(&lp.emit_header());
out.extend_from_slice(&lp.emit_body());
out
}
pub fn emit_debug_str(&mut self) -> Vec<u8> {
let mut merged: Vec<u8> = vec![0];
let mut all_strings: Vec<String> = Vec::new();
for cu in &self.compile_units {
for s in cu.string_offsets.keys() {
if !all_strings.contains(s) {
all_strings.push(s.clone());
}
}
}
all_strings.sort();
for s in &all_strings {
merged.extend_from_slice(s.as_bytes());
merged.push(0);
}
self.string_table = merged.clone();
merged
}
pub fn emit_debug_str_offsets(&self) -> Vec<u8> {
let mut out = Vec::new();
for cu in &self.compile_units {
if cu.string_offsets.is_empty() {
continue;
}
let len_pos = out.len();
out.extend_from_slice(&[0u8; 4]);
out.extend_from_slice(&2u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
let mut sorted_strings: Vec<(&String, &u64)> = cu.string_offsets.iter().collect();
sorted_strings.sort_by_key(|(s, _)| s.as_str());
let mut cumulative: u64 = 1; let mut offset_map: std::collections::HashMap<String, u64> =
std::collections::HashMap::new();
let mut all_strings: Vec<String> = Vec::new();
for (s, _) in &sorted_strings {
if !all_strings.contains(s) {
all_strings.push((*s).clone());
}
}
all_strings.sort();
for s in &all_strings {
offset_map.insert(s.clone(), cumulative);
cumulative += s.len() as u64 + 1;
}
for (s, _) in &sorted_strings {
let offset = offset_map.get(*s).copied().unwrap_or(0);
out.extend_from_slice(&(offset as u32).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_all(&mut self) -> std::collections::HashMap<String, Vec<u8>> {
let mut sections = std::collections::HashMap::new();
let debug_info = self.emit_debug_info();
let debug_abbrev = self.emit_debug_abbrev();
let debug_line = self.emit_debug_line();
let debug_str = self.emit_debug_str();
sections.insert(".debug_info".to_string(), debug_info);
sections.insert(".debug_abbrev".to_string(), debug_abbrev);
sections.insert(".debug_line".to_string(), debug_line);
sections.insert(".debug_str".to_string(), debug_str);
if self.is_dwarf5 {
let debug_str_offsets = self.emit_debug_str_offsets();
sections.insert(".debug_str_offsets".to_string(), debug_str_offsets);
}
sections
}
pub fn build_debug_sections(
&mut self,
module: &Module,
output: &mut std::collections::HashMap<String, Vec<u8>>,
) {
let cu_name = &module.source_filename;
if cu_name.is_empty() {
return;
}
let comp_dir = "/tmp";
let language = dwarf_languages::DW_LANG_C;
let cu_idx = self.create_compile_unit(cu_name, comp_dir, language, 0);
let file_idx;
let func_names: Vec<String>;
{
let cu = &mut self.compile_units[cu_idx];
cu.line_program.add_directory(comp_dir);
file_idx = cu.line_program.add_file(cu_name, 1);
}
func_names = module
.functions
.iter()
.map(|f| f.borrow().name.clone())
.collect();
for func_name in &func_names {
let low_pc = 0x1000u64;
let high_pc = 0x50u64;
{
let cu = &mut self.compile_units[cu_idx];
DebugInfoEmitter::add_line_entry(cu, low_pc, file_idx, 1, 0);
}
DebugInfoEmitter::add_subprogram(
&mut self.compile_units[cu_idx],
func_name,
low_pc,
high_pc,
None,
);
}
let high_pc = {
let cu = &self.compile_units[cu_idx];
cu.subprograms.last().map(|sp| sp.high_pc).unwrap_or(0x50)
};
DebugInfoEmitter::set_cu_high_pc(&mut self.compile_units[cu_idx], high_pc);
*output = self.emit_all();
}
pub fn add_source_file(cu: &mut CompileUnit, dir_path: &str, file_name: &str) -> u64 {
cu.line_program.add_directory(dir_path);
cu.line_program.add_file(file_name, 1)
}
pub fn add_line_entry(
cu: &mut CompileUnit,
address: u64,
file_index: u64,
line: u64,
column: u64,
) {
cu.line_program.add_line(address, file_index, line, column);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compile_unit_new() {
let cu = CompileUnit::new();
assert_eq!(cu.die.tag, dwarf_tags::DW_TAG_compile_unit);
assert!(cu.subprograms.is_empty());
assert!(cu.global_variables.is_empty());
assert_eq!(cu.offset, 0);
}
#[test]
fn test_compile_unit_default() {
let cu = CompileUnit::default();
assert!(cu.string_offsets.is_empty());
}
#[test]
fn test_compile_unit_add_string() {
let mut cu = CompileUnit::new();
let off1 = cu.add_string("hello");
let off2 = cu.add_string("world");
assert!(off1 != off2);
let off1_again = cu.add_string("hello");
assert_eq!(off1, off1_again);
}
#[test]
fn test_compile_unit_get_string_offset() {
let mut cu = CompileUnit::new();
cu.add_string("test_string");
assert!(cu.get_string_offset("test_string").is_some());
assert!(cu.get_string_offset("nonexistent").is_none());
}
#[test]
fn test_subprogram_new() {
let sp = Subprogram::new("my_func", 0x1000, 0x50);
assert_eq!(sp.name, "my_func");
assert_eq!(sp.low_pc, 0x1000);
assert_eq!(sp.high_pc, 0x50);
assert!(sp.frame_base.is_none());
assert!(sp.variables.is_empty());
assert!(sp.lexical_blocks.is_empty());
}
#[test]
fn test_subprogram_with_frame_base() {
let mut sp = Subprogram::new("func", 0x2000, 0x80);
sp.frame_base = Some(vec![0x50, 0x30]); assert!(sp.frame_base.is_some());
}
#[test]
fn test_variable_new() {
let var = Variable::new("my_var");
assert_eq!(var.name, "my_var");
assert_eq!(var.die.tag, dwarf_tags::DW_TAG_variable);
assert!(!var.is_parameter);
assert!(var.var_type.is_none());
assert!(var.location.is_none());
}
#[test]
fn test_variable_new_parameter() {
let var = Variable::new_parameter("arg1");
assert_eq!(var.name, "arg1");
assert_eq!(var.die.tag, dwarf_tags::DW_TAG_formal_parameter);
assert!(var.is_parameter);
}
#[test]
fn test_variable_with_location() {
let mut var = Variable::new("v");
var.location = Some(vec![0x91, 0x68]); assert!(var.location.is_some());
}
#[test]
fn test_lexical_block_new() {
let lb = LexicalBlock::new(0x1100);
assert_eq!(lb.low_pc, 0x1100);
assert_eq!(lb.high_pc, 0);
assert_eq!(lb.die.tag, dwarf_tags::DW_TAG_lexical_block);
assert!(lb.variables.is_empty());
}
#[test]
fn test_inlined_subroutine_new() {
let is = InlinedSubroutine::new(0x1200, 0x20, 42);
assert_eq!(is.low_pc, 0x1200);
assert_eq!(is.high_pc, 0x20);
assert_eq!(is.abstract_origin, 42);
assert_eq!(is.die.tag, dwarf_tags::DW_TAG_inlined_subroutine);
}
#[test]
fn test_emitter_new() {
let emitter = DebugInfoEmitter::new("test-compiler", true, 8);
assert_eq!(emitter.address_size, 8);
assert_eq!(emitter.producer, "test-compiler");
assert!(emitter.is_dwarf5);
assert!(emitter.compile_units.is_empty());
assert_eq!(emitter.string_table, vec![0]);
}
#[test]
fn test_emitter_new_dwarf4() {
let emitter = DebugInfoEmitter::new("producer", false, 4);
assert!(!emitter.is_dwarf5);
assert_eq!(emitter.address_size, 4);
}
#[test]
fn test_create_compile_unit() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x4000);
let cu = &emitter.compile_units[idx];
assert_eq!(cu.die.tag, dwarf_tags::DW_TAG_compile_unit);
assert!(!cu.string_offsets.is_empty());
assert_eq!(emitter.compile_units.len(), 1);
}
#[test]
fn test_set_cu_high_pc() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
DebugInfoEmitter::set_cu_high_pc(cu, 0x500);
let has_high_pc = cu
.die
.attributes
.iter()
.any(|a| a.attr == dwarf_attributes::DW_AT_high_pc);
assert!(has_high_pc);
}
#[test]
fn test_add_subprogram() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let sp = DebugInfoEmitter::add_subprogram(cu, "main", 0x1100, 0x50, None);
assert_eq!(sp.name, "main");
assert_eq!(sp.low_pc, 0x1100);
assert_eq!(sp.high_pc, 0x50);
assert_eq!(cu.subprograms.len(), 1);
}
#[test]
fn test_add_subprogram_with_frame_base() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let fb = vec![0x91, 0x68];
let sp = DebugInfoEmitter::add_subprogram(cu, "func", 0x2000, 0x80, Some(fb.clone()));
assert!(sp.frame_base.is_some());
assert_eq!(sp.frame_base.as_ref().unwrap(), &fb);
}
#[test]
fn test_add_variable() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let name_off = cu.add_string("local_var");
let sp = DebugInfoEmitter::add_subprogram(cu, "main", 0x1100, 0x50, None);
let loc = vec![0x91, 0x6C];
let var = DebugInfoEmitter::add_variable(sp, "local_var", name_off, loc.clone());
assert_eq!(var.name, "local_var");
assert!(var.location.is_some());
assert_eq!(sp.variables.len(), 1);
}
#[test]
fn test_add_formal_parameter() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let name_off = cu.add_string("argc");
let sp = DebugInfoEmitter::add_subprogram(cu, "main", 0x1100, 0x50, None);
let loc = vec![0x50, 0x05];
let param = DebugInfoEmitter::add_formal_parameter(sp, "argc", name_off, 0, loc.clone());
assert_eq!(param.name, "argc");
assert!(param.is_parameter);
assert_eq!(sp.variables.len(), 1);
}
#[test]
fn test_add_type() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let offset = DebugInfoEmitter::add_type(
&emitter.next_die_offset,
cu,
"int",
4,
dwarf_encodings::DW_ATE_signed,
);
assert!(offset < emitter.next_die_offset.get());
assert!(cu.die.has_children);
assert_eq!(cu.die.children.len(), 1);
assert_eq!(cu.die.children[0].tag, dwarf_tags::DW_TAG_base_type);
}
#[test]
fn test_add_pointer_type() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let int_offset = DebugInfoEmitter::add_type(
&emitter.next_die_offset,
cu,
"int",
4,
dwarf_encodings::DW_ATE_signed,
);
let ptr_offset =
DebugInfoEmitter::add_pointer_type(&emitter.next_die_offset, cu, int_offset);
assert!(ptr_offset > int_offset);
assert_eq!(cu.die.children.len(), 2);
assert_eq!(cu.die.children[1].tag, dwarf_tags::DW_TAG_pointer_type);
}
#[test]
fn test_add_struct_type() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let int_offset = DebugInfoEmitter::add_type(
&emitter.next_die_offset,
cu,
"int",
4,
dwarf_encodings::DW_ATE_signed,
);
let members = vec![
("x".to_string(), int_offset, 0),
("y".to_string(), int_offset, 4),
];
let struct_offset =
DebugInfoEmitter::add_struct_type(&emitter.next_die_offset, cu, "Point", 8, members);
assert!(struct_offset > int_offset);
let struct_die = &cu.die.children[1];
assert_eq!(struct_die.tag, dwarf_tags::DW_TAG_structure_type);
assert!(struct_die.has_children);
assert_eq!(struct_die.children.len(), 2);
assert_eq!(struct_die.children[0].tag, dwarf_tags::DW_TAG_member);
}
#[test]
fn test_add_array_type() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let int_offset = DebugInfoEmitter::add_type(
&emitter.next_die_offset,
cu,
"int",
4,
dwarf_encodings::DW_ATE_signed,
);
let arr_offset =
DebugInfoEmitter::add_array_type(&emitter.next_die_offset, cu, int_offset, 10);
assert!(arr_offset > int_offset);
let arr_die = &cu.die.children[1];
assert_eq!(arr_die.tag, dwarf_tags::DW_TAG_array_type);
assert!(arr_die.has_children);
assert_eq!(arr_die.children[0].tag, dwarf_tags::DW_TAG_subrange_type);
}
#[test]
fn test_add_subroutine_type() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let _offset = DebugInfoEmitter::add_subroutine_type(&emitter.next_die_offset, cu);
assert!(cu.die.has_children);
assert_eq!(cu.die.children[0].tag, dwarf_tags::DW_TAG_subroutine_type);
}
#[test]
fn test_begin_and_end_lexical_block() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let sp = DebugInfoEmitter::add_subprogram(cu, "main", 0x1100, 0x50, None);
let lb = DebugInfoEmitter::begin_lexical_block(sp, 0x1120);
assert_eq!(lb.low_pc, 0x1120);
assert_eq!(lb.high_pc, 0);
DebugInfoEmitter::end_lexical_block(lb, 0x10);
assert_eq!(lb.high_pc, 0x10);
}
#[test]
fn test_add_source_file() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let file_idx = DebugInfoEmitter::add_source_file(cu, "/src", "test.c");
assert!(file_idx > 0);
}
#[test]
fn test_add_line_entry() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
DebugInfoEmitter::add_source_file(cu, "/src", "test.c");
DebugInfoEmitter::add_line_entry(cu, 0x1100, 1, 5, 8);
assert_eq!(cu.line_program.rows.len(), 1);
assert_eq!(cu.line_program.rows[0].line, 5);
assert_eq!(cu.line_program.rows[0].column, 8);
}
#[test]
fn test_emit_debug_info_empty() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let info = emitter.emit_debug_info();
assert!(info.is_empty());
}
#[test]
fn test_emit_debug_info_dwarf5() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let info = emitter.emit_debug_info();
assert!(!info.is_empty());
let version = u16::from_le_bytes(info[4..6].try_into().unwrap());
assert_eq!(version, 5);
assert_eq!(info[6], 0x01);
}
#[test]
fn test_emit_debug_info_dwarf4() {
let mut emitter = DebugInfoEmitter::new("test", false, 8);
emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let info = emitter.emit_debug_info();
assert!(!info.is_empty());
let version = u16::from_le_bytes(info[4..6].try_into().unwrap());
assert_eq!(version, 4);
}
#[test]
fn test_emit_debug_abbrev() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
emitter.emit_debug_info();
let abbrev = emitter.emit_debug_abbrev();
assert!(!abbrev.is_empty());
}
#[test]
fn test_emit_debug_line() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
cu.line_program.add_file("test.c", 0);
cu.line_program.add_line(0x1000, 1, 1, 0);
let line_data = emitter.emit_debug_line();
assert!(!line_data.is_empty());
}
#[test]
fn test_emit_debug_str() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
cu.add_string("hello");
cu.add_string("world");
let str_data = emitter.emit_debug_str();
assert!(!str_data.is_empty());
let s = String::from_utf8_lossy(&str_data);
assert!(s.contains("hello"));
assert!(s.contains("world"));
}
#[test]
fn test_emit_debug_str_offsets_dwarf5() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
cu.add_string("string1");
cu.add_string("string2");
emitter.emit_debug_str();
let offsets = emitter.emit_debug_str_offsets();
assert!(!offsets.is_empty());
}
#[test]
fn test_emit_all() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let sections = emitter.emit_all();
assert!(sections.contains_key(".debug_info"));
assert!(sections.contains_key(".debug_abbrev"));
assert!(sections.contains_key(".debug_line"));
assert!(sections.contains_key(".debug_str"));
assert!(sections.contains_key(".debug_str_offsets"));
assert!(!sections[".debug_info"].is_empty());
assert!(!sections[".debug_abbrev"].is_empty());
}
#[test]
fn test_emit_all_dwarf4() {
let mut emitter = DebugInfoEmitter::new("test", false, 4);
emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let sections = emitter.emit_all();
assert!(sections.contains_key(".debug_info"));
assert!(sections.contains_key(".debug_abbrev"));
assert!(sections.contains_key(".debug_line"));
assert!(sections.contains_key(".debug_str"));
assert!(!sections.contains_key(".debug_str_offsets"));
}
#[test]
fn test_emit_debug_info_with_subprogram() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
let _sp = DebugInfoEmitter::add_subprogram(cu, "main", 0x1100, 0x50, None);
let info = emitter.emit_debug_info();
assert!(info.len() > 11);
}
#[test]
fn test_emit_debug_info_with_types() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let idx = emitter.create_compile_unit("test.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
let cu = &mut emitter.compile_units[idx];
DebugInfoEmitter::add_type(
&emitter.next_die_offset,
cu,
"int",
4,
dwarf_encodings::DW_ATE_signed,
);
let info = emitter.emit_debug_info();
assert!(!info.is_empty());
}
#[test]
fn test_build_debug_sections_from_module() {
let mut emitter = DebugInfoEmitter::new("llvm-native", true, 8);
let module = Module::new("test_module");
let mut output = std::collections::HashMap::new();
emitter.build_debug_sections(&module, &mut output);
}
#[test]
fn test_build_debug_sections_empty_module() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
let module = Module::new("empty");
let mut output = std::collections::HashMap::new();
emitter.build_debug_sections(&module, &mut output);
assert!(output.is_empty());
}
#[test]
fn test_multiple_compile_units() {
let mut emitter = DebugInfoEmitter::new("test", true, 8);
emitter.create_compile_unit("a.c", "/src", dwarf_languages::DW_LANG_C, 0x1000);
emitter.create_compile_unit("b.c", "/src", dwarf_languages::DW_LANG_C, 0x2000);
assert_eq!(emitter.compile_units.len(), 2);
let info = emitter.emit_debug_info();
assert!(!info.is_empty());
}
}