|
|
|
@ -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<File>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ElfOutput {
|
|
|
|
|
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 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::<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 {
|
|
|
|
@ -142,38 +45,62 @@ impl Output<ElfObject> for ElfOutput {
|
|
|
|
|
const PHS: u16 = size_of::<Phdr64>() as u16;
|
|
|
|
|
const SHS: u16 = size_of::<Shdr64>() 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<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;
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
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<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> {
|
|
|
|
|
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<File>, offset: usize) -> Result<usize, Error> {
|
|
|
|
|
let page_size = page_size::get();
|
|
|
|
|
|
|
|
|
|