libsdb/
elf.rs

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; // drop
156
157        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}