diff --git a/src/common/loadable.rs b/src/common/loadable.rs index 310353c..bb366dd 100644 --- a/src/common/loadable.rs +++ b/src/common/loadable.rs @@ -10,8 +10,28 @@ pub enum SegmentType { Bss, } -type SegmentSections = Vec; +#[derive(Default)] +pub struct SegmentSections { + sections_info: Vec, + data_size: u64, +} + +impl SegmentSections { + pub fn push(&mut self, si: SectionInfo) { + self.data_size += si.data_size; + self.sections_info.push(si); + } + + pub fn iter(&self) -> impl Iterator { + self.sections_info.iter() + } + + pub fn data_size(&self) -> u64 { + self.data_size + } +} +// TODO: use attributes for field section names, indexes etc. #[derive(Default)] pub struct Loadable { text: SegmentSections, @@ -32,11 +52,19 @@ impl Loadable { Ok(()) } - pub fn sections(&self) -> impl Iterator { - let text = once(&self.text).map(|s| (".text", s)); - let rodata = once(&self.rodata).map(|s| (".rodata", s)); - let data = once(&self.data).map(|s| (".data", s)); - let bss = once(&self.bss).map(|s| (".bss", s)); + pub const fn section_names() -> &'static [&'static str] { + &[".text", ".rodata", ".data", ".bss"] + } + + pub const fn section_count() -> usize { + Self::section_names().len() + } + + pub fn segment_sections(&self) -> impl Iterator { + let text = once(&self.text); + let rodata = once(&self.rodata); + let data = once(&self.data); + let bss = once(&self.bss); text.chain(rodata).chain(data).chain(bss) } diff --git a/src/formats/elf/output.rs b/src/formats/elf/output.rs index 47d3ecc..a13d052 100644 --- a/src/formats/elf/output.rs +++ b/src/formats/elf/output.rs @@ -8,10 +8,9 @@ use std::{ }; use elf_utilities::{ - file::ELF64, header::Ehdr64, - section::{build_string_table, Contents64, Section64, Shdr64, Type as ShType}, - segment::{Phdr64, Segment64, Type as SeType, PF_R, PF_W, PF_X}, + section::{build_string_table, Shdr64, Type as ShType}, + segment::{Phdr64, Type as SeType, PF_R, PF_W, PF_X}, }; use crate::{ @@ -23,117 +22,21 @@ use super::ElfObject; pub struct ElfOutput { destination: PathBuf, - file: ELF64, writer: BufWriter, } impl ElfOutput { pub fn new(destination: PathBuf) -> Result { - use elf_utilities::header::{Class, Data, Machine, Type, Version, OSABI}; - - let mut elf = ELF64::default(); - elf.ehdr.set_elf_type(Type::Exec); - elf.ehdr.set_class(Class::Bit64); - elf.ehdr.set_machine(Machine::X8664); - elf.ehdr.set_object_version(Version::Current); - elf.ehdr.set_file_version(Version::Current); - elf.ehdr.set_osabi(OSABI::Linux); - elf.ehdr.set_data(Data::LSB2); - let str_path = expand_path(&destination)?; - let writer = file_writer(str_path, 0o755)?; + let writer = make_file_writer(str_path, 0o755)?; let result = Self { destination, - file: elf, writer, }; Ok(result) } - - fn populate_sections(&mut self, loadable: &Loadable) -> Result<(), Error> { - let mut names = Vec::new(); - let mut name_idx = 0usize; - - for (name, sections) in loadable.sections() { - let mut data_size = 0; - - for t in sections.iter() { - data_size += t.data_size; - } - - if data_size == 0 { - continue; - } - - names.push(name); - let section = Section64 { - name: name.into(), - header: make_section_header(name_idx, ShType::ProgBits, data_size), - contents: Contents64::Raw(Vec::new()), // placeholder only - }; - name_idx += name.len() + 1; - self.file.add_section(section); - } - - let name = ".shstrtab"; - names.push(name); - let string_table = build_string_table(names, true); - let section = Section64 { - name: name.into(), - header: make_section_header(name_idx, ShType::StrTab, string_table.len() as u64), - contents: Contents64::Raw(string_table), - }; - self.file.add_section(section); - - Ok(()) - } - - fn populate_segment(&mut self, offset: &mut u64, size: u64, which: SegmentType) { - if size == 0 { - return; - } - - let mut segment = Segment64 { - header: Phdr64::default(), - }; - segment.header.set_type(SeType::Load); - segment.header.p_filesz = size; - segment.header.p_memsz = size; - segment.header.p_offset = *offset; - - let mut page_size = next_page(size); - - match which { - SegmentType::Text => segment.header.p_flags = PF_R | PF_X, - SegmentType::Data => segment.header.p_flags = PF_R | PF_W, - SegmentType::Bss => { - segment.header.p_flags = PF_R; - segment.header.p_filesz = 0; - page_size = 0; - } - } - - self.file.segments.push(segment); - self.file.ehdr.e_phnum += 1; - self.file.ehdr.e_phentsize = size_of::() as u16; - - *offset += page_size; - } - - fn populate_segments(&mut self, loadable: &Loadable) -> Result { - let mut offset = 0u64; - // program header/segments - // contains .text + .rodata as one segment - self.populate_segment(&mut offset, loadable.program_size(), SegmentType::Text); - // contains .data as one segment - self.populate_segment(&mut offset, loadable.data_size(), SegmentType::Data); - // contains .bss as one segment - self.populate_segment(&mut offset, loadable.bss_size(), SegmentType::Bss); - - Ok(offset) - } } impl Output for ElfOutput { @@ -142,38 +45,62 @@ impl Output for ElfOutput { const PHS: u16 = size_of::() as u16; const SHS: u16 = size_of::() as u16; - self.populate_sections(loadable)?; - let page_size = self.populate_segments(loadable)?; - - self.file.ehdr.e_shnum = self.file.sections.len() as u16; - self.file.ehdr.e_entry = 4096; // TODO - self.file.ehdr.e_phentsize = PHS; - self.file.ehdr.e_shentsize = SHS; - self.file.ehdr.e_phoff = EHS; - self.file.ehdr.e_shoff = self.file.ehdr.e_phoff - + (usize::from(self.file.ehdr.e_phentsize) * self.file.segments.len()) as u64; + let strtab = make_strtab(); + let mut ehdr = make_elf_header(); + ehdr.e_shnum = Loadable::section_count() as u16 + 1; + ehdr.e_phnum = 3; // TODO: probably move to loadable? + ehdr.e_shstrndx = ehdr.e_shnum - 1; + ehdr.e_entry = 4096; // TODO: find .start symbol eventual location + ehdr.e_phentsize = PHS; + ehdr.e_shentsize = SHS; + ehdr.e_phoff = EHS; + ehdr.e_shoff = + ehdr.e_phoff + (usize::from(ehdr.e_phentsize) * usize::from(ehdr.e_phnum)) as u64; let mut offset = 0; // write ELF header - offset += self.writer.write(&self.file.ehdr.to_le_bytes())?; + offset += self.writer.write(&ehdr.to_le_bytes())?; // write program header table - for seg in self.file.segments.iter_mut() { - seg.header.p_offset = offset as u64; - offset += self.writer.write(&seg.header.to_le_bytes())?; - } + // contains .text + .rodata as one segment header + let ph = make_program_header(offset, loadable.program_size(), SegmentType::Text); + offset += self.writer.write(&ph.to_le_bytes())?; + // contains .data as one segment header + let ph = make_program_header(offset, loadable.data_size(), SegmentType::Data); + offset += self.writer.write(&ph.to_le_bytes())?; + // contains .bss as one segment header + let ph = make_program_header(offset, loadable.bss_size(), SegmentType::Bss); + offset += self.writer.write(&ph.to_le_bytes())?; eprintln!("SH start: {}", offset); - // write section header table - for sec in self.file.sections.iter_mut() { - if sec.header.get_type() == ShType::StrTab { - sec.header.sh_offset = offset as u64 + u64::from(SHS); - } - offset += self.writer.write(&sec.header.to_le_bytes())?; - if sec.header.get_type() == ShType::StrTab { - offset += self.writer.write(&sec.to_le_bytes())?; - } + let mut name_idx = 0; + // write section header table (text + rodata + data + bss) + for sections in loadable.segment_sections() { + // TODO: find out data_offset for these headers + let sh = make_section_header( + strtab.1[name_idx], + ShType::ProgBits, + 0, + sections.data_size(), + ); + name_idx += 1; + offset += self.writer.write(&sh.to_le_bytes())?; + } + + // .shstrtab as last section header written (+ the name data including) + { + // write .shstrtab header + let data_offset = (offset + usize::from(SHS)) as u64; + let strtab_header = make_section_header( + strtab.1[name_idx], + ShType::StrTab, + data_offset, + strtab.0.len() as u64, + ); + offset += self.writer.write(&strtab_header.to_le_bytes())?; + // write .shstrtab data + offset += self.writer.write(&strtab.0)?; } // program data (segments) @@ -196,7 +123,7 @@ impl Output for ElfOutput { } } -fn file_writer(output_filename: &str, permission: u32) -> Result, Error> { +fn make_file_writer(output_filename: &str, permission: u32) -> Result, Error> { use std::os::unix::fs::OpenOptionsExt; let file = std::fs::OpenOptions::new() @@ -209,7 +136,23 @@ fn file_writer(output_filename: &str, permission: u32) -> Result Ok(BufWriter::new(file)) } -fn make_section_header(name_idx: usize, stype: ShType, size: u64) -> Shdr64 { +fn make_elf_header() -> Ehdr64 { + use elf_utilities::header::{Class, Data, Machine, Type, Version, OSABI}; + + let mut ehdr = Ehdr64::default(); + ehdr.set_elf_type(Type::Exec); + ehdr.set_class(Class::Bit64); + ehdr.set_machine(Machine::X8664); + ehdr.set_object_version(Version::Current); + ehdr.set_file_version(Version::Current); + ehdr.set_osabi(OSABI::Linux); + ehdr.set_data(Data::LSB2); + ehdr.e_ehsize = size_of::() as u16; + + ehdr +} + +fn make_section_header(name_idx: usize, stype: ShType, offset: u64, size: u64) -> Shdr64 { let mut h = Shdr64::default(); h.set_type(stype); @@ -221,11 +164,58 @@ fn make_section_header(name_idx: usize, stype: ShType, size: u64) -> Shdr64 { h.sh_link = 0; h.sh_size = size; // filled at finalize() - // h.sh_offset = offset; + h.sh_offset = offset; h } +fn make_program_header(offset: usize, size: u64, which: SegmentType) -> Phdr64 { + let mut header = Phdr64::default(); + + header.set_type(SeType::Load); + header.p_filesz = size; + header.p_memsz = size; + header.p_offset = offset as u64; + + match which { + SegmentType::Text => header.p_flags = PF_R | PF_X, + SegmentType::Data => header.p_flags = PF_R | PF_W, + SegmentType::Bss => { + header.p_flags = PF_R; + header.p_filesz = 0; + } + } + + header +} + +// strtab as bytes + indexes to individual strings +fn make_strtab() -> (Vec, Vec) { + let mut section_names = Vec::from(Loadable::section_names()); + section_names.push(".shstrtab"); + + let strtab_bytes = build_string_table(section_names, true); + + let mut indexes = Vec::new(); + let mut on_string = false; + for (i, byte) in strtab_bytes.iter().enumerate() { + if *byte == 0 { + on_string = false; + continue; + } + + if on_string { + continue; + } + + assert!(i > 0, "First byte of .shstrab not 0"); + indexes.push(i - 1); + on_string = true; + } + + (strtab_bytes, indexes) +} + fn expand_path(path: &Path) -> Result<&str, Error> { use std::io::Error as IOError; use std::io::ErrorKind; @@ -237,14 +227,6 @@ fn expand_path(path: &Path) -> Result<&str, Error> { }) } -fn next_page(size: u64) -> u64 { - let page_size: u64 = page_size::get() as u64; - - let pages_needed = (size / page_size) + 1; - - page_size * pages_needed -} - fn pad_to_next_page(writer: &mut BufWriter, offset: usize) -> Result { let page_size = page_size::get();