basic storage allocation

This commit is contained in:
nora 2023-10-20 20:38:16 +02:00
parent b76d57ee9d
commit b04e3b8a55
7 changed files with 171 additions and 21 deletions

View file

@ -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"] }

View file

@ -1,3 +1,6 @@
mod storage;
mod utils;
#[macro_use]
extern crate tracing;
@ -26,6 +29,14 @@ pub struct Opts {
pub objs: Vec<PathBuf>,
}
#[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<ElfReader<'a>>,
elves: Vec<ElfFile<'a>>,
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::<Result<Vec<_>, 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);

View file

@ -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();

76
elven-wald/src/storage.rs Normal file
View file

@ -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<SegmentPart>,
}
pub fn allocate_storage<'a>(base_addr: Addr, files: &[ElfFile<'a>]) -> Result<StorageAllocation> {
let mut allocs = IndexMap::<_, Vec<Allocation>>::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 })
}

32
elven-wald/src/utils.rs Normal file
View file

@ -0,0 +1,32 @@
use elven_parser::Addr;
pub trait AlignExt<T>: Copy {
fn align_down(self, align: T) -> Self;
fn align_up(self, align: T) -> Self;
}
impl AlignExt<u64> 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<u64> 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))
}
}