mirror of
https://github.com/Noratrieb/elven-forest.git
synced 2026-01-14 18:55:01 +01:00
write me
This commit is contained in:
parent
31fe171557
commit
6ae845a9b3
8 changed files with 448 additions and 82 deletions
BIN
a.out
Executable file
BIN
a.out
Executable file
Binary file not shown.
|
|
@ -3,8 +3,7 @@ use std::{fmt::Display, fs::File};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use elven_parser::{
|
use elven_parser::{
|
||||||
consts::{self as c, DynamicTag, ShType, SymbolVisibility, RX86_64},
|
consts::{self as c, DynamicTag, ShType, SymbolVisibility, RX86_64},
|
||||||
defs::{Addr, Elf, Sym, SymInfo},
|
read::{Addr, ElfReadError, ElfReader, Sym, SymInfo},
|
||||||
ElfParseError,
|
|
||||||
};
|
};
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
use tabled::{object::Rows, Disable, Style, Table, Tabled};
|
use tabled::{object::Rows, Disable, Style, Table, Tabled};
|
||||||
|
|
@ -63,7 +62,7 @@ fn print_file(path: &str) -> anyhow::Result<()> {
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
let mmap = unsafe { Mmap::map(&file) }?;
|
let mmap = unsafe { Mmap::map(&file) }?;
|
||||||
|
|
||||||
let elf = Elf::new(&mmap)?;
|
let elf = ElfReader::new(&mmap)?;
|
||||||
|
|
||||||
println!("\nHeader");
|
println!("\nHeader");
|
||||||
|
|
||||||
|
|
@ -98,7 +97,7 @@ fn print_file(path: &str) -> anyhow::Result<()> {
|
||||||
offset: sh.offset.0,
|
offset: sh.offset.0,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, ElfParseError>>()?;
|
.collect::<Result<Vec<_>, ElfReadError>>()?;
|
||||||
|
|
||||||
print_table(Table::new(sections));
|
print_table(Table::new(sections));
|
||||||
|
|
||||||
|
|
@ -125,7 +124,7 @@ fn print_file(path: &str) -> anyhow::Result<()> {
|
||||||
value: sym.value,
|
value: sym.value,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, ElfParseError>>()?;
|
.collect::<Result<Vec<_>, ElfReadError>>()?;
|
||||||
|
|
||||||
print_table(Table::new(symbols));
|
print_table(Table::new(symbols));
|
||||||
|
|
||||||
|
|
@ -152,7 +151,7 @@ fn print_file(path: &str) -> anyhow::Result<()> {
|
||||||
addend,
|
addend,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, ElfParseError>>()?;
|
.collect::<Result<Vec<_>, ElfReadError>>()?;
|
||||||
|
|
||||||
print_table(Table::new(relas));
|
print_table(Table::new(relas));
|
||||||
|
|
||||||
|
|
@ -171,7 +170,7 @@ fn print_file(path: &str) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sym_display_name(elf: Elf<'_>, sym: &Sym) -> Result<String, ElfParseError> {
|
fn sym_display_name(elf: ElfReader<'_>, sym: &Sym) -> Result<String, ElfReadError> {
|
||||||
Ok(if sym.info.r#type() == c::STT_SECTION {
|
Ok(if sym.info.r#type() == c::STT_SECTION {
|
||||||
elf.sh_string(elf.section_header(sym.shndx)?.name)?
|
elf.sh_string(elf.section_header(sym.shndx)?.name)?
|
||||||
.to_string()
|
.to_string()
|
||||||
|
|
|
||||||
|
|
@ -124,14 +124,18 @@ pub const EI_PAD: usize = 9; /* Byte index of padding bytes */
|
||||||
|
|
||||||
pub const EI_NIDENT: usize = 16;
|
pub const EI_NIDENT: usize = 16;
|
||||||
|
|
||||||
pub const ET_NONE: u16 = 0;
|
const_group_with_fmt! {
|
||||||
pub const ET_REL: u16 = 1;
|
pub struct Type(u16): "type"
|
||||||
pub const ET_EXEC: u16 = 2;
|
|
||||||
pub const ET_DYN: u16 = 3;
|
pub const ET_NONE = 0;
|
||||||
pub const ET_CORE: u16 = 4;
|
pub const ET_REL = 1;
|
||||||
|
pub const ET_EXEC = 2;
|
||||||
|
pub const ET_DYN = 3;
|
||||||
|
pub const ET_CORE = 4;
|
||||||
|
}
|
||||||
|
|
||||||
const_group_with_fmt! {
|
const_group_with_fmt! {
|
||||||
pub struct Machine(u16): "Machine"
|
pub struct Machine(u16): "machine"
|
||||||
|
|
||||||
pub const EM_NONE = 0; /* No machine */
|
pub const EM_NONE = 0; /* No machine */
|
||||||
pub const EM_X86_64 = 62; /* AMD x86-64 architecture */
|
pub const EM_X86_64 = 62; /* AMD x86-64 architecture */
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{
|
||||||
slice::SliceIndex,
|
slice::SliceIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ElfParseError, Result};
|
use crate::read::{ElfReadError, Result};
|
||||||
|
|
||||||
macro_rules! define_idx {
|
macro_rules! define_idx {
|
||||||
(
|
(
|
||||||
|
|
@ -95,6 +95,6 @@ impl<T> ElfIndexExt for [T] {
|
||||||
) -> Result<&<I::SliceIdx as SliceIndex<Self>>::Output> {
|
) -> Result<&<I::SliceIdx as SliceIndex<Self>>::Output> {
|
||||||
let bound = idx.bound();
|
let bound = idx.bound();
|
||||||
self.get(idx.to_slice_idx())
|
self.get(idx.to_slice_idx())
|
||||||
.ok_or(ElfParseError::IndexOutOfBounds(msg, bound))
|
.ok_or(ElfReadError::IndexOutOfBounds(msg, bound))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,6 @@
|
||||||
#![allow(clippy::must_use_candidate, clippy::missing_errors_doc)]
|
#![allow(clippy::must_use_candidate, clippy::missing_errors_doc)]
|
||||||
|
|
||||||
use consts::{DynamicTag, ShType};
|
|
||||||
|
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod defs;
|
|
||||||
mod idx;
|
mod idx;
|
||||||
|
pub mod read;
|
||||||
#[derive(Debug, Clone, thiserror::Error)]
|
pub mod write;
|
||||||
pub enum ElfParseError {
|
|
||||||
#[error("The file is too small. Expected at least {0} bytes, found {1} bytes")]
|
|
||||||
FileTooSmall(usize, usize),
|
|
||||||
#[error("The input is not aligned in memory. Expected align {0}, found align {1}")]
|
|
||||||
UnalignedInput(usize, usize),
|
|
||||||
#[error("The magic of the file did not match. Maybe it's not an ELF file?. Found {0:x?}")]
|
|
||||||
WrongMagic([u8; 4]),
|
|
||||||
#[error("A program header entry has a different size than expected. Expected {0}, found {1}")]
|
|
||||||
InvalidPhEntSize(usize, usize),
|
|
||||||
#[error("A section header entry has a different size than expected. Expected {0}, found {1}")]
|
|
||||||
InvalidShEntSize(usize, usize),
|
|
||||||
#[error("The string table section is marked as UNDEF")]
|
|
||||||
StrTableSectionNotPresent,
|
|
||||||
#[error("An index is out of bounds: {0}: {1}")]
|
|
||||||
IndexOutOfBounds(&'static str, usize),
|
|
||||||
#[error("String in string table does not end with a nul terminator: String offset: {0}")]
|
|
||||||
NoStringNulTerm(usize),
|
|
||||||
#[error("The {0} section was not found")]
|
|
||||||
SectionTypeNotFound(ShType),
|
|
||||||
#[error("The section with the name {0:?} was not found")]
|
|
||||||
SectionNotFound(std::result::Result<String, Vec<u8>>),
|
|
||||||
#[error("Dynamic entry not found: {0}")]
|
|
||||||
DynEntryNotFound(DynamicTag),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, ElfParseError>;
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
consts::{self as c, DynamicTag, ShType},
|
consts::{self as c, DynamicTag, ShType},
|
||||||
idx::{define_idx, ElfIndexExt, ToIdxUsize},
|
idx::{define_idx, ElfIndexExt, ToIdxUsize},
|
||||||
ElfParseError, Result,
|
|
||||||
};
|
};
|
||||||
use bstr::BStr;
|
use bstr::BStr;
|
||||||
|
|
||||||
|
|
@ -17,9 +16,36 @@ use std::{
|
||||||
|
|
||||||
use bytemuck::{Pod, PodCastError, Zeroable};
|
use bytemuck::{Pod, PodCastError, Zeroable};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, thiserror::Error)]
|
||||||
|
pub enum ElfReadError {
|
||||||
|
#[error("An index into {2} is out of bounds. Expected at least {0} bytes, found {1} bytes")]
|
||||||
|
RegionOutOfBounds(usize, usize, String),
|
||||||
|
#[error("The input is not aligned in memory. Expected align {0}, found align {1}")]
|
||||||
|
UnalignedInput(usize, usize),
|
||||||
|
#[error("The magic of the file did not match. Maybe it's not an ELF file?. Found {0:x?}")]
|
||||||
|
WrongMagic([u8; 4]),
|
||||||
|
#[error("A program header entry has a different size than expected. Expected {0}, found {1}")]
|
||||||
|
InvalidPhEntSize(usize, usize),
|
||||||
|
#[error("A section header entry has a different size than expected. Expected {0}, found {1}")]
|
||||||
|
InvalidShEntSize(usize, usize),
|
||||||
|
#[error("The string table section is marked as UNDEF")]
|
||||||
|
StrTableSectionNotPresent,
|
||||||
|
#[error("An index is out of bounds: {0}: {1}")]
|
||||||
|
IndexOutOfBounds(&'static str, usize),
|
||||||
|
#[error("String in string table does not end with a nul terminator: String offset: {0}")]
|
||||||
|
NoStringNulTerm(usize),
|
||||||
|
#[error("The {0} section was not found")]
|
||||||
|
SectionTypeNotFound(ShType),
|
||||||
|
#[error("The {0} with the name {1:?} was not found")]
|
||||||
|
NotFoundByName(&'static str, std::result::Result<String, Vec<u8>>),
|
||||||
|
#[error("Dynamic entry not found: {0}")]
|
||||||
|
DynEntryNotFound(DynamicTag),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, ElfReadError>;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
|
||||||
pub struct Addr(pub u64);
|
pub struct Addr(pub u64);
|
||||||
|
|
||||||
impl Debug for Addr {
|
impl Debug for Addr {
|
||||||
|
|
@ -58,7 +84,7 @@ define_idx! {
|
||||||
|
|
||||||
/// A raw ELF. Does not come with cute ears for now.
|
/// A raw ELF. Does not come with cute ears for now.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Elf<'a> {
|
pub struct ElfReader<'a> {
|
||||||
pub data: &'a [u8],
|
pub data: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,7 +92,7 @@ pub struct Elf<'a> {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ElfHeader {
|
pub struct ElfHeader {
|
||||||
pub ident: ElfIdent,
|
pub ident: ElfIdent,
|
||||||
pub r#type: u16,
|
pub r#type: c::Type,
|
||||||
pub machine: c::Machine,
|
pub machine: c::Machine,
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
pub entry: Addr,
|
pub entry: Addr,
|
||||||
|
|
@ -81,6 +107,16 @@ pub struct ElfHeader {
|
||||||
pub shstrndex: c::SectionIdx,
|
pub shstrndex: c::SectionIdx,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const HEADER_ENTRY_OFFSET: usize = 24;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn elf_header_entry_offset() {
|
||||||
|
let mut header = ElfHeader::zeroed();
|
||||||
|
let buf = bytemuck::cast_mut::<ElfHeader, [u64; mem::size_of::<ElfHeader>() / 8]>(&mut header);
|
||||||
|
buf[HEADER_ENTRY_OFFSET / 8] = 0x001122334455667788;
|
||||||
|
assert_eq!(header.entry, Addr(0x001122334455667788));
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ElfIdent {
|
pub struct ElfIdent {
|
||||||
|
|
@ -202,25 +238,25 @@ pub struct Dyn {
|
||||||
pub val: u64,
|
pub val: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Elf<'a> {
|
impl<'a> ElfReader<'a> {
|
||||||
pub fn new(data: &'a [u8]) -> Result<Self> {
|
pub fn new(data: &'a [u8]) -> Result<Self> {
|
||||||
let magic = data[..c::SELFMAG].try_into().map_err(|_| {
|
let magic = data[..c::SELFMAG].try_into().map_err(|_| {
|
||||||
let mut padded = [0, 0, 0, 0];
|
let mut padded = [0, 0, 0, 0];
|
||||||
padded.copy_from_slice(data);
|
padded.copy_from_slice(data);
|
||||||
ElfParseError::WrongMagic(padded)
|
ElfReadError::WrongMagic(padded)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if magic != *c::ELFMAG {
|
if magic != *c::ELFMAG {
|
||||||
return Err(ElfParseError::WrongMagic(magic));
|
return Err(ElfReadError::WrongMagic(magic));
|
||||||
}
|
}
|
||||||
|
|
||||||
let elf = Elf { data };
|
let elf = ElfReader { data };
|
||||||
|
|
||||||
Ok(elf)
|
Ok(elf)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn header(&self) -> Result<&ElfHeader> {
|
pub fn header(&self) -> Result<&ElfHeader> {
|
||||||
load_ref(self.data)
|
load_ref(self.data, "header")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn program_headers(&self) -> Result<&[Phdr]> {
|
pub fn program_headers(&self) -> Result<&[Phdr]> {
|
||||||
|
|
@ -233,7 +269,7 @@ impl<'a> Elf<'a> {
|
||||||
let expected_ent_size = mem::size_of::<Phdr>();
|
let expected_ent_size = mem::size_of::<Phdr>();
|
||||||
let actual_ent_size = usize::from(header.phentsize);
|
let actual_ent_size = usize::from(header.phentsize);
|
||||||
if actual_ent_size != expected_ent_size {
|
if actual_ent_size != expected_ent_size {
|
||||||
return Err(ElfParseError::InvalidPhEntSize(
|
return Err(ElfReadError::InvalidPhEntSize(
|
||||||
expected_ent_size,
|
expected_ent_size,
|
||||||
actual_ent_size,
|
actual_ent_size,
|
||||||
));
|
));
|
||||||
|
|
@ -242,6 +278,7 @@ impl<'a> Elf<'a> {
|
||||||
load_slice(
|
load_slice(
|
||||||
self.data.get_elf(header.phoff.., "program header offset")?,
|
self.data.get_elf(header.phoff.., "program header offset")?,
|
||||||
header.phnum.into(),
|
header.phnum.into(),
|
||||||
|
"program headers",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,14 +292,15 @@ impl<'a> Elf<'a> {
|
||||||
let expected_ent_size = mem::size_of::<Shdr>();
|
let expected_ent_size = mem::size_of::<Shdr>();
|
||||||
let actual_ent_size = usize::from(header.shentsize);
|
let actual_ent_size = usize::from(header.shentsize);
|
||||||
if actual_ent_size != expected_ent_size {
|
if actual_ent_size != expected_ent_size {
|
||||||
return Err(ElfParseError::InvalidPhEntSize(
|
return Err(ElfReadError::InvalidPhEntSize(
|
||||||
expected_ent_size,
|
expected_ent_size,
|
||||||
actual_ent_size,
|
actual_ent_size,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
load_slice(
|
load_slice(
|
||||||
self.data.get_elf(header.shoff.., "sectoin header offset")?,
|
self.data.get_elf(header.shoff.., "section header offset")?,
|
||||||
header.shnum.into(),
|
header.shnum.into(),
|
||||||
|
"section headers",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,7 +317,8 @@ impl<'a> Elf<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let name = name.to_vec();
|
let name = name.to_vec();
|
||||||
Err(ElfParseError::SectionNotFound(
|
Err(ElfReadError::NotFoundByName(
|
||||||
|
"section",
|
||||||
string::String::from_utf8(name).map_err(FromUtf8Error::into_bytes),
|
string::String::from_utf8(name).map_err(FromUtf8Error::into_bytes),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
@ -288,7 +327,7 @@ impl<'a> Elf<'a> {
|
||||||
self.section_headers()?
|
self.section_headers()?
|
||||||
.iter()
|
.iter()
|
||||||
.find(|sh| sh.r#type == ty)
|
.find(|sh| sh.r#type == ty)
|
||||||
.ok_or(ElfParseError::SectionTypeNotFound(ShType(ty)))
|
.ok_or(ElfReadError::SectionTypeNotFound(ShType(ty)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn section_content(&self, sh: &Shdr) -> Result<&[u8]> {
|
pub fn section_content(&self, sh: &Shdr) -> Result<&[u8]> {
|
||||||
|
|
@ -306,7 +345,7 @@ impl<'a> Elf<'a> {
|
||||||
let shstrndex = header.shstrndex;
|
let shstrndex = header.shstrndex;
|
||||||
|
|
||||||
if shstrndex == c::SHN_UNDEF {
|
if shstrndex == c::SHN_UNDEF {
|
||||||
return Err(ElfParseError::StrTableSectionNotPresent);
|
return Err(ElfReadError::StrTableSectionNotPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if shstrndex >= c::SHN_LORESERVE {
|
if shstrndex >= c::SHN_LORESERVE {
|
||||||
|
|
@ -334,7 +373,7 @@ impl<'a> Elf<'a> {
|
||||||
let end = indexed
|
let end = indexed
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&c| c == b'\0')
|
.position(|&c| c == b'\0')
|
||||||
.ok_or(ElfParseError::NoStringNulTerm(idx.to_idx_usize()))?;
|
.ok_or(ElfReadError::NoStringNulTerm(idx.to_idx_usize()))?;
|
||||||
Ok(BStr::new(&indexed[..end]))
|
Ok(BStr::new(&indexed[..end]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,7 +383,7 @@ impl<'a> Elf<'a> {
|
||||||
let end = indexed
|
let end = indexed
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&c| c == b'\0')
|
.position(|&c| c == b'\0')
|
||||||
.ok_or(ElfParseError::NoStringNulTerm(idx.to_idx_usize()))?;
|
.ok_or(ElfReadError::NoStringNulTerm(idx.to_idx_usize()))?;
|
||||||
Ok(BStr::new(&indexed[..end]))
|
Ok(BStr::new(&indexed[..end]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,7 +400,7 @@ impl<'a> Elf<'a> {
|
||||||
let end = indexed
|
let end = indexed
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&c| c == b'\0')
|
.position(|&c| c == b'\0')
|
||||||
.ok_or(ElfParseError::NoStringNulTerm(idx.to_idx_usize()))?;
|
.ok_or(ElfReadError::NoStringNulTerm(idx.to_idx_usize()))?;
|
||||||
Ok(BStr::new(&indexed[..end]))
|
Ok(BStr::new(&indexed[..end]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -372,7 +411,11 @@ impl<'a> Elf<'a> {
|
||||||
.filter(|sh| sh.r#type == c::SHT_RELA)
|
.filter(|sh| sh.r#type == c::SHT_RELA)
|
||||||
.map(|sh| {
|
.map(|sh| {
|
||||||
let content = self.section_content(sh)?;
|
let content = self.section_content(sh)?;
|
||||||
let relas = load_slice::<Rela>(content, content.len() / mem::size_of::<Rela>())?;
|
let relas = load_slice::<Rela>(
|
||||||
|
content,
|
||||||
|
content.len() / mem::size_of::<Rela>(),
|
||||||
|
"relocations",
|
||||||
|
)?;
|
||||||
Ok((sh, relas))
|
Ok((sh, relas))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?
|
.collect::<Result<Vec<_>>>()?
|
||||||
|
|
@ -385,13 +428,27 @@ impl<'a> Elf<'a> {
|
||||||
|
|
||||||
let data = self.section_content(sh)?;
|
let data = self.section_content(sh)?;
|
||||||
|
|
||||||
load_slice(data, data.len() / mem::size_of::<Sym>())
|
load_slice(data, data.len() / mem::size_of::<Sym>(), "symbols")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn symbol(&self, idx: SymIdx) -> Result<&Sym> {
|
pub fn symbol(&self, idx: SymIdx) -> Result<&Sym> {
|
||||||
self.symbols()?.get_elf(idx, "symbol index")
|
self.symbols()?.get_elf(idx, "symbol index")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn symbol_by_name(&self, name: &[u8]) -> Result<&Sym> {
|
||||||
|
for symbol in self.symbols()? {
|
||||||
|
let sym_name = self.string(symbol.name)?;
|
||||||
|
if sym_name == name {
|
||||||
|
return Ok(symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ElfReadError::NotFoundByName(
|
||||||
|
"symbol",
|
||||||
|
string::String::from_utf8(name.to_vec()).map_err(FromUtf8Error::into_bytes),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dyn_symbols(&self) -> Result<&[Sym]> {
|
pub fn dyn_symbols(&self) -> Result<&[Sym]> {
|
||||||
let addr = self.dyn_entry_by_tag(c::DT_SYMTAB)?;
|
let addr = self.dyn_entry_by_tag(c::DT_SYMTAB)?;
|
||||||
let size = self.dyn_entry_by_tag(c::DT_SYMENT)?;
|
let size = self.dyn_entry_by_tag(c::DT_SYMENT)?;
|
||||||
|
|
@ -400,7 +457,7 @@ impl<'a> Elf<'a> {
|
||||||
|
|
||||||
let data = self.dyn_content(addr.val, size.val)?;
|
let data = self.dyn_content(addr.val, size.val)?;
|
||||||
|
|
||||||
load_slice(data, data.len() / mem::size_of::<Sym>())
|
load_slice(data, data.len() / mem::size_of::<Sym>(), "dyn symbols")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dyn_symbol(&self, idx: SymIdx) -> Result<&Sym> {
|
pub fn dyn_symbol(&self, idx: SymIdx) -> Result<&Sym> {
|
||||||
|
|
@ -411,14 +468,14 @@ impl<'a> Elf<'a> {
|
||||||
let sh = self.section_header_by_name(b".dynamic")?;
|
let sh = self.section_header_by_name(b".dynamic")?;
|
||||||
let data = self.section_content(sh)?;
|
let data = self.section_content(sh)?;
|
||||||
|
|
||||||
load_slice(data, data.len() / mem::size_of::<Dyn>())
|
load_slice(data, data.len() / mem::size_of::<Dyn>(), "dyn entries")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dyn_entry_by_tag(&self, tag: u64) -> Result<&Dyn> {
|
pub fn dyn_entry_by_tag(&self, tag: u64) -> Result<&Dyn> {
|
||||||
self.dyn_entries()?
|
self.dyn_entries()?
|
||||||
.iter()
|
.iter()
|
||||||
.find(|dy| dy.tag == tag)
|
.find(|dy| dy.tag == tag)
|
||||||
.ok_or(ElfParseError::DynEntryNotFound(DynamicTag(tag)))
|
.ok_or(ElfReadError::DynEntryNotFound(DynamicTag(tag)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dyn_content(&self, addr: u64, size: u64) -> Result<&[u8]> {
|
pub fn dyn_content(&self, addr: u64, size: u64) -> Result<&[u8]> {
|
||||||
|
|
@ -428,16 +485,24 @@ impl<'a> Elf<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_ref<T: Pod>(data: &[u8]) -> Result<&T> {
|
fn load_ref<'a, T: Pod>(data: &'a [u8], kind: impl Into<String>) -> Result<&'a T> {
|
||||||
load_slice(data, 1).map(|slice| &slice[0])
|
load_slice(data, 1, kind).map(|slice| &slice[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_slice<T: Pod>(data: &[u8], amount_of_elems: usize) -> Result<&[T]> {
|
fn load_slice<'a, T: Pod>(
|
||||||
|
data: &'a [u8],
|
||||||
|
amount_of_elems: usize,
|
||||||
|
kind: impl Into<String>,
|
||||||
|
) -> Result<&'a [T]> {
|
||||||
let size = mem::size_of::<T>() * amount_of_elems;
|
let size = mem::size_of::<T>() * amount_of_elems;
|
||||||
let align = mem::align_of::<T>();
|
let align = mem::align_of::<T>();
|
||||||
|
|
||||||
if data.len() < size {
|
if data.len() < size {
|
||||||
return Err(ElfParseError::FileTooSmall(size, data.len()));
|
return Err(ElfReadError::RegionOutOfBounds(
|
||||||
|
size,
|
||||||
|
data.len(),
|
||||||
|
kind.into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let data_addr = (data as *const [u8]).cast::<u8>() as usize;
|
let data_addr = (data as *const [u8]).cast::<u8>() as usize;
|
||||||
|
|
@ -452,7 +517,7 @@ fn load_slice<T: Pod>(data: &[u8], amount_of_elems: usize) -> Result<&[T]> {
|
||||||
unreachable!("already checked for these errors: {e}")
|
unreachable!("already checked for these errors: {e}")
|
||||||
}
|
}
|
||||||
PodCastError::TargetAlignmentGreaterAndInputNotAligned => {
|
PodCastError::TargetAlignmentGreaterAndInputNotAligned => {
|
||||||
ElfParseError::UnalignedInput(align, data_align)
|
ElfReadError::UnalignedInput(align, data_align)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -485,7 +550,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn rust_hello_world_bin() -> super::Result<()> {
|
fn rust_hello_world_bin() -> super::Result<()> {
|
||||||
let file = load_test_file("hello_world");
|
let file = load_test_file("hello_world");
|
||||||
let elf = Elf::new(&file)?;
|
let elf = ElfReader::new(&file)?;
|
||||||
let header = elf.header()?;
|
let header = elf.header()?;
|
||||||
|
|
||||||
assert_eq!(header.ident.class, c::ELFCLASS64);
|
assert_eq!(header.ident.class, c::ELFCLASS64);
|
||||||
|
|
@ -507,8 +572,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn c_hello_world_object() -> super::Result<()> {
|
fn c_hello_world_object() -> super::Result<()> {
|
||||||
let file = load_test_file("hello_world_obj");
|
let file = load_test_file("hello_world_obj.o");
|
||||||
let elf = Elf::new(&file)?;
|
let elf = ElfReader::new(&file)?;
|
||||||
let header = elf.header()?;
|
let header = elf.header()?;
|
||||||
|
|
||||||
assert_eq!(header.ident.class, c::ELFCLASS64);
|
assert_eq!(header.ident.class, c::ELFCLASS64);
|
||||||
256
elven-parser/src/write.rs
Normal file
256
elven-parser/src/write.rs
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
use crate::consts::{Machine, SectionIdx, ShType, Type, SHT_NULL, SHT_STRTAB};
|
||||||
|
use crate::read::{self, Addr, ElfIdent, Offset, ShStringIdx};
|
||||||
|
use std::io;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum WriteElfError {
|
||||||
|
#[error("Too many {0}")]
|
||||||
|
TooMany(&'static str),
|
||||||
|
#[error("Writer IO error")]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, WriteElfError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ElfWriter {
|
||||||
|
header: read::ElfHeader,
|
||||||
|
entry: Entry,
|
||||||
|
sections_headers: Vec<Section>,
|
||||||
|
programs_headers: Vec<ProgramHeader>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Header {
|
||||||
|
pub ident: ElfIdent,
|
||||||
|
pub r#type: Type,
|
||||||
|
pub machine: Machine,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Entry {
|
||||||
|
pub section: SectionIdx,
|
||||||
|
pub rel_offset: Offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Section {
|
||||||
|
pub name: read::ShStringIdx,
|
||||||
|
pub r#type: ShType,
|
||||||
|
pub flags: u64,
|
||||||
|
pub fixed_entsize: Option<NonZeroU64>,
|
||||||
|
pub content: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ProgramHeader {
|
||||||
|
pub content: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const SH_STRTAB: usize = 1;
|
||||||
|
|
||||||
|
impl ElfWriter {
|
||||||
|
pub fn new(header: Header) -> Self {
|
||||||
|
let header = read::ElfHeader {
|
||||||
|
ident: header.ident,
|
||||||
|
r#type: header.r#type,
|
||||||
|
machine: header.machine,
|
||||||
|
version: 1,
|
||||||
|
entry: Addr(u64::MAX),
|
||||||
|
phoff: Offset(0),
|
||||||
|
shoff: Offset(0),
|
||||||
|
flags: u32::MAX,
|
||||||
|
ehsize: size_of::<read::ElfHeader> as u16,
|
||||||
|
phentsize: size_of::<read::Phdr>() as u16,
|
||||||
|
phnum: u16::MAX,
|
||||||
|
shentsize: size_of::<read::Shdr>() as u16,
|
||||||
|
shnum: u16::MAX,
|
||||||
|
// Set below.
|
||||||
|
shstrndex: SectionIdx(SH_STRTAB as u16),
|
||||||
|
};
|
||||||
|
|
||||||
|
let null_section = Section {
|
||||||
|
// The null string.
|
||||||
|
name: read::ShStringIdx(0),
|
||||||
|
r#type: ShType(SHT_NULL),
|
||||||
|
flags: 0,
|
||||||
|
content: Vec::new(),
|
||||||
|
fixed_entsize: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let shstrtab = Section {
|
||||||
|
// The first string which happens to be .shstrtab below.
|
||||||
|
name: read::ShStringIdx(1),
|
||||||
|
r#type: ShType(SHT_STRTAB),
|
||||||
|
flags: 0,
|
||||||
|
// Set up the null string and also the .shstrtab, our section.
|
||||||
|
content: b"\0.shstrtab\0".to_vec(),
|
||||||
|
fixed_entsize: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
header,
|
||||||
|
entry: Entry {
|
||||||
|
section: SectionIdx(0),
|
||||||
|
rel_offset: Offset(0),
|
||||||
|
},
|
||||||
|
sections_headers: vec![null_section, shstrtab],
|
||||||
|
programs_headers: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_entry(&mut self, entry: Entry) {
|
||||||
|
self.entry = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_sh_string(&mut self, content: &[u8]) -> ShStringIdx {
|
||||||
|
let shstrtab = &mut self.sections_headers[SH_STRTAB];
|
||||||
|
let idx = shstrtab.content.len();
|
||||||
|
shstrtab.content.extend(content);
|
||||||
|
shstrtab.content.push(0);
|
||||||
|
ShStringIdx(idx as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_section(&mut self, section: Section) {
|
||||||
|
self.sections_headers.push(section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod writing {
|
||||||
|
use bytemuck::Pod;
|
||||||
|
|
||||||
|
use crate::read::{Addr, ElfHeader, Offset, Shdr, HEADER_ENTRY_OFFSET};
|
||||||
|
use std::{io::Write, mem::size_of, num::NonZeroU64};
|
||||||
|
|
||||||
|
use super::{ElfWriter, Result, WriteElfError};
|
||||||
|
|
||||||
|
impl ElfWriter {
|
||||||
|
pub fn write(&self) -> Result<Vec<u8>> {
|
||||||
|
let mut output = Vec::new();
|
||||||
|
|
||||||
|
let mut current_known_position = 0;
|
||||||
|
|
||||||
|
let mut header = self.header;
|
||||||
|
|
||||||
|
header.shnum = self
|
||||||
|
.sections_headers
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| WriteElfError::TooMany("sections"))?;
|
||||||
|
|
||||||
|
header.phnum = self
|
||||||
|
.programs_headers
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| WriteElfError::TooMany("program headers"))?;
|
||||||
|
|
||||||
|
// We know the size of the header.
|
||||||
|
current_known_position += size_of::<ElfHeader>() as u64;
|
||||||
|
|
||||||
|
// We put the section headers directly after the header.
|
||||||
|
if !self.sections_headers.is_empty() {
|
||||||
|
header.shoff = Offset(current_known_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There will be all the section headers right after the header.
|
||||||
|
current_known_position += (header.shentsize * header.shnum) as u64;
|
||||||
|
|
||||||
|
// We put the program headers directly after the section headers.
|
||||||
|
if !self.programs_headers.is_empty() {
|
||||||
|
header.phoff = Offset(current_known_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There will be all the program headers right after the section headers.
|
||||||
|
current_known_position += (header.phentsize * header.phnum) as u64;
|
||||||
|
|
||||||
|
write_pod(&header, &mut output);
|
||||||
|
|
||||||
|
for (sh_idx, section) in self.sections_headers.iter().enumerate() {
|
||||||
|
let header = Shdr {
|
||||||
|
name: section.name,
|
||||||
|
r#type: section.r#type,
|
||||||
|
flags: section.flags,
|
||||||
|
addr: Addr(0),
|
||||||
|
offset: Offset(current_known_position),
|
||||||
|
size: section.content.len() as u64,
|
||||||
|
link: 0,
|
||||||
|
info: 0,
|
||||||
|
addralign: 0,
|
||||||
|
entsize: section.fixed_entsize.map(NonZeroU64::get).unwrap_or(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
if sh_idx == self.entry.section.0 as usize {
|
||||||
|
let base = current_known_position;
|
||||||
|
let entry = base + self.entry.rel_offset.0;
|
||||||
|
let entry_pos = &mut output[HEADER_ENTRY_OFFSET..][..size_of::<u64>()];
|
||||||
|
let entry_ref = bytemuck::cast_slice_mut::<u8, u64>(entry_pos);
|
||||||
|
entry_ref[0] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will write the content for this section at that offset and also make sure to align the next one.
|
||||||
|
// FIXME: Align to the alignment of the next section.
|
||||||
|
current_known_position += align_up(section.content.len() as u64, 8);
|
||||||
|
|
||||||
|
write_pod(&header, &mut output);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(self.programs_headers.len(), 0); // FIXME: yeah
|
||||||
|
|
||||||
|
for section in &self.sections_headers {
|
||||||
|
let section_size = section.content.len() as u64;
|
||||||
|
let aligned_size = align_up(section_size, 8);
|
||||||
|
let padding = aligned_size - section_size;
|
||||||
|
|
||||||
|
output.write_all(§ion.content)?;
|
||||||
|
for _ in 0..padding {
|
||||||
|
output.write_all(&[0u8])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_pod<T: Pod>(data: &T, output: &mut Vec<u8>) {
|
||||||
|
let data = std::slice::from_ref(data);
|
||||||
|
write_pod_slice(data, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_pod_slice<T: Pod>(data: &[T], output: &mut Vec<u8>) {
|
||||||
|
let data = bytemuck::cast_slice::<T, u8>(data);
|
||||||
|
output.extend(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn align_up(n: u64, align: u64) -> u64 {
|
||||||
|
// n=0b0101, align=0b0100
|
||||||
|
let required_mask = align - 1; // 0b0011
|
||||||
|
let masked = n & required_mask; // 0b0001
|
||||||
|
|
||||||
|
if masked == 0 {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
let next_down = n - masked; // 0b0100
|
||||||
|
next_down + align // 0b0110
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::align_up;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn align_up_correct() {
|
||||||
|
assert_eq!(align_up(0b0101, 0b0010), 0b0110);
|
||||||
|
assert_eq!(align_up(16, 8), 16);
|
||||||
|
assert_eq!(align_up(15, 8), 16);
|
||||||
|
assert_eq!(align_up(14, 8), 16);
|
||||||
|
assert_eq!(align_up(11, 8), 16);
|
||||||
|
assert_eq!(align_up(10, 8), 16);
|
||||||
|
assert_eq!(align_up(9, 8), 16);
|
||||||
|
assert_eq!(align_up(8, 8), 8);
|
||||||
|
assert_eq!(align_up(0, 1), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,17 @@ extern crate tracing;
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use elven_parser::defs::Elf;
|
use elven_parser::{
|
||||||
|
consts::{self as c, ShType, SHT_PROGBITS},
|
||||||
|
read::{ElfIdent, ElfReader, Offset},
|
||||||
|
write::{self, ElfWriter, Entry, Section},
|
||||||
|
};
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
use std::{fs::File, path::PathBuf};
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::{BufWriter, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
pub struct Opts {
|
pub struct Opts {
|
||||||
|
|
@ -17,7 +25,8 @@ pub fn run(opts: Opts) -> Result<()> {
|
||||||
.objs
|
.objs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|path| {
|
.map(|path| {
|
||||||
let file = File::open(path).with_context(|| format!("opening {}", path.display()))?;
|
let file =
|
||||||
|
fs::File::open(path).with_context(|| format!("opening {}", path.display()))?;
|
||||||
unsafe {
|
unsafe {
|
||||||
Mmap::map(&file).with_context(|| format!("memory mapping {}", path.display()))
|
Mmap::map(&file).with_context(|| format!("memory mapping {}", path.display()))
|
||||||
}
|
}
|
||||||
|
|
@ -38,11 +47,73 @@ pub fn run(opts: Opts) -> Result<()> {
|
||||||
.iter()
|
.iter()
|
||||||
.zip(&opts.objs)
|
.zip(&opts.objs)
|
||||||
.map(|(mmap, path)| {
|
.map(|(mmap, path)| {
|
||||||
Elf::new(mmap).with_context(|| format!("parsing ELF file {}", path.display()))
|
ElfReader::new(mmap).with_context(|| format!("parsing ELF file {}", path.display()))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||||
|
|
||||||
let main_elf = elfs[0];
|
let elf = elfs[0];
|
||||||
|
|
||||||
|
let text_sh = elf.section_header_by_name(b".text")?;
|
||||||
|
let text_content = elf.section_content(text_sh)?;
|
||||||
|
|
||||||
|
let _start_sym = elf.symbol_by_name(b"_start")?;
|
||||||
|
|
||||||
|
let section = _start_sym.shndx;
|
||||||
|
|
||||||
|
let entry = Entry {
|
||||||
|
section,
|
||||||
|
rel_offset: Offset(_start_sym.value.0),
|
||||||
|
};
|
||||||
|
|
||||||
|
write_output(text_content, entry)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_output(text: &[u8], entry: Entry) -> Result<()> {
|
||||||
|
let ident = ElfIdent {
|
||||||
|
magic: *c::ELFMAG,
|
||||||
|
class: c::Class(c::ELFCLASS64),
|
||||||
|
data: c::Data(c::ELFDATA2LSB),
|
||||||
|
version: 1,
|
||||||
|
osabi: c::OsAbi(c::ELFOSABI_SYSV),
|
||||||
|
abiversion: 0,
|
||||||
|
_pad: [0; 7],
|
||||||
|
};
|
||||||
|
|
||||||
|
let header = write::Header {
|
||||||
|
ident,
|
||||||
|
r#type: c::Type(c::ET_DYN),
|
||||||
|
machine: c::Machine(c::EM_X86_64),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut write = ElfWriter::new(header);
|
||||||
|
|
||||||
|
let text_name = write.add_sh_string(b".text");
|
||||||
|
write.add_section(Section {
|
||||||
|
name: text_name,
|
||||||
|
r#type: ShType(SHT_PROGBITS),
|
||||||
|
flags: 0,
|
||||||
|
fixed_entsize: None,
|
||||||
|
content: text.to_vec(),
|
||||||
|
});
|
||||||
|
|
||||||
|
write.set_entry(entry);
|
||||||
|
|
||||||
|
let output = write.write().context("writing output file")?;
|
||||||
|
|
||||||
|
let mut output_file = fs::File::create("a.out").context("creating ./a.out")?;
|
||||||
|
BufWriter::new(&mut output_file).write_all(&output)?;
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut permissions = output_file.metadata()?.permissions();
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
let mode = permissions.mode();
|
||||||
|
permissions.set_mode(mode | 0o111);
|
||||||
|
};
|
||||||
|
output_file.set_permissions(permissions)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue