aboutsummaryrefslogtreecommitdiff
path: root/src/rust-elfloader/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/rust-elfloader/src/lib.rs')
-rw-r--r--src/rust-elfloader/src/lib.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/rust-elfloader/src/lib.rs b/src/rust-elfloader/src/lib.rs
new file mode 100644
index 0000000..e344bcb
--- /dev/null
+++ b/src/rust-elfloader/src/lib.rs
@@ -0,0 +1,177 @@
+#![feature(no_std, core, core_prelude, core_slice_ext, custom_derive)]
+#![no_std]
+
+#![crate_name = "elfloader"]
+#![crate_type = "lib"]
+
+#[cfg(test)]
+#[macro_use]
+extern crate std;
+
+pub mod elf;
+use core::fmt;
+use core::mem::{transmute, size_of};
+
+pub type PAddr = u64;
+pub type VAddr = usize;
+
+/// Abstract representation of a loadable ELF binary.
+pub struct ElfBinary<'s> {
+ name: &'s str,
+ region: &'s [u8],
+ header: &'s elf::FileHeader,
+}
+
+impl<'s> fmt::Debug for ElfBinary<'s> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} {}", self.name, self.header)
+ }
+}
+
+/// Implement this for ELF loading.
+pub trait ElfLoader {
+ /// Allocates a virtual region of size amount of bytes.
+ fn allocate(&mut self, base: VAddr, size: usize, flags: elf::ProgFlag);
+
+ /// Copies the region into the base.
+ fn load(&mut self, base: VAddr, region: &'static [u8]);
+}
+
+// T must be a POD for this to be safe
+unsafe fn slice_pod<T>(region: &[u8], offset: usize, count: usize) -> &[T] {
+ assert!(region.len() - offset >= count * size_of::<T>());
+ core::slice::from_raw_parts(region[offset..].as_ptr() as *const T, count)
+}
+
+impl<'s> ElfBinary<'s> {
+
+ /// Create a new ElfBinary.
+ /// Makes sure that the provided region has valid ELF magic byte sequence
+ /// and is big enough to contain at least the ELF file header
+ /// otherwise it will return None.
+ pub fn new(name: &'s str, region: &'s [u8]) -> Option<ElfBinary<'s>> {
+ if region.len() >= size_of::<elf::FileHeader>() && region.starts_with(elf::ELF_MAGIC) {
+ let header: &elf::FileHeader = unsafe { &slice_pod(region, 0, 1)[0] };
+ return Some(ElfBinary { name: name, region: region, header: header });
+ }
+
+ None
+ }
+
+ /// Print the program headers.
+ pub fn print_program_headers(&self) {
+ for p in self.program_headers() {
+ //log!("pheader = {}", p);
+ }
+ }
+
+ /// Create a slice of the program headers.
+ pub fn program_headers(&self) -> &'s [elf::ProgramHeader] {
+ let correct_header_size = self.header.phentsize as usize == size_of::<elf::ProgramHeader>();
+ let pheader_region_size = self.header.phoff as usize + self.header.phnum as usize * self.header.phentsize as usize;
+ let big_enough_region = self.region.len() >= pheader_region_size;
+
+ if self.header.phoff == 0 || !correct_header_size || !big_enough_region {
+ return &[];
+ }
+
+ unsafe {
+ slice_pod(self.region, self.header.phoff as usize, self.header.phnum as usize)
+ }
+ }
+
+ // Get the string at offset str_offset in the string table strtab
+ fn strtab_str(&self, strtab: &'s elf::SectionHeader, str_offset: elf::StrOffset) -> &'s str {
+ assert!(strtab.shtype == elf::SHT_STRTAB);
+ let data = self.section_data(strtab);
+ let offset = str_offset.0 as usize;
+ let mut end = offset;
+ while data[end] != 0 {
+ end += 1;
+ }
+ core::str::from_utf8(&data[offset..end]).unwrap()
+ }
+
+ // Get the name of the section
+ pub fn symbol_name(&self, symbol: &'s elf::Symbol) -> &'s str {
+ let strtab = self.section_headers().iter().find(|s| s.shtype == elf::SHT_STRTAB && self.section_name(s) == ".strtab").unwrap();
+ self.strtab_str(strtab, symbol.name)
+ }
+
+ // Get the data of the section
+ pub fn section_data(&self, section: &'s elf::SectionHeader) -> &'s [u8] {
+ &self.region[(section.offset as usize)..(section.offset as usize + section.size as usize)]
+ }
+
+ // Get the name of the section
+ pub fn section_name(&self, section: &'s elf::SectionHeader) -> &'s str {
+ self.strtab_str(&self.section_headers()[self.header.shstrndx as usize], section.name)
+ }
+
+ // Get the symbols of the section
+ fn section_symbols(&self, section: &'s elf::SectionHeader) -> &'s [elf::Symbol] {
+ assert!(section.shtype == elf::SHT_SYMTAB);
+ unsafe {
+ slice_pod(self.section_data(section), 0, section.size as usize / size_of::<elf::Symbol>())
+ }
+ }
+
+ // Enumerate all the symbols in the file
+ pub fn for_each_symbol<F: FnMut(&'s elf::Symbol)>(&self, mut func: F) {
+ for sym in self.section_headers().iter().filter(|s| s.shtype == elf::SHT_SYMTAB).flat_map(|s| self.section_symbols(s).iter()) {
+ func(sym);
+ }
+ }
+
+ /// Create a slice of the section headers.
+ pub fn section_headers(&self) -> &'s [elf::SectionHeader] {
+ let correct_header_size = self.header.shentsize as usize == size_of::<elf::SectionHeader>();
+ let sheader_region_size = self.header.shoff as usize + self.header.shnum as usize * self.header.shentsize as usize;
+ let big_enough_region = self.region.len() >= sheader_region_size;
+
+ if self.header.shoff == 0 || !correct_header_size || !big_enough_region {
+ return &[];
+ }
+
+ unsafe {
+ slice_pod(self.region, self.header.shoff as usize, self.header.shnum as usize)
+ }
+ }
+
+ /// Can we load the binary on our platform?
+ fn can_load(&self) -> bool {
+ let correct_class = self.header.ident.class == elf::ELFCLASS64;
+ let correct_elfversion = self.header.ident.version == elf::EV_CURRENT;
+ let correct_data = self.header.ident.data == elf::ELFDATA2LSB;
+ let correct_osabi = self.header.ident.osabi == elf::ELFOSABI_SYSV || self.header.ident.osabi == elf::ELFOSABI_LINUX;
+ let correct_type = self.header.elftype == elf::ET_EXEC || self.header.elftype == elf::ET_DYN;
+ let correct_machine = self.header.machine == elf::EM_X86_64;
+
+ correct_class && correct_data && correct_elfversion && correct_machine && correct_osabi && correct_type
+ }
+
+ fn load_header(&self, p: &elf::ProgramHeader, loader: &mut ElfLoader) {
+ let big_enough_region = self.region.len() >= (p.offset + p.filesz) as usize;
+ if !big_enough_region {
+ //log!("Unable to load {}", p);
+ return;
+ }
+
+ loader.allocate(p.vaddr as usize, p.memsz as usize, p.flags);
+ let segment: &'static [u8] = unsafe {
+ core::slice::from_raw_parts(
+ transmute(&self.region[p.offset as usize]), p.filesz as usize)
+ };
+ loader.load(p.vaddr as usize, segment);
+ }
+
+ pub fn load(&self, loader: &mut ElfLoader) {
+ for p in self.program_headers() {
+ let x = match p.progtype {
+ elf::PT_LOAD => self.load_header(p, loader),
+ _ => ()
+ };
+ }
+ }
+
+}