1use super::types::FileOffset;
2
3use super::dwarf::Dwarf;
4
5use super::ffi::demangle;
6use bytemuck::checked::pod_read_unaligned;
7use bytemuck::{Pod, Zeroable};
8use bytes::Bytes;
9use elf::abi::STT_TLS;
10use goblin::elf::sym::st_type;
11use multimap::MultiMap;
12use nix::libc::{Elf64_Ehdr, Elf64_Shdr, Elf64_Sym};
13use nix::{
14 fcntl::{OFlag, open},
15 sys::{
16 mman::{MapFlags, ProtFlags, mmap, munmap},
17 stat::{Mode, fstat},
18 },
19};
20use std::cell::{OnceCell, RefCell};
21use std::collections::{BTreeMap, HashMap};
22use std::rc::{Rc, Weak};
23use std::{
24 num::NonZeroUsize,
25 os::fd::{FromRawFd, OwnedFd},
26 path::{Path, PathBuf},
27};
28
29use super::bit::cstr_view;
30use super::bit::from_array_bytes;
31use super::sdb_error::SdbError;
32use super::types::FileAddress;
33use super::types::VirtualAddress;
34use std::{mem, ptr};
35
36#[derive(Debug, Clone, Copy)]
37#[repr(transparent)]
38pub struct SdbElf64Ehdr(pub Elf64_Ehdr);
39
40unsafe impl Pod for SdbElf64Ehdr {}
41
42unsafe impl Zeroable for SdbElf64Ehdr {}
43
44#[derive(Debug, Clone, Copy)]
45#[repr(transparent)]
46pub struct SdbElf64Shdr(pub Elf64_Shdr);
47
48unsafe impl Pod for SdbElf64Shdr {}
49
50unsafe impl Zeroable for SdbElf64Shdr {}
51
52#[derive(Debug, Clone, Copy)]
53#[repr(transparent)]
54pub struct SdbElf64Sym(pub Elf64_Sym);
55
56unsafe impl Pod for SdbElf64Sym {}
57
58unsafe impl Zeroable for SdbElf64Sym {}
59
60#[derive(Debug)]
61pub struct Elf {
62 fd: OwnedFd,
63 path: PathBuf,
64 file_size: usize,
65 data: Bytes,
66 header: SdbElf64Ehdr,
67 section_headers: Vec<Rc<SdbElf64Shdr>>,
68 section_map: HashMap<String, Rc<SdbElf64Shdr>>,
69 load_bias: RefCell<VirtualAddress>,
70 symbol_table: Vec<Rc<SdbElf64Sym>>,
71 symbol_name_map: RefCell<MultiMap<String, Rc<SdbElf64Sym>>>,
72 symbol_addr_map: RefCell<BTreeMap<FileAddressRange, Rc<SdbElf64Sym>>>,
73 dwarf: OnceCell<Rc<Dwarf>>,
74}
75
76impl Elf {
77 pub fn data_pointer_as_file_offset(self: &Rc<Self>, ptr: &Bytes) -> FileOffset {
78 FileOffset::new(self, ptr.as_ptr() as u64 - self.data.as_ptr() as u64)
79 }
80 pub fn file_offset_as_data_pointer(self: &Rc<Self>, offset: FileOffset) -> Bytes {
81 self.data.slice(offset.off() as usize..)
82 }
83
84 pub fn get_section_start_address(self: &Rc<Self>, name: &str) -> Option<FileAddress> {
85 return self
86 .get_section(name)
87 .map(|section| FileAddress::new(self, section.0.sh_addr));
88 }
89
90 pub fn build_symbol_maps(self: &Rc<Self>) {
91 let this = self;
92 for symbol in &this.symbol_table {
93 let mangled_name = this.get_string(symbol.0.st_name as usize).to_owned();
94 let demangled_name = demangle(&mangled_name);
95 let mut symbol_name_map = this.symbol_name_map.borrow_mut();
96 if let Some(demangled_name) = demangled_name {
97 symbol_name_map.insert(demangled_name, symbol.clone());
98 }
99 symbol_name_map.insert(mangled_name.to_owned(), symbol.clone());
100 if symbol.0.st_value != 0
101 && symbol.0.st_name != 0
102 && st_type(symbol.0.st_info) != STT_TLS
103 {
104 let addr_range = FileAddressRange(
105 FileAddress::new(self, symbol.0.st_value),
106 FileAddress::new(self, symbol.0.st_value + symbol.0.st_size),
107 );
108 this.symbol_addr_map
109 .borrow_mut()
110 .insert(addr_range, symbol.clone());
111 }
112 }
113 }
114
115 pub fn get_symbol_at_virt_address(
116 self: &Rc<Self>,
117 address: VirtualAddress,
118 ) -> Option<Rc<SdbElf64Sym>> {
119 self.get_symbol_at_file_address(address.to_file_addr_elf(self))
120 }
121
122 pub fn get_symbol_containing_virt_address(
123 self: &Rc<Self>,
124 address: VirtualAddress,
125 ) -> Option<Rc<SdbElf64Sym>> {
126 self.get_symbol_containing_file_address(address.to_file_addr_elf(self))
127 }
128
129 pub fn new(path: &Path) -> Result<Rc<Self>, SdbError> {
130 let raw_fd = open(path, OFlag::O_RDONLY, Mode::empty())
131 .map_err(|_| SdbError::new_err("Could not open ELF file"))?;
132 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
133
134 let stat =
135 fstat(raw_fd).map_err(|_| SdbError::new_err("Could not retrieve ELF file stats"))?;
136 let file_size = stat.st_size as usize;
137
138 let map = unsafe {
139 mmap(
140 None,
141 NonZeroUsize::new(file_size).ok_or(SdbError::new_err("ELF file is empty"))?,
142 ProtFlags::PROT_READ,
143 MapFlags::MAP_SHARED,
144 fd.try_clone()
145 .map_err(|_| SdbError::new_err("ELF file is closed"))?,
146 0,
147 )
148 .map_err(|_| SdbError::new_err("Could not mmap ELF file"))?
149 };
150
151 let bytes = unsafe { std::slice::from_raw_parts(map.as_ptr() as *const u8, file_size) };
152 let mut data = Vec::<u8>::with_capacity(file_size);
153 data.extend_from_slice(bytes);
154 unsafe { munmap(map, file_size).expect("mmap uniquely managed by Elf object") };
155 let _ = bytes; let header = pod_read_unaligned(&data[..mem::size_of::<SdbElf64Ehdr>()]);
158
159 let mut obj = Self {
160 fd,
161 path: path.to_path_buf(),
162 file_size,
163 data: Bytes::from(data),
164 header,
165 section_headers: Vec::default(),
166 section_map: HashMap::default(),
167 load_bias: RefCell::new(0.into()),
168 symbol_table: Vec::default(),
169 symbol_name_map: RefCell::new(MultiMap::default()),
170 symbol_addr_map: RefCell::new(BTreeMap::default()),
171 dwarf: OnceCell::new(),
172 };
173 obj.parse_section_headers();
174 obj.build_section_map();
175 obj.parse_symbol_table();
176 let ret = Rc::new(obj);
177 ret.build_symbol_maps();
178 ret.dwarf.set(Dwarf::new(&Rc::downgrade(&ret))?).unwrap();
179 Ok(ret)
180 }
181
182 pub fn path(&self) -> &Path {
183 &self.path
184 }
185
186 pub fn get_header(&self) -> &SdbElf64Ehdr {
187 &self.header
188 }
189
190 fn parse_section_headers(&mut self) {
191 let mut n_headers = self.header.0.e_shnum as usize;
192
193 if n_headers == 0 && self.header.0.e_shentsize as usize != 0 {
194 let sh0: SdbElf64Shdr = pod_read_unaligned(
195 &self.data[self.header.0.e_shoff as usize..mem::size_of::<SdbElf64Shdr>()],
196 );
197 n_headers = sh0.0.sh_size as usize;
198 }
199
200 let section_headers: Vec<SdbElf64Shdr> = from_array_bytes(
201 &self.data[self.header.0.e_shoff as usize
202 ..self.header.0.e_shoff as usize + n_headers * mem::size_of::<SdbElf64Shdr>()],
203 );
204 self.section_headers = section_headers.into_iter().map(Rc::new).collect();
205 }
206
207 pub fn get_section_name(&self, index: usize) -> &str {
208 let section = &self.section_headers[self.header.0.e_shstrndx as usize];
209 let offset = section.0.sh_offset as usize + index;
210 cstr_view(&self.data[offset..])
211 }
212
213 pub fn get_section(&self, name: &str) -> Option<Rc<SdbElf64Shdr>> {
214 self.section_map.get(name).cloned()
215 }
216
217 pub fn get_section_contents(&self, name: &str) -> Bytes {
218 if let Some(section) = self.get_section(name) {
219 return self.data.slice(
220 section.0.sh_offset as usize..(section.0.sh_offset + section.0.sh_size) as usize,
221 );
222 }
223 return Bytes::new();
224 }
225
226 fn build_section_map(&mut self) {
227 for section in &self.section_headers {
228 self.section_map.insert(
229 self.get_section_name(section.0.sh_name as usize)
230 .to_string(),
231 section.clone(),
232 );
233 }
234 }
235
236 pub fn get_string(&self, index: usize) -> &str {
237 let mut opt_strtab = self.get_section(".strtab");
238 if opt_strtab.is_none() {
239 opt_strtab = self.get_section(".dynstr");
240 if opt_strtab.is_none() {
241 return "";
242 }
243 }
244 cstr_view(&self.data[opt_strtab.unwrap().0.sh_offset as usize + index..])
245 }
246
247 pub fn load_bias(&self) -> VirtualAddress {
248 *self.load_bias.borrow()
249 }
250
251 pub fn notify_loaded(&self, address: VirtualAddress) {
252 *self.load_bias.borrow_mut() = address
253 }
254
255 pub fn get_section_containing_file_addr(
256 &self,
257 address: &FileAddress,
258 ) -> Option<Rc<SdbElf64Shdr>> {
259 if ptr::eq(self, &*address.rc_elf_file()) {
260 for section in &self.section_headers {
261 if section.0.sh_addr <= address.addr()
262 && (section.0.sh_addr + section.0.sh_size) > address.addr()
263 {
264 return Some(section.clone());
265 }
266 }
267 }
268
269 return None;
270 }
271 pub fn get_section_containing_virt_addr(
272 &self,
273 address: VirtualAddress,
274 ) -> Option<Rc<SdbElf64Shdr>> {
275 for section in &self.section_headers {
276 if (*self.load_bias.borrow() + section.0.sh_addr as i64) <= address
277 && (*self.load_bias.borrow() + section.0.sh_addr as i64 + section.0.sh_size as i64)
278 > address
279 {
280 return Some(section.clone());
281 }
282 }
283 return None;
284 }
285
286 fn parse_symbol_table(&mut self) {
287 let mut opt_symtab = self.get_section(".symtab");
288 if opt_symtab.is_none() {
289 opt_symtab = self.get_section(".dynsym");
290 if opt_symtab.is_none() {
291 return;
292 }
293 }
294 let symtab = opt_symtab.unwrap();
295 let symtab: Vec<SdbElf64Sym> = from_array_bytes(
296 &self.data[symtab.0.sh_offset as usize
297 ..symtab.0.sh_offset as usize + symtab.0.sh_size as usize],
298 );
299 self.symbol_table = symtab.into_iter().map(Rc::new).collect()
300 }
301
302 pub fn get_symbols_by_name(&self, name: &str) -> Vec<Rc<SdbElf64Sym>> {
303 self.symbol_name_map
304 .borrow()
305 .get_vec(name)
306 .map(|vec| vec.to_owned())
307 .unwrap_or_default()
308 }
309
310 pub fn get_symbol_at_file_address(&self, address: FileAddress) -> Option<Rc<SdbElf64Sym>> {
311 if !ptr::eq(self, address.rc_elf_file().as_ref()) {
312 return None;
313 }
314 self.symbol_addr_map
315 .borrow()
316 .get(&FileAddressRange(address, FileAddress::null()))
317 .cloned()
318 }
319
320 pub fn get_symbol_containing_file_address(
321 &self,
322 address: FileAddress,
323 ) -> Option<Rc<SdbElf64Sym>> {
324 if !ptr::eq(address.rc_elf_file().as_ref(), self) {
325 return None;
326 }
327 let borrow_map = self.symbol_addr_map.borrow();
328
329 if let Some((key, val)) = borrow_map
330 .range(FileAddressRange(address.clone(), FileAddress::null())..)
331 .next()
332 {
333 if key.0 == address {
334 return Some((*val).clone());
335 }
336 }
337 if let Some((key, val)) = borrow_map
338 .range(..FileAddressRange(address.clone(), FileAddress::null()))
339 .next_back()
340 {
341 if key.0 < address && key.1 > address {
342 return Some((*val).clone());
343 }
344 }
345 return None;
346 }
347
348 pub fn get_dwarf(&self) -> Rc<Dwarf> {
349 self.dwarf.get().unwrap().clone()
350 }
351}
352
353#[derive(Debug)]
354struct FileAddressRange(FileAddress, FileAddress);
355
356impl PartialEq for FileAddressRange {
357 fn eq(&self, other: &Self) -> bool {
358 self.0 == other.0 && self.1 == other.1
359 }
360}
361
362impl Eq for FileAddressRange {}
363
364impl PartialOrd for FileAddressRange {
365 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
366 Some(self.cmp(other))
367 }
368}
369
370impl Ord for FileAddressRange {
371 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
372 self.0.cmp(&other.0)
373 }
374}
375
376#[derive(Debug, Default)]
377pub struct ElfCollection {
378 elves: Vec<Rc<Elf>>,
379}
380
381impl ElfCollection {
382 pub fn push(&mut self, elf: Rc<Elf>) {
383 self.elves.push(elf);
384 }
385
386 pub fn iter(&self) -> std::slice::Iter<Rc<Elf>> {
387 self.elves.iter()
388 }
389
390 pub fn get_elf_containing_address(&self, address: VirtualAddress) -> Weak<Elf> {
391 for elf in &self.elves {
392 if elf.get_section_containing_virt_addr(address).is_some() {
393 return Rc::downgrade(elf);
394 }
395 }
396 Weak::new()
397 }
398
399 pub fn get_elf_by_path(&self, path: &Path) -> Weak<Elf> {
400 for elf in &self.elves {
401 if elf.path() == path {
402 return Rc::downgrade(elf);
403 }
404 }
405 Weak::new()
406 }
407
408 pub fn get_elf_by_filename(&self, filename: &str) -> Weak<Elf> {
409 for elf in &self.elves {
410 if elf.path().file_name().unwrap() == filename {
411 return Rc::downgrade(elf);
412 }
413 }
414 Weak::new()
415 }
416}