diff --git a/elven-forest/src/main.rs b/elven-forest/src/main.rs index d681d17..cd9ebc1 100644 --- a/elven-forest/src/main.rs +++ b/elven-forest/src/main.rs @@ -2,8 +2,8 @@ use std::{fmt::Display, fs::File}; use anyhow::Context; use elven_parser::{ - consts::{ShType, RX86_64}, - defs::Elf, + consts::{DynamicTag, ShType, RX86_64}, + defs::{Addr, Elf}, ElfParseError, }; use memmap2::Mmap; @@ -40,6 +40,12 @@ struct RelaTable { addend: u64, } +#[derive(Tabled)] +struct DynTable { + tag: DynamicTag, + value: Addr, +} + fn print_file(path: &str) -> anyhow::Result<()> { println!("{path}"); @@ -59,7 +65,7 @@ fn print_file(path: &str) -> anyhow::Result<()> { HeaderTable("osabi", &ident.osabi), HeaderTable("type", &header.r#type), HeaderTable("machine", &header.machine), - HeaderTable("entrypoint (hex)", &header.entry), + HeaderTable("entrypoint", &header.entry), ]; let mut table = Table::new(header_tab); @@ -90,8 +96,10 @@ fn print_file(path: &str) -> anyhow::Result<()> { .relas()? .map(|(sh, rela)| { let section = elf.sh_string(sh.name)?.to_string(); + let sym = elf.symbol(rela.info.sym())?; let symbol = elf.string(sym.name)?.to_string(); + let offset = rela.offset.0; let r#type = elven_parser::consts::RX86_64(rela.info.r#type()); let addend = rela.addend; @@ -108,6 +116,16 @@ fn print_file(path: &str) -> anyhow::Result<()> { print_table(Table::new(relas)); + if let Ok(dyns) = elf.dyn_entries() { + println!("\nDynamic entries"); + + let dyns = dyns.iter().map(|dy| DynTable { + tag: dy.tag, + value: Addr(dy.val), + }); + print_table(Table::new(dyns)); + } + println!(); Ok(()) diff --git a/elven-parser/src/consts.rs b/elven-parser/src/consts.rs index 810b4e9..91e6c48 100644 --- a/elven-parser/src/consts.rs +++ b/elven-parser/src/consts.rs @@ -129,8 +129,12 @@ pub const ET_EXEC: u16 = 2; pub const ET_DYN: u16 = 3; pub const ET_CORE: u16 = 4; -pub const EM_NONE: u16 = 0; /* No machine */ -pub const EM_X86_64: u16 = 62; /* AMD x86-64 architecture */ +const_group_with_fmt! { + pub struct Machine(u16): "Machine" + + pub const EM_NONE = 0; /* No machine */ + pub const EM_X86_64 = 62; /* AMD x86-64 architecture */ +} pub const EV_NONE: u32 = 0; @@ -297,6 +301,105 @@ const_group_with_fmt! { pub const R_X86_64_NUM = 43; } +// ------------------ +// Dynamic +// ------------------ + +const_group_with_fmt! { + pub struct DynamicTag(u64): "DT" + + pub const DT_NULL = 0; /* Marks end of dynamic section */ + pub const DT_NEEDED = 1; /* Name of needed library */ + pub const DT_PLTRELSZ = 2; /* Size in bytes of PLT relocs */ + pub const DT_PLTGOT = 3; /* Processor defined value */ + pub const DT_HASH = 4; /* Address of symbol hash table */ + pub const DT_STRTAB = 5; /* Address of string table */ + pub const DT_SYMTAB = 6; /* Address of symbol table */ + pub const DT_RELA = 7; /* Address of Rela relocs */ + pub const DT_RELASZ = 8; /* Total size of Rela relocs */ + pub const DT_RELAENT = 9; /* Size of one Rela reloc */ + pub const DT_STRSZ = 10; /* Size of string table */ + pub const DT_SYMENT = 11; /* Size of one symbol table entry */ + pub const DT_INIT = 12; /* Address of init function */ + pub const DT_FINI = 13; /* Address of termination function */ + pub const DT_SONAME = 14; /* Name of shared object */ + pub const DT_RPATH = 15; /* Library search path (deprecated) */ + pub const DT_SYMBOLIC = 16; /* Start symbol search here */ + pub const DT_REL = 17; /* Address of Rel relocs */ + pub const DT_RELSZ = 18; /* Total size of Rel relocs */ + pub const DT_RELENT = 19; /* Size of one Rel reloc */ + pub const DT_PLTREL = 20; /* Type of reloc in PLT */ + pub const DT_DEBUG = 21; /* For debugging; unspecified */ + pub const DT_TEXTREL = 22; /* Reloc might modify .text */ + pub const DT_JMPREL = 23; /* Address of PLT relocs */ + pub const DT_BIND_NOW = 24; /* Process relocations of object */ + pub const DT_INIT_ARRAY = 25; /* Array with addresses of init fct */ + pub const DT_FINI_ARRAY = 26; /* Array with addresses of fini fct */ + pub const DT_INIT_ARRAYSZ = 27; /* Size in bytes of DT_INIT_ARRAY */ + pub const DT_FINI_ARRAYSZ = 28; /* Size in bytes of DT_FINI_ARRAY */ + pub const DT_RUNPATH = 29; /* Library search path */ + pub const DT_FLAGS = 30; /* Flags for the object being loaded */ + pub const DT_PREINIT_ARRAY = 32; /* Array with addresses of preinit fct*/ + pub const DT_PREINIT_ARRAYSZ = 33; /* size in bytes of DT_PREINIT_ARRAY */ + pub const DT_SYMTAB_SHNDX = 34; /* Address of SYMTAB_SHNDX section */ + pub const DT_NUM = 35; /* Number used */ + pub const DT_PROCNUM = 0x37; /* Most used by any processor */ + + /* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ + pub const DT_GNU_PRELINKED = 0x6ffffdf5; /* Prelinking timestamp */ + pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6; /* Size of conflict section */ + pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7; /* Size of library list */ + pub const DT_CHECKSUM = 0x6ffffdf8; + pub const DT_PLTPADSZ = 0x6ffffdf9; + pub const DT_MOVEENT = 0x6ffffdfa; + pub const DT_MOVESZ = 0x6ffffdfb; + pub const DT_FEATURE_1 = 0x6ffffdfc; /* Feature selection (DTF_*). */ + pub const DT_POSFLAG_1 = 0x6ffffdfd; /* Flags for DT_* entries, effecting the: u following DT_* entry. */ + pub const DT_SYMINSZ = 0x6ffffdfe; /* Size of syminfo table (in bytes) */ + pub const DT_SYMINENT = 0x6ffffdff; /* Entry size of syminfo */ + + /* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ + pub const DT_GNU_HASH = 0x6ffffef5; /* GNU-style hash table. */ + pub const DT_TLSDESC_PLT = 0x6ffffef6; + pub const DT_TLSDESC_GOT = 0x6ffffef7; + pub const DT_GNU_CONFLICT = 0x6ffffef8; /* Start of conflict section */ + pub const DT_GNU_LIBLIST = 0x6ffffef9; /* Library list */ + pub const DT_CONFIG = 0x6ffffefa; /* Configuration information. */ + pub const DT_DEPAUDIT = 0x6ffffefb; /* Dependency auditing. */ + pub const DT_AUDIT = 0x6ffffefc; /* Object auditing. */ + pub const DT_PLTPAD = 0x6ffffefd; /* PLT padding. */ + pub const DT_MOVETAB = 0x6ffffefe; /* Move table. */ + pub const DT_SYMINFO = 0x6ffffeff; /* Syminfo table. */ + + /* The versioning entry types. The next are defined as part of the + GNU extension. */ + pub const DT_VERSYM = 0x6ffffff0; + pub const DT_RELACOUNT = 0x6ffffff9; + pub const DT_RELCOUNT = 0x6ffffffa; + /* These were chosen by Sun. */ + pub const DT_FLAGS_1 = 0x6ffffffb; /* State flags, see DF_1_* below. */ + pub const DT_VERDEF = 0x6ffffffc; /* Address of version definition table */ + pub const DT_VERDEFNUM = 0x6ffffffd; /* Number of version definitions */ + pub const DT_VERNEED = 0x6ffffffe; /* Address of table with needed versions */ + pub const DT_VERNEEDNUM = 0x6fffffff; /* Number of needed versions */ +} + +pub const DT_ENCODING: u64 = 32; /* Start of encoded range */ +pub const DT_LOOS: u64 = 0x6000000d; /* Start of OS-specific */ +pub const DT_HIOS: u64 = 0x6ffff000; /* End of OS-specific */ +pub const DT_LOPROC: u64 = 0x70000000; /* Start of processor-specific */ +pub const DT_HIPROC: u64 = 0x7fffffff; /* End of processor-specific */ +pub const DT_ADDRRNGLO: u64 = 0x6ffffe00; +pub const DT_ADDRRNGHI: u64 = 0x6ffffeff; +pub const DT_VALRNGLO: u64 = 0x6ffffd00; +pub const DT_VALRNGHI: u64 = 0x6ffffdff; + impl SectionIdx { pub fn usize(self) -> usize { self.0 as usize diff --git a/elven-parser/src/defs.rs b/elven-parser/src/defs.rs index 48e510a..5bf0f0d 100644 --- a/elven-parser/src/defs.rs +++ b/elven-parser/src/defs.rs @@ -3,13 +3,16 @@ //! See https://man7.org/linux/man-pages/man5/elf.5.html use crate::{ - consts as c, + consts::{self as c, DynamicTag, ShType}, idx::{define_idx, ElfIndexExt, ToIdxUsize}, ElfParseError, Result, }; use bstr::BStr; -use std::{fmt::{Debug, Display}, mem, string}; +use std::{ + fmt::{Debug, Display}, + mem, string, +}; use bytemuck::{Pod, PodCastError, Zeroable}; @@ -20,13 +23,13 @@ pub struct Addr(pub u64); impl Debug for Addr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}", self.0) + write!(f, "0x{:x}", self.0) } } impl Display for Addr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}", self.0) + write!(f, "0x{:x}", self.0) } } @@ -63,7 +66,7 @@ pub struct Elf<'a> { pub struct ElfHeader { pub ident: ElfIdent, pub r#type: u16, - pub machine: u16, + pub machine: c::Machine, pub version: u32, pub entry: Addr, pub phoff: Offset, @@ -185,6 +188,13 @@ impl Debug for RelInfo { } } +#[derive(Debug, Clone, Copy, Zeroable, Pod)] +#[repr(C)] +pub struct Dyn { + pub tag: c::DynamicTag, + pub val: u64, +} + impl<'a> Elf<'a> { pub fn new(data: &'a [u8]) -> Result { let magic = data[..c::SELFMAG].try_into().unwrap(); @@ -263,6 +273,13 @@ impl<'a> Elf<'a> { )) } + pub fn section_header_by_type(&self, ty: u32) -> Result<&Shdr> { + self.section_headers()? + .iter() + .find(|sh| sh.r#type == ty) + .ok_or(ElfParseError::SectionTypeNotFound(ShType(ty))) + } + pub fn section_content(&self, sh: &Shdr) -> Result<&[u8]> { if sh.r#type.0 == c::SHT_NOBITS { return Ok(&[]); @@ -320,6 +337,23 @@ impl<'a> Elf<'a> { Ok(BStr::new(&indexed[..end])) } + pub fn dyn_string(&self, idx: StringIdx) -> Result<&BStr> { + let tab_addr = self.dyn_entry_by_tag(c::DT_STRTAB)?; + let tab_sz = self.dyn_entry_by_tag(c::DT_STRSZ)?; + + let str_table = self + .data + .get_elf(tab_addr.val.., "dyn string table")? + .get_elf(..tab_sz.val, "dyn string table size")?; + + let indexed = str_table.get_elf(idx.., "string offset")?; + let end = indexed + .iter() + .position(|&c| c == b'\0') + .ok_or(ElfParseError::NoStringNulTerm(idx.to_idx_usize()))?; + Ok(BStr::new(&indexed[..end])) + } + pub fn relas(&self) -> Result> { Ok(self .section_headers()? @@ -336,11 +370,7 @@ impl<'a> Elf<'a> { } pub fn symbols(&self) -> Result<&[Sym]> { - let sh = self - .section_headers()? - .iter() - .find(|sh| sh.r#type == c::SHT_SYMTAB) - .ok_or(ElfParseError::SymtabNotFound)?; + let sh = self.section_header_by_type(c::SHT_SYMTAB)?; let data = self.section_content(sh)?; @@ -350,6 +380,41 @@ impl<'a> Elf<'a> { pub fn symbol(&self, idx: SymIdx) -> Result<&Sym> { self.symbols()?.get_elf(idx, "symbol index") } + + pub fn dyn_symbols(&self) -> Result<&[Sym]> { + let addr = self.dyn_entry_by_tag(c::DT_SYMTAB)?; + let size = self.dyn_entry_by_tag(c::DT_SYMENT)?; + + dbg!(addr, size); + + let data = self.dyn_content(addr.val, size.val)?; + + load_slice(data, data.len() / mem::size_of::()) + } + + pub fn dyn_symbol(&self, idx: SymIdx) -> Result<&Sym> { + dbg!(self.dyn_symbols()?).get_elf(idx, "symbol index") + } + + pub fn dyn_entries(&self) -> Result<&[Dyn]> { + let sh = self.section_header_by_name(b".dynamic")?; + let data = self.section_content(sh)?; + + load_slice(data, data.len() / mem::size_of::()) + } + + pub fn dyn_entry_by_tag(&self, tag: u64) -> Result<&Dyn> { + self.dyn_entries()? + .iter() + .find(|dy| dy.tag == tag) + .ok_or(ElfParseError::DynEntryNotFound(DynamicTag(tag))) + } + + pub fn dyn_content(&self, addr: u64, size: u64) -> Result<&[u8]> { + self.data + .get_elf(addr.., "dyn content offset")? + .get_elf(..size, "section size") + } } fn load_ref(data: &[u8]) -> Result<&T> { diff --git a/elven-parser/src/lib.rs b/elven-parser/src/lib.rs index df44471..a1a55aa 100644 --- a/elven-parser/src/lib.rs +++ b/elven-parser/src/lib.rs @@ -1,3 +1,5 @@ +use consts::{DynamicTag, ShType}; + pub mod consts; pub mod defs; mod idx; @@ -20,10 +22,12 @@ pub enum ElfParseError { IndexOutOfBounds(&'static str, usize), #[error("String in string table does not end with a nul terminator: String offset: {0}")] NoStringNulTerm(usize), - #[error("The SHT_SYMTAB section was not found")] - SymtabNotFound, + #[error("The {0} section was not found")] + SectionTypeNotFound(ShType), #[error("The section with the name {0:?} was not found")] SectionNotFound(std::result::Result>), + #[error("Dynamic entry not found: {0}")] + DynEntryNotFound(DynamicTag), } pub type Result = std::result::Result;