use crate::error::ParseProblem;
use crate::helper::{ensure, Parser, ProblemLocation, Seeker};
use crate::Result;
#[derive(Debug, Clone)]
pub struct Rel {
pub module: u32,
pub version: u32,
pub name_offset: u32,
pub name_size: u32,
pub sections: Vec<Section>,
pub import_tables: Vec<ImportTable>,
pub prolog: Option<Symbol>,
pub epilog: Option<Symbol>,
pub unresolved: Option<Symbol>,
pub alignment: u32,
pub bss_alignment: u32,
pub fix_size: u32,
pub relocation_offset: Option<u32>,
pub import_offset: Option<u32>,
pub import_size: Option<u32>,
}
#[derive(Debug, Clone, Default)]
pub struct Section {
pub offset: u32,
pub size: u32,
pub executable: bool,
pub unknown: bool,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImportKind {
None,
Addr32,
Addr24,
Addr16,
Addr16Lo,
Addr16Hi,
Addr16Ha,
Addr14,
Rel24,
Rel14,
DolphinNop,
DolphinSection,
DolphinEnd,
DolphinMRKREF,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Import {
pub kind: ImportKind,
pub section: u8,
pub offset: u16,
pub addend: u32,
}
#[derive(Debug, Clone)]
pub struct ImportTable {
pub module: u32,
pub offset: u32,
pub imports: Vec<Import>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Symbol {
pub section: u32,
pub offset: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SectionOffset {
pub section: u32,
pub offset: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Relocation {
pub kind: ImportKind,
pub module: u32,
pub reference: SectionOffset,
pub target: SectionOffset,
}
impl Rel {
pub fn from_binary<D: Parser + Seeker>(mut reader: D) -> Result<Self> {
let base = reader.position()?;
let module = reader.bu32()?;
let _next = reader.bu32()?; let _prev = reader.bu32()?; let section_count = reader.bu32()?;
let section_offset = reader.bu32()?;
let name_offset = reader.bu32()?;
let name_size = reader.bu32()?;
let version = reader.bu32()?;
let _bss_size = reader.bu32()?;
let relocation_offset = reader.bu32()?;
let import_offset = reader.bu32()?;
let import_size = reader.bu32()?;
let prolog_section = reader.u8()?;
let epilog_section = reader.u8()?;
let unresolved_section = reader.u8()?;
let _bss_section = reader.u8()?; let prolog_offset = reader.bu32()?;
let epilog_offset = reader.bu32()?;
let unresolved_offset = reader.bu32()?;
let (align, bss_align) = if version >= 2 {
let align = reader.bu32()?;
let bss_align = reader.bu32()?;
(align, bss_align)
} else {
(1, 1)
};
let fix_size = if version >= 3 {
reader.bu32()?
} else {
0
};
ensure!(
version <= 3,
ParseProblem::UnsupportedVersion(version as usize, std::panic::Location::current())
);
ensure!(
section_count > 1,
ParseProblem::InvalidRange("no sections", std::panic::Location::current())
);
ensure!(
section_count < 32,
ParseProblem::InvalidRange(
"section count limit exceeded",
std::panic::Location::current()
)
);
ensure!(
section_offset >= 0x40,
ParseProblem::InvalidRange("section offset < 0x40", std::panic::Location::current())
);
let prolog = optional_symbol(prolog_section, prolog_offset);
let epilog = optional_symbol(epilog_section, epilog_offset);
let unresolved = optional_symbol(unresolved_section, unresolved_offset);
let sections = parse_sections(&mut reader, base, section_offset, section_count)?;
let import_tables = parse_imports(&mut reader, base, import_offset, import_size)?;
Ok(Rel {
module,
version,
name_offset,
name_size,
sections,
import_tables,
prolog,
epilog,
unresolved,
alignment: align,
bss_alignment: bss_align,
fix_size,
relocation_offset: Some(relocation_offset),
import_offset: Some(import_offset),
import_size: Some(import_size),
})
}
pub fn relocations(&self) -> RelocationIterator {
RelocationIterator {
rel: self,
table: 0,
section: None,
index: 0,
offset: 0,
}
}
}
fn optional_symbol(section: u8, offset: u32) -> Option<Symbol> {
if section != 0 {
Some(Symbol {
section: section as u32,
offset,
})
} else {
None
}
}
fn parse_sections<D: Parser + Seeker>(
reader: &mut D,
base: u64,
section_offset: u32,
section_count: u32,
) -> Result<Vec<Section>> {
let mut sections = Vec::<Section>::with_capacity(section_count as usize);
for i in 0..section_count {
let section_offset = base + section_offset as u64 + i as u64 * 8;
reader.goto(section_offset)?;
let offset_flags = reader.bu32()?; let offset = offset_flags & !0x3_u32;
let flags = offset_flags & 0x3_u32;
let size = reader.bu32()?;
let data = if offset > 0 {
ensure!(
size <= 0x2000000,
ParseProblem::InvalidRange("section too large", std::panic::Location::current())
);
reader.goto(base + offset as u64)?;
reader.read_as_vec(size as usize)?
} else {
Vec::new()
};
sections.push(Section {
offset,
size,
executable: flags & 1 != 0,
unknown: flags & 2 != 0,
data,
});
}
Ok(sections)
}
fn parse_imports<D: Parser + Seeker>(
reader: &mut D,
base: u64,
import_offset: u32,
import_size: u32,
) -> Result<Vec<ImportTable>> {
let mut import_tables = Vec::<ImportTable>::new();
let import_table_count = import_size / 8;
for i in 0..import_table_count {
reader.goto(base + (import_offset + i * 8) as u64)?;
let module = reader.bu32()?;
let offset = reader.bu32()?;
let mut imports = Vec::new();
reader.goto(base + offset as u64)?;
loop {
let offset = reader.bu16()?;
let kind = reader.u8()?;
let section = reader.u8()?;
let addend = reader.bu32()?;
let kind = match kind {
0 => ImportKind::None,
1 => ImportKind::Addr32,
2 => ImportKind::Addr24,
3 => ImportKind::Addr16,
4 => ImportKind::Addr16Lo,
5 => ImportKind::Addr16Hi,
6 => ImportKind::Addr16Ha,
7 => ImportKind::Addr14,
10 => ImportKind::Rel24,
11 => ImportKind::Rel14,
201 => ImportKind::DolphinNop,
202 => ImportKind::DolphinSection,
203 => ImportKind::DolphinEnd,
204 => ImportKind::DolphinMRKREF,
_ => {
return Err(ParseProblem::InvalidData(
"unknown import kind",
std::panic::Location::current(),
)
.into())
},
};
imports.push(Import {
kind,
section,
offset,
addend,
});
if kind == ImportKind::DolphinEnd {
break;
}
}
import_tables.push(ImportTable {
module,
offset,
imports,
});
}
Ok(import_tables)
}
pub struct RelocationIterator<'rel> {
rel: &'rel Rel,
table: usize,
section: Option<usize>,
index: usize,
offset: u32,
}
impl<'rel> Iterator for RelocationIterator<'rel> {
type Item = Relocation;
fn next(&mut self) -> Option<Self::Item> {
if self.table >= self.rel.import_tables.len() {
return None;
}
let table = &self.rel.import_tables[self.table];
if self.index >= table.imports.len() {
self.index = 0;
self.table += 1;
return self.next();
}
let import = &table.imports[self.index];
self.index += 1;
match import.kind {
ImportKind::None | ImportKind::DolphinEnd => self.next(),
ImportKind::DolphinMRKREF => {
self.next()
},
ImportKind::DolphinNop => {
self.offset += import.offset as u32;
self.next()
},
ImportKind::DolphinSection => {
self.section = Some(import.section as usize);
self.offset = 0;
self.next()
},
kind => {
if let Some(section) = self.section {
let relocation = Relocation {
kind,
module: table.module,
reference: SectionOffset {
section: import.section as u32,
offset: import.addend,
},
target: SectionOffset {
section: section as u32,
offset: self.offset + import.offset as u32,
},
};
self.offset = relocation.target.offset;
Some(relocation)
} else {
None
}
},
}
}
}