1#[derive(Debug)]
32pub enum ElfError {
33 TooShort,
35 BadMagic,
37 Not32Bit,
39 WrongEndian,
41 NotRiscV,
43 InvalidProgramHeader,
45 InvalidSectionHeader,
47}
48
49impl std::fmt::Display for ElfError {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 match self {
52 ElfError::TooShort => write!(f, "file too short to be ELF"),
53 ElfError::BadMagic => write!(f, "bad ELF magic bytes"),
54 ElfError::Not32Bit => write!(f, "not a 32-bit ELF (EI_CLASS != 1)"),
55 ElfError::WrongEndian => write!(f, "not a little-endian ELF (EI_DATA != 1)"),
56 ElfError::NotRiscV => write!(f, "not a RISC-V ELF (e_machine != 243)"),
57 ElfError::InvalidProgramHeader => write!(f, "invalid or out-of-bounds program header"),
58 ElfError::InvalidSectionHeader => write!(f, "invalid or out-of-bounds section header"),
59 }
60 }
61}
62
63impl std::error::Error for ElfError {}
64
65const EM_RISCV: u16 = 243;
68const PT_LOAD: u32 = 1;
69const SHT_SYMTAB: u32 = 2; const STT_OBJECT: u8 = 1; const STT_FUNC: u8 = 2; const STB_LOCAL: u8 = 0; const STB_GLOBAL: u8 = 1; const STB_WEAK: u8 = 2; #[repr(C)]
81struct Elf32Ehdr {
82 e_ident: [u8; 16],
83 e_type: u16,
84 e_machine: u16,
85 e_version: u32,
86 e_entry: u32,
87 e_phoff: u32, e_shoff: u32, _e_flags: u32,
90 _e_ehsize: u16,
91 e_phentsize: u16,
92 e_phnum: u16,
93 e_shentsize: u16,
94 e_shnum: u16,
95 e_shstrndx: u16, }
97
98#[repr(C)]
99struct Elf32Phdr {
100 p_type: u32,
101 p_offset: u32,
102 p_vaddr: u32,
103 _p_paddr: u32,
104 p_filesz: u32,
105 p_memsz: u32,
106 _p_flags: u32,
107 _p_align: u32,
108}
109
110#[repr(C)]
111struct Elf32Shdr {
112 sh_name: u32, sh_type: u32,
114 _sh_flags: u32,
115 _sh_addr: u32,
116 sh_offset: u32, sh_size: u32,
118 sh_link: u32, _sh_info: u32,
120 _sh_addralign: u32,
121 sh_entsize: u32, }
123
124#[repr(C)]
126struct Elf32Sym {
127 st_name: u32, st_value: u32, st_size: u32, st_info: u8, _st_other: u8,
132 _st_shndx: u16,
133}
134
135impl Elf32Sym {
136 fn stt(&self) -> u8 {
137 self.st_info & 0xf
138 } fn stb(&self) -> u8 {
140 (self.st_info >> 4) & 0xf
141 } }
143
144#[derive(Debug, Clone)]
148pub struct ElfSegment {
149 pub vaddr: u32,
151 pub data: Vec<u8>,
153 pub mem_size: u32,
155}
156
157#[derive(Debug, Clone)]
159pub struct ElfImage {
160 pub entry: u32,
162 pub segments: Vec<ElfSegment>,
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub enum SymbolKind {
171 Func,
173 Object,
175}
176
177#[derive(Debug, Clone)]
179pub struct Symbol {
180 pub name: String,
182 pub addr: u32,
184 pub size: u32,
186 pub kind: SymbolKind,
188}
189
190#[derive(Debug, Default)]
200pub struct SymbolTable {
201 by_addr: Vec<Symbol>,
202 by_name: Vec<(String, usize)>, }
204
205impl SymbolTable {
206 pub fn is_empty(&self) -> bool {
208 self.by_addr.is_empty()
209 }
210
211 pub fn len(&self) -> usize {
213 self.by_addr.len()
214 }
215
216 pub fn lookup_addr(&self, addr: u32) -> Option<&Symbol> {
222 let count = self.by_addr.partition_point(|s| s.addr <= addr);
225 if count == 0 {
226 return None;
227 }
228
229 let sym = &self.by_addr[count - 1];
230
231 if sym.addr == addr {
232 return Some(sym); }
234
235 if sym.size > 0 && addr < sym.addr.saturating_add(sym.size) {
237 Some(sym)
238 } else {
239 None
240 }
241 }
242
243 pub fn lookup_name(&self, name: &str) -> Option<&Symbol> {
247 let idx = self
248 .by_name
249 .binary_search_by_key(&name, |(n, _)| n.as_str())
250 .ok()?;
251 let sym_idx = self.by_name[idx].1;
252 Some(&self.by_addr[sym_idx])
253 }
254
255 pub fn iter(&self) -> impl Iterator<Item = &Symbol> {
257 self.by_addr.iter()
258 }
259
260 fn build(mut symbols: Vec<Symbol>) -> Self {
261 symbols.sort_by_key(|s| s.addr);
263
264 let mut by_name: Vec<(String, usize)> = symbols
266 .iter()
267 .enumerate()
268 .map(|(i, s)| (s.name.clone(), i))
269 .collect();
270 by_name.sort_by(|a, b| a.0.cmp(&b.0));
271
272 SymbolTable {
273 by_addr: symbols,
274 by_name,
275 }
276 }
277}
278
279unsafe fn read_slice<T>(data: &[u8], offset: usize, count: usize) -> &[T] {
288 std::slice::from_raw_parts(data[offset..].as_ptr() as *const T, count)
289}
290
291fn read_cstr(strtab: &[u8], offset: usize) -> &str {
293 let slice = &strtab[offset..];
294 let len = slice.iter().position(|&b| b == 0).unwrap_or(slice.len());
295 std::str::from_utf8(&slice[..len]).unwrap_or("<invalid utf8>")
296}
297
298pub fn parse_elf(data: &[u8]) -> Result<ElfImage, ElfError> {
304 let hdr = parse_header(data)?;
305
306 let segments = parse_segments(data, hdr)?;
307
308 Ok(ElfImage {
309 entry: hdr.e_entry,
310 segments,
311 })
312}
313
314pub fn parse_symbol_table(data: &[u8]) -> Result<Option<SymbolTable>, ElfError> {
323 let hdr = parse_header(data)?;
324
325 let shoff = hdr.e_shoff as usize;
326 let shnum = hdr.e_shnum as usize;
327 let shentsize = hdr.e_shentsize as usize;
328
329 if shoff == 0 || shnum == 0 {
331 return Ok(None);
332 }
333
334 let sh_end = shoff
335 .checked_add(
336 shnum
337 .checked_mul(shentsize)
338 .ok_or(ElfError::InvalidSectionHeader)?,
339 )
340 .ok_or(ElfError::InvalidSectionHeader)?;
341
342 if sh_end > data.len() {
343 return Err(ElfError::InvalidSectionHeader);
344 }
345
346 let shdrs: &[Elf32Shdr] = unsafe { read_slice(data, shoff, shnum) };
348
349 let symtab_shdr = shdrs.iter().find(|s| s.sh_type == SHT_SYMTAB);
351 let symtab_shdr = match symtab_shdr {
352 Some(s) => s,
353 None => return Ok(None), };
355
356 let strtab_idx = symtab_shdr.sh_link as usize;
358 if strtab_idx >= shnum {
359 return Err(ElfError::InvalidSectionHeader);
360 }
361 let strtab_shdr = &shdrs[strtab_idx];
362
363 let sym_offset = symtab_shdr.sh_offset as usize;
365 let sym_size = symtab_shdr.sh_size as usize;
366 let sym_entsize = symtab_shdr.sh_entsize as usize;
367
368 if sym_entsize == 0 {
369 return Err(ElfError::InvalidSectionHeader);
370 }
371
372 let sym_end = sym_offset
373 .checked_add(sym_size)
374 .ok_or(ElfError::InvalidSectionHeader)?;
375 if sym_end > data.len() {
376 return Err(ElfError::InvalidSectionHeader);
377 }
378
379 let str_offset = strtab_shdr.sh_offset as usize;
380 let str_end = str_offset
381 .checked_add(strtab_shdr.sh_size as usize)
382 .ok_or(ElfError::InvalidSectionHeader)?;
383 if str_end > data.len() {
384 return Err(ElfError::InvalidSectionHeader);
385 }
386
387 let strtab = &data[str_offset..str_end];
388
389 let sym_count = sym_size / sym_entsize;
390
391 let raw_syms: &[Elf32Sym] = unsafe { read_slice(data, sym_offset, sym_count) };
393
394 let mut symbols = Vec::new();
395 for sym in raw_syms {
396 let kind = match sym.stt() {
398 STT_FUNC => SymbolKind::Func,
399 STT_OBJECT => SymbolKind::Object,
400 _ => continue,
401 };
402
403 match sym.stb() {
406 STB_GLOBAL | STB_WEAK | STB_LOCAL => {}
407 _ => continue,
408 }
409
410 if sym.st_value == 0 {
412 continue;
413 }
414
415 let name_offset = sym.st_name as usize;
416 if name_offset >= strtab.len() {
417 continue;
418 }
419
420 let name = read_cstr(strtab, name_offset).to_owned();
421 if name.is_empty() {
422 continue;
423 }
424
425 if name.starts_with('.') || name.starts_with('$') {
429 continue;
430 }
431
432 symbols.push(Symbol {
433 name,
434 addr: sym.st_value,
435 size: sym.st_size,
436 kind,
437 });
438 }
439
440 if symbols.is_empty() {
441 return Ok(None);
442 }
443
444 Ok(Some(SymbolTable::build(symbols)))
445}
446
447fn parse_header(data: &[u8]) -> Result<&Elf32Ehdr, ElfError> {
451 if data.len() < std::mem::size_of::<Elf32Ehdr>() {
452 return Err(ElfError::TooShort);
453 }
454
455 let hdr = unsafe { &*(data.as_ptr() as *const Elf32Ehdr) };
457
458 if &hdr.e_ident[0..4] != b"\x7fELF" {
459 return Err(ElfError::BadMagic);
460 }
461 if hdr.e_ident[4] != 1 {
462 return Err(ElfError::Not32Bit);
463 }
464 if hdr.e_ident[5] != 1 {
465 return Err(ElfError::WrongEndian);
466 }
467 if hdr.e_machine != EM_RISCV {
468 return Err(ElfError::NotRiscV);
469 }
470
471 Ok(hdr)
472}
473
474fn parse_segments(data: &[u8], hdr: &Elf32Ehdr) -> Result<Vec<ElfSegment>, ElfError> {
476 let phoff = hdr.e_phoff as usize;
477 let phnum = hdr.e_phnum as usize;
478 let phentsize = hdr.e_phentsize as usize;
479
480 let ph_end = phoff
481 .checked_add(
482 phnum
483 .checked_mul(phentsize)
484 .ok_or(ElfError::InvalidProgramHeader)?,
485 )
486 .ok_or(ElfError::InvalidProgramHeader)?;
487
488 if ph_end > data.len() {
489 return Err(ElfError::InvalidProgramHeader);
490 }
491
492 let phdrs: &[Elf32Phdr] = unsafe { read_slice(data, phoff, phnum) };
494
495 let mut segments = Vec::new();
496 for ph in phdrs {
497 if ph.p_type != PT_LOAD {
498 continue;
499 }
500
501 let start = ph.p_offset as usize;
502 let end = start
503 .checked_add(ph.p_filesz as usize)
504 .ok_or(ElfError::InvalidProgramHeader)?;
505
506 if end > data.len() {
507 return Err(ElfError::InvalidProgramHeader);
508 }
509
510 segments.push(ElfSegment {
511 vaddr: ph.p_vaddr,
512 data: data[start..end].to_vec(),
513 mem_size: ph.p_memsz,
514 });
515 }
516
517 Ok(segments)
518}