elf_rs-0.3.1/.cargo_vcs_info.json0000644000000001360000000000100123070ustar { "git": { "sha1": "9f77869e910366977530c9a3d03a1d138476c8df" }, "path_in_vcs": "" }elf_rs-0.3.1/.gitignore000064400000000000000000000000361046102023000130660ustar 00000000000000/target **/*.rs.bk Cargo.lock elf_rs-0.3.1/.travis.yml000064400000000000000000000001431046102023000132060ustar 00000000000000language: rust rust: - stable - beta - nightly matrix: allow_failures: - rust: nightly elf_rs-0.3.1/Cargo.lock0000644000000014460000000000100102670ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "elf_rs" version = "0.3.1" dependencies = [ "bitflags", "num-traits", ] [[package]] name = "num-traits" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] elf_rs-0.3.1/Cargo.toml0000644000000016720000000000100103130ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "elf_rs" version = "0.3.1" authors = ["Vincent Hou "] description = "A simple no_std ELF file reader for ELF32 and ELF64" readme = "README.md" categories = ["no-std"] license-file = "LICENSE" repository = "https://github.com/vincenthouyi/elf_rs" [dependencies.bitflags] version = "2.4" [dependencies.num-traits] version = "0.2" default-features = false [badges.travis-ci] branch = "master" repository = "vincenthouyi/elf_rs" elf_rs-0.3.1/Cargo.toml.orig0000644000000007350000000000100112510ustar [package] name = "elf_rs" version = "0.3.1" authors = ["Vincent Hou "] edition = "2018" description = "A simple no_std ELF file reader for ELF32 and ELF64" readme = "README.md" categories = ["no-std"] repository = "https://github.com/vincenthouyi/elf_rs" license-file = "LICENSE" [badges] travis-ci = { repository = "vincenthouyi/elf_rs", branch = "master" } [dependencies] bitflags = "2.4" num-traits = { version = "0.2", default-features = false } elf_rs-0.3.1/Cargo.toml.orig000064400000000000000000000007351046102023000137730ustar 00000000000000[package] name = "elf_rs" version = "0.3.1" authors = ["Vincent Hou "] edition = "2018" description = "A simple no_std ELF file reader for ELF32 and ELF64" readme = "README.md" categories = ["no-std"] repository = "https://github.com/vincenthouyi/elf_rs" license-file = "LICENSE" [badges] travis-ci = { repository = "vincenthouyi/elf_rs", branch = "master" } [dependencies] bitflags = "2.4" num-traits = { version = "0.2", default-features = false } elf_rs-0.3.1/LICENSE000064400000000000000000000020671046102023000121110ustar 00000000000000The MIT License (MIT) Copyright (c) 2019-2020 Yi Hou Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. elf_rs-0.3.1/README.md000064400000000000000000000024241046102023000123600ustar 00000000000000elf_rs === [![Build Status](https://app.travis-ci.com/vincenthouyi/elf_rs.svg?branch=master)](https://app.travis-ci.com/vincenthouyi/elf_rs) [![Crates.io](https://img.shields.io/crates/v/elf_rs)](https://crates.io/crates/elf_rs) This is a no_std library for ELF file handling. It supports ELF32 and ELF64 format. Usage === To read an elf file, supply `elf_rs::Elf` with a `&[u8]` memory: ```rust extern crate elf_rs; use std::io::Read; use std::fs::File; use std::env; use elf_rs::*; fn read_elf(filename: &String) { let mut elf_file = File::open(filename).expect("open file failed"); let mut elf_buf = Vec::::new(); elf_file.read_to_end(&mut elf_buf).expect("read file failed"); let elf = Elf::from_bytes(&elf_buf).expect("load elf file failed"); println!("{:?} header: {:?}", elf, elf.elf_header()); for p in elf.program_header_iter() { println!("{:x?}", p); } for s in elf.section_header_iter() { println!("{:x?}", s); } let s = elf.lookup_section(b".text"); println!("s {:?}", s); } ``` Under example directory there is a demo `readelf` to read an ELF file. ``` $ cargo run --example readelf ``` License === it is distributed under the terms of the MIT license. Please see LICENSE file for details. elf_rs-0.3.1/examples/readelf.rs000064400000000000000000000022621046102023000146670ustar 00000000000000extern crate elf_rs; use std::env; use std::fs::File; use std::io::Read; use elf_rs::*; fn read_elf(filename: &str) -> Result<(), ()> { let mut elf_file = File::open(filename).map_err(|e| { println!("failed to open file {}: {}", filename, e); () })?; let mut elf_buf = Vec::::new(); elf_file.read_to_end(&mut elf_buf).map_err(|e| { println!("failed to read file {}: {}", filename, e); () })?; let elf = Elf::from_bytes(&mut elf_buf).map_err(|e| { println!("failed to extract elf file {}: {:?}", filename, e); () })?; println!("{:#x?}", elf); println!("{:#?}", elf.elf_header()); for p in elf.program_header_iter() { println!("{:#x?}", p); } for s in elf.section_header_iter() { println!("{:#x?}", s); } if let Some(s) = elf.lookup_section(b".text") { println!(".test section: {:#x?}", s); } Ok(()) } fn main() -> Result<(), ()> { let args: Vec = env::args().collect(); if args.len() < 2 { println!("Need specify file path!"); return Err(()); } let filename = &args[1]; read_elf(&filename)?; Ok(()) } elf_rs-0.3.1/src/elf/elf.rs000064400000000000000000000107211046102023000135510ustar 00000000000000use super::{ ElfFile, ElfHeader, ElfType, ProgramHeaderEntry, ProgramHeaderIter, SectionHeaderEntry, SectionHeaderIter, }; use crate::elf_header::{ElfClass, ElfHeader32, ElfHeader64, ELF_MAGIC}; use crate::program_header::{ProgramHeader32, ProgramHeader64}; use crate::section_header::{SectionHeader32, SectionHeader64}; use crate::Error; use core::fmt; use core::marker::PhantomData; use core::mem::size_of; use core::slice::from_raw_parts; #[derive(Debug)] pub enum ElfType64 {} impl ElfType for ElfType64 { type ElfHeader = ElfHeader64; type ProgramHeader = ProgramHeader64; type SectionHeader = SectionHeader64; fn elf_class() -> ElfClass { ElfClass::Elf64 } } #[derive(Debug)] pub enum ElfType32 {} impl ElfType for ElfType32 { type ElfHeader = ElfHeader32; type ProgramHeader = ProgramHeader32; type SectionHeader = SectionHeader32; fn elf_class() -> ElfClass { ElfClass::Elf32 } } pub struct Elf<'a, ET>(&'a [u8], PhantomData); impl<'a, ET: ElfType> Elf<'a, ET> { pub(crate) fn new(buf: &'a [u8]) -> Self { Self(buf, PhantomData) } pub fn from_bytes(buf: &'a [u8]) -> Result { if !buf.starts_with(&ELF_MAGIC) { return Err(Error::InvalidMagic); } if buf.len() < size_of::() { return Err(Error::BufferTooShort); } let elf = Self::new(buf); if buf.len() < elf.elf_header().elf_header_size() as usize { return Err(Error::BufferTooShort); } if elf.elf_header().class() != ET::elf_class() { return Err(Error::InvalidClass); } Ok(elf) } pub fn content(&self) -> &[u8] { self.0 } pub fn elf_header_raw(&self) -> &ET::ElfHeader { unsafe { &*(self.content().as_ptr() as *const ET::ElfHeader) } } pub fn program_headers_raw(&self) -> Option<&'a [ET::ProgramHeader]> { let ph_off = self.elf_header().program_header_offset() as usize; let ph_num = self.elf_header().program_header_entry_num() as usize; let ph_top = ph_off.saturating_add(ph_num.saturating_mul(size_of::())); self.content() .get(ph_off..ph_top) .map(|mem| unsafe { from_raw_parts(mem.as_ptr() as *const ET::ProgramHeader, ph_num) }) } pub fn program_header_iter(&self) -> ProgramHeaderIter { ProgramHeaderIter::new(self) } pub fn program_header_nth(&self, index: usize) -> Option { self.program_headers_raw() .and_then(|s| s.get(index)) .map(|ph| ProgramHeaderEntry::new(self, ph)) } pub fn section_headers_raw(&'a self) -> Option<&'a [ET::SectionHeader]> { let sh_off = self.elf_header().section_header_offset() as usize; let sh_num = self.elf_header().section_header_entry_num() as usize; let sh_top = sh_off.saturating_add(sh_num.saturating_mul(size_of::())); self.content() .get(sh_off..sh_top) .map(|mem| unsafe { from_raw_parts(mem.as_ptr() as *const ET::SectionHeader, sh_num) }) } pub fn section_header_iter(&self) -> SectionHeaderIter { SectionHeaderIter::new(self) } pub fn section_header_nth(&self, index: usize) -> Option { self.section_headers_raw() .and_then(|s| s.get(index)) .map(|sh| SectionHeaderEntry::new(self, sh)) } } impl<'a, ET: ElfType> ElfFile for Elf<'a, ET> { fn content(&self) -> &[u8] { self.content() } fn elf_header(&self) -> ElfHeader { ElfHeader::new(self, self.elf_header_raw()) } fn program_header_nth(&self, index: usize) -> Option { self.program_header_nth(index) } fn program_header_iter(&self) -> ProgramHeaderIter { self.program_header_iter() } fn section_header_iter(&self) -> SectionHeaderIter { self.section_header_iter() } fn section_header_nth(&self, index: usize) -> Option { self.section_header_nth(index) } } pub type Elf32<'a> = Elf<'a, ElfType32>; pub type Elf64<'a> = Elf<'a, ElfType64>; impl<'a, ET: ElfType> fmt::Debug for Elf<'a, ET> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("ELF Buffer") .field("Memory Location", &self.content().as_ptr()) .field("Buffer Size", &self.content().len()) .finish() } } elf_rs-0.3.1/src/elf/elf_header.rs000064400000000000000000000033731046102023000150660ustar 00000000000000use super::ElfFile; use crate::elf_header::ElfHeaderRaw; use core::fmt; use core::ops; pub struct ElfHeader<'a> { _elf_file: &'a dyn ElfFile, inner: &'a dyn ElfHeaderRaw, } impl<'a> ElfHeader<'a> { pub fn new(elf_file: &'a dyn ElfFile, inner: &'a dyn ElfHeaderRaw) -> Self { Self { _elf_file: elf_file, inner, } } } impl<'a> ops::Deref for ElfHeader<'a> { type Target = dyn ElfHeaderRaw + 'a; fn deref(&self) -> &Self::Target { self.inner } } impl<'a> fmt::Debug for ElfHeader<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("ELF Header") .field("Class", &self.class()) .field("Endianness", &self.endianness()) .field("ELF Header Version", &self.header_version()) .field("ABI", &self.abi()) .field("ABI Version", &self.abi_version()) .field("File Type", &self.elftype()) .field("Target Machine", &self.machine()) .field("ELF Version", &self.elf_version()) .field("Entry Point", &self.entry_point()) .field("Program Header Offset", &self.program_header_offset()) .field("Section Header Offset", &self.section_header_offset()) .field("Flags", &self.flags()) .field("ELF Header Size", &self.elf_header_size()) .field("Program Header Size", &self.program_header_entry_size()) .field("Program Header Number", &self.program_header_entry_num()) .field("Section Header Size", &self.section_header_entry_size()) .field("Section Header Number", &self.section_header_entry_num()) .field(".shstr Section Index", &self.shstr_index()) .finish() } } elf_rs-0.3.1/src/elf/mod.rs000064400000000000000000000024431046102023000135640ustar 00000000000000mod elf; pub use elf::{Elf32, Elf64}; mod elf_header; pub use elf_header::ElfHeader; mod program_header; pub use program_header::{ProgramHeaderEntry, ProgramHeaderIter}; mod section_header; pub use section_header::{SectionHeaderEntry, SectionHeaderIter}; pub trait ElfType { type ElfHeader: crate::elf_header::ElfHeaderRaw; type ProgramHeader: crate::program_header::ProgramHeaderRaw; type SectionHeader: crate::section_header::SectionHeaderRaw; fn elf_class() -> crate::elf_header::ElfClass; } pub trait ElfFile { fn content(&self) -> &[u8]; fn elf_header(&self) -> ElfHeader; fn program_header_nth(&self, index: usize) -> Option; fn program_header_iter(&self) -> ProgramHeaderIter; fn section_header_nth(&self, index: usize) -> Option; fn section_header_iter(&self) -> SectionHeaderIter; fn shstr_section(&self) -> Option { let shstr_index = self.elf_header().shstr_index() as usize; self.section_header_nth(shstr_index) } fn lookup_section(&self, name: &[u8]) -> Option { self.section_header_iter() .find(|s| s.section_name() == Some(name)) } fn entry_point(&self) -> u64 { self.elf_header().entry_point() } } elf_rs-0.3.1/src/elf/program_header.rs000064400000000000000000000033551046102023000157670ustar 00000000000000use super::ElfFile; use crate::ProgramHeaderRaw; use core::fmt; use core::ops; pub struct ProgramHeaderEntry<'a> { elf_file: &'a dyn ElfFile, inner: &'a dyn ProgramHeaderRaw, } impl<'a> ProgramHeaderEntry<'a> { pub fn new(elf_file: &'a dyn ElfFile, inner: &'a dyn ProgramHeaderRaw) -> Self { Self { elf_file, inner } } pub fn content(&self) -> Option<&'a [u8]> { let offset = self.inner.offset() as usize; let size = self.inner.filesz() as usize; let top = offset.saturating_add(size); self.elf_file.content().get(offset..top) } } impl<'a> ops::Deref for ProgramHeaderEntry<'a> { type Target = dyn ProgramHeaderRaw + 'a; fn deref(&self) -> &Self::Target { self.inner } } impl<'a> fmt::Debug for ProgramHeaderEntry<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Program Header") .field("type", &self.ph_type()) .field("flags", &self.flags()) .field("offset", &self.offset()) .field("vaddr", &self.vaddr()) .field("paddr", &self.paddr()) .field("filesize", &self.filesz()) .field("memsize", &self.memsz()) .field("alignment", &self.align()) .finish() } } pub struct ProgramHeaderIter<'a> { elf_file: &'a dyn ElfFile, index: usize, } impl<'a> ProgramHeaderIter<'a> { pub fn new(elf_file: &'a dyn ElfFile) -> Self { Self { elf_file, index: 0 } } } impl<'a> Iterator for ProgramHeaderIter<'a> { type Item = ProgramHeaderEntry<'a>; fn next(&mut self) -> Option { self.elf_file.program_header_nth(self.index).map(|e| { self.index += 1; e }) } } elf_rs-0.3.1/src/elf/section_header.rs000064400000000000000000000043321046102023000157600ustar 00000000000000use super::ElfFile; use crate::section_header::SectionHeaderRaw; use core::fmt; use core::ops; pub struct SectionHeaderEntry<'a> { elf_file: &'a dyn ElfFile, inner: &'a dyn SectionHeaderRaw, } impl<'a> ops::Deref for SectionHeaderEntry<'a> { type Target = dyn SectionHeaderRaw + 'a; fn deref(&self) -> &Self::Target { self.inner } } impl<'a> SectionHeaderEntry<'a> { pub fn new(elf_file: &'a dyn ElfFile, inner: &'a dyn SectionHeaderRaw) -> Self { Self { elf_file, inner } } pub fn content(&self) -> Option<&'a [u8]> { let offset = self.inner.offset() as usize; let size = self.inner.size() as usize; let top = offset.saturating_add(size); self.elf_file.content().get(offset..top) } pub fn section_name(&self) -> Option<&'a [u8]> { let name_off = self.inner.name_off() as usize; let shstr_content = self.elf_file.shstr_section()?.content()?; shstr_content.get(name_off..)?.split(|&x| x == b'\0').next() } } impl<'a> fmt::Debug for SectionHeaderEntry<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let sh_name = self .section_name() .and_then(|n| core::str::from_utf8(n).ok()) .unwrap_or(""); f.debug_struct("Section Header") .field("name", &sh_name) .field("type", &self.sh_type()) .field("flags", &self.flags()) .field("addr", &self.addr()) .field("offset", &self.offset()) .field("size", &self.size()) .field("link", &self.link()) .field("info", &self.info()) .field("address alignment", &self.addralign()) .field("entry size", &self.entsize()) .finish() } } pub struct SectionHeaderIter<'a> { elf_file: &'a dyn ElfFile, index: usize, } impl<'a> SectionHeaderIter<'a> { pub fn new(elf_file: &'a dyn ElfFile) -> Self { Self { elf_file, index: 0 } } } impl<'a> Iterator for SectionHeaderIter<'a> { type Item = SectionHeaderEntry<'a>; fn next(&mut self) -> Option { self.elf_file.section_header_nth(self.index).map(|e| { self.index += 1; e }) } } elf_rs-0.3.1/src/elf_header/elf_header.rs000064400000000000000000000044511046102023000163740ustar 00000000000000use core::ptr::read_unaligned; use num_traits::PrimInt; use super::{ElfAbi, ElfClass, ElfEndian, ElfHeaderRaw, ElfMachine, ElfType}; #[repr(C)] #[derive(Debug)] pub struct ElfHeaderGen { magic: [u8; 4], class: u8, endianness: u8, header_version: u8, abi: u8, abi_version: u8, unused: [u8; 7], elftype: u16, machine: u16, elf_version: u32, entry: T, phoff: T, shoff: T, flags: u32, ehsize: u16, phentsize: u16, phnum: u16, shentsize: u16, shnum: u16, shstrndx: u16, } impl> ElfHeaderRaw for ElfHeaderGen { fn class(&self) -> ElfClass { self.class.into() } fn endianness(&self) -> ElfEndian { self.endianness.into() } fn header_version(&self) -> u8 { self.header_version } fn abi(&self) -> ElfAbi { self.abi.into() } fn abi_version(&self) -> u8 { self.abi_version } fn elftype(&self) -> ElfType { unsafe { read_unaligned(&self.elftype).into() } } fn machine(&self) -> ElfMachine { unsafe { read_unaligned(&self.machine).into() } } fn elf_version(&self) -> u32 { unsafe { read_unaligned(&self.elf_version) } } fn entry_point(&self) -> u64 { unsafe { read_unaligned(&self.entry).into() } } fn program_header_offset(&self) -> u64 { unsafe { read_unaligned(&self.phoff).into() } } fn section_header_offset(&self) -> u64 { unsafe { read_unaligned(&self.shoff).into() } } fn flags(&self) -> u32 { unsafe { read_unaligned(&self.flags) } } fn elf_header_size(&self) -> u16 { unsafe { read_unaligned(&self.ehsize) } } fn program_header_entry_size(&self) -> u16 { unsafe { read_unaligned(&self.phentsize) } } fn program_header_entry_num(&self) -> u16 { unsafe { read_unaligned(&self.phnum) } } fn section_header_entry_size(&self) -> u16 { unsafe { read_unaligned(&self.shentsize) } } fn section_header_entry_num(&self) -> u16 { unsafe { read_unaligned(&self.shnum) } } fn shstr_index(&self) -> u16 { unsafe { read_unaligned(&self.shstrndx) } } } pub type ElfHeader32 = ElfHeaderGen; pub type ElfHeader64 = ElfHeaderGen; elf_rs-0.3.1/src/elf_header/mod.rs000064400000000000000000000110131046102023000150650ustar 00000000000000mod elf_header; pub use elf_header::{ElfHeader32, ElfHeader64}; pub const ELF_MAGIC: [u8; 4] = [0x7f, b'E', b'L', b'F']; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ElfClass { Elf32, // 1 Elf64, // 2 Unknown(u8), } impl From for ElfClass { fn from(n: u8) -> Self { match n { 1 => ElfClass::Elf32, 2 => ElfClass::Elf64, n => ElfClass::Unknown(n), } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ElfEndian { LittleEndian, // 1, BigEndian, // 2, Unknown(u8), } impl From for ElfEndian { fn from(n: u8) -> Self { match n { 1 => ElfEndian::LittleEndian, 2 => ElfEndian::BigEndian, n => ElfEndian::Unknown(n), } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ElfAbi { SystemV, // 0x00, HPUX, // 0x01, NetBSD, // 0x02, Linux, // 0x03, Hurd, // 0x04, Solaris, // 0x06, AIX, // 0x07, IRIX, // 0x08, FreeBSD, // 0x09, Tru64, // 0x0A, NovellModesto, // 0x0B, OpenBSD, // 0x0C, OpenVMS, // 0x0D, NonStopKernel, // 0x0E, AROS, // 0x0F, FenixOS, // 0x10, CloudABI, // 0x11, Unknown(u8), } impl From for ElfAbi { fn from(n: u8) -> Self { match n { 0x00 => ElfAbi::SystemV, 0x01 => ElfAbi::HPUX, 0x02 => ElfAbi::NetBSD, 0x03 => ElfAbi::Linux, 0x04 => ElfAbi::Hurd, 0x06 => ElfAbi::Solaris, 0x07 => ElfAbi::AIX, 0x08 => ElfAbi::IRIX, 0x09 => ElfAbi::FreeBSD, 0x0A => ElfAbi::Tru64, 0x0B => ElfAbi::NovellModesto, 0x0C => ElfAbi::OpenBSD, 0x0D => ElfAbi::OpenVMS, 0x0E => ElfAbi::NonStopKernel, 0x0F => ElfAbi::AROS, 0x10 => ElfAbi::FenixOS, 0x11 => ElfAbi::CloudABI, n => ElfAbi::Unknown(n), } } } const ET_LOOS: u16 = 0xfe00; const ET_HIOS: u16 = 0xfeff; const ET_LOPROC: u16 = 0xff00; const ET_HIPROC: u16 = 0xffff; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ElfType { ET_NONE, // 0x00, ET_REL, // 0x01, ET_EXEC, // 0x02, ET_DYN, // 0x03, ET_CORE, // 0x04, OsSpecific(u16), ProcessorSpecific(u16), Unknown(u16), } impl From for ElfType { fn from(n: u16) -> Self { match n { 0x00 => ElfType::ET_NONE, 0x01 => ElfType::ET_REL, 0x02 => ElfType::ET_EXEC, 0x03 => ElfType::ET_DYN, 0x04 => ElfType::ET_CORE, x @ ET_LOOS..=ET_HIOS => ElfType::OsSpecific(x), x @ ET_LOPROC..=ET_HIPROC => ElfType::ProcessorSpecific(x), n => ElfType::Unknown(n), } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ElfMachine { Unknown, // 0x00 SPARC, // 0x02 x86, // 0x03 MIPS, // 0x08 PowerPC, // 0x14 S390, // 0x16 ARM, // 0x28 SuperH, // 0x2A IA_64, // 0x32 x86_64, // 0x3E AArch64, // 0xB7 RISC_V, // 0xF3 MachineUnknown(u16), } impl From for ElfMachine { fn from(n: u16) -> Self { match n { 0x00 => ElfMachine::Unknown, 0x02 => ElfMachine::SPARC, 0x03 => ElfMachine::x86, 0x08 => ElfMachine::MIPS, 0x14 => ElfMachine::PowerPC, 0x16 => ElfMachine::S390, 0x28 => ElfMachine::ARM, 0x2A => ElfMachine::SuperH, 0x32 => ElfMachine::IA_64, 0x3E => ElfMachine::x86_64, 0xB7 => ElfMachine::AArch64, 0xF3 => ElfMachine::RISC_V, n => ElfMachine::MachineUnknown(n), } } } pub trait ElfHeaderRaw { fn class(&self) -> ElfClass; fn endianness(&self) -> ElfEndian; fn header_version(&self) -> u8; fn abi(&self) -> ElfAbi; fn abi_version(&self) -> u8; fn elftype(&self) -> ElfType; fn machine(&self) -> ElfMachine; fn elf_version(&self) -> u32; fn entry_point(&self) -> u64; fn program_header_offset(&self) -> u64; fn section_header_offset(&self) -> u64; fn flags(&self) -> u32; fn elf_header_size(&self) -> u16; fn program_header_entry_size(&self) -> u16; fn program_header_entry_num(&self) -> u16; fn section_header_entry_size(&self) -> u16; fn section_header_entry_num(&self) -> u16; fn shstr_index(&self) -> u16; } elf_rs-0.3.1/src/lib.rs000064400000000000000000000064601046102023000130100ustar 00000000000000//! A simple no_std ELF file reader for ELF32 and ELF64. //! //! ## Minimal Example //! ```ignore //! use elf_rs::Elf; //! //! /// Minimal example. Works in `no_std`-contexts and the parsing //! /// itself needs zero allocations. //! fn main() { //! let elf_bytes = include_bytes!("path/to/file.elf"); //! let elf = elf_rs::Elf::from_bytes(elf_bytes).unwrap(); //! let elf64 = match elf { //! Elf::Elf64(elf) => elf, //! _ => panic!("got Elf32, expected Elf64"), //! }; //! let pr_hdrs = elf64.program_header_iter().collect::>(); //! dbg!(pr_hdrs); //! } //! ``` #![no_std] #![allow(non_camel_case_types)] #[macro_use] extern crate bitflags; extern crate num_traits; use core::mem::size_of; mod elf; mod elf_header; mod program_header; mod section_header; pub use elf::{ Elf32, Elf64, ElfFile, ElfHeader, ProgramHeaderEntry, ProgramHeaderIter, SectionHeaderEntry, SectionHeaderIter, }; pub use elf_header::{ ElfAbi, ElfClass, ElfEndian, ElfHeader32, ElfHeader64, ElfHeaderRaw, ElfMachine, ElfType, }; pub use program_header::{ ProgramHeader32, ProgramHeader64, ProgramHeaderFlags, ProgramHeaderRaw, ProgramType, }; pub use section_header::{ SectionHeader32, SectionHeader64, SectionHeaderFlags, SectionHeaderRaw, SectionType, }; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Error { BufferTooShort, InvalidMagic, InvalidClass, } #[derive(Debug)] pub enum Elf<'a> { Elf32(Elf32<'a>), Elf64(Elf64<'a>), } impl<'a> Elf<'a> { pub fn from_bytes(elf_buf: &'a [u8]) -> Result { if elf_buf.len() < size_of::() { return Err(Error::BufferTooShort); } if !elf_buf.starts_with(&elf_header::ELF_MAGIC) { return Err(Error::InvalidMagic); } let tmp_elf = Elf32::new(elf_buf); match tmp_elf.elf_header().class() { ElfClass::Elf64 => Elf64::from_bytes(elf_buf).map(|e| Elf::Elf64(e)), ElfClass::Elf32 => Elf32::from_bytes(elf_buf).map(|e| Elf::Elf32(e)), ElfClass::Unknown(_) => Err(Error::InvalidClass), } } } impl<'a> ElfFile for Elf<'a> { fn content(&self) -> &[u8] { match self { Elf::Elf32(e) => e.content(), Elf::Elf64(e) => e.content(), } } fn elf_header(&self) -> ElfHeader { match self { Elf::Elf32(e) => e.elf_header(), Elf::Elf64(e) => e.elf_header(), } } fn program_header_nth(&self, index: usize) -> Option { match self { Elf::Elf32(e) => e.program_header_nth(index), Elf::Elf64(e) => e.program_header_nth(index), } } fn program_header_iter(&self) -> ProgramHeaderIter { match self { Elf::Elf32(e) => e.program_header_iter(), Elf::Elf64(e) => e.program_header_iter(), } } fn section_header_nth(&self, index: usize) -> Option { match self { Elf::Elf32(e) => e.section_header_nth(index), Elf::Elf64(e) => e.section_header_nth(index), } } fn section_header_iter(&self) -> SectionHeaderIter { match self { Elf::Elf32(e) => e.section_header_iter(), Elf::Elf64(e) => e.section_header_iter(), } } } elf_rs-0.3.1/src/program_header/mod.rs000064400000000000000000000037271046102023000160030ustar 00000000000000mod program_header32; mod program_header64; pub use program_header32::ProgramHeader32; pub use program_header64::ProgramHeader64; const LOOS: u32 = 0x60000000; const HIOS: u32 = 0x6FFFFFFF; const LOPROC: u32 = 0x70000000; const HIPROC: u32 = 0x7FFFFFFF; bitflags! { /// The flags of an ELF program header. Always 32 bit long, also /// for 64-bit ELFs. /// /// Also called "Segment Permissions" in ELF specification or "p_flags". #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct ProgramHeaderFlags: u32 { const EXECUTE = 1; const WRITE = 2; const READ = 4; } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ProgramType { NULL, // 0x00000000, LOAD, // 0x00000001, DYNAMIC, // 0x00000002, INTERP, // 0x00000003, NOTE, // 0x00000004, SHLIB, // 0x00000005, PHDR, // 0x00000006, OsSpecific(u32), // 0x60000000 - 0x6FFFFFFF, ProcessorSpecific(u32), // 0x70000000 - 0x7FFFFFFF, Unknown(u32), } impl From for ProgramType { fn from(n: u32) -> Self { match n { 0x00000000 => ProgramType::NULL, 0x00000001 => ProgramType::LOAD, 0x00000002 => ProgramType::DYNAMIC, 0x00000003 => ProgramType::INTERP, 0x00000004 => ProgramType::NOTE, 0x00000005 => ProgramType::SHLIB, 0x00000006 => ProgramType::PHDR, x @ LOOS..=HIOS => ProgramType::OsSpecific(x), x @ LOPROC..=HIPROC => ProgramType::ProcessorSpecific(x), x => ProgramType::Unknown(x), } } } pub trait ProgramHeaderRaw { fn ph_type(&self) -> ProgramType; fn flags(&self) -> ProgramHeaderFlags; fn offset(&self) -> u64; fn vaddr(&self) -> u64; fn paddr(&self) -> u64; fn filesz(&self) -> u64; fn memsz(&self) -> u64; fn align(&self) -> u64; } elf_rs-0.3.1/src/program_header/program_header32.rs000064400000000000000000000021561046102023000203430ustar 00000000000000use crate::program_header::{ProgramHeaderFlags, ProgramHeaderRaw, ProgramType}; use core::ptr::read_unaligned; #[derive(Debug)] #[repr(C)] pub struct ProgramHeader32 { p_type: u32, p_offset: u32, p_vaddr: u32, p_paddr: u32, p_filesz: u32, p_memsz: u32, p_flags: ProgramHeaderFlags, p_align: u32, } impl ProgramHeaderRaw for ProgramHeader32 { fn ph_type(&self) -> ProgramType { unsafe { read_unaligned(&self.p_type).into() } } fn flags(&self) -> ProgramHeaderFlags { unsafe { read_unaligned(&self.p_flags) } } fn offset(&self) -> u64 { unsafe { read_unaligned(&self.p_offset) as u64 } } fn vaddr(&self) -> u64 { unsafe { read_unaligned(&self.p_vaddr) as u64 } } fn paddr(&self) -> u64 { unsafe { read_unaligned(&self.p_paddr) as u64 } } fn filesz(&self) -> u64 { unsafe { read_unaligned(&self.p_filesz) as u64 } } fn memsz(&self) -> u64 { unsafe { read_unaligned(&self.p_memsz) as u64 } } fn align(&self) -> u64 { unsafe { read_unaligned(&self.p_align) as u64 } } } elf_rs-0.3.1/src/program_header/program_header64.rs000064400000000000000000000021041046102023000203410ustar 00000000000000use crate::program_header::{ProgramHeaderFlags, ProgramHeaderRaw, ProgramType}; use core::ptr::read_unaligned; #[derive(Debug)] #[repr(C)] pub struct ProgramHeader64 { p_type: u32, p_flags: ProgramHeaderFlags, p_offset: u64, p_vaddr: u64, p_paddr: u64, p_filesz: u64, p_memsz: u64, p_align: u64, } impl ProgramHeaderRaw for ProgramHeader64 { fn ph_type(&self) -> ProgramType { unsafe { read_unaligned(&self.p_type).into() } } fn flags(&self) -> ProgramHeaderFlags { unsafe { read_unaligned(&self.p_flags) } } fn offset(&self) -> u64 { unsafe { read_unaligned(&self.p_offset) } } fn vaddr(&self) -> u64 { unsafe { read_unaligned(&self.p_vaddr) } } fn paddr(&self) -> u64 { unsafe { read_unaligned(&self.p_paddr) } } fn filesz(&self) -> u64 { unsafe { read_unaligned(&self.p_filesz) } } fn memsz(&self) -> u64 { unsafe { read_unaligned(&self.p_memsz) } } fn align(&self) -> u64 { unsafe { read_unaligned(&self.p_align) } } } elf_rs-0.3.1/src/section_header/mod.rs000064400000000000000000000063471046102023000160010ustar 00000000000000mod section_header; pub use section_header::{SectionHeader32, SectionHeader64}; const SHT_LOOS: u32 = 0x60000000; const SHT_HIOS: u32 = 0x6fffffff; const SHT_LOPROC: u32 = 0x70000000; const SHT_HIPROC: u32 = 0x7fffffff; const SHT_LOUSER: u32 = 0x80000000; const SHT_HIUSER: u32 = 0xffffffff; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum SectionType { SHT_NULL, // 0x0, SHT_PROGBITS, // 0x1, SHT_SYMTAB, // 0x2, SHT_STRTAB, // 0x3, SHT_RELA, // 0x4, SHT_HASH, // 0x5, SHT_DYNAMIC, // 0x6, SHT_NOTE, // 0x7, SHT_NOBITS, // 0x8, SHT_REL, // 0x9, SHT_SHLIB, // 0x0A, SHT_DYNSYM, // 0x0B, SHT_INIT_ARRAY, // 0x0E, SHT_FINI_ARRAY, // 0x0F, SHT_PREINIT_ARRAY, // 0x10, SHT_GROUP, // 0x11, SHT_SYMTAB_SHNDX, // 0x12, SHT_NUM, // 0x13, OsSpecific(u32), ProcessorSpecific(u32), ApplicationSpecific(u32), Unknown(u32), } impl From for SectionType { fn from(n: u32) -> Self { match n { 0x0 => SectionType::SHT_NULL, 0x1 => SectionType::SHT_PROGBITS, 0x2 => SectionType::SHT_SYMTAB, 0x3 => SectionType::SHT_STRTAB, 0x4 => SectionType::SHT_RELA, 0x5 => SectionType::SHT_HASH, 0x6 => SectionType::SHT_DYNAMIC, 0x7 => SectionType::SHT_NOTE, 0x8 => SectionType::SHT_NOBITS, 0x9 => SectionType::SHT_REL, 0x0A => SectionType::SHT_SHLIB, 0x0B => SectionType::SHT_DYNSYM, 0x0E => SectionType::SHT_INIT_ARRAY, 0x0F => SectionType::SHT_FINI_ARRAY, 0x10 => SectionType::SHT_PREINIT_ARRAY, 0x11 => SectionType::SHT_GROUP, 0x12 => SectionType::SHT_SYMTAB_SHNDX, 0x13 => SectionType::SHT_NUM, x @ SHT_LOOS..=SHT_HIOS => SectionType::OsSpecific(x), x @ SHT_LOPROC..=SHT_HIPROC => SectionType::ProcessorSpecific(x), x @ SHT_LOUSER..=SHT_HIUSER => SectionType::ApplicationSpecific(x), n => SectionType::Unknown(n), } } } bitflags! { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct SectionHeaderFlags: u64 { const SHF_WRITE = 0x1; const SHF_ALLOC = 0x2; const SHF_EXECINSTR = 0x4; const SHF_MERGE = 0x10; const SHF_STRINGS = 0x20; const SHF_INFO_LINK = 0x40; const SHF_LINK_ORDER = 0x80; const SHF_OS_NONCONFORMING = 0x100; const SHF_GROUP = 0x200; const SHF_TLS = 0x400; const SHF_MASKOS = 0x0ff00000; const SHF_MASKPROC = 0xf0000000; const SHF_ORDERED = 0x40000000; const SHF_EXCLUDE = 0x80000000; } } pub trait SectionHeaderRaw { fn name_off(&self) -> u32; fn sh_type(&self) -> SectionType; fn flags(&self) -> SectionHeaderFlags; fn addr(&self) -> u64; fn offset(&self) -> u64; fn size(&self) -> u64; fn link(&self) -> u32; fn info(&self) -> u32; fn addralign(&self) -> u64; fn entsize(&self) -> u64; } elf_rs-0.3.1/src/section_header/section_header.rs000064400000000000000000000030421046102023000201630ustar 00000000000000use core::ptr::read_unaligned; use num_traits::PrimInt; use super::{SectionHeaderFlags, SectionHeaderRaw, SectionType}; #[repr(C)] #[derive(Debug)] pub struct SectionHeaderGen { sh_name: u32, sh_type: u32, sh_flags: T, sh_addr: T, sh_offset: T, sh_size: T, sh_link: u32, sh_info: u32, sh_addralign: T, sh_entsize: T, } impl> SectionHeaderRaw for SectionHeaderGen { fn name_off(&self) -> u32 { unsafe { read_unaligned(&self.sh_name) } } fn sh_type(&self) -> SectionType { unsafe { read_unaligned(&self.sh_type).into() } } fn flags(&self) -> SectionHeaderFlags { let flags = unsafe { read_unaligned(&self.sh_flags).into() }; SectionHeaderFlags::from_bits_truncate(flags) } fn addr(&self) -> u64 { unsafe { read_unaligned(&self.sh_addr).into() } } fn offset(&self) -> u64 { unsafe { read_unaligned(&self.sh_offset).into() } } fn size(&self) -> u64 { unsafe { read_unaligned(&self.sh_size).into() } } fn link(&self) -> u32 { unsafe { read_unaligned(&self.sh_link).into() } } fn info(&self) -> u32 { unsafe { read_unaligned(&self.sh_info).into() } } fn addralign(&self) -> u64 { unsafe { read_unaligned(&self.sh_addralign).into() } } fn entsize(&self) -> u64 { unsafe { read_unaligned(&self.sh_entsize).into() } } } pub type SectionHeader32 = SectionHeaderGen; pub type SectionHeader64 = SectionHeaderGen; elf_rs-0.3.1/tests/integration_tests.rs000064400000000000000000000013601046102023000163540ustar 00000000000000const TEST_ELF_FILE: &str = "tests/data/ls"; #[test] fn test_unaligned_buffer() { extern crate elf_rs; use std::fs::File; use std::io::Read; use elf_rs::{Elf, ElfFile}; for offset in 0..64 { let mut elf_file = File::open(TEST_ELF_FILE).expect("failed to open file"); let mut elf_buf = Vec::::new(); for _ in 0..offset { elf_buf.push(0) } elf_file .read_to_end(&mut elf_buf) .expect("failed to read file"); let elf = Elf::from_bytes(&mut elf_buf[offset..]).expect("fail to load elf file"); println!("elf {:?}", elf); if let Elf::Elf64(e) = elf { println!("elf header {:?}", e.elf_header()); } } }