468 lines
14 KiB
Rust
468 lines
14 KiB
Rust
use std::{collections::HashMap, env, fs, path::PathBuf};
|
|
|
|
use anyhow::{anyhow, bail, Context, Result};
|
|
use base64::{engine::general_purpose::STANDARD, Engine};
|
|
use flagset::{flags, FlagSet};
|
|
use flate2::{write::GzEncoder, Compression};
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
|
|
|
flags! {
|
|
#[repr(u8)]
|
|
#[derive(Deserialize_repr, Serialize_repr)]
|
|
pub enum ObjSymbolFlags: u8 {
|
|
Global,
|
|
Local,
|
|
Weak,
|
|
Common,
|
|
Hidden,
|
|
}
|
|
}
|
|
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub enum ObjSymbolKind {
|
|
Unknown,
|
|
Function,
|
|
Object,
|
|
Section,
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub enum ObjRelocKind {
|
|
Absolute,
|
|
PpcAddr16Hi,
|
|
PpcAddr16Ha,
|
|
PpcAddr16Lo,
|
|
PpcRel24,
|
|
PpcRel14,
|
|
PpcEmbSda21,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct OutSymbol {
|
|
pub kind: ObjSymbolKind,
|
|
pub name: String,
|
|
pub size: u32,
|
|
pub flags: ObjSymbolFlagSet,
|
|
pub section: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct OutReloc {
|
|
pub offset: u32,
|
|
pub kind: ObjRelocKind,
|
|
pub symbol: usize,
|
|
pub addend: i32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct FunctionSignature {
|
|
pub symbol: usize,
|
|
pub hash: String,
|
|
pub signature: String,
|
|
pub symbols: Vec<OutSymbol>,
|
|
pub relocations: Vec<OutReloc>,
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize_repr, Serialize_repr)]
|
|
#[repr(u8)]
|
|
pub enum SigSymbolKind {
|
|
Unknown = 0,
|
|
Function = 1,
|
|
Object = 2,
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize_repr, Serialize_repr)]
|
|
#[repr(u8)]
|
|
pub enum SigSection {
|
|
Init = 0,
|
|
Extab = 1,
|
|
Extabindex = 2,
|
|
Text = 3,
|
|
Ctors = 4,
|
|
Dtors = 5,
|
|
Rodata = 6,
|
|
Data = 7,
|
|
Bss = 8,
|
|
Sdata = 9,
|
|
Sbss = 10,
|
|
Sdata2 = 11,
|
|
Sbss2 = 12,
|
|
Dbgtext = 13,
|
|
Unknown = 255,
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
#[repr(u8)]
|
|
pub enum SigSymbolFlag {
|
|
Global = 1 << 0,
|
|
Local = 1 << 1,
|
|
Weak = 1 << 2,
|
|
Common = 1 << 3,
|
|
Hidden = 1 << 4,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct SigSymbol {
|
|
pub kind: SigSymbolKind,
|
|
pub name: String,
|
|
pub size: u32,
|
|
pub flags: u8, // SigSymbolFlag
|
|
pub section: SigSection,
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize_repr, Serialize_repr)]
|
|
#[repr(u8)]
|
|
pub enum SigRelocKind {
|
|
Absolute = 0,
|
|
PpcAddr16Hi = 1,
|
|
PpcAddr16Ha = 2,
|
|
PpcAddr16Lo = 3,
|
|
PpcRel24 = 4,
|
|
PpcRel14 = 5,
|
|
PpcEmbSda21 = 6,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct SigReloc {
|
|
pub offset: u32,
|
|
pub symbol: usize,
|
|
pub kind: SigRelocKind,
|
|
pub addend: i32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct Sig {
|
|
pub symbol: usize,
|
|
pub data: Vec<u8>,
|
|
pub relocations: Vec<SigReloc>,
|
|
pub search: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct Output {
|
|
pub symbols: Vec<SigSymbol>,
|
|
pub signatures: HashMap<String, Sig>,
|
|
}
|
|
|
|
pub fn parse_yml(sig_str: &str) -> Result<Vec<FunctionSignature>> {
|
|
Ok(serde_yaml::from_str(sig_str)?)
|
|
}
|
|
|
|
const SIGNATURES: &[(&str, bool)] = &[
|
|
("__start", false),
|
|
("__init_registers", false),
|
|
("__init_hardware", false),
|
|
("__init_data", false),
|
|
("__set_debug_bba", false),
|
|
("__OSPSInit", false),
|
|
("__OSFPRInit", false),
|
|
("__OSCacheInit", false),
|
|
("DMAErrorHandler", false),
|
|
("DBInit", false),
|
|
("OSInit", false),
|
|
("__OSThreadInit", false),
|
|
("__OSInitIPCBuffer", false),
|
|
("EXIInit", false),
|
|
("EXIGetID", false),
|
|
("exit", false),
|
|
("_ExitProcess", false),
|
|
("__fini_cpp", false),
|
|
("__fini_cpp_exceptions", false),
|
|
("__destroy_global_chain", false),
|
|
("__init_cpp", false),
|
|
("__init_cpp_exceptions", false),
|
|
("InitMetroTRK", false),
|
|
("InitMetroTRKCommTable", false),
|
|
("OSExceptionInit", false),
|
|
("OSDefaultExceptionHandler", false),
|
|
("__OSUnhandledException", false),
|
|
("OSDisableScheduler", false),
|
|
("__OSReschedule", false),
|
|
("__OSInitSystemCall", false),
|
|
("OSInitAlarm", false),
|
|
("__OSInitAlarm", false),
|
|
// TODO aliases
|
|
// ("__OSEVStart", false),
|
|
// ("__OSDBINTSTART", false),
|
|
// ("__OSDBJUMPSTART", false),
|
|
("SIInit", false),
|
|
("SIGetType", false),
|
|
("SISetSamplingRate", false),
|
|
("SISetXY", false),
|
|
("VIGetTvFormat", false),
|
|
("DVDInit", false),
|
|
("DVDSetAutoFatalMessaging", false),
|
|
("OSSetArenaLo", false),
|
|
("OSSetArenaHi", false),
|
|
("OSSetMEM1ArenaLo", false),
|
|
("OSSetMEM1ArenaHi", false),
|
|
("OSSetMEM2ArenaLo", false),
|
|
("OSSetMEM2ArenaHi", false),
|
|
("__OSInitAudioSystem", false),
|
|
("__OSInitMemoryProtection", false),
|
|
// ("BATConfig", false), TODO
|
|
("ReportOSInfo", false),
|
|
("__check_pad3", false),
|
|
("OSResetSystem", false),
|
|
("OSReturnToMenu", false),
|
|
("__OSReturnToMenu", false),
|
|
("__OSShutdownDevices", false),
|
|
("__OSInitSram", false),
|
|
("__OSSyncSram", false),
|
|
("__OSGetExceptionHandler", false),
|
|
("OSRegisterResetFunction", false),
|
|
("OSRegisterShutdownFunction", false),
|
|
("DecrementerExceptionHandler", false),
|
|
("DecrementerExceptionCallback", false),
|
|
("__OSInterruptInit", false),
|
|
("__OSContextInit", false),
|
|
("OSSwitchFPUContext", false),
|
|
("OSReport", false),
|
|
("TRK_main", false),
|
|
("TRKNubWelcome", false),
|
|
("TRKInitializeNub", false),
|
|
("TRKInitializeIntDrivenUART", false),
|
|
("TRKEXICallBack", false),
|
|
("TRKLoadContext", false),
|
|
("TRKInterruptHandler", false),
|
|
("TRKExceptionHandler", false),
|
|
("TRKSaveExtended1Block", false),
|
|
("TRKNubMainLoop", false),
|
|
("TRKTargetContinue", false),
|
|
("TRKSwapAndGo", false),
|
|
("TRKRestoreExtended1Block", false),
|
|
("TRKInterruptHandlerEnableInterrupts", false),
|
|
("memset", false),
|
|
("__msl_runtime_constraint_violation_s", false),
|
|
("ClearArena", false),
|
|
("IPCCltInit", false),
|
|
("__OSInitSTM", false),
|
|
("IOS_Open", false),
|
|
("__ios_Ipc2", false),
|
|
("IPCiProfQueueReq", false),
|
|
("SCInit", false),
|
|
("SCReloadConfFileAsync", false),
|
|
("NANDPrivateOpenAsync", false),
|
|
("nandIsInitialized", false),
|
|
("nandOpen", false),
|
|
("nandGenerateAbsPath", false),
|
|
("nandGetHeadToken", false),
|
|
("ISFS_OpenAsync", false),
|
|
("nandConvertErrorCode", false),
|
|
("NANDLoggingAddMessageAsync", false),
|
|
("__NANDPrintErrorMessage", false),
|
|
("__OSInitNet", false),
|
|
("__DVDCheckDevice", false),
|
|
("__OSInitPlayTime", false),
|
|
("__OSStartPlayRecord", false),
|
|
("NANDInit", false),
|
|
("ISFS_OpenLib", false),
|
|
("ESP_GetTitleId", false),
|
|
("NANDSetAutoErrorMessaging", false),
|
|
("__DVDFSInit", false),
|
|
("__DVDClearWaitingQueue", false),
|
|
("__DVDInitWA", false),
|
|
("__DVDLowSetWAType", false),
|
|
("__fstLoad", false),
|
|
("DVDReset", false),
|
|
("DVDLowReset", false),
|
|
("DVDReadDiskID", false),
|
|
("stateReady", false),
|
|
("DVDLowWaitCoverClose", false),
|
|
("__DVDStoreErrorCode", false),
|
|
("DVDLowStopMotor", false),
|
|
("DVDGetDriveStatus", false),
|
|
("printf", false),
|
|
("sprintf", false),
|
|
("vprintf", false),
|
|
("vsprintf", false),
|
|
("vsnprintf", false),
|
|
("__pformatter", false),
|
|
("longlong2str", false),
|
|
("__mod2u", false),
|
|
("__FileWrite", false),
|
|
("fwrite", false),
|
|
("__fwrite", false),
|
|
("__stdio_atexit", false),
|
|
("__StringWrite", false),
|
|
("RSOStaticLocateObject", true),
|
|
];
|
|
|
|
fn main() -> Result<()> {
|
|
let output = std::process::Command::new("git").args(["rev-parse", "HEAD"]).output()?;
|
|
let rev = String::from_utf8(output.stdout)?;
|
|
println!("cargo:rustc-env=GIT_COMMIT_SHA={rev}");
|
|
println!("cargo:rustc-rerun-if-changed=.git/HEAD");
|
|
|
|
let mut symbols = Vec::<SigSymbol>::new();
|
|
let mut out = HashMap::<String, Sig>::new();
|
|
let in_dir = PathBuf::from("assets/signatures");
|
|
for &(name, search) in SIGNATURES {
|
|
let path = in_dir.join(format!("{name}.yml"));
|
|
println!("cargo:rustc-rerun-if-changed={}", path.display());
|
|
let str = fs::read_to_string(&path)
|
|
.with_context(|| format!("Failed to open '{}'", path.display()))?;
|
|
apply_sig(&str, &mut symbols, &mut out, search)?;
|
|
}
|
|
let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
|
|
rmp_serde::encode::write(&mut encoder, &Output { symbols, signatures: out })?;
|
|
let compressed = encoder.finish()?;
|
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
fs::write(out_dir.join("signatures.bin"), &compressed)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn apply_sig(
|
|
sig_str: &str,
|
|
symbols: &mut Vec<SigSymbol>,
|
|
out: &mut HashMap<String, Sig>,
|
|
search: bool,
|
|
) -> Result<()> {
|
|
let data = parse_yml(sig_str)?;
|
|
for in_sig in data {
|
|
let in_sym = &in_sig.symbols[in_sig.symbol];
|
|
let mut out_sig = Sig {
|
|
symbol: add_symbol(symbols, in_sym)?,
|
|
data: STANDARD.decode(&in_sig.signature)?,
|
|
relocations: vec![],
|
|
search,
|
|
};
|
|
for in_reloc in &in_sig.relocations {
|
|
out_sig.relocations.push(SigReloc {
|
|
offset: in_reloc.offset,
|
|
symbol: add_symbol(symbols, &in_sig.symbols[in_reloc.symbol])?,
|
|
kind: to_sig_reloc_kind(in_reloc.kind)?,
|
|
addend: in_reloc.addend,
|
|
});
|
|
}
|
|
out.insert(in_sym.name.clone(), out_sig);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn to_sym_section(s: Option<&str>) -> Result<SigSection> {
|
|
match s {
|
|
None => Ok(SigSection::Unknown),
|
|
Some(".init") => Ok(SigSection::Init),
|
|
Some("extab") => Ok(SigSection::Extab),
|
|
Some("extabindex") => Ok(SigSection::Extabindex),
|
|
Some(".text") => Ok(SigSection::Text),
|
|
Some(".ctors") => Ok(SigSection::Ctors),
|
|
Some(".dtors") => Ok(SigSection::Dtors),
|
|
Some(".rodata") => Ok(SigSection::Rodata),
|
|
Some(".data") => Ok(SigSection::Data),
|
|
Some(".bss") => Ok(SigSection::Bss),
|
|
Some(".sdata") => Ok(SigSection::Sdata),
|
|
Some(".sbss") => Ok(SigSection::Sbss),
|
|
Some(".sdata2") => Ok(SigSection::Sdata2),
|
|
Some(".sbss2") => Ok(SigSection::Sbss2),
|
|
Some(".dbgtext") => Ok(SigSection::Dbgtext),
|
|
Some(section) => Err(anyhow!("Unknown section {}", section)),
|
|
}
|
|
}
|
|
|
|
fn to_sig_symbol_kind(kind: ObjSymbolKind) -> Result<SigSymbolKind> {
|
|
match kind {
|
|
ObjSymbolKind::Unknown => Ok(SigSymbolKind::Unknown),
|
|
ObjSymbolKind::Function => Ok(SigSymbolKind::Function),
|
|
ObjSymbolKind::Object => Ok(SigSymbolKind::Object),
|
|
ObjSymbolKind::Section => Err(anyhow!("Section symbols unsupported")),
|
|
}
|
|
}
|
|
|
|
fn to_sig_symbol_flags(flags: ObjSymbolFlagSet) -> Result<u8> {
|
|
let mut out = 0;
|
|
for flag in flags.0 {
|
|
match flag {
|
|
ObjSymbolFlags::Global => {
|
|
out |= SigSymbolFlag::Global as u8;
|
|
}
|
|
ObjSymbolFlags::Local => {
|
|
out |= SigSymbolFlag::Local as u8;
|
|
}
|
|
ObjSymbolFlags::Weak => {
|
|
out |= SigSymbolFlag::Weak as u8;
|
|
}
|
|
ObjSymbolFlags::Common => {
|
|
out |= SigSymbolFlag::Common as u8;
|
|
}
|
|
ObjSymbolFlags::Hidden => {
|
|
out |= SigSymbolFlag::Hidden as u8;
|
|
}
|
|
}
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
fn to_sig_reloc_kind(kind: ObjRelocKind) -> Result<SigRelocKind> {
|
|
match kind {
|
|
ObjRelocKind::Absolute => Ok(SigRelocKind::Absolute),
|
|
ObjRelocKind::PpcAddr16Hi => Ok(SigRelocKind::PpcAddr16Hi),
|
|
ObjRelocKind::PpcAddr16Ha => Ok(SigRelocKind::PpcAddr16Ha),
|
|
ObjRelocKind::PpcAddr16Lo => Ok(SigRelocKind::PpcAddr16Lo),
|
|
ObjRelocKind::PpcRel24 => Ok(SigRelocKind::PpcRel24),
|
|
ObjRelocKind::PpcRel14 => Ok(SigRelocKind::PpcRel14),
|
|
ObjRelocKind::PpcEmbSda21 => Ok(SigRelocKind::PpcEmbSda21),
|
|
}
|
|
}
|
|
|
|
fn add_symbol(symbols: &mut Vec<SigSymbol>, in_sym: &OutSymbol) -> Result<usize> {
|
|
let sig_section = to_sym_section(in_sym.section.as_deref())?;
|
|
let sig_symbol_kind = to_sig_symbol_kind(in_sym.kind)?;
|
|
let sig_symbol_flags = to_sig_symbol_flags(in_sym.flags)?;
|
|
if let Some((idx, existing)) = symbols.iter_mut().enumerate().find(|(_, sym)| {
|
|
sym.kind == sig_symbol_kind && sym.size == in_sym.size && sym.name == in_sym.name
|
|
}) {
|
|
if existing.kind != sig_symbol_kind {
|
|
bail!(
|
|
"Mismatched types for {}: {:?} != {:?}",
|
|
in_sym.name,
|
|
sig_symbol_kind,
|
|
existing.kind
|
|
);
|
|
}
|
|
if existing.section != sig_section {
|
|
if existing.section == SigSection::Unknown || sig_section == SigSection::Unknown {
|
|
existing.section = SigSection::Unknown;
|
|
} else {
|
|
eprintln!(
|
|
"Mismatched sections for {}: {:?} != {:?}",
|
|
in_sym.name, sig_section, existing.section
|
|
);
|
|
existing.section = SigSection::Unknown;
|
|
}
|
|
}
|
|
if existing.size != in_sym.size {
|
|
bail!("Mismatched size for {}: {} != {}", in_sym.name, in_sym.size, existing.size);
|
|
}
|
|
if existing.flags != sig_symbol_flags {
|
|
if (existing.flags & (SigSymbolFlag::Weak as u8) != 0
|
|
&& sig_symbol_flags & (SigSymbolFlag::Weak as u8) == 0)
|
|
|| (sig_symbol_flags & (SigSymbolFlag::Weak as u8) != 0
|
|
&& existing.flags & (SigSymbolFlag::Weak as u8) == 0)
|
|
{
|
|
existing.flags |= SigSymbolFlag::Weak as u8;
|
|
} else {
|
|
eprintln!(
|
|
"Mismatched flags for {}: {} != {}",
|
|
in_sym.name, sig_symbol_flags, existing.flags
|
|
);
|
|
}
|
|
}
|
|
return Ok(idx);
|
|
}
|
|
let idx = symbols.len();
|
|
symbols.push(SigSymbol {
|
|
kind: sig_symbol_kind,
|
|
name: in_sym.name.clone(),
|
|
size: in_sym.size,
|
|
flags: sig_symbol_flags,
|
|
section: sig_section,
|
|
});
|
|
Ok(idx)
|
|
}
|