This commit is contained in:
nora 2023-02-11 23:58:25 +01:00
parent 03a4287bee
commit 8aa4068b1b
5 changed files with 234 additions and 49 deletions

View file

@ -6,6 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bstr = "1.2.0"
bytemuck = { version = "1.13.0", features = ["derive", "min_const_generics"] }
memmap2 = "0.5.8"
thiserror = "1.0.38"

View file

@ -22,7 +22,7 @@ macro_rules! const_group_with_fmt {
$(
$value => f.write_str(stringify!($name)),
)*
a => write!(f, "Other {}: {a}", $group_name,)
a => write!(f, "{}({a})", $group_name)
}
}
}
@ -32,6 +32,24 @@ macro_rules! const_group_with_fmt {
self.0 == *other
}
}
impl PartialEq<$struct_name> for $ty {
fn eq(&self, other: &$struct_name) -> bool {
*self == other.0
}
}
impl PartialOrd<$ty> for $struct_name {
fn partial_cmp(&self, other: &$ty) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(other)
}
}
impl PartialOrd<$struct_name> for $ty {
fn partial_cmp(&self, other: &$struct_name) -> Option<std::cmp::Ordering> {
self.partial_cmp(&other.0)
}
}
};
}
@ -45,7 +63,7 @@ pub const SELFMAG: usize = 4;
pub const EI_CLASS: usize = 4; /* File class byte index */
const_group_with_fmt! {
pub struct Class(u8): "Class"
pub struct Class(u8): "class"
pub const ELFCLASSNONE = 0; /* Invalid class */
pub const ELFCLASS32 = 1; /* 32-bit objects */
@ -55,7 +73,7 @@ pub const ELFCLASSNUM: u8 = 3;
pub const EI_DATA: usize = 5; /* Data encoding byte index */
const_group_with_fmt! {
pub struct Data(u8): "Data"
pub struct Data(u8): "data"
pub const ELFDATANONE = 0; /* Invalid data encoding */
pub const ELFDATA2LSB = 1; /* 2's complement, little endian */
@ -108,21 +126,26 @@ pub const EV_NONE: u32 = 0;
// Sections
// ------------------
pub const SHN_UNDEF: u16 = 0; /* Undefined section */
const_group_with_fmt! {
pub struct SectionIdx(u16): "SHN"
pub const SHN_UNDEF = 0; /* Undefined section */
pub const SHN_BEFORE = 0xff00; /* Order section before all others (Solaris). */
pub const SHN_AFTER = 0xff01; /* Order section after all others (Solaris). */
pub const SHN_ABS = 0xfff1; /* Associated symbol is absolute */
pub const SHN_COMMON = 0xfff2; /* Associated symbol is common */
pub const SHN_XINDEX = 0xffff; /* Index is in extra table. */
}
pub const SHN_LORESERVE: u16 = 0xff00; /* Start of reserved indices */
pub const SHN_LOPROC: u16 = 0xff00; /* Start of processor-specific */
pub const SHN_BEFORE: u16 = 0xff00; /* Order section before all others (Solaris). */
pub const SHN_AFTER: u16 = 0xff01; /* Order section after all others (Solaris). */
pub const SHN_HIPROC: u16 = 0xff1f; /* End of processor-specific */
pub const SHN_LOOS: u16 = 0xff20; /* Start of OS-specific */
pub const SHN_HIOS: u16 = 0xff3f; /* End of OS-specific */
pub const SHN_ABS: u16 = 0xfff1; /* Associated symbol is absolute */
pub const SHN_COMMON: u16 = 0xfff2; /* Associated symbol is common */
pub const SHN_XINDEX: u16 = 0xffff; /* Index is in extra table. */
pub const SHN_HIRESERVE: u16 = 0xffff; /* End of reserved indices */
const_group_with_fmt! {
pub struct ShType(u32): "Section header type"
pub struct ShType(u32): "SHT"
pub const SHT_NULL = 0; /* Section header table entry unused */
pub const SHT_PROGBITS = 1; /* Program data */
@ -163,8 +186,58 @@ pub const SHT_LOSUNW: u32 = 0x6ffffffa; /* Sun-specific low bound. */
pub const SHT_HISUNW: u32 = 0x6fffffff; /* Sun-specific high bound. */
pub const SHT_HIOS: u32 = 0x6fffffff; /* End OS-specific type */
// ------------------
// Symbols
// ------------------
const_group_with_fmt! {
pub struct RX86_64(u32): "x86_64 Relocation type"
pub struct SymbolType(u8): "STT"
pub const STT_NOTYPE = 0; /* Symbol type is unspecified */
pub const STT_OBJECT = 1; /* Symbol is a data object */
pub const STT_FUNC = 2; /* Symbol is a code object */
pub const STT_SECTION = 3; /* Symbol associated with a section */
pub const STT_FILE = 4; /* Symbol's name is file name */
pub const STT_COMMON = 5; /* Symbol is a common data object */
pub const STT_TLS = 6; /* Symbol is thread-local data object*/
pub const STT_NUM = 7; /* Number of defined types. */
pub const STT_GNU_IFUNC = 10; /* Symbol is indirect code object */
pub const STT_HIOS = 12; /* End of OS-specific */
pub const STT_LOPROC = 13; /* Start of processor-specific */
pub const STT_HIPROC = 15; /* End of processor-specific */
}
pub const STT_LOOS: u32 = 10; /* Start of OS-specific */
const_group_with_fmt! {
pub struct SymbolBinding(u8): "STB"
pub const STB_LOCAL = 0; /* Local symbol */
pub const STB_GLOBAL = 1; /* Global symbol */
pub const STB_WEAK = 2; /* Weak symbol */
pub const STB_NUM = 3; /* Number of defined types. */
pub const STB_GNU_UNIQUE = 10; /* Unique symbol. */
pub const STB_HIOS = 12; /* End of OS-specific */
pub const STB_LOPROC = 13; /* Start of processor-specific */
pub const STB_HIPROC = 15; /* End of processor-specific */
}
pub const STB_LOOS: u8 = 10; /* Start of OS-specific */
/* Symbol visibility specification encoded in the st_other field. */
const_group_with_fmt! {
pub struct SymbolVisibility(u8): "STV"
pub const STV_DEFAULT = 0; /* Default symbol visibility rules */
pub const STV_INTERNAL = 1; /* Processor specific hidden class */
pub const STV_HIDDEN = 2; /* Sym unavailable in other modules */
pub const STV_PROTECTED = 3; /* Not preemptible, not exported */
}
// ------------------
// Relocations
// ------------------
const_group_with_fmt! {
pub struct RX86_64(u32): "R_X86_64"
pub const R_X86_64_NONE = 0; /* No reloc */
pub const R_X86_64_64 = 1; /* Direct 64 bit */
@ -211,3 +284,9 @@ const_group_with_fmt! {
pub const R_X86_64_REX_GOTPCRELX = 42; /* Load from 32 bit signed pc relative offset to GOT entry with REX prefix, relaxable. */
pub const R_X86_64_NUM = 43;
}
impl SectionIdx {
pub fn usize(self) -> usize {
self.0 as usize
}
}

View file

@ -2,16 +2,10 @@
//!
//! See https://man7.org/linux/man-pages/man5/elf.5.html
pub mod consts;
use consts as c;
use crate::consts as c;
use bstr::BStr;
use std::{
ffi::CStr,
fmt::Debug,
mem,
ops::{self},
slice::SliceIndex,
};
use std::{fmt::Debug, mem, ops, slice::SliceIndex, string};
use bytemuck::{Pod, PodCastError, Zeroable};
@ -32,11 +26,15 @@ impl Offset {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
#[repr(transparent)]
pub struct Section(pub u16);
pub struct ShStringIdx(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
#[repr(transparent)]
pub struct Versym(pub u16);
pub struct StringIdx(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
#[repr(transparent)]
pub struct SymIdx(pub u32);
#[derive(Debug, Clone, thiserror::Error)]
pub enum ElfParseError {
@ -58,6 +56,8 @@ pub enum ElfParseError {
NoStringNulTerm(usize),
#[error("The SHT_SYMTAB section was not found")]
SymtabNotFound,
#[error("The section with the name {0:?} was not found")]
SectionNotFound(std::result::Result<string::String, Vec<u8>>),
}
type Result<T> = std::result::Result<T, ElfParseError>;
@ -84,7 +84,7 @@ pub struct ElfHeader {
pub phnum: u16,
pub shentsize: u16,
pub shnum: u16,
pub shstrndex: u16,
pub shstrndex: c::SectionIdx,
}
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
@ -117,7 +117,7 @@ pub struct Phdr {
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Shdr {
pub name: u32,
pub name: ShStringIdx,
pub r#type: c::ShType,
pub flags: u64,
pub addr: Addr,
@ -132,14 +132,34 @@ pub struct Shdr {
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Sym {
pub name: u32,
pub info: u8,
pub other: u8,
pub shndx: u16,
pub name: StringIdx,
pub info: SymInfo,
pub other: c::SymbolVisibility,
pub shndx: c::SectionIdx,
pub value: Addr,
pub size: u64,
}
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(transparent)]
pub struct SymInfo(pub u8);
impl SymInfo {
pub fn r#type(self) -> c::SymbolType {
c::SymbolType(self.0 & 0xf)
}
pub fn binding(self) -> c::SymbolBinding {
c::SymbolBinding(self.0 >> 4)
}
}
impl Debug for SymInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?},{:?}", self.r#type(), self.binding())
}
}
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Rel {
@ -157,11 +177,11 @@ pub struct Rela {
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(transparent)]
pub struct RelInfo(u64);
pub struct RelInfo(pub u64);
impl RelInfo {
pub fn sym(&self) -> u32 {
(self.0 >> 32) as u32
pub fn sym(&self) -> SymIdx {
SymIdx((self.0 >> 32) as u32)
}
pub fn r#type(&self) -> u32 {
@ -171,7 +191,7 @@ impl RelInfo {
impl Debug for RelInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} @ {}", c::RX86_64(self.r#type()), self.sym())
write!(f, "{:?} @ {}", c::RX86_64(self.r#type()), self.sym().0)
}
}
@ -210,7 +230,7 @@ impl<'a> Elf<'a> {
let off = header.phoff.usize();
load_slice(
&self.data.get_elf(off.., "program header offset")?,
self.data.get_elf(off.., "program header offset")?,
header.phnum.into(),
)
}
@ -232,14 +252,27 @@ impl<'a> Elf<'a> {
}
let off = header.shoff.usize();
load_slice(
&self.data.get_elf(off.., "sectoin header offset")?,
self.data.get_elf(off.., "sectoin header offset")?,
header.shnum.into(),
)
}
pub fn section_header(&self, idx: usize) -> Result<&Shdr> {
pub fn section_header(&self, idx: c::SectionIdx) -> Result<&Shdr> {
let sections = self.section_headers()?;
sections.get_elf(idx, "section number")
sections.get_elf(idx.usize(), "section number")
}
pub fn section_header_by_name(&self, name: &[u8]) -> Result<&Shdr> {
let sections = self.section_headers()?;
for sh in sections {
if self.sh_string(sh.name)? == name {
return Ok(sh);
}
}
let name = name.to_vec();
Err(ElfParseError::SectionNotFound(
string::String::from_utf8(name).map_err(|err| err.into_bytes()),
))
}
pub fn section_content(&self, sh: &Shdr) -> Result<&[u8]> {
@ -247,13 +280,12 @@ impl<'a> Elf<'a> {
return Ok(&[]);
}
Ok(&self
.data
self.data
.get_elf(sh.offset.usize().., "section offset")?
.get_elf(..(sh.size as usize), "section size")?)
.get_elf(..(sh.size as usize), "section size")
}
pub fn str_table(&self) -> Result<&[u8]> {
pub fn sh_str_table(&self) -> Result<&[u8]> {
let header = self.header()?;
let shstrndex = header.shstrndex;
@ -271,18 +303,35 @@ impl<'a> Elf<'a> {
)
}
let strtab_header = self.section_header(shstrndex as usize)?;
let strtab_header = self.section_header(shstrndex)?;
self.section_content(strtab_header)
}
pub fn string(&self, idx: usize) -> Result<&CStr> {
pub fn str_table(&self) -> Result<&[u8]> {
let sh = self.section_header_by_name(b".strtab")?;
self.section_content(sh)
}
pub fn sh_string(&self, idx: ShStringIdx) -> Result<&BStr> {
let idx = idx.0 as usize;
let str_table = self.sh_str_table()?;
let indexed = str_table.get_elf(idx.., "string offset")?;
let end = indexed
.iter()
.position(|&c| c == b'\0')
.ok_or(ElfParseError::NoStringNulTerm(idx))?;
Ok(BStr::new(&indexed[..end]))
}
pub fn string(&self, idx: StringIdx) -> Result<&BStr> {
let idx = idx.0 as usize;
let str_table = self.str_table()?;
let indexed = str_table.get_elf(idx.., "string offset")?;
let end = indexed
.iter()
.position(|&c| c == b'\0')
.ok_or(ElfParseError::NoStringNulTerm(idx))?;
Ok(CStr::from_bytes_with_nul(&indexed[..=end]).unwrap())
Ok(BStr::new(&indexed[..end]))
}
pub fn relas(&self) -> Result<impl Iterator<Item = (&Shdr, &Rela)>> {
@ -311,6 +360,11 @@ impl<'a> Elf<'a> {
load_slice(data, data.len() / mem::size_of::<Sym>())
}
pub fn symbol(&self, idx: SymIdx) -> Result<&Sym> {
let idx = idx.0 as usize;
self.symbols()?.get_elf(idx, "symbol index")
}
}
fn load_ref<T: Pod>(data: &[u8]) -> Result<&T> {
@ -417,7 +471,7 @@ mod tests {
elf.section_headers()?;
for section in elf.section_headers()? {
let name = elf.string(section.name as usize)?.to_str().unwrap();
let name = elf.sh_string(section.name)?.to_string();
println!("{name:20} {:5} {:?}", section.size, section.r#type);
}
@ -439,18 +493,31 @@ mod tests {
elf.program_headers()?;
elf.section_headers()?;
println!("Sections:\n");
for sh in elf.section_headers()? {
let name = elf.string(sh.name as usize)?.to_str().unwrap();
let name = elf.sh_string(sh.name)?.to_string();
println!("{name:20} {:5} {:?}", sh.size, sh.r#type);
}
println!("Relocations:");
println!("Relocations:\n");
println!("{:20} {:10} {}", "Section", "Symbol", "Relocation");
let mut has_puts = false;
for (sh, rela) in elf.relas()? {
let section_name = elf.string(sh.name as usize)?.to_str().unwrap();
println!("{section_name:20} {:?}", rela);
let section_name = elf.sh_string(sh.name)?.to_string();
let sym = elf.symbol(rela.info.sym())?;
let sym_name = elf.string(sym.name)?.to_string();
println!("{section_name:20} {sym_name:10} {rela:?}");
if sym_name == "puts" {
has_puts = true;
}
}
assert!(has_puts, "puts symbol not found");
Ok(())
}
}

View file

@ -1 +1,2 @@
pub mod raw;
pub mod consts;
pub mod defs;