From dc591025708ee0737831ba61d768e174f29fc014 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Fri, 1 Jan 2021 15:47:07 -0800 Subject: [PATCH 1/4] first basic segment/section out --- src/common/section.rs | 13 +- src/common/storage.rs | 2 +- src/formats/elf.rs | 3 +- src/formats/elf/output.rs | 246 +++++++++++++++++++++++++++++-------- src/formats/elf/segment.rs | 83 +++++++++++++ 5 files changed, 286 insertions(+), 61 deletions(-) create mode 100644 src/formats/elf/segment.rs diff --git a/src/common/section.rs b/src/common/section.rs index e362456..23e2a37 100644 --- a/src/common/section.rs +++ b/src/common/section.rs @@ -14,7 +14,7 @@ pub struct SectionInfo<'data> { pub enum Section<'data> { Text(SectionInfo<'data>), - Data(SectionInfo<'data>), + Data(SectionInfo<'data>, bool), // readonly bool Bss(SectionInfo<'data>), } @@ -22,7 +22,7 @@ impl Section<'_> { pub fn size(&self) -> Result { match self { Section::Text(s) => Ok(s.size), - Section::Data(s) => Ok(s.size), + Section::Data(s, _) => Ok(s.size), Section::Bss(s) => Ok(s.size), } } @@ -34,8 +34,10 @@ impl<'data> TryFrom<(&str, SectionInfo<'data>)> for Section<'data> { fn try_from(value: (&str, SectionInfo<'data>)) -> Result { if value.0.starts_with(".text") { Ok(Section::Text(value.1)) - } else if value.0.starts_with(".rodata") || value.0.starts_with(".data") { - Ok(Section::Data(value.1)) + } else if value.0.starts_with(".rodata") { + Ok(Section::Data(value.1, true)) + } else if value.0.starts_with(".data") { + Ok(Section::Data(value.1, false)) } else if value.0.starts_with(".bss") { Ok(Section::Bss(value.1)) } else { @@ -60,7 +62,8 @@ impl Display for Section<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Section::Text(s) => writeln!(f, "TEXT {}", s), - Section::Data(s) => writeln!(f, "DATA {}", s), + Section::Data(s, false) => writeln!(f, "DATA {}", s), + Section::Data(s, true) => writeln!(f, "RO-DATA {}", s), Section::Bss(s) => writeln!(f, "BSS {}", s), } } diff --git a/src/common/storage.rs b/src/common/storage.rs index 97d4d75..99ebd83 100644 --- a/src/common/storage.rs +++ b/src/common/storage.rs @@ -21,7 +21,7 @@ impl Storage { } pub fn destination(&self) -> Result { - let mut dest = canonicalize(&self.origin).map_err(|e| Error::from(e))?; + let mut dest = canonicalize(&self.origin)?; if !dest.pop() { let err = std::io::Error::new(ErrorKind::Other, "Destination path invalid"); Err(Error::IOError(Box::new(err))) diff --git a/src/formats/elf.rs b/src/formats/elf.rs index 4cfdb88..ddd8ade 100644 --- a/src/formats/elf.rs +++ b/src/formats/elf.rs @@ -4,6 +4,7 @@ use crate::linker::Linker; mod object; mod output; +mod segment; use elf_utilities::file::ELF64; pub use object::*; @@ -17,7 +18,7 @@ impl Linker<'_> { } impl<'data> Lazy<&'data str, ELF64> for Symbol<'data> { - fn value(&self, src: &ELF64) -> Result<&'data str, Error> { + fn value(&self, _src: &ELF64) -> Result<&'data str, Error> { Err(Error::InvalidSectionData) // TODO } diff --git a/src/formats/elf/output.rs b/src/formats/elf/output.rs index 2004c64..347053c 100644 --- a/src/formats/elf/output.rs +++ b/src/formats/elf/output.rs @@ -1,27 +1,27 @@ -use std::path::PathBuf; +use std::{fs::File, io::BufWriter, io::Write, mem::size_of, path::PathBuf}; use elf_utilities::{ - file::{ELF64Dumper, ELF64}, - section::Section64, - segment::{Segment64, Type}, + file::ELF64, + header::Ehdr64, + section::{build_string_table, Contents64, Section64, Shdr64}, + segment::{Phdr64, PF_R, PF_W, PF_X}, }; use crate::{ - common::{Output, Section, SectionInfo}, + common::{Output, Section}, error::Error, }; +use super::segment::*; use super::ElfObject; pub struct ElfOutput<'data> { destination: PathBuf, - dump: ELF64Dumper, - text: Vec>, - data: Vec>, - bss: Vec>, + file: ELF64, + segment_data: SegmentData<'data>, } -impl ElfOutput<'_> { +impl<'data> ElfOutput<'data> { pub fn new(destination: PathBuf) -> Self { use elf_utilities::header::{Class, Data, Machine, Type, Version, OSABI}; @@ -36,55 +36,119 @@ impl ElfOutput<'_> { Self { destination, - dump: ELF64Dumper::new(elf), - text: Vec::new(), - data: Vec::new(), - bss: Vec::new(), + file: elf, + segment_data: SegmentData::new(), } } pub fn from_object(object: &ElfObject, destination: PathBuf) -> Self { - use elf_utilities::header::Type; - let other = object.elf(); - let mut elf = ELF64::default(); - elf.ehdr = other.ehdr.clone(); - elf.ehdr.set_elf_type(Type::Exec); - Self { destination, - dump: ELF64Dumper::new(elf), - text: Vec::new(), - data: Vec::new(), - bss: Vec::new(), + file: elf_bin_from_object(other), + segment_data: SegmentData::new(), } } - fn populate_section_data(&mut self) -> Result { - use elf_utilities::section::{Contents64, Shdr64, Type}; + fn populate_sections(&mut self) -> Result { + let mut names = Vec::new(); + let mut names_len = 0usize; + let mut result = 0u64; + + for (name, sections) in self.segment_data.iter_mut() { + let mut data = Vec::new(); - let mut data = Vec::new(); + for t in sections.iter_mut() { + if let Some(iter) = &mut t.data { + data.extend(iter.as_mut()); + } + } - for t in self.text.iter_mut() { - if let Some(iter) = &mut t.data { - data.extend(iter.as_mut()); + if data.is_empty() { + continue; } + + result += data.len() as u64; + names_len += name.len(); + names.push(name); + let section = Section64 { + name: name.into(), + header: make_section_header(&name, names_len, data.len()), + contents: Contents64::Raw(data), + }; + self.file.add_section(section); } - let mut header = Shdr64::default(); - header.set_type(Type::ProgBits); - header.sh_entsize = data.len() as u64; + let name = ".shstrtab"; + names_len += name.len(); + names.push(name); + let string_table = build_string_table(names, true); + result += string_table.len() as u64; + let section = Section64 { + name: name.into(), + header: make_section_header(&name, names_len, string_table.len()), + contents: Contents64::Raw(string_table), + }; + self.file.add_section(section); + + Ok(result) + } + + fn populate_segment(&mut self, offset: &mut u64, size: u64, which: SegmentType) -> u64 { + use elf_utilities::segment::{Segment64, Type}; + + if size == 0 { + return 0; + } - let sec64 = Section64 { - name: ".text".into(), - header, - contents: Contents64::Raw(data), + let mut segment = Segment64 { + header: Phdr64::default(), }; + segment.header.set_type(Type::Load); + segment.header.p_filesz = size; + segment.header.p_memsz = size; + segment.header.p_offset = *offset; + + 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_memsz = 0; + } + } + + self.file.segments.push(segment); + self.file.ehdr.e_phnum += 1; + self.file.ehdr.e_phentsize = size_of::() as u16; - self.dump.file.add_section(sec64); + *offset += size; - Ok(0) + size + } + + fn populate_segments(&mut self) -> Result { + let mut offset = 0u64; + let mut result = 0u64; + // program header/segments + // contains .text + .rodata as one segment + result += self.populate_segment( + &mut offset, + self.segment_data.program_size(), + SegmentType::Text, + ); + // contains .data as one segment + result += self.populate_segment( + &mut offset, + self.segment_data.data_size(), + SegmentType::Data, + ); + // contains .bss as one segment + result += + self.populate_segment(&mut offset, self.segment_data.bss_size(), SegmentType::Bss); + + Ok(result) } } @@ -94,24 +158,28 @@ impl<'data> Output<'data> for ElfOutput<'data> { } fn append_section(&mut self, section: Section<'data>) -> Result<(), Error> { - match section { - Section::Text(si) => self.text.push(si), - Section::Data(si) => self.data.push(si), - Section::Bss(si) => self.bss.push(si), - } - - Ok(()) + self.segment_data.append_section(section) } fn finalize(mut self) -> Result { use std::io::Error as IOError; use std::io::ErrorKind; - self.populate_section_data()?; - let mut segment = Segment64::default(); - segment.header.set_type(Type::Load); - self.dump.file.segments.push(segment); - self.dump.file.finalize(); + const EHS: u64 = size_of::() as u64; + const PHS: u64 = size_of::() as u64; + const SHS: u16 = size_of::() as u16; + + let segment_data_size = self.populate_sections()?; + self.populate_segments()?; + + self.file.ehdr.e_phentsize = PHS as u16; + self.file.ehdr.e_shentsize = SHS; + self.file.ehdr.e_phoff = EHS; // TODO: align + self.file.ehdr.e_shoff = self.file.ehdr.e_phoff + + u64::from(self.file.ehdr.e_phnum * self.file.ehdr.e_phentsize) + + segment_data_size; + self.file.ehdr.e_shnum = self.file.sections.len() as u16; + self.file.ehdr.e_entry = 0x100; // TODO let str_path = self.destination.to_str().ok_or_else(|| { let ioe = IOError::new(ErrorKind::Other, "Path expansion fail"); @@ -119,10 +187,80 @@ impl<'data> Output<'data> for ElfOutput<'data> { Error::IOError(boxed) })?; - self.dump - .generate_elf_file(str_path, 0o755) - .map_err(|e| Error::IOError(e))?; + let mut writer = file_writer(str_path, 0o755)?; + let mut offset = 0; + + offset += writer.write(&self.file.ehdr.to_le_bytes())?; + + // write program header table + for seg in self.file.segments.iter_mut() { + seg.header.p_offset = offset as u64; + offset += writer.write(&seg.header.to_le_bytes())?; + } + + // write section/segment data + for sec in self.file.sections.iter_mut() { + sec.header.sh_offset = offset as u64; + offset += writer.write(&sec.to_le_bytes())?; + } + + // write section header table + for sec in self.file.sections.iter() { + eprintln!("NAme index: {}", sec.header.sh_name); + offset += writer.write(&sec.header.to_le_bytes())?; + } + writer.flush()?; Ok(self.destination) } } + +fn file_writer(output_filename: &str, permission: u32) -> Result, Error> { + use std::os::unix::fs::OpenOptionsExt; + + let file = std::fs::OpenOptions::new() + .create(true) + .read(true) + .write(true) + .mode(permission) + .open(output_filename)?; + + Ok(BufWriter::new(file)) +} + +// init new ELF64 from an object file meant as executable output +fn elf_bin_from_object(other: &ELF64) -> ELF64 { + use elf_utilities::header::Type; + + let mut elf = ELF64::default(); + elf.ehdr.set_elf_type(Type::Exec); + elf.ehdr.e_ehsize = other.ehdr.e_ehsize; + elf.ehdr.e_version = other.ehdr.e_version; + elf.ehdr.e_ident = other.ehdr.e_ident; + elf.ehdr.e_machine = other.ehdr.e_machine; + + elf +} + +fn make_section_header(name: &str, names_len: usize, size: usize) -> Shdr64 { + use elf_utilities::section::Type; + + let mut h = Shdr64::default(); + match name { + ".shstrtab" => h.set_type(Type::StrTab), + ".bss" => h.set_type(Type::NoBits), + _ => h.set_type(Type::ProgBits), + } + + // section index used for name and section indexing (currently same) + let idx = 1 + (names_len as u32); // add 0 at start + // name index + h.sh_name = idx; + // link index + h.sh_link = 0; //idx; + h.sh_size = size as u64; + // TODO + // h.sh_offset = offset; + + h +} diff --git a/src/formats/elf/segment.rs b/src/formats/elf/segment.rs new file mode 100644 index 0000000..241b467 --- /dev/null +++ b/src/formats/elf/segment.rs @@ -0,0 +1,83 @@ +use crate::common::Section; +use crate::common::SectionInfo; +use crate::error::Error; + +const SI_TEXT: usize = 0; +const SI_RODATA: usize = 1; +const SI_DATA: usize = 2; +const SI_BSS: usize = 3; + +pub enum SegmentType { + Text = SI_TEXT as isize, + Data = SI_DATA as isize, + Bss = SI_BSS as isize, +} + +const SEGMENT_NAMES: [&str; 4] = [".text", ".rodata", ".data", ".bss"]; + +pub struct SegmentData<'data>([Vec>; 4]); + +impl<'data> SegmentData<'data> { + pub fn new() -> Self { + Self([Vec::new(), Vec::new(), Vec::new(), Vec::new()]) + } + + pub fn append_section(&mut self, section: Section<'data>) -> Result<(), Error> { + match section { + Section::Text(si) => self.0[SI_TEXT].push(si), + Section::Data(si, false) => self.0[SI_DATA].push(si), + Section::Data(si, true) => self.0[SI_RODATA].push(si), + Section::Bss(si) => self.0[SI_BSS].push(si), + } + + Ok(()) + } + + pub fn iter_mut( + &mut self, + ) -> impl Iterator>)> { + self.0 + .iter_mut() + .enumerate() + .map(|(i, s)| (SEGMENT_NAMES[i], s)) + } + + // .text + .rodata + pub fn program_size(&self) -> u64 { + let text_iter = self.0[SI_TEXT].iter(); + let rodata_iter = self.0[SI_RODATA].iter(); + + let mut result = 0u64; + for section in text_iter.chain(rodata_iter) { + result += section.size + } + + result + } + + // data + pub fn data_size(&self) -> u64 { + let mut result = 0u64; + for section in self.0[SI_DATA].iter() { + result += section.size + } + + result + } + + // bss + pub fn bss_size(&self) -> u64 { + let mut result = 0u64; + for section in self.0[SI_BSS].iter() { + result += section.size + } + + result + } +} + +impl Default for SegmentData<'_> { + fn default() -> Self { + Self::new() + } +} From 5f432c6eeac03079f7ea8d52ce373f7225654e4c Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Mon, 4 Jan 2021 22:40:48 -0800 Subject: [PATCH 2/4] fix string matching --- src/formats/elf/output.rs | 58 ++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/formats/elf/output.rs b/src/formats/elf/output.rs index 347053c..a9de61d 100644 --- a/src/formats/elf/output.rs +++ b/src/formats/elf/output.rs @@ -1,10 +1,10 @@ -use std::{fs::File, io::BufWriter, io::Write, mem::size_of, path::PathBuf}; +use std::{fs::File, io::BufWriter, io::Write, mem::size_of, path::{Path, PathBuf}}; use elf_utilities::{ file::ELF64, header::Ehdr64, - section::{build_string_table, Contents64, Section64, Shdr64}, - segment::{Phdr64, PF_R, PF_W, PF_X}, + section::{build_string_table, Contents64, Section64, Shdr64, Type as ShType}, + segment::{Phdr64, Segment64, Type as SeType, PF_R, PF_W, PF_X}, }; use crate::{ @@ -53,7 +53,7 @@ impl<'data> ElfOutput<'data> { fn populate_sections(&mut self) -> Result { let mut names = Vec::new(); - let mut names_len = 0usize; + let mut name_idx = 0usize; let mut result = 0u64; for (name, sections) in self.segment_data.iter_mut() { @@ -70,24 +70,23 @@ impl<'data> ElfOutput<'data> { } result += data.len() as u64; - names_len += name.len(); names.push(name); let section = Section64 { name: name.into(), - header: make_section_header(&name, names_len, data.len()), + header: make_section_header(name_idx, ShType::ProgBits, data.len()), contents: Contents64::Raw(data), }; + name_idx += name.len() + 1; self.file.add_section(section); } let name = ".shstrtab"; - names_len += name.len(); names.push(name); let string_table = build_string_table(names, true); result += string_table.len() as u64; let section = Section64 { name: name.into(), - header: make_section_header(&name, names_len, string_table.len()), + header: make_section_header(name_idx, ShType::StrTab, string_table.len()), contents: Contents64::Raw(string_table), }; self.file.add_section(section); @@ -96,8 +95,6 @@ impl<'data> ElfOutput<'data> { } fn populate_segment(&mut self, offset: &mut u64, size: u64, which: SegmentType) -> u64 { - use elf_utilities::segment::{Segment64, Type}; - if size == 0 { return 0; } @@ -105,7 +102,7 @@ impl<'data> ElfOutput<'data> { let mut segment = Segment64 { header: Phdr64::default(), }; - segment.header.set_type(Type::Load); + segment.header.set_type(SeType::Load); segment.header.p_filesz = size; segment.header.p_memsz = size; segment.header.p_offset = *offset; @@ -162,9 +159,6 @@ impl<'data> Output<'data> for ElfOutput<'data> { } fn finalize(mut self) -> Result { - use std::io::Error as IOError; - use std::io::ErrorKind; - const EHS: u64 = size_of::() as u64; const PHS: u64 = size_of::() as u64; const SHS: u16 = size_of::() as u16; @@ -181,11 +175,7 @@ impl<'data> Output<'data> for ElfOutput<'data> { self.file.ehdr.e_shnum = self.file.sections.len() as u16; self.file.ehdr.e_entry = 0x100; // TODO - let str_path = self.destination.to_str().ok_or_else(|| { - let ioe = IOError::new(ErrorKind::Other, "Path expansion fail"); - let boxed = Box::new(ioe); - Error::IOError(boxed) - })?; + let str_path = expand_path(&self.destination)?; let mut writer = file_writer(str_path, 0o755)?; let mut offset = 0; @@ -206,7 +196,6 @@ impl<'data> Output<'data> for ElfOutput<'data> { // write section header table for sec in self.file.sections.iter() { - eprintln!("NAme index: {}", sec.header.sh_name); offset += writer.write(&sec.header.to_le_bytes())?; } writer.flush()?; @@ -242,25 +231,30 @@ fn elf_bin_from_object(other: &ELF64) -> ELF64 { elf } -fn make_section_header(name: &str, names_len: usize, size: usize) -> Shdr64 { - use elf_utilities::section::Type; - +fn make_section_header(name_idx: usize, stype: ShType, size: usize) -> Shdr64 { let mut h = Shdr64::default(); - match name { - ".shstrtab" => h.set_type(Type::StrTab), - ".bss" => h.set_type(Type::NoBits), - _ => h.set_type(Type::ProgBits), - } + h.set_type(stype); // section index used for name and section indexing (currently same) - let idx = 1 + (names_len as u32); // add 0 at start - // name index + let idx = 1 + (name_idx as u32); // add 0 at start + // name index h.sh_name = idx; // link index - h.sh_link = 0; //idx; + h.sh_link = 0; h.sh_size = size as u64; - // TODO + // filled at finalize() // h.sh_offset = offset; h } + +fn expand_path(path: &Path) -> Result<&str, Error> { + use std::io::Error as IOError; + use std::io::ErrorKind; + + path.to_str().ok_or_else(|| { + let ioe = IOError::new(ErrorKind::Other, "Path expansion fail"); + let boxed = Box::new(ioe); + Error::IOError(boxed) + }) +} \ No newline at end of file From f5ae47bf4259148b81fc72f50dbcbbf42b598b53 Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Thu, 7 Jan 2021 10:25:39 -0800 Subject: [PATCH 3/4] WIP --- src/common/section.rs | 26 ++++++++++++++- src/error.rs | 7 ++++ src/formats/elf/output.rs | 66 ++++++++++++++++++++++++++------------ src/formats/elf/segment.rs | 20 ++++++++++-- src/linker.rs | 2 +- 5 files changed, 96 insertions(+), 25 deletions(-) diff --git a/src/common/section.rs b/src/common/section.rs index 23e2a37..47dbfaf 100644 --- a/src/common/section.rs +++ b/src/common/section.rs @@ -4,7 +4,17 @@ use std::{ fmt::{Display, Formatter}, }; -pub type SectionDataIterator<'data> = Box + 'data>; +pub trait CBI<'data, T>: Iterator { + fn clone_to_box(&self) -> Box + 'data>; +} + +impl<'data, T, X: 'data + Iterator + Clone> CBI<'data, T> for X { + fn clone_to_box(&self) -> Box + 'data> { + Box::new(self.clone()) + } +} + +pub type SectionDataIterator<'data> = Box + 'data>; pub struct SectionInfo<'data> { pub size: u64, @@ -12,12 +22,26 @@ pub struct SectionInfo<'data> { pub offset: u64, } +#[derive(Clone)] pub enum Section<'data> { Text(SectionInfo<'data>), Data(SectionInfo<'data>, bool), // readonly bool Bss(SectionInfo<'data>), } +impl<'data> Clone for SectionInfo<'data> { + fn clone(&self) -> Self { + Self { + size: self.size, + data: match self.data.as_ref() { + Some(iter) => Some(iter.clone_to_box()), + None => None, + }, + offset: self.offset, + } + } +} + impl Section<'_> { pub fn size(&self) -> Result { match self { diff --git a/src/error.rs b/src/error.rs index 73a185f..bb96a56 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,7 @@ pub enum Error { InvalidSectionName, InvalidSectionData, ParseError(Box), + TryFromIntError, // LinkingError(Trace), } @@ -33,6 +34,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: std::num::TryFromIntError) -> Self { + Self::TryFromIntError + } +} + impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "TODO!") diff --git a/src/formats/elf/output.rs b/src/formats/elf/output.rs index a9de61d..cbc7e61 100644 --- a/src/formats/elf/output.rs +++ b/src/formats/elf/output.rs @@ -1,4 +1,11 @@ -use std::{fs::File, io::BufWriter, io::Write, mem::size_of, path::{Path, PathBuf}}; +use std::{ + convert::TryInto, + fs::File, + io::BufWriter, + io::{Seek, SeekFrom, Write}, + mem::size_of, + path::{Path, PathBuf}, +}; use elf_utilities::{ file::ELF64, @@ -56,7 +63,7 @@ impl<'data> ElfOutput<'data> { let mut name_idx = 0usize; let mut result = 0u64; - for (name, sections) in self.segment_data.iter_mut() { + for (name, sections) in self.segment_data.sections_mut() { let mut data = Vec::new(); for t in sections.iter_mut() { @@ -94,9 +101,9 @@ impl<'data> ElfOutput<'data> { Ok(result) } - fn populate_segment(&mut self, offset: &mut u64, size: u64, which: SegmentType) -> u64 { + fn populate_segment(&mut self, offset: &mut u64, size: u64, which: SegmentType) { if size == 0 { - return 0; + return; } let mut segment = Segment64 { @@ -120,32 +127,28 @@ impl<'data> ElfOutput<'data> { self.file.ehdr.e_phnum += 1; self.file.ehdr.e_phentsize = size_of::() as u16; - *offset += size; - - size + *offset += next_page(size); } fn populate_segments(&mut self) -> Result { let mut offset = 0u64; - let mut result = 0u64; // program header/segments // contains .text + .rodata as one segment - result += self.populate_segment( + self.populate_segment( &mut offset, self.segment_data.program_size(), SegmentType::Text, ); // contains .data as one segment - result += self.populate_segment( + self.populate_segment( &mut offset, self.segment_data.data_size(), SegmentType::Data, ); // contains .bss as one segment - result += - self.populate_segment(&mut offset, self.segment_data.bss_size(), SegmentType::Bss); + self.populate_segment(&mut offset, self.segment_data.bss_size(), SegmentType::Bss); - Ok(result) + Ok(offset) } } @@ -160,18 +163,18 @@ impl<'data> Output<'data> for ElfOutput<'data> { fn finalize(mut self) -> Result { const EHS: u64 = size_of::() as u64; - const PHS: u64 = size_of::() as u64; + const PHS: u16 = size_of::() as u16; const SHS: u16 = size_of::() as u16; - let segment_data_size = self.populate_sections()?; - self.populate_segments()?; + self.populate_sections()?; + let page_size = self.populate_segments()?; - self.file.ehdr.e_phentsize = PHS as u16; + self.file.ehdr.e_phentsize = PHS; self.file.ehdr.e_shentsize = SHS; self.file.ehdr.e_phoff = EHS; // TODO: align self.file.ehdr.e_shoff = self.file.ehdr.e_phoff + u64::from(self.file.ehdr.e_phnum * self.file.ehdr.e_phentsize) - + segment_data_size; + + page_size; self.file.ehdr.e_shnum = self.file.sections.len() as u16; self.file.ehdr.e_entry = 0x100; // TODO @@ -191,9 +194,10 @@ impl<'data> Output<'data> for ElfOutput<'data> { // write section/segment data for sec in self.file.sections.iter_mut() { sec.header.sh_offset = offset as u64; - offset += writer.write(&sec.to_le_bytes())?; + offset += write_padded(&mut writer, offset, &sec.to_le_bytes())?; } + eprintln!("SHT: {}", offset); // write section header table for sec in self.file.sections.iter() { offset += writer.write(&sec.header.to_le_bytes())?; @@ -237,7 +241,7 @@ fn make_section_header(name_idx: usize, stype: ShType, size: usize) -> Shdr64 { // section index used for name and section indexing (currently same) let idx = 1 + (name_idx as u32); // add 0 at start - // name index + // name index h.sh_name = idx; // link index h.sh_link = 0; @@ -257,4 +261,24 @@ fn expand_path(path: &Path) -> Result<&str, Error> { let boxed = Box::new(ioe); Error::IOError(boxed) }) -} \ No newline at end of file +} + +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 write_padded(writer: &mut BufWriter, offset: usize, data: &[u8]) -> Result { + let page_size = page_size::get(); + let mut written = 0usize; + + written += writer.write(data)?; + let padding = page_size - (page_size % offset); + + written += writer.get_mut().seek(SeekFrom::Current(padding as i64))? as usize; + + Ok(written) +} diff --git a/src/formats/elf/segment.rs b/src/formats/elf/segment.rs index 241b467..4ad8d22 100644 --- a/src/formats/elf/segment.rs +++ b/src/formats/elf/segment.rs @@ -1,5 +1,5 @@ -use crate::common::Section; use crate::common::SectionInfo; +use crate::common::{Section, SectionDataIterator}; use crate::error::Error; const SI_TEXT: usize = 0; @@ -33,7 +33,7 @@ impl<'data> SegmentData<'data> { Ok(()) } - pub fn iter_mut( + pub fn sections_mut( &mut self, ) -> impl Iterator>)> { self.0 @@ -42,6 +42,22 @@ impl<'data> SegmentData<'data> { .map(|(i, s)| (SEGMENT_NAMES[i], s)) } + pub fn program_data<'local>(&'local self) -> Box + 'local> { + let text_iter = self.0[SI_TEXT].iter(); + let rodata_iter = self.0[SI_RODATA].iter(); + + let data1 = text_iter.filter_map(|si| match si.data.as_ref() { + Some(iter) => Some(iter.clone_to_box()), + None => None, + }); + let data2 = rodata_iter.filter_map(|si| match si.data.as_ref() { + Some(iter) => Some(iter.clone_to_box()), + None => None, + }); + + Box::new(data1.chain(data2).flatten()) + } + // .text + .rodata pub fn program_size(&self) -> u64 { let text_iter = self.0[SI_TEXT].iter(); diff --git a/src/linker.rs b/src/linker.rs index 39e8453..a3bf6f6 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -18,7 +18,7 @@ impl<'data> Linker<'data> { self.relocatables.push(relocatable); } - pub fn link<'l: 'data>(&'l self, output: &mut dyn Output<'data>) -> Result { + pub fn link(&'data self, output: &mut dyn Output<'data>) -> Result { let allocated = output.allocate(self.total_size()?)?; for r in self.relocatables.iter() { From c3fbd3ed22c2d5af02a637d4d9cb3732f0e125bc Mon Sep 17 00:00:00 2001 From: Ales Katona Date: Fri, 8 Jan 2021 22:38:23 -0800 Subject: [PATCH 4/4] WIP --- src/common/output.rs | 7 --- src/common/section.rs | 34 +++++-------- src/formats/elf/object.rs | 4 +- src/formats/elf/output.rs | 99 +++++++++++++++++++++----------------- src/formats/elf/segment.rs | 26 +++++----- src/linker.rs | 16 +----- 6 files changed, 80 insertions(+), 106 deletions(-) diff --git a/src/common/output.rs b/src/common/output.rs index 9e279b9..46532f7 100644 --- a/src/common/output.rs +++ b/src/common/output.rs @@ -4,8 +4,6 @@ use super::Section; use crate::error::Error; pub trait Output<'data> { - fn allocate(&mut self, size: u64) -> Result; - fn append_section(&mut self, section: Section<'data>) -> Result<(), Error>; // fn prepare_symbol(&mut self, symbol: impl Lazy) -> Result<&mut Self, Error>; @@ -16,11 +14,6 @@ pub trait Output<'data> { pub struct DummyOutput; impl<'data> Output<'data> for DummyOutput { - fn allocate(&mut self, size: u64) -> Result { - eprintln!("Allocating: {}", size); - Ok(size) - } - fn append_section(&mut self, section: Section<'data>) -> Result<(), Error> { eprintln!("Appending section: {}", section); Ok(()) diff --git a/src/common/section.rs b/src/common/section.rs index 47dbfaf..5cab17d 100644 --- a/src/common/section.rs +++ b/src/common/section.rs @@ -4,21 +4,22 @@ use std::{ fmt::{Display, Formatter}, }; -pub trait CBI<'data, T>: Iterator { - fn clone_to_box(&self) -> Box + 'data>; -} +// pub trait CBI<'data, T>: Iterator { +// fn clone_to_box(&self) -> Box + 'data>; +// } -impl<'data, T, X: 'data + Iterator + Clone> CBI<'data, T> for X { - fn clone_to_box(&self) -> Box + 'data> { - Box::new(self.clone()) - } -} +// impl<'data, T, X: 'data + Iterator + Clone> CBI<'data, T> for X { +// fn clone_to_box(&self) -> Box + 'data> { +// Box::new(self.clone()) +// } +// } -pub type SectionDataIterator<'data> = Box + 'data>; +// pub type SectionDataIterator<'data> = Box + 'data>; +#[derive(Clone)] pub struct SectionInfo<'data> { pub size: u64, - pub data: Option>, + pub data: Option<&'data [u8]>, pub offset: u64, } @@ -29,19 +30,6 @@ pub enum Section<'data> { Bss(SectionInfo<'data>), } -impl<'data> Clone for SectionInfo<'data> { - fn clone(&self) -> Self { - Self { - size: self.size, - data: match self.data.as_ref() { - Some(iter) => Some(iter.clone_to_box()), - None => None, - }, - offset: self.offset, - } - } -} - impl Section<'_> { pub fn size(&self) -> Result { match self { diff --git a/src/formats/elf/object.rs b/src/formats/elf/object.rs index 62b1ba4..1389f58 100644 --- a/src/formats/elf/object.rs +++ b/src/formats/elf/object.rs @@ -52,12 +52,12 @@ impl Relocatable for ElfObject { Type::ProgBits => { if s.header.sh_size > 0 { if let Some(di) = match &s.contents { - Contents64::Raw(v) => Some(v.iter()), + Contents64::Raw(v) => Some(v), _ => None, } { let si = SectionInfo { size: s.header.sh_size, - data: Some(Box::new(di.copied())), + data: Some(di), offset: s.header.sh_offset, }; let s_name: &str = &s.name; diff --git a/src/formats/elf/output.rs b/src/formats/elf/output.rs index cbc7e61..beb95c0 100644 --- a/src/formats/elf/output.rs +++ b/src/formats/elf/output.rs @@ -25,7 +25,7 @@ use super::ElfObject; pub struct ElfOutput<'data> { destination: PathBuf, file: ELF64, - segment_data: SegmentData<'data>, + input_data: InputData<'data>, } impl<'data> ElfOutput<'data> { @@ -44,7 +44,7 @@ impl<'data> ElfOutput<'data> { Self { destination, file: elf, - segment_data: SegmentData::new(), + input_data: InputData::new(), } } @@ -54,34 +54,32 @@ impl<'data> ElfOutput<'data> { Self { destination, file: elf_bin_from_object(other), - segment_data: SegmentData::new(), + input_data: InputData::new(), } } - fn populate_sections(&mut self) -> Result { + fn populate_sections(&mut self) -> Result<(), Error> { let mut names = Vec::new(); let mut name_idx = 0usize; - let mut result = 0u64; - for (name, sections) in self.segment_data.sections_mut() { - let mut data = Vec::new(); + for (name, sections) in self.input_data.sections_mut() { + let mut data_size = 0; - for t in sections.iter_mut() { - if let Some(iter) = &mut t.data { - data.extend(iter.as_mut()); + for t in sections.iter() { + if let Some(bytes) = t.data { + data_size += bytes.len(); } } - if data.is_empty() { + if data_size == 0{ continue; } - result += data.len() as u64; names.push(name); let section = Section64 { name: name.into(), - header: make_section_header(name_idx, ShType::ProgBits, data.len()), - contents: Contents64::Raw(data), + 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); @@ -90,7 +88,6 @@ impl<'data> ElfOutput<'data> { let name = ".shstrtab"; names.push(name); let string_table = build_string_table(names, true); - result += string_table.len() as u64; let section = Section64 { name: name.into(), header: make_section_header(name_idx, ShType::StrTab, string_table.len()), @@ -98,7 +95,7 @@ impl<'data> ElfOutput<'data> { }; self.file.add_section(section); - Ok(result) + Ok(()) } fn populate_segment(&mut self, offset: &mut u64, size: u64, which: SegmentType) { @@ -114,12 +111,15 @@ impl<'data> ElfOutput<'data> { 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_memsz = 0; + page_size = 0; } } @@ -127,7 +127,7 @@ impl<'data> ElfOutput<'data> { self.file.ehdr.e_phnum += 1; self.file.ehdr.e_phentsize = size_of::() as u16; - *offset += next_page(size); + *offset += page_size; } fn populate_segments(&mut self) -> Result { @@ -136,29 +136,25 @@ impl<'data> ElfOutput<'data> { // contains .text + .rodata as one segment self.populate_segment( &mut offset, - self.segment_data.program_size(), + self.input_data.program_size(), SegmentType::Text, ); // contains .data as one segment self.populate_segment( &mut offset, - self.segment_data.data_size(), + self.input_data.data_size(), SegmentType::Data, ); // contains .bss as one segment - self.populate_segment(&mut offset, self.segment_data.bss_size(), SegmentType::Bss); + self.populate_segment(&mut offset, self.input_data.bss_size(), SegmentType::Bss); Ok(offset) } } impl<'data> Output<'data> for ElfOutput<'data> { - fn allocate(&mut self, size: u64) -> Result { - Ok(size) - } - fn append_section(&mut self, section: Section<'data>) -> Result<(), Error> { - self.segment_data.append_section(section) + self.input_data.append_section(section) } fn finalize(mut self) -> Result { @@ -169,20 +165,19 @@ impl<'data> Output<'data> for ElfOutput<'data> { self.populate_sections()?; let page_size = self.populate_segments()?; + 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; // TODO: align - self.file.ehdr.e_shoff = self.file.ehdr.e_phoff - + u64::from(self.file.ehdr.e_phnum * self.file.ehdr.e_phentsize) - + page_size; - self.file.ehdr.e_shnum = self.file.sections.len() as u16; - self.file.ehdr.e_entry = 0x100; // TODO + 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 str_path = expand_path(&self.destination)?; let mut writer = file_writer(str_path, 0o755)?; let mut offset = 0; + // write ELF header offset += writer.write(&self.file.ehdr.to_le_bytes())?; // write program header table @@ -191,17 +186,32 @@ impl<'data> Output<'data> for ElfOutput<'data> { offset += writer.write(&seg.header.to_le_bytes())?; } - // write section/segment data + eprintln!("SH start: {}", offset); + // write section header table for sec in self.file.sections.iter_mut() { - sec.header.sh_offset = offset as u64; - offset += write_padded(&mut writer, offset, &sec.to_le_bytes())?; + if sec.header.get_type() == ShType::StrTab { + sec.header.sh_offset = offset as u64 + u64::from(SHS); + } + offset += writer.write(&sec.header.to_le_bytes())?; + if sec.header.get_type() == ShType::StrTab { + offset += writer.write(&sec.to_le_bytes())?; + } } - eprintln!("SHT: {}", offset); - // write section header table - for sec in self.file.sections.iter() { - offset += writer.write(&sec.header.to_le_bytes())?; + // program data (segments) + offset += pad_to_next_page(&mut writer, offset)?; + eprintln!("Prog start: {}", offset); + // write section/segment data + for bytes in self.input_data.program_bytes() { + offset += writer.write(bytes)?; + } + offset += pad_to_next_page(&mut writer, offset)?; + + eprintln!("Data start: {}", offset); + for bytes in self.input_data.data_bytes() { + offset += writer.write(bytes)?; } + writer.flush()?; Ok(self.destination) @@ -213,7 +223,7 @@ fn file_writer(output_filename: &str, permission: u32) -> Result let file = std::fs::OpenOptions::new() .create(true) - .read(true) + .truncate(true) .write(true) .mode(permission) .open(output_filename)?; @@ -271,14 +281,13 @@ fn next_page(size: u64) -> u64 { page_size * pages_needed } -fn write_padded(writer: &mut BufWriter, offset: usize, data: &[u8]) -> Result { +fn pad_to_next_page(writer: &mut BufWriter, offset: usize) -> Result { let page_size = page_size::get(); - let mut written = 0usize; - written += writer.write(data)?; - let padding = page_size - (page_size % offset); + let padding = page_size - (offset % page_size); - written += writer.get_mut().seek(SeekFrom::Current(padding as i64))? as usize; + eprintln!("Padding from: {} with: {}", offset, padding); + writer.seek(SeekFrom::Current(padding.try_into()?))?; - Ok(written) + Ok(padding) } diff --git a/src/formats/elf/segment.rs b/src/formats/elf/segment.rs index 4ad8d22..56a3ff6 100644 --- a/src/formats/elf/segment.rs +++ b/src/formats/elf/segment.rs @@ -1,5 +1,5 @@ use crate::common::SectionInfo; -use crate::common::{Section, SectionDataIterator}; +use crate::common::{Section}; use crate::error::Error; const SI_TEXT: usize = 0; @@ -15,9 +15,9 @@ pub enum SegmentType { const SEGMENT_NAMES: [&str; 4] = [".text", ".rodata", ".data", ".bss"]; -pub struct SegmentData<'data>([Vec>; 4]); +pub struct InputData<'data>([Vec>; 4]); -impl<'data> SegmentData<'data> { +impl<'data> InputData<'data> { pub fn new() -> Self { Self([Vec::new(), Vec::new(), Vec::new(), Vec::new()]) } @@ -42,20 +42,18 @@ impl<'data> SegmentData<'data> { .map(|(i, s)| (SEGMENT_NAMES[i], s)) } - pub fn program_data<'local>(&'local self) -> Box + 'local> { + pub fn program_bytes<'local>(&'local self) -> impl Iterator { let text_iter = self.0[SI_TEXT].iter(); let rodata_iter = self.0[SI_RODATA].iter(); - let data1 = text_iter.filter_map(|si| match si.data.as_ref() { - Some(iter) => Some(iter.clone_to_box()), - None => None, - }); - let data2 = rodata_iter.filter_map(|si| match si.data.as_ref() { - Some(iter) => Some(iter.clone_to_box()), - None => None, - }); + let data1 = text_iter.filter_map(|si| si.data); + let data2 = rodata_iter.filter_map(|si| si.data); - Box::new(data1.chain(data2).flatten()) + data1.chain(data2) + } + + pub fn data_bytes<'local>(&'local self) -> impl Iterator { + self.0[SI_DATA].iter().filter_map(|si| si.data) } // .text + .rodata @@ -92,7 +90,7 @@ impl<'data> SegmentData<'data> { } } -impl Default for SegmentData<'_> { +impl Default for InputData<'_> { fn default() -> Self { Self::new() } diff --git a/src/linker.rs b/src/linker.rs index a3bf6f6..3a9cd94 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -19,27 +19,13 @@ impl<'data> Linker<'data> { } pub fn link(&'data self, output: &mut dyn Output<'data>) -> Result { - let allocated = output.allocate(self.total_size()?)?; - for r in self.relocatables.iter() { for s in r.sections() { output.append_section(s?)?; } } - Ok(allocated) - } - - fn total_size(&self) -> Result { - let mut result = 0u64; - - for o in self.relocatables.iter() { - for s in o.sections() { - result += s?.size()?; - } - } - - Ok(result) + Ok(0) } }