use headers directly

master
Ales Katona 4 years ago
parent f8acbd37f3
commit b9cc07fa1d
Signed by: almindor
GPG Key ID: 2F773149BF38B48F

@ -10,8 +10,28 @@ pub enum SegmentType {
Bss, Bss,
} }
type SegmentSections = Vec<SectionInfo>; #[derive(Default)]
pub struct SegmentSections {
sections_info: Vec<SectionInfo>,
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<Item = &SectionInfo> {
self.sections_info.iter()
}
pub fn data_size(&self) -> u64 {
self.data_size
}
}
// TODO: use attributes for field section names, indexes etc.
#[derive(Default)] #[derive(Default)]
pub struct Loadable { pub struct Loadable {
text: SegmentSections, text: SegmentSections,
@ -32,11 +52,19 @@ impl Loadable {
Ok(()) Ok(())
} }
pub fn sections(&self) -> impl Iterator<Item = (&'static str, &SegmentSections)> { pub const fn section_names() -> &'static [&'static str] {
let text = once(&self.text).map(|s| (".text", s)); &[".text", ".rodata", ".data", ".bss"]
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_count() -> usize {
Self::section_names().len()
}
pub fn segment_sections(&self) -> impl Iterator<Item = &SegmentSections> {
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) text.chain(rodata).chain(data).chain(bss)
} }

@ -8,10 +8,9 @@ use std::{
}; };
use elf_utilities::{ use elf_utilities::{
file::ELF64,
header::Ehdr64, header::Ehdr64,
section::{build_string_table, Contents64, Section64, Shdr64, Type as ShType}, section::{build_string_table, Shdr64, Type as ShType},
segment::{Phdr64, Segment64, Type as SeType, PF_R, PF_W, PF_X}, segment::{Phdr64, Type as SeType, PF_R, PF_W, PF_X},
}; };
use crate::{ use crate::{
@ -23,117 +22,21 @@ use super::ElfObject;
pub struct ElfOutput { pub struct ElfOutput {
destination: PathBuf, destination: PathBuf,
file: ELF64,
writer: BufWriter<File>, writer: BufWriter<File>,
} }
impl ElfOutput { impl ElfOutput {
pub fn new(destination: PathBuf) -> Result<Self, Error> { pub fn new(destination: PathBuf) -> Result<Self, Error> {
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 str_path = expand_path(&destination)?;
let writer = file_writer(str_path, 0o755)?; let writer = make_file_writer(str_path, 0o755)?;
let result = Self { let result = Self {
destination, destination,
file: elf,
writer, writer,
}; };
Ok(result) 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::<Phdr64>() as u16;
*offset += page_size;
}
fn populate_segments(&mut self, loadable: &Loadable) -> Result<u64, Error> {
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<ElfObject> for ElfOutput { impl Output<ElfObject> for ElfOutput {
@ -142,38 +45,62 @@ impl Output<ElfObject> for ElfOutput {
const PHS: u16 = size_of::<Phdr64>() as u16; const PHS: u16 = size_of::<Phdr64>() as u16;
const SHS: u16 = size_of::<Shdr64>() as u16; const SHS: u16 = size_of::<Shdr64>() as u16;
self.populate_sections(loadable)?; let strtab = make_strtab();
let page_size = self.populate_segments(loadable)?; let mut ehdr = make_elf_header();
ehdr.e_shnum = Loadable::section_count() as u16 + 1;
self.file.ehdr.e_shnum = self.file.sections.len() as u16; ehdr.e_phnum = 3; // TODO: probably move to loadable?
self.file.ehdr.e_entry = 4096; // TODO ehdr.e_shstrndx = ehdr.e_shnum - 1;
self.file.ehdr.e_phentsize = PHS; ehdr.e_entry = 4096; // TODO: find .start symbol eventual location
self.file.ehdr.e_shentsize = SHS; ehdr.e_phentsize = PHS;
self.file.ehdr.e_phoff = EHS; ehdr.e_shentsize = SHS;
self.file.ehdr.e_shoff = self.file.ehdr.e_phoff ehdr.e_phoff = EHS;
+ (usize::from(self.file.ehdr.e_phentsize) * self.file.segments.len()) as u64; ehdr.e_shoff =
ehdr.e_phoff + (usize::from(ehdr.e_phentsize) * usize::from(ehdr.e_phnum)) as u64;
let mut offset = 0; let mut offset = 0;
// write ELF header // 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 // write program header table
for seg in self.file.segments.iter_mut() { // contains .text + .rodata as one segment header
seg.header.p_offset = offset as u64; let ph = make_program_header(offset, loadable.program_size(), SegmentType::Text);
offset += self.writer.write(&seg.header.to_le_bytes())?; 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); eprintln!("SH start: {}", offset);
// write section header table let mut name_idx = 0;
for sec in self.file.sections.iter_mut() { // write section header table (text + rodata + data + bss)
if sec.header.get_type() == ShType::StrTab { for sections in loadable.segment_sections() {
sec.header.sh_offset = offset as u64 + u64::from(SHS); // TODO: find out data_offset for these headers
} let sh = make_section_header(
offset += self.writer.write(&sec.header.to_le_bytes())?; strtab.1[name_idx],
if sec.header.get_type() == ShType::StrTab { ShType::ProgBits,
offset += self.writer.write(&sec.to_le_bytes())?; 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) // program data (segments)
@ -196,7 +123,7 @@ impl Output<ElfObject> for ElfOutput {
} }
} }
fn file_writer(output_filename: &str, permission: u32) -> Result<BufWriter<File>, Error> { fn make_file_writer(output_filename: &str, permission: u32) -> Result<BufWriter<File>, Error> {
use std::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::OpenOptionsExt;
let file = std::fs::OpenOptions::new() let file = std::fs::OpenOptions::new()
@ -209,7 +136,23 @@ fn file_writer(output_filename: &str, permission: u32) -> Result<BufWriter<File>
Ok(BufWriter::new(file)) 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::<Ehdr64>() as u16;
ehdr
}
fn make_section_header(name_idx: usize, stype: ShType, offset: u64, size: u64) -> Shdr64 {
let mut h = Shdr64::default(); let mut h = Shdr64::default();
h.set_type(stype); 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_link = 0;
h.sh_size = size; h.sh_size = size;
// filled at finalize() // filled at finalize()
// h.sh_offset = offset; h.sh_offset = offset;
h 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<u8>, Vec<usize>) {
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> { fn expand_path(path: &Path) -> Result<&str, Error> {
use std::io::Error as IOError; use std::io::Error as IOError;
use std::io::ErrorKind; 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<File>, offset: usize) -> Result<usize, Error> { fn pad_to_next_page(writer: &mut BufWriter<File>, offset: usize) -> Result<usize, Error> {
let page_size = page_size::get(); let page_size = page_size::get();

Loading…
Cancel
Save