use std::{borrow::Cow, convert::TryInto, iter, ops::Range, slice};
use goblin::{
elf::{Elf, Header, ProgramHeader, Reloc, SectionHeader, Sym},
elf64::{
header::{EI_ABIVERSION, EI_CLASS, EI_DATA, EI_OSABI, EI_VERSION},
reloc::RelocIterator,
sym::SymIterator,
},
error::Error as GoblinError,
};
use crate::{
elf::ElfError,
elf_parser::{
consts::{SHF_ALLOC, SHF_WRITE, SHT_NOBITS, STT_FUNC},
types::{
Elf64Addr, Elf64Ehdr, Elf64Off, Elf64Phdr, Elf64Rel, Elf64Shdr, Elf64Sym, Elf64Word,
Elf64Xword, ElfIdent,
},
Elf64, ElfParserError,
},
error::EbpfError,
};
pub trait ElfParser<'a>: Sized {
type ProgramHeader: ElfProgramHeader + 'a;
type ProgramHeaders: Iterator<Item = &'a Self::ProgramHeader>;
type SectionHeader: ElfSectionHeader + 'a;
type SectionHeaders: Iterator<Item = &'a Self::SectionHeader>;
type Symbol: ElfSymbol + 'a;
type Symbols: Iterator<Item = Cow<'a, Self::Symbol>>;
type Relocation: ElfRelocation + 'a;
type Relocations: Iterator<Item = Cow<'a, Self::Relocation>>;
fn parse(data: &'a [u8]) -> Result<Self, ElfError>;
fn header(&self) -> &Elf64Ehdr;
fn program_headers(&'a self) -> Self::ProgramHeaders;
fn section_headers(&'a self) -> Self::SectionHeaders;
fn section(&self, name: &[u8]) -> Result<Self::SectionHeader, ElfError>;
fn section_name(&self, sh_name: Elf64Word) -> Option<&[u8]>;
fn symbols(&'a self) -> Self::Symbols;
fn symbol_name(&self, st_name: Elf64Word) -> Option<&[u8]>;
fn dynamic_symbol(&self, index: Elf64Word) -> Option<Self::Symbol>;
fn dynamic_symbol_name(&self, st_name: Elf64Word) -> Option<&[u8]>;
fn dynamic_relocations(&'a self) -> Self::Relocations;
}
pub trait ElfProgramHeader {
fn p_vaddr(&self) -> Elf64Addr;
fn p_memsz(&self) -> Elf64Xword;
fn p_offset(&self) -> Elf64Off;
fn vm_range(&self) -> Range<Elf64Addr> {
let addr = self.p_vaddr();
addr..addr.saturating_add(self.p_memsz())
}
}
pub trait ElfSectionHeader {
fn sh_name(&self) -> Elf64Word;
fn sh_addr(&self) -> Elf64Addr;
fn sh_offset(&self) -> Elf64Off;
fn sh_size(&self) -> Elf64Xword;
fn sh_flags(&self) -> Elf64Xword;
fn sh_type(&self) -> Elf64Word;
fn is_writable(&self) -> bool {
self.sh_flags() & (SHF_ALLOC | SHF_WRITE) == SHF_ALLOC | SHF_WRITE
}
fn file_range(&self) -> Option<Range<usize>> {
(self.sh_type() != SHT_NOBITS).then(|| {
let offset = self.sh_offset() as usize;
offset..offset.saturating_add(self.sh_size() as usize)
})
}
fn vm_range(&self) -> Range<Elf64Addr> {
let addr = self.sh_addr();
addr..addr.saturating_add(self.sh_size())
}
}
pub trait ElfSymbol: Clone {
fn st_name(&self) -> Elf64Word;
fn st_info(&self) -> u8;
fn st_value(&self) -> Elf64Addr;
fn is_function(&self) -> bool {
(self.st_info() & 0xF) == STT_FUNC
}
}
pub trait ElfRelocation: Clone {
fn r_offset(&self) -> Elf64Addr;
fn r_type(&self) -> Elf64Word;
fn r_sym(&self) -> Elf64Word;
}
pub struct GoblinParser<'a> {
elf: Elf<'a>,
header: Elf64Ehdr,
}
impl<'a> ElfParser<'a> for GoblinParser<'a> {
type ProgramHeader = ProgramHeader;
type ProgramHeaders = slice::Iter<'a, ProgramHeader>;
type SectionHeader = SectionHeader;
type SectionHeaders = slice::Iter<'a, SectionHeader>;
type Symbol = Sym;
type Symbols = iter::Map<SymIterator<'a>, fn(Self::Symbol) -> Cow<'a, Self::Symbol>>;
type Relocation = Reloc;
type Relocations =
iter::Map<RelocIterator<'a>, fn(Self::Relocation) -> Cow<'a, Self::Relocation>>;
fn parse(data: &'a [u8]) -> Result<GoblinParser<'a>, ElfError> {
let elf = Elf::parse(data)?;
Ok(Self {
header: elf.header.into(),
elf,
})
}
fn header(&self) -> &Elf64Ehdr {
&self.header
}
fn program_headers(&'a self) -> Self::ProgramHeaders {
self.elf.program_headers.iter()
}
fn section_headers(&'a self) -> Self::SectionHeaders {
self.elf.section_headers.iter()
}
fn section(&self, name: &[u8]) -> Result<Self::SectionHeader, ElfError> {
match self.elf.section_headers.iter().find(|section_header| {
if let Some(this_name) = self.section_name(section_header.sh_name as Elf64Word) {
return this_name == name;
}
false
}) {
Some(section) => Ok(section.clone()),
None => Err(ElfError::SectionNotFound(
std::str::from_utf8(name)
.unwrap_or("UTF-8 error")
.to_string(),
)),
}
}
fn section_name(&self, sh_name: Elf64Word) -> Option<&[u8]> {
self.elf
.shdr_strtab
.get_at(sh_name as usize)
.map(|name| name.as_bytes())
}
fn symbols(&'a self) -> Self::Symbols {
self.elf.syms.iter().map(Cow::Owned)
}
fn symbol_name(&self, st_name: Elf64Word) -> Option<&[u8]> {
self.elf
.strtab
.get_at(st_name as usize)
.map(|name| name.as_bytes())
}
fn dynamic_symbol(&self, index: Elf64Word) -> Option<Self::Symbol> {
self.elf.dynsyms.get(index as usize)
}
fn dynamic_symbol_name(&self, st_name: Elf64Word) -> Option<&[u8]> {
self.elf
.dynstrtab
.get_at(st_name as usize)
.map(|name| name.as_bytes())
}
fn dynamic_relocations(&self) -> Self::Relocations {
self.elf.dynrels.iter().map(Cow::Owned)
}
}
impl From<Header> for Elf64Ehdr {
fn from(h: Header) -> Self {
Elf64Ehdr {
e_ident: ElfIdent {
ei_mag: h.e_ident[0..4].try_into().unwrap(),
ei_class: h.e_ident[EI_CLASS],
ei_data: h.e_ident[EI_DATA],
ei_version: h.e_ident[EI_VERSION],
ei_osabi: h.e_ident[EI_OSABI],
ei_abiversion: h.e_ident[EI_ABIVERSION],
ei_pad: [0u8; 7],
},
e_type: h.e_type,
e_machine: h.e_machine,
e_version: h.e_version,
e_entry: h.e_entry,
e_phoff: h.e_phoff,
e_shoff: h.e_shoff,
e_flags: h.e_flags,
e_ehsize: h.e_ehsize,
e_phentsize: h.e_phentsize,
e_phnum: h.e_phnum,
e_shentsize: h.e_shentsize,
e_shnum: h.e_shnum,
e_shstrndx: h.e_shstrndx,
}
}
}
impl ElfProgramHeader for ProgramHeader {
fn p_vaddr(&self) -> Elf64Addr {
self.p_vaddr
}
fn p_memsz(&self) -> Elf64Xword {
self.p_memsz
}
fn p_offset(&self) -> Elf64Off {
self.p_offset
}
}
impl ElfSectionHeader for SectionHeader {
fn sh_name(&self) -> Elf64Word {
self.sh_name as _
}
fn sh_flags(&self) -> Elf64Xword {
self.sh_flags
}
fn sh_addr(&self) -> Elf64Addr {
self.sh_addr
}
fn sh_offset(&self) -> Elf64Off {
self.sh_offset
}
fn sh_size(&self) -> Elf64Xword {
self.sh_size
}
fn sh_type(&self) -> Elf64Word {
self.sh_type
}
}
impl ElfSymbol for Sym {
fn st_name(&self) -> Elf64Word {
self.st_name as _
}
fn st_info(&self) -> u8 {
self.st_info
}
fn st_value(&self) -> Elf64Addr {
self.st_value
}
}
impl ElfRelocation for Reloc {
fn r_offset(&self) -> Elf64Addr {
self.r_offset
}
fn r_type(&self) -> Elf64Word {
self.r_type
}
fn r_sym(&self) -> Elf64Word {
self.r_sym as Elf64Word
}
}
#[derive(Debug)]
pub struct NewParser<'a> {
elf: Elf64<'a>,
}
impl<'a> ElfParser<'a> for NewParser<'a> {
type ProgramHeader = Elf64Phdr;
type ProgramHeaders = slice::Iter<'a, Self::ProgramHeader>;
type SectionHeader = Elf64Shdr;
type SectionHeaders = slice::Iter<'a, Self::SectionHeader>;
type Symbol = Elf64Sym;
type Symbols =
iter::Map<slice::Iter<'a, Self::Symbol>, fn(&'a Self::Symbol) -> Cow<'a, Self::Symbol>>;
type Relocation = Elf64Rel;
type Relocations = iter::Map<
slice::Iter<'a, Self::Relocation>,
fn(&'a Self::Relocation) -> Cow<'a, Self::Relocation>,
>;
fn parse(data: &'a [u8]) -> Result<NewParser<'a>, ElfError> {
Ok(Self {
elf: Elf64::parse(data)?,
})
}
fn header(&self) -> &Elf64Ehdr {
self.elf.file_header()
}
fn program_headers(&'a self) -> Self::ProgramHeaders {
self.elf.program_header_table().iter()
}
fn section_headers(&'a self) -> Self::SectionHeaders {
self.elf.section_header_table().iter()
}
fn section(&self, name: &[u8]) -> Result<Self::SectionHeader, ElfError> {
for section_header in self.elf.section_header_table() {
if self.elf.section_name(section_header.sh_name)? == name {
return Ok(section_header.clone());
}
}
Err(ElfError::SectionNotFound(
std::str::from_utf8(name)
.unwrap_or("UTF-8 error")
.to_string(),
))
}
fn section_name(&self, sh_name: Elf64Word) -> Option<&[u8]> {
self.elf.section_name(sh_name).ok()
}
fn symbols(&'a self) -> Self::Symbols {
self.elf
.symbol_table()
.ok()
.flatten()
.unwrap_or(&[])
.iter()
.map(Cow::Borrowed)
}
fn symbol_name(&self, st_name: Elf64Word) -> Option<&[u8]> {
self.elf.symbol_name(st_name).ok()
}
fn dynamic_symbol(&self, index: Elf64Word) -> Option<Self::Symbol> {
self.elf
.dynamic_symbol_table()
.and_then(|table| table.get(index as usize).cloned())
}
fn dynamic_symbol_name(&self, st_name: Elf64Word) -> Option<&[u8]> {
self.elf.dynamic_symbol_name(st_name).ok()
}
fn dynamic_relocations(&'a self) -> Self::Relocations {
self.elf
.dynamic_relocations_table()
.unwrap_or(&[])
.iter()
.map(Cow::Borrowed)
}
}
impl ElfProgramHeader for Elf64Phdr {
fn p_vaddr(&self) -> Elf64Addr {
self.p_vaddr
}
fn p_memsz(&self) -> Elf64Xword {
self.p_memsz
}
fn p_offset(&self) -> Elf64Off {
self.p_offset
}
}
impl ElfSectionHeader for Elf64Shdr {
fn sh_name(&self) -> Elf64Word {
self.sh_name as _
}
fn sh_flags(&self) -> Elf64Xword {
self.sh_flags
}
fn sh_addr(&self) -> Elf64Addr {
self.sh_addr
}
fn sh_offset(&self) -> Elf64Off {
self.sh_offset
}
fn sh_size(&self) -> Elf64Xword {
self.sh_size
}
fn sh_type(&self) -> Elf64Word {
self.sh_type
}
}
impl ElfSymbol for Elf64Sym {
fn st_name(&self) -> Elf64Word {
self.st_name
}
fn st_info(&self) -> u8 {
self.st_info
}
fn st_value(&self) -> Elf64Addr {
self.st_value
}
}
impl ElfRelocation for Elf64Rel {
fn r_offset(&self) -> Elf64Addr {
self.r_offset
}
fn r_type(&self) -> Elf64Word {
(self.r_info & 0xFFFFFFFF) as Elf64Word
}
fn r_sym(&self) -> Elf64Word {
self.r_info.checked_shr(32).unwrap_or(0) as Elf64Word
}
}
impl From<ElfParserError> for ElfError {
fn from(err: ElfParserError) -> Self {
match err {
ElfParserError::InvalidSectionHeader
| ElfParserError::InvalidString
| ElfParserError::StringTooLong(_, _)
| ElfParserError::InvalidSize
| ElfParserError::Overlap
| ElfParserError::SectionNotInOrder
| ElfParserError::NoSectionNameStringTable
| ElfParserError::InvalidDynamicSectionTable
| ElfParserError::InvalidRelocationTable
| ElfParserError::InvalidAlignment
| ElfParserError::NoStringTable
| ElfParserError::NoDynamicStringTable
| ElfParserError::InvalidFileHeader => ElfError::FailedToParse(err.to_string()),
ElfParserError::InvalidProgramHeader => ElfError::InvalidProgramHeader,
ElfParserError::OutOfBounds => ElfError::ValueOutOfBounds,
}
}
}
impl From<GoblinError> for ElfError {
fn from(error: GoblinError) -> Self {
match error {
GoblinError::Malformed(string) => Self::FailedToParse(format!("malformed: {string}")),
GoblinError::BadMagic(magic) => Self::FailedToParse(format!("bad magic: {magic:#x}")),
GoblinError::Scroll(error) => Self::FailedToParse(format!("read-write: {error}")),
GoblinError::IO(error) => Self::FailedToParse(format!("io: {error}")),
GoblinError::BufferTooShort(n, error) => {
Self::FailedToParse(format!("buffer too short {n} {error}"))
}
_ => Self::FailedToParse("cause unkown".to_string()),
}
}
}
impl From<GoblinError> for EbpfError {
fn from(error: GoblinError) -> Self {
ElfError::from(error).into()
}
}