use gpcas_isa::ELFFileHeaderFront;
const STACK_SIZE: u64 = 0x10_0000; const VECTOR_SIZE: u64 = 0x100; const ENVIRONMENT_SIZE: u64 = 0x100;
const BASE_IP: u32 = 0x1000;
const BASE_DATAP: u32 = 0x2000;
const BASE_THREADP: u32 = 0x4000;
const FLAG_ALLOCATE: u32 = 0x100;
const PERMISSION_EXECUTE: u32 = 0x1;
const PERMISSION_WRITE: u32 = 0x2;
const PERMISSION_READ: u32 = 0x4;
pub struct Program {
pub data: Vec<u8>,
pub start_address: usize,
pub stack_address: usize,
pub ip_base: u64,
pub datap: u64,
pub threadp: u64,
pub memory_map: Vec<Segment>,
}
pub struct Segment {
pub address: u64,
addend: u64,
}
impl Segment {
pub fn flags(&self) -> u64 {
self.addend & 0x7
}
pub fn offset(&self) -> u64 {
(self.addend as i64 & -0x8) as u64
}
}
pub struct ForwardComELFFile {
header: ForwardComELFFileHeader,
program_headers: Vec<ProgramHeader>,
}
#[derive(Debug, Default)]
#[repr(C)]
struct ForwardComELFFileHeader {
pub front: ELFFileHeaderFront,
pub program_entry_location: u64,
pub program_header_table_offset: u64,
pub section_header_table_offset: u64,
pub flags: u32,
pub header_size: u16,
pub program_header_table_entry_size: u16,
pub program_header_table_entry_count: u16,
pub section_header_table_entry_size: u16,
pub section_header_table_entry_count: u32,
pub section_header_string_table_index: u32,
pub stack_vector_count: u32,
pub stack_size: u64,
pub ip_base: u64,
pub datap_base: u64,
pub threadp_base: u64,
}
impl From<[u8; std::mem::size_of::<ForwardComELFFileHeader>()]> for ForwardComELFFileHeader {
fn from(from: [u8; std::mem::size_of::<ForwardComELFFileHeader>()]) -> Self {
unsafe { std::mem::transmute(from) }
}
}
#[derive(Debug, Default)]
#[repr(C)]
struct ProgramHeader {
pub segment_type: u32,
pub flags: u32,
pub file_offset: u64,
pub virtual_address: u64,
pub physical_address: u64,
pub file_size: u64,
pub memory_size: u64,
pub alignment: u8,
_padding: [u8; 7],
}
impl From<[u8; std::mem::size_of::<ProgramHeader>()]> for ProgramHeader {
fn from(from: [u8; std::mem::size_of::<ProgramHeader>()]) -> Self {
unsafe { std::mem::transmute(from) }
}
}
pub fn load_forwardcom_elf(program_data: &[u8]) -> Result<Program, String> {
let elf_data = parse_forwardcom_elf(program_data)?;
let program_size = elf_data
.program_headers
.iter()
.fold(VECTOR_SIZE + ENVIRONMENT_SIZE + STACK_SIZE, |acc, entry| {
acc + align(entry.memory_size, entry.alignment)
});
let mut program_memory: Vec<u8> = vec![0; program_size as usize];
let mut current_offset = ENVIRONMENT_SIZE;
let mut last_flags = 0x0;
let mut ip_base = 0x0;
let mut datap = 0x0;
let mut threadp = 0x0;
let mut stack_address = 0x0;
let mut memory_map = Vec::new();
for program_header in elf_data.program_headers {
current_offset = align(current_offset as u64, program_header.alignment);
if program_header.virtual_address == 0 {
match program_header.flags & (BASE_IP | BASE_DATAP | BASE_THREADP) {
BASE_IP => ip_base = current_offset,
BASE_DATAP => datap = current_offset,
BASE_THREADP => threadp = current_offset,
_ => return Err("Found program segment without address base!".to_string()),
}
if last_flags & BASE_IP > 0 {
last_flags = BASE_DATAP | FLAG_ALLOCATE | PERMISSION_WRITE | PERMISSION_READ;
memory_map.push(Segment {
address: current_offset,
addend: last_flags as u64,
});
current_offset = align(current_offset + STACK_SIZE, 3);
datap = current_offset;
stack_address = current_offset;
}
}
if (program_header.flags & (PERMISSION_EXECUTE | PERMISSION_WRITE | PERMISSION_READ))
!= (last_flags & (PERMISSION_EXECUTE | PERMISSION_WRITE | PERMISSION_READ))
{
memory_map.push(Segment {
address: current_offset,
addend: (program_header.flags
& (PERMISSION_EXECUTE | PERMISSION_WRITE | PERMISSION_READ))
as u64,
})
}
unsafe {
program_memory
.as_mut_ptr()
.add(current_offset as usize)
.copy_from_nonoverlapping(
program_data
.as_ptr()
.add(program_header.file_offset as usize),
program_header.file_size as usize,
);
}
current_offset = align(
current_offset + program_header.memory_size,
program_header.alignment,
);
last_flags = program_header.flags;
}
if (last_flags & PERMISSION_READ) > 0 {
current_offset = align(current_offset + VECTOR_SIZE, 3);
}
if stack_address == 0 {
last_flags = BASE_DATAP | FLAG_ALLOCATE | PERMISSION_WRITE | PERMISSION_READ;
memory_map.push(Segment {
address: current_offset,
addend: last_flags as u64,
});
current_offset = align(current_offset + STACK_SIZE, 3);
stack_address = current_offset;
}
memory_map.push(Segment {
address: current_offset,
addend: 0x0,
});
Ok(Program {
data: program_memory,
start_address: (ip_base + elf_data.header.program_entry_location) as usize,
stack_address: stack_address as usize,
ip_base: ip_base + elf_data.header.ip_base,
datap: datap + elf_data.header.datap_base,
threadp: threadp + elf_data.header.threadp_base,
memory_map,
})
}
#[inline]
fn align(base: u64, alignment: u8) -> u64 {
(((base + (1 << alignment as u64) - 1) as i64) & -(1 << alignment as i64)) as u64
}
fn parse_forwardcom_elf(program_data: &[u8]) -> Result<ForwardComELFFile, String> {
if program_data.len() < std::mem::size_of::<ForwardComELFFileHeader>() {
Err("The header is too small to be a valid ForwardCom ELF file!".to_string())
} else {
let header_data: [u8; std::mem::size_of::<ForwardComELFFileHeader>()] = program_data
[..std::mem::size_of::<ForwardComELFFileHeader>()]
.try_into()
.unwrap();
let header: ForwardComELFFileHeader = header_data.into();
let mut program_headers: Vec<ProgramHeader> = Vec::new();
if header.front.class_id != 2 {
Err("The file is not a 64 bit file!".to_string())
} else if header.front.data_encoding_id != 1 {
Err("The file is not encoded in little endian format!".to_string())
} else if header.front.elf_version != 1 {
Err("The file is encoded in an unsupported ELF version!".to_string())
} else if header.front.abi_id != 250 {
Err("The file does not use the ForwardCom ABI!".to_string())
} else if header.front.abi_version != 1 {
Err("The file uses an unsupported version of the ForwardCom ABI!".to_string())
} else if header.front.file_type != 2 {
Err("The file is not an executable!".to_string())
} else if header.front.machine != 0x6233 {
Err("The file is not for ForwardCom!".to_string())
} else if (header.flags & 0x1) > 0 {
Err("The file contains unresolved references!".to_string())
} else if header.header_size != 104 {
Err("The file header has the wrong size!".to_string())
} else if header.program_header_table_entry_size as usize
!= std::mem::size_of::<ProgramHeader>()
{
Err("The file contains program headers of unsupported size!".to_string())
} else {
let offset = header.program_header_table_offset as usize;
for i in 0..header.program_header_table_entry_count as usize {
let offset = offset + i * std::mem::size_of::<ProgramHeader>();
let program_header_data: [u8; std::mem::size_of::<ProgramHeader>()] = program_data
[offset..offset + std::mem::size_of::<ProgramHeader>()]
.try_into()
.unwrap();
program_headers.push(program_header_data.into());
}
Ok(ForwardComELFFile {
header,
program_headers,
})
}
}
}