#![no_std] extern crate alloc; extern crate log; use core::{fmt, str, convert}; use alloc::string::String; use log::{info, trace}; use elf::*; pub mod elf; mod file; mod image; use image::{DynamicSection, Image}; mod reloc; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Arch { Arm, OpenRisc, } #[derive(Debug)] pub enum Error { Parsing(&'static str), Lookup(String) } impl convert::From<&'static str> for Error { fn from(desc: &'static str) -> Error { Error::Parsing(desc) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &Error::Parsing(desc) => write!(f, "parse error: {}", desc), &Error::Lookup(ref sym) => write!(f, "symbol lookup error: {}", sym), } } } fn elf_hash(name: &[u8]) -> u32 { let mut h: u32 = 0; for c in name { h = (h << 4) + *c as u32; let g = h & 0xf0000000; if g != 0 { h ^= g >> 24; h &= !g; } } h } pub struct Library { image: Image, dyn_section: DynamicSection, } impl Library { fn strtab(&self) -> &[u8] { self.image.get_ref_slice_unchecked(&self.dyn_section.strtab) } fn symtab(&self) -> &[Elf32_Sym] { self.image.get_ref_slice_unchecked(&self.dyn_section.symtab) } fn hash(&self) -> &[Elf32_Word] { self.image.get_ref_slice_unchecked(&self.dyn_section.hash) } fn hash_bucket(&self) -> &[Elf32_Word] { &self.hash()[self.dyn_section.hash_bucket.clone()] } fn hash_chain(&self) -> &[Elf32_Word] { &self.hash()[self.dyn_section.hash_chain.clone()] } fn rel(&self) -> &[Elf32_Rel] { self.image.get_ref_slice_unchecked(&self.dyn_section.rel) } fn rela(&self) -> &[Elf32_Rela] { self.image.get_ref_slice_unchecked(&self.dyn_section.rela) } fn pltrel(&self) -> &[Elf32_Rel] { self.image.get_ref_slice_unchecked(&self.dyn_section.pltrel) } pub fn lookup(&self, name: &[u8]) -> Option { let hash = elf_hash(name); let mut index = self.hash_bucket()[hash as usize % self.hash_bucket().len()] as usize; loop { if index == STN_UNDEF { return None } let sym = &self.symtab()[index]; let sym_name_off = sym.st_name as usize; match self.strtab().get(sym_name_off..sym_name_off + name.len()) { Some(sym_name) if sym_name == name => { if ELF32_ST_BIND(sym.st_info) & STB_GLOBAL == 0 { return None } match sym.st_shndx { SHN_UNDEF => return None, SHN_ABS => return Some(self.image.ptr() as u32 + sym.st_value), _ => return Some(self.image.ptr() as u32 + sym.st_value) } } _ => (), } index = self.hash_chain()[index] as usize; } } pub fn name_starting_at(&self, offset: usize) -> Result<&[u8], Error> { let size = self.strtab().iter().skip(offset).position(|&x| x == 0) .ok_or("symbol in symbol table not null-terminated")?; Ok(self.strtab().get(offset..offset + size) .ok_or("cannot read symbol name")?) } } pub fn load( data: &[u8], resolve: &dyn Fn(&[u8]) -> Option ) -> Result { // validate ELF file let file = file::File::new(data) .ok_or("cannot read ELF header")?; if file.ehdr.e_type != ET_DYN { return Err("not a shared library")? } let arch = file.arch() .ok_or("not for a supported architecture")?; // prepare target memory let image_size = file.program_headers() .filter_map(|phdr| phdr.map(|phdr| phdr.p_vaddr + phdr.p_memsz)) .max() .unwrap_or(0) as usize; let image_align = file.program_headers() .filter_map(|phdr| phdr.and_then(|phdr| { if phdr.p_type == PT_LOAD { Some(phdr.p_align) } else { None } })) .max() .unwrap_or(4) as usize; // 1 image for all segments let mut image = image::Image::new(image_size, image_align) .map_err(|_| "cannot allocate target image")?; info!("ELF target: {} bytes, align to {:X}, allocated at {:08X}", image_size, image_align, image.ptr() as usize); // LOAD for phdr in file.program_headers() { let phdr = phdr.ok_or("cannot read program header")?; if phdr.p_type != PT_LOAD { continue; } trace!("Program header: {:08X}+{:08X} to {:08X}", phdr.p_offset, phdr.p_filesz, image.ptr() as u32 ); let src = file.get(phdr.p_offset as usize..(phdr.p_offset + phdr.p_filesz) as usize) .ok_or("program header requests an out of bounds load (in file)")?; let dst = image.get_mut(phdr.p_vaddr as usize.. (phdr.p_vaddr + phdr.p_filesz) as usize) .ok_or("program header requests an out of bounds load (in target)")?; dst.copy_from_slice(src); } // relocate DYNAMIC let dyn_range = file.dyn_header_vaddr() .ok_or("cannot find a dynamic header")?; let dyn_section = image.dyn_section(dyn_range.clone())?; info!("Relocating {} rela, {} rel, {} pltrel", dyn_section.rela.len(), dyn_section.rel.len(), dyn_section.pltrel.len()); let lib = Library { image, dyn_section, }; for rela in lib.rela() { reloc::relocate(arch, &lib, rela, resolve)?; } for rel in lib.rel() { reloc::relocate(arch, &lib, rel, resolve)?; } for pltrel in lib.pltrel() { reloc::relocate(arch, &lib, pltrel, resolve)?; } Ok(lib) }