From b04e3b8a55486db411f3a4bed7353a49c9a37028 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Fri, 20 Oct 2023 20:38:16 +0200 Subject: [PATCH] basic storage allocation --- Cargo.lock | 23 ++++++++++++ elven-parser/src/read.rs | 2 +- elven-wald/Cargo.toml | 1 + elven-wald/src/lib.rs | 56 +++++++++++++++++++---------- elven-wald/src/main.rs | 2 +- elven-wald/src/storage.rs | 76 +++++++++++++++++++++++++++++++++++++++ elven-wald/src/utils.rs | 32 +++++++++++++++++ 7 files changed, 171 insertions(+), 21 deletions(-) create mode 100644 elven-wald/src/storage.rs create mode 100644 elven-wald/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index c9fb3ab..5100391 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -197,12 +197,19 @@ dependencies = [ "bstr", "clap", "elven-parser", + "indexmap", "memmap2", "tempfile", "tracing", "tracing-subscriber", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.5" @@ -225,12 +232,28 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "indexmap" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "lazy_static" version = "1.4.0" diff --git a/elven-parser/src/read.rs b/elven-parser/src/read.rs index 684d7b6..3491705 100644 --- a/elven-parser/src/read.rs +++ b/elven-parser/src/read.rs @@ -5,7 +5,7 @@ use crate::{ consts::{self as c, DynamicTag, ShType}, idx::{define_idx, ElfIndexExt, ToIdxUsize}, - Addr, Offset, + Addr, Offset, write::Section, }; use bstr::BStr; diff --git a/elven-wald/Cargo.toml b/elven-wald/Cargo.toml index 6e50140..c3d2e78 100644 --- a/elven-wald/Cargo.toml +++ b/elven-wald/Cargo.toml @@ -10,6 +10,7 @@ anyhow = "1.0.69" bstr = "1.3.0" clap = { version = "4.1.4", features = ["derive"] } elven-parser = { path = "../elven-parser" } +indexmap = "2.0.2" memmap2 = "0.5.8" tracing = "0.1.37" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } diff --git a/elven-wald/src/lib.rs b/elven-wald/src/lib.rs index 84312af..6cde90e 100644 --- a/elven-wald/src/lib.rs +++ b/elven-wald/src/lib.rs @@ -1,3 +1,6 @@ +mod storage; +mod utils; + #[macro_use] extern crate tracing; @@ -26,6 +29,14 @@ pub struct Opts { pub objs: Vec, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct FileId(usize); + +struct ElfFile<'a> { + id: FileId, + elf: ElfReader<'a>, +} + #[derive(Debug)] struct SymDef<'a> { _name: &'a BStr, @@ -35,7 +46,7 @@ struct SymDef<'a> { } struct LinkCtxt<'a> { - elves: Vec>, + elves: Vec>, sym_defs: HashMap<&'a BStr, SymDef<'a>>, } @@ -56,17 +67,18 @@ pub fn run(opts: Opts) -> Result<()> { bail!("you gotta supply at least one object file"); } - if opts.objs.len() > 1 { - bail!("hey hey hey one stop back please. you want to link MULTIPLE files TOGETHER? im sorry i cant do that"); - } - info!(objs=?opts.objs, "Linking files"); let elves = mmaps .iter() .zip(&opts.objs) - .map(|(mmap, path)| { - ElfReader::new(mmap).with_context(|| format!("parsing ELF file {}", path.display())) + .enumerate() + .map(|(idx, (mmap, path))| { + Ok(ElfFile { + id: FileId(idx), + elf: ElfReader::new(mmap) + .with_context(|| format!("parsing ELF file {}", path.display()))?, + }) }) .collect::, anyhow::Error>>()?; @@ -75,14 +87,19 @@ pub fn run(opts: Opts) -> Result<()> { sym_defs: HashMap::new(), }; + let storage = + storage::allocate_storage(BASE_EXEC_ADDR, &cx.elves).context("while allocating storage")?; + + dbg!(storage); + cx.resolve()?; 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 text_sh = cx.elves[0].elf.section_header_by_name(b".text")?; + let text_content = cx.elves[0].elf.section_content(text_sh)?; - let _start_sym = cx.elves[0].symbol_by_name(b"_start")?; + let _start_sym = cx.elves[0].elf.symbol_by_name(b"_start")?; write_output(&opts, text_content, _start_sym.value)?; @@ -90,12 +107,12 @@ pub fn run(opts: Opts) -> Result<()> { } 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_PAGE_ALIGN: 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()? { + for e_sym in elf.elf.symbols()? { let ty = e_sym.info.r#type(); // Undefined symbols are not a definition. @@ -104,8 +121,10 @@ impl<'a> LinkCtxt<'a> { } let name = match ty.0 { - c::STT_SECTION => elf.sh_string(elf.section_header(e_sym.shndx)?.name)?, - _ => elf.string(e_sym.name)?, + c::STT_SECTION => elf + .elf + .sh_string(elf.elf.section_header(e_sym.shndx)?.name)?, + _ => elf.elf.string(e_sym.name)?, }; match self.sym_defs.entry(name) { @@ -154,7 +173,7 @@ fn write_output(opts: &Opts, text: &[u8], entry_offset_from_text: Addr) -> Resul fixed_entsize: None, content: text.to_vec(), // align nicely - addr_align: Some(NonZeroU64::new(DEFAULT_PROGRAM_HEADER_ALIGN_THAT_LD_USES_HERE).unwrap()), + addr_align: Some(NonZeroU64::new(DEFAULT_PAGE_ALIGN).unwrap()), })?; let elf_header_and_program_headers = ProgramHeader { @@ -168,13 +187,12 @@ fn write_output(opts: &Opts, text: &[u8], entry_offset_from_text: Addr) -> Resul paddr: BASE_EXEC_ADDR, filesz: 176, // FIXME: Do not hardocde this lol memsz: 176, - align: DEFAULT_PROGRAM_HEADER_ALIGN_THAT_LD_USES_HERE, + align: DEFAULT_PAGE_ALIGN, }; write.add_program_header(elf_header_and_program_headers); - let entry_addr = - BASE_EXEC_ADDR + DEFAULT_PROGRAM_HEADER_ALIGN_THAT_LD_USES_HERE + entry_offset_from_text; + let entry_addr = BASE_EXEC_ADDR + DEFAULT_PAGE_ALIGN + entry_offset_from_text; let text_program_header = ProgramHeader { r#type: PT_LOAD.into(), @@ -187,7 +205,7 @@ fn write_output(opts: &Opts, text: &[u8], entry_offset_from_text: Addr) -> Resul paddr: entry_addr, filesz: text.len() as u64, memsz: text.len() as u64, - align: DEFAULT_PROGRAM_HEADER_ALIGN_THAT_LD_USES_HERE, + align: DEFAULT_PAGE_ALIGN, }; write.add_program_header(text_program_header); diff --git a/elven-wald/src/main.rs b/elven-wald/src/main.rs index ef1d0f1..d87bf63 100644 --- a/elven-wald/src/main.rs +++ b/elven-wald/src/main.rs @@ -8,7 +8,7 @@ fn main() -> anyhow::Result<()> { tracing_subscriber::fmt() .with_env_filter( EnvFilter::builder() - .with_default_directive(LevelFilter::INFO.into()) + .with_default_directive(LevelFilter::DEBUG.into()) .from_env_lossy(), ) .init(); diff --git a/elven-wald/src/storage.rs b/elven-wald/src/storage.rs new file mode 100644 index 0000000..e78e60a --- /dev/null +++ b/elven-wald/src/storage.rs @@ -0,0 +1,76 @@ +use std::ops::Add; + +use anyhow::Result; +use bstr::{BStr, BString}; +use elven_parser::{read::ElfReadError, Addr}; +use indexmap::IndexMap; + +use crate::{utils::AlignExt, ElfFile, FileId, DEFAULT_PAGE_ALIGN}; + +#[derive(Debug)] +pub struct Allocation { + file: FileId, + section: BString, + size: u64, + align: u64, +} + +#[derive(Debug)] +pub struct SegmentPart { + base: Addr, + file: FileId, + section: BString, + size: u64, +} + +#[derive(Debug)] +pub struct StorageAllocation { + segment_parts: Vec, +} + +pub fn allocate_storage<'a>(base_addr: Addr, files: &[ElfFile<'a>]) -> Result { + let mut allocs = IndexMap::<_, Vec>::new(); + + for file in files { + let elf = file.elf; + + for name in [b".text".as_slice(), b".data", b".bss"] { + let section = elf.section_header_by_name(name); + match section { + Ok(section) => { + allocs.entry(BStr::new(name)).or_default().push(Allocation { + file: file.id, + section: name.into(), + size: section.size, + align: section.addralign, + }); + } + Err(ElfReadError::NotFoundByName(_, _)) => {} + Err(e) => return Err(e.into()), + } + } + } + + debug!(?allocs, "Allocation pass one completed"); + + let mut current_addr = base_addr; + let mut segment_parts = Vec::new(); + for section in allocs { + current_addr = current_addr.align_up(DEFAULT_PAGE_ALIGN); + for alloc in section.1 { + let align = alloc.align; + let addr = current_addr.align_up(align); + + current_addr = addr + alloc.size; + + segment_parts.push(SegmentPart { + base: addr, + file: alloc.file, + size: alloc.size, + section: section.0.to_owned(), + }); + } + } + + Ok(StorageAllocation { segment_parts }) +} diff --git a/elven-wald/src/utils.rs b/elven-wald/src/utils.rs new file mode 100644 index 0000000..92edaee --- /dev/null +++ b/elven-wald/src/utils.rs @@ -0,0 +1,32 @@ +use elven_parser::Addr; + +pub trait AlignExt: Copy { + fn align_down(self, align: T) -> Self; + fn align_up(self, align: T) -> Self; +} + +impl AlignExt for u64 { + fn align_down(self, align: Self) -> Self { + assert!(align.is_power_of_two() && align > 0); + // We want to set all the aligment bits to zero. + // 0b0101 aligned to 0b0100 => 0b0100 + // mask is !0b0011 = 0b1100 + let mask = !(align - 1); + self & mask + } + fn align_up(self, align: Self) -> Self { + assert!(align.is_power_of_two() && align > 0); + // 0b0101 aligned to 0b0100 => 0b1000 + (self + align - 1) & !(align - 1) + } +} + +impl AlignExt for Addr { + fn align_down(self, align: u64) -> Self { + Addr(self.u64().align_down(align)) + } + + fn align_up(self, align: u64) -> Self { + Addr(self.u64().align_up(align)) + } +}