This commit is contained in:
nora 2023-02-21 19:51:17 +01:00
parent 177c869133
commit dc4cf750aa
4 changed files with 100 additions and 34 deletions

5
Cargo.lock generated
View file

@ -16,9 +16,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.2.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1"
dependencies = [ dependencies = [
"memchr", "memchr",
"once_cell", "once_cell",
@ -128,6 +128,7 @@ name = "elven-wald"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bstr",
"clap", "clap",
"elven-parser", "elven-parser",
"memmap2", "memmap2",

View file

@ -228,11 +228,11 @@ impl<'a> ElfReader<'a> {
Ok(elf) Ok(elf)
} }
pub fn header(&self) -> Result<&ElfHeader> { pub fn header(&self) -> Result<&'a ElfHeader> {
load_ref(self.data, "header") load_ref(self.data, "header")
} }
pub fn program_headers(&self) -> Result<&[Phdr]> { pub fn program_headers(&self) -> Result<&'a [Phdr]> {
let header = self.header()?; let header = self.header()?;
if header.phnum == 0 { if header.phnum == 0 {
@ -255,7 +255,7 @@ impl<'a> ElfReader<'a> {
) )
} }
pub fn section_headers(&self) -> Result<&[Shdr]> { pub fn section_headers(&self) -> Result<&'a [Shdr]> {
let header = self.header()?; let header = self.header()?;
if header.shnum == 0 { if header.shnum == 0 {
@ -277,12 +277,12 @@ impl<'a> ElfReader<'a> {
) )
} }
pub fn section_header(&self, idx: c::SectionIdx) -> Result<&Shdr> { pub fn section_header(&self, idx: c::SectionIdx) -> Result<&'a Shdr> {
let sections = self.section_headers()?; let sections = self.section_headers()?;
sections.get_elf(idx.usize(), "section number") sections.get_elf(idx.usize(), "section number")
} }
pub fn section_header_by_name(&self, name: &[u8]) -> Result<&Shdr> { pub fn section_header_by_name(&self, name: &[u8]) -> Result<&'a Shdr> {
let sections = self.section_headers()?; let sections = self.section_headers()?;
for sh in sections { for sh in sections {
if self.sh_string(sh.name)? == name { if self.sh_string(sh.name)? == name {
@ -296,14 +296,14 @@ impl<'a> ElfReader<'a> {
)) ))
} }
pub fn section_header_by_type(&self, ty: u32) -> Result<&Shdr> { pub fn section_header_by_type(&self, ty: u32) -> Result<&'a Shdr> {
self.section_headers()? self.section_headers()?
.iter() .iter()
.find(|sh| sh.r#type == ty) .find(|sh| sh.r#type == ty)
.ok_or(ElfReadError::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<&'a [u8]> {
if sh.r#type.0 == c::SHT_NOBITS { if sh.r#type.0 == c::SHT_NOBITS {
return Ok(&[]); return Ok(&[]);
} }
@ -313,7 +313,7 @@ impl<'a> ElfReader<'a> {
.get_elf(..sh.size, "section size") .get_elf(..sh.size, "section size")
} }
pub fn sh_str_table(&self) -> Result<&[u8]> { pub fn sh_str_table(&self) -> Result<&'a [u8]> {
let header = self.header()?; let header = self.header()?;
let shstrndex = header.shstrndex; let shstrndex = header.shstrndex;
@ -335,12 +335,12 @@ impl<'a> ElfReader<'a> {
self.section_content(strtab_header) self.section_content(strtab_header)
} }
pub fn str_table(&self) -> Result<&[u8]> { pub fn str_table(&self) -> Result<&'a [u8]> {
let sh = self.section_header_by_name(b".strtab")?; let sh = self.section_header_by_name(b".strtab")?;
self.section_content(sh) self.section_content(sh)
} }
pub fn sh_string(&self, idx: ShStringIdx) -> Result<&BStr> { pub fn sh_string(&self, idx: ShStringIdx) -> Result<&'a BStr> {
let str_table = self.sh_str_table()?; let str_table = self.sh_str_table()?;
let indexed = str_table.get_elf(idx.., "string offset")?; let indexed = str_table.get_elf(idx.., "string offset")?;
let end = indexed let end = indexed
@ -350,7 +350,7 @@ impl<'a> ElfReader<'a> {
Ok(BStr::new(&indexed[..end])) Ok(BStr::new(&indexed[..end]))
} }
pub fn string(&self, idx: StringIdx) -> Result<&BStr> { pub fn string(&self, idx: StringIdx) -> Result<&'a BStr> {
let str_table = self.str_table()?; let str_table = self.str_table()?;
let indexed = str_table.get_elf(idx.., "string offset")?; let indexed = str_table.get_elf(idx.., "string offset")?;
let end = indexed let end = indexed
@ -360,7 +360,7 @@ impl<'a> ElfReader<'a> {
Ok(BStr::new(&indexed[..end])) Ok(BStr::new(&indexed[..end]))
} }
pub fn dyn_string(&self, idx: StringIdx) -> Result<&BStr> { pub fn dyn_string(&self, idx: StringIdx) -> Result<&'a BStr> {
let tab_addr = self.dyn_entry_by_tag(c::DT_STRTAB)?; let tab_addr = self.dyn_entry_by_tag(c::DT_STRTAB)?;
let tab_sz = self.dyn_entry_by_tag(c::DT_STRSZ)?; let tab_sz = self.dyn_entry_by_tag(c::DT_STRSZ)?;
@ -377,7 +377,7 @@ impl<'a> ElfReader<'a> {
Ok(BStr::new(&indexed[..end])) Ok(BStr::new(&indexed[..end]))
} }
pub fn relas(&self) -> Result<impl Iterator<Item = (&Shdr, &Rela)>> { pub fn relas(&self) -> Result<impl Iterator<Item = (&'a Shdr, &'a Rela)>> {
Ok(self Ok(self
.section_headers()? .section_headers()?
.iter() .iter()
@ -396,7 +396,7 @@ impl<'a> ElfReader<'a> {
.flat_map(|(sh, relas)| relas.iter().map(move |rela| (sh, rela)))) .flat_map(|(sh, relas)| relas.iter().map(move |rela| (sh, rela))))
} }
pub fn symbols(&self) -> Result<&[Sym]> { pub fn symbols(&self) -> Result<&'a [Sym]> {
let sh = self.section_header_by_type(c::SHT_SYMTAB)?; let sh = self.section_header_by_type(c::SHT_SYMTAB)?;
let data = self.section_content(sh)?; let data = self.section_content(sh)?;
@ -404,11 +404,11 @@ impl<'a> ElfReader<'a> {
load_slice(data, data.len() / mem::size_of::<Sym>(), "symbols") 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<&'a 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> { pub fn symbol_by_name(&self, name: &[u8]) -> Result<&'a Sym> {
for symbol in self.symbols()? { for symbol in self.symbols()? {
let sym_name = self.string(symbol.name)?; let sym_name = self.string(symbol.name)?;
if sym_name == name { if sym_name == name {
@ -422,7 +422,7 @@ impl<'a> ElfReader<'a> {
)) ))
} }
pub fn dyn_symbols(&self) -> Result<&[Sym]> { pub fn dyn_symbols(&self) -> Result<&'a [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)?;
@ -433,25 +433,25 @@ impl<'a> ElfReader<'a> {
load_slice(data, data.len() / mem::size_of::<Sym>(), "dyn symbols") 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<&'a Sym> {
dbg!(self.dyn_symbols()?).get_elf(idx, "symbol index") dbg!(self.dyn_symbols()?).get_elf(idx, "symbol index")
} }
pub fn dyn_entries(&self) -> Result<&[Dyn]> { pub fn dyn_entries(&self) -> Result<&'a [Dyn]> {
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>(), "dyn entries") 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<&'a Dyn> {
self.dyn_entries()? self.dyn_entries()?
.iter() .iter()
.find(|dy| dy.tag == tag) .find(|dy| dy.tag == tag)
.ok_or(ElfReadError::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<&'a [u8]> {
self.data self.data
.get_elf(addr.., "dyn content offset")? .get_elf(addr.., "dyn content offset")?
.get_elf(..size, "section size") .get_elf(..size, "section size")

View file

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.69" anyhow = "1.0.69"
bstr = "1.3.0"
clap = { version = "4.1.4", features = ["derive"] } clap = { version = "4.1.4", features = ["derive"] }
elven-parser = { path = "../elven-parser" } elven-parser = { path = "../elven-parser" }
memmap2 = "0.5.8" memmap2 = "0.5.8"

View file

@ -2,17 +2,20 @@
extern crate tracing; extern crate tracing;
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use bstr::BStr;
use clap::Parser; use clap::Parser;
use elven_parser::{ use elven_parser::{
consts::{self as c, PhFlags, SectionIdx, ShFlags, ShType, PT_LOAD, SHT_PROGBITS}, consts::{self as c, PhFlags, SectionIdx, ShFlags, ShType, PT_LOAD, SHN_UNDEF, SHT_PROGBITS},
read::{ElfIdent, ElfReader}, read::{ElfIdent, ElfReader},
write::{self, ElfWriter, ProgramHeader, Section, SectionRelativeAbsoluteAddr}, write::{self, ElfWriter, ProgramHeader, Section, SectionRelativeAbsoluteAddr},
Addr, Offset, Addr, Offset,
}; };
use memmap2::Mmap; use memmap2::Mmap;
use std::{ use std::{
fs, collections::{hash_map::Entry, HashMap},
fs::{self, File},
io::{BufWriter, Write}, io::{BufWriter, Write},
num::NonZeroU64,
path::PathBuf, path::PathBuf,
}; };
@ -21,6 +24,19 @@ pub struct Opts {
pub objs: Vec<PathBuf>, pub objs: Vec<PathBuf>,
} }
#[derive(Debug)]
struct SymDef<'a> {
name: &'a BStr,
defined_in: u32,
/// `shndx` from ELF
refers_to_section: SectionIdx,
}
struct LinkCtxt<'a> {
elves: Vec<ElfReader<'a>>,
sym_defs: HashMap<&'a BStr, SymDef<'a>>,
}
pub fn run(opts: Opts) -> Result<()> { pub fn run(opts: Opts) -> Result<()> {
let mmaps = opts let mmaps = opts
.objs .objs
@ -44,7 +60,7 @@ pub fn run(opts: Opts) -> Result<()> {
info!(objs=?opts.objs, "Linking files"); info!(objs=?opts.objs, "Linking files");
let elfs = mmaps let elves = mmaps
.iter() .iter()
.zip(&opts.objs) .zip(&opts.objs)
.map(|(mmap, path)| { .map(|(mmap, path)| {
@ -52,12 +68,19 @@ pub fn run(opts: Opts) -> Result<()> {
}) })
.collect::<Result<Vec<_>, anyhow::Error>>()?; .collect::<Result<Vec<_>, anyhow::Error>>()?;
let elf = elfs[0]; let mut cx = LinkCtxt {
elves,
sym_defs: HashMap::new(),
};
let text_sh = elf.section_header_by_name(b".text")?; cx.resolve()?;
let text_content = elf.section_content(text_sh)?;
let _start_sym = elf.symbol_by_name(b"_start")?; dbg!(cx.sym_defs);
let text_sh = cx.elves[0].section_header_by_name(b".text")?;
let text_content = cx.elves[0].section_content(text_sh)?;
let _start_sym = cx.elves[0].symbol_by_name(b"_start")?;
write_output(text_content, _start_sym.value)?; write_output(text_content, _start_sym.value)?;
@ -67,6 +90,41 @@ pub fn run(opts: Opts) -> Result<()> {
pub const BASE_EXEC_ADDR: Addr = Addr(0x400000); // whatever ld does pub const BASE_EXEC_ADDR: Addr = Addr(0x400000); // whatever ld does
pub const DEFAULT_PROGRAM_HEADER_ALIGN_THAT_LD_USES_HERE: u64 = 0x1000; pub const DEFAULT_PROGRAM_HEADER_ALIGN_THAT_LD_USES_HERE: u64 = 0x1000;
impl<'a> LinkCtxt<'a> {
fn resolve(&mut self) -> Result<()> {
for (elf_idx, elf) in self.elves.iter().enumerate() {
for e_sym in elf.symbols()? {
let ty = e_sym.info.r#type();
// Undefined symbols are not a definition.
if e_sym.shndx == SHN_UNDEF {
continue;
}
let name = match ty.0 {
c::STT_SECTION => elf.sh_string(elf.section_header(e_sym.shndx)?.name)?,
_ => elf.string(e_sym.name)?,
};
match self.sym_defs.entry(name) {
Entry::Occupied(entry) => {
bail!("duplicate symbol {name}. Already defined in {}, duplicate definition in {}", entry.get().defined_in, elf_idx);
}
Entry::Vacant(entry) => {
entry.insert(SymDef {
name,
defined_in: elf_idx as u32,
refers_to_section: e_sym.shndx,
});
}
}
}
}
Ok(())
}
}
fn write_output(text: &[u8], entry_offset_from_text: Addr) -> Result<()> { fn write_output(text: &[u8], entry_offset_from_text: Addr) -> Result<()> {
let ident = ElfIdent { let ident = ElfIdent {
magic: *c::ELFMAG, magic: *c::ELFMAG,
@ -93,7 +151,8 @@ fn write_output(text: &[u8], entry_offset_from_text: Addr) -> Result<()> {
flags: ShFlags::SHF_ALLOC | ShFlags::SHF_EXECINSTR, flags: ShFlags::SHF_ALLOC | ShFlags::SHF_EXECINSTR,
fixed_entsize: None, fixed_entsize: None,
content: text.to_vec(), content: text.to_vec(),
addr_align: None, // align nicely
addr_align: Some(NonZeroU64::new(DEFAULT_PROGRAM_HEADER_ALIGN_THAT_LD_USES_HERE).unwrap()),
})?; })?;
let elf_header_and_program_headers = ProgramHeader { let elf_header_and_program_headers = ProgramHeader {
@ -138,15 +197,20 @@ fn write_output(text: &[u8], entry_offset_from_text: Addr) -> Result<()> {
let mut output_file = fs::File::create("a.out").context("creating ./a.out")?; let mut output_file = fs::File::create("a.out").context("creating ./a.out")?;
BufWriter::new(&mut output_file).write_all(&output)?; BufWriter::new(&mut output_file).write_all(&output)?;
make_file_executable(&output_file)?;
Ok(())
}
fn make_file_executable(file: &File) -> Result<()> {
#[allow(unused_mut)] #[allow(unused_mut)]
let mut permissions = output_file.metadata()?.permissions(); let mut permissions = file.metadata()?.permissions();
#[cfg(unix)] #[cfg(unix)]
{ {
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
let mode = permissions.mode(); let mode = permissions.mode();
permissions.set_mode(mode | 0o111); permissions.set_mode(mode | 0o111);
}; };
output_file.set_permissions(permissions)?; file.set_permissions(permissions)?;
Ok(()) Ok(())
} }