use {
crate::section::{DebugSection, SectionType},
gimli::{
DW_AT_comp_dir, DW_AT_decl_file, DW_AT_decl_line, DW_AT_high_pc, DW_AT_language,
DW_AT_low_pc, DW_AT_name, DW_AT_producer, DW_AT_stmt_list, DW_LANG_Mips_Assembler,
DW_TAG_label, Encoding, Format, LineEncoding, LittleEndian, SectionId,
write::{Address, AttributeValue, DwarfUnit, EndianVec, LineProgram, LineString, Sections},
},
};
const SBPF_INSTRUCTION_LENGTH: u8 = 8;
#[derive(Debug, Clone)]
pub struct DebugData {
pub filename: String,
pub directory: String,
pub lines: Vec<(u64, u32)>,
pub labels: Vec<(String, u64, u32)>,
pub code_start: u64,
pub code_end: u64,
}
fn calc_name_offset(names: &[String]) -> u32 {
(names
.iter()
.filter(|n| !n.is_empty())
.map(|n| n.len() + 1)
.sum::<usize>()
+ 1) as u32
}
pub fn generate_debug_sections(
data: &DebugData,
text_offset: u64,
section_names: &mut Vec<String>,
current_offset: &mut u64,
) -> Vec<DebugSection> {
let code_start = data.code_start + text_offset;
let code_end = data.code_end + text_offset;
let mut dwarf = generate_dwarf_sections(data, text_offset, code_start, code_end);
let mut sections = Vec::new();
let mut abbrev = DebugSection::new(
SectionId::DebugAbbrev.name(),
calc_name_offset(section_names),
dwarf.debug_abbrev.take(),
);
section_names.push(SectionId::DebugAbbrev.name().to_string());
abbrev.set_offset(*current_offset);
*current_offset += abbrev.size();
sections.push(abbrev);
let mut info = DebugSection::new(
SectionId::DebugInfo.name(),
calc_name_offset(section_names),
dwarf.debug_info.take(),
);
section_names.push(SectionId::DebugInfo.name().to_string());
info.set_offset(*current_offset);
*current_offset += info.size();
sections.push(info);
let mut line = DebugSection::new(
SectionId::DebugLine.name(),
calc_name_offset(section_names),
dwarf.debug_line.take(),
);
section_names.push(SectionId::DebugLine.name().to_string());
line.set_offset(*current_offset);
*current_offset += line.size();
sections.push(line);
let mut line_str = DebugSection::new(
SectionId::DebugLineStr.name(),
calc_name_offset(section_names),
dwarf.debug_line_str.take(),
);
section_names.push(SectionId::DebugLineStr.name().to_string());
line_str.set_offset(*current_offset);
*current_offset += line_str.size();
sections.push(line_str);
sections
}
fn generate_dwarf_sections(
data: &DebugData,
text_offset: u64,
code_start: u64,
code_end: u64,
) -> Sections<EndianVec<LittleEndian>> {
let encoding = Encoding {
format: Format::Dwarf32,
version: 5,
address_size: 8,
};
let line_encoding = LineEncoding {
minimum_instruction_length: SBPF_INSTRUCTION_LENGTH,
..LineEncoding::default()
};
let mut dwarf = DwarfUnit::new(encoding);
let dir_string_id = dwarf.line_strings.add(data.directory.clone().into_bytes());
let file_string_id = dwarf.line_strings.add(data.filename.clone().into_bytes());
let mut line_program = LineProgram::new(
encoding,
line_encoding,
LineString::LineStringRef(dir_string_id),
None,
LineString::LineStringRef(file_string_id),
None,
);
let dir_id = line_program.default_directory();
let file_id = line_program.add_file(LineString::LineStringRef(file_string_id), dir_id, None);
line_program.begin_sequence(Some(Address::Constant(code_start)));
for (address, line) in &data.lines {
let adjusted_addr = address + text_offset;
line_program.row().file = file_id;
line_program.row().address_offset = adjusted_addr - code_start;
line_program.row().line = *line as u64;
line_program.generate_row();
}
line_program.end_sequence(code_end);
dwarf.unit.line_program = line_program;
let root_id = dwarf.unit.root();
let root = dwarf.unit.get_mut(root_id);
root.set(
DW_AT_name,
AttributeValue::String(data.filename.clone().into_bytes()),
);
root.set(
DW_AT_comp_dir,
AttributeValue::String(data.directory.clone().into_bytes()),
);
root.set(
DW_AT_producer,
AttributeValue::String(b"sbpf-assembler".to_vec()),
);
root.set(
DW_AT_language,
AttributeValue::Language(DW_LANG_Mips_Assembler),
);
root.set(
DW_AT_low_pc,
AttributeValue::Address(Address::Constant(code_start)),
);
root.set(
DW_AT_high_pc,
AttributeValue::Address(Address::Constant(code_end)),
);
root.set(DW_AT_stmt_list, AttributeValue::LineProgramRef);
for (name, address, line) in &data.labels {
let adjusted_addr = address + text_offset;
let label_id = dwarf.unit.add(root_id, DW_TAG_label);
let label_die = dwarf.unit.get_mut(label_id);
label_die.set(
DW_AT_name,
AttributeValue::String(name.clone().into_bytes()),
);
label_die.set(DW_AT_decl_file, AttributeValue::Data4(0));
label_die.set(DW_AT_decl_line, AttributeValue::Data4(*line));
label_die.set(
DW_AT_low_pc,
AttributeValue::Address(Address::Constant(adjusted_addr)),
);
}
let mut sections = Sections::new(EndianVec::new(LittleEndian));
dwarf.write(&mut sections).expect("Failed to write DWARF");
sections
}
pub fn reuse_debug_sections(
parsed_debug_sections: Vec<DebugSection>,
section_names: &mut Vec<String>,
current_offset: &mut u64,
) -> Vec<SectionType> {
let mut sections = Vec::default();
for mut debug_section in parsed_debug_sections.into_iter() {
debug_section.set_name_offset(calc_name_offset(section_names));
section_names.push(debug_section.name().to_string());
debug_section.set_offset(*current_offset);
*current_offset += debug_section.size();
if debug_section.name() == SectionId::DebugAbbrev.name() {
sections.push(SectionType::DebugAbbrev(debug_section));
} else if debug_section.name() == SectionId::DebugInfo.name() {
sections.push(SectionType::DebugInfo(debug_section));
} else if debug_section.name() == SectionId::DebugLine.name() {
sections.push(SectionType::DebugLine(debug_section));
} else if debug_section.name() == SectionId::DebugLineStr.name() {
sections.push(SectionType::DebugLineStr(debug_section));
} else if debug_section.name() == SectionId::DebugStr.name() {
sections.push(SectionType::DebugStr(debug_section));
} else if debug_section.name() == SectionId::DebugFrame.name() {
sections.push(SectionType::DebugFrame(debug_section));
} else if debug_section.name() == SectionId::DebugLoc.name() {
sections.push(SectionType::DebugLoc(debug_section));
} else if debug_section.name() == SectionId::DebugRanges.name() {
sections.push(SectionType::DebugRanges(debug_section));
} else {
eprintln!(
"Unimplemented debug section: {}, consider adding it",
debug_section.name()
);
continue;
}
}
sections
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_debug_sections() {
let mut section_names = vec![".text".to_string()];
let mut offset = 100u64;
let data = DebugData {
filename: "test.s".to_string(),
directory: "/tmp".to_string(),
lines: vec![(0, 5), (8, 6)],
labels: vec![("entrypoint".to_string(), 0, 4)],
code_start: 0,
code_end: 16,
};
let sections = generate_debug_sections(&data, 0x100, &mut section_names, &mut offset);
assert_eq!(sections.len(), 4);
assert_eq!(sections[0].name(), SectionId::DebugAbbrev.name());
assert_eq!(sections[1].name(), SectionId::DebugInfo.name());
assert_eq!(sections[2].name(), SectionId::DebugLine.name());
assert_eq!(sections[3].name(), SectionId::DebugLineStr.name());
for section in §ions {
assert!(!section.bytecode().is_empty());
}
}
}