use goblin::elf::Elf;
use std::{collections::HashMap, mem, path::Path};
#[derive(serde_derive::Deserialize, serde_derive::Serialize, Debug)]
struct SceStubLibraryEntry {
name: u32,
version: [u8; 2],
flags: u16,
len: u8,
v_stub_count: u8,
stub_count: u16,
nid_table: u32,
stub_table: u32,
}
pub fn fix<T: AsRef<Path>>(path: T) {
let mut bytes = std::fs::read(&path).unwrap();
let elf = Elf::parse(&bytes).unwrap();
let shstrtab = {
let sh = &elf.section_headers[elf.header.e_shstrndx as usize];
let start = sh.sh_offset as usize;
let end = (sh.sh_offset + sh.sh_size) as usize;
&bytes[start..end]
};
let sections = elf
.section_headers
.iter()
.map(|sh| {
let name = shstrtab[sh.sh_name..]
.iter()
.take_while(|b| **b != 0)
.map(|b| *b as char)
.collect::<String>();
(name, sh)
})
.collect::<HashMap<_, _>>();
let lib_stub = match sections.get(".lib.stub") {
Some(s) => s,
None => return,
};
let lib_stub_btm = sections
.get(".lib.stub.btm")
.expect("could not find .lib.stub.btm section");
let rodata_sce_nid = sections
.get(".rodata.sceNid")
.expect("Could not find .rodata.sceNid section");
let start = lib_stub.sh_offset as usize;
let end = lib_stub_btm.sh_offset as usize;
assert_eq!((end - start) % mem::size_of::<SceStubLibraryEntry>(), 0);
let stubs_nid_sorted = {
let mut entries = bytes[start..end]
.chunks(mem::size_of::<SceStubLibraryEntry>())
.map(bincode::deserialize)
.map(Result::unwrap)
.enumerate()
.collect::<Vec<(_, SceStubLibraryEntry)>>();
entries.sort_unstable_by_key(|(_, s)| s.nid_table);
entries
};
let stub_entry_bufs = bytes[start..end].chunks_mut(mem::size_of::<SceStubLibraryEntry>());
for (i, stub_entry_buf) in stub_entry_bufs.enumerate() {
const NID_SIZE: u32 = 4;
let mut stub: SceStubLibraryEntry = bincode::deserialize(stub_entry_buf).unwrap();
let nid_end = stubs_nid_sorted
.get(1 + stubs_nid_sorted.iter().position(|&(j, _)| i == j).unwrap())
.map(|(_, s)| s.nid_table)
.unwrap_or(rodata_sce_nid.sh_addr as u32 + rodata_sce_nid.sh_size as u32);
stub.stub_count = ((nid_end - stub.nid_table) / NID_SIZE) as u16;
let serialized = bincode::serialize(&stub).unwrap();
stub_entry_buf.copy_from_slice(&serialized);
}
std::fs::write(path, bytes).unwrap();
}