use crate::types::{ElfFile, ElfHeader64, ProgramHeader64, SectionHeader64};
use gaia_binary::{LittleEndian, WriteBytesExt};
use gaia_types::GaiaError;
use std::io::{Seek, Write};
#[derive(Debug)]
pub struct ElfWriter<W> {
writer: W,
}
impl<W> ElfWriter<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
pub fn finish(self) -> W {
self.writer
}
}
impl<W: Write> ElfWriter<W> {
pub fn write_elf_file(&mut self, elf_file: &ElfFile) -> Result<(), GaiaError>
where
W: Seek,
{
self.write_elf_header(&elf_file.header)?;
for program_header in &elf_file.program_headers {
self.write_program_header(program_header)?;
}
self.align_to_boundary(0x1000)?;
self.writer.write_all(&elf_file.data)?;
Ok(())
}
pub fn write_elf_header(&mut self, header: &ElfHeader64) -> Result<(), GaiaError> {
self.writer.write_all(&header.e_ident)?;
self.writer.write_u16::<LittleEndian>(header.e_type)?;
self.writer.write_u16::<LittleEndian>(header.e_machine)?;
self.writer.write_u32::<LittleEndian>(header.e_version)?;
self.writer.write_u64::<LittleEndian>(header.e_entry)?;
self.writer.write_u64::<LittleEndian>(header.e_phoff)?;
self.writer.write_u64::<LittleEndian>(header.e_shoff)?;
self.writer.write_u32::<LittleEndian>(header.e_flags)?;
self.writer.write_u16::<LittleEndian>(header.e_ehsize)?;
self.writer.write_u16::<LittleEndian>(header.e_phentsize)?;
self.writer.write_u16::<LittleEndian>(header.e_phnum)?;
self.writer.write_u16::<LittleEndian>(header.e_shentsize)?;
self.writer.write_u16::<LittleEndian>(header.e_shnum)?;
self.writer.write_u16::<LittleEndian>(header.e_shstrndx)?;
Ok(())
}
pub fn write_program_header(&mut self, header: &ProgramHeader64) -> Result<(), GaiaError> {
self.writer.write_u32::<LittleEndian>(header.p_type)?;
self.writer.write_u32::<LittleEndian>(header.p_flags)?;
self.writer.write_u64::<LittleEndian>(header.p_offset)?;
self.writer.write_u64::<LittleEndian>(header.p_vaddr)?;
self.writer.write_u64::<LittleEndian>(header.p_paddr)?;
self.writer.write_u64::<LittleEndian>(header.p_filesz)?;
self.writer.write_u64::<LittleEndian>(header.p_memsz)?;
self.writer.write_u64::<LittleEndian>(header.p_align)?;
Ok(())
}
pub fn write_section_header(&mut self, header: &SectionHeader64) -> Result<(), GaiaError> {
self.writer.write_u32::<LittleEndian>(header.sh_name)?;
self.writer.write_u32::<LittleEndian>(header.sh_type)?;
self.writer.write_u64::<LittleEndian>(header.sh_flags)?;
self.writer.write_u64::<LittleEndian>(header.sh_addr)?;
self.writer.write_u64::<LittleEndian>(header.sh_offset)?;
self.writer.write_u64::<LittleEndian>(header.sh_size)?;
self.writer.write_u32::<LittleEndian>(header.sh_link)?;
self.writer.write_u32::<LittleEndian>(header.sh_info)?;
self.writer.write_u64::<LittleEndian>(header.sh_addralign)?;
self.writer.write_u64::<LittleEndian>(header.sh_entsize)?;
Ok(())
}
pub fn align_to_boundary(&mut self, boundary: u32) -> Result<(), GaiaError>
where
W: Seek,
{
let current_pos = self.writer.stream_position()?;
let alignment = boundary as u64;
let padding = (alignment - (current_pos % alignment)) % alignment;
for _ in 0..padding {
self.writer.write_u8(0)?;
}
Ok(())
}
pub fn pad_to_offset(&mut self, offset: u64) -> Result<(), GaiaError>
where
W: Seek,
{
let current_pos = self.writer.stream_position()?;
if current_pos < offset {
let padding = offset - current_pos;
for _ in 0..padding {
self.writer.write_u8(0)?;
}
}
Ok(())
}
}
#[derive(Debug)]
pub struct ElfBuilder {
elf_file: ElfFile,
}
impl ElfBuilder {
pub fn new() -> Self {
Self { elf_file: ElfFile::new() }
}
pub fn set_entry_point(&mut self, entry: u64) -> &mut Self {
self.elf_file.set_entry_point(entry);
self
}
pub fn add_code_segment(&mut self, code: Vec<u8>) -> &mut Self {
let code_size = code.len() as u64;
let code_offset = 0x1000; let code_vaddr = 0x401000;
let program_header = ProgramHeader64::new_load_segment(code_offset, code_vaddr, code_size);
self.elf_file.add_program_header(program_header);
self.elf_file.data = code;
self
}
pub fn build(self) -> ElfFile {
self.elf_file
}
pub fn write_to<W: Write + Seek>(&self, writer: W) -> Result<W, GaiaError> {
let mut elf_writer = ElfWriter::new(writer);
elf_writer.write_elf_file(&self.elf_file)?;
Ok(elf_writer.finish())
}
pub fn to_bytes(&self) -> Vec<u8> {
self.elf_file.to_bytes()
}
}
impl Default for ElfBuilder {
fn default() -> Self {
Self::new()
}
}