diff --git a/firmware/libdyld/src/file.rs b/firmware/libdyld/src/file.rs index 57e6a9aa..accebcc3 100644 --- a/firmware/libdyld/src/file.rs +++ b/firmware/libdyld/src/file.rs @@ -1,4 +1,4 @@ -use core::{mem, ptr, fmt, slice, str, convert, ops::{Deref, Range}}; +use core::{mem, ptr, ops::{Deref, Range}}; use super::{ Arch, elf::*, diff --git a/firmware/libdyld/src/image.rs b/firmware/libdyld/src/image.rs index 9d64c3bf..c7926f25 100644 --- a/firmware/libdyld/src/image.rs +++ b/firmware/libdyld/src/image.rs @@ -9,64 +9,15 @@ use super::{ Error, }; -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 DynamicSection<'a> { - pub strtab: &'a [u8], - pub symtab: &'a [Elf32_Sym], - pub hash_bucket: &'a [Elf32_Word], - pub hash_chain: &'a [Elf32_Word], - pub rel: &'a [Elf32_Rel], - pub rela: &'a [Elf32_Rela], - pub pltrel: &'a [Elf32_Rel], -} - -impl<'a> DynamicSection<'a> { - 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(sym.st_value), - _ => return Some(sym.st_value) - } - } - _ => (), - } - - index = self.hash_chain[index] as usize; - } - } - - pub fn name_starting_at(&self, offset: usize) -> Result<&'a [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 struct DynamicSection { + pub strtab: Range, + pub symtab: Range, + pub hash: Range, + pub hash_bucket: Range, + pub hash_chain: Range, + pub rel: Range, + pub rela: Range, + pub pltrel: Range, } /// target memory image @@ -90,7 +41,7 @@ impl Image { } /// assumes that self.data is properly aligned - pub fn get_ref(&self, offset: usize) -> Option<&T> + pub(crate) fn get_ref(&self, offset: usize) -> Option<&T> where T: Copy, { @@ -104,15 +55,15 @@ impl Image { } } - fn get_ref_slice(&self, offset: usize, len: usize) -> Option<&[T]> { - if self.data.len() < offset + mem::size_of::() * len { - None - } else if (self.data.as_ptr() as usize + offset) & (mem::align_of::() - 1) != 0 { - None - } else { - let ptr = self.data.as_ptr().wrapping_offset(offset as isize) as *const T; - Some(unsafe { slice::from_raw_parts(ptr, len) }) - } + /// assumes that self.data is properly aligned + /// + /// range: in bytes + pub(crate) fn get_ref_slice_unchecked(&self, range: &Range) -> &[T] { + let offset = range.start; + let len = (range.end - range.start) / mem::size_of::(); + + let ptr = self.data.as_ptr().wrapping_offset(offset as isize) as *const T; + unsafe { slice::from_raw_parts(ptr, len) } } fn dyn_headers<'a>(&'a self, range: Range) -> @@ -148,13 +99,13 @@ impl Image { DT_SYMTAB => symtab_off = val, DT_SYMENT => sym_ent = val, DT_REL => rel_off = val, - DT_RELSZ => rel_sz = val / mem::size_of::(), + DT_RELSZ => rel_sz = val, DT_RELENT => rel_ent = val, DT_RELA => rela_off = val, - DT_RELASZ => rela_sz = val / mem::size_of::(), + DT_RELASZ => rela_sz = val, DT_RELAENT => rela_ent = val, DT_JMPREL => pltrel_off = val, - DT_PLTRELSZ => pltrel_sz = val / mem::size_of::(), + DT_PLTRELSZ => pltrel_sz = val, DT_HASH => { nbucket = *self.get_ref::(val + 0) .ok_or("cannot read hash bucket count")? as usize; @@ -167,44 +118,44 @@ impl Image { } } + if strtab_off + strtab_sz > self.data.len() { + return Err("invalid strtab offset/size")? + } + if symtab_off + symtab_sz > self.data.len() { + return Err("invalid symtab offset/size")? + } if sym_ent != mem::size_of::() { return Err("incorrect symbol entry size")? } + if rel_off + rel_sz > self.data.len() { + return Err("invalid rel offset/size")? + } if rel_ent != 0 && rel_ent != mem::size_of::() { return Err("incorrect relocation entry size")? } + if rela_off + rela_sz > self.data.len() { + return Err("invalid rela offset/size")? + } if rela_ent != 0 && rela_ent != mem::size_of::() { return Err("incorrect relocation entry size")? } + if pltrel_off + pltrel_sz > self.data.len() { + return Err("invalid pltrel offset/size")? + } // These are the same--there are as many chains as buckets, and the chains only contain // the symbols that overflowed the bucket. symtab_sz = nchain; - let hash = self.get_ref_slice::(hash_off, hash_sz) - .ok_or("cannot read hash entries")?; - let strtab = self.get_ref_slice(strtab_off, strtab_sz) - .ok_or("cannot read string table")?; - let symtab = self.get_ref_slice::(symtab_off, symtab_sz) - .ok_or("cannot read symbol table")?; - let hash_bucket = &hash[..nbucket]; - let hash_chain = &hash[nbucket..nbucket + nchain]; - let rel = self.get_ref_slice::(rel_off, rel_sz) - .ok_or("cannot read rel entries")?; - let rela = self.get_ref_slice::(rela_off, rela_sz) - .ok_or("cannot read rela entries")?; - let pltrel = self.get_ref_slice::(pltrel_off, pltrel_sz) - .ok_or("cannot read pltrel entries")?; - // debug!("ELF: {} rela, {} rel, {} pltrel entries", rela_sz, rel_sz, pltrel_sz); - Ok(DynamicSection { - strtab, - symtab, - hash_bucket, - hash_chain, - rel, - rela, - pltrel, + strtab: strtab_off..strtab_off + strtab_sz, + symtab: symtab_off..symtab_off + symtab_sz, + hash: hash_off..hash_off + hash_sz, + hash_bucket: 0..nbucket, + hash_chain: nbucket..nbucket + nchain, + rel: rel_off..rel_off + rel_sz, + rela: rela_off..rela_off + rela_sz, + pltrel: pltrel_off..pltrel_off + rela_sz, }) } diff --git a/firmware/libdyld/src/lib.rs b/firmware/libdyld/src/lib.rs index 5a9f9c2e..9a1f456f 100644 --- a/firmware/libdyld/src/lib.rs +++ b/firmware/libdyld/src/lib.rs @@ -3,9 +3,9 @@ extern crate alloc; extern crate log; -use core::{mem, ptr, fmt, slice, str, convert, ops::Range}; +use core::{fmt, str, convert}; use alloc::string::String; -use log::{info, trace, error}; +use log::{info, trace}; use elf::*; pub mod elf; @@ -44,16 +44,90 @@ impl fmt::Display for Error { } } +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_range: Range, - dyn_section: DynamicSection<'static>, + dyn_section: DynamicSection, } impl Library { - pub fn lookup(&self, name: &[u8]) -> Option { - self.dyn_section.lookup(name) - .map(|addr| self.image.ptr() as u32 + addr) + 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")?) } } @@ -113,23 +187,20 @@ pub fn load( 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()); - - for rela in dyn_section.rela { - reloc::relocate(arch, &image, &dyn_section, rela, resolve)?; - } - for rel in dyn_section.rela { - reloc::relocate(arch, &image, &dyn_section, rel, resolve)?; - } - for pltrel in dyn_section.pltrel { - reloc::relocate(arch, &image, &dyn_section, pltrel, resolve)?; - } - - let dyn_section = unsafe { - core::mem::transmute(dyn_section) - }; - Ok(Library { + let lib = Library { image, - dyn_range, 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) } diff --git a/firmware/libdyld/src/reloc.rs b/firmware/libdyld/src/reloc.rs index c9a15e4a..a295d344 100644 --- a/firmware/libdyld/src/reloc.rs +++ b/firmware/libdyld/src/reloc.rs @@ -4,7 +4,8 @@ use super::{ Arch, elf::*, Error, - image::{DynamicSection, Image}, + image::Image, + Library, }; pub trait Relocatable { @@ -87,16 +88,16 @@ fn format_sym_name(sym_name: &[u8]) -> String { .unwrap_or(String::from("")) } -pub fn relocate<'a, R: Relocatable>( - arch: Arch, image: &'a Image, dynamic_section: &'a DynamicSection<'a>, - rel: &'a R, resolve: &dyn Fn(&[u8]) -> Option +pub fn relocate( + arch: Arch, lib: &Library, + rel: &R, resolve: &dyn Fn(&[u8]) -> Option ) -> Result<(), Error> { // debug!("rel r_offset={:08X} r_info={:08X} r_addend={:08X}", rel.offset(), rel.r_info, rela.r_addend); let sym; if rel.sym_info() == 0 { sym = None; } else { - sym = Some(dynamic_section.symtab.get(rel.sym_info() as usize) + sym = Some(lib.symtab().get(rel.sym_info() as usize) .ok_or("symbol out of bounds of symbol table")?) } @@ -108,18 +109,18 @@ pub fn relocate<'a, R: Relocatable>( return Ok(()), RelType::Relative => { - let addend = rel.addend(image); - value = image.ptr().wrapping_offset(addend as isize) as Elf32_Word; + let addend = rel.addend(&lib.image); + value = lib.image.ptr().wrapping_offset(addend as isize) as Elf32_Word; } RelType::Lookup => { let sym = sym.ok_or("relocation requires an associated symbol")?; - let sym_name = dynamic_section.name_starting_at(sym.st_name as usize)?; + let sym_name = lib.name_starting_at(sym.st_name as usize)?; - if let Some(addr) = dynamic_section.lookup(sym_name) { + if let Some(addr) = lib.lookup(sym_name) { // First, try to resolve against itself. trace!("looked up symbol {} in image", format_sym_name(sym_name)); - value = image.ptr() as u32 + addr; + value = lib.image.ptr() as u32 + addr; } else if let Some(addr) = resolve(sym_name) { // Second, call the user-provided function. trace!("resolved symbol {:?}", format_sym_name(sym_name)); @@ -132,5 +133,5 @@ pub fn relocate<'a, R: Relocatable>( } debug!("rel_type={:?} write at {:08X} value {:08X}", rel_type, rel.offset(), value); - image.write(rel.offset(), value) + lib.image.write(rel.offset(), value) }