diff --git a/Cargo.lock b/Cargo.lock index 2f2e2d0..bf0e71c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.8.2" @@ -27,11 +42,14 @@ name = "anyhow" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +dependencies = [ + "backtrace", +] [[package]] name = "ar" version = "0.8.0" -source = "git+https://github.com/bjorn3/rust-ar.git?branch=do_not_remove_cg_clif_ranlib#de9ab0e56bf3a208381d342aa5b60f9ff2891648" +source = "git+https://github.com/bjorn3/rust-ar.git?branch=write_symbol_table#a61d6f5b84b0240f7afddf8cebc7e58ee1719829" [[package]] name = "argh" @@ -68,6 +86,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -161,7 +194,7 @@ dependencies = [ [[package]] name = "decomp-toolkit" -version = "0.2.2" +version = "0.3.0" dependencies = [ "anyhow", "ar", @@ -175,9 +208,9 @@ dependencies = [ "filetime", "fixedbitset", "flagset", + "flate2", "hex", "indexmap", - "lazy_static", "log", "memchr", "memmap2", @@ -187,6 +220,7 @@ dependencies = [ "once_cell", "ppc750cl", "regex", + "rmp-serde", "serde", "serde_repr", "serde_yaml", @@ -276,6 +310,16 @@ dependencies = [ "serde", ] +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -286,6 +330,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "gimli" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" + [[package]] name = "hashbrown" version = "0.12.3" @@ -366,12 +416,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.137" @@ -408,6 +452,15 @@ dependencies = [ "libc", ] +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "multimap" version = "0.8.3" @@ -465,6 +518,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + [[package]] name = "ppc750cl" version = "0.2.0" @@ -529,6 +588,34 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustix" version = "0.36.6" diff --git a/Cargo.toml b/Cargo.toml index 53aad5a..465d1b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "decomp-toolkit" description = "GameCube/Wii decompilation project tools." authors = ["Luke Street "] license = "MIT OR Apache-2.0" -version = "0.2.3" +version = "0.3.0" edition = "2021" publish = false build = "build.rs" @@ -21,8 +21,8 @@ panic = "abort" strip = "debuginfo" [dependencies] -anyhow = "1.0.64" -ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } +anyhow = { version = "1.0.64", features = ["backtrace"] } +ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "write_symbol_table" } argh = "0.1.8" base16ct = "0.1.1" base64 = "0.21.0" @@ -35,7 +35,6 @@ fixedbitset = "0.4.2" flagset = { version = "0.4.3", features = ["serde"] } hex = "0.4.3" indexmap = "1.9.2" -lazy_static = "1.4.0" log = "0.4.17" memchr = "2.5.0" memmap2 = "0.5.7" @@ -51,3 +50,14 @@ serde_yaml = "0.9.16" sha-1 = "0.10.0" smallvec = "1.10.0" topological-sort = "0.2.2" +flate2 = "1.0.25" + +[build-dependencies] +anyhow = { version = "1.0.64", features = ["backtrace"] } +base64 = "0.21.0" +flagset = { version = "0.4.3", features = ["serde"] } +serde = "1.0.152" +serde_repr = "0.1.10" +serde_yaml = "0.9.16" +rmp-serde = "1.1.1" +flate2 = "1.0.25" diff --git a/assets/ClearArena.yml b/assets/signatures/ClearArena.yml similarity index 100% rename from assets/ClearArena.yml rename to assets/signatures/ClearArena.yml diff --git a/assets/DBInit.yml b/assets/signatures/DBInit.yml similarity index 100% rename from assets/DBInit.yml rename to assets/signatures/DBInit.yml diff --git a/assets/DMAErrorHandler.yml b/assets/signatures/DMAErrorHandler.yml similarity index 100% rename from assets/DMAErrorHandler.yml rename to assets/signatures/DMAErrorHandler.yml diff --git a/assets/DVDGetDriveStatus.yml b/assets/signatures/DVDGetDriveStatus.yml similarity index 100% rename from assets/DVDGetDriveStatus.yml rename to assets/signatures/DVDGetDriveStatus.yml diff --git a/assets/DVDInit.yml b/assets/signatures/DVDInit.yml similarity index 100% rename from assets/DVDInit.yml rename to assets/signatures/DVDInit.yml diff --git a/assets/DVDLowReset.yml b/assets/signatures/DVDLowReset.yml similarity index 100% rename from assets/DVDLowReset.yml rename to assets/signatures/DVDLowReset.yml diff --git a/assets/DVDLowStopMotor.yml b/assets/signatures/DVDLowStopMotor.yml similarity index 100% rename from assets/DVDLowStopMotor.yml rename to assets/signatures/DVDLowStopMotor.yml diff --git a/assets/DVDLowWaitCoverClose.yml b/assets/signatures/DVDLowWaitCoverClose.yml similarity index 100% rename from assets/DVDLowWaitCoverClose.yml rename to assets/signatures/DVDLowWaitCoverClose.yml diff --git a/assets/DVDReadDiskID.yml b/assets/signatures/DVDReadDiskID.yml similarity index 100% rename from assets/DVDReadDiskID.yml rename to assets/signatures/DVDReadDiskID.yml diff --git a/assets/DVDReset.yml b/assets/signatures/DVDReset.yml similarity index 100% rename from assets/DVDReset.yml rename to assets/signatures/DVDReset.yml diff --git a/assets/DVDSetAutoFatalMessaging.yml b/assets/signatures/DVDSetAutoFatalMessaging.yml similarity index 100% rename from assets/DVDSetAutoFatalMessaging.yml rename to assets/signatures/DVDSetAutoFatalMessaging.yml diff --git a/assets/DecrementerExceptionCallback.yml b/assets/signatures/DecrementerExceptionCallback.yml similarity index 100% rename from assets/DecrementerExceptionCallback.yml rename to assets/signatures/DecrementerExceptionCallback.yml diff --git a/assets/DecrementerExceptionHandler.yml b/assets/signatures/DecrementerExceptionHandler.yml similarity index 100% rename from assets/DecrementerExceptionHandler.yml rename to assets/signatures/DecrementerExceptionHandler.yml diff --git a/assets/ESP_GetTitleId.yml b/assets/signatures/ESP_GetTitleId.yml similarity index 100% rename from assets/ESP_GetTitleId.yml rename to assets/signatures/ESP_GetTitleId.yml diff --git a/assets/EXIGetID.yml b/assets/signatures/EXIGetID.yml similarity index 100% rename from assets/EXIGetID.yml rename to assets/signatures/EXIGetID.yml diff --git a/assets/EXIInit.yml b/assets/signatures/EXIInit.yml similarity index 100% rename from assets/EXIInit.yml rename to assets/signatures/EXIInit.yml diff --git a/assets/IOS_Open.yml b/assets/signatures/IOS_Open.yml similarity index 100% rename from assets/IOS_Open.yml rename to assets/signatures/IOS_Open.yml diff --git a/assets/IPCCltInit.yml b/assets/signatures/IPCCltInit.yml similarity index 100% rename from assets/IPCCltInit.yml rename to assets/signatures/IPCCltInit.yml diff --git a/assets/IPCiProfQueueReq.yml b/assets/signatures/IPCiProfQueueReq.yml similarity index 100% rename from assets/IPCiProfQueueReq.yml rename to assets/signatures/IPCiProfQueueReq.yml diff --git a/assets/ISFS_OpenAsync.yml b/assets/signatures/ISFS_OpenAsync.yml similarity index 100% rename from assets/ISFS_OpenAsync.yml rename to assets/signatures/ISFS_OpenAsync.yml diff --git a/assets/ISFS_OpenLib.yml b/assets/signatures/ISFS_OpenLib.yml similarity index 100% rename from assets/ISFS_OpenLib.yml rename to assets/signatures/ISFS_OpenLib.yml diff --git a/assets/InitMetroTRK.yml b/assets/signatures/InitMetroTRK.yml similarity index 100% rename from assets/InitMetroTRK.yml rename to assets/signatures/InitMetroTRK.yml diff --git a/assets/InitMetroTRKCommTable.yml b/assets/signatures/InitMetroTRKCommTable.yml similarity index 100% rename from assets/InitMetroTRKCommTable.yml rename to assets/signatures/InitMetroTRKCommTable.yml diff --git a/assets/NANDInit.yml b/assets/signatures/NANDInit.yml similarity index 100% rename from assets/NANDInit.yml rename to assets/signatures/NANDInit.yml diff --git a/assets/NANDLoggingAddMessageAsync.yml b/assets/signatures/NANDLoggingAddMessageAsync.yml similarity index 100% rename from assets/NANDLoggingAddMessageAsync.yml rename to assets/signatures/NANDLoggingAddMessageAsync.yml diff --git a/assets/NANDPrivateOpenAsync.yml b/assets/signatures/NANDPrivateOpenAsync.yml similarity index 100% rename from assets/NANDPrivateOpenAsync.yml rename to assets/signatures/NANDPrivateOpenAsync.yml diff --git a/assets/NANDSetAutoErrorMessaging.yml b/assets/signatures/NANDSetAutoErrorMessaging.yml similarity index 100% rename from assets/NANDSetAutoErrorMessaging.yml rename to assets/signatures/NANDSetAutoErrorMessaging.yml diff --git a/assets/OSDefaultExceptionHandler.yml b/assets/signatures/OSDefaultExceptionHandler.yml similarity index 100% rename from assets/OSDefaultExceptionHandler.yml rename to assets/signatures/OSDefaultExceptionHandler.yml diff --git a/assets/OSDisableScheduler.yml b/assets/signatures/OSDisableScheduler.yml similarity index 100% rename from assets/OSDisableScheduler.yml rename to assets/signatures/OSDisableScheduler.yml diff --git a/assets/OSExceptionInit.yml b/assets/signatures/OSExceptionInit.yml similarity index 100% rename from assets/OSExceptionInit.yml rename to assets/signatures/OSExceptionInit.yml diff --git a/assets/OSExceptionVector.yml b/assets/signatures/OSExceptionVector.yml similarity index 100% rename from assets/OSExceptionVector.yml rename to assets/signatures/OSExceptionVector.yml diff --git a/assets/OSInit.yml b/assets/signatures/OSInit.yml similarity index 100% rename from assets/OSInit.yml rename to assets/signatures/OSInit.yml diff --git a/assets/OSInitAlarm.yml b/assets/signatures/OSInitAlarm.yml similarity index 100% rename from assets/OSInitAlarm.yml rename to assets/signatures/OSInitAlarm.yml diff --git a/assets/OSRegisterResetFunction.yml b/assets/signatures/OSRegisterResetFunction.yml similarity index 100% rename from assets/OSRegisterResetFunction.yml rename to assets/signatures/OSRegisterResetFunction.yml diff --git a/assets/OSRegisterShutdownFunction.yml b/assets/signatures/OSRegisterShutdownFunction.yml similarity index 100% rename from assets/OSRegisterShutdownFunction.yml rename to assets/signatures/OSRegisterShutdownFunction.yml diff --git a/assets/OSReport.yml b/assets/signatures/OSReport.yml similarity index 100% rename from assets/OSReport.yml rename to assets/signatures/OSReport.yml diff --git a/assets/OSResetSystem.yml b/assets/signatures/OSResetSystem.yml similarity index 100% rename from assets/OSResetSystem.yml rename to assets/signatures/OSResetSystem.yml diff --git a/assets/OSReturnToMenu.yml b/assets/signatures/OSReturnToMenu.yml similarity index 100% rename from assets/OSReturnToMenu.yml rename to assets/signatures/OSReturnToMenu.yml diff --git a/assets/OSSetArenaHi.yml b/assets/signatures/OSSetArenaHi.yml similarity index 100% rename from assets/OSSetArenaHi.yml rename to assets/signatures/OSSetArenaHi.yml diff --git a/assets/OSSetArenaLo.yml b/assets/signatures/OSSetArenaLo.yml similarity index 100% rename from assets/OSSetArenaLo.yml rename to assets/signatures/OSSetArenaLo.yml diff --git a/assets/OSSetMEM1ArenaHi.yml b/assets/signatures/OSSetMEM1ArenaHi.yml similarity index 100% rename from assets/OSSetMEM1ArenaHi.yml rename to assets/signatures/OSSetMEM1ArenaHi.yml diff --git a/assets/OSSetMEM1ArenaLo.yml b/assets/signatures/OSSetMEM1ArenaLo.yml similarity index 100% rename from assets/OSSetMEM1ArenaLo.yml rename to assets/signatures/OSSetMEM1ArenaLo.yml diff --git a/assets/OSSetMEM2ArenaHi.yml b/assets/signatures/OSSetMEM2ArenaHi.yml similarity index 100% rename from assets/OSSetMEM2ArenaHi.yml rename to assets/signatures/OSSetMEM2ArenaHi.yml diff --git a/assets/OSSetMEM2ArenaLo.yml b/assets/signatures/OSSetMEM2ArenaLo.yml similarity index 100% rename from assets/OSSetMEM2ArenaLo.yml rename to assets/signatures/OSSetMEM2ArenaLo.yml diff --git a/assets/OSSwitchFPUContext.yml b/assets/signatures/OSSwitchFPUContext.yml similarity index 100% rename from assets/OSSwitchFPUContext.yml rename to assets/signatures/OSSwitchFPUContext.yml diff --git a/assets/signatures/RSOStaticLocateObject.yml b/assets/signatures/RSOStaticLocateObject.yml new file mode 100644 index 0000000..3bd5aa1 --- /dev/null +++ b/assets/signatures/RSOStaticLocateObject.yml @@ -0,0 +1,572 @@ +- symbol: 0 + hash: e9350e81a596b1f391270020c46cdd66059f8d7c + signature: lCH/4P////98CAKm/////5ABACT/////k+EAHP////+TwQAY/////5OhABT/////k4EAEP////98fBt4/////3+e43j/////OAAAAP////+YHgAj/////4AeAAz/////fADyFP////+QHgAM/////4AeABD/////fADyFP////+QHgAQ/////4AeADD/////fADyFP////+QHgAw/////4AeADj/////fADyFP////+QHgA4/////4AeAED/////fADyFP////+QHgBA/////4AeAEj/////fADyFP////+QHgBI/////4AeAEz/////fADyFP////+QHgBM/////4AeAFT/////fADyFP////+QHgBU/////zugAAH/////SAABaP////+AfAAM/////1egGDj/////f+MCFP////8oHQAN/////0GBAST/////PGAAAP//AAA4YwAA//8AAFegEDr/////fGMALv////98aQOm/////06ABCD/////PGAAAP//AAA4AwAA//8AAJAfAAD/////SAABKP////88YAAA//8AADgDAAD//wAAkB8AAP////9IAAEY/////4AfAAT/////LAAAAP////9BggAc/////zxgAAD//wAAOGMAAP//AAB/pOt4/////4C/AAT/////TMYxgv////9IAAAB/AAAAzgAAAD/////kB8AAP////9IAADo/////4AfAAT/////LAAAAP////9BggAc/////zxgAAD//wAAOGMAAP//AAB/pOt4/////4C/AAT/////TMYxgv////9IAAAB/AAAAzgAAAD/////kB8AAP////9IAAC4/////zxgAAD//wAAOAMAAP//AACQHwAA/////0gAAKj/////PGAAAP//AAA4AwAA//8AAJAfAAD/////SAAAmP////88YAAA//8AADgDAAD//wAAkB8AAP////9IAACI/////zxgAAD//wAAOAMAAP//AACQHwAA/////0gAAHj/////PGAAAP//AAA4AwAA//8AAJAfAAD/////SAAAaP////88YAAA//8AADgDAAD//wAAkB8AAP////9IAABY/////zxgAAD//wAAOAMAAP//AACQHwAA/////0gAAEj/////OAAAAP////+QHwAA/////0gAADz/////OAAAAP////+QHwAA/////0gAADD/////gB8ABP////8sAAAA/////0GCABz/////PGAAAP//AAA4YwAA//8AAH+k63j/////gL8ABP////9MxjGC/////0gAAAH8AAADOAAAAP////+QHwAA/////zu9AAH/////gB4ACP////98HQBA/////0GA/pT/////gB4APP////88YKqr/////zhjqqv/////fAMAFv////9UAOj+/////5ABAAz/////O6AAAP////9IAAAs/////4B+ADj/////HB0ADP////98AwIU/////5ABAAj/////gGEACP////+AAwAA/////3wA8hT/////gGEACP////+QAwAA/////zu9AAH/////gAEADP////98HQBA/////0GA/9D/////OGAAAf////+D4QAc/////4PBABj/////g6EAFP////+DgQAQ/////4ABACT/////fAgDpv////84IQAg/////06AACD///// + symbols: + - kind: Function + name: RSOStaticLocateObject + size: 636 + flags: 1 + section: .text + - kind: Object + name: '@665' + size: 56 + flags: 2 + section: .data + - kind: Unknown + name: _f_init + size: 0 + flags: 1 + section: .init + - kind: Unknown + name: _f_text + size: 0 + flags: 1 + section: .text + - kind: Object + name: '@661' + size: 39 + flags: 2 + section: .data + - kind: Function + name: OSReport + size: 0 + flags: 5 + section: .text + - kind: Object + name: '@662' + size: 39 + flags: 2 + section: .data + - kind: Unknown + name: _f_rodata + size: 0 + flags: 1 + section: .rodata + - kind: Unknown + name: _f_data + size: 0 + flags: 1 + section: .data + - kind: Unknown + name: _f_bss + size: 0 + flags: 1 + section: .bss + - kind: Unknown + name: _f_sdata + size: 0 + flags: 1 + section: .sdata + - kind: Unknown + name: _f_sbss + size: 0 + flags: 1 + section: .sbss + - kind: Unknown + name: _f_sdata2 + size: 0 + flags: 1 + section: .sdata2 + - kind: Unknown + name: _f_sbss2 + size: 0 + flags: 1 + section: .sbss2 + - kind: Object + name: '@663' + size: 40 + flags: 2 + section: .data + relocations: + - offset: 168 + kind: PpcAddr16Ha + symbol: 1 + addend: 0 + - offset: 172 + kind: PpcAddr16Lo + symbol: 1 + addend: 0 + - offset: 192 + kind: PpcAddr16Ha + symbol: 2 + addend: 0 + - offset: 196 + kind: PpcAddr16Lo + symbol: 2 + addend: 0 + - offset: 208 + kind: PpcAddr16Ha + symbol: 3 + addend: 0 + - offset: 212 + kind: PpcAddr16Lo + symbol: 3 + addend: 0 + - offset: 236 + kind: PpcAddr16Ha + symbol: 4 + addend: 0 + - offset: 240 + kind: PpcAddr16Lo + symbol: 4 + addend: 0 + - offset: 256 + kind: PpcRel24 + symbol: 5 + addend: 0 + - offset: 284 + kind: PpcAddr16Ha + symbol: 6 + addend: 0 + - offset: 288 + kind: PpcAddr16Lo + symbol: 6 + addend: 0 + - offset: 304 + kind: PpcRel24 + symbol: 5 + addend: 0 + - offset: 320 + kind: PpcAddr16Ha + symbol: 7 + addend: 0 + - offset: 324 + kind: PpcAddr16Lo + symbol: 7 + addend: 0 + - offset: 336 + kind: PpcAddr16Ha + symbol: 8 + addend: 0 + - offset: 340 + kind: PpcAddr16Lo + symbol: 8 + addend: 0 + - offset: 352 + kind: PpcAddr16Ha + symbol: 9 + addend: 0 + - offset: 356 + kind: PpcAddr16Lo + symbol: 9 + addend: 0 + - offset: 368 + kind: PpcAddr16Ha + symbol: 10 + addend: 0 + - offset: 372 + kind: PpcAddr16Lo + symbol: 10 + addend: 0 + - offset: 384 + kind: PpcAddr16Ha + symbol: 11 + addend: 0 + - offset: 388 + kind: PpcAddr16Lo + symbol: 11 + addend: 0 + - offset: 400 + kind: PpcAddr16Ha + symbol: 12 + addend: 0 + - offset: 404 + kind: PpcAddr16Lo + symbol: 12 + addend: 0 + - offset: 416 + kind: PpcAddr16Ha + symbol: 13 + addend: 0 + - offset: 420 + kind: PpcAddr16Lo + symbol: 13 + addend: 0 + - offset: 468 + kind: PpcAddr16Ha + symbol: 14 + addend: 0 + - offset: 472 + kind: PpcAddr16Lo + symbol: 14 + addend: 0 + - offset: 488 + kind: PpcRel24 + symbol: 5 + addend: 0 +- symbol: 0 + hash: 462811ae6e7cd307c3448b066102e65ffabfd490 + signature: lCH/sP////98CAKm/////5ABAFT/////OWEAUP////9IAAAB/AAAA4CDAAz/////OoAAAP////+AAwAQ/////z5gAAD//wAAfUQaFP////+AowAw/////30gGhT/////gIMAOP////99BRoU/////4ADAED/////fOQaFP////+AowBI/////3zAGhT/////gIMATP////+AAwBU/////3ylGhT/////fIQaFP////8/wAAA//8AAHwAGhT/////P6AAAP//AAA/gAAA//8AAD9gAAD//wAAP0AAAP//AAA/IAAA//8AAD8AAAD//wAAPuAAAP//AAA+wAAA//8AAJqDACP/////fH8beP////86cwAA//8AAJFDAAz/////O94AAP//AAA7vQAA//8AADucAAD//wAAkSMAEP////87ewAA//8AADtaAAD//wAAOzkAAP//AACRAwAw/////zsYAAD//wAAOvcAAP//AAA61gAA//8AAJDjADj/////OiAAAf////854AAE/////zpAAAj/////kMMAQP////8+oAAA//8AAJCjAEj/////kIMATP////+QAwBU/////0gAAPD/////gB8ADP////8oEQAN/////34AkhT/////QYEAtP////84dQAA//8AAHxjeC7/////fGkDpv////9OgAQg/////5LQAAD/////SAAAvP////+S8AAA/////0gAALT/////gLAABP////8sBQAA/////0GCABT/////fiSLeP////84cwAA/////0zGMYL/////SAAAAfwAAAOSkAAA/////0gAAJD/////gLAABP////8sBQAA/////0GCABT/////fiSLeP////84cwAo/////0zGMYL/////SAAAAfwAAAOSkAAA/////0gAAGz/////kxAAAP////9IAABk/////5MwAAD/////SAAAXP////+TUAAA/////0gAAFT/////k3AAAP////9IAABM/////5OQAAD/////SAAARP////+TsAAA/////0gAADz/////k9AAAP////9IAAA0/////5KQAAD/////SAAALP////+SkAAA/////0gAACT/////gLAABP////8sBQAA/////0GCABT/////fiSLeP////84cwBQ/////0zGMYL/////SAAAAfwAAAOSkAAA/////zpSAAj/////OjEAAf////857wAE/////4AfAAj/////fBEAQP////9BgP8M/////zxgqqv/////gB8APP////84Y6qr/////ziAAAD/////fAMAFv////9UBej//////0GCAPz/////KAUACP////84xf/4/////0CBAMT/////OAYAB/////84YAAA/////1QA6P7/////fAkDpv////8oBgAA/////0CBAKz/////gN8AOP////84hAAI/////3wGGC7/////fAD6FP////98Bhku/////4AfADj/////fMAaFP////+ABgAM/////3wA+hT/////kAYADP////+AHwA4/////3zAGhT/////gAYAGP////98APoU/////5AGABj/////gB8AOP////98wBoU/////4AGACT/////fAD6FP////+QBgAk/////4AfADj/////fMAaFP////+ABgAw/////3wA+hT/////kAYAMP////+AHwA4/////3zAGhT/////gAYAPP////98APoU/////5AGADz/////gB8AOP////98wBoU/////4AGAEj/////fAD6FP////+QBgBI/////4AfADj/////fMAaFP////84YwBg/////4AGAFT/////fAD6FP////+QBgBU/////0IA/1z/////fAQoUP////8cxAAM/////3wJA6b/////fAQoQP////9AgAAc/////4B/ADj/////fAMwLv////98APoU/////3wDMS7/////OMYADP////9CAP/s/////zlhAFD/////OGAAAf////9IAAAB/AAAA4ABAFT/////fAgDpv////84IQBQ/////06AACD///// + symbols: + - kind: Function + name: RSOStaticLocateObject + size: 780 + flags: 1 + section: .text + - kind: Unknown + name: _savegpr_15 + size: 0 + flags: 1 + section: .text + - kind: Object + name: '@1841' + size: 39 + flags: 2 + section: .data + - kind: Unknown + name: _f_sbss2 + size: 0 + flags: 1 + section: .sbss2 + - kind: Unknown + name: _f_sdata2 + size: 0 + flags: 1 + section: .sdata2 + - kind: Unknown + name: _f_sbss + size: 0 + flags: 1 + section: .sbss + - kind: Unknown + name: _f_sdata + size: 0 + flags: 1 + section: .sdata + - kind: Unknown + name: _f_bss + size: 0 + flags: 1 + section: .bss + - kind: Unknown + name: _f_data + size: 0 + flags: 1 + section: .data + - kind: Unknown + name: _f_rodata + size: 0 + flags: 1 + section: .rodata + - kind: Unknown + name: _f_text + size: 0 + flags: 1 + section: .text + - kind: Unknown + name: _f_init + size: 0 + flags: 1 + section: .init + - kind: Object + name: '@1845' + size: 56 + flags: 2 + section: .data + - kind: Function + name: OSReport + size: 0 + flags: 5 + section: .text + - kind: Unknown + name: _restgpr_15 + size: 0 + flags: 1 + section: .text + relocations: + - offset: 16 + kind: PpcRel24 + symbol: 1 + addend: 0 + - offset: 32 + kind: PpcAddr16Ha + symbol: 2 + addend: 0 + - offset: 88 + kind: PpcAddr16Ha + symbol: 3 + addend: 0 + - offset: 96 + kind: PpcAddr16Ha + symbol: 4 + addend: 0 + - offset: 100 + kind: PpcAddr16Ha + symbol: 5 + addend: 0 + - offset: 104 + kind: PpcAddr16Ha + symbol: 6 + addend: 0 + - offset: 108 + kind: PpcAddr16Ha + symbol: 7 + addend: 0 + - offset: 112 + kind: PpcAddr16Ha + symbol: 8 + addend: 0 + - offset: 116 + kind: PpcAddr16Ha + symbol: 9 + addend: 0 + - offset: 120 + kind: PpcAddr16Ha + symbol: 10 + addend: 0 + - offset: 124 + kind: PpcAddr16Ha + symbol: 11 + addend: 0 + - offset: 136 + kind: PpcAddr16Lo + symbol: 2 + addend: 0 + - offset: 144 + kind: PpcAddr16Lo + symbol: 3 + addend: 0 + - offset: 148 + kind: PpcAddr16Lo + symbol: 4 + addend: 0 + - offset: 152 + kind: PpcAddr16Lo + symbol: 5 + addend: 0 + - offset: 160 + kind: PpcAddr16Lo + symbol: 6 + addend: 0 + - offset: 164 + kind: PpcAddr16Lo + symbol: 7 + addend: 0 + - offset: 168 + kind: PpcAddr16Lo + symbol: 8 + addend: 0 + - offset: 176 + kind: PpcAddr16Lo + symbol: 9 + addend: 0 + - offset: 180 + kind: PpcAddr16Lo + symbol: 10 + addend: 0 + - offset: 184 + kind: PpcAddr16Lo + symbol: 11 + addend: 0 + - offset: 208 + kind: PpcAddr16Ha + symbol: 12 + addend: 0 + - offset: 244 + kind: PpcAddr16Lo + symbol: 12 + addend: 0 + - offset: 300 + kind: PpcRel24 + symbol: 13 + addend: 0 + - offset: 336 + kind: PpcRel24 + symbol: 13 + addend: 0 + - offset: 444 + kind: PpcRel24 + symbol: 13 + addend: 0 + - offset: 760 + kind: PpcRel24 + symbol: 14 + addend: 0 +- symbol: 0 + hash: 2738adeb0411257ac88b6bf57b8b7a48094be29a + signature: lCH/sP////98CAKm/////5ABAFT/////OWEAUP////9IAAAB/AAAA4CDAAz/////PmAAAP//AACAAwAQ/////zqAAAD/////fUQaFP////+AowAw/////30gGhT/////gIMAOP////99BRoU/////4ADAED/////fOQaFP////+AowBI/////3zAGhT/////gIMATP////+AAwBU/////3ylGhT/////fIQaFP////8/wAAA//8AAHwAGhT/////P6AAAP//AAA/gAAA//8AAD9gAAD//wAAP0AAAP//AAA/IAAA//8AAD8AAAD//wAAPuAAAP//AAA+wAAA//8AAJqDACP/////fH8beP////86cwAA//8AAJFDAAz/////O94AAP//AAA7vQAA//8AADucAAD//wAAkSMAEP////87ewAA//8AADtaAAD//wAAOzkAAP//AACRAwAw/////zsYAAD//wAAOvcAAP//AAA61gAA//8AAJDjADj/////OiAAAf////854AAE/////zpAAAj/////kMMAQP////8+oAAA//8AAJCjAEj/////kIMATP////+QAwBU/////0gAAPD/////gB8ADP////8oEQAN/////34AkhT/////QYEAtP////84dQAA//8AAHxjeC7/////fGkDpv////9OgAQg/////5LQAAD/////SAAAvP////+S8AAA/////0gAALT/////gLAABP////8sBQAA/////0GCABT/////fiSLeP////84cwAA/////0zGMYL/////SAAAAfwAAAOSkAAA/////0gAAJD/////gLAABP////8sBQAA/////0GCABT/////fiSLeP////84cwAo/////0zGMYL/////SAAAAfwAAAOSkAAA/////0gAAGz/////kxAAAP////9IAABk/////5MwAAD/////SAAAXP////+TUAAA/////0gAAFT/////k3AAAP////9IAABM/////5OQAAD/////SAAARP////+TsAAA/////0gAADz/////k9AAAP////9IAAA0/////5KQAAD/////SAAALP////+SkAAA/////0gAACT/////gLAABP////8sBQAA/////0GCABT/////fiSLeP////84cwBQ/////0zGMYL/////SAAAAfwAAAOSkAAA/////zpSAAj/////OjEAAf////857wAE/////4AfAAj/////fBEAQP////9BgP8M/////zxgqqv/////gB8APP////84Y6qr/////ziAAAD/////fAMAFv////9UBej//////0GCAPz/////KAUACP////84xf/4/////0CBAMT/////OAYAB/////84YAAA/////1QA6P7/////fAkDpv////8oBgAA/////0CBAKz/////gN8AOP////84hAAI/////3wGGC7/////fAD6FP////98Bhku/////4AfADj/////fMAaFP////+ABgAM/////3wA+hT/////kAYADP////+AHwA4/////3zAGhT/////gAYAGP////98APoU/////5AGABj/////gB8AOP////98wBoU/////4AGACT/////fAD6FP////+QBgAk/////4AfADj/////fMAaFP////+ABgAw/////3wA+hT/////kAYAMP////+AHwA4/////3zAGhT/////gAYAPP////98APoU/////5AGADz/////gB8AOP////98wBoU/////4AGAEj/////fAD6FP////+QBgBI/////4AfADj/////fMAaFP////84YwBg/////4AGAFT/////fAD6FP////+QBgBU/////0IA/1z/////fAQoUP////8cxAAM/////3wJA6b/////fAQoQP////9AgAAc/////4B/ADj/////fAMwLv////98APoU/////3wDMS7/////OMYADP////9CAP/s/////zlhAFD/////OGAAAf////9IAAAB/AAAA4ABAFT/////fAgDpv////84IQBQ/////06AACD///// + symbols: + - kind: Function + name: RSOStaticLocateObject + size: 780 + flags: 1 + section: .text + - kind: Unknown + name: _savegpr_15 + size: 0 + flags: 1 + section: .text + - kind: Object + name: '@1982' + size: 39 + flags: 2 + section: .data + - kind: Unknown + name: _f_sbss2 + size: 0 + flags: 1 + section: .sbss2 + - kind: Unknown + name: _f_sdata2 + size: 0 + flags: 1 + section: .sdata2 + - kind: Unknown + name: _f_sbss + size: 0 + flags: 1 + section: .sbss + - kind: Unknown + name: _f_sdata + size: 0 + flags: 1 + section: .sdata + - kind: Unknown + name: _f_bss + size: 0 + flags: 1 + section: .bss + - kind: Unknown + name: _f_data + size: 0 + flags: 1 + section: .data + - kind: Unknown + name: _f_rodata + size: 0 + flags: 1 + section: .rodata + - kind: Unknown + name: _f_text + size: 0 + flags: 1 + section: .text + - kind: Unknown + name: _f_init + size: 0 + flags: 1 + section: .init + - kind: Object + name: '@1986' + size: 56 + flags: 2 + section: .data + - kind: Function + name: OSReport + size: 0 + flags: 1 + section: .text + - kind: Unknown + name: _restgpr_15 + size: 0 + flags: 1 + section: .text + relocations: + - offset: 16 + kind: PpcRel24 + symbol: 1 + addend: 0 + - offset: 24 + kind: PpcAddr16Ha + symbol: 2 + addend: 0 + - offset: 88 + kind: PpcAddr16Ha + symbol: 3 + addend: 0 + - offset: 96 + kind: PpcAddr16Ha + symbol: 4 + addend: 0 + - offset: 100 + kind: PpcAddr16Ha + symbol: 5 + addend: 0 + - offset: 104 + kind: PpcAddr16Ha + symbol: 6 + addend: 0 + - offset: 108 + kind: PpcAddr16Ha + symbol: 7 + addend: 0 + - offset: 112 + kind: PpcAddr16Ha + symbol: 8 + addend: 0 + - offset: 116 + kind: PpcAddr16Ha + symbol: 9 + addend: 0 + - offset: 120 + kind: PpcAddr16Ha + symbol: 10 + addend: 0 + - offset: 124 + kind: PpcAddr16Ha + symbol: 11 + addend: 0 + - offset: 136 + kind: PpcAddr16Lo + symbol: 2 + addend: 0 + - offset: 144 + kind: PpcAddr16Lo + symbol: 3 + addend: 0 + - offset: 148 + kind: PpcAddr16Lo + symbol: 4 + addend: 0 + - offset: 152 + kind: PpcAddr16Lo + symbol: 5 + addend: 0 + - offset: 160 + kind: PpcAddr16Lo + symbol: 6 + addend: 0 + - offset: 164 + kind: PpcAddr16Lo + symbol: 7 + addend: 0 + - offset: 168 + kind: PpcAddr16Lo + symbol: 8 + addend: 0 + - offset: 176 + kind: PpcAddr16Lo + symbol: 9 + addend: 0 + - offset: 180 + kind: PpcAddr16Lo + symbol: 10 + addend: 0 + - offset: 184 + kind: PpcAddr16Lo + symbol: 11 + addend: 0 + - offset: 208 + kind: PpcAddr16Ha + symbol: 12 + addend: 0 + - offset: 244 + kind: PpcAddr16Lo + symbol: 12 + addend: 0 + - offset: 300 + kind: PpcRel24 + symbol: 13 + addend: 0 + - offset: 336 + kind: PpcRel24 + symbol: 13 + addend: 0 + - offset: 444 + kind: PpcRel24 + symbol: 13 + addend: 0 + - offset: 760 + kind: PpcRel24 + symbol: 14 + addend: 0 diff --git a/assets/ReportOSInfo.yml b/assets/signatures/ReportOSInfo.yml similarity index 100% rename from assets/ReportOSInfo.yml rename to assets/signatures/ReportOSInfo.yml diff --git a/assets/SCInit.yml b/assets/signatures/SCInit.yml similarity index 100% rename from assets/SCInit.yml rename to assets/signatures/SCInit.yml diff --git a/assets/SCReloadConfFileAsync.yml b/assets/signatures/SCReloadConfFileAsync.yml similarity index 100% rename from assets/SCReloadConfFileAsync.yml rename to assets/signatures/SCReloadConfFileAsync.yml diff --git a/assets/SIGetType.yml b/assets/signatures/SIGetType.yml similarity index 100% rename from assets/SIGetType.yml rename to assets/signatures/SIGetType.yml diff --git a/assets/SIInit.yml b/assets/signatures/SIInit.yml similarity index 100% rename from assets/SIInit.yml rename to assets/signatures/SIInit.yml diff --git a/assets/SISetSamplingRate.yml b/assets/signatures/SISetSamplingRate.yml similarity index 100% rename from assets/SISetSamplingRate.yml rename to assets/signatures/SISetSamplingRate.yml diff --git a/assets/SISetXY.yml b/assets/signatures/SISetXY.yml similarity index 100% rename from assets/SISetXY.yml rename to assets/signatures/SISetXY.yml diff --git a/assets/TRKEXICallBack.yml b/assets/signatures/TRKEXICallBack.yml similarity index 100% rename from assets/TRKEXICallBack.yml rename to assets/signatures/TRKEXICallBack.yml diff --git a/assets/TRKExceptionHandler.yml b/assets/signatures/TRKExceptionHandler.yml similarity index 100% rename from assets/TRKExceptionHandler.yml rename to assets/signatures/TRKExceptionHandler.yml diff --git a/assets/TRKInitializeIntDrivenUART.yml b/assets/signatures/TRKInitializeIntDrivenUART.yml similarity index 100% rename from assets/TRKInitializeIntDrivenUART.yml rename to assets/signatures/TRKInitializeIntDrivenUART.yml diff --git a/assets/TRKInitializeNub.yml b/assets/signatures/TRKInitializeNub.yml similarity index 100% rename from assets/TRKInitializeNub.yml rename to assets/signatures/TRKInitializeNub.yml diff --git a/assets/TRKInterruptHandler.yml b/assets/signatures/TRKInterruptHandler.yml similarity index 100% rename from assets/TRKInterruptHandler.yml rename to assets/signatures/TRKInterruptHandler.yml diff --git a/assets/TRKInterruptHandlerEnableInterrupts.yml b/assets/signatures/TRKInterruptHandlerEnableInterrupts.yml similarity index 100% rename from assets/TRKInterruptHandlerEnableInterrupts.yml rename to assets/signatures/TRKInterruptHandlerEnableInterrupts.yml diff --git a/assets/TRKLoadContext.yml b/assets/signatures/TRKLoadContext.yml similarity index 100% rename from assets/TRKLoadContext.yml rename to assets/signatures/TRKLoadContext.yml diff --git a/assets/TRKNubMainLoop.yml b/assets/signatures/TRKNubMainLoop.yml similarity index 100% rename from assets/TRKNubMainLoop.yml rename to assets/signatures/TRKNubMainLoop.yml diff --git a/assets/TRKNubWelcome.yml b/assets/signatures/TRKNubWelcome.yml similarity index 100% rename from assets/TRKNubWelcome.yml rename to assets/signatures/TRKNubWelcome.yml diff --git a/assets/TRKRestoreExtended1Block.yml b/assets/signatures/TRKRestoreExtended1Block.yml similarity index 100% rename from assets/TRKRestoreExtended1Block.yml rename to assets/signatures/TRKRestoreExtended1Block.yml diff --git a/assets/TRKSaveExtended1Block.yml b/assets/signatures/TRKSaveExtended1Block.yml similarity index 100% rename from assets/TRKSaveExtended1Block.yml rename to assets/signatures/TRKSaveExtended1Block.yml diff --git a/assets/TRKSwapAndGo.yml b/assets/signatures/TRKSwapAndGo.yml similarity index 100% rename from assets/TRKSwapAndGo.yml rename to assets/signatures/TRKSwapAndGo.yml diff --git a/assets/TRKTargetContinue.yml b/assets/signatures/TRKTargetContinue.yml similarity index 100% rename from assets/TRKTargetContinue.yml rename to assets/signatures/TRKTargetContinue.yml diff --git a/assets/TRK_main.yml b/assets/signatures/TRK_main.yml similarity index 100% rename from assets/TRK_main.yml rename to assets/signatures/TRK_main.yml diff --git a/assets/VIGetTvFormat.yml b/assets/signatures/VIGetTvFormat.yml similarity index 100% rename from assets/VIGetTvFormat.yml rename to assets/signatures/VIGetTvFormat.yml diff --git a/assets/_ExitProcess.yml b/assets/signatures/_ExitProcess.yml similarity index 100% rename from assets/_ExitProcess.yml rename to assets/signatures/_ExitProcess.yml diff --git a/assets/__DVDCheckDevice.yml b/assets/signatures/__DVDCheckDevice.yml similarity index 100% rename from assets/__DVDCheckDevice.yml rename to assets/signatures/__DVDCheckDevice.yml diff --git a/assets/__DVDClearWaitingQueue.yml b/assets/signatures/__DVDClearWaitingQueue.yml similarity index 100% rename from assets/__DVDClearWaitingQueue.yml rename to assets/signatures/__DVDClearWaitingQueue.yml diff --git a/assets/__DVDFSInit.yml b/assets/signatures/__DVDFSInit.yml similarity index 100% rename from assets/__DVDFSInit.yml rename to assets/signatures/__DVDFSInit.yml diff --git a/assets/__DVDInitWA.yml b/assets/signatures/__DVDInitWA.yml similarity index 100% rename from assets/__DVDInitWA.yml rename to assets/signatures/__DVDInitWA.yml diff --git a/assets/__DVDLowSetWAType.yml b/assets/signatures/__DVDLowSetWAType.yml similarity index 100% rename from assets/__DVDLowSetWAType.yml rename to assets/signatures/__DVDLowSetWAType.yml diff --git a/assets/__DVDStoreErrorCode.yml b/assets/signatures/__DVDStoreErrorCode.yml similarity index 100% rename from assets/__DVDStoreErrorCode.yml rename to assets/signatures/__DVDStoreErrorCode.yml diff --git a/assets/__FileWrite.yml b/assets/signatures/__FileWrite.yml similarity index 100% rename from assets/__FileWrite.yml rename to assets/signatures/__FileWrite.yml diff --git a/assets/__NANDPrintErrorMessage.yml b/assets/signatures/__NANDPrintErrorMessage.yml similarity index 100% rename from assets/__NANDPrintErrorMessage.yml rename to assets/signatures/__NANDPrintErrorMessage.yml diff --git a/assets/__OSCacheInit.yml b/assets/signatures/__OSCacheInit.yml similarity index 100% rename from assets/__OSCacheInit.yml rename to assets/signatures/__OSCacheInit.yml diff --git a/assets/__OSContextInit.yml b/assets/signatures/__OSContextInit.yml similarity index 100% rename from assets/__OSContextInit.yml rename to assets/signatures/__OSContextInit.yml diff --git a/assets/__OSDBIntegrator.yml b/assets/signatures/__OSDBIntegrator.yml similarity index 100% rename from assets/__OSDBIntegrator.yml rename to assets/signatures/__OSDBIntegrator.yml diff --git a/assets/__OSDBJump.yml b/assets/signatures/__OSDBJump.yml similarity index 100% rename from assets/__OSDBJump.yml rename to assets/signatures/__OSDBJump.yml diff --git a/assets/__OSFPRInit.yml b/assets/signatures/__OSFPRInit.yml similarity index 100% rename from assets/__OSFPRInit.yml rename to assets/signatures/__OSFPRInit.yml diff --git a/assets/__OSGetExceptionHandler.yml b/assets/signatures/__OSGetExceptionHandler.yml similarity index 100% rename from assets/__OSGetExceptionHandler.yml rename to assets/signatures/__OSGetExceptionHandler.yml diff --git a/assets/__OSInitAlarm.yml b/assets/signatures/__OSInitAlarm.yml similarity index 100% rename from assets/__OSInitAlarm.yml rename to assets/signatures/__OSInitAlarm.yml diff --git a/assets/__OSInitAudioSystem.yml b/assets/signatures/__OSInitAudioSystem.yml similarity index 100% rename from assets/__OSInitAudioSystem.yml rename to assets/signatures/__OSInitAudioSystem.yml diff --git a/assets/__OSInitIPCBuffer.yml b/assets/signatures/__OSInitIPCBuffer.yml similarity index 100% rename from assets/__OSInitIPCBuffer.yml rename to assets/signatures/__OSInitIPCBuffer.yml diff --git a/assets/__OSInitMemoryProtection.yml b/assets/signatures/__OSInitMemoryProtection.yml similarity index 100% rename from assets/__OSInitMemoryProtection.yml rename to assets/signatures/__OSInitMemoryProtection.yml diff --git a/assets/__OSInitNet.yml b/assets/signatures/__OSInitNet.yml similarity index 100% rename from assets/__OSInitNet.yml rename to assets/signatures/__OSInitNet.yml diff --git a/assets/__OSInitPlayTime.yml b/assets/signatures/__OSInitPlayTime.yml similarity index 100% rename from assets/__OSInitPlayTime.yml rename to assets/signatures/__OSInitPlayTime.yml diff --git a/assets/__OSInitSTM.yml b/assets/signatures/__OSInitSTM.yml similarity index 100% rename from assets/__OSInitSTM.yml rename to assets/signatures/__OSInitSTM.yml diff --git a/assets/__OSInitSram.yml b/assets/signatures/__OSInitSram.yml similarity index 100% rename from assets/__OSInitSram.yml rename to assets/signatures/__OSInitSram.yml diff --git a/assets/__OSInitSystemCall.yml b/assets/signatures/__OSInitSystemCall.yml similarity index 100% rename from assets/__OSInitSystemCall.yml rename to assets/signatures/__OSInitSystemCall.yml diff --git a/assets/__OSInterruptInit.yml b/assets/signatures/__OSInterruptInit.yml similarity index 100% rename from assets/__OSInterruptInit.yml rename to assets/signatures/__OSInterruptInit.yml diff --git a/assets/__OSPSInit.yml b/assets/signatures/__OSPSInit.yml similarity index 100% rename from assets/__OSPSInit.yml rename to assets/signatures/__OSPSInit.yml diff --git a/assets/__OSReschedule.yml b/assets/signatures/__OSReschedule.yml similarity index 100% rename from assets/__OSReschedule.yml rename to assets/signatures/__OSReschedule.yml diff --git a/assets/__OSReturnToMenu.yml b/assets/signatures/__OSReturnToMenu.yml similarity index 100% rename from assets/__OSReturnToMenu.yml rename to assets/signatures/__OSReturnToMenu.yml diff --git a/assets/__OSShutdownDevices.yml b/assets/signatures/__OSShutdownDevices.yml similarity index 100% rename from assets/__OSShutdownDevices.yml rename to assets/signatures/__OSShutdownDevices.yml diff --git a/assets/__OSStartPlayRecord.yml b/assets/signatures/__OSStartPlayRecord.yml similarity index 100% rename from assets/__OSStartPlayRecord.yml rename to assets/signatures/__OSStartPlayRecord.yml diff --git a/assets/__OSSyncSram.yml b/assets/signatures/__OSSyncSram.yml similarity index 100% rename from assets/__OSSyncSram.yml rename to assets/signatures/__OSSyncSram.yml diff --git a/assets/__OSThreadInit.yml b/assets/signatures/__OSThreadInit.yml similarity index 100% rename from assets/__OSThreadInit.yml rename to assets/signatures/__OSThreadInit.yml diff --git a/assets/__OSUnhandledException.yml b/assets/signatures/__OSUnhandledException.yml similarity index 100% rename from assets/__OSUnhandledException.yml rename to assets/signatures/__OSUnhandledException.yml diff --git a/assets/__StringWrite.yml b/assets/signatures/__StringWrite.yml similarity index 100% rename from assets/__StringWrite.yml rename to assets/signatures/__StringWrite.yml diff --git a/assets/__check_pad3.yml b/assets/signatures/__check_pad3.yml similarity index 100% rename from assets/__check_pad3.yml rename to assets/signatures/__check_pad3.yml diff --git a/assets/__destroy_global_chain.yml b/assets/signatures/__destroy_global_chain.yml similarity index 100% rename from assets/__destroy_global_chain.yml rename to assets/signatures/__destroy_global_chain.yml diff --git a/assets/__fini_cpp.yml b/assets/signatures/__fini_cpp.yml similarity index 97% rename from assets/__fini_cpp.yml rename to assets/signatures/__fini_cpp.yml index 145090c..c61ce22 100644 --- a/assets/__fini_cpp.yml +++ b/assets/signatures/__fini_cpp.yml @@ -7,7 +7,7 @@ size: 76 flags: 2 section: .text - - kind: Object + - kind: Unknown name: _dtors size: 0 flags: 1 diff --git a/assets/__fini_cpp_exceptions.yml b/assets/signatures/__fini_cpp_exceptions.yml similarity index 100% rename from assets/__fini_cpp_exceptions.yml rename to assets/signatures/__fini_cpp_exceptions.yml diff --git a/assets/__fstLoad.yml b/assets/signatures/__fstLoad.yml similarity index 100% rename from assets/__fstLoad.yml rename to assets/signatures/__fstLoad.yml diff --git a/assets/__fwrite.yml b/assets/signatures/__fwrite.yml similarity index 100% rename from assets/__fwrite.yml rename to assets/signatures/__fwrite.yml diff --git a/assets/__init_cpp.yml b/assets/signatures/__init_cpp.yml similarity index 97% rename from assets/__init_cpp.yml rename to assets/signatures/__init_cpp.yml index 4d79165..4dcc85e 100644 --- a/assets/__init_cpp.yml +++ b/assets/signatures/__init_cpp.yml @@ -7,7 +7,7 @@ size: 72 flags: 2 section: .text - - kind: Object + - kind: Unknown name: _ctors size: 0 flags: 1 @@ -30,7 +30,7 @@ size: 76 flags: 2 section: .text - - kind: Object + - kind: Unknown name: _ctors size: 0 flags: 1 @@ -53,7 +53,7 @@ size: 76 flags: 2 section: .text - - kind: Object + - kind: Unknown name: _ctors size: 0 flags: 1 @@ -76,7 +76,7 @@ size: 76 flags: 2 section: .text - - kind: Object + - kind: Unknown name: _ctors size: 0 flags: 1 @@ -99,7 +99,7 @@ size: 76 flags: 1 section: .text - - kind: Object + - kind: Unknown name: _ctors size: 0 flags: 1 @@ -122,7 +122,7 @@ size: 84 flags: 2 section: .text - - kind: Object + - kind: Unknown name: _ctors size: 0 flags: 1 @@ -145,7 +145,7 @@ size: 84 flags: 2 section: .text - - kind: Object + - kind: Unknown name: _ctors size: 0 flags: 1 diff --git a/assets/__init_cpp_exceptions.yml b/assets/signatures/__init_cpp_exceptions.yml similarity index 100% rename from assets/__init_cpp_exceptions.yml rename to assets/signatures/__init_cpp_exceptions.yml diff --git a/assets/__init_data.yml b/assets/signatures/__init_data.yml similarity index 100% rename from assets/__init_data.yml rename to assets/signatures/__init_data.yml diff --git a/assets/__init_hardware.yml b/assets/signatures/__init_hardware.yml similarity index 100% rename from assets/__init_hardware.yml rename to assets/signatures/__init_hardware.yml diff --git a/assets/__init_registers.yml b/assets/signatures/__init_registers.yml similarity index 100% rename from assets/__init_registers.yml rename to assets/signatures/__init_registers.yml diff --git a/assets/__ios_Ipc2.yml b/assets/signatures/__ios_Ipc2.yml similarity index 100% rename from assets/__ios_Ipc2.yml rename to assets/signatures/__ios_Ipc2.yml diff --git a/assets/__mod2u.yml b/assets/signatures/__mod2u.yml similarity index 100% rename from assets/__mod2u.yml rename to assets/signatures/__mod2u.yml diff --git a/assets/__msl_runtime_constraint_violation_s.yml b/assets/signatures/__msl_runtime_constraint_violation_s.yml similarity index 100% rename from assets/__msl_runtime_constraint_violation_s.yml rename to assets/signatures/__msl_runtime_constraint_violation_s.yml diff --git a/assets/__pformatter.yml b/assets/signatures/__pformatter.yml similarity index 100% rename from assets/__pformatter.yml rename to assets/signatures/__pformatter.yml diff --git a/assets/__register_fragment.yml b/assets/signatures/__register_fragment.yml similarity index 100% rename from assets/__register_fragment.yml rename to assets/signatures/__register_fragment.yml diff --git a/assets/__set_debug_bba.yml b/assets/signatures/__set_debug_bba.yml similarity index 100% rename from assets/__set_debug_bba.yml rename to assets/signatures/__set_debug_bba.yml diff --git a/assets/__start.yml b/assets/signatures/__start.yml similarity index 100% rename from assets/__start.yml rename to assets/signatures/__start.yml diff --git a/assets/__stdio_atexit.yml b/assets/signatures/__stdio_atexit.yml similarity index 100% rename from assets/__stdio_atexit.yml rename to assets/signatures/__stdio_atexit.yml diff --git a/assets/exit.yml b/assets/signatures/exit.yml similarity index 99% rename from assets/exit.yml rename to assets/signatures/exit.yml index a28f2fd..a52614d 100644 --- a/assets/exit.yml +++ b/assets/signatures/exit.yml @@ -35,7 +35,7 @@ size: 76 flags: 5 section: .text - - kind: Object + - kind: Unknown name: _dtors size: 0 flags: 1 @@ -87,7 +87,7 @@ size: 0 flags: 1 section: .text - - kind: Object + - kind: Unknown name: _dtors size: 0 flags: 1 @@ -162,7 +162,7 @@ size: 0 flags: 1 section: .text - - kind: Object + - kind: Unknown name: _dtors size: 0 flags: 1 @@ -278,7 +278,7 @@ size: 0 flags: 1 section: .text - - kind: Object + - kind: Unknown name: _dtors size: 0 flags: 1 @@ -419,7 +419,7 @@ size: 0 flags: 1 section: .text - - kind: Object + - kind: Unknown name: _dtors size: 0 flags: 1 diff --git a/assets/fwrite.yml b/assets/signatures/fwrite.yml similarity index 100% rename from assets/fwrite.yml rename to assets/signatures/fwrite.yml diff --git a/assets/longlong2str.yml b/assets/signatures/longlong2str.yml similarity index 100% rename from assets/longlong2str.yml rename to assets/signatures/longlong2str.yml diff --git a/assets/memset.yml b/assets/signatures/memset.yml similarity index 100% rename from assets/memset.yml rename to assets/signatures/memset.yml diff --git a/assets/nandConvertErrorCode.yml b/assets/signatures/nandConvertErrorCode.yml similarity index 100% rename from assets/nandConvertErrorCode.yml rename to assets/signatures/nandConvertErrorCode.yml diff --git a/assets/nandGenerateAbsPath.yml b/assets/signatures/nandGenerateAbsPath.yml similarity index 100% rename from assets/nandGenerateAbsPath.yml rename to assets/signatures/nandGenerateAbsPath.yml diff --git a/assets/nandGetHeadToken.yml b/assets/signatures/nandGetHeadToken.yml similarity index 100% rename from assets/nandGetHeadToken.yml rename to assets/signatures/nandGetHeadToken.yml diff --git a/assets/nandIsInitialized.yml b/assets/signatures/nandIsInitialized.yml similarity index 100% rename from assets/nandIsInitialized.yml rename to assets/signatures/nandIsInitialized.yml diff --git a/assets/nandOpen.yml b/assets/signatures/nandOpen.yml similarity index 100% rename from assets/nandOpen.yml rename to assets/signatures/nandOpen.yml diff --git a/assets/printf.yml b/assets/signatures/printf.yml similarity index 100% rename from assets/printf.yml rename to assets/signatures/printf.yml diff --git a/assets/sprintf.yml b/assets/signatures/sprintf.yml similarity index 100% rename from assets/sprintf.yml rename to assets/signatures/sprintf.yml diff --git a/assets/stateReady.yml b/assets/signatures/stateReady.yml similarity index 100% rename from assets/stateReady.yml rename to assets/signatures/stateReady.yml diff --git a/assets/vprintf.yml b/assets/signatures/vprintf.yml similarity index 100% rename from assets/vprintf.yml rename to assets/signatures/vprintf.yml diff --git a/assets/vsnprintf.yml b/assets/signatures/vsnprintf.yml similarity index 100% rename from assets/vsnprintf.yml rename to assets/signatures/vsnprintf.yml diff --git a/assets/vsprintf.yml b/assets/signatures/vsprintf.yml similarity index 100% rename from assets/vsprintf.yml rename to assets/signatures/vsprintf.yml diff --git a/build.rs b/build.rs index 2903705..a11d36f 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,467 @@ -fn main() -> Result<(), Box> { +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); + +#[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, +} + +#[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, + pub relocations: Vec, +} + +#[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, + pub relocations: Vec, + pub search: bool, +} + +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct Output { + pub symbols: Vec, + pub signatures: HashMap, +} + +pub fn parse_yml(sig_str: &str) -> Result> { + 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::::new(); + let mut out = HashMap::::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, + out: &mut HashMap, + 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 { + 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 { + 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 { + 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 { + 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, in_sym: &OutSymbol) -> Result { + 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) +} diff --git a/src/util/cfa.rs b/src/analysis/cfa.rs similarity index 71% rename from src/util/cfa.rs rename to src/analysis/cfa.rs index b39f145..883f680 100644 --- a/src/util/cfa.rs +++ b/src/analysis/cfa.rs @@ -1,22 +1,15 @@ -use std::{ - collections::{btree_map::Entry, BTreeMap, BTreeSet}, - num::NonZeroU32, - ops::Range, -}; +use std::collections::{BTreeMap, BTreeSet}; -use anyhow::{anyhow, bail, ensure, Context, Result}; -use fixedbitset::FixedBitSet; -use flagset::FlagSet; -use ppc750cl::{Argument, Ins, Opcode, GPR}; +use anyhow::{anyhow, bail, Result}; -use crate::util::{ - executor::{disassemble, ExecCbData, ExecCbResult, Executor}, - obj::{ - ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, - ObjSymbolKind, +use crate::{ + analysis::{ + executor::{ExecCbData, ExecCbResult, Executor}, + skip_alignment, + slices::{FunctionSlices, TailCallResult}, + vm::{BranchTarget, GprValue, StepResult, VM}, }, - slices::{FunctionSlices, TailCallResult}, - vm::{BranchTarget, GprValue, StepResult, VM}, + obj::{ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind}, }; #[derive(Debug, Default)] @@ -336,139 +329,14 @@ impl AnalyzerState { } } -pub trait AnalysisPass { - fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()>; -} - -pub struct FindTRKInterruptVectorTable {} - -pub const TRK_TABLE_HEADER: &str = "Metrowerks Target Resident Kernel for PowerPC"; -pub const TRK_TABLE_SIZE: u32 = 0x1F34; // always? - -// TRK_MINNOW_DOLPHIN.a __exception.s -impl AnalysisPass for FindTRKInterruptVectorTable { - fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> { - for (&start, _) in state.function_bounds.iter().filter(|&(_, &end)| end == 0) { - let (section, data) = match obj.section_data(start, 0) { - Ok((section, data)) => (section, data), - Err(_) => continue, - }; - if data.starts_with(TRK_TABLE_HEADER.as_bytes()) - && data[TRK_TABLE_HEADER.as_bytes().len()] == 0 - { - log::info!("Found gTRKInterruptVectorTable @ {:#010X}", start); - state.known_symbols.insert(start, ObjSymbol { - name: "gTRKInterruptVectorTable".to_string(), - demangled_name: None, - address: start as u64, - section: Some(section.index), - size: 0, - size_known: true, - flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)), - kind: ObjSymbolKind::Unknown, - }); - let end = start + TRK_TABLE_SIZE; - state.known_symbols.insert(end, ObjSymbol { - name: "gTRKInterruptVectorTableEnd".to_string(), - demangled_name: None, - address: end as u64, - section: Some(section.index), - size: 0, - size_known: true, - flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)), - kind: ObjSymbolKind::Unknown, - }); - - return Ok(()); - } - } - log::info!("gTRKInterruptVectorTable not found"); - Ok(()) - } -} - -pub struct FindSaveRestSleds {} - -const SLEDS: [([u8; 4], &'static str, &'static str); 4] = [ - ([0xd9, 0xcb, 0xff, 0x70], "__save_fpr", "_savefpr_"), - ([0xc9, 0xcb, 0xff, 0x70], "__restore_fpr", "_restfpr_"), - ([0x91, 0xcb, 0xff, 0xb8], "__save_gpr", "_savegpr_"), - ([0x81, 0xcb, 0xff, 0xb8], "__restore_gpr", "_restgpr_"), -]; - -// Runtime.PPCEABI.H.a runtime.c -impl AnalysisPass for FindSaveRestSleds { - fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> { - const SLED_SIZE: usize = 19 * 4; // registers 14-31 + blr - let mut clear_ranges: Vec> = vec![]; - for (&start, _) in state.function_bounds.iter().filter(|&(_, &end)| end != 0) { - let (section, data) = obj.section_data(start, 0)?; - for (needle, func, label) in &SLEDS { - if data.starts_with(needle) { - log::info!("Found {} @ {:#010X}", func, start); - clear_ranges.push(start + 4..start + SLED_SIZE as u32); - state.known_symbols.insert(start, ObjSymbol { - name: func.to_string(), - demangled_name: None, - address: start as u64, - section: Some(section.index), - size: SLED_SIZE as u64, - size_known: true, - flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: ObjSymbolKind::Function, - }); - for i in 14..=31 { - let addr = start + (i - 14) * 4; - state.known_symbols.insert(addr, ObjSymbol { - name: format!("{}{}", label, i), - demangled_name: None, - address: addr as u64, - section: Some(section.index), - size: 0, - size_known: true, - flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: ObjSymbolKind::Unknown, - }); - } - } - } - } - for range in clear_ranges { - for addr in range.step_by(4) { - state.function_entries.remove(&addr); - state.function_bounds.remove(&addr); - state.function_slices.remove(&addr); - } - } - Ok(()) - } -} - -fn skip_alignment(obj: &ObjInfo, mut addr: u32, end: u32) -> Option { - let mut data = match obj.section_data(addr, end) { - Ok((_, data)) => data, - Err(_) => return None, - }; - loop { - if data.is_empty() { - break None; - } - if data[0..4] == [0u8; 4] { - addr += 4; - data = &data[4..]; - } else { - break Some(addr); - } - } -} - /// Execute VM from entry point following branches and function calls /// until SDA bases are initialized (__init_registers) pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result { let mut executor = Executor::new(obj); executor.push(obj.entry as u32, VM::new(), false); - let result = - executor.run(obj, |ExecCbData { executor, vm, result, section, ins, block_start }| { + let result = executor.run( + obj, + |ExecCbData { executor, vm, result, section: _, ins, block_start: _ }| { match result { StepResult::Continue | StepResult::LoadStore { .. } => { return Ok(ExecCbResult::Continue); @@ -499,7 +367,8 @@ pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result { } Ok(ExecCbResult::EndBlock) - })?; + }, + )?; match result { Some((sda2_base, sda_base)) => { obj.sda2_base = Some(sda2_base); diff --git a/src/util/executor.rs b/src/analysis/executor.rs similarity index 61% rename from src/util/executor.rs rename to src/analysis/executor.rs index 7bd9117..ba06ef2 100644 --- a/src/util/executor.rs +++ b/src/analysis/executor.rs @@ -1,26 +1,15 @@ -use std::{collections::BTreeSet, num::NonZeroU32}; - -use anyhow::{Context, Result}; +use anyhow::Result; use fixedbitset::FixedBitSet; use ppc750cl::Ins; -use crate::util::{ +use crate::{ + analysis::{ + disassemble, + vm::{StepResult, VM}, + }, obj::{ObjInfo, ObjSection, ObjSectionKind}, - vm::{StepResult, VM}, }; -pub fn disassemble(section: &ObjSection, address: u32) -> Option { - read_u32(§ion.data, address, section.address as u32).map(|code| Ins::new(code, address)) -} - -pub fn read_u32(data: &[u8], address: u32, section_address: u32) -> Option { - let offset = (address - section_address) as usize; - if data.len() < offset + 4 { - return None; - } - Some(u32::from_be_bytes(data[offset..offset + 4].try_into().unwrap())) -} - /// Space-efficient implementation for tracking visited code addresses struct VisitedAddresses { inner: Vec, @@ -155,67 +144,3 @@ impl Executor { self.visited.contains(section, address) } } - -fn is_valid_jump_table_addr(obj: &ObjInfo, addr: u32) -> bool { - matches!(obj.section_at(addr), Ok(section) if section.kind != ObjSectionKind::Bss) -} - -fn get_jump_table_entries( - obj: &ObjInfo, - addr: u32, - size: Option, - from: u32, - function_start: u32, - function_end: u32, -) -> Result<(Vec, u32)> { - let section = obj.section_at(addr).with_context(|| { - format!("Failed to get jump table entries @ {:#010X} size {:?}", addr, size) - })?; - let offset = (addr as u64 - section.address) as usize; - if let Some(size) = size.map(|n| n.get()) { - log::debug!( - "Located jump table @ {:#010X} with entry count {} (from {:#010X})", - addr, - size / 4, - from - ); - let jt_data = §ion.data[offset..offset + size as usize]; - let entries = - jt_data.chunks_exact(4).map(|c| u32::from_be_bytes(c.try_into().unwrap())).collect(); - Ok((entries, size)) - } else { - let mut entries = Vec::new(); - let mut cur_addr = addr; - while let Some(value) = read_u32(§ion.data, cur_addr, section.address as u32) { - if value < function_start || value >= function_end { - break; - } - entries.push(value); - cur_addr += 4; - } - let size = cur_addr - addr; - log::debug!( - "Guessed jump table @ {:#010X} with entry count {} (from {:#010X})", - addr, - size / 4, - from - ); - Ok((entries, size)) - } -} - -pub fn uniq_jump_table_entries( - obj: &ObjInfo, - addr: u32, - size: Option, - from: u32, - function_start: u32, - function_end: u32, -) -> Result<(BTreeSet, u32)> { - if !is_valid_jump_table_addr(obj, addr) { - return Ok((BTreeSet::new(), 0)); - } - let (entries, size) = - get_jump_table_entries(obj, addr, size, from, function_start, function_end)?; - Ok((BTreeSet::from_iter(entries.iter().cloned().filter(|&addr| addr != 0)), size)) -} diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs new file mode 100644 index 0000000..19063cf --- /dev/null +++ b/src/analysis/mod.rs @@ -0,0 +1,107 @@ +use std::{collections::BTreeSet, num::NonZeroU32}; + +use anyhow::{Context, Result}; +use ppc750cl::Ins; + +use crate::obj::{ObjInfo, ObjSection, ObjSectionKind}; + +pub mod cfa; +pub mod executor; +pub mod pass; +pub mod slices; +pub mod tracker; +pub mod vm; + +pub fn disassemble(section: &ObjSection, address: u32) -> Option { + read_u32(§ion.data, address, section.address as u32).map(|code| Ins::new(code, address)) +} + +pub fn read_u32(data: &[u8], address: u32, section_address: u32) -> Option { + let offset = (address - section_address) as usize; + if data.len() < offset + 4 { + return None; + } + Some(u32::from_be_bytes(data[offset..offset + 4].try_into().unwrap())) +} + +fn is_valid_jump_table_addr(obj: &ObjInfo, addr: u32) -> bool { + matches!(obj.section_at(addr), Ok(section) if section.kind != ObjSectionKind::Bss) +} + +fn get_jump_table_entries( + obj: &ObjInfo, + addr: u32, + size: Option, + from: u32, + function_start: u32, + function_end: u32, +) -> Result<(Vec, u32)> { + let section = obj.section_at(addr).with_context(|| { + format!("Failed to get jump table entries @ {:#010X} size {:?}", addr, size) + })?; + let offset = (addr as u64 - section.address) as usize; + if let Some(size) = size.map(|n| n.get()) { + log::trace!( + "Located jump table @ {:#010X} with entry count {} (from {:#010X})", + addr, + size / 4, + from + ); + let jt_data = §ion.data[offset..offset + size as usize]; + let entries = + jt_data.chunks_exact(4).map(|c| u32::from_be_bytes(c.try_into().unwrap())).collect(); + Ok((entries, size)) + } else { + let mut entries = Vec::new(); + let mut cur_addr = addr; + while let Some(value) = read_u32(§ion.data, cur_addr, section.address as u32) { + if value < function_start || value >= function_end { + break; + } + entries.push(value); + cur_addr += 4; + } + let size = cur_addr - addr; + log::debug!( + "Guessed jump table @ {:#010X} with entry count {} (from {:#010X})", + addr, + size / 4, + from + ); + Ok((entries, size)) + } +} + +pub fn uniq_jump_table_entries( + obj: &ObjInfo, + addr: u32, + size: Option, + from: u32, + function_start: u32, + function_end: u32, +) -> Result<(BTreeSet, u32)> { + if !is_valid_jump_table_addr(obj, addr) { + return Ok((BTreeSet::new(), 0)); + } + let (entries, size) = + get_jump_table_entries(obj, addr, size, from, function_start, function_end)?; + Ok((BTreeSet::from_iter(entries.iter().cloned().filter(|&addr| addr != 0)), size)) +} + +pub fn skip_alignment(obj: &ObjInfo, mut addr: u32, end: u32) -> Option { + let mut data = match obj.section_data(addr, end) { + Ok((_, data)) => data, + Err(_) => return None, + }; + loop { + if data.is_empty() { + break None; + } + if data[0..4] == [0u8; 4] { + addr += 4; + data = &data[4..]; + } else { + break Some(addr); + } + } +} diff --git a/src/analysis/pass.rs b/src/analysis/pass.rs new file mode 100644 index 0000000..b675d08 --- /dev/null +++ b/src/analysis/pass.rs @@ -0,0 +1,117 @@ +use std::ops::Range; + +use anyhow::Result; +use flagset::FlagSet; + +use crate::{ + analysis::cfa::AnalyzerState, + obj::{ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind}, +}; + +pub trait AnalysisPass { + fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()>; +} + +pub struct FindTRKInterruptVectorTable {} + +pub const TRK_TABLE_HEADER: &str = "Metrowerks Target Resident Kernel for PowerPC"; +pub const TRK_TABLE_SIZE: u32 = 0x1F34; // always? + +// TRK_MINNOW_DOLPHIN.a __exception.s +impl AnalysisPass for FindTRKInterruptVectorTable { + fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> { + for (&start, _) in state.function_bounds.iter().filter(|&(_, &end)| end == 0) { + let (section, data) = match obj.section_data(start, 0) { + Ok((section, data)) => (section, data), + Err(_) => continue, + }; + if data.starts_with(TRK_TABLE_HEADER.as_bytes()) + && data[TRK_TABLE_HEADER.as_bytes().len()] == 0 + { + log::info!("Found gTRKInterruptVectorTable @ {:#010X}", start); + state.known_symbols.insert(start, ObjSymbol { + name: "gTRKInterruptVectorTable".to_string(), + demangled_name: None, + address: start as u64, + section: Some(section.index), + size: 0, + size_known: true, + flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)), + kind: ObjSymbolKind::Unknown, + }); + let end = start + TRK_TABLE_SIZE; + state.known_symbols.insert(end, ObjSymbol { + name: "gTRKInterruptVectorTableEnd".to_string(), + demangled_name: None, + address: end as u64, + section: Some(section.index), + size: 0, + size_known: true, + flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)), + kind: ObjSymbolKind::Unknown, + }); + + return Ok(()); + } + } + log::info!("gTRKInterruptVectorTable not found"); + Ok(()) + } +} + +pub struct FindSaveRestSleds {} + +const SLEDS: [([u8; 4], &'static str, &'static str); 4] = [ + ([0xd9, 0xcb, 0xff, 0x70], "__save_fpr", "_savefpr_"), + ([0xc9, 0xcb, 0xff, 0x70], "__restore_fpr", "_restfpr_"), + ([0x91, 0xcb, 0xff, 0xb8], "__save_gpr", "_savegpr_"), + ([0x81, 0xcb, 0xff, 0xb8], "__restore_gpr", "_restgpr_"), +]; + +// Runtime.PPCEABI.H.a runtime.c +impl AnalysisPass for FindSaveRestSleds { + fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> { + const SLED_SIZE: usize = 19 * 4; // registers 14-31 + blr + let mut clear_ranges: Vec> = vec![]; + for (&start, _) in state.function_bounds.iter().filter(|&(_, &end)| end != 0) { + let (section, data) = obj.section_data(start, 0)?; + for (needle, func, label) in &SLEDS { + if data.starts_with(needle) { + log::info!("Found {} @ {:#010X}", func, start); + clear_ranges.push(start + 4..start + SLED_SIZE as u32); + state.known_symbols.insert(start, ObjSymbol { + name: func.to_string(), + demangled_name: None, + address: start as u64, + section: Some(section.index), + size: SLED_SIZE as u64, + size_known: true, + flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), + kind: ObjSymbolKind::Function, + }); + for i in 14..=31 { + let addr = start + (i - 14) * 4; + state.known_symbols.insert(addr, ObjSymbol { + name: format!("{}{}", label, i), + demangled_name: None, + address: addr as u64, + section: Some(section.index), + size: 0, + size_known: true, + flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), + kind: ObjSymbolKind::Unknown, + }); + } + } + } + } + for range in clear_ranges { + for addr in range.step_by(4) { + state.function_entries.remove(&addr); + state.function_bounds.remove(&addr); + state.function_slices.remove(&addr); + } + } + Ok(()) + } +} diff --git a/src/util/slices.rs b/src/analysis/slices.rs similarity index 95% rename from src/util/slices.rs rename to src/analysis/slices.rs index 9e4f70d..64a9637 100644 --- a/src/util/slices.rs +++ b/src/analysis/slices.rs @@ -6,10 +6,14 @@ use std::{ use anyhow::{bail, ensure, Context, Result}; use ppc750cl::{Ins, Opcode}; -use crate::util::{ - executor::{disassemble, uniq_jump_table_entries, ExecCbData, ExecCbResult, Executor, VMState}, - obj::{ObjInfo, ObjSection, ObjSectionKind}, - vm::{BranchTarget, StepResult, VM}, +use crate::{ + analysis::{ + disassemble, + executor::{ExecCbData, ExecCbResult, Executor}, + uniq_jump_table_entries, + vm::{BranchTarget, StepResult, VM}, + }, + obj::{ObjInfo, ObjSection}, }; #[derive(Debug, Default, Clone)] @@ -43,9 +47,6 @@ impl FunctionSlices { } pub fn add_block_start(&mut self, addr: u32) -> bool { - if addr == 0xFFFFFFFF { - panic!(); - } // Slice previous block. if let Some((_, end)) = self.blocks.range_mut(..addr).last() { let last_end = *end; @@ -190,7 +191,7 @@ impl FunctionSlices { self.blocks.insert(block_start, ins.addr + 4); self.branches.insert(ins.addr, vec![addr]); if addr == ins.addr { - // pass + // Infinite loop } else if addr >= function_start && matches!(function_end, Some(known_end) if addr < known_end) { @@ -200,7 +201,7 @@ impl FunctionSlices { } } else if matches!(obj.section_data(ins.addr, ins.addr + 4), Ok((_, data)) if data == [0u8; 4]) { - // If this branch has 0'd padding after it, assume tail call. + // If this branch has zeroed padding after it, assume tail call. self.function_references.insert(addr); } else { self.possible_blocks.insert(addr); @@ -240,7 +241,7 @@ impl FunctionSlices { Ok(ExecCbResult::EndBlock) } }, - StepResult::Branch(mut branches) => { + StepResult::Branch(branches) => { // End of block self.blocks.insert(block_start, ins.addr + 4); @@ -339,18 +340,14 @@ impl FunctionSlices { ensure!(self.can_finalize(), "Can't finalize"); match (self.prologue, self.epilogue) { - (Some(p), Some(e)) => { - // log::info!("Prologue/epilogue pair: {:#010X} - {:#010X}", p, e); - } - (Some(p), None) => { - // log::info!("{:#010X?}", self); - // bail!("Unpaired prologue {:#010X}", p); + (Some(_), Some(_)) | (None, None) => {} + (Some(_), None) => { + // Likely __noreturn } (None, Some(e)) => { log::info!("{:#010X?}", self); bail!("Unpaired epilogue {:#010X}", e); } - (None, None) => {} } let end = self.end(); @@ -408,7 +405,6 @@ impl FunctionSlices { function_end: u32, known_functions: &BTreeSet, ) -> TailCallResult { - // log::info!("Determing if {:#010X} is a tail call", addr); // If jump target is already a known block or within known function bounds, not a tail call. if self.blocks.contains_key(&addr) || (addr >= function_start && addr < function_end) { return TailCallResult::Not; diff --git a/src/util/tracker.rs b/src/analysis/tracker.rs similarity index 78% rename from src/util/tracker.rs rename to src/analysis/tracker.rs index d24044e..2964507 100644 --- a/src/util/tracker.rs +++ b/src/analysis/tracker.rs @@ -1,30 +1,21 @@ use std::{ - collections::{BTreeMap, BTreeSet, VecDeque}, + collections::{BTreeMap, BTreeSet}, mem::take, }; use anyhow::{bail, Result}; -use ppc750cl::{disasm_iter, Argument, Ins, Opcode}; +use ppc750cl::Opcode; -use crate::util::{ - executor::{uniq_jump_table_entries, ExecCbData, ExecCbResult, Executor}, - obj::{ - nested_push, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, - ObjSymbolKind, +use crate::{ + analysis::{ + executor::{ExecCbData, ExecCbResult, Executor}, + uniq_jump_table_entries, + vm::{is_store_op, BranchTarget, GprValue, StepResult, VM}, }, - slices::FunctionSlices, - vm::{is_store_op, BranchTarget, GprValue, StepResult, VM}, + obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolKind}, + util::nested::NestedVec, }; -#[derive(Debug, Eq, PartialEq)] -pub enum Label { - Local, - Global, - Data, - JumpTable, - VTable, -} - #[derive(Debug, Copy, Clone)] pub enum Relocation { Ha(u32), @@ -44,15 +35,14 @@ pub enum DataKind { Byte, Float, Double, - String, - String16, + // String, + // String16, } pub struct Tracker { processed_functions: BTreeSet, sda2_base: u32, // r2 sda_base: u32, // r13 - labels: BTreeMap, pub relocations: BTreeMap, data_types: BTreeMap, stack_address: Option, @@ -74,7 +64,6 @@ impl Tracker { processed_functions: Default::default(), sda2_base: obj.sda2_base.unwrap(), sda_base: obj.sda_base.unwrap(), - labels: Default::default(), relocations: Default::default(), data_types: Default::default(), stack_address: obj.stack_address, @@ -101,7 +90,7 @@ impl Tracker { pub fn process(&mut self, obj: &ObjInfo) -> Result<()> { log::debug!("Processing code sections"); self.process_code(obj)?; - for (section_index, section) in obj.sections.iter().enumerate() { + for section in &obj.sections { if matches!(section.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData) { log::debug!("Processing section {}, address {:#X}", section.index, section.address); self.process_data(obj, section)?; @@ -110,31 +99,31 @@ impl Tracker { Ok(()) } - fn update_stack_address(&mut self, addr: u32) { - if let Some(db_stack_addr) = self.db_stack_addr { - if db_stack_addr == addr { - return; - } - } - if let Some(stack_addr) = self.stack_address { - if stack_addr != addr { - log::error!("Stack address overridden from {:#010X} to {:#010X}", stack_addr, addr); - return; - } - } - log::debug!("Located stack address: {:08X}", addr); - self.stack_address = Some(addr); - let db_stack_addr = addr + 0x2000; - self.db_stack_addr = Some(db_stack_addr); - self.arena_lo = Some((db_stack_addr + 0x1F) & !0x1F); - // __ArenaHi is fixed (until it isn't?) - self.arena_hi = Some(0x81700000); - log::debug!("_stack_addr: {:#010X}", addr); - log::debug!("_stack_end: {:#010X}", self.stack_end.unwrap()); - log::debug!("_db_stack_addr: {:#010X}", db_stack_addr); - log::debug!("__ArenaLo: {:#010X}", self.arena_lo.unwrap()); - log::debug!("__ArenaHi: {:#010X}", self.arena_hi.unwrap()); - } + // fn update_stack_address(&mut self, addr: u32) { + // if let Some(db_stack_addr) = self.db_stack_addr { + // if db_stack_addr == addr { + // return; + // } + // } + // if let Some(stack_addr) = self.stack_address { + // if stack_addr != addr { + // log::error!("Stack address overridden from {:#010X} to {:#010X}", stack_addr, addr); + // return; + // } + // } + // log::debug!("Located stack address: {:08X}", addr); + // self.stack_address = Some(addr); + // let db_stack_addr = addr + 0x2000; + // self.db_stack_addr = Some(db_stack_addr); + // self.arena_lo = Some((db_stack_addr + 0x1F) & !0x1F); + // // __ArenaHi is fixed (until it isn't?) + // self.arena_hi = Some(0x81700000); + // log::debug!("_stack_addr: {:#010X}", addr); + // log::debug!("_stack_end: {:#010X}", self.stack_end.unwrap()); + // log::debug!("_db_stack_addr: {:#010X}", db_stack_addr); + // log::debug!("__ArenaLo: {:#010X}", self.arena_lo.unwrap()); + // log::debug!("__ArenaHi: {:#010X}", self.arena_hi.unwrap()); + // } fn process_code(&mut self, obj: &ObjInfo) -> Result<()> { let mut symbol_map = BTreeMap::new(); @@ -156,10 +145,11 @@ impl Tracker { } } // Special handling for gTRKInterruptVectorTable - if let (Some(trk_interrupt_table), Some(trk_interrupt_vector_table_end)) = ( - obj.symbols.iter().find(|sym| sym.name == "gTRKInterruptVectorTable"), - obj.symbols.iter().find(|sym| sym.name == "gTRKInterruptVectorTableEnd"), - ) {} + // TODO + // if let (Some(trk_interrupt_table), Some(trk_interrupt_vector_table_end)) = ( + // obj.symbols.iter().find(|sym| sym.name == "gTRKInterruptVectorTable"), + // obj.symbols.iter().find(|sym| sym.name == "gTRKInterruptVectorTableEnd"), + // ) {} Ok(()) } @@ -194,7 +184,7 @@ impl Tracker { function_end: u32, possible_missed_branches: &mut BTreeMap>, ) -> Result> { - let ExecCbData { executor, vm, result, section, ins, block_start } = data; + let ExecCbData { executor, vm, result, section: _, ins, block_start: _ } = data; let is_function_addr = |addr: u32| addr >= function_start && addr < function_end; match result { @@ -236,13 +226,8 @@ impl Tracker { } Opcode::Ori => { // ori rA, rS, UIMM - let source = ins.field_rS(); let target = ins.field_rA(); if let GprValue::Constant(value) = vm.gpr[target].value { - // if target == 1 { - // log::debug!("Stack address written from {:#010X}", ins.addr); - // self.update_stack_address(value); - // } if self.is_valid_address(obj, ins.addr, value) { if let (Some(hi_addr), Some(lo_addr)) = (vm.gpr[target].hi_addr, vm.gpr[target].lo_addr) @@ -444,18 +429,34 @@ impl Tracker { { return true; } - if addr > 0x80000000 && addr < 0x80003100 { - return true; - } + // if addr > 0x80000000 && addr < 0x80003100 { + // return true; + // } for section in &obj.sections { if addr >= section.address as u32 && addr <= (section.address + section.size) as u32 { - return true; + // References to code sections will never be unaligned + return section.kind != ObjSectionKind::Code || addr & 3 == 0; } } false } - fn special_symbol(&self, obj: &mut ObjInfo, addr: u32) -> Option { + fn special_symbol( + &self, + obj: &mut ObjInfo, + addr: u32, + reloc_kind: ObjRelocKind, + ) -> Option { + if !matches!(reloc_kind, ObjRelocKind::PpcAddr16Ha | ObjRelocKind::PpcAddr16Lo) { + return None; + } + // HACK for RSOStaticLocateObject + for section in &obj.sections { + if addr == section.address as u32 { + let name = format!("_f_{}", section.name.trim_start_matches('.')); + return Some(generate_special_symbol(obj, addr, &name)); + } + } let mut check_symbol = |opt: Option, name: &str| -> Option { if let Some(value) = opt { if addr == value { @@ -532,96 +533,96 @@ impl Tracker { Relocation::Rel24(v) => (ObjRelocKind::PpcRel24, v), Relocation::Absolute(v) => (ObjRelocKind::Absolute, v), }; - let (target_symbol, addend) = if let Some(symbol) = self.special_symbol(obj, target) { - (symbol, 0) - } else { - let target_section = - match obj.sections.iter().find(|s| { + let (target_symbol, addend) = + if let Some(symbol) = self.special_symbol(obj, target, reloc_kind) { + (symbol, 0) + } else { + let target_section = match obj.sections.iter().find(|s| { target >= s.address as u32 && target < (s.address + s.size) as u32 }) { Some(v) => v, None => continue, }; - // Try to find a previous sized symbol that encompasses the target - let sym_map = &mut symbol_maps[target_section.index]; - let target_symbol = { - let mut result = None; - for (&addr, symbol_idxs) in sym_map.range(..=target).rev() { - let symbol_idx = if symbol_idxs.len() == 1 { - symbol_idxs.first().cloned().unwrap() - } else { - let mut symbol_idxs = symbol_idxs.clone(); - symbol_idxs.sort_by_key(|&symbol_idx| { - let symbol = &obj.symbols[symbol_idx]; - let mut rank = match symbol.kind { - ObjSymbolKind::Function | ObjSymbolKind::Object => { - match reloc_kind { + // Try to find a previous sized symbol that encompasses the target + let sym_map = &mut symbol_maps[target_section.index]; + let target_symbol = { + let mut result = None; + for (_addr, symbol_idxs) in sym_map.range(..=target).rev() { + let symbol_idx = if symbol_idxs.len() == 1 { + symbol_idxs.first().cloned().unwrap() + } else { + let mut symbol_idxs = symbol_idxs.clone(); + symbol_idxs.sort_by_key(|&symbol_idx| { + let symbol = &obj.symbols[symbol_idx]; + let mut rank = match symbol.kind { + ObjSymbolKind::Function | ObjSymbolKind::Object => { + match reloc_kind { + ObjRelocKind::PpcAddr16Hi + | ObjRelocKind::PpcAddr16Ha + | ObjRelocKind::PpcAddr16Lo => 1, + ObjRelocKind::Absolute + | ObjRelocKind::PpcRel24 + | ObjRelocKind::PpcRel14 + | ObjRelocKind::PpcEmbSda21 => 2, + } + } + // Label + ObjSymbolKind::Unknown => match reloc_kind { ObjRelocKind::PpcAddr16Hi | ObjRelocKind::PpcAddr16Ha - | ObjRelocKind::PpcAddr16Lo => 1, - ObjRelocKind::Absolute - | ObjRelocKind::PpcRel24 - | ObjRelocKind::PpcRel14 - | ObjRelocKind::PpcEmbSda21 => 2, - } + | ObjRelocKind::PpcAddr16Lo + if !symbol.name.starts_with("..") => + { + 3 + } + _ => 1, + }, + ObjSymbolKind::Section => -1, + }; + if symbol.size > 0 { + rank += 1; } - // Label - ObjSymbolKind::Unknown => match reloc_kind { - ObjRelocKind::PpcAddr16Hi - | ObjRelocKind::PpcAddr16Ha - | ObjRelocKind::PpcAddr16Lo - if !symbol.name.starts_with("..") => - { - 3 - } - _ => 1, - }, - ObjSymbolKind::Section => -1, - }; - if symbol.size > 0 { - rank += 1; + -rank + }); + match symbol_idxs.first().cloned() { + Some(v) => v, + None => continue, } - -rank - }); - match symbol_idxs.first().cloned() { - Some(v) => v, - None => continue, - } - }; - let symbol = &obj.symbols[symbol_idx]; - if symbol.address == target as u64 { - result = Some(symbol_idx); - break; - } - if symbol.size > 0 { - if symbol.address + symbol.size > target as u64 { + }; + let symbol = &obj.symbols[symbol_idx]; + if symbol.address == target as u64 { result = Some(symbol_idx); + break; + } + if symbol.size > 0 { + if symbol.address + symbol.size > target as u64 { + result = Some(symbol_idx); + } + break; } - break; } + result + }; + if let Some(symbol_idx) = target_symbol { + let symbol = &obj.symbols[symbol_idx]; + (symbol_idx, target as i64 - symbol.address as i64) + } else { + // Create a new label + let symbol_idx = obj.symbols.len(); + obj.symbols.push(ObjSymbol { + name: format!("lbl_{:08X}", target), + demangled_name: None, + address: target as u64, + section: Some(target_section.index), + size: 0, + size_known: false, + flags: Default::default(), + kind: Default::default(), + }); + sym_map.nested_push(target, symbol_idx); + (symbol_idx, 0) } - result }; - if let Some(symbol_idx) = target_symbol { - let symbol = &obj.symbols[symbol_idx]; - (symbol_idx, target as i64 - symbol.address as i64) - } else { - // Create a new label - let symbol_idx = obj.symbols.len(); - obj.symbols.push(ObjSymbol { - name: format!("lbl_{:08X}", target), - demangled_name: None, - address: target as u64, - section: Some(target_section.index), - size: 0, - size_known: false, - flags: Default::default(), - kind: Default::default(), - }); - nested_push(sym_map, target, symbol_idx); - (symbol_idx, 0) - } - }; let reloc = ObjReloc { kind: reloc_kind, address: addr as u64, target_symbol, addend }; let section = match obj .sections @@ -660,8 +661,6 @@ impl Tracker { } } -fn is_branch_with_link(ins: &Ins) -> bool { ins.is_branch() && ins.field_LK() } - fn data_kind_from_op(op: Opcode) -> DataKind { match op { Opcode::Lbz => DataKind::Byte, diff --git a/src/util/vm.rs b/src/analysis/vm.rs similarity index 99% rename from src/util/vm.rs rename to src/analysis/vm.rs index dbf6ba0..8067cda 100644 --- a/src/util/vm.rs +++ b/src/analysis/vm.rs @@ -2,7 +2,7 @@ use std::num::NonZeroU32; use ppc750cl::{Argument, Ins, Opcode, GPR}; -use crate::util::obj::ObjInfo; +use crate::obj::ObjInfo; #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] pub enum GprValue { @@ -684,7 +684,7 @@ mod tests { fn test_load_indexed_3() { let mut vm = VM::new(); assert_eq!(vm.step(&Ins::new(0x28000127, 0x800ed458)), StepResult::Continue); // cmplwi r0, 0x127 - assert_eq!(vm.cr, Cr { + assert_eq!(vm.cr[0], Cr { signed: false, left: GprValue::Unknown, right: GprValue::Constant(295), diff --git a/src/cmd/ar.rs b/src/cmd/ar.rs index 239133e..3005e5f 100644 --- a/src/cmd/ar.rs +++ b/src/cmd/ar.rs @@ -1,14 +1,16 @@ use std::{ collections::{btree_map::Entry, BTreeMap}, fs::File, - io::{BufRead, BufReader, BufWriter, Write}, + io::{BufRead, BufWriter, Write}, path::PathBuf, }; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{anyhow, bail, Result}; use argh::FromArgs; use object::{Object, ObjectSymbol, SymbolScope}; +use crate::util::file::{buf_reader, map_file}; + #[derive(FromArgs, PartialEq, Debug)] /// Commands for processing static libraries. #[argh(subcommand, name = "ar")] @@ -49,10 +51,7 @@ fn create(args: CreateArgs) -> Result<()> { path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?; match path_str.strip_prefix('@') { Some(rsp_file) => { - let reader = BufReader::new( - File::open(rsp_file) - .with_context(|| format!("Failed to open file '{rsp_file}'"))?, - ); + let reader = buf_reader(rsp_file)?; for result in reader.lines() { let line = result?; if !line.is_empty() { @@ -79,11 +78,8 @@ fn create(args: CreateArgs) -> Result<()> { Entry::Vacant(e) => e.insert(Vec::new()), Entry::Occupied(_) => bail!("Duplicate file name '{path_str}'"), }; - let object_file = File::open(path) - .with_context(|| format!("Failed to open object file '{}'", path.display()))?; - let map = unsafe { memmap2::MmapOptions::new().map(&object_file) } - .with_context(|| format!("Failed to mmap object file: '{}'", path.display()))?; - let obj = object::File::parse(map.as_ref())?; + let mmap = map_file(path)?; + let obj = object::File::parse(&*mmap)?; for symbol in obj.symbols() { if symbol.scope() == SymbolScope::Dynamic { entries.push(symbol.name_bytes()?.to_vec()); @@ -93,8 +89,13 @@ fn create(args: CreateArgs) -> Result<()> { // Write archive let out = BufWriter::new(File::create(&args.out)?); - let mut builder = - ar::GnuBuilder::new(out, identifiers, ar::GnuSymbolTableFormat::Size32, symbol_table)?; + let mut builder = ar::GnuBuilder::new_with_symbol_table( + out, + true, + identifiers, + ar::GnuSymbolTableFormat::Size32, + symbol_table, + )?; for path in files { let path_str = path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?; diff --git a/src/cmd/dol.rs b/src/cmd/dol.rs index 4adcbee..edea0f3 100644 --- a/src/cmd/dol.rs +++ b/src/cmd/dol.rs @@ -1,30 +1,37 @@ use std::{ collections::BTreeMap, - fs::File, - io::{BufRead, BufReader, BufWriter}, + fs, + fs::{DirBuilder, File}, + io::{BufRead, BufWriter, Write}, path::{Path, PathBuf}, }; +use std::collections::{hash_map, HashMap}; use anyhow::{anyhow, bail, Context, Result}; use argh::FromArgs; -use crate::util::{ - cfa::{ - locate_sda_bases, AnalysisPass, AnalyzerState, FindSaveRestSleds, - FindTRKInterruptVectorTable, +use crate::{ + analysis::{ + cfa::AnalyzerState, + pass::{AnalysisPass, FindSaveRestSleds, FindTRKInterruptVectorTable}, + read_u32, + tracker::Tracker, }, - config::{parse_symbol_line, write_symbols}, - dol::process_dol, - elf::process_elf, - executor::read_u32, - map::process_map, obj::{ - ObjInfo, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, - ObjSymbolKind, + signatures::{apply_signature, check_signatures, check_signatures_str, parse_signatures}, + split::split_obj, + ObjInfo, ObjRelocKind, ObjSectionKind, ObjSymbolKind, + }, + util::{ + asm::write_asm, + config::{apply_splits, parse_symbol_line, write_symbols}, + dol::process_dol, + elf::process_elf, + file::{map_file, map_reader}, + map::process_map, }, - sigs::check_signatures, - tracker::Tracker, }; +use crate::util::elf::write_elf; #[derive(FromArgs, PartialEq, Debug)] /// Commands for processing DOL files. @@ -42,7 +49,7 @@ enum SubCommand { } #[derive(FromArgs, PartialEq, Eq, Debug)] -/// Disassembles a DOL file. +/// disassembles a DOL file #[argh(subcommand, name = "disasm")] pub struct DisasmArgs { #[argh(option, short = 'm')] @@ -51,12 +58,18 @@ pub struct DisasmArgs { #[argh(option, short = 's')] /// path to symbols file symbols_file: Option, + #[argh(option, short = 'p')] + /// path to splits file + splits_file: Option, #[argh(option, short = 'e')] /// ELF file to validate against (debugging only) elf_file: Option, #[argh(positional)] /// DOL file dol_file: PathBuf, + #[argh(option, short = 'o')] + /// output file (or directory, if splitting) + out: PathBuf, } #[derive(FromArgs, PartialEq, Eq, Debug)] @@ -76,152 +89,201 @@ pub fn run(args: Args) -> Result<()> { } const SIGNATURES: &[(&str, &str)] = &[ - ("__init_registers", include_str!("../../assets/__init_registers.yml")), - ("__init_hardware", include_str!("../../assets/__init_hardware.yml")), - ("__init_data", include_str!("../../assets/__init_data.yml")), - ("__set_debug_bba", include_str!("../../assets/__set_debug_bba.yml")), - ("__OSPSInit", include_str!("../../assets/__OSPSInit.yml")), - ("__OSFPRInit", include_str!("../../assets/__OSFPRInit.yml")), - ("__OSCacheInit", include_str!("../../assets/__OSCacheInit.yml")), - ("DMAErrorHandler", include_str!("../../assets/DMAErrorHandler.yml")), - ("DBInit", include_str!("../../assets/DBInit.yml")), - ("OSInit", include_str!("../../assets/OSInit.yml")), - ("__OSThreadInit", include_str!("../../assets/__OSThreadInit.yml")), - ("__OSInitIPCBuffer", include_str!("../../assets/__OSInitIPCBuffer.yml")), - ("EXIInit", include_str!("../../assets/EXIInit.yml")), - ("EXIGetID", include_str!("../../assets/EXIGetID.yml")), - ("exit", include_str!("../../assets/exit.yml")), - ("_ExitProcess", include_str!("../../assets/_ExitProcess.yml")), - ("__fini_cpp", include_str!("../../assets/__fini_cpp.yml")), - ("__destroy_global_chain", include_str!("../../assets/__destroy_global_chain.yml")), - ("InitMetroTRK", include_str!("../../assets/InitMetroTRK.yml")), - ("InitMetroTRKCommTable", include_str!("../../assets/InitMetroTRKCommTable.yml")), - ("OSExceptionInit", include_str!("../../assets/OSExceptionInit.yml")), - ("OSDefaultExceptionHandler", include_str!("../../assets/OSDefaultExceptionHandler.yml")), - ("__OSUnhandledException", include_str!("../../assets/__OSUnhandledException.yml")), - ("OSDisableScheduler", include_str!("../../assets/OSDisableScheduler.yml")), - ("__OSReschedule", include_str!("../../assets/__OSReschedule.yml")), - ("__OSInitSystemCall", include_str!("../../assets/__OSInitSystemCall.yml")), - ("OSInitAlarm", include_str!("../../assets/OSInitAlarm.yml")), - ("__OSInitAlarm", include_str!("../../assets/__OSInitAlarm.yml")), - ("__OSEVStart", include_str!("../../assets/OSExceptionVector.yml")), - ("__OSDBINTSTART", include_str!("../../assets/__OSDBIntegrator.yml")), - ("__OSDBJUMPSTART", include_str!("../../assets/__OSDBJump.yml")), - ("SIInit", include_str!("../../assets/SIInit.yml")), - ("SIGetType", include_str!("../../assets/SIGetType.yml")), - ("SISetSamplingRate", include_str!("../../assets/SISetSamplingRate.yml")), - ("SISetXY", include_str!("../../assets/SISetXY.yml")), - ("VIGetTvFormat", include_str!("../../assets/VIGetTvFormat.yml")), - ("DVDInit", include_str!("../../assets/DVDInit.yml")), - ("DVDSetAutoFatalMessaging", include_str!("../../assets/DVDSetAutoFatalMessaging.yml")), - ("OSSetArenaLo", include_str!("../../assets/OSSetArenaLo.yml")), - ("OSSetArenaHi", include_str!("../../assets/OSSetArenaHi.yml")), - ("OSSetMEM1ArenaLo", include_str!("../../assets/OSSetMEM1ArenaLo.yml")), - ("OSSetMEM1ArenaHi", include_str!("../../assets/OSSetMEM1ArenaHi.yml")), - ("OSSetMEM2ArenaLo", include_str!("../../assets/OSSetMEM2ArenaLo.yml")), - ("OSSetMEM2ArenaHi", include_str!("../../assets/OSSetMEM2ArenaHi.yml")), - ("__OSInitAudioSystem", include_str!("../../assets/__OSInitAudioSystem.yml")), - ("__OSInitMemoryProtection", include_str!("../../assets/__OSInitMemoryProtection.yml")), - // ("BATConfig", include_str!("../../assets/BATConfig.yml")), TODO - ("ReportOSInfo", include_str!("../../assets/ReportOSInfo.yml")), - ("__check_pad3", include_str!("../../assets/__check_pad3.yml")), - ("OSResetSystem", include_str!("../../assets/OSResetSystem.yml")), - ("OSReturnToMenu", include_str!("../../assets/OSReturnToMenu.yml")), - ("__OSReturnToMenu", include_str!("../../assets/__OSReturnToMenu.yml")), - ("__OSShutdownDevices", include_str!("../../assets/__OSShutdownDevices.yml")), - ("__OSInitSram", include_str!("../../assets/__OSInitSram.yml")), - ("__OSSyncSram", include_str!("../../assets/__OSSyncSram.yml")), - ("__OSGetExceptionHandler", include_str!("../../assets/__OSGetExceptionHandler.yml")), - ("OSRegisterResetFunction", include_str!("../../assets/OSRegisterResetFunction.yml")), - ("OSRegisterShutdownFunction", include_str!("../../assets/OSRegisterShutdownFunction.yml")), - ("DecrementerExceptionHandler", include_str!("../../assets/DecrementerExceptionHandler.yml")), - ("DecrementerExceptionCallback", include_str!("../../assets/DecrementerExceptionCallback.yml")), - ("__OSInterruptInit", include_str!("../../assets/__OSInterruptInit.yml")), - ("__OSContextInit", include_str!("../../assets/__OSContextInit.yml")), - ("OSSwitchFPUContext", include_str!("../../assets/OSSwitchFPUContext.yml")), - ("OSReport", include_str!("../../assets/OSReport.yml")), - ("TRK_main", include_str!("../../assets/TRK_main.yml")), - ("TRKNubWelcome", include_str!("../../assets/TRKNubWelcome.yml")), - ("TRKInitializeNub", include_str!("../../assets/TRKInitializeNub.yml")), - ("TRKInitializeIntDrivenUART", include_str!("../../assets/TRKInitializeIntDrivenUART.yml")), - ("TRKEXICallBack", include_str!("../../assets/TRKEXICallBack.yml")), - ("TRKLoadContext", include_str!("../../assets/TRKLoadContext.yml")), - ("TRKInterruptHandler", include_str!("../../assets/TRKInterruptHandler.yml")), - ("TRKExceptionHandler", include_str!("../../assets/TRKExceptionHandler.yml")), - ("TRKSaveExtended1Block", include_str!("../../assets/TRKSaveExtended1Block.yml")), - ("TRKNubMainLoop", include_str!("../../assets/TRKNubMainLoop.yml")), - ("TRKTargetContinue", include_str!("../../assets/TRKTargetContinue.yml")), - ("TRKSwapAndGo", include_str!("../../assets/TRKSwapAndGo.yml")), - ("TRKRestoreExtended1Block", include_str!("../../assets/TRKRestoreExtended1Block.yml")), + ("__init_registers", include_str!("../../assets/signatures/__init_registers.yml")), + ("__init_hardware", include_str!("../../assets/signatures/__init_hardware.yml")), + ("__init_data", include_str!("../../assets/signatures/__init_data.yml")), + ("__set_debug_bba", include_str!("../../assets/signatures/__set_debug_bba.yml")), + ("__OSPSInit", include_str!("../../assets/signatures/__OSPSInit.yml")), + ("__OSFPRInit", include_str!("../../assets/signatures/__OSFPRInit.yml")), + ("__OSCacheInit", include_str!("../../assets/signatures/__OSCacheInit.yml")), + ("DMAErrorHandler", include_str!("../../assets/signatures/DMAErrorHandler.yml")), + ("DBInit", include_str!("../../assets/signatures/DBInit.yml")), + ("OSInit", include_str!("../../assets/signatures/OSInit.yml")), + ("__OSThreadInit", include_str!("../../assets/signatures/__OSThreadInit.yml")), + ("__OSInitIPCBuffer", include_str!("../../assets/signatures/__OSInitIPCBuffer.yml")), + ("EXIInit", include_str!("../../assets/signatures/EXIInit.yml")), + ("EXIGetID", include_str!("../../assets/signatures/EXIGetID.yml")), + ("exit", include_str!("../../assets/signatures/exit.yml")), + ("_ExitProcess", include_str!("../../assets/signatures/_ExitProcess.yml")), + ("__fini_cpp", include_str!("../../assets/signatures/__fini_cpp.yml")), + ("__destroy_global_chain", include_str!("../../assets/signatures/__destroy_global_chain.yml")), + ("InitMetroTRK", include_str!("../../assets/signatures/InitMetroTRK.yml")), + ("InitMetroTRKCommTable", include_str!("../../assets/signatures/InitMetroTRKCommTable.yml")), + ("OSExceptionInit", include_str!("../../assets/signatures/OSExceptionInit.yml")), + ( + "OSDefaultExceptionHandler", + include_str!("../../assets/signatures/OSDefaultExceptionHandler.yml"), + ), + ("__OSUnhandledException", include_str!("../../assets/signatures/__OSUnhandledException.yml")), + ("OSDisableScheduler", include_str!("../../assets/signatures/OSDisableScheduler.yml")), + ("__OSReschedule", include_str!("../../assets/signatures/__OSReschedule.yml")), + ("__OSInitSystemCall", include_str!("../../assets/signatures/__OSInitSystemCall.yml")), + ("OSInitAlarm", include_str!("../../assets/signatures/OSInitAlarm.yml")), + ("__OSInitAlarm", include_str!("../../assets/signatures/__OSInitAlarm.yml")), + ("__OSEVStart", include_str!("../../assets/signatures/OSExceptionVector.yml")), + ("__OSDBINTSTART", include_str!("../../assets/signatures/__OSDBIntegrator.yml")), + ("__OSDBJUMPSTART", include_str!("../../assets/signatures/__OSDBJump.yml")), + ("SIInit", include_str!("../../assets/signatures/SIInit.yml")), + ("SIGetType", include_str!("../../assets/signatures/SIGetType.yml")), + ("SISetSamplingRate", include_str!("../../assets/signatures/SISetSamplingRate.yml")), + ("SISetXY", include_str!("../../assets/signatures/SISetXY.yml")), + ("VIGetTvFormat", include_str!("../../assets/signatures/VIGetTvFormat.yml")), + ("DVDInit", include_str!("../../assets/signatures/DVDInit.yml")), + ( + "DVDSetAutoFatalMessaging", + include_str!("../../assets/signatures/DVDSetAutoFatalMessaging.yml"), + ), + ("OSSetArenaLo", include_str!("../../assets/signatures/OSSetArenaLo.yml")), + ("OSSetArenaHi", include_str!("../../assets/signatures/OSSetArenaHi.yml")), + ("OSSetMEM1ArenaLo", include_str!("../../assets/signatures/OSSetMEM1ArenaLo.yml")), + ("OSSetMEM1ArenaHi", include_str!("../../assets/signatures/OSSetMEM1ArenaHi.yml")), + ("OSSetMEM2ArenaLo", include_str!("../../assets/signatures/OSSetMEM2ArenaLo.yml")), + ("OSSetMEM2ArenaHi", include_str!("../../assets/signatures/OSSetMEM2ArenaHi.yml")), + ("__OSInitAudioSystem", include_str!("../../assets/signatures/__OSInitAudioSystem.yml")), + ( + "__OSInitMemoryProtection", + include_str!("../../assets/signatures/__OSInitMemoryProtection.yml"), + ), + // ("BATConfig", include_str!("../../assets/signatures/BATConfig.yml")), TODO + ("ReportOSInfo", include_str!("../../assets/signatures/ReportOSInfo.yml")), + ("__check_pad3", include_str!("../../assets/signatures/__check_pad3.yml")), + ("OSResetSystem", include_str!("../../assets/signatures/OSResetSystem.yml")), + ("OSReturnToMenu", include_str!("../../assets/signatures/OSReturnToMenu.yml")), + ("__OSReturnToMenu", include_str!("../../assets/signatures/__OSReturnToMenu.yml")), + ("__OSShutdownDevices", include_str!("../../assets/signatures/__OSShutdownDevices.yml")), + ("__OSInitSram", include_str!("../../assets/signatures/__OSInitSram.yml")), + ("__OSSyncSram", include_str!("../../assets/signatures/__OSSyncSram.yml")), + ( + "__OSGetExceptionHandler", + include_str!("../../assets/signatures/__OSGetExceptionHandler.yml"), + ), + ( + "OSRegisterResetFunction", + include_str!("../../assets/signatures/OSRegisterResetFunction.yml"), + ), + ( + "OSRegisterShutdownFunction", + include_str!("../../assets/signatures/OSRegisterShutdownFunction.yml"), + ), + ( + "DecrementerExceptionHandler", + include_str!("../../assets/signatures/DecrementerExceptionHandler.yml"), + ), + ( + "DecrementerExceptionCallback", + include_str!("../../assets/signatures/DecrementerExceptionCallback.yml"), + ), + ("__OSInterruptInit", include_str!("../../assets/signatures/__OSInterruptInit.yml")), + ("__OSContextInit", include_str!("../../assets/signatures/__OSContextInit.yml")), + ("OSSwitchFPUContext", include_str!("../../assets/signatures/OSSwitchFPUContext.yml")), + ("OSReport", include_str!("../../assets/signatures/OSReport.yml")), + ("TRK_main", include_str!("../../assets/signatures/TRK_main.yml")), + ("TRKNubWelcome", include_str!("../../assets/signatures/TRKNubWelcome.yml")), + ("TRKInitializeNub", include_str!("../../assets/signatures/TRKInitializeNub.yml")), + ( + "TRKInitializeIntDrivenUART", + include_str!("../../assets/signatures/TRKInitializeIntDrivenUART.yml"), + ), + ("TRKEXICallBack", include_str!("../../assets/signatures/TRKEXICallBack.yml")), + ("TRKLoadContext", include_str!("../../assets/signatures/TRKLoadContext.yml")), + ("TRKInterruptHandler", include_str!("../../assets/signatures/TRKInterruptHandler.yml")), + ("TRKExceptionHandler", include_str!("../../assets/signatures/TRKExceptionHandler.yml")), + ("TRKSaveExtended1Block", include_str!("../../assets/signatures/TRKSaveExtended1Block.yml")), + ("TRKNubMainLoop", include_str!("../../assets/signatures/TRKNubMainLoop.yml")), + ("TRKTargetContinue", include_str!("../../assets/signatures/TRKTargetContinue.yml")), + ("TRKSwapAndGo", include_str!("../../assets/signatures/TRKSwapAndGo.yml")), + ( + "TRKRestoreExtended1Block", + include_str!("../../assets/signatures/TRKRestoreExtended1Block.yml"), + ), ( "TRKInterruptHandlerEnableInterrupts", - include_str!("../../assets/TRKInterruptHandlerEnableInterrupts.yml"), + include_str!("../../assets/signatures/TRKInterruptHandlerEnableInterrupts.yml"), ), - ("memset", include_str!("../../assets/memset.yml")), + ("memset", include_str!("../../assets/signatures/memset.yml")), ( "__msl_runtime_constraint_violation_s", - include_str!("../../assets/__msl_runtime_constraint_violation_s.yml"), + include_str!("../../assets/signatures/__msl_runtime_constraint_violation_s.yml"), ), - ("ClearArena", include_str!("../../assets/ClearArena.yml")), - ("IPCCltInit", include_str!("../../assets/IPCCltInit.yml")), - ("__OSInitSTM", include_str!("../../assets/__OSInitSTM.yml")), - ("IOS_Open", include_str!("../../assets/IOS_Open.yml")), - ("__ios_Ipc2", include_str!("../../assets/__ios_Ipc2.yml")), - ("IPCiProfQueueReq", include_str!("../../assets/IPCiProfQueueReq.yml")), - ("SCInit", include_str!("../../assets/SCInit.yml")), - ("SCReloadConfFileAsync", include_str!("../../assets/SCReloadConfFileAsync.yml")), - ("NANDPrivateOpenAsync", include_str!("../../assets/NANDPrivateOpenAsync.yml")), - ("nandIsInitialized", include_str!("../../assets/nandIsInitialized.yml")), - ("nandOpen", include_str!("../../assets/nandOpen.yml")), - ("nandGenerateAbsPath", include_str!("../../assets/nandGenerateAbsPath.yml")), - ("nandGetHeadToken", include_str!("../../assets/nandGetHeadToken.yml")), - ("ISFS_OpenAsync", include_str!("../../assets/ISFS_OpenAsync.yml")), - ("nandConvertErrorCode", include_str!("../../assets/nandConvertErrorCode.yml")), - ("NANDLoggingAddMessageAsync", include_str!("../../assets/NANDLoggingAddMessageAsync.yml")), - ("__NANDPrintErrorMessage", include_str!("../../assets/__NANDPrintErrorMessage.yml")), - ("__OSInitNet", include_str!("../../assets/__OSInitNet.yml")), - ("__DVDCheckDevice", include_str!("../../assets/__DVDCheckDevice.yml")), - ("__OSInitPlayTime", include_str!("../../assets/__OSInitPlayTime.yml")), - ("__OSStartPlayRecord", include_str!("../../assets/__OSStartPlayRecord.yml")), - ("NANDInit", include_str!("../../assets/NANDInit.yml")), - ("ISFS_OpenLib", include_str!("../../assets/ISFS_OpenLib.yml")), - ("ESP_GetTitleId", include_str!("../../assets/ESP_GetTitleId.yml")), - ("NANDSetAutoErrorMessaging", include_str!("../../assets/NANDSetAutoErrorMessaging.yml")), - ("__DVDFSInit", include_str!("../../assets/__DVDFSInit.yml")), - ("__DVDClearWaitingQueue", include_str!("../../assets/__DVDClearWaitingQueue.yml")), - ("__DVDInitWA", include_str!("../../assets/__DVDInitWA.yml")), - ("__DVDLowSetWAType", include_str!("../../assets/__DVDLowSetWAType.yml")), - ("__fstLoad", include_str!("../../assets/__fstLoad.yml")), - ("DVDReset", include_str!("../../assets/DVDReset.yml")), - ("DVDLowReset", include_str!("../../assets/DVDLowReset.yml")), - ("DVDReadDiskID", include_str!("../../assets/DVDReadDiskID.yml")), - ("stateReady", include_str!("../../assets/stateReady.yml")), - ("DVDLowWaitCoverClose", include_str!("../../assets/DVDLowWaitCoverClose.yml")), - ("__DVDStoreErrorCode", include_str!("../../assets/__DVDStoreErrorCode.yml")), - ("DVDLowStopMotor", include_str!("../../assets/DVDLowStopMotor.yml")), - ("DVDGetDriveStatus", include_str!("../../assets/DVDGetDriveStatus.yml")), - ("printf", include_str!("../../assets/printf.yml")), - ("sprintf", include_str!("../../assets/sprintf.yml")), - ("vprintf", include_str!("../../assets/vprintf.yml")), - ("vsprintf", include_str!("../../assets/vsprintf.yml")), - ("vsnprintf", include_str!("../../assets/vsnprintf.yml")), - ("__pformatter", include_str!("../../assets/__pformatter.yml")), - ("longlong2str", include_str!("../../assets/longlong2str.yml")), - ("__mod2u", include_str!("../../assets/__mod2u.yml")), - ("__FileWrite", include_str!("../../assets/__FileWrite.yml")), - ("fwrite", include_str!("../../assets/fwrite.yml")), - ("__fwrite", include_str!("../../assets/__fwrite.yml")), - ("__stdio_atexit", include_str!("../../assets/__stdio_atexit.yml")), - ("__StringWrite", include_str!("../../assets/__StringWrite.yml")), + ("ClearArena", include_str!("../../assets/signatures/ClearArena.yml")), + ("IPCCltInit", include_str!("../../assets/signatures/IPCCltInit.yml")), + ("__OSInitSTM", include_str!("../../assets/signatures/__OSInitSTM.yml")), + ("IOS_Open", include_str!("../../assets/signatures/IOS_Open.yml")), + ("__ios_Ipc2", include_str!("../../assets/signatures/__ios_Ipc2.yml")), + ("IPCiProfQueueReq", include_str!("../../assets/signatures/IPCiProfQueueReq.yml")), + ("SCInit", include_str!("../../assets/signatures/SCInit.yml")), + ("SCReloadConfFileAsync", include_str!("../../assets/signatures/SCReloadConfFileAsync.yml")), + ("NANDPrivateOpenAsync", include_str!("../../assets/signatures/NANDPrivateOpenAsync.yml")), + ("nandIsInitialized", include_str!("../../assets/signatures/nandIsInitialized.yml")), + ("nandOpen", include_str!("../../assets/signatures/nandOpen.yml")), + ("nandGenerateAbsPath", include_str!("../../assets/signatures/nandGenerateAbsPath.yml")), + ("nandGetHeadToken", include_str!("../../assets/signatures/nandGetHeadToken.yml")), + ("ISFS_OpenAsync", include_str!("../../assets/signatures/ISFS_OpenAsync.yml")), + ("nandConvertErrorCode", include_str!("../../assets/signatures/nandConvertErrorCode.yml")), + ( + "NANDLoggingAddMessageAsync", + include_str!("../../assets/signatures/NANDLoggingAddMessageAsync.yml"), + ), + ( + "__NANDPrintErrorMessage", + include_str!("../../assets/signatures/__NANDPrintErrorMessage.yml"), + ), + ("__OSInitNet", include_str!("../../assets/signatures/__OSInitNet.yml")), + ("__DVDCheckDevice", include_str!("../../assets/signatures/__DVDCheckDevice.yml")), + ("__OSInitPlayTime", include_str!("../../assets/signatures/__OSInitPlayTime.yml")), + ("__OSStartPlayRecord", include_str!("../../assets/signatures/__OSStartPlayRecord.yml")), + ("NANDInit", include_str!("../../assets/signatures/NANDInit.yml")), + ("ISFS_OpenLib", include_str!("../../assets/signatures/ISFS_OpenLib.yml")), + ("ESP_GetTitleId", include_str!("../../assets/signatures/ESP_GetTitleId.yml")), + ( + "NANDSetAutoErrorMessaging", + include_str!("../../assets/signatures/NANDSetAutoErrorMessaging.yml"), + ), + ("__DVDFSInit", include_str!("../../assets/signatures/__DVDFSInit.yml")), + ("__DVDClearWaitingQueue", include_str!("../../assets/signatures/__DVDClearWaitingQueue.yml")), + ("__DVDInitWA", include_str!("../../assets/signatures/__DVDInitWA.yml")), + ("__DVDLowSetWAType", include_str!("../../assets/signatures/__DVDLowSetWAType.yml")), + ("__fstLoad", include_str!("../../assets/signatures/__fstLoad.yml")), + ("DVDReset", include_str!("../../assets/signatures/DVDReset.yml")), + ("DVDLowReset", include_str!("../../assets/signatures/DVDLowReset.yml")), + ("DVDReadDiskID", include_str!("../../assets/signatures/DVDReadDiskID.yml")), + ("stateReady", include_str!("../../assets/signatures/stateReady.yml")), + ("DVDLowWaitCoverClose", include_str!("../../assets/signatures/DVDLowWaitCoverClose.yml")), + ("__DVDStoreErrorCode", include_str!("../../assets/signatures/__DVDStoreErrorCode.yml")), + ("DVDLowStopMotor", include_str!("../../assets/signatures/DVDLowStopMotor.yml")), + ("DVDGetDriveStatus", include_str!("../../assets/signatures/DVDGetDriveStatus.yml")), + ("printf", include_str!("../../assets/signatures/printf.yml")), + ("sprintf", include_str!("../../assets/signatures/sprintf.yml")), + ("vprintf", include_str!("../../assets/signatures/vprintf.yml")), + ("vsprintf", include_str!("../../assets/signatures/vsprintf.yml")), + ("vsnprintf", include_str!("../../assets/signatures/vsnprintf.yml")), + ("__pformatter", include_str!("../../assets/signatures/__pformatter.yml")), + ("longlong2str", include_str!("../../assets/signatures/longlong2str.yml")), + ("__mod2u", include_str!("../../assets/signatures/__mod2u.yml")), + ("__FileWrite", include_str!("../../assets/signatures/__FileWrite.yml")), + ("fwrite", include_str!("../../assets/signatures/fwrite.yml")), + ("__fwrite", include_str!("../../assets/signatures/__fwrite.yml")), + ("__stdio_atexit", include_str!("../../assets/signatures/__stdio_atexit.yml")), + ("__StringWrite", include_str!("../../assets/signatures/__StringWrite.yml")), +]; +const POST_SIGNATURES: &[(&str, &str)] = &[ + ("RSOStaticLocateObject", include_str!("../../assets/signatures/RSOStaticLocateObject.yml")), + // ("GXInit", include_str!("../../assets/signatures/GXInit.yml")), ]; pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> { let entry = obj.entry as u32; - check_signatures(obj, entry, include_str!("../../assets/__start.yml"))?; + if let Some(signature) = + check_signatures_str(obj, entry, include_str!("../../assets/signatures/__start.yml"))? + { + apply_signature(obj, entry, &signature)?; + } for &(name, sig_str) in SIGNATURES { if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == name) { let addr = symbol.address as u32; - check_signatures(obj, addr, sig_str)?; + if let Some(signature) = check_signatures_str(obj, addr, sig_str)? { + apply_signature(obj, addr, &signature)?; + } } } if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == "__init_user") { @@ -229,7 +291,12 @@ pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> { let mut analyzer = AnalyzerState::default(); analyzer.process_function_at(&obj, symbol.address as u32)?; for addr in analyzer.function_entries { - if check_signatures(obj, addr, include_str!("../../assets/__init_cpp.yml"))? { + if let Some(signature) = check_signatures_str( + obj, + addr, + include_str!("../../assets/signatures/__init_cpp.yml"), + )? { + apply_signature(obj, addr, &signature)?; break; } } @@ -240,7 +307,13 @@ pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> { let target = read_u32(§ion.data, symbol.address as u32, section.address as u32) .ok_or_else(|| anyhow!("Failed to read _ctors data"))?; if target != 0 { - check_signatures(obj, target, include_str!("../../assets/__init_cpp_exceptions.yml"))?; + if let Some(signature) = check_signatures_str( + obj, + target, + include_str!("../../assets/signatures/__init_cpp_exceptions.yml"), + )? { + apply_signature(obj, target, &signature)?; + } } } if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == "_dtors") { @@ -249,12 +322,34 @@ pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> { let target = read_u32(§ion.data, symbol.address as u32 + 4, section.address as u32) .ok_or_else(|| anyhow!("Failed to read _dtors data"))?; if target != 0 { - check_signatures(obj, target, include_str!("../../assets/__fini_cpp_exceptions.yml"))?; + if let Some(signature) = check_signatures_str( + obj, + target, + include_str!("../../assets/signatures/__fini_cpp_exceptions.yml"), + )? { + apply_signature(obj, target, &signature)?; + } } } Ok(()) } +pub fn apply_signatures_post(obj: &mut ObjInfo) -> Result<()> { + log::info!("Checking post CFA signatures..."); + for &(_name, sig_str) in POST_SIGNATURES { + let signatures = parse_signatures(sig_str)?; + for symbol in obj.symbols.iter().filter(|symbol| symbol.kind == ObjSymbolKind::Function) { + let addr = symbol.address as u32; + if let Some(signature) = check_signatures(obj, addr, &signatures)? { + apply_signature(obj, addr, &signature)?; + break; + } + } + } + log::info!("Done!"); + Ok(()) +} + fn info(args: InfoArgs) -> Result<()> { let mut obj = process_dol(&args.dol_file)?; apply_signatures(&mut obj)?; @@ -288,6 +383,8 @@ fn info(args: InfoArgs) -> Result<()> { FindSaveRestSleds::execute(&mut state, &obj)?; state.apply(&mut obj)?; + apply_signatures_post(&mut obj)?; + println!("{}:", obj.name); println!("Entry point: {:#010X}", obj.entry); println!("\nSections:"); @@ -329,20 +426,20 @@ fn disasm(args: DisasmArgs) -> Result<()> { // } if let Some(map) = &args.map_file { - let mut reader = BufReader::new( - File::open(map) - .with_context(|| format!("Failed to open map file '{}'", map.display()))?, - ); - let _entries = process_map(&mut reader)?; + let mmap = map_file(map)?; + let _entries = process_map(map_reader(&mmap))?; + } + + if let Some(splits_file) = &args.splits_file { + let map = map_file(splits_file)?; + apply_splits(map_reader(&map), &mut obj)?; } let mut state = AnalyzerState::default(); if let Some(symbols_path) = &args.symbols_file { - let mut reader = BufReader::new(File::open(symbols_path).with_context(|| { - format!("Failed to open symbols file '{}'", symbols_path.display()) - })?); - for result in reader.lines() { + let map = map_file(symbols_path)?; + for result in map_reader(&map).lines() { let line = match result { Ok(line) => line, Err(e) => bail!("Failed to process symbols file: {e:?}"), @@ -407,10 +504,66 @@ fn disasm(args: DisasmArgs) -> Result<()> { log::info!("Applying relocations"); tracker.apply(&mut obj, false)?; - // - // log::info!("Writing disassembly"); - // let mut w = BufWriter::new(File::create("out.s")?); - // write_asm(&mut w, &obj)?; + + if args.splits_file.is_some() { + + log::info!("Splitting {} objects", obj.link_order.len()); + let split_objs = split_obj(&obj)?; + + // Create out dirs + let asm_dir = args.out.join("asm"); + let include_dir = args.out.join("include"); + let obj_dir = args.out.join("expected"); + DirBuilder::new().recursive(true).create(&include_dir)?; + fs::write(include_dir.join("macros.inc"), include_bytes!("../../assets/macros.inc"))?; + + log::info!("Writing object files"); + let mut file_map = HashMap::>::new(); + for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) { + let out_obj = write_elf(split_obj)?; + match file_map.entry(unit.clone()) { + hash_map::Entry::Vacant(e) => e.insert(out_obj), + hash_map::Entry::Occupied(_) => bail!("Duplicate file {unit}"), + }; + } + + let mut rsp_file = BufWriter::new(File::create("rsp")?); + for unit in &obj.link_order { + let object = file_map + .get(unit) + .ok_or_else(|| anyhow!("Failed to find object file for unit '{unit}'"))?; + let out_path = obj_dir.join(unit); + writeln!(rsp_file, "{}", out_path.display())?; + if let Some(parent) = out_path.parent() { + DirBuilder::new().recursive(true).create(parent)?; + } + let mut file = File::create(&out_path) + .with_context(|| format!("Failed to create '{}'", out_path.display()))?; + file.write_all(object)?; + file.flush()?; + } + rsp_file.flush()?; + + log::info!("Writing disassembly"); + let mut files_out = File::create(args.out.join("link_order.txt"))?; + for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) { + let out_path = asm_dir.join(format!("{}.s", unit.trim_end_matches(".o"))); + + if let Some(parent) = out_path.parent() { + DirBuilder::new().recursive(true).create(parent)?; + } + let mut w = BufWriter::new(File::create(out_path)?); + write_asm(&mut w, split_obj)?; + w.flush()?; + + writeln!(files_out, "{}", unit)?; + } + files_out.flush()?; + } else { + log::info!("Writing disassembly"); + let mut w = BufWriter::new(File::create("out.s")?); + write_asm(&mut w, &obj)?; + } if let Some(symbols_path) = &args.symbols_file { let mut symbols_writer = BufWriter::new( @@ -500,6 +653,7 @@ fn validate>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) - ); } } + return Ok(()); // TODO for real_section in &real_obj.sections { let obj_section = match obj.sections.get(real_section.index) { Some(v) => v, @@ -562,20 +716,17 @@ fn validate>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) - } for (&obj_addr, obj_reloc) in &obj_map { let obj_symbol = &obj.symbols[obj_reloc.target_symbol]; - let real_reloc = match real_map.get(&obj_addr) { - Some(v) => v, - None => { - log::warn!( - "Relocation not real @ {:#010X} {:?} to {:#010X}+{:X} ({})", - obj_addr, - obj_reloc.kind, - obj_symbol.address, - obj_reloc.addend, - obj_symbol.demangled_name.as_ref().unwrap_or(&obj_symbol.name) - ); - continue; - } - }; + if !real_map.contains_key(&obj_addr) { + log::warn!( + "Relocation not real @ {:#010X} {:?} to {:#010X}+{:X} ({})", + obj_addr, + obj_reloc.kind, + obj_symbol.address, + obj_reloc.addend, + obj_symbol.demangled_name.as_ref().unwrap_or(&obj_symbol.name) + ); + continue; + } } } Ok(()) diff --git a/src/cmd/elf.rs b/src/cmd/elf.rs index e062299..7906079 100644 --- a/src/cmd/elf.rs +++ b/src/cmd/elf.rs @@ -1,9 +1,9 @@ use std::{ - collections::{btree_map, btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}, + collections::{btree_map, hash_map, BTreeMap, HashMap}, fs, fs::{DirBuilder, File}, io::{BufRead, BufReader, BufWriter, Write}, - path::{Path, PathBuf}, + path::PathBuf, }; use anyhow::{anyhow, bail, ensure, Context, Result}; @@ -13,18 +13,19 @@ use object::{ Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionFlags, SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection, }; -use ppc750cl::Ins; -use serde::{Deserialize, Serialize}; -use sha1::{Digest, Sha1}; -use crate::util::{ - asm::write_asm, - config::write_symbols, - elf::{process_elf, write_elf}, - obj::{ObjKind, ObjReloc, ObjRelocKind, ObjSymbolFlagSet, ObjSymbolKind}, - sigs::{check_signature, compare_signature, generate_signature, FunctionSignature}, - split::split_obj, - tracker::Tracker, +use crate::{ + obj::{ + signatures::{compare_signature, generate_signature, FunctionSignature}, + split::split_obj, + ObjKind, + }, + util::{ + asm::write_asm, + config::{write_splits, write_symbols}, + elf::{process_elf, write_elf}, + file::buf_reader, + }, }; #[derive(FromArgs, PartialEq, Debug)] @@ -91,6 +92,9 @@ pub struct ConfigArgs { #[argh(positional)] /// output directory out_dir: PathBuf, + #[argh(option, short = 'm')] + /// path to obj_files.mk + obj_files: Option, } #[derive(FromArgs, PartialEq, Eq, Debug)] @@ -120,15 +124,43 @@ pub fn run(args: Args) -> Result<()> { fn config(args: ConfigArgs) -> Result<()> { log::info!("Loading {}", args.in_file.display()); - let mut obj = process_elf(&args.in_file)?; + let obj = process_elf(&args.in_file)?; DirBuilder::new().recursive(true).create(&args.out_dir)?; - let symbols_path = args.out_dir.join("symbols.txt"); - let mut symbols_writer = BufWriter::new( - File::create(&symbols_path) - .with_context(|| format!("Failed to create '{}'", symbols_path.display()))?, - ); - write_symbols(&mut symbols_writer, &obj)?; + { + let symbols_path = args.out_dir.join("symbols.txt"); + let mut symbols_writer = BufWriter::new( + File::create(&symbols_path) + .with_context(|| format!("Failed to create '{}'", symbols_path.display()))?, + ); + write_symbols(&mut symbols_writer, &obj)?; + } + + { + let obj_files = if let Some(path) = &args.obj_files { + Some( + BufReader::new( + File::open(path) + .with_context(|| format!("Failed to open '{}'", path.display()))?, + ) + .lines() + .filter(|line| match line { + Ok(line) => line.contains(".o"), + Err(_) => false, + }) + .map(|result| result.unwrap()) + .collect::>(), + ) + } else { + None + }; + let splits_path = args.out_dir.join("splits.txt"); + let mut splits_writer = BufWriter::new( + File::create(&splits_path) + .with_context(|| format!("Failed to create '{}'", splits_path.display()))?, + ); + write_splits(&mut splits_writer, &obj, obj_files)?; + } Ok(()) } @@ -449,10 +481,7 @@ fn signatures(args: SignaturesArgs) -> Result<()> { path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?; match path_str.strip_prefix('@') { Some(rsp_file) => { - let reader = BufReader::new( - File::open(rsp_file) - .with_context(|| format!("Failed to open file '{rsp_file}'"))?, - ); + let reader = buf_reader(rsp_file)?; for result in reader.lines() { let line = result?; if !line.is_empty() { @@ -466,10 +495,10 @@ fn signatures(args: SignaturesArgs) -> Result<()> { } } - let mut signatures: HashMap, FunctionSignature> = HashMap::new(); + let mut signatures: HashMap = HashMap::new(); for path in files { log::info!("Processing {}", path.display()); - let (data, signature) = match generate_signature(&path, &args.symbol) { + let signature = match generate_signature(&path, &args.symbol) { Ok(Some(signature)) => signature, Ok(None) => continue, Err(e) => { @@ -478,13 +507,13 @@ fn signatures(args: SignaturesArgs) -> Result<()> { } }; log::info!("Comparing hash {}", signature.hash); - if let Some((_, existing)) = signatures.iter_mut().find(|(a, b)| *a == &data) { + if let Some(existing) = signatures.get_mut(&signature.hash) { compare_signature(existing, &signature)?; } else { - signatures.insert(data, signature); + signatures.insert(signature.hash.clone(), signature); } } - let mut signatures = signatures.into_iter().map(|(a, b)| b).collect::>(); + let mut signatures = signatures.into_values().collect::>(); log::info!("{} unique signatures", signatures.len()); signatures.sort_by_key(|s| s.signature.len()); let out = diff --git a/src/cmd/elf2dol.rs b/src/cmd/elf2dol.rs index d457989..37a276a 100644 --- a/src/cmd/elf2dol.rs +++ b/src/cmd/elf2dol.rs @@ -1,23 +1,25 @@ use std::{ fs::File, io::{BufWriter, Seek, SeekFrom, Write}, + path::PathBuf, }; use anyhow::{anyhow, bail, ensure, Context, Result}; use argh::FromArgs; -use memmap2::MmapOptions; use object::{Architecture, Endianness, Object, ObjectKind, ObjectSection, SectionKind}; +use crate::util::file::map_file; + #[derive(FromArgs, PartialEq, Eq, Debug)] /// Converts an ELF file to a DOL file. #[argh(subcommand, name = "elf2dol")] pub struct Args { #[argh(positional)] /// path to input ELF - elf_file: String, + elf_file: PathBuf, #[argh(positional)] /// path to output DOL - dol_file: String, + dol_file: PathBuf, } #[derive(Debug, Clone, Default)] @@ -42,10 +44,7 @@ const MAX_TEXT_SECTIONS: usize = 7; const MAX_DATA_SECTIONS: usize = 11; pub fn run(args: Args) -> Result<()> { - let elf_file = File::open(&args.elf_file) - .with_context(|| format!("Failed to open ELF file '{}'", args.elf_file))?; - let map = unsafe { MmapOptions::new().map(&elf_file) } - .with_context(|| format!("Failed to mmap ELF file: '{}'", args.elf_file))?; + let map = map_file(&args.elf_file)?; let obj_file = object::read::File::parse(&*map)?; match obj_file.architecture() { Architecture::PowerPc => {} @@ -61,7 +60,7 @@ pub fn run(args: Args) -> Result<()> { let mut offset = 0x100u32; let mut out = BufWriter::new( File::create(&args.dol_file) - .with_context(|| format!("Failed to create DOL file '{}'", args.dol_file))?, + .with_context(|| format!("Failed to create DOL file '{}'", args.dol_file.display()))?, ); out.seek(SeekFrom::Start(offset as u64))?; diff --git a/src/cmd/map.rs b/src/cmd/map.rs index a410885..9f2b5d6 100644 --- a/src/cmd/map.rs +++ b/src/cmd/map.rs @@ -1,9 +1,12 @@ -use std::{fs::File, io::BufReader}; +use std::path::PathBuf; -use anyhow::{bail, ensure, Context, Result}; +use anyhow::{bail, ensure, Result}; use argh::FromArgs; -use crate::util::map::{process_map, resolve_link_order, SymbolEntry, SymbolRef}; +use crate::util::{ + file::{map_file, map_reader}, + map::{process_map, resolve_link_order, SymbolEntry, SymbolRef}, +}; #[derive(FromArgs, PartialEq, Debug)] /// Commands for processing CodeWarrior maps. @@ -29,7 +32,7 @@ enum SubCommand { pub struct EntriesArgs { #[argh(positional)] /// path to input map - map_file: String, + map_file: PathBuf, #[argh(positional)] /// TU to display entries for unit: String, @@ -41,7 +44,7 @@ pub struct EntriesArgs { pub struct SymbolArgs { #[argh(positional)] /// path to input map - map_file: String, + map_file: PathBuf, #[argh(positional)] /// symbol to display references for symbol: String, @@ -53,7 +56,7 @@ pub struct SymbolArgs { pub struct OrderArgs { #[argh(positional)] /// path to input map - map_file: String, + map_file: PathBuf, } #[derive(FromArgs, PartialEq, Eq, Debug)] @@ -62,7 +65,7 @@ pub struct OrderArgs { pub struct SlicesArgs { #[argh(positional)] /// path to input map - map_file: String, + map_file: PathBuf, } #[derive(FromArgs, PartialEq, Eq, Debug)] @@ -71,7 +74,7 @@ pub struct SlicesArgs { pub struct SymbolsArgs { #[argh(positional)] /// path to input map - map_file: String, + map_file: PathBuf, } pub fn run(args: Args) -> Result<()> { @@ -85,11 +88,8 @@ pub fn run(args: Args) -> Result<()> { } fn entries(args: EntriesArgs) -> Result<()> { - let reader = BufReader::new( - File::open(&args.map_file) - .with_context(|| format!("Failed to open file '{}'", args.map_file))?, - ); - let entries = process_map(reader)?; + let map = map_file(&args.map_file)?; + let entries = process_map(map_reader(&map))?; match entries.unit_entries.get_vec(&args.unit) { Some(vec) => { for symbol_ref in vec { @@ -109,11 +109,8 @@ fn entries(args: EntriesArgs) -> Result<()> { } fn symbol(args: SymbolArgs) -> Result<()> { - let reader = BufReader::new( - File::open(&args.map_file) - .with_context(|| format!("Failed to open file '{}'", args.map_file))?, - ); - let entries = process_map(reader)?; + let map = map_file(&args.map_file)?; + let entries = process_map(map_reader(&map))?; let mut opt_ref: Option<(SymbolRef, SymbolEntry)> = None; for (symbol_ref, entry) in &entries.symbols { if symbol_ref.name == args.symbol { @@ -164,11 +161,8 @@ fn symbol(args: SymbolArgs) -> Result<()> { } fn order(args: OrderArgs) -> Result<()> { - let reader = BufReader::new( - File::open(&args.map_file) - .with_context(|| format!("Failed to open file '{}'", args.map_file))?, - ); - let entries = process_map(reader)?; + let map = map_file(&args.map_file)?; + let entries = process_map(map_reader(&map))?; let order = resolve_link_order(&entries.unit_order)?; for unit in order { println!("{unit}"); @@ -177,11 +171,8 @@ fn order(args: OrderArgs) -> Result<()> { } fn slices(args: SlicesArgs) -> Result<()> { - let reader = BufReader::new( - File::open(&args.map_file) - .with_context(|| format!("Failed to open file '{}'", args.map_file))?, - ); - let entries = process_map(reader)?; + let map = map_file(&args.map_file)?; + let entries = process_map(map_reader(&map))?; let order = resolve_link_order(&entries.unit_order)?; for unit in order { let unit_path = if let Some((lib, name)) = unit.split_once(' ') { @@ -210,11 +201,8 @@ fn slices(args: SlicesArgs) -> Result<()> { } fn symbols(args: SymbolsArgs) -> Result<()> { - let reader = BufReader::new( - File::open(&args.map_file) - .with_context(|| format!("Failed to open file '{}'", args.map_file))?, - ); - let _entries = process_map(reader)?; + let map = map_file(&args.map_file)?; + let _entries = process_map(map_reader(&map))?; // for (address, symbol) in entries.address_to_symbol { // if symbol.name.starts_with('@') { // continue; diff --git a/src/cmd/metroidbuildinfo.rs b/src/cmd/metroidbuildinfo.rs index f10ec72..2cadfd3 100644 --- a/src/cmd/metroidbuildinfo.rs +++ b/src/cmd/metroidbuildinfo.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use anyhow::{bail, ensure, Context, Result}; use argh::FromArgs; use memchr::memmem; @@ -9,18 +11,19 @@ use memmap2::MmapOptions; pub struct Args { #[argh(positional)] /// path to source binary - binary: String, + binary: PathBuf, #[argh(positional)] /// path to build info string - build_info: String, + build_info: PathBuf, } const BUILD_STRING_MAX: usize = 35; const BUILD_STRING_TAG: &str = "!#$MetroidBuildInfo!#$"; pub fn run(args: Args) -> Result<()> { - let build_string = std::fs::read_to_string(&args.build_info) - .with_context(|| format!("Failed to read build info string from '{}'", args.build_info))?; + let build_string = std::fs::read_to_string(&args.build_info).with_context(|| { + format!("Failed to read build info string from '{}'", args.build_info.display()) + })?; let build_string_trim = build_string.trim_end(); let build_string_bytes = build_string_trim.as_bytes(); ensure!( @@ -28,13 +31,12 @@ pub fn run(args: Args) -> Result<()> { "Build string '{build_string_trim}' is greater than maximum size of {BUILD_STRING_MAX}" ); - let binary_file = std::fs::File::options() - .read(true) - .write(true) - .open(&args.binary) - .with_context(|| format!("Failed to open binary for writing: '{}'", args.binary))?; + let binary_file = + std::fs::File::options().read(true).write(true).open(&args.binary).with_context(|| { + format!("Failed to open binary for writing: '{}'", args.binary.display()) + })?; let mut map = unsafe { MmapOptions::new().map_mut(&binary_file) } - .with_context(|| format!("Failed to mmap binary: '{}'", args.binary))?; + .with_context(|| format!("Failed to mmap binary: '{}'", args.binary.display()))?; let start = match memmem::find(&map, BUILD_STRING_TAG.as_bytes()) { Some(idx) => idx + BUILD_STRING_TAG.as_bytes().len(), None => bail!("Failed to find build string tag in binary"), diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 05809b7..cd0b7cf 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,9 +1,10 @@ -pub(crate) mod ar; -pub(crate) mod demangle; -pub(crate) mod dol; -pub(crate) mod elf; -pub(crate) mod elf2dol; -pub(crate) mod map; -pub(crate) mod metroidbuildinfo; -pub(crate) mod rel; -pub(crate) mod shasum; +pub mod ar; +pub mod demangle; +pub mod dol; +pub mod elf; +pub mod elf2dol; +pub mod map; +pub mod metroidbuildinfo; +pub mod rel; +pub mod rso; +pub mod shasum; diff --git a/src/cmd/rel.rs b/src/cmd/rel.rs index 6758c37..d15ab2b 100644 --- a/src/cmd/rel.rs +++ b/src/cmd/rel.rs @@ -1,25 +1,28 @@ use std::{ collections::{btree_map, BTreeMap}, fs::File, - io::{BufWriter, Write}, + io::Write, path::PathBuf, }; -use anyhow::{anyhow, bail, ensure, Context, Result}; +use anyhow::{bail, ensure, Context, Result}; use argh::FromArgs; use crate::{ + analysis::{ + cfa::AnalyzerState, + pass::{AnalysisPass, FindSaveRestSleds, FindTRKInterruptVectorTable}, + tracker::Tracker, + }, cmd::dol::apply_signatures, + obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolKind}, util::{ dol::process_dol, elf::write_elf, - obj::{ObjInfo, ObjSection, ObjSymbol}, + nested::{NestedMap, NestedVec}, rel::process_rel, }, }; -use crate::util::cfa::{AnalysisPass, AnalyzerState, FindSaveRestSleds, FindTRKInterruptVectorTable}; -use crate::util::obj::{nested_push, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbolKind}; -use crate::util::tracker::Tracker; #[derive(FromArgs, PartialEq, Debug)] /// Commands for processing REL files. @@ -111,12 +114,7 @@ fn merge(args: MergeArgs) -> Result<()> { file_offset: mod_section.file_offset, section_known: mod_section.section_known, }); - nested_try_insert( - &mut section_map, - module.module_id, - mod_section.elf_index as u32, - offset, - )?; + section_map.nested_insert(module.module_id, mod_section.elf_index as u32, offset)?; let symbols = module.symbols_for_section(mod_section.index); for (_, mod_symbol) in symbols { obj.symbols.push(ObjSymbol { @@ -159,7 +157,7 @@ fn merge(args: MergeArgs) -> Result<()> { let sym_map = &mut symbol_maps[target_section_index]; let target_symbol = { let mut result = None; - for (&addr, symbol_idxs) in sym_map.range(..=target_addr).rev() { + for (_addr, symbol_idxs) in sym_map.range(..=target_addr).rev() { let symbol_idx = if symbol_idxs.len() == 1 { symbol_idxs.first().cloned().unwrap() } else { @@ -183,10 +181,10 @@ fn merge(args: MergeArgs) -> Result<()> { ObjRelocKind::PpcAddr16Hi | ObjRelocKind::PpcAddr16Ha | ObjRelocKind::PpcAddr16Lo - if !symbol.name.starts_with("..") => - { - 3 - } + if !symbol.name.starts_with("..") => + { + 3 + } _ => 1, }, ObjSymbolKind::Section => -1, @@ -231,7 +229,7 @@ fn merge(args: MergeArgs) -> Result<()> { flags: Default::default(), kind: Default::default(), }); - nested_push(sym_map, target_addr, symbol_idx); + sym_map.nested_push(target_addr, symbol_idx); (symbol_idx, 0) }; obj.sections[target_section_index].relocations.push(ObjReloc { @@ -288,25 +286,3 @@ fn merge(args: MergeArgs) -> Result<()> { file.flush()?; Ok(()) } - -#[inline] -fn nested_try_insert( - map: &mut BTreeMap>, - v1: T1, - v2: T2, - v3: T3, -) -> Result<()> -where - T1: Eq + Ord, - T2: Eq + Ord, -{ - let map = match map.entry(v1) { - btree_map::Entry::Occupied(entry) => entry.into_mut(), - btree_map::Entry::Vacant(entry) => entry.insert(Default::default()), - }; - match map.entry(v2) { - btree_map::Entry::Occupied(_) => bail!("Entry already exists"), - btree_map::Entry::Vacant(entry) => entry.insert(v3), - }; - Ok(()) -} diff --git a/src/cmd/rso.rs b/src/cmd/rso.rs new file mode 100644 index 0000000..2ee00e9 --- /dev/null +++ b/src/cmd/rso.rs @@ -0,0 +1,41 @@ +use std::path::PathBuf; + +use anyhow::Result; +use argh::FromArgs; + +use crate::util::rso::process_rso; + +#[derive(FromArgs, PartialEq, Debug)] +/// Commands for processing RSO files. +#[argh(subcommand, name = "rso")] +pub struct Args { + #[argh(subcommand)] + command: SubCommand, +} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand)] +enum SubCommand { + Info(InfoArgs), +} + +#[derive(FromArgs, PartialEq, Eq, Debug)] +/// Views RSO file information. +#[argh(subcommand, name = "info")] +pub struct InfoArgs { + #[argh(positional)] + /// RSO file + rso_file: PathBuf, +} + +pub fn run(args: Args) -> Result<()> { + match args.command { + SubCommand::Info(c_args) => info(c_args), + } +} + +fn info(args: InfoArgs) -> Result<()> { + let rso = process_rso(&args.rso_file)?; + println!("Read RSO module {}", rso.name); + Ok(()) +} diff --git a/src/cmd/shasum.rs b/src/cmd/shasum.rs index 40f3423..f367d8a 100644 --- a/src/cmd/shasum.rs +++ b/src/cmd/shasum.rs @@ -1,7 +1,7 @@ use std::{ fs::{File, OpenOptions}, io::{BufRead, BufReader, Read}, - path::Path, + path::{Path, PathBuf}, }; use anyhow::{anyhow, bail, Context, Result}; @@ -18,17 +18,17 @@ pub struct Args { check: bool, #[argh(positional)] /// path to file - file: String, + file: PathBuf, #[argh(option, short = 'o')] /// touch output file on successful check - output: Option, + output: Option, } const DEFAULT_BUF_SIZE: usize = 8192; pub fn run(args: Args) -> Result<()> { - let file = - File::open(&args.file).with_context(|| format!("Failed to open file '{}'", args.file))?; + let file = File::open(&args.file) + .with_context(|| format!("Failed to open file '{}'", args.file.display()))?; if args.check { check(args, file) } else { @@ -69,7 +69,8 @@ fn check(args: Args, file: File) -> Result<()> { std::process::exit(1); } if let Some(out_path) = args.output { - touch(&out_path).with_context(|| format!("Failed to touch output file '{out_path}'"))?; + touch(&out_path) + .with_context(|| format!("Failed to touch output file '{}'", out_path.display()))?; } Ok(()) } @@ -79,7 +80,7 @@ fn hash(args: Args, file: File) -> Result<()> { let mut hash_buf = [0u8; 40]; let hash_str = base16ct::lower::encode_str(&hash, &mut hash_buf) .map_err(|e| anyhow!("Failed to encode hash: {e}"))?; - println!("{} {}", hash_str, args.file); + println!("{} {}", hash_str, args.file.display()); Ok(()) } diff --git a/src/main.rs b/src/main.rs index a00d4bb..df30f8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ use argh::FromArgs; -mod argh_version; -mod cmd; -mod util; +pub mod analysis; +pub mod argh_version; +pub mod cmd; +pub mod obj; +pub mod util; #[derive(FromArgs, PartialEq, Debug)] /// GameCube/Wii decompilation project tools. @@ -22,6 +24,7 @@ enum SubCommand { Map(cmd::map::Args), MetroidBuildInfo(cmd::metroidbuildinfo::Args), Rel(cmd::rel::Args), + Rso(cmd::rso::Args), Shasum(cmd::shasum::Args), } @@ -38,6 +41,7 @@ fn main() { SubCommand::Map(c_args) => cmd::map::run(c_args), SubCommand::MetroidBuildInfo(c_args) => cmd::metroidbuildinfo::run(c_args), SubCommand::Rel(c_args) => cmd::rel::run(c_args), + SubCommand::Rso(c_args) => cmd::rso::run(c_args), SubCommand::Shasum(c_args) => cmd::shasum::run(c_args), }; if let Err(e) = result { diff --git a/src/util/obj.rs b/src/obj/mod.rs similarity index 87% rename from src/util/obj.rs rename to src/obj/mod.rs index 54f5f80..5529f87 100644 --- a/src/util/obj.rs +++ b/src/obj/mod.rs @@ -1,18 +1,18 @@ +pub mod signatures; +pub mod split; + use std::{ cmp::min, collections::{btree_map, BTreeMap}, - fmt, hash::{Hash, Hasher}, }; -use std::marker::PhantomData; use anyhow::{anyhow, bail, Result}; use flagset::{flags, FlagSet}; -use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; -use serde_yaml::Value; -use serde_repr::{Serialize_repr, Deserialize_repr}; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; -use crate::util::rel::RelReloc; +use crate::util::{nested::NestedVec, rel::RelReloc}; flags! { #[repr(u8)] @@ -26,7 +26,7 @@ flags! { } } #[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct ObjSymbolFlagSet(pub(crate) FlagSet); +pub struct ObjSymbolFlagSet(pub FlagSet); #[allow(clippy::derive_hash_xor_eq)] impl Hash for ObjSymbolFlagSet { fn hash(&self, state: &mut H) { self.0.bits().hash(state) } @@ -103,7 +103,8 @@ pub struct ObjInfo { pub arena_hi: Option, // Extracted - pub splits: BTreeMap, + pub splits: BTreeMap>, + pub named_sections: BTreeMap, pub link_order: Vec, // From extab @@ -146,7 +147,7 @@ impl ObjInfo { pub fn build_symbol_map(&self, section_idx: usize) -> Result>> { let mut symbols = BTreeMap::>::new(); for (symbol_idx, symbol) in self.symbols_for_section(section_idx) { - nested_push(&mut symbols, symbol.address as u32, symbol_idx); + symbols.nested_push(symbol.address as u32, symbol_idx); } Ok(symbols) } @@ -187,16 +188,3 @@ impl ObjSection { Ok(relocations) } } - -#[inline] -pub fn nested_push(map: &mut BTreeMap>, v1: T1, v2: T2) -where T1: Ord { - match map.entry(v1) { - btree_map::Entry::Occupied(mut e) => { - e.get_mut().push(v2); - } - btree_map::Entry::Vacant(e) => { - e.insert(vec![v2]); - } - } -} diff --git a/src/util/sigs.rs b/src/obj/signatures.rs similarity index 84% rename from src/util/sigs.rs rename to src/obj/signatures.rs index c6041b4..923a264 100644 --- a/src/util/sigs.rs +++ b/src/obj/signatures.rs @@ -6,17 +6,16 @@ use std::{ use anyhow::{anyhow, bail, ensure, Result}; use base64::{engine::general_purpose::STANDARD, Engine}; use cwdemangle::{demangle, DemangleOptions}; -use ppc750cl::Ins; -use serde::{forward_to_deserialize_any, Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; -use crate::util::{ - elf::process_elf, +use crate::{ + analysis::tracker::{Relocation, Tracker}, + array_ref, obj::{ - ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, - ObjSymbolFlags, ObjSymbolKind, + ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolKind, }, - tracker::{Relocation, Tracker}, + util::elf::process_elf, }; #[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] @@ -45,18 +44,6 @@ pub struct FunctionSignature { pub relocations: Vec, } -/// Creates a fixed-size array reference from a slice. -#[macro_export] -macro_rules! array_ref { - ($slice:expr, $offset:expr, $size:expr) => {{ - #[inline] - fn to_array(slice: &[T]) -> &[T; $size] { - unsafe { &*(slice.as_ptr() as *const [_; $size]) } - } - to_array(&$slice[$offset..$offset + $size]) - }}; -} - pub fn check_signature(mut data: &[u8], sig: &FunctionSignature) -> Result { let sig_data = STANDARD.decode(&sig.signature)?; // println!( @@ -79,28 +66,47 @@ pub fn check_signature(mut data: &[u8], sig: &FunctionSignature) -> Result Ok(true) } -pub fn check_signatures(obj: &mut ObjInfo, addr: u32, sig_str: &str) -> Result { - let signatures: Vec = serde_yaml::from_str(sig_str)?; +pub fn parse_signatures(sig_str: &str) -> Result> { + Ok(serde_yaml::from_str(sig_str)?) +} + +pub fn check_signatures_str( + obj: &ObjInfo, + addr: u32, + sig_str: &str, +) -> Result> { + check_signatures(obj, addr, &parse_signatures(sig_str)?) +} + +pub fn check_signatures( + obj: &ObjInfo, + addr: u32, + signatures: &Vec, +) -> Result> { let (_, data) = obj.section_data(addr, 0)?; let mut name = None; - for signature in &signatures { + for signature in signatures { if name.is_none() { name = Some(signature.symbols[signature.symbol].name.clone()); } if check_signature(data, signature)? { - log::debug!("Found {} @ {:#010X}", signature.symbols[signature.symbol].name, addr); - apply_signature(obj, addr, signature)?; - return Ok(true); + log::debug!( + "Found {} @ {:#010X} (hash {})", + signature.symbols[signature.symbol].name, + addr, + signature.hash + ); + return Ok(Some(signature.clone())); } } - if let Some(name) = name { - log::debug!("Didn't find {} @ {:#010X}", name, addr); - } - Ok(false) + // if let Some(name) = name { + // log::debug!("Didn't find {} @ {:#010X}", name, addr); + // } + Ok(None) } pub fn apply_symbol(obj: &mut ObjInfo, target: u32, sig_symbol: &OutSymbol) -> Result { - let target_section_index = obj.section_at(target).ok().map(|section| section.index); + let mut target_section_index = obj.section_at(target).ok().map(|section| section.index); if let Some(target_section_index) = target_section_index { let target_section = &mut obj.sections[target_section_index]; if !target_section.section_known { @@ -119,21 +125,26 @@ pub fn apply_symbol(obj: &mut ObjInfo, target: u32, sig_symbol: &OutSymbol) -> R } } } + if sig_symbol.kind == ObjSymbolKind::Unknown + && (sig_symbol.name.starts_with("_f_") || sig_symbol.name.starts_with("_SDA")) + { + // Hack to mark linker generated symbols as ABS + target_section_index = None; + } let target_symbol_idx = if let Some((symbol_idx, existing)) = obj.symbols.iter_mut().enumerate().find(|(_, symbol)| { symbol.address == target as u64 && symbol.kind == sig_symbol.kind - // HACK to avoid replacing different ABS symbols + // Hack to avoid replacing different ABS symbols && (symbol.section.is_some() || symbol.name == sig_symbol.name) }) { - // TODO apply to existing - log::debug!("Replacing {:?} with {}", existing, sig_symbol.name); + log::debug!("Replacing {:?} with {:?}", existing, sig_symbol); *existing = ObjSymbol { name: sig_symbol.name.clone(), demangled_name: demangle(&sig_symbol.name, &DemangleOptions::default()), address: target as u64, section: target_section_index, - size: if existing.size_known { existing.size } else { sig_symbol.size as u64 }, + size: if sig_symbol.size == 0 { existing.size } else { sig_symbol.size as u64 }, size_known: existing.size_known || sig_symbol.size != 0, flags: sig_symbol.flags, kind: sig_symbol.kind, @@ -256,10 +267,7 @@ pub fn compare_signature(existing: &mut FunctionSignature, new: &FunctionSignatu Ok(()) } -pub fn generate_signature( - path: &Path, - symbol_name: &str, -) -> Result, FunctionSignature)>> { +pub fn generate_signature(path: &Path, symbol_name: &str) -> Result> { let mut out_symbols: Vec = Vec::new(); let mut out_relocs: Vec = Vec::new(); let mut symbol_map: BTreeMap = BTreeMap::new(); @@ -270,10 +278,17 @@ pub fn generate_signature( || obj.stack_address.is_none() || obj.stack_end.is_none() || obj.db_stack_addr.is_none() - // || obj.arena_hi.is_none() - // || obj.arena_lo.is_none() { - log::warn!("Failed to locate all abs symbols {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?}", obj.sda2_base, obj.sda_base, obj.stack_address, obj.stack_end, obj.db_stack_addr, obj.arena_hi, obj.arena_lo); + log::warn!( + "Failed to locate all abs symbols {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?}", + obj.sda2_base, + obj.sda_base, + obj.stack_address, + obj.stack_end, + obj.db_stack_addr, + obj.arena_hi, + obj.arena_lo + ); return Ok(None); } let mut tracker = Tracker::new(&obj); @@ -285,7 +300,6 @@ pub fn generate_signature( if symbol.name != symbol_name && symbol.name != symbol_name.replace("TRK", "TRK_") { continue; } - // log::info!("Tracking {}", symbol.name); tracker.process_function(&obj, symbol)?; } tracker.apply(&mut obj, true)?; // true @@ -370,28 +384,16 @@ pub fn generate_signature( kind: reloc.kind, symbol: symbol_idx, addend: reloc.addend as i32, - // instruction: format!("{}", Ins::new(*ins, addr).simplified()), }); } - // println!("{}", Ins::new(*ins, addr).simplified()); } - // if out_symbols.is_empty() || out_relocs.is_empty() { - // bail!("Failed to locate any symbols or relocs"); - // } - // println!("Data: {:#010X?}", instructions); let mut data = vec![0u8; instructions.len() * 8]; for (idx, &(ins, pat)) in instructions.iter().enumerate() { data[idx * 8..idx * 8 + 4].copy_from_slice(&ins.to_be_bytes()); data[idx * 8 + 4..idx * 8 + 8].copy_from_slice(&pat.to_be_bytes()); } - // println!( - // "OK: Data (len {}): {:X?} | SYMBOLS: {:?} | RELOCS: {:?}", - // data.len(), - // data, - // out_symbols, - // out_relocs - // ); + let encoded = STANDARD.encode(&data); let mut hasher = Sha1::new(); hasher.update(&data); @@ -399,13 +401,13 @@ pub fn generate_signature( let mut hash_buf = [0u8; 40]; let hash_str = base16ct::lower::encode_str(&hash, &mut hash_buf) .map_err(|e| anyhow!("Failed to encode hash: {e}"))?; - return Ok(Some((data, FunctionSignature { + return Ok(Some(FunctionSignature { symbol: 0, hash: hash_str.to_string(), signature: encoded, symbols: out_symbols, relocations: out_relocs, - }))); + })); } Ok(None) } diff --git a/src/util/split.rs b/src/obj/split.rs similarity index 71% rename from src/util/split.rs rename to src/obj/split.rs index b5d96ef..4ee3cc4 100644 --- a/src/util/split.rs +++ b/src/obj/split.rs @@ -2,7 +2,7 @@ use std::{cmp::min, collections::HashMap}; use anyhow::{anyhow, bail, ensure, Result}; -use crate::util::obj::{ +use crate::obj::{ ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, }; @@ -32,6 +32,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { arena_lo: None, arena_hi: None, splits: Default::default(), + named_sections: Default::default(), link_order: vec![], known_functions: Default::default(), unresolved_relocations: vec![], @@ -41,6 +42,9 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { for (section_idx, section) in obj.sections.iter().enumerate() { let mut current_address = section.address as u32; let mut section_end = (section.address + section.size) as u32; + // if matches!(section.name.as_str(), "extab" | "extabindex") { + // continue; + // } // .ctors and .dtors end with a linker-generated null pointer, // adjust section size appropriately if matches!(section.name.as_str(), ".ctors" | ".dtors") @@ -48,7 +52,11 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { { section_end -= 4; } - let mut file_iter = obj.splits.range(current_address..).peekable(); + let mut file_iter = obj + .splits + .range(current_address..) + .flat_map(|(addr, v)| v.iter().map(move |u| (addr, u))) + .peekable(); // Build address to relocation / address to symbol maps let relocations = section.build_relocation_map()?; @@ -72,8 +80,20 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { file_addr ); let mut file_end = section_end; - if let Some(&(&next_addr, _)) = file_iter.peek() { - file_end = min(next_addr, section_end); + let mut dont_go_forward = false; + if let Some(&(&next_addr, next_unit)) = file_iter.peek() { + if file_addr == next_addr { + log::warn!("Duplicating {} in {unit} and {next_unit}", section.name); + dont_go_forward = true; + file_end = obj + .splits + .range(current_address + 1..) + .next() + .map(|(&addr, _)| addr) + .unwrap_or(section_end); + } else { + file_end = min(next_addr, section_end); + } } let file = name_to_obj @@ -147,8 +167,13 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { ..(file_end as u64 - section.address) as usize] .to_vec(), }; + let name = if let Some(name) = obj.named_sections.get(¤t_address) { + name.clone() + } else { + section.name.clone() + }; file.sections.push(ObjSection { - name: section.name.clone(), + name, kind: section.kind, address: 0, size: file_end as u64 - current_address as u64, @@ -162,7 +187,9 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { section_known: true, }); - current_address = file_end; + if !dont_go_forward { + current_address = file_end; + } } } @@ -186,6 +213,17 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { ..Default::default() }); reloc.target_symbol = out_sym_idx; + if matches!(section.name.as_str(), "extab" | "extabindex") { + log::warn!( + "Extern relocation @ {:#010X} {} ({:#010X} {}): {:#010X} {}", + reloc.address + section.original_address, + section.name, + section.original_address, + out_obj.name, + target_sym.address, + target_sym.demangled_name.as_deref().unwrap_or(&target_sym.name) + ); + } } } } @@ -237,10 +275,73 @@ fn default_section_align(section: &ObjSection) -> u64 { /// Linker-generated symbols to extern #[inline] -fn is_skip_symbol(name: &str) -> bool { matches!(name, "_ctors" | "_dtors") } +fn is_skip_symbol(name: &str) -> bool { + matches!( + name, + "_ctors" + | "_dtors" + | "_f_init" + | "_f_init_rom" + | "_e_init" + | "_fextab" + | "_fextab_rom" + | "_eextab" + | "_fextabindex" + | "_fextabindex_rom" + | "_eextabindex" + | "_f_text" + | "_f_text_rom" + | "_e_text" + | "_f_ctors" + | "_f_ctors_rom" + | "_e_ctors" + | "_f_dtors" + | "_f_dtors_rom" + | "_e_dtors" + | "_f_rodata" + | "_f_rodata_rom" + | "_e_rodata" + | "_f_data" + | "_f_data_rom" + | "_e_data" + | "_f_sdata" + | "_f_sdata_rom" + | "_e_sdata" + | "_f_sbss" + | "_f_sbss_rom" + | "_e_sbss" + | "_f_sdata2" + | "_f_sdata2_rom" + | "_e_sdata2" + | "_f_sbss2" + | "_f_sbss2_rom" + | "_e_sbss2" + | "_f_bss" + | "_f_bss_rom" + | "_e_bss" + | "_f_stack" + | "_f_stack_rom" + | "_e_stack" + | "_stack_addr" + | "_stack_end" + | "_db_stack_addr" + | "_db_stack_end" + | "_heap_addr" + | "_heap_end" + | "_nbfunctions" + | "SIZEOF_HEADERS" + | "_SDA_BASE_" + | "_SDA2_BASE_" + | "_ABS_SDA_BASE_" + | "_ABS_SDA2_BASE_" + ) +} /// Linker generated symbols to strip entirely #[inline] fn is_linker_symbol(name: &str) -> bool { - matches!(name, "_eti_init_info" | "_rom_copy_info" | "_bss_init_info") + matches!( + name, + "_eti_init_info" | "_rom_copy_info" | "_bss_init_info" | "_ctors$99" | "_dtors$99" + ) } diff --git a/src/util/asm.rs b/src/util/asm.rs index 78eb004..43276e4 100644 --- a/src/util/asm.rs +++ b/src/util/asm.rs @@ -7,9 +7,12 @@ use std::{ use anyhow::{anyhow, bail, ensure, Result}; use ppc750cl::{disasm_iter, Argument, Ins, Opcode}; -use crate::util::obj::{ - nested_push, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, - ObjSymbolFlags, ObjSymbolKind, +use crate::{ + obj::{ + ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags, + ObjSymbolKind, + }, + util::nested::NestedVec, }; #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -42,12 +45,12 @@ pub fn write_asm(w: &mut W, obj: &ObjInfo) -> Result<()> { if symbol.kind == ObjSymbolKind::Section { continue; } - nested_push(&mut entries, symbol.address as u32, SymbolEntry { + entries.nested_push(symbol.address as u32, SymbolEntry { index: symbol_index, kind: SymbolEntryKind::Start, }); if symbol.size > 0 { - nested_push(&mut entries, (symbol.address + symbol.size) as u32, SymbolEntry { + entries.nested_push((symbol.address + symbol.size) as u32, SymbolEntry { index: symbol_index, kind: SymbolEntryKind::End, }); @@ -68,7 +71,7 @@ pub fn write_asm(w: &mut W, obj: &ObjInfo) -> Result<()> { } // Replace section-relative jump relocations (generated by GCC) - // These aren't possible to express accurately in GNU assembler + // These aren't always possible to express accurately in GNU assembler if matches!(relocations.get(&ins.addr), Some(reloc) if reloc.addend == 0) { continue; } @@ -452,10 +455,12 @@ fn write_data( if symbol_kind == ObjSymbolKind::Function { ensure!( current_address & 3 == 0 && data.len() & 3 == 0, - "Unaligned code write @ {} {:#010X} size {:#X}", + "Unaligned code write @ {} {:#010X} size {:#X} (next entry: {:?}, reloc: {:?})", section.name, current_address, - data.len() + data.len(), + entry, + reloc, ); write_code_chunk(w, symbols, entries, relocations, section, current_address, data)?; } else { @@ -635,7 +640,7 @@ fn write_section_header( write!(w, ".section {}", section.name)?; write!(w, ", \"a\", @nobits")?; } - ".ctors" | ".dtors" | "extab" | "extabindex" => { + ".ctors" | ".dtors" | ".ctors$10" | ".dtors$10" | ".dtors$15" | "extab" | "extabindex" => { write!(w, ".section {}", section.name)?; write!(w, ", \"a\"")?; } @@ -671,8 +676,7 @@ fn write_reloc_symbol( } fn write_symbol_name(w: &mut W, name: &str) -> std::io::Result<()> { - // TODO more? - if name.contains('@') || name.contains('<') { + if name.contains('@') || name.contains('<') || name.contains('\\') { write!(w, "\"{name}\"")?; } else { write!(w, "{name}")?; diff --git a/src/util/config.rs b/src/util/config.rs index 72bf87b..8aa7a41 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -1,14 +1,17 @@ -use std::{io::Write, num::ParseIntError, ops::BitAndAssign}; +use std::{ + io::{BufRead, Write}, + iter, + num::ParseIntError, + str::FromStr, +}; use anyhow::{anyhow, bail, Result}; use cwdemangle::{demangle, DemangleOptions}; -use flagset::FlagSet; use once_cell::sync::Lazy; use regex::Regex; -use crate::util::obj::{ - ObjInfo, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, -}; +use crate::obj::{ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind}; +use crate::util::nested::NestedVec; fn parse_hex(s: &str) -> Result { u32::from_str_radix(s.trim_start_matches("0x"), 16) @@ -21,7 +24,7 @@ pub fn parse_symbol_line(line: &str, obj: &ObjInfo) -> Result> ) .unwrap() }); - static COMMENT_LINE: Lazy = Lazy::new(|| Regex::new("^\\s*//.*$").unwrap()); + static COMMENT_LINE: Lazy = Lazy::new(|| Regex::new("^\\s*(?://|#).*$").unwrap()); if let Some(captures) = SYMBOL_LINE.captures(&line) { let name = captures["name"].to_string(); @@ -73,6 +76,7 @@ pub fn parse_symbol_line(line: &str, obj: &ObjInfo) -> Result> } fn is_skip_symbol(symbol: &ObjSymbol) -> bool { + let _ = symbol; // symbol.name.starts_with("lbl_") // || symbol.name.starts_with("func_") // || symbol.name.starts_with("switch_") @@ -177,3 +181,135 @@ fn symbol_flags_from_str(s: &str) -> Option { _ => None, } } + +pub fn write_splits( + w: &mut W, + obj: &ObjInfo, + obj_files: Option>, +) -> Result<()> { + let mut obj_files_iter = obj_files.map(|v| v.into_iter()); + for unit in &obj.link_order { + let obj_file = if let Some(obj_files_iter) = &mut obj_files_iter { + obj_files_iter.next() + } else { + None + }; + log::info!("Processing {} (obj file {:?})", unit, obj_file); + if let Some(obj_file) = obj_file { + let trim_unit = unit + .trim_end_matches("_1") + .trim_end_matches(" (asm)") + .trim_end_matches(".o") + .trim_end_matches(".cpp") + .trim_end_matches(".c"); + if !obj_file.contains(trim_unit) { + bail!("Unit mismatch: {} vs {}", unit, obj_file); + } + let trim_obj = obj_file + .trim_end_matches(" \\") + .trim_start_matches("\t$(BUILD_DIR)/") + .trim_start_matches("asm/") + .trim_start_matches("src/"); + writeln!(w, "{}:", trim_obj)?; + } else { + writeln!(w, "{}:", unit)?; + } + let mut split_iter = obj.splits.iter() + .flat_map(|(addr, v)| v.iter().map(move |u| (addr, u))).peekable(); + while let Some((&addr, it_unit)) = split_iter.next() { + if it_unit != unit { + continue; + } + let end = split_iter.peek().map(|(&addr, _)| addr).unwrap_or(u32::MAX); + let section = obj.section_at(addr)?; + writeln!(w, "\t{:<11} start:{:#010X} end:{:#010X}", section.name, addr, end)?; + // align:{} + } + writeln!(w)?; + } + Ok(()) +} + +enum SplitLine { + Unit { name: String }, + Section { name: String, start: u32, end: u32, align: Option }, + None, +} + +fn parse_split_line(line: &str) -> Result { + static UNIT_LINE: Lazy = + Lazy::new(|| Regex::new("^\\s*(?P[^\\s:]+)\\s*:\\s*$").unwrap()); + static SECTION_LINE: Lazy = + Lazy::new(|| Regex::new("^\\s*(?P\\S+)\\s*(?P.*)$").unwrap()); + static COMMENT_LINE: Lazy = Lazy::new(|| Regex::new("^\\s*(?://|#).*$").unwrap()); + + if line.is_empty() || COMMENT_LINE.is_match(line) { + Ok(SplitLine::None) + } else if let Some(captures) = UNIT_LINE.captures(&line) { + let name = captures["name"].to_string(); + Ok(SplitLine::Unit { name }) + } else if let Some(captures) = SECTION_LINE.captures(&line) { + let mut name = captures["name"].to_string(); + let mut start: Option = None; + let mut end: Option = None; + let mut align: Option = None; + + let attrs = captures["attrs"].split(' '); + for attr in attrs { + if let Some((attr, value)) = attr.split_once(':') { + match attr { + "start" => { + start = Some(parse_hex(&value)?); + } + "end" => { + end = Some(parse_hex(&value)?); + } + "align" => align = Some(u32::from_str(value)?), + "rename" => name = value.to_string(), + _ => bail!("Unknown attribute '{name}'"), + } + } else { + bail!("Unknown attribute '{attr}'") + } + } + if let (Some(start), Some(end)) = (start, end) { + Ok(SplitLine::Section { name, start, end, align }) + } else { + Err(anyhow!("Missing attribute: '{line}'")) + } + } else { + Err(anyhow!("Failed to parse line: '{line}'")) + } +} + +pub fn apply_splits(r: R, obj: &mut ObjInfo) -> Result<()> { + enum SplitState { + None, + Unit(String), + } + let mut state = SplitState::None; + for result in r.lines() { + let line = match result { + Ok(line) => line, + Err(e) => return Err(e.into()), + }; + let split_line = parse_split_line(&line)?; + match (&mut state, split_line) { + (SplitState::None | SplitState::Unit(_), SplitLine::Unit { name }) => { + obj.link_order.push(name.clone()); + state = SplitState::Unit(name); + } + (SplitState::None, SplitLine::Section { name, .. }) => { + bail!("Section {} defined outside of unit", name); + } + (SplitState::Unit(unit), SplitLine::Section { name, start, end, align }) => { + let _ = end; + let _ = align; + obj.splits.nested_push(start, unit.clone()); + obj.named_sections.insert(start, name); + } + _ => {} + } + } + Ok(()) +} diff --git a/src/util/dol.rs b/src/util/dol.rs index ca17a82..9e76ed6 100644 --- a/src/util/dol.rs +++ b/src/util/dol.rs @@ -1,14 +1,15 @@ -use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path}; +use std::{collections::BTreeMap, path::Path}; use anyhow::{anyhow, bail, ensure, Result}; use dol::{Dol, DolSection, DolSectionType}; -use crate::util::{ - cfa::{locate_sda_bases, AnalyzerState}, +use crate::{ + analysis::cfa::locate_sda_bases, obj::{ ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, }, + util::file::{map_file, map_reader}, }; const MAX_TEXT_SECTIONS: usize = 7; @@ -28,7 +29,10 @@ pub fn process_dol>(path: P) -> Result { .and_then(|filename| filename.to_str()) .unwrap_or_default() .to_string(); - let dol = Dol::read_from(BufReader::new(File::open(path)?))?; + let dol = { + let mmap = map_file(path)?; + Dol::read_from(map_reader(&mmap))? + }; let mut obj = ObjInfo { module_id: 0, kind: ObjKind::Executable, @@ -45,6 +49,7 @@ pub fn process_dol>(path: P) -> Result { arena_lo: None, arena_hi: None, splits: Default::default(), + named_sections: Default::default(), link_order: vec![], known_functions: Default::default(), unresolved_relocations: vec![], @@ -447,7 +452,7 @@ pub fn process_dol>(path: P) -> Result { } // Locate _SDA2_BASE_ & _SDA_BASE_ - let sda_bases = match locate_sda_bases(&mut obj) { + match locate_sda_bases(&mut obj) { Ok(true) => { let sda2_base = obj.sda2_base.unwrap(); let sda_base = obj.sda_base.unwrap(); @@ -478,7 +483,7 @@ pub fn process_dol>(path: P) -> Result { Err(e) => { log::warn!("Failed to locate SDA bases: {:?}", e); } - }; + } Ok(obj) } diff --git a/src/util/dwarf.rs b/src/util/dwarf.rs index 79e826f..3b9f716 100644 --- a/src/util/dwarf.rs +++ b/src/util/dwarf.rs @@ -380,6 +380,7 @@ pub fn read_debug_section(reader: &mut R) -> Result { Ok(tags) } +#[allow(unused)] pub fn read_aranges_section(reader: &mut R) -> Result<()> { let len = { let old_pos = reader.stream_position()?; diff --git a/src/util/elf.rs b/src/util/elf.rs index 1b0652d..ba3873b 100644 --- a/src/util/elf.rs +++ b/src/util/elf.rs @@ -1,6 +1,5 @@ use std::{ collections::{btree_map::Entry, hash_map, BTreeMap, HashMap}, - fs::File, io::Cursor, path::Path, }; @@ -9,30 +8,32 @@ use anyhow::{anyhow, bail, ensure, Context, Result}; use cwdemangle::demangle; use flagset::Flags; use indexmap::IndexMap; -use memmap2::MmapOptions; use object::{ elf, elf::{SHF_ALLOC, SHF_EXECINSTR, SHF_WRITE, SHT_NOBITS, SHT_PROGBITS}, write::{ elf::{ProgramHeader, Rel, SectionHeader, SectionIndex, SymbolIndex}, - Mangling, SectionId, StringId, SymbolId, + StringId, }, - Architecture, BinaryFormat, Endianness, Object, ObjectKind, ObjectSection, ObjectSymbol, - Relocation, RelocationEncoding, RelocationKind, RelocationTarget, Section, SectionKind, Symbol, - SymbolFlags, SymbolKind, SymbolScope, SymbolSection, + Architecture, Endianness, Object, ObjectKind, ObjectSection, ObjectSymbol, Relocation, + RelocationKind, RelocationTarget, Section, SectionKind, Symbol, SymbolKind, SymbolScope, + SymbolSection, }; -use crate::util::{ - dwarf::{ - process_address, process_type, read_debug_section, type_string, ud_type, ud_type_string, - AttributeKind, TagKind, TypeKind, - }, +use crate::{ obj::{ ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, }, - sigs::OutSymbol, + util::{ + dwarf::{ + process_address, process_type, read_debug_section, type_string, ud_type, + ud_type_string, AttributeKind, TagKind, TypeKind, + }, + file::map_file, + }, }; +use crate::util::nested::NestedVec; enum BoundaryState { /// Looking for a file symbol, any section symbols are queued @@ -46,11 +47,8 @@ enum BoundaryState { const ENABLE_DWARF: bool = false; pub fn process_elf>(path: P) -> Result { - let elf_file = File::open(&path) - .with_context(|| format!("Failed to open ELF file '{}'", path.as_ref().display()))?; - let map = unsafe { MmapOptions::new().map(&elf_file) } - .with_context(|| format!("Failed to mmap ELF file: '{}'", path.as_ref().display()))?; - let obj_file = object::read::File::parse(&*map)?; + let mmap = map_file(path)?; + let obj_file = object::read::File::parse(&*mmap)?; let architecture = match obj_file.architecture() { Architecture::PowerPc => ObjArchitecture::PowerPc, arch => bail!("Unexpected architecture: {arch:?}"), @@ -105,12 +103,12 @@ pub fn process_elf>(path: P) -> Result { relocations: vec![], original_address: 0, // TODO load from abs symbol file_offset: section.file_range().map(|(v, _)| v).unwrap_or_default(), + section_known: true, }); } let mut symbols: Vec = vec![]; let mut symbol_indexes: Vec> = vec![]; - let mut current_file: Option = None; let mut section_starts = IndexMap::>::new(); let mut name_to_index = HashMap::::new(); // for resolving duplicate names let mut boundary_state = BoundaryState::LookForFile(Default::default()); @@ -170,7 +168,6 @@ pub fn process_elf>(path: P) -> Result { } indexmap::map::Entry::Vacant(e) => e.insert(Default::default()), }; - current_file = Some(file_name.clone()); match &mut boundary_state { BoundaryState::LookForFile(queue) => { if queue.is_empty() { @@ -199,10 +196,12 @@ pub fn process_elf>(path: P) -> Result { queue.push((symbol.address(), section_name)); } BoundaryState::LookForSections(file_name) => { - let sections = section_starts - .get_mut(file_name) - .ok_or_else(|| anyhow!("Failed to create entry"))?; - sections.push((symbol.address(), section_name)); + if section_indexes[section_index.0].is_some() { + let sections = section_starts + .get_mut(file_name) + .ok_or_else(|| anyhow!("Failed to create entry"))?; + sections.push((symbol.address(), section_name)); + } } BoundaryState::FilesEnded => { log::warn!( @@ -216,28 +215,29 @@ pub fn process_elf>(path: P) -> Result { _ => match symbol.section() { // Linker generated symbols indicate the end SymbolSection::Absolute => { - current_file = None; boundary_state = BoundaryState::FilesEnded; } SymbolSection::Section(section_index) => match &mut boundary_state { BoundaryState::LookForFile(_) => {} BoundaryState::LookForSections(file_name) => { - let sections = section_starts - .get_mut(file_name) - .ok_or_else(|| anyhow!("Failed to create entry"))?; - let section = obj_file.section_by_index(section_index)?; - let section_name = section.name()?; - if let Some((addr, _)) = sections - .iter_mut() - .find(|(addr, name)| *addr == 0 && name == section_name) - { - // If the section symbol had address 0, determine address - // from first symbol within that section. - *addr = symbol.address(); - } else if !sections.iter().any(|(_, name)| name == section_name) { - // Otherwise, if there was no section symbol, assume this - // symbol indicates the section address. - sections.push((symbol.address(), section_name.to_string())); + if section_indexes[section_index.0].is_some() { + let sections = section_starts + .get_mut(file_name) + .ok_or_else(|| anyhow!("Failed to create entry"))?; + let section = obj_file.section_by_index(section_index)?; + let section_name = section.name()?; + if let Some((addr, _)) = sections + .iter_mut() + .find(|(addr, name)| *addr == 0 && name == section_name) + { + // If the section symbol had address 0, determine address + // from first symbol within that section. + *addr = symbol.address(); + } else if !sections.iter().any(|(_, name)| name == section_name) { + // Otherwise, if there was no section symbol, assume this + // symbol indicates the section address. + sections.push((symbol.address(), section_name.to_string())); + } } } BoundaryState::FilesEnded => {} @@ -259,7 +259,7 @@ pub fn process_elf>(path: P) -> Result { } let mut link_order = Vec::::new(); - let mut splits = BTreeMap::::new(); + let mut splits = BTreeMap::>::new(); if kind == ObjKind::Executable { // Link order is trivially deduced for file_name in section_starts.keys() { @@ -269,7 +269,7 @@ pub fn process_elf>(path: P) -> Result { // Create a map of address -> file splits for (file_name, sections) in section_starts { for (address, _) in sections { - splits.insert(address as u32, file_name.clone()); + splits.nested_push(address as u32, file_name.clone()); } } @@ -309,6 +309,7 @@ pub fn process_elf>(path: P) -> Result { arena_lo, arena_hi, splits, + named_sections: Default::default(), link_order, known_functions: Default::default(), unresolved_relocations: vec![], @@ -332,6 +333,7 @@ pub fn write_elf(obj: &ObjInfo) -> Result> { sym: object::write::elf::Sym, } + writer.reserve_null_section_index(); let mut out_sections: Vec = Vec::with_capacity(obj.sections.len()); for section in &obj.sections { let name = writer.add_section_name(section.name.as_bytes()); @@ -356,14 +358,15 @@ pub fn write_elf(obj: &ObjInfo) -> Result> { } } let symtab = writer.reserve_symtab_section_index(); - let shstrtab = writer.reserve_shstrtab_section_index(); - let strtab = writer.reserve_strtab_section_index(); + writer.reserve_shstrtab_section_index(); + writer.reserve_strtab_section_index(); // Add symbols let mut out_symbols: Vec = Vec::with_capacity(obj.symbols.len()); let mut symbol_offset = 0; let mut num_local = 0; if !obj.name.is_empty() { + // Add file symbol let name_index = writer.add_string(obj.name.as_bytes()); let index = writer.reserve_symbol_index(None); out_symbols.push(OutSymbol { @@ -435,10 +438,7 @@ pub fn write_elf(obj: &ObjInfo) -> Result> { if sym.st_info >> 4 == elf::STB_LOCAL { num_local = writer.symbol_count(); } - out_symbols.push(OutSymbol { - index, - sym, - }); + out_symbols.push(OutSymbol { index, sym }); } writer.reserve_file_header(); @@ -540,27 +540,27 @@ pub fn write_elf(obj: &ObjInfo) -> Result> { ObjRelocKind::PpcAddr16Hi => { r_offset = (r_offset & !3) + 2; elf::R_PPC_ADDR16_HI - }, + } ObjRelocKind::PpcAddr16Ha => { r_offset = (r_offset & !3) + 2; elf::R_PPC_ADDR16_HA - }, + } ObjRelocKind::PpcAddr16Lo => { r_offset = (r_offset & !3) + 2; elf::R_PPC_ADDR16_LO - }, + } ObjRelocKind::PpcRel24 => { - r_offset = (r_offset & !3); + r_offset = r_offset & !3; elf::R_PPC_REL24 - }, + } ObjRelocKind::PpcRel14 => { - r_offset = (r_offset & !3); + r_offset = r_offset & !3; elf::R_PPC_REL14 - }, + } ObjRelocKind::PpcEmbSda21 => { r_offset = (r_offset & !3) + 2; elf::R_PPC_EMB_SDA21 - }, + } }; writer.write_relocation(true, &Rel { r_offset, diff --git a/src/util/file.rs b/src/util/file.rs new file mode 100644 index 0000000..642763b --- /dev/null +++ b/src/util/file.rs @@ -0,0 +1,57 @@ +use std::{ + fs::File, + io::{BufReader, Cursor, Read}, + path::Path, +}; + +use anyhow::{Context, Result}; +use byteorder::ReadBytesExt; +use memmap2::{Mmap, MmapOptions}; + +/// Opens a memory mapped file. +pub fn map_file>(path: P) -> Result { + let file = File::open(&path) + .with_context(|| format!("Failed to open file '{}'", path.as_ref().display()))?; + let map = unsafe { MmapOptions::new().map(&file) } + .with_context(|| format!("Failed to mmap file: '{}'", path.as_ref().display()))?; + Ok(map) +} + +pub type Reader<'a> = Cursor<&'a [u8]>; + +/// Creates a reader for the memory mapped file. +#[inline] +pub fn map_reader(mmap: &Mmap) -> Reader { Cursor::new(&*mmap) } + +/// Creates a buffered reader around a file (not memory mapped). +pub fn buf_reader>(path: P) -> Result> { + let file = File::open(&path) + .with_context(|| format!("Failed to open file '{}'", path.as_ref().display()))?; + Ok(BufReader::new(file)) +} + +/// Reads a string with known size at the specified offset. +pub fn read_string(reader: &mut Reader, off: u64, size: usize) -> Result { + let mut data = vec![0u8; size]; + let pos = reader.position(); + reader.set_position(off); + reader.read_exact(&mut data)?; + reader.set_position(pos); + Ok(String::from_utf8(data)?) +} + +/// Reads a zero-terminated string at the specified offset. +pub fn read_c_string(reader: &mut Reader, off: u64) -> Result { + let pos = reader.position(); + reader.set_position(off); + let mut s = String::new(); + loop { + let b = reader.read_u8()?; + if b == 0 { + break; + } + s.push(b as char); + } + reader.set_position(pos); + Ok(s) +} diff --git a/src/util/map.rs b/src/util/map.rs index cd10e3a..aa37e04 100644 --- a/src/util/map.rs +++ b/src/util/map.rs @@ -1,13 +1,13 @@ use std::{ - collections::{btree_map, hash_map, BTreeMap, HashMap}, + collections::{btree_map, BTreeMap, HashMap}, hash::Hash, io::BufRead, }; use anyhow::{bail, ensure, Error, Result}; use cwdemangle::{demangle, DemangleOptions}; -use lazy_static::lazy_static; use multimap::MultiMap; +use once_cell::sync::Lazy; use regex::{Captures, Regex}; use topological_sort::TopologicalSort; @@ -168,29 +168,36 @@ pub fn resolve_link_order(section_unit_order: &[(String, Vec)]) -> Resul Ok(global_unit_order) } -lazy_static! { - static ref LINK_MAP_START: Regex = Regex::new("^Link map of (?P.*)$").unwrap(); - static ref LINK_MAP_ENTRY: Regex = Regex::new( - "^\\s*(?P\\d+)] (?P.*) \\((?P.*),(?P.*)\\) found in (?P.*)$", - ) - .unwrap(); - static ref LINK_MAP_ENTRY_GENERATED: Regex = - Regex::new("^\\s*(?P\\d+)] (?P.*) found as linker generated symbol$").unwrap(); - static ref LINK_MAP_ENTRY_DUPLICATE: Regex = - Regex::new("^\\s*(?P\\d+)] >>> UNREFERENCED DUPLICATE (?P.*)$").unwrap(); - static ref SECTION_LAYOUT_START: Regex = Regex::new("^(?P
.*) section layout$").unwrap(); - static ref SECTION_LAYOUT_SYMBOL: Regex = Regex::new( - "^\\s*(?P[0-9A-Fa-f]+|UNUSED)\\s+(?P[0-9A-Fa-f]+)\\s+(?P[0-9A-Fa-f]+|\\.{8})\\s+(?P\\d+)?\\s*(?P.*?)(?:\\s+\\(entry of (?P.*?)\\))?\\s+(?P.*)$", - ) - .unwrap(); - static ref SECTION_LAYOUT_HEADER: Regex = Regex::new( - "^(\\s*Starting\\s+Virtual\\s*|\\s*address\\s+Size\\s+address\\s*|\\s*-----------------------\\s*)$", - ) - .unwrap(); - static ref MEMORY_MAP_HEADER: Regex = Regex::new("^\\s*Memory map:\\s*$").unwrap(); - static ref EXTERN_SYMBOL: Regex = Regex::new("^\\s*>>> SYMBOL NOT FOUND: (.*)$").unwrap(); - static ref LINKER_SYMBOLS_HEADER: Regex = Regex::new("^\\s*Linker generated symbols:\\s*$").unwrap(); +macro_rules! static_regex { + ($name:ident, $str:expr) => { + static $name: Lazy = Lazy::new(|| Regex::new($str).unwrap()); + }; } +static_regex!(LINK_MAP_START, "^Link map of (?P.*)$"); +static_regex!( + LINK_MAP_ENTRY, + "^\\s*(?P\\d+)] (?P.*) \\((?P.*),(?P.*)\\) found in (?P.*)$" +); +static_regex!( + LINK_MAP_ENTRY_GENERATED, + "^\\s*(?P\\d+)] (?P.*) found as linker generated symbol$" +); +static_regex!( + LINK_MAP_ENTRY_DUPLICATE, + "^\\s*(?P\\d+)] >>> UNREFERENCED DUPLICATE (?P.*)$" +); +static_regex!(SECTION_LAYOUT_START, "^(?P
.*) section layout$"); +static_regex!( + SECTION_LAYOUT_SYMBOL, + "^\\s*(?P[0-9A-Fa-f]+|UNUSED)\\s+(?P[0-9A-Fa-f]+)\\s+(?P[0-9A-Fa-f]+|\\.{8})\\s+(?P\\d+)?\\s*(?P.*?)(?:\\s+\\(entry of (?P.*?)\\))?\\s+(?P.*)$" +); +static_regex!( + SECTION_LAYOUT_HEADER, + "^(\\s*Starting\\s+Virtual\\s*|\\s*address\\s+Size\\s+address\\s*|\\s*-----------------------\\s*)$" +); +static_regex!(MEMORY_MAP_HEADER, "^\\s*Memory map:\\s*$"); +static_regex!(EXTERN_SYMBOL, "^\\s*>>> SYMBOL NOT FOUND: (.*)$"); +static_regex!(LINKER_SYMBOLS_HEADER, "^\\s*Linker generated symbols:\\s*$"); #[derive(Default)] pub struct MapEntries { @@ -544,25 +551,3 @@ pub fn process_map(reader: R) -> Result { // entries.unit_order = section_order.unit_order; Ok(entries) } - -#[inline] -fn nested_try_insert( - map: &mut HashMap>, - v1: T1, - v2: T2, - v3: T3, -) -> Result<()> -where - T1: Hash + Eq, - T2: Hash + Eq, -{ - let map = match map.entry(v1) { - hash_map::Entry::Occupied(entry) => entry.into_mut(), - hash_map::Entry::Vacant(entry) => entry.insert(Default::default()), - }; - match map.entry(v2) { - hash_map::Entry::Occupied(_) => bail!("Entry already exists"), - hash_map::Entry::Vacant(entry) => entry.insert(v3), - }; - Ok(()) -} diff --git a/src/util/mod.rs b/src/util/mod.rs index 89037e3..e222033 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,15 +1,22 @@ -pub(crate) mod asm; -pub(crate) mod cfa; -pub(crate) mod config; -pub(crate) mod dol; -pub(crate) mod dwarf; -pub(crate) mod elf; -pub(crate) mod executor; -pub(crate) mod map; -pub(crate) mod obj; -pub(crate) mod rel; -pub(crate) mod sigs; -pub(crate) mod slices; -pub(crate) mod split; -pub(crate) mod tracker; -pub(crate) mod vm; +pub mod asm; +pub mod config; +pub mod dol; +pub mod dwarf; +pub mod elf; +pub mod file; +pub mod map; +pub mod nested; +pub mod rel; +pub mod rso; + +/// Creates a fixed-size array reference from a slice. +#[macro_export] +macro_rules! array_ref { + ($slice:expr, $offset:expr, $size:expr) => {{ + #[inline] + fn to_array(slice: &[T]) -> &[T; $size] { + unsafe { &*(slice.as_ptr() as *const [_; $size]) } + } + to_array(&$slice[$offset..$offset + $size]) + }}; +} diff --git a/src/util/nested.rs b/src/util/nested.rs new file mode 100644 index 0000000..c726ed0 --- /dev/null +++ b/src/util/nested.rs @@ -0,0 +1,65 @@ +use std::{ + collections::{btree_map, hash_map, BTreeMap, HashMap}, + hash::Hash, +}; + +use anyhow::{bail, Result}; + +pub trait NestedMap { + fn nested_insert(&mut self, v1: T1, v2: T2, v3: T3) -> Result<()>; +} + +pub trait NestedVec { + fn nested_push(&mut self, v1: T1, v2: T2); +} + +impl NestedMap for BTreeMap> +where + T1: Eq + Ord, + T2: Eq + Ord, +{ + fn nested_insert(&mut self, v1: T1, v2: T2, v3: T3) -> Result<()> { + let inner = match self.entry(v1) { + btree_map::Entry::Occupied(entry) => entry.into_mut(), + btree_map::Entry::Vacant(entry) => entry.insert(Default::default()), + }; + match inner.entry(v2) { + btree_map::Entry::Occupied(_) => bail!("Entry already exists"), + btree_map::Entry::Vacant(entry) => entry.insert(v3), + }; + Ok(()) + } +} + +impl NestedMap for HashMap> +where + T1: Eq + Hash, + T2: Eq + Hash, +{ + fn nested_insert(&mut self, v1: T1, v2: T2, v3: T3) -> Result<()> { + let inner = match self.entry(v1) { + hash_map::Entry::Occupied(entry) => entry.into_mut(), + hash_map::Entry::Vacant(entry) => entry.insert(Default::default()), + }; + match inner.entry(v2) { + hash_map::Entry::Occupied(_) => bail!("Entry already exists"), + hash_map::Entry::Vacant(entry) => entry.insert(v3), + }; + Ok(()) + } +} + +impl NestedVec for BTreeMap> +where T1: Ord +{ + fn nested_push(&mut self, v1: T1, v2: T2) { + match self.entry(v1) { + btree_map::Entry::Occupied(mut e) => { + e.get_mut().push(v2); + } + btree_map::Entry::Vacant(e) => { + e.insert(vec![v2]); + } + } + } +} diff --git a/src/util/rel.rs b/src/util/rel.rs index cf2f273..81d394a 100644 --- a/src/util/rel.rs +++ b/src/util/rel.rs @@ -1,19 +1,15 @@ -use std::{ - fs::File, - io::{BufReader, Read, Seek, SeekFrom}, - path::Path, -}; +use std::{io::Read, path::Path}; use anyhow::{anyhow, bail, ensure, Result}; use byteorder::{BigEndian, ReadBytesExt}; -use object::elf::{ - R_PPC_ADDR16, R_PPC_ADDR16_HA, R_PPC_ADDR16_HI, R_PPC_ADDR16_LO, R_PPC_ADDR24, R_PPC_ADDR32, - R_PPC_NONE, R_PPC_REL14, R_PPC_REL24, R_PPC_UADDR32, -}; +use object::elf; -use crate::util::obj::{ - ObjArchitecture, ObjInfo, ObjKind, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, - ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, +use crate::{ + obj::{ + ObjArchitecture, ObjInfo, ObjKind, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, + ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, + }, + util::file::{map_file, map_reader, read_string}, }; /// Do not relocate anything, but accumulate the offset field for the next relocation offset calculation. @@ -29,7 +25,9 @@ pub const R_DOLPHIN_END: u32 = 203; pub const R_DOLPHIN_MRKREF: u32 = 204; pub fn process_rel>(path: P) -> Result { - let mut reader = BufReader::new(File::open(&path)?); + let mmap = map_file(path)?; + let mut reader = map_reader(&mmap); + let module_id = reader.read_u32::()?; ensure!(reader.read_u32::()? == 0, "Expected 'next' to be 0"); ensure!(reader.read_u32::()? == 0, "Expected 'prev' to be 0"); @@ -60,7 +58,7 @@ pub fn process_rel>(path: P) -> Result { let fix_size = if version >= 3 { Some(reader.read_u32::()?) } else { None }; let mut sections = Vec::with_capacity(num_sections as usize); - reader.seek(SeekFrom::Start(section_info_offset as u64))?; + reader.set_position(section_info_offset as u64); let mut total_bss_size = 0; for idx in 0..num_sections { let offset = reader.read_u32::()?; @@ -74,11 +72,11 @@ pub fn process_rel>(path: P) -> Result { let data = if offset == 0 { vec![] } else { - let position = reader.stream_position()?; - reader.seek(SeekFrom::Start(offset as u64))?; + let position = reader.position(); + reader.set_position(offset as u64); let mut data = vec![0u8; size as usize]; reader.read_exact(&mut data)?; - reader.seek(SeekFrom::Start(position))?; + reader.set_position(position); data }; @@ -148,8 +146,8 @@ pub fn process_rel>(path: P) -> Result { let mut unresolved_relocations = Vec::new(); let mut imp_idx = 0; let imp_end = (imp_offset + imp_size) as u64; - reader.seek(SeekFrom::Start(imp_offset as u64))?; - while reader.stream_position()? < imp_end { + reader.set_position(imp_offset as u64); + while reader.position() < imp_end { let reloc_module_id = reader.read_u32::()?; let reloc_offset = reader.read_u32::()?; @@ -165,12 +163,17 @@ pub fn process_rel>(path: P) -> Result { if reloc_module_id == module_id { if let Some(fix_size) = fix_size { - ensure!(fix_size == reloc_offset, "fix_size mismatch: {:#X} != {:#X}", fix_size, reloc_offset); + ensure!( + fix_size == reloc_offset, + "fix_size mismatch: {:#X} != {:#X}", + fix_size, + reloc_offset + ); } } - let position = reader.stream_position()?; - reader.seek(SeekFrom::Start(reloc_offset as u64))?; + let position = reader.position(); + reader.set_position(reloc_offset as u64); let mut address = 0u32; let mut section = u8::MAX; loop { @@ -179,20 +182,20 @@ pub fn process_rel>(path: P) -> Result { let target_section = reader.read_u8()?; let addend = reader.read_u32::()?; let kind = match type_id { - R_PPC_NONE => continue, - R_PPC_ADDR32 | R_PPC_UADDR32 => ObjRelocKind::Absolute, - // R_PPC_ADDR24 => ObjRelocKind::PpcAddr24, - // R_PPC_ADDR16 => ObjRelocKind::PpcAddr16, - R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo, - R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi, - R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha, - // R_PPC_ADDR14 => ObjRelocKind::PpcAddr14, - // R_PPC_ADDR14_BRTAKEN => ObjRelocKind::PpcAddr14BrTaken, - // R_PPC_ADDR14_BRNTAKEN => ObjRelocKind::PpcAddr14BrnTaken, - R_PPC_REL24 => ObjRelocKind::PpcRel24, - R_PPC_REL14 => ObjRelocKind::PpcRel14, - // R_PPC_REL14_BRTAKEN => ObjRelocKind::PpcRel14BrTaken, - // R_PPC_REL14_BRNTAKEN => ObjRelocKind::PpcRel14BrnTaken, + elf::R_PPC_NONE => continue, + elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 => ObjRelocKind::Absolute, + // elf::R_PPC_ADDR24 => ObjRelocKind::PpcAddr24, + // elf::R_PPC_ADDR16 => ObjRelocKind::PpcAddr16, + elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo, + elf::R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi, + elf::R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha, + // elf::R_PPC_ADDR14 => ObjRelocKind::PpcAddr14, + // elf::R_PPC_ADDR14_BRTAKEN => ObjRelocKind::PpcAddr14BrTaken, + // elf::R_PPC_ADDR14_BRNTAKEN => ObjRelocKind::PpcAddr14BrnTaken, + elf::R_PPC_REL24 => ObjRelocKind::PpcRel24, + elf::R_PPC_REL14 => ObjRelocKind::PpcRel14, + // elf::R_PPC_REL14_BRTAKEN => ObjRelocKind::PpcRel14BrTaken, + // elf::R_PPC_REL14_BRNTAKEN => ObjRelocKind::PpcRel14BrnTaken, R_DOLPHIN_NOP => { address += offset as u32; continue; @@ -216,14 +219,18 @@ pub fn process_rel>(path: P) -> Result { addend, }); } - reader.seek(SeekFrom::Start(position))?; + reader.set_position(position); } + let name = match name_offset { + 0 => String::new(), + _ => read_string(&mut reader, name_offset as u64, name_size as usize)?, + }; Ok(ObjInfo { module_id, kind: ObjKind::Relocatable, architecture: ObjArchitecture::PowerPc, - name: "".to_string(), + name, symbols, sections, entry: 0, @@ -235,6 +242,7 @@ pub fn process_rel>(path: P) -> Result { arena_lo: None, arena_hi: None, splits: Default::default(), + named_sections: Default::default(), link_order: vec![], known_functions: Default::default(), unresolved_relocations, diff --git a/src/util/rso.rs b/src/util/rso.rs new file mode 100644 index 0000000..8e715f6 --- /dev/null +++ b/src/util/rso.rs @@ -0,0 +1,222 @@ +use std::{io::Read, path::Path}; + +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::{map_file, map_reader, read_c_string, read_string}, +}; + +pub fn process_rso>(path: P) -> Result { + let mmap = map_file(path)?; + let mut reader = map_reader(&mmap); + + ensure!(reader.read_u32::()? == 0, "Expected 'next' to be 0"); + ensure!(reader.read_u32::()? == 0, "Expected 'prev' to be 0"); + let num_sections = reader.read_u32::()?; + let section_info_offset = reader.read_u32::()?; + let name_offset = reader.read_u32::()?; + let name_size = reader.read_u32::()?; + let version = reader.read_u32::()?; + ensure!(version == 1, "Unsupported RSO version {}", version); + let bss_size = reader.read_u32::()?; + 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::()?; + let epilog_offset = reader.read_u32::()?; + let unresolved_offset = reader.read_u32::()?; + let internal_rel_offset = reader.read_u32::()?; + let internal_rel_size = reader.read_u32::()?; + let external_rel_offset = reader.read_u32::()?; + let external_rel_size = reader.read_u32::()?; + let export_table_offset = reader.read_u32::()?; + let export_table_size = reader.read_u32::()?; + let export_table_name_offset = reader.read_u32::()?; + let import_table_offset = reader.read_u32::()?; + let import_table_size = reader.read_u32::()?; + let import_table_name_offset = reader.read_u32::()?; + + let mut sections = Vec::with_capacity(num_sections as usize); + reader.set_position(section_info_offset as u64); + let mut total_bss_size = 0; + for idx in 0..num_sections { + let offset = reader.read_u32::()?; + let size = reader.read_u32::()?; + log::info!("Section {}: {:#X} {:#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.position(); + reader.set_position(offset as u64); + let mut data = vec![0u8; size as usize]; + reader.read_exact(&mut data)?; + reader.set_position(position); + data + }; + + // println!("Section {} offset {:#X} size {:#X}", idx, offset, size); + + let index = sections.len(); + 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, + index, + elf_index: idx as usize, + relocations: vec![], + original_address: 0, + file_offset: offset as u64, + section_known: false, + }); + 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 = |section_idx: u8, offset: u32, name: &str| -> Result<()> { + if section_idx > 0 { + let section = sections + .iter() + .find(|section| section.elf_index == section_idx as usize) + .ok_or_else(|| anyhow!("Failed to locate {name} section {section_idx}"))?; + log::info!("Adding {name} section {section_idx} offset {offset:#X}"); + symbols.push(ObjSymbol { + name: name.to_string(), + demangled_name: None, + address: offset as u64, + section: Some(section.index), + size: 0, + size_known: false, + flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), + kind: ObjSymbolKind::Function, + }); + } + Ok(()) + }; + add_symbol(prolog_section, prolog_offset, "_prolog")?; + add_symbol(epilog_section, epilog_offset, "_epilog")?; + add_symbol(unresolved_section, unresolved_offset, "_unresolved")?; + + reader.set_position(external_rel_offset as u64); + while reader.position() < (external_rel_offset + external_rel_size) as u64 { + let offset = reader.read_u32::()?; + let id_and_type = reader.read_u32::()?; + let id = (id_and_type & 0xFFFFFF00) >> 8; + let rel_type = id_and_type & 0xFF; + let sym_offset = reader.read_u32::()?; + log::info!( + "Reloc offset: {:#X}, id: {}, type: {}, sym offset: {:#X}", + offset, + id, + rel_type, + sym_offset + ); + } + + reader.set_position(export_table_offset as u64); + while reader.position() < (export_table_offset + export_table_size) as u64 { + let name_off = reader.read_u32::()?; + let name = read_c_string(&mut reader, (export_table_name_offset + name_off) as u64)?; + let sym_off = reader.read_u32::()?; + let section_idx = reader.read_u32::()?; + let hash_n = reader.read_u32::()?; + let calc = symbol_hash(&name); + let demangled_name = demangle(&name, &DemangleOptions::default()); + let section = sections + .iter() + .find(|section| section.elf_index == section_idx as usize) + .map(|section| section.index); + log::info!( + "Export: {}, sym off: {:#X}, section: {}, ELF hash: {:#X}, {:#X}", + demangled_name.as_deref().unwrap_or(&name), + sym_off, + section_idx, + hash_n, + calc + ); + symbols.push(ObjSymbol { + name, + demangled_name, + address: sym_off as u64, + section, + size: 0, + size_known: false, + flags: Default::default(), + kind: Default::default(), + }); + } + reader.set_position(import_table_offset as u64); + while reader.position() < (import_table_offset + import_table_size) as u64 { + let name_off = reader.read_u32::()?; + let name = read_c_string(&mut reader, (import_table_name_offset + name_off) as u64)?; + let sym_off = reader.read_u32::()?; + let section_idx = reader.read_u32::()?; + log::info!("Import: {}, sym off: {}, section: {}", name, sym_off, section_idx); + } + + let name = match name_offset { + 0 => String::new(), + _ => read_string(&mut reader, name_offset as u64, name_size as usize)?, + }; + Ok(ObjInfo { + kind: ObjKind::Relocatable, + architecture: ObjArchitecture::PowerPc, + name, + symbols, + sections, + entry: 0, + sda2_base: None, + sda_base: None, + stack_address: None, + stack_end: None, + db_stack_addr: None, + arena_lo: None, + arena_hi: None, + splits: Default::default(), + named_sections: Default::default(), + link_order: vec![], + known_functions: Default::default(), + module_id: 0, + unresolved_relocations: vec![], + }) +} + +fn symbol_hash(s: &str) -> u32 { + s.bytes().fold(0u32, |hash, c| { + let mut m = (hash << 4) + c as u32; + let n = m & 0xF0000000; + if n != 0 { + m ^= n >> 24; + } + m & !n + }) +}