1use {
2 crate::section::{DebugSection, SectionType},
3 gimli::{
4 DW_AT_comp_dir, DW_AT_decl_file, DW_AT_decl_line, DW_AT_high_pc, DW_AT_language,
5 DW_AT_low_pc, DW_AT_name, DW_AT_producer, DW_AT_stmt_list, DW_LANG_Mips_Assembler,
6 DW_TAG_label, Encoding, Format, LineEncoding, LittleEndian, SectionId,
7 write::{Address, AttributeValue, DwarfUnit, EndianVec, LineProgram, LineString, Sections},
8 },
9};
10
11const SBPF_INSTRUCTION_LENGTH: u8 = 8;
12
13#[derive(Debug, Clone)]
14pub struct DebugData {
15 pub filename: String,
16 pub directory: String,
17 pub lines: Vec<(u64, u32)>,
18 pub labels: Vec<(String, u64, u32)>,
19 pub code_start: u64,
20 pub code_end: u64,
21}
22
23fn calc_name_offset(names: &[String]) -> u32 {
24 (names
25 .iter()
26 .filter(|n| !n.is_empty())
27 .map(|n| n.len() + 1)
28 .sum::<usize>()
29 + 1) as u32
30}
31
32pub fn generate_debug_sections(
34 data: &DebugData,
35 text_offset: u64,
36 section_names: &mut Vec<String>,
37 current_offset: &mut u64,
38) -> Vec<DebugSection> {
39 let code_start = data.code_start + text_offset;
40 let code_end = data.code_end + text_offset;
41 let mut dwarf = generate_dwarf_sections(data, text_offset, code_start, code_end);
42
43 let mut sections = Vec::new();
44
45 let mut abbrev = DebugSection::new(
47 SectionId::DebugAbbrev.name(),
48 calc_name_offset(section_names),
49 dwarf.debug_abbrev.take(),
50 );
51 section_names.push(SectionId::DebugAbbrev.name().to_string());
52 abbrev.set_offset(*current_offset);
53 *current_offset += abbrev.size();
54 sections.push(abbrev);
55
56 let mut info = DebugSection::new(
58 SectionId::DebugInfo.name(),
59 calc_name_offset(section_names),
60 dwarf.debug_info.take(),
61 );
62 section_names.push(SectionId::DebugInfo.name().to_string());
63 info.set_offset(*current_offset);
64 *current_offset += info.size();
65 sections.push(info);
66
67 let mut line = DebugSection::new(
69 SectionId::DebugLine.name(),
70 calc_name_offset(section_names),
71 dwarf.debug_line.take(),
72 );
73 section_names.push(SectionId::DebugLine.name().to_string());
74 line.set_offset(*current_offset);
75 *current_offset += line.size();
76 sections.push(line);
77
78 let mut line_str = DebugSection::new(
80 SectionId::DebugLineStr.name(),
81 calc_name_offset(section_names),
82 dwarf.debug_line_str.take(),
83 );
84 section_names.push(SectionId::DebugLineStr.name().to_string());
85 line_str.set_offset(*current_offset);
86 *current_offset += line_str.size();
87 sections.push(line_str);
88
89 sections
90}
91
92fn generate_dwarf_sections(
94 data: &DebugData,
95 text_offset: u64,
96 code_start: u64,
97 code_end: u64,
98) -> Sections<EndianVec<LittleEndian>> {
99 let encoding = Encoding {
100 format: Format::Dwarf32,
101 version: 5,
102 address_size: 8,
103 };
104
105 let line_encoding = LineEncoding {
106 minimum_instruction_length: SBPF_INSTRUCTION_LENGTH,
107 ..LineEncoding::default()
108 };
109
110 let mut dwarf = DwarfUnit::new(encoding);
111
112 let dir_string_id = dwarf.line_strings.add(data.directory.clone().into_bytes());
114 let file_string_id = dwarf.line_strings.add(data.filename.clone().into_bytes());
115
116 let mut line_program = LineProgram::new(
118 encoding,
119 line_encoding,
120 LineString::LineStringRef(dir_string_id),
121 None,
122 LineString::LineStringRef(file_string_id),
123 None,
124 );
125
126 let dir_id = line_program.default_directory();
127 let file_id = line_program.add_file(LineString::LineStringRef(file_string_id), dir_id, None);
128
129 line_program.begin_sequence(Some(Address::Constant(code_start)));
131 for (address, line) in &data.lines {
132 let adjusted_addr = address + text_offset;
133 line_program.row().file = file_id;
134 line_program.row().address_offset = adjusted_addr - code_start;
135 line_program.row().line = *line as u64;
136 line_program.generate_row();
137 }
138 line_program.end_sequence(code_end);
139
140 dwarf.unit.line_program = line_program;
141
142 let root_id = dwarf.unit.root();
144 let root = dwarf.unit.get_mut(root_id);
145
146 root.set(
147 DW_AT_name,
148 AttributeValue::String(data.filename.clone().into_bytes()),
149 );
150 root.set(
151 DW_AT_comp_dir,
152 AttributeValue::String(data.directory.clone().into_bytes()),
153 );
154 root.set(
155 DW_AT_producer,
156 AttributeValue::String(b"sbpf-assembler".to_vec()),
157 );
158 root.set(
159 DW_AT_language,
160 AttributeValue::Language(DW_LANG_Mips_Assembler),
161 );
162 root.set(
163 DW_AT_low_pc,
164 AttributeValue::Address(Address::Constant(code_start)),
165 );
166 root.set(
167 DW_AT_high_pc,
168 AttributeValue::Address(Address::Constant(code_end)),
169 );
170 root.set(DW_AT_stmt_list, AttributeValue::LineProgramRef);
171
172 for (name, address, line) in &data.labels {
174 let adjusted_addr = address + text_offset;
175 let label_id = dwarf.unit.add(root_id, DW_TAG_label);
176 let label_die = dwarf.unit.get_mut(label_id);
177
178 label_die.set(
179 DW_AT_name,
180 AttributeValue::String(name.clone().into_bytes()),
181 );
182 label_die.set(DW_AT_decl_file, AttributeValue::Data4(0));
183 label_die.set(DW_AT_decl_line, AttributeValue::Data4(*line));
184 label_die.set(
185 DW_AT_low_pc,
186 AttributeValue::Address(Address::Constant(adjusted_addr)),
187 );
188 }
189
190 let mut sections = Sections::new(EndianVec::new(LittleEndian));
192 dwarf.write(&mut sections).expect("Failed to write DWARF");
193 sections
194}
195
196pub fn reuse_debug_sections(
198 parsed_debug_sections: Vec<DebugSection>,
199 section_names: &mut Vec<String>,
200 current_offset: &mut u64,
201) -> Vec<SectionType> {
202 let mut sections = Vec::default();
204 for mut debug_section in parsed_debug_sections.into_iter() {
205 debug_section.set_name_offset(calc_name_offset(section_names));
206 section_names.push(debug_section.name().to_string());
207 debug_section.set_offset(*current_offset);
208 *current_offset += debug_section.size();
209 if debug_section.name() == SectionId::DebugAbbrev.name() {
210 sections.push(SectionType::DebugAbbrev(debug_section));
211 } else if debug_section.name() == SectionId::DebugInfo.name() {
212 sections.push(SectionType::DebugInfo(debug_section));
213 } else if debug_section.name() == SectionId::DebugLine.name() {
214 sections.push(SectionType::DebugLine(debug_section));
215 } else if debug_section.name() == SectionId::DebugLineStr.name() {
216 sections.push(SectionType::DebugLineStr(debug_section));
217 } else if debug_section.name() == SectionId::DebugStr.name() {
218 sections.push(SectionType::DebugStr(debug_section));
219 } else if debug_section.name() == SectionId::DebugFrame.name() {
220 sections.push(SectionType::DebugFrame(debug_section));
221 } else if debug_section.name() == SectionId::DebugLoc.name() {
222 sections.push(SectionType::DebugLoc(debug_section));
223 } else if debug_section.name() == SectionId::DebugRanges.name() {
224 sections.push(SectionType::DebugRanges(debug_section));
225 } else {
226 eprintln!(
227 "Unimplemented debug section: {}, consider adding it",
228 debug_section.name()
229 );
230 continue;
231 }
232 }
233 sections
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn test_generate_debug_sections() {
242 let mut section_names = vec![".text".to_string()];
243 let mut offset = 100u64;
244
245 let data = DebugData {
246 filename: "test.s".to_string(),
247 directory: "/tmp".to_string(),
248 lines: vec![(0, 5), (8, 6)],
249 labels: vec![("entrypoint".to_string(), 0, 4)],
250 code_start: 0,
251 code_end: 16,
252 };
253
254 let sections = generate_debug_sections(&data, 0x100, &mut section_names, &mut offset);
255
256 assert_eq!(sections.len(), 4);
257 assert_eq!(sections[0].name(), SectionId::DebugAbbrev.name());
258 assert_eq!(sections[1].name(), SectionId::DebugInfo.name());
259 assert_eq!(sections[2].name(), SectionId::DebugLine.name());
260 assert_eq!(sections[3].name(), SectionId::DebugLineStr.name());
261
262 for section in §ions {
263 assert!(!section.bytecode().is_empty());
264 }
265 }
266}