1use {
2 crate::error::{DebuggerError, DebuggerResult},
3 gimli::{EndianSlice, RunTimeEndian, SectionId},
4 object::{Object, ObjectSection},
5 sbpf_vm::memory::Memory,
6 std::{borrow::Cow, collections::HashMap, path::PathBuf},
7};
8
9#[derive(Debug, Clone)]
10pub struct RODataSymbol {
11 pub name: String,
12 pub address: u64,
13 pub content: String,
14}
15
16pub fn rodata_from_section(
17 section: &sbpf_disassembler::rodata::RodataSection,
18) -> Vec<RODataSymbol> {
19 section
20 .items
21 .iter()
22 .map(|item| RODataSymbol {
23 name: item.label.clone(),
24 address: Memory::RODATA_START + item.offset,
25 content: item.data_type.to_asm(),
26 })
27 .collect()
28}
29
30#[derive(Debug, Clone)]
31pub struct SourceLocation {
32 pub file: String,
33 pub line: u32,
34 pub column: u32,
35 _address: u64,
36}
37
38#[derive(Clone)]
39pub struct LineMap {
40 address_to_line: HashMap<u64, usize>,
41 line_to_addresses: HashMap<usize, Vec<u64>>,
42 source_locations: HashMap<u64, SourceLocation>,
43 labels: HashMap<u64, String>,
44 files: Vec<String>,
45 text_offset: u64,
46}
47
48impl Default for LineMap {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl LineMap {
55 pub fn new() -> Self {
56 Self {
57 address_to_line: HashMap::new(),
58 line_to_addresses: HashMap::new(),
59 source_locations: HashMap::new(),
60 labels: HashMap::new(),
61 files: Vec::new(),
62 text_offset: 0,
63 }
64 }
65
66 pub fn from_elf_file(file_path: &str) -> DebuggerResult<Self> {
67 let file_data = std::fs::read(file_path)?;
68 Self::from_elf_data(&file_data)
69 }
70
71 pub fn from_elf_data(file_data: &[u8]) -> DebuggerResult<Self> {
72 let object = object::File::parse(file_data)?;
73 let mut line_map = Self::new();
74 line_map.text_offset = object
75 .section_by_name(".text")
76 .map(|section| section.address())
77 .unwrap_or(0);
78 line_map.parse_debug_info_from_object(&object)?;
79 Ok(line_map)
80 }
81
82 fn parse_debug_info_from_object(&mut self, obj_file: &object::File) -> DebuggerResult<()> {
83 let endian = if obj_file.is_little_endian() {
84 RunTimeEndian::Little
85 } else {
86 RunTimeEndian::Big
87 };
88
89 let load_section = |id: SectionId| -> Result<Cow<[u8]>, gimli::Error> {
90 match obj_file.section_by_name(id.name()) {
91 Some(section) => match section.uncompressed_data() {
92 Ok(data) => Ok(data),
93 Err(_) => Ok(Cow::Borrowed(&[])),
94 },
95 None => Ok(Cow::Borrowed(&[])),
96 }
97 };
98
99 let borrow_section = |section| EndianSlice::new(Cow::as_ref(section), endian);
100
101 let dwarf_sections =
102 gimli::DwarfSections::load(&load_section).map_err(DebuggerError::Dwarf)?;
103 let dwarf = dwarf_sections.borrow(borrow_section);
104
105 let mut iter = dwarf.units();
106 while let Some(header) = iter.next().map_err(DebuggerError::Dwarf)? {
107 let unit = dwarf.unit(header).map_err(DebuggerError::Dwarf)?;
108 let unit = unit.unit_ref(&dwarf);
109
110 if let Some(program) = unit.line_program.clone() {
111 let comp_dir = if let Some(ref dir) = unit.comp_dir {
112 PathBuf::from(dir.to_string_lossy().into_owned())
113 } else {
114 PathBuf::new()
115 };
116
117 let mut rows = program.rows();
118 while let Some((header, row)) = rows.next_row().map_err(DebuggerError::Dwarf)? {
119 if !row.end_sequence() {
120 let mut file_path = String::new();
121 if let Some(file) = row.file(header) {
122 let mut path = PathBuf::new();
123 path.clone_from(&comp_dir);
124
125 if file.directory_index() != 0
126 && let Some(dir) = file.directory(header)
127 {
128 path.push(
129 unit.attr_string(dir)
130 .map_err(DebuggerError::Dwarf)?
131 .to_string_lossy()
132 .as_ref(),
133 );
134 }
135
136 path.push(
137 unit.attr_string(file.path_name())
138 .map_err(DebuggerError::Dwarf)?
139 .to_string_lossy()
140 .as_ref(),
141 );
142 file_path = path.to_string_lossy().to_string();
143 }
144
145 let line = match row.line() {
146 Some(line) => line.get() as u32,
147 None => 0,
148 };
149 let column = match row.column() {
150 gimli::ColumnType::LeftEdge => 0,
151 gimli::ColumnType::Column(column) => column.get() as u32,
152 };
153
154 let address = row.address();
155
156 self.address_to_line.insert(address, line as usize);
157 self.line_to_addresses
158 .entry(line as usize)
159 .or_default()
160 .push(address);
161
162 let source_loc = SourceLocation {
163 file: file_path.clone(),
164 line,
165 column,
166 _address: address,
167 };
168 self.source_locations.insert(address, source_loc);
169
170 if !file_path.is_empty() && !self.files.contains(&file_path) {
171 self.files.push(file_path);
172 }
173 }
174 }
175 }
176
177 let mut entries = unit.entries();
179 while let Some(entry) = entries.next_dfs().map_err(DebuggerError::Dwarf)? {
180 if entry.tag() == gimli::DW_TAG_label
181 && let (Some(name_attr), Some(pc_attr)) = (
182 entry.attr_value(gimli::DW_AT_name),
183 entry.attr_value(gimli::DW_AT_low_pc),
184 )
185 && let Ok(name) = unit.attr_string(name_attr)
186 && let gimli::AttributeValue::Addr(addr) = pc_attr
187 {
188 self.labels.insert(addr, name.to_string_lossy().to_string());
189 }
190 }
191 }
192
193 Ok(())
194 }
195
196 pub fn get_label_for_address(&self, address: u64) -> Option<&str> {
197 self.labels.get(&address).map(|s| s.as_str())
198 }
199
200 pub fn get_text_offset(&self) -> u64 {
201 self.text_offset
202 }
203
204 pub fn get_line_for_address(&self, address: u64) -> Option<usize> {
205 self.address_to_line.get(&address).copied()
206 }
207
208 pub fn get_addresses_for_line(&self, line: usize) -> Option<&[u64]> {
209 self.line_to_addresses.get(&line).map(|v| v.as_slice())
210 }
211
212 pub fn get_line_for_pc(&self, pc: u64) -> Option<usize> {
213 let address = pc.saturating_add(self.text_offset);
214 self.get_line_for_address(address)
215 }
216
217 pub fn get_pcs_for_line(&self, line: usize) -> Vec<u64> {
218 if let Some(addresses) = self.get_addresses_for_line(line) {
219 addresses
220 .iter()
221 .map(|addr| addr.saturating_sub(self.text_offset))
222 .collect()
223 } else {
224 Vec::new()
225 }
226 }
227
228 pub fn get_source_location(&self, pc: u64) -> Option<&SourceLocation> {
229 let address = pc.saturating_add(self.text_offset);
230 self.source_locations.get(&address)
231 }
232
233 pub fn get_line_to_addresses(&self) -> &HashMap<usize, Vec<u64>> {
234 &self.line_to_addresses
235 }
236
237 pub fn get_line_to_pcs(&self) -> HashMap<usize, Vec<u64>> {
238 self.line_to_addresses
239 .iter()
240 .map(|(line, addrs)| {
241 (
242 *line,
243 addrs
244 .iter()
245 .map(|addr| addr.saturating_sub(self.text_offset))
246 .collect(),
247 )
248 })
249 .collect()
250 }
251}