first basic segment/section out

master
Ales Katona 4 years ago
parent 54adb8559d
commit dc59102570
Signed by: almindor
GPG Key ID: 2F773149BF38B48F

@ -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<u64, Error> {
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<Self, Self::Error> {
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),
}
}

@ -21,7 +21,7 @@ impl Storage {
}
pub fn destination(&self) -> Result<PathBuf, Error> {
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)))

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

@ -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<SectionInfo<'data>>,
data: Vec<SectionInfo<'data>>,
bss: Vec<SectionInfo<'data>>,
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<u64, Error> {
use elf_utilities::section::{Contents64, Shdr64, Type};
fn populate_sections(&mut self) -> Result<u64, Error> {
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();
for t in self.text.iter_mut() {
for t in sections.iter_mut() {
if let Some(iter) = &mut t.data {
data.extend(iter.as_mut());
}
}
let mut header = Shdr64::default();
header.set_type(Type::ProgBits);
header.sh_entsize = data.len() as u64;
if data.is_empty() {
continue;
}
let sec64 = Section64 {
name: ".text".into(),
header,
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);
}
self.dump.file.add_section(sec64);
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(0)
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 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::<Phdr64>() as u16;
*offset += size;
size
}
fn populate_segments(&mut self) -> Result<u64, Error> {
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<PathBuf, Error> {
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::<Ehdr64>() as u64;
const PHS: u64 = size_of::<Phdr64>() as u64;
const SHS: u16 = size_of::<Shdr64>() 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<BufWriter<File>, 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
}

@ -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<SectionInfo<'data>>; 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<Item = (&'static str, &mut Vec<SectionInfo<'data>>)> {
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()
}
}
Loading…
Cancel
Save