mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-06-11 00:53:39 +00:00
228 lines
8.3 KiB
Rust
228 lines
8.3 KiB
Rust
use std::io::{Read, Seek, SeekFrom};
|
|
|
|
use anyhow::{anyhow, ensure, Result};
|
|
use byteorder::{BigEndian, ReadBytesExt};
|
|
use cwdemangle::{demangle, DemangleOptions};
|
|
|
|
use crate::{
|
|
obj::{
|
|
ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
|
|
ObjSymbolFlags, ObjSymbolKind,
|
|
},
|
|
util::file::{read_c_string, read_string},
|
|
};
|
|
|
|
/// For RSO references to the DOL, the sections are hardcoded.
|
|
pub const DOL_SECTION_NAMES: [Option<&str>; 14] = [
|
|
None, // s_null
|
|
Some(".init"),
|
|
Some(".text"),
|
|
Some(".ctors"),
|
|
Some(".dtors"),
|
|
Some(".rodata"),
|
|
Some(".data"),
|
|
Some(".bss"),
|
|
Some(".sdata"),
|
|
Some(".sdata2"),
|
|
None, // s_zero
|
|
Some(".sbss"),
|
|
Some(".sbss2"),
|
|
None, // s_zero2
|
|
];
|
|
/// extabindex section index.
|
|
pub const DOL_SECTION_ETI: u32 = 241;
|
|
/// ABS symbol section index.
|
|
pub const DOL_SECTION_ABS: u32 = 65521;
|
|
|
|
pub fn process_rso<R: Read + Seek>(reader: &mut R) -> Result<ObjInfo> {
|
|
ensure!(reader.read_u32::<BigEndian>()? == 0, "Expected 'next' to be 0");
|
|
ensure!(reader.read_u32::<BigEndian>()? == 0, "Expected 'prev' to be 0");
|
|
let num_sections = reader.read_u32::<BigEndian>()?;
|
|
let section_info_offset = reader.read_u32::<BigEndian>()?;
|
|
let name_offset = reader.read_u32::<BigEndian>()?;
|
|
let name_size = reader.read_u32::<BigEndian>()?;
|
|
let version = reader.read_u32::<BigEndian>()?;
|
|
ensure!(version == 1, "Unsupported RSO version {}", version);
|
|
let bss_size = reader.read_u32::<BigEndian>()?;
|
|
let prolog_section = reader.read_u8()?;
|
|
let epilog_section = reader.read_u8()?;
|
|
let unresolved_section = reader.read_u8()?;
|
|
ensure!(reader.read_u8()? == 0, "Expected 'bssSection' to be 0");
|
|
let prolog_offset = reader.read_u32::<BigEndian>()?;
|
|
let epilog_offset = reader.read_u32::<BigEndian>()?;
|
|
let unresolved_offset = reader.read_u32::<BigEndian>()?;
|
|
let _internal_rel_offset = reader.read_u32::<BigEndian>()?;
|
|
let _internal_rel_size = reader.read_u32::<BigEndian>()?;
|
|
let external_rel_offset = reader.read_u32::<BigEndian>()?;
|
|
let external_rel_size = reader.read_u32::<BigEndian>()?;
|
|
let export_table_offset = reader.read_u32::<BigEndian>()?;
|
|
let export_table_size = reader.read_u32::<BigEndian>()?;
|
|
let export_table_name_offset = reader.read_u32::<BigEndian>()?;
|
|
let import_table_offset = reader.read_u32::<BigEndian>()?;
|
|
let import_table_size = reader.read_u32::<BigEndian>()?;
|
|
let import_table_name_offset = reader.read_u32::<BigEndian>()?;
|
|
|
|
let mut sections = Vec::with_capacity(num_sections as usize);
|
|
reader.seek(SeekFrom::Start(section_info_offset as u64))?;
|
|
let mut total_bss_size = 0;
|
|
for idx in 0..num_sections {
|
|
let offset = reader.read_u32::<BigEndian>()?;
|
|
let size = reader.read_u32::<BigEndian>()?;
|
|
log::debug!("Section {}: offset {:#X}, size {:#X}", idx, offset, size);
|
|
if size == 0 {
|
|
continue;
|
|
}
|
|
let exec = (offset & 1) == 1;
|
|
let offset = offset & !3;
|
|
|
|
let data = if offset == 0 {
|
|
vec![]
|
|
} else {
|
|
let position = reader.stream_position()?;
|
|
reader.seek(SeekFrom::Start(offset as u64))?;
|
|
let mut data = vec![0u8; size as usize];
|
|
reader.read_exact(&mut data)?;
|
|
reader.seek(SeekFrom::Start(position))?;
|
|
data
|
|
};
|
|
|
|
// println!("Section {} offset {:#X} size {:#X}", idx, offset, size);
|
|
|
|
sections.push(ObjSection {
|
|
name: format!(".section{}", idx),
|
|
kind: if offset == 0 {
|
|
ObjSectionKind::Bss
|
|
} else if exec {
|
|
ObjSectionKind::Code
|
|
} else {
|
|
ObjSectionKind::Data
|
|
},
|
|
address: 0,
|
|
size: size as u64,
|
|
data,
|
|
align: 0,
|
|
elf_index: idx as usize,
|
|
relocations: Default::default(),
|
|
original_address: 0,
|
|
file_offset: offset as u64,
|
|
section_known: false,
|
|
splits: Default::default(),
|
|
});
|
|
if offset == 0 {
|
|
total_bss_size += size;
|
|
}
|
|
}
|
|
ensure!(
|
|
total_bss_size == bss_size,
|
|
"Mismatched BSS size: {:#X} != {:#X}",
|
|
total_bss_size,
|
|
bss_size
|
|
);
|
|
|
|
let mut symbols = Vec::new();
|
|
let mut add_symbol = |rel_section_idx: u8, offset: u32, name: &str| -> Result<()> {
|
|
if rel_section_idx > 0 {
|
|
let (section_index, _) = sections
|
|
.iter()
|
|
.enumerate()
|
|
.find(|&(_, section)| section.elf_index == rel_section_idx as usize)
|
|
.ok_or_else(|| anyhow!("Failed to locate {name} section {rel_section_idx}"))?;
|
|
log::debug!("Adding {name} section {rel_section_idx} offset {offset:#X}");
|
|
symbols.push(ObjSymbol {
|
|
name: name.to_string(),
|
|
address: offset as u64,
|
|
section: Some(section_index),
|
|
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
|
kind: ObjSymbolKind::Function,
|
|
..Default::default()
|
|
});
|
|
}
|
|
Ok(())
|
|
};
|
|
add_symbol(prolog_section, prolog_offset, "_prolog")?;
|
|
add_symbol(epilog_section, epilog_offset, "_epilog")?;
|
|
add_symbol(unresolved_section, unresolved_offset, "_unresolved")?;
|
|
|
|
reader.seek(SeekFrom::Start(external_rel_offset as u64))?;
|
|
while reader.stream_position()? < (external_rel_offset + external_rel_size) as u64 {
|
|
let offset = reader.read_u32::<BigEndian>()?;
|
|
let id_and_type = reader.read_u32::<BigEndian>()?;
|
|
let id = (id_and_type & 0xFFFFFF00) >> 8;
|
|
let rel_type = id_and_type & 0xFF;
|
|
let sym_offset = reader.read_u32::<BigEndian>()?;
|
|
log::debug!(
|
|
"Reloc offset: {:#X}, id: {}, type: {}, sym offset: {:#X}",
|
|
offset,
|
|
id,
|
|
rel_type,
|
|
sym_offset
|
|
);
|
|
}
|
|
|
|
reader.seek(SeekFrom::Start(export_table_offset as u64))?;
|
|
while reader.stream_position()? < (export_table_offset + export_table_size) as u64 {
|
|
let name_off = reader.read_u32::<BigEndian>()?;
|
|
let name = read_c_string(reader, (export_table_name_offset + name_off) as u64)?;
|
|
let sym_off = reader.read_u32::<BigEndian>()?;
|
|
let section_idx = reader.read_u32::<BigEndian>()?;
|
|
let hash_n = reader.read_u32::<BigEndian>()?;
|
|
let calc = symbol_hash(&name);
|
|
ensure!(
|
|
hash_n == calc,
|
|
"Mismatched calculated hash for symbol {}: {:#X} != {:#X}",
|
|
name,
|
|
hash_n,
|
|
calc
|
|
);
|
|
let demangled_name = demangle(&name, &DemangleOptions::default());
|
|
let section = sections
|
|
.iter()
|
|
.enumerate()
|
|
.find(|&(_, section)| section.elf_index == section_idx as usize)
|
|
.map(|(idx, _)| idx)
|
|
// HACK: selfiles won't have any sections
|
|
.unwrap_or(section_idx as usize);
|
|
log::debug!(
|
|
"Export: {}, sym off: {:#X}, section: {}, ELF hash: {:#X}",
|
|
demangled_name.as_deref().unwrap_or(&name),
|
|
sym_off,
|
|
section_idx,
|
|
hash_n
|
|
);
|
|
symbols.push(ObjSymbol {
|
|
name,
|
|
demangled_name,
|
|
address: sym_off as u64,
|
|
section: Some(section),
|
|
..Default::default()
|
|
});
|
|
}
|
|
reader.seek(SeekFrom::Start(import_table_offset as u64))?;
|
|
while reader.stream_position()? < (import_table_offset + import_table_size) as u64 {
|
|
let name_off = reader.read_u32::<BigEndian>()?;
|
|
let name = read_c_string(reader, (import_table_name_offset + name_off) as u64)?;
|
|
let sym_off = reader.read_u32::<BigEndian>()?;
|
|
let section_idx = reader.read_u32::<BigEndian>()?;
|
|
log::debug!("Import: {}, sym off: {}, section: {}", name, sym_off, section_idx);
|
|
}
|
|
|
|
let name = match name_offset {
|
|
0 => String::new(),
|
|
_ => read_string(reader, name_offset as u64, name_size as usize)?,
|
|
};
|
|
|
|
let obj = ObjInfo::new(ObjKind::Relocatable, ObjArchitecture::PowerPc, name, symbols, sections);
|
|
Ok(obj)
|
|
}
|
|
|
|
fn symbol_hash(s: &str) -> u32 {
|
|
s.bytes().fold(0u32, |hash, c| {
|
|
let mut m = (hash << 4).wrapping_add(c as u32);
|
|
let n = m & 0xF0000000;
|
|
if n != 0 {
|
|
m ^= n >> 24;
|
|
}
|
|
m & !n
|
|
})
|
|
}
|