mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-09-22 19:19:50 +00:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
89864dc76e | |||
9c5210184d | |||
31c42d58db | |||
|
d3596dbaa4 | ||
b56b399201 | |||
8620099731 | |||
ae00c35ec3 | |||
ba2589646e | |||
|
7bc0bc474d | ||
|
d969819b78 | ||
f4a67ee619 | |||
|
d92a892c2b | ||
|
5e33fea49f |
154
Cargo.lock
generated
154
Cargo.lock
generated
@ -167,9 +167,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@ -179,9 +179,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.2"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
@ -339,16 +339,16 @@ checksum = "c2e06f9bce634a3c898eb1e5cb949ff63133cbb218af93cc9b38b31d6f3ea285"
|
||||
|
||||
[[package]]
|
||||
name = "cwextab"
|
||||
version = "1.1.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "701f6867c92e1b64ddcc4b416194be3121b8f7ba5352a70ed5fd3295a7d8e0e1"
|
||||
checksum = "9dd95393b8cc20937e4757d9c22b89d016613e934c60dcb073bd8a5aade79fcf"
|
||||
dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decomp-toolkit"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
@ -365,7 +365,7 @@ dependencies = [
|
||||
"enable-ansi-support",
|
||||
"encoding_rs",
|
||||
"filetime",
|
||||
"fixedbitset 0.5.7",
|
||||
"fixedbitset",
|
||||
"flagset",
|
||||
"glob",
|
||||
"hex",
|
||||
@ -480,9 +480,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.1"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
@ -496,12 +496,6 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
@ -697,10 +691,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.72"
|
||||
version = "0.3.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
||||
checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@ -781,11 +776,11 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -911,12 +906,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
version = "0.50.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1018,12 +1012,6 @@ dependencies = [
|
||||
"snafu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.1.0"
|
||||
@ -1071,11 +1059,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
|
||||
dependencies = [
|
||||
"fixedbitset 0.4.2",
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
@ -1105,9 +1093,9 @@ checksum = "be8e5b9d48ab30323e8ece6d655d20fc16a570e4f1af0de6890d3e9ebd284ba0"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.22"
|
||||
version = "0.2.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
|
||||
checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.101",
|
||||
@ -1133,9 +1121,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.13.3"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
|
||||
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
@ -1143,11 +1131,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.13.3"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15"
|
||||
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck",
|
||||
"itertools",
|
||||
"log",
|
||||
@ -1164,9 +1151,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.13.3"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5"
|
||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
@ -1177,9 +1164,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.13.3"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
|
||||
checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
@ -1252,17 +1239,8 @@ checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.8",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1273,15 +1251,9 @@ checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
@ -1605,7 +1577,7 @@ dependencies = [
|
||||
"flate2",
|
||||
"fnv",
|
||||
"once_cell",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@ -1695,9 +1667,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
@ -1706,9 +1678,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1717,9 +1689,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@ -1738,14 +1710,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.18"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
@ -1756,9 +1728,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tsify-next"
|
||||
version = "0.5.4"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4a645dca4ee0800f5ab60ce166deba2db6a0315de795a2691e138a3d55d756"
|
||||
checksum = "7d0f2208feeb5f7a6edb15a2389c14cd42480ef6417318316bb866da5806a61d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
@ -1768,9 +1740,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tsify-next-macros"
|
||||
version = "0.5.4"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d5c06f8a51d759bb58129e30b2631739e7e1e4579fad1f30ac09a6c88e488a6"
|
||||
checksum = "f81253930d0d388a3ab8fa4ae56da9973ab171ef833d1be2e9080fc3ce502bd6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1853,24 +1825,25 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.95"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||
checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.95"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||
checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
@ -1879,9 +1852,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.95"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||
checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@ -1889,9 +1862,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.95"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1902,9 +1875,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.95"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||
checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
@ -1928,7 +1904,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3,7 +3,7 @@ name = "decomp-toolkit"
|
||||
description = "Yet another GameCube/Wii decompilation toolkit."
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
repository = "https://github.com/encounter/decomp-toolkit"
|
||||
|
@ -297,6 +297,8 @@ Dumps DWARF 1.1 information from an ELF file. (Does **not** support DWARF 2+)
|
||||
|
||||
```shell
|
||||
$ dtk dwarf dump input.elf
|
||||
# or, to include data that was stripped by MWLD
|
||||
$ dtk dwarf dump input.elf --include-erased
|
||||
```
|
||||
|
||||
### elf disasm
|
||||
|
@ -75,12 +75,14 @@ ignore = [
|
||||
#"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
|
||||
#{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
|
||||
{ id = "RUSTSEC-2024-0384", reason = "unmaintained transient dependency, will be updated in next nod version" },
|
||||
{ id = "RUSTSEC-2025-0056", reason = "adler crate used via nodtool has no maintained alternative yet" },
|
||||
{ id = "RUSTSEC-2025-0048", reason = "tsify-next is unmaintained but required by objdiff-core" },
|
||||
]
|
||||
# If this is true, then cargo deny will use the git executable to fetch advisory database.
|
||||
# If this is false, then it uses a built-in git library.
|
||||
# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
|
||||
# See Git Authentication for more information about setting up git authentication.
|
||||
#git-fetch-with-cli = true
|
||||
git-fetch-with-cli = true
|
||||
|
||||
# This section is considered when running `cargo deny check licenses`
|
||||
# More documentation for the licenses section can be found here:
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::BTreeMap,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
fmt::{Debug, Display, Formatter, UpperHex},
|
||||
ops::{Add, AddAssign, BitAnd, Sub},
|
||||
};
|
||||
@ -572,6 +572,26 @@ pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result<bool> {
|
||||
Some((sda2_base, sda_base)) => {
|
||||
obj.sda2_base = Some(sda2_base);
|
||||
obj.sda_base = Some(sda_base);
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_SDA2_BASE_".to_string(),
|
||||
address: sda2_base as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_SDA_BASE_".to_string(),
|
||||
address: sda_base as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
Ok(true)
|
||||
}
|
||||
None => Ok(false),
|
||||
@ -581,7 +601,7 @@ pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result<bool> {
|
||||
/// ProDG hardcodes .bss and .sbss section initialization in `entry`
|
||||
/// This function locates the memset calls and returns a list of
|
||||
/// (address, size) pairs for the .bss sections.
|
||||
pub fn locate_bss_memsets(obj: &mut ObjInfo) -> Result<Vec<(u32, u32)>> {
|
||||
pub fn locate_bss_memsets(obj: &ObjInfo) -> Result<Vec<(u32, u32)>> {
|
||||
let mut bss_sections: Vec<(u32, u32)> = Vec::new();
|
||||
let Some(entry) = obj.entry else {
|
||||
return Ok(bss_sections);
|
||||
@ -632,3 +652,50 @@ pub fn locate_bss_memsets(obj: &mut ObjInfo) -> Result<Vec<(u32, u32)>> {
|
||||
)?;
|
||||
Ok(bss_sections)
|
||||
}
|
||||
|
||||
/// Execute VM from specified entry point following inner-section branches and function calls,
|
||||
/// noting all branch targets outside the current section.
|
||||
pub fn locate_cross_section_branch_targets(
|
||||
obj: &ObjInfo,
|
||||
entry: SectionAddress,
|
||||
) -> Result<BTreeSet<SectionAddress>> {
|
||||
let mut branch_targets = BTreeSet::<SectionAddress>::new();
|
||||
let mut executor = Executor::new(obj);
|
||||
executor.push(entry, VM::new(), false);
|
||||
executor.run(
|
||||
obj,
|
||||
|ExecCbData { executor, vm, result, ins_addr, section: _, ins: _, block_start: _ }| {
|
||||
match result {
|
||||
StepResult::Continue | StepResult::LoadStore { .. } => {
|
||||
Ok(ExecCbResult::<()>::Continue)
|
||||
}
|
||||
StepResult::Illegal => bail!("Illegal instruction @ {}", ins_addr),
|
||||
StepResult::Jump(target) => {
|
||||
if let BranchTarget::Address(RelocationTarget::Address(addr)) = target {
|
||||
if addr.section == entry.section {
|
||||
executor.push(addr, vm.clone_all(), true);
|
||||
} else {
|
||||
branch_targets.insert(addr);
|
||||
}
|
||||
}
|
||||
Ok(ExecCbResult::EndBlock)
|
||||
}
|
||||
StepResult::Branch(branches) => {
|
||||
for branch in branches {
|
||||
if let BranchTarget::Address(RelocationTarget::Address(addr)) =
|
||||
branch.target
|
||||
{
|
||||
if addr.section == entry.section {
|
||||
executor.push(addr, branch.vm, true);
|
||||
} else {
|
||||
branch_targets.insert(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ExecCbResult::Continue)
|
||||
}
|
||||
}
|
||||
},
|
||||
)?;
|
||||
Ok(branch_targets)
|
||||
}
|
||||
|
@ -244,7 +244,9 @@ pub fn uniq_jump_table_entries(
|
||||
return Ok((BTreeSet::new(), 0));
|
||||
}
|
||||
let (entries, size) =
|
||||
get_jump_table_entries(obj, addr, size, from, function_start, function_end)?;
|
||||
get_jump_table_entries(obj, addr, size, from, function_start, function_end).with_context(
|
||||
|| format!("While fetching jump table entries starting at {addr:#010X}"),
|
||||
)?;
|
||||
Ok((BTreeSet::from_iter(entries.iter().cloned()), size))
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||
|
||||
use crate::{
|
||||
obj::{ObjDataKind, ObjInfo, ObjSectionKind, ObjSymbolKind, SymbolIndex},
|
||||
util::split::is_linker_generated_label,
|
||||
util::{config::is_auto_symbol, split::is_linker_generated_label},
|
||||
};
|
||||
|
||||
pub fn detect_objects(obj: &mut ObjInfo) -> Result<()> {
|
||||
@ -134,7 +134,9 @@ pub fn detect_strings(obj: &mut ObjInfo) -> Result<()> {
|
||||
StringResult::None => {}
|
||||
StringResult::String { length, terminated } => {
|
||||
let size = if terminated { length + 1 } else { length };
|
||||
if !symbol.size_known || symbol.size == size as u64 {
|
||||
if symbol.size == size as u64
|
||||
|| (is_auto_symbol(symbol) && symbol.size > size as u64)
|
||||
{
|
||||
let str = String::from_utf8_lossy(&data[..length]);
|
||||
log::debug!("Found string '{}' @ {}", str, symbol.name);
|
||||
symbols_set.push((symbol_idx, ObjDataKind::String, size));
|
||||
@ -142,7 +144,9 @@ pub fn detect_strings(obj: &mut ObjInfo) -> Result<()> {
|
||||
}
|
||||
StringResult::WString { length, str } => {
|
||||
let size = length + 2;
|
||||
if !symbol.size_known || symbol.size == size as u64 {
|
||||
if symbol.size == size as u64
|
||||
|| (is_auto_symbol(symbol) && symbol.size > size as u64)
|
||||
{
|
||||
log::debug!("Found wide string '{}' @ {}", str, symbol.name);
|
||||
symbols_set.push((symbol_idx, ObjDataKind::String16, size));
|
||||
}
|
||||
|
@ -45,6 +45,17 @@ type BlockRange = Range<SectionAddress>;
|
||||
|
||||
type InsCheck = dyn Fn(Ins) -> bool;
|
||||
|
||||
/// Stop searching for prologue/epilogue sequences if the next instruction
|
||||
/// is a branch or uses r0 or r1.
|
||||
fn is_end_of_seq(next: &Ins) -> bool {
|
||||
next.is_branch()
|
||||
|| next
|
||||
.defs()
|
||||
.iter()
|
||||
.chain(next.uses().iter())
|
||||
.any(|a| matches!(a, ppc750cl::Argument::GPR(ppc750cl::GPR(0 | 1))))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn check_sequence(
|
||||
section: &ObjSection,
|
||||
@ -52,29 +63,26 @@ fn check_sequence(
|
||||
ins: Option<Ins>,
|
||||
sequence: &[(&InsCheck, &InsCheck)],
|
||||
) -> Result<bool> {
|
||||
let mut found = false;
|
||||
let ins = ins
|
||||
.or_else(|| disassemble(section, addr.address))
|
||||
.with_context(|| format!("Failed to disassemble instruction at {addr:#010X}"))?;
|
||||
for &(first, second) in sequence {
|
||||
let Some(ins) = ins.or_else(|| disassemble(section, addr.address)) else {
|
||||
continue;
|
||||
};
|
||||
if !first(ins) {
|
||||
continue;
|
||||
}
|
||||
let Some(next) = disassemble(section, addr.address + 4) else {
|
||||
continue;
|
||||
};
|
||||
if second(next)
|
||||
// Also check the following instruction, in case the scheduler
|
||||
// put something in between.
|
||||
|| (!next.is_branch()
|
||||
&& matches!(disassemble(section, addr.address + 8), Some(ins) if second(ins)))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
let mut current_addr = addr.address + 4;
|
||||
while let Some(next) = disassemble(section, current_addr) {
|
||||
if second(next) {
|
||||
return Ok(true);
|
||||
}
|
||||
if is_end_of_seq(&next) {
|
||||
// If we hit a branch or an instruction that uses r0 or r1, stop searching.
|
||||
break;
|
||||
}
|
||||
current_addr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(found)
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn check_prologue_sequence(
|
||||
@ -89,15 +97,19 @@ fn check_prologue_sequence(
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_stwu(ins: Ins) -> bool {
|
||||
// stwu r1, d(r1)
|
||||
ins.op == Opcode::Stwu && ins.field_rs() == 1 && ins.field_ra() == 1
|
||||
// stwu[x] r1, d(r1)
|
||||
matches!(ins.op, Opcode::Stwu | Opcode::Stwux) && ins.field_rs() == 1 && ins.field_ra() == 1
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_stw(ins: Ins) -> bool {
|
||||
// stw r0, d(r1)
|
||||
ins.op == Opcode::Stw && ins.field_rs() == 0 && ins.field_ra() == 1
|
||||
}
|
||||
check_sequence(section, addr, ins, &[(&is_stwu, &is_mflr), (&is_mflr, &is_stw)])
|
||||
check_sequence(section, addr, ins, &[
|
||||
(&is_stwu, &is_mflr),
|
||||
(&is_mflr, &is_stw),
|
||||
(&is_mflr, &is_stwu),
|
||||
])
|
||||
}
|
||||
|
||||
impl FunctionSlices {
|
||||
@ -148,7 +160,28 @@ impl FunctionSlices {
|
||||
}
|
||||
if check_prologue_sequence(section, addr, Some(ins))? {
|
||||
if let Some(prologue) = self.prologue {
|
||||
if prologue != addr && prologue != addr - 4 {
|
||||
let invalid_seq = if prologue == addr {
|
||||
false
|
||||
} else if prologue > addr {
|
||||
true
|
||||
} else {
|
||||
// Check if any instructions between the prologue and this address
|
||||
// are branches or use r0 or r1.
|
||||
let mut current_addr = prologue.address + 4;
|
||||
loop {
|
||||
if current_addr == addr.address {
|
||||
break false;
|
||||
}
|
||||
let next = disassemble(section, current_addr).with_context(|| {
|
||||
format!("Failed to disassemble {current_addr:#010X}")
|
||||
})?;
|
||||
if is_end_of_seq(&next) {
|
||||
break true;
|
||||
}
|
||||
current_addr += 4;
|
||||
}
|
||||
};
|
||||
if invalid_seq {
|
||||
bail!("Found multiple functions inside a symbol: {:#010X} and {:#010X}. Check symbols.txt?", prologue, addr)
|
||||
}
|
||||
} else {
|
||||
@ -180,7 +213,11 @@ impl FunctionSlices {
|
||||
ins.op == Opcode::Or && ins.field_rd() == 1
|
||||
}
|
||||
|
||||
if check_sequence(section, addr, Some(ins), &[(&is_mtlr, &is_addi), (&is_or, &is_mtlr)])? {
|
||||
if check_sequence(section, addr, Some(ins), &[
|
||||
(&is_mtlr, &is_addi),
|
||||
(&is_mtlr, &is_or),
|
||||
(&is_or, &is_mtlr),
|
||||
])? {
|
||||
if let Some(epilogue) = self.epilogue {
|
||||
if epilogue != addr {
|
||||
bail!("Found duplicate epilogue: {:#010X} and {:#010X}", epilogue, addr)
|
||||
@ -340,7 +377,14 @@ impl FunctionSlices {
|
||||
function_end.or_else(|| self.end()),
|
||||
)?;
|
||||
log::debug!("-> size {}: {:?}", size, entries);
|
||||
if (entries.contains(&next_address) || self.blocks.contains_key(&next_address))
|
||||
let max_block = self
|
||||
.blocks
|
||||
.keys()
|
||||
.next_back()
|
||||
.copied()
|
||||
.unwrap_or(next_address)
|
||||
.max(next_address);
|
||||
if entries.iter().any(|&addr| addr > function_start && addr <= max_block)
|
||||
&& !entries.iter().any(|&addr| {
|
||||
self.is_known_function(known_functions, addr)
|
||||
.is_some_and(|fn_addr| fn_addr != function_start)
|
||||
@ -703,7 +747,7 @@ impl FunctionSlices {
|
||||
}
|
||||
}
|
||||
// If we discovered a function prologue, known tail call.
|
||||
if slices.prologue.is_some() {
|
||||
if slices.prologue.is_some() || slices.has_r1_load {
|
||||
log::trace!("Prologue discovered; known tail call: {:#010X}", addr);
|
||||
return TailCallResult::Is;
|
||||
}
|
||||
|
@ -417,9 +417,13 @@ impl Tracker {
|
||||
Ok(ExecCbResult::Continue)
|
||||
}
|
||||
StepResult::Jump(target) => match target {
|
||||
BranchTarget::Return => Ok(ExecCbResult::EndBlock),
|
||||
BranchTarget::Unknown
|
||||
| BranchTarget::Return
|
||||
| BranchTarget::JumpTable { address: RelocationTarget::External, .. } => {
|
||||
let next_addr = ins_addr + 4;
|
||||
if next_addr < function_end {
|
||||
possible_missed_branches.insert(ins_addr + 4, vm.clone_all());
|
||||
}
|
||||
Ok(ExecCbResult::EndBlock)
|
||||
}
|
||||
BranchTarget::Address(addr) => {
|
||||
|
@ -234,6 +234,9 @@ pub struct ProjectConfig {
|
||||
/// Marks all emitted symbols as "exported" to prevent the linker from removing them.
|
||||
#[serde(default = "bool_true", skip_serializing_if = "is_true")]
|
||||
pub export_all: bool,
|
||||
/// Promotes local symbols referenced by other units to global.
|
||||
#[serde(default = "bool_true", skip_serializing_if = "is_true")]
|
||||
pub globalize_symbols: bool,
|
||||
/// Optional base path for all object files.
|
||||
#[serde(with = "unix_path_serde_option", default, skip_serializing_if = "is_default")]
|
||||
pub object_base: Option<Utf8UnixPathBuf>,
|
||||
@ -259,6 +262,7 @@ impl Default for ProjectConfig {
|
||||
symbols_known: false,
|
||||
fill_gaps: true,
|
||||
export_all: true,
|
||||
globalize_symbols: true,
|
||||
object_base: None,
|
||||
extract_objects: true,
|
||||
}
|
||||
@ -313,6 +317,10 @@ pub struct ExtractConfig {
|
||||
/// Path is relative to `out_dir/include`.
|
||||
#[serde(with = "unix_path_serde_option", default, skip_serializing_if = "Option::is_none")]
|
||||
pub header: Option<Utf8UnixPathBuf>,
|
||||
/// If specified, any relocations within the symbol will be written to the given file in JSON
|
||||
/// format. Path is relative to `out_dir/bin`.
|
||||
#[serde(with = "unix_path_serde_option", default, skip_serializing_if = "Option::is_none")]
|
||||
pub relocations: Option<Utf8UnixPathBuf>,
|
||||
/// The type for the extracted symbol in the header file. By default, the header will emit
|
||||
/// a full symbol declaration (a.k.a. `symbol`), but this can be set to `raw` to emit the raw
|
||||
/// data as a byte array. `none` avoids emitting a header entirely, in which case the `header`
|
||||
@ -399,11 +407,24 @@ pub struct OutputExtract {
|
||||
pub binary: Option<Utf8UnixPathBuf>,
|
||||
#[serde(with = "unix_path_serde_option")]
|
||||
pub header: Option<Utf8UnixPathBuf>,
|
||||
#[serde(with = "unix_path_serde_option")]
|
||||
pub relocations: Option<Utf8UnixPathBuf>,
|
||||
pub header_type: String,
|
||||
pub custom_type: Option<String>,
|
||||
pub custom_data: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExtractRelocInfo {
|
||||
offset: u32,
|
||||
#[serde(rename = "type")]
|
||||
kind: ObjRelocKind,
|
||||
target: String,
|
||||
addend: i64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
module: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, Hash)]
|
||||
pub struct OutputLink {
|
||||
pub modules: Vec<String>,
|
||||
@ -538,7 +559,9 @@ pub fn info(args: InfoArgs) -> Result<()> {
|
||||
apply_selfile(&mut obj, file.map()?)?;
|
||||
}
|
||||
|
||||
println!("{}:", obj.name);
|
||||
if !obj.name.is_empty() {
|
||||
println!("{}:", obj.name);
|
||||
}
|
||||
if let Some(entry) = obj.entry {
|
||||
println!("Entry point: {entry:#010X}");
|
||||
}
|
||||
@ -838,7 +861,7 @@ fn load_dol_module(
|
||||
};
|
||||
if config.clean_extab.unwrap_or(false) {
|
||||
log::debug!("Cleaning extab for {}", config.name());
|
||||
clean_extab(&mut obj)?;
|
||||
clean_extab(&mut obj, std::iter::empty())?;
|
||||
}
|
||||
Ok((obj, object_path))
|
||||
}
|
||||
@ -965,7 +988,7 @@ fn split_write_obj(
|
||||
|
||||
debug!("Splitting {} objects", module.obj.link_order.len());
|
||||
let module_name = module.config.name().to_string();
|
||||
let split_objs = split_obj(&module.obj, Some(module_name.as_str()))?;
|
||||
let split_objs = split_obj(&module.obj, Some(module_name.as_str()), config.globalize_symbols)?;
|
||||
|
||||
debug!("Writing object files");
|
||||
DirBuilder::new()
|
||||
@ -1057,12 +1080,35 @@ fn split_write_obj(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(relocations) = &extract.relocations {
|
||||
let start = symbol.address as u32 - section.address as u32;
|
||||
let end = start + symbol.size as u32;
|
||||
let mut reloc_entries = Vec::new();
|
||||
for (addr, reloc) in section.relocations.range(start..end) {
|
||||
let target_symbol = &module.obj.symbols[reloc.target_symbol];
|
||||
reloc_entries.push(ExtractRelocInfo {
|
||||
offset: addr - start,
|
||||
kind: reloc.kind,
|
||||
target: target_symbol.name.clone(),
|
||||
addend: reloc.addend,
|
||||
module: reloc.module,
|
||||
});
|
||||
}
|
||||
let relocations_json = serde_json::to_vec_pretty(&reloc_entries)?;
|
||||
let out_path = base_dir.join("bin").join(relocations.with_encoding());
|
||||
if let Some(parent) = out_path.parent() {
|
||||
DirBuilder::new().recursive(true).create(parent)?;
|
||||
}
|
||||
write_if_changed(&out_path, &relocations_json)?;
|
||||
}
|
||||
|
||||
// Copy to output config
|
||||
out_config.extract.push(OutputExtract {
|
||||
symbol: symbol.name.clone(),
|
||||
rename: extract.rename.clone(),
|
||||
binary: extract.binary.clone(),
|
||||
header: extract.header.clone(),
|
||||
relocations: extract.relocations.clone(),
|
||||
header_type: header_kind.to_string(),
|
||||
custom_type: extract.custom_type.clone(),
|
||||
custom_data: extract.custom_data.clone(),
|
||||
|
@ -136,7 +136,7 @@ fn disasm(args: DisasmArgs) -> Result<()> {
|
||||
match obj.kind {
|
||||
ObjKind::Executable => {
|
||||
log::info!("Splitting {} objects", obj.link_order.len());
|
||||
let split_objs = split_obj(&obj, None)?;
|
||||
let split_objs = split_obj(&obj, None, false)?;
|
||||
|
||||
let asm_dir = args.out.join("asm");
|
||||
let include_dir = args.out.join("include");
|
||||
|
@ -7,7 +7,6 @@ use typed_path::Utf8NativePathBuf;
|
||||
|
||||
use crate::{
|
||||
util::{
|
||||
alf::ALF_MAGIC,
|
||||
dol::{process_dol, write_dol},
|
||||
file::buf_writer,
|
||||
path::native_path,
|
||||
@ -16,11 +15,11 @@ use crate::{
|
||||
};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Converts an ELF (or ALF) file to a DOL file.
|
||||
/// Converts an ELF, ALF, or BootStage file to a DOL file.
|
||||
#[argp(subcommand, name = "elf2dol")]
|
||||
pub struct Args {
|
||||
#[argp(positional, from_str_fn(native_path))]
|
||||
/// path to input ELF or ALF file
|
||||
/// path to input ELF, ALF or BootStage file
|
||||
elf_file: Utf8NativePathBuf,
|
||||
#[argp(positional, from_str_fn(native_path))]
|
||||
/// path to output DOL
|
||||
@ -54,8 +53,8 @@ const MAX_DATA_SECTIONS: usize = 11;
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
let mut file = open_file(&args.elf_file, true)?;
|
||||
let data = file.map()?;
|
||||
if data.len() >= 4 && data[0..4] == ALF_MAGIC {
|
||||
return convert_alf(args, data);
|
||||
if data.len() >= 4 && data[0..4] != object::elf::ELFMAG {
|
||||
return convert_dol_like(args, data);
|
||||
}
|
||||
|
||||
let obj_file = object::read::File::parse(data)?;
|
||||
@ -163,7 +162,8 @@ pub fn run(args: Args) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_alf(args: Args, data: &[u8]) -> Result<()> {
|
||||
/// Converts a DOL-like format (ALF or BootStage) to a DOL file.
|
||||
fn convert_dol_like(args: Args, data: &[u8]) -> Result<()> {
|
||||
let obj = process_dol(data, "")?;
|
||||
let mut out = buf_writer(&args.dol_file)?;
|
||||
write_dol(&obj, &mut out)?;
|
||||
|
@ -30,15 +30,18 @@ enum SubCommand {
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Rewrites extab data in a DOL or ELF file, zeroing out any uninitialized padding bytes.
|
||||
/// Rewrites extab data in a DOL or ELF file, replacing any uninitialized padding bytes.
|
||||
#[argp(subcommand, name = "clean")]
|
||||
pub struct CleanArgs {
|
||||
#[argp(positional, from_str_fn(native_path))]
|
||||
/// path to input file
|
||||
/// Path to input file
|
||||
input: Utf8NativePathBuf,
|
||||
#[argp(positional, from_str_fn(native_path))]
|
||||
/// path to output file
|
||||
/// Path to output file
|
||||
output: Utf8NativePathBuf,
|
||||
#[argp(option, short = 'p')]
|
||||
/// Data to replace padding bytes with, encoded as a hexadecimal string. If not specified, padding bytes will be zeroed instead.
|
||||
padding: Option<String>,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
@ -56,7 +59,13 @@ fn clean_extab(args: CleanArgs) -> Result<()> {
|
||||
let name = args.input.file_stem().unwrap_or_default();
|
||||
process_dol(file.map()?, name)?
|
||||
};
|
||||
let num_cleaned = util::extab::clean_extab(&mut obj)?;
|
||||
let padding: Vec<u8> = match args.padding {
|
||||
None => Vec::new(),
|
||||
Some(padding_str) => {
|
||||
hex::decode(padding_str).context("Failed to decode padding bytes from hex")?
|
||||
}
|
||||
};
|
||||
let num_cleaned = util::extab::clean_extab(&mut obj, padding.iter().copied())?;
|
||||
tracing::debug!("Cleaned {num_cleaned} extab symbols");
|
||||
let mut out = buf_writer(&args.output)?;
|
||||
if is_elf {
|
||||
|
@ -123,7 +123,7 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_obj(buf: &[u8]) -> Result<File> {
|
||||
fn load_obj(buf: &[u8]) -> Result<File<'_>> {
|
||||
let obj = File::parse(buf)?;
|
||||
match obj.architecture() {
|
||||
Architecture::PowerPc => {}
|
||||
|
771
src/util/dol.rs
771
src/util/dol.rs
@ -4,10 +4,13 @@ use std::{
|
||||
io::{Cursor, Read, Seek, SeekFrom, Write},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
analysis::cfa::{locate_bss_memsets, locate_sda_bases, SectionAddress},
|
||||
analysis::cfa::{
|
||||
locate_bss_memsets, locate_cross_section_branch_targets, locate_sda_bases, SectionAddress,
|
||||
},
|
||||
array_ref,
|
||||
obj::{
|
||||
ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
|
||||
@ -209,6 +212,13 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
Box::new(DolFile::from_reader(&mut reader, Endian::Big)?)
|
||||
};
|
||||
|
||||
let mut entry_point = dol.entry_point();
|
||||
let mut is_bootstage = false;
|
||||
if entry_point & 0x80000000 == 0 {
|
||||
entry_point |= 0x80000000;
|
||||
is_bootstage = true;
|
||||
}
|
||||
|
||||
// Locate _rom_copy_info
|
||||
let first_rom_section = dol
|
||||
.sections()
|
||||
@ -216,20 +226,38 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
.find(|section| section.kind != DolSectionKind::Bss)
|
||||
.ok_or_else(|| anyhow!("Failed to locate first rom section"))?;
|
||||
let init_section = dol
|
||||
.section_by_address(dol.entry_point())
|
||||
.section_by_address(entry_point)
|
||||
.ok_or_else(|| anyhow!("Failed to locate .init section"))?;
|
||||
let first_data_section = dol
|
||||
.sections()
|
||||
.iter()
|
||||
.find(|s| s.kind == DolSectionKind::Data)
|
||||
.ok_or_else(|| anyhow!("Failed to locate .data section"))?;
|
||||
let rom_copy_section = if is_bootstage { first_data_section } else { init_section };
|
||||
|
||||
if is_bootstage {
|
||||
// Real entry point is stored at the end of the bootstage init section, always(?) 0x81330000
|
||||
entry_point = read_u32(buf, dol.as_ref(), init_section.address + init_section.size - 4)?;
|
||||
}
|
||||
|
||||
let rom_copy_info_addr = {
|
||||
let mut addr = init_section.address + init_section.size
|
||||
- MAX_ROM_COPY_INFO_SIZE as u32
|
||||
- MAX_BSS_INIT_INFO_SIZE as u32;
|
||||
let mut addr = if is_bootstage {
|
||||
// Start searching from the beginning of the BootStage "data" section
|
||||
rom_copy_section.address
|
||||
} else {
|
||||
// Start searching from the end of the .init section
|
||||
rom_copy_section.address + rom_copy_section.size
|
||||
- MAX_ROM_COPY_INFO_SIZE as u32
|
||||
- MAX_BSS_INIT_INFO_SIZE as u32
|
||||
};
|
||||
loop {
|
||||
let value = read_u32(buf, dol.as_ref(), addr)?;
|
||||
if value == first_rom_section.address {
|
||||
if value == first_rom_section.address || value == entry_point {
|
||||
log::debug!("Found _rom_copy_info @ {addr:#010X}");
|
||||
break Some(addr);
|
||||
}
|
||||
addr += 4;
|
||||
if addr >= init_section.address + init_section.size {
|
||||
if addr >= rom_copy_section.address + rom_copy_section.size {
|
||||
log::warn!("Failed to locate _rom_copy_info");
|
||||
break None;
|
||||
}
|
||||
@ -252,7 +280,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
log::debug!("Found _rom_copy_info end @ {addr:#010X}");
|
||||
break Some(addr);
|
||||
}
|
||||
if addr >= init_section.address + init_section.size {
|
||||
if addr >= rom_copy_section.address + rom_copy_section.size {
|
||||
log::warn!("Failed to locate _rom_copy_info end");
|
||||
break None;
|
||||
}
|
||||
@ -270,12 +298,14 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
let bss_init_info_addr = match rom_copy_info_end {
|
||||
Some(mut addr) => loop {
|
||||
let value = read_u32(buf, dol.as_ref(), addr)?;
|
||||
if value == bss_section.address {
|
||||
if is_bootstage
|
||||
|| (value >= bss_section.address && value < bss_section.address + bss_section.size)
|
||||
{
|
||||
log::debug!("Found _bss_init_info @ {addr:#010X}");
|
||||
break Some(addr);
|
||||
}
|
||||
addr += 4;
|
||||
if addr >= init_section.address + init_section.size {
|
||||
if addr >= rom_copy_section.address + rom_copy_section.size {
|
||||
log::warn!("Failed to locate _bss_init_info");
|
||||
break None;
|
||||
}
|
||||
@ -294,7 +324,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
log::debug!("Found _bss_init_info end @ {addr:#010X}");
|
||||
break Some(addr);
|
||||
}
|
||||
if addr >= init_section.address + init_section.size {
|
||||
if addr >= rom_copy_section.address + rom_copy_section.size {
|
||||
log::warn!("Failed to locate _bss_init_info end");
|
||||
break None;
|
||||
}
|
||||
@ -303,170 +333,105 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Locate _eti_init_info
|
||||
let num_text_sections =
|
||||
dol.sections().iter().filter(|section| section.kind == DolSectionKind::Text).count();
|
||||
let mut eti_entries: Vec<EtiEntry> = Vec::new();
|
||||
let mut eti_init_info_range: Option<(u32, u32)> = None;
|
||||
let mut extab_section: Option<SectionIndex> = None;
|
||||
let mut extabindex_section: Option<SectionIndex> = None;
|
||||
'outer: for dol_section in
|
||||
dol.sections().iter().filter(|section| section.kind == DolSectionKind::Data)
|
||||
{
|
||||
// Use section size from _rom_copy_info
|
||||
let dol_section_size = match rom_sections.get(&dol_section.address) {
|
||||
Some(&size) => size,
|
||||
None => dol_section.size,
|
||||
};
|
||||
let dol_section_end = dol_section.address + dol_section_size;
|
||||
|
||||
let eti_init_info_addr = {
|
||||
let mut addr = dol_section_end - (ETI_INIT_INFO_SIZE * (num_text_sections + 1)) as u32;
|
||||
loop {
|
||||
let eti_init_info = read_eti_init_info(buf, dol.as_ref(), addr)?;
|
||||
if validate_eti_init_info(
|
||||
dol.as_ref(),
|
||||
&eti_init_info,
|
||||
dol_section,
|
||||
dol_section_end,
|
||||
&rom_sections,
|
||||
)? {
|
||||
log::debug!("Found _eti_init_info @ {addr:#010X}");
|
||||
break addr;
|
||||
}
|
||||
addr += 4;
|
||||
if addr > dol_section_end - ETI_INIT_INFO_SIZE as u32 {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let eti_init_info_end = {
|
||||
let mut addr = eti_init_info_addr;
|
||||
loop {
|
||||
let eti_init_info = read_eti_init_info(buf, dol.as_ref(), addr)?;
|
||||
addr += 16;
|
||||
if eti_init_info.is_zero() {
|
||||
break;
|
||||
}
|
||||
if addr > dol_section_end - ETI_INIT_INFO_SIZE as u32 {
|
||||
bail!(
|
||||
"Failed to locate _eti_init_info end (start @ {:#010X})",
|
||||
eti_init_info_addr
|
||||
);
|
||||
}
|
||||
if !validate_eti_init_info(
|
||||
dol.as_ref(),
|
||||
&eti_init_info,
|
||||
dol_section,
|
||||
dol_section_end,
|
||||
&rom_sections,
|
||||
)? {
|
||||
bail!("Invalid _eti_init_info entry: {:#010X?}", eti_init_info);
|
||||
}
|
||||
for addr in (eti_init_info.eti_start..eti_init_info.eti_end).step_by(12) {
|
||||
let eti_entry = read_eti_entry(buf, dol.as_ref(), addr)?;
|
||||
let entry_section =
|
||||
dol.section_by_address(eti_entry.extab_addr).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Failed to locate section for extab address {:#010X}",
|
||||
eti_entry.extab_addr
|
||||
)
|
||||
})?;
|
||||
if let Some(extab_section) = extab_section {
|
||||
ensure!(
|
||||
entry_section.index == extab_section,
|
||||
"Mismatched sections for extabindex entries: {} != {}",
|
||||
entry_section.index,
|
||||
extab_section
|
||||
);
|
||||
} else {
|
||||
extab_section = Some(entry_section.index);
|
||||
}
|
||||
eti_entries.push(eti_entry);
|
||||
}
|
||||
}
|
||||
log::debug!("Found _eti_init_info end @ {addr:#010X}");
|
||||
addr
|
||||
};
|
||||
|
||||
eti_init_info_range = Some((eti_init_info_addr, eti_init_info_end));
|
||||
extabindex_section = Some(dol_section.index);
|
||||
break;
|
||||
}
|
||||
if eti_init_info_range.is_none() {
|
||||
log::debug!("Failed to locate _eti_init_info");
|
||||
}
|
||||
|
||||
// Add text and data sections
|
||||
let mut sections = vec![];
|
||||
for dol_section in dol.sections().iter() {
|
||||
// We'll split .bss later
|
||||
if dol_section.kind == DolSectionKind::Bss && dol.has_unified_bss() {
|
||||
continue;
|
||||
}
|
||||
if is_bootstage {
|
||||
// Create sections based on _rom_copy_info
|
||||
for (idx, (&addr, &size)) in rom_sections.iter().enumerate() {
|
||||
let dol_section = dol
|
||||
.section_by_address(addr)
|
||||
.ok_or_else(|| anyhow!("Failed to locate section for ROM address {addr:#010X}"))?;
|
||||
let data = dol.virtual_data_at(buf, addr, size)?;
|
||||
|
||||
let (name, kind, known) = match dol_section.index {
|
||||
idx if idx == init_section.index => (".init".to_string(), ObjSectionKind::Code, true),
|
||||
idx if Some(idx) == extab_section => {
|
||||
("extab".to_string(), ObjSectionKind::ReadOnlyData, true)
|
||||
}
|
||||
idx if Some(idx) == extabindex_section => {
|
||||
("extabindex".to_string(), ObjSectionKind::ReadOnlyData, true)
|
||||
}
|
||||
_ if num_text_sections == 2 && dol_section.kind == DolSectionKind::Text => {
|
||||
(".text".to_string(), ObjSectionKind::Code, true)
|
||||
}
|
||||
idx => match dol_section.kind {
|
||||
DolSectionKind::Text => (format!(".text{idx}"), ObjSectionKind::Code, false),
|
||||
DolSectionKind::Data => (format!(".data{idx}"), ObjSectionKind::Data, false),
|
||||
DolSectionKind::Bss => (format!(".bss{idx}"), ObjSectionKind::Bss, false),
|
||||
},
|
||||
};
|
||||
|
||||
let (size, data): (u32, &[u8]) = if kind == ObjSectionKind::Bss {
|
||||
(dol_section.size, &[])
|
||||
} else {
|
||||
// Use section size from _rom_copy_info
|
||||
let size = match rom_sections.get(&dol_section.address) {
|
||||
Some(&size) => size,
|
||||
None => {
|
||||
if !rom_sections.is_empty() {
|
||||
log::warn!(
|
||||
"Section {} ({:#010X}) doesn't exist in _rom_copy_info",
|
||||
dol_section.index,
|
||||
dol_section.address
|
||||
);
|
||||
}
|
||||
dol_section.size
|
||||
let (name, kind, known) = if entry_point >= addr && entry_point < addr + size {
|
||||
(".init".to_string(), ObjSectionKind::Code, true)
|
||||
} else {
|
||||
match dol_section.kind {
|
||||
DolSectionKind::Text => (format!(".text{idx}"), ObjSectionKind::Code, false),
|
||||
DolSectionKind::Data => (format!(".data{idx}"), ObjSectionKind::Data, false),
|
||||
DolSectionKind::Bss => (format!(".bss{idx}"), ObjSectionKind::Bss, false),
|
||||
}
|
||||
};
|
||||
(size, dol.virtual_data_at(buf, dol_section.address, size)?)
|
||||
};
|
||||
|
||||
sections.push(ObjSection {
|
||||
name,
|
||||
kind,
|
||||
address: dol_section.address as u64,
|
||||
size: size as u64,
|
||||
data: data.to_vec(),
|
||||
align: 0,
|
||||
elf_index: 0,
|
||||
relocations: Default::default(),
|
||||
virtual_address: Some(dol_section.address as u64),
|
||||
file_offset: dol_section.file_offset as u64,
|
||||
section_known: known,
|
||||
splits: Default::default(),
|
||||
});
|
||||
let file_offset = addr - dol_section.address + dol_section.file_offset;
|
||||
sections.push(ObjSection {
|
||||
name,
|
||||
kind,
|
||||
address: addr as u64,
|
||||
size: size as u64,
|
||||
data: data.to_vec(),
|
||||
align: 0,
|
||||
elf_index: 0,
|
||||
relocations: Default::default(),
|
||||
virtual_address: Some(addr as u64),
|
||||
file_offset: file_offset as u64,
|
||||
section_known: known,
|
||||
splits: Default::default(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for dol_section in dol.sections().iter() {
|
||||
// We'll split .bss later
|
||||
if dol_section.kind == DolSectionKind::Bss && dol.has_unified_bss() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (name, kind, known) = match dol_section.index {
|
||||
idx if idx == init_section.index => {
|
||||
(".init".to_string(), ObjSectionKind::Code, true)
|
||||
}
|
||||
idx => match dol_section.kind {
|
||||
DolSectionKind::Text => (format!(".text{idx}"), ObjSectionKind::Code, false),
|
||||
DolSectionKind::Data => (format!(".data{idx}"), ObjSectionKind::Data, false),
|
||||
DolSectionKind::Bss => (format!(".bss{idx}"), ObjSectionKind::Bss, false),
|
||||
},
|
||||
};
|
||||
|
||||
let (size, data): (u32, &[u8]) = if kind == ObjSectionKind::Bss {
|
||||
(dol_section.size, &[])
|
||||
} else {
|
||||
// Use section size from _rom_copy_info
|
||||
let size = match rom_sections.get(&dol_section.address) {
|
||||
Some(&size) => size,
|
||||
None => {
|
||||
if !rom_sections.is_empty() {
|
||||
log::warn!(
|
||||
"Section {} ({:#010X}) doesn't exist in _rom_copy_info",
|
||||
dol_section.index,
|
||||
dol_section.address
|
||||
);
|
||||
}
|
||||
dol_section.size
|
||||
}
|
||||
};
|
||||
(size, dol.virtual_data_at(buf, dol_section.address, size)?)
|
||||
};
|
||||
|
||||
sections.push(ObjSection {
|
||||
name,
|
||||
kind,
|
||||
address: dol_section.address as u64,
|
||||
size: size as u64,
|
||||
data: data.to_vec(),
|
||||
align: 0,
|
||||
elf_index: 0,
|
||||
relocations: Default::default(),
|
||||
virtual_address: Some(dol_section.address as u64),
|
||||
file_offset: dol_section.file_offset as u64,
|
||||
section_known: known,
|
||||
splits: Default::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if dol.has_unified_bss() {
|
||||
// Add BSS sections from _bss_init_info
|
||||
for (idx, (&addr, &size)) in bss_sections.iter().enumerate() {
|
||||
ensure!(
|
||||
addr >= bss_section.address
|
||||
&& addr < bss_section.address + bss_section.size
|
||||
&& addr + size <= bss_section.address + bss_section.size,
|
||||
is_bootstage
|
||||
|| (addr >= bss_section.address
|
||||
&& addr < bss_section.address + bss_section.size
|
||||
&& addr + size <= bss_section.address + bss_section.size),
|
||||
"Invalid BSS range {:#010X}-{:#010X} (DOL BSS: {:#010X}-{:#010X})",
|
||||
addr,
|
||||
addr + size,
|
||||
@ -515,8 +480,8 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
vec![],
|
||||
temp_sections,
|
||||
);
|
||||
obj.entry = Some(dol.entry_point() as u64);
|
||||
let bss_sections = locate_bss_memsets(&mut obj)?;
|
||||
obj.entry = Some(entry_point as u64);
|
||||
let bss_sections = locate_bss_memsets(&obj)?;
|
||||
match bss_sections.len() {
|
||||
0 => log::warn!("Failed to locate BSS sections"),
|
||||
2 => {
|
||||
@ -559,24 +524,10 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
}
|
||||
|
||||
// Apply section indices
|
||||
let mut init_section_index: Option<SectionIndex> = None;
|
||||
for (idx, section) in sections.iter_mut().enumerate() {
|
||||
let idx = idx as SectionIndex;
|
||||
match section.name.as_str() {
|
||||
".init" => {
|
||||
init_section_index = Some(idx);
|
||||
}
|
||||
"extab" => {
|
||||
extab_section = Some(idx);
|
||||
}
|
||||
"extabindex" => {
|
||||
extabindex_section = Some(idx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Assume the original ELF section index is +1
|
||||
// ELF files start with a NULL section
|
||||
section.elf_index = idx + 1;
|
||||
section.elf_index = (idx as SectionIndex) + 1;
|
||||
}
|
||||
|
||||
// Guess section alignment
|
||||
@ -588,12 +539,13 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
align = (align + 1).next_power_of_two();
|
||||
}
|
||||
if align_up(last_section_end, align) != section_start {
|
||||
bail!(
|
||||
log::warn!(
|
||||
"Couldn't determine alignment for section '{}' ({:#010X} -> {:#010X})",
|
||||
section.name,
|
||||
last_section_end,
|
||||
section_start
|
||||
);
|
||||
align = 32;
|
||||
}
|
||||
last_section_end = section_start + section.size as u32;
|
||||
section.align = align as u64;
|
||||
@ -607,17 +559,18 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
vec![],
|
||||
sections,
|
||||
);
|
||||
obj.entry = Some(dol.entry_point() as u64);
|
||||
obj.entry = Some(entry_point as u64);
|
||||
|
||||
// Generate _rom_copy_info symbol
|
||||
if let (Some(rom_copy_info_addr), Some(rom_copy_info_end)) =
|
||||
(rom_copy_info_addr, rom_copy_info_end)
|
||||
{
|
||||
let (section_index, _) = obj.sections.at_address(rom_copy_info_addr)?;
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_rom_copy_info".to_string(),
|
||||
address: rom_copy_info_addr as u64,
|
||||
section: init_section_index,
|
||||
section: Some(section_index),
|
||||
size: (rom_copy_info_end - rom_copy_info_addr) as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
@ -632,11 +585,12 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
if let (Some(bss_init_info_addr), Some(bss_init_info_end)) =
|
||||
(bss_init_info_addr, bss_init_info_end)
|
||||
{
|
||||
let (section_index, _) = obj.sections.at_address(bss_init_info_addr)?;
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_bss_init_info".to_string(),
|
||||
address: bss_init_info_addr as u64,
|
||||
section: init_section_index,
|
||||
section: Some(section_index),
|
||||
size: (bss_init_info_end - bss_init_info_addr) as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
@ -647,150 +601,24 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
|
||||
)?;
|
||||
}
|
||||
|
||||
// Generate _eti_init_info symbol
|
||||
if let Some((eti_init_info_addr, eti_init_info_end)) = eti_init_info_range {
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_eti_init_info".to_string(),
|
||||
address: eti_init_info_addr as u64,
|
||||
section: extabindex_section,
|
||||
size: (eti_init_info_end - eti_init_info_addr) as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
kind: ObjSymbolKind::Object,
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
// Locate .text section
|
||||
if let Err(e) = locate_text(&mut obj) {
|
||||
log::warn!("Failed to locate .text section: {:?}", e);
|
||||
}
|
||||
|
||||
// Generate symbols for extab & extabindex entries
|
||||
if let (Some(extabindex_section_index), Some(extab_section_index)) =
|
||||
(extabindex_section, extab_section)
|
||||
{
|
||||
let extab_section = &obj.sections[extab_section_index];
|
||||
let extab_section_address = extab_section.address;
|
||||
let extab_section_size = extab_section.size;
|
||||
|
||||
for entry in &eti_entries {
|
||||
// Add functions from extabindex entries as known function bounds
|
||||
let (section_index, _) = obj.sections.at_address(entry.function).map_err(|_| {
|
||||
anyhow!(
|
||||
"Failed to locate section for function {:#010X} (referenced from extabindex entry {:#010X})",
|
||||
entry.function,
|
||||
entry.address,
|
||||
)
|
||||
})?;
|
||||
let addr = SectionAddress::new(section_index, entry.function);
|
||||
if let Some(Some(old_value)) =
|
||||
obj.known_functions.insert(addr, Some(entry.function_size))
|
||||
{
|
||||
if old_value != entry.function_size {
|
||||
log::warn!(
|
||||
"Conflicting sizes for {:#010X}: {:#X} != {:#X}",
|
||||
entry.function,
|
||||
entry.function_size,
|
||||
old_value
|
||||
);
|
||||
}
|
||||
}
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: format!("@eti_{:08X}", entry.address),
|
||||
address: entry.address as u64,
|
||||
section: Some(extabindex_section_index),
|
||||
size: 12,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local | ObjSymbolFlags::Hidden),
|
||||
kind: ObjSymbolKind::Object,
|
||||
..Default::default()
|
||||
},
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut entry_iter = eti_entries.iter().peekable();
|
||||
loop {
|
||||
let (addr, size) = match (entry_iter.next(), entry_iter.peek()) {
|
||||
(Some(a), Some(&b)) => (a.extab_addr, b.extab_addr - a.extab_addr),
|
||||
(Some(a), None) => (
|
||||
a.extab_addr,
|
||||
(extab_section_address + extab_section_size) as u32 - a.extab_addr,
|
||||
),
|
||||
_ => break,
|
||||
};
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: format!("@etb_{addr:08X}"),
|
||||
address: addr as u64,
|
||||
section: Some(extab_section_index),
|
||||
size: size as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local | ObjSymbolFlags::Hidden),
|
||||
kind: ObjSymbolKind::Object,
|
||||
..Default::default()
|
||||
},
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
// Locate extab and extabindex sections
|
||||
if let Err(e) = locate_extab_extabindex(&mut obj) {
|
||||
log::warn!("Failed to locate extab/etabindex: {:?}", e);
|
||||
}
|
||||
|
||||
// Add .ctors and .dtors functions to known functions if they exist
|
||||
for (_, section) in obj.sections.iter() {
|
||||
if section.size & 3 != 0 {
|
||||
continue;
|
||||
}
|
||||
let mut entries = vec![];
|
||||
let mut current_addr = section.address as u32;
|
||||
for chunk in section.data.chunks_exact(4) {
|
||||
let addr = u32::from_be_bytes(chunk.try_into()?);
|
||||
if addr == 0 || addr & 3 != 0 {
|
||||
break;
|
||||
}
|
||||
let Ok((section_index, section)) = obj.sections.at_address(addr) else {
|
||||
break;
|
||||
};
|
||||
if section.kind != ObjSectionKind::Code {
|
||||
break;
|
||||
}
|
||||
entries.push(SectionAddress::new(section_index, addr));
|
||||
current_addr += 4;
|
||||
}
|
||||
// .ctors and .dtors end with a null pointer
|
||||
if current_addr != (section.address + section.size) as u32 - 4
|
||||
|| section.data_range(current_addr, 0)?.iter().any(|&b| b != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
obj.known_functions.extend(entries.into_iter().map(|addr| (addr, None)));
|
||||
// Locate .ctors and .dtors sections
|
||||
if let Err(e) = locate_ctors_dtors(&mut obj) {
|
||||
log::warn!("Failed to locate .ctors/.dtors: {:?}", e);
|
||||
}
|
||||
|
||||
// Locate _SDA2_BASE_ & _SDA_BASE_
|
||||
match locate_sda_bases(&mut obj) {
|
||||
Ok(true) => {
|
||||
let sda2_base = obj.sda2_base.unwrap();
|
||||
let sda_base = obj.sda_base.unwrap();
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_SDA2_BASE_".to_string(),
|
||||
address: sda2_base as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_SDA_BASE_".to_string(),
|
||||
address: sda_base as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
log::warn!("Unable to locate SDA bases");
|
||||
}
|
||||
@ -830,39 +658,42 @@ struct EtiEntry {
|
||||
extab_addr: u32,
|
||||
}
|
||||
|
||||
fn read_eti_init_info(buf: &[u8], dol: &dyn DolLike, addr: u32) -> Result<EtiInitInfo> {
|
||||
let eti_start = read_u32(buf, dol, addr)?;
|
||||
let eti_end = read_u32(buf, dol, addr + 4)?;
|
||||
let code_start = read_u32(buf, dol, addr + 8)?;
|
||||
let code_size = read_u32(buf, dol, addr + 12)?;
|
||||
fn read_u32_obj(obj: &ObjInfo, addr: u32) -> Result<u32> {
|
||||
let (_, section) = obj.sections.at_address(addr)?;
|
||||
let data = section.data_range(addr, addr + 4)?;
|
||||
Ok(u32::from_be_bytes(data.try_into()?))
|
||||
}
|
||||
|
||||
fn read_eti_init_info(obj: &ObjInfo, addr: u32) -> Result<EtiInitInfo> {
|
||||
let eti_start = read_u32_obj(obj, addr)?;
|
||||
let eti_end = read_u32_obj(obj, addr + 4)?;
|
||||
let code_start = read_u32_obj(obj, addr + 8)?;
|
||||
let code_size = read_u32_obj(obj, addr + 12)?;
|
||||
Ok(EtiInitInfo { eti_start, eti_end, code_start, code_size })
|
||||
}
|
||||
|
||||
fn read_eti_entry(buf: &[u8], dol: &dyn DolLike, address: u32) -> Result<EtiEntry> {
|
||||
let function = read_u32(buf, dol, address)?;
|
||||
let function_size = read_u32(buf, dol, address + 4)?;
|
||||
let extab_addr = read_u32(buf, dol, address + 8)?;
|
||||
fn read_eti_entry(obj: &ObjInfo, address: u32) -> Result<EtiEntry> {
|
||||
let function = read_u32_obj(obj, address)?;
|
||||
let function_size = read_u32_obj(obj, address + 4)?;
|
||||
let extab_addr = read_u32_obj(obj, address + 8)?;
|
||||
Ok(EtiEntry { address, function, function_size, extab_addr })
|
||||
}
|
||||
|
||||
fn validate_eti_init_info(
|
||||
dol: &dyn DolLike,
|
||||
obj: &ObjInfo,
|
||||
eti_init_info: &EtiInitInfo,
|
||||
eti_section: &DolSection,
|
||||
eti_section_addr: u32,
|
||||
eti_section_end: u32,
|
||||
rom_sections: &BTreeMap<u32, u32>,
|
||||
) -> Result<bool> {
|
||||
if eti_init_info.eti_start >= eti_section.address
|
||||
if eti_init_info.eti_start >= eti_section_addr
|
||||
&& eti_init_info.eti_start < eti_section_end
|
||||
&& eti_init_info.eti_end >= eti_section.address
|
||||
&& eti_init_info.eti_end >= eti_section_addr
|
||||
&& eti_init_info.eti_end < eti_section_end
|
||||
{
|
||||
if let Some(code_section) = dol.section_by_address(eti_init_info.code_start) {
|
||||
let code_section_size = match rom_sections.get(&code_section.address) {
|
||||
Some(&size) => size,
|
||||
None => code_section.size,
|
||||
};
|
||||
if eti_init_info.code_size <= code_section_size {
|
||||
if let Ok((_, code_section)) = obj.sections.at_address(eti_init_info.code_start) {
|
||||
if eti_init_info.code_start + eti_init_info.code_size
|
||||
<= (code_section.address + code_section.size) as u32
|
||||
{
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
@ -945,3 +776,261 @@ where T: Write + ?Sized {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rename_section(
|
||||
obj: &mut ObjInfo,
|
||||
index: SectionIndex,
|
||||
name: &str,
|
||||
kind: ObjSectionKind,
|
||||
) -> Result<()> {
|
||||
let section = &mut obj.sections[index];
|
||||
ensure!(
|
||||
!section.section_known,
|
||||
"Section {} is already known, cannot rename to {}",
|
||||
section.name,
|
||||
name
|
||||
);
|
||||
section.name = name.to_string();
|
||||
section.kind = kind;
|
||||
section.section_known = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn locate_text(obj: &mut ObjInfo) -> Result<()> {
|
||||
// If we didn't find .text, infer from branch targets from .init section
|
||||
if !obj.sections.iter().any(|(_, s)| s.section_known && s.name == ".text") {
|
||||
let entry_point = obj.entry.ok_or_else(|| anyhow!("Missing entry point"))? as u32;
|
||||
let (entry_section_index, _) = obj.sections.at_address(entry_point)?;
|
||||
let entry = SectionAddress::new(entry_section_index, entry_point);
|
||||
let branch_targets = locate_cross_section_branch_targets(obj, entry)?;
|
||||
let mut section_indexes = branch_targets.iter().map(|s| s.section).dedup();
|
||||
let text_section_index =
|
||||
section_indexes.next().ok_or_else(|| anyhow!("Failed to locate .text section"))?;
|
||||
ensure!(section_indexes.next().is_none(), "Multiple possible .text sections found");
|
||||
rename_section(obj, text_section_index, ".text", ObjSectionKind::Code)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn locate_ctors_dtors(obj: &mut ObjInfo) -> Result<()> {
|
||||
// Add .ctors and .dtors functions to known functions if they exist
|
||||
let mut ctors_section_index = None;
|
||||
let mut dtors_section_index = None;
|
||||
for (section_index, section) in obj.sections.iter() {
|
||||
if section.size & 3 != 0 {
|
||||
continue;
|
||||
}
|
||||
let mut entries = vec![];
|
||||
let mut current_addr = section.address as u32;
|
||||
for chunk in section.data.chunks_exact(4) {
|
||||
let addr = u32::from_be_bytes(chunk.try_into()?);
|
||||
if addr == 0 || addr & 3 != 0 {
|
||||
break;
|
||||
}
|
||||
let Ok((section_index, section)) = obj.sections.at_address(addr) else {
|
||||
break;
|
||||
};
|
||||
if section.kind != ObjSectionKind::Code {
|
||||
break;
|
||||
}
|
||||
entries.push(SectionAddress::new(section_index, addr));
|
||||
current_addr += 4;
|
||||
}
|
||||
// .ctors and .dtors end with a null pointer
|
||||
if current_addr != (section.address + section.size) as u32 - 4
|
||||
|| section.data_range(current_addr, 0)?.iter().any(|&b| b != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
obj.known_functions.extend(entries.into_iter().map(|addr| (addr, None)));
|
||||
match (ctors_section_index, dtors_section_index) {
|
||||
(None, None) => ctors_section_index = Some(section_index),
|
||||
(Some(_), None) => dtors_section_index = Some(section_index),
|
||||
_ => log::warn!("Multiple possible .ctors/.dtors sections found"),
|
||||
}
|
||||
}
|
||||
if let Some(ctors_section) = ctors_section_index {
|
||||
rename_section(obj, ctors_section, ".ctors", ObjSectionKind::ReadOnlyData)?;
|
||||
}
|
||||
if let Some(dtors_section) = dtors_section_index {
|
||||
rename_section(obj, dtors_section, ".dtors", ObjSectionKind::ReadOnlyData)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn locate_extab_extabindex(obj: &mut ObjInfo) -> Result<()> {
|
||||
let num_text_sections =
|
||||
obj.sections.iter().filter(|(_, section)| section.kind == ObjSectionKind::Code).count();
|
||||
let mut eti_entries: Vec<EtiEntry> = Vec::new();
|
||||
let mut eti_init_info_range: Option<(u32, u32)> = None;
|
||||
let mut extab_section_index: Option<SectionIndex> = None;
|
||||
let mut extabindex_section_index: Option<SectionIndex> = None;
|
||||
'outer: for (dol_section_index, dol_section) in
|
||||
obj.sections.iter().filter(|(_, section)| section.kind == ObjSectionKind::Data)
|
||||
{
|
||||
let dol_section_start = dol_section.address as u32;
|
||||
let dol_section_end = dol_section_start + dol_section.size as u32;
|
||||
let eti_init_info_addr = {
|
||||
let mut addr = dol_section_end - (ETI_INIT_INFO_SIZE * (num_text_sections + 1)) as u32;
|
||||
loop {
|
||||
let eti_init_info = read_eti_init_info(obj, addr)?;
|
||||
if validate_eti_init_info(obj, &eti_init_info, dol_section_start, dol_section_end)?
|
||||
{
|
||||
log::debug!("Found _eti_init_info @ {addr:#010X}");
|
||||
break addr;
|
||||
}
|
||||
addr += 4;
|
||||
if addr > dol_section_end - ETI_INIT_INFO_SIZE as u32 {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let eti_init_info_end = {
|
||||
let mut addr = eti_init_info_addr;
|
||||
loop {
|
||||
let eti_init_info = read_eti_init_info(obj, addr)?;
|
||||
addr += 16;
|
||||
if eti_init_info.is_zero() {
|
||||
break;
|
||||
}
|
||||
if addr > dol_section_end - ETI_INIT_INFO_SIZE as u32 {
|
||||
bail!(
|
||||
"Failed to locate _eti_init_info end (start @ {:#010X})",
|
||||
eti_init_info_addr
|
||||
);
|
||||
}
|
||||
if !validate_eti_init_info(obj, &eti_init_info, dol_section_start, dol_section_end)?
|
||||
{
|
||||
bail!("Invalid _eti_init_info entry: {:#010X?}", eti_init_info);
|
||||
}
|
||||
for addr in (eti_init_info.eti_start..eti_init_info.eti_end).step_by(12) {
|
||||
let eti_entry = read_eti_entry(obj, addr)?;
|
||||
let (entry_section_index, _) =
|
||||
obj.sections.at_address(eti_entry.extab_addr).with_context(|| {
|
||||
format!(
|
||||
"Failed to locate section for extab address {:#010X}",
|
||||
eti_entry.extab_addr
|
||||
)
|
||||
})?;
|
||||
if let Some(extab_section) = extab_section_index {
|
||||
ensure!(
|
||||
entry_section_index == extab_section,
|
||||
"Mismatched sections for extabindex entries: {} != {}",
|
||||
entry_section_index,
|
||||
extab_section
|
||||
);
|
||||
} else {
|
||||
extab_section_index = Some(entry_section_index);
|
||||
}
|
||||
eti_entries.push(eti_entry);
|
||||
}
|
||||
}
|
||||
log::debug!("Found _eti_init_info end @ {addr:#010X}");
|
||||
addr
|
||||
};
|
||||
|
||||
eti_init_info_range = Some((eti_init_info_addr, eti_init_info_end));
|
||||
extabindex_section_index = Some(dol_section_index);
|
||||
break;
|
||||
}
|
||||
if eti_init_info_range.is_none() {
|
||||
log::debug!("Failed to locate _eti_init_info");
|
||||
}
|
||||
if let Some(extab_section_index) = extab_section_index {
|
||||
rename_section(obj, extab_section_index, "extab", ObjSectionKind::ReadOnlyData)?;
|
||||
}
|
||||
if let Some(extabindex_section_index) = extabindex_section_index {
|
||||
rename_section(obj, extabindex_section_index, "extabindex", ObjSectionKind::ReadOnlyData)?;
|
||||
}
|
||||
|
||||
// Generate _eti_init_info symbol
|
||||
if let Some((eti_init_info_addr, eti_init_info_end)) = eti_init_info_range {
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_eti_init_info".to_string(),
|
||||
address: eti_init_info_addr as u64,
|
||||
section: extabindex_section_index,
|
||||
size: (eti_init_info_end - eti_init_info_addr) as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
kind: ObjSymbolKind::Object,
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Generate symbols for extab & extabindex entries
|
||||
if let (Some(extabindex_section_index), Some(extab_section_index)) =
|
||||
(extabindex_section_index, extab_section_index)
|
||||
{
|
||||
let extab_section = &obj.sections[extab_section_index];
|
||||
let extab_section_address = extab_section.address;
|
||||
let extab_section_size = extab_section.size;
|
||||
|
||||
for entry in &eti_entries {
|
||||
// Add functions from extabindex entries as known function bounds
|
||||
let (section_index, _) = obj.sections.at_address(entry.function).map_err(|_| {
|
||||
anyhow!(
|
||||
"Failed to locate section for function {:#010X} (referenced from extabindex entry {:#010X})",
|
||||
entry.function,
|
||||
entry.address,
|
||||
)
|
||||
})?;
|
||||
let addr = SectionAddress::new(section_index, entry.function);
|
||||
if let Some(Some(old_value)) =
|
||||
obj.known_functions.insert(addr, Some(entry.function_size))
|
||||
{
|
||||
if old_value != entry.function_size {
|
||||
log::warn!(
|
||||
"Conflicting sizes for {:#010X}: {:#X} != {:#X}",
|
||||
entry.function,
|
||||
entry.function_size,
|
||||
old_value
|
||||
);
|
||||
}
|
||||
}
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: format!("@eti_{:08X}", entry.address),
|
||||
address: entry.address as u64,
|
||||
section: Some(extabindex_section_index),
|
||||
size: 12,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local | ObjSymbolFlags::Hidden),
|
||||
kind: ObjSymbolKind::Object,
|
||||
..Default::default()
|
||||
},
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut entry_iter = eti_entries.iter().peekable();
|
||||
loop {
|
||||
let (addr, size) = match (entry_iter.next(), entry_iter.peek()) {
|
||||
(Some(a), Some(&b)) => (a.extab_addr, b.extab_addr - a.extab_addr),
|
||||
(Some(a), None) => (
|
||||
a.extab_addr,
|
||||
(extab_section_address + extab_section_size) as u32 - a.extab_addr,
|
||||
),
|
||||
_ => break,
|
||||
};
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: format!("@etb_{addr:08X}"),
|
||||
address: addr as u64,
|
||||
section: Some(extab_section_index),
|
||||
size: size as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local | ObjSymbolFlags::Hidden),
|
||||
kind: ObjSymbolKind::Object,
|
||||
..Default::default()
|
||||
},
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -358,6 +358,7 @@ pub struct Tag {
|
||||
pub kind: TagKind,
|
||||
pub is_erased: bool, // Tag was deleted but has been reconstructed
|
||||
pub is_erased_root: bool, // Tag is erased and is the root of a tree of erased tags
|
||||
pub data_endian: Endian, // Endianness of the tag data (could be different from the address endianness for erased tags)
|
||||
pub attributes: Vec<Attribute>,
|
||||
}
|
||||
|
||||
@ -554,6 +555,7 @@ where
|
||||
kind: TagKind::Padding,
|
||||
is_erased,
|
||||
is_erased_root: false,
|
||||
data_endian,
|
||||
attributes: Vec::new(),
|
||||
});
|
||||
return Ok(tags);
|
||||
@ -563,26 +565,42 @@ where
|
||||
let tag = TagKind::try_from(tag_num).context("Unknown DWARF tag type")?;
|
||||
if tag == TagKind::Padding {
|
||||
if include_erased {
|
||||
// Erased entries that have become padding are little-endian, and we
|
||||
// have to guess the length and tag of the first entry. We assume
|
||||
// the entry is either a variable or a function, and read until we
|
||||
// find the high_pc attribute. Only MwGlobalRef will follow, and
|
||||
// these are unlikely to be confused with the length of the next
|
||||
// entry.
|
||||
// Erased entries that have become padding could be either
|
||||
// little-endian or big-endian, and we have to guess the length and
|
||||
// tag of the first entry. We assume the entry is either a variable
|
||||
// or a function, and read until we find the high_pc attribute. Only
|
||||
// MwGlobalRef will follow, and these are unlikely to be confused
|
||||
// with the length of the next entry.
|
||||
let mut attributes = Vec::new();
|
||||
let mut is_function = false;
|
||||
|
||||
// Guess endianness based on first attribute
|
||||
let data_endian = if is_erased {
|
||||
data_endian
|
||||
} else {
|
||||
// Peek next two bytes
|
||||
let mut buf = [0u8; 2];
|
||||
reader.read_exact(&mut buf)?;
|
||||
let attr_tag = u16::from_reader(&mut Cursor::new(&buf), data_endian)?;
|
||||
reader.seek(SeekFrom::Current(-2))?;
|
||||
match AttributeKind::try_from(attr_tag) {
|
||||
Ok(_) => data_endian,
|
||||
Err(_) => data_endian.flip(),
|
||||
}
|
||||
};
|
||||
|
||||
while reader.stream_position()? < position + size as u64 {
|
||||
// Peek next two bytes
|
||||
let mut buf = [0u8; 2];
|
||||
reader.read_exact(&mut buf)?;
|
||||
let attr_tag = u16::from_reader(&mut Cursor::new(&buf), Endian::Little)?;
|
||||
let attr_tag = u16::from_reader(&mut Cursor::new(&buf), data_endian)?;
|
||||
reader.seek(SeekFrom::Current(-2))?;
|
||||
|
||||
if is_function && attr_tag != AttributeKind::MwGlobalRef as u16 {
|
||||
break;
|
||||
}
|
||||
|
||||
let attr = read_attribute(reader, Endian::Little, addr_endian)?;
|
||||
let attr = read_attribute(reader, data_endian, addr_endian)?;
|
||||
if attr.kind == AttributeKind::HighPc {
|
||||
is_function = true;
|
||||
}
|
||||
@ -594,12 +612,13 @@ where
|
||||
kind,
|
||||
is_erased: true,
|
||||
is_erased_root: true,
|
||||
data_endian,
|
||||
attributes,
|
||||
});
|
||||
|
||||
// Read the rest of the tags
|
||||
while reader.stream_position()? < position + size as u64 {
|
||||
for tag in read_tags(reader, Endian::Little, addr_endian, include_erased, true)? {
|
||||
for tag in read_tags(reader, data_endian, addr_endian, include_erased, true)? {
|
||||
tags.push(tag);
|
||||
}
|
||||
}
|
||||
@ -616,6 +635,7 @@ where
|
||||
kind: tag,
|
||||
is_erased,
|
||||
is_erased_root: false,
|
||||
data_endian,
|
||||
attributes,
|
||||
});
|
||||
}
|
||||
@ -2028,9 +2048,9 @@ fn process_array_tag(info: &DwarfInfo, tag: &Tag) -> Result<ArrayType> {
|
||||
(AttributeKind::Sibling, _) => {}
|
||||
(AttributeKind::SubscrData, AttributeValue::Block(data)) => {
|
||||
subscr_data =
|
||||
Some(process_array_subscript_data(data, info.e, tag.is_erased).with_context(
|
||||
|| format!("Failed to process SubscrData for tag: {tag:?}"),
|
||||
)?)
|
||||
Some(process_array_subscript_data(data, info.e).with_context(|| {
|
||||
format!("Failed to process SubscrData for tag: {tag:?}")
|
||||
})?)
|
||||
}
|
||||
(AttributeKind::Ordering, val) => match val {
|
||||
AttributeValue::Data2(d2) => {
|
||||
@ -2056,11 +2076,7 @@ fn process_array_tag(info: &DwarfInfo, tag: &Tag) -> Result<ArrayType> {
|
||||
Ok(ArrayType { element_type: Box::from(element_type), dimensions })
|
||||
}
|
||||
|
||||
fn process_array_subscript_data(
|
||||
data: &[u8],
|
||||
e: Endian,
|
||||
is_erased: bool,
|
||||
) -> Result<(Type, Vec<ArrayDimension>)> {
|
||||
fn process_array_subscript_data(data: &[u8], e: Endian) -> Result<(Type, Vec<ArrayDimension>)> {
|
||||
let mut element_type = None;
|
||||
let mut dimensions = Vec::new();
|
||||
let mut data = data;
|
||||
@ -2101,8 +2117,7 @@ fn process_array_subscript_data(
|
||||
SubscriptFormat::ElementType => {
|
||||
let mut cursor = Cursor::new(data);
|
||||
// TODO: is this the right endianness to use for erased tags?
|
||||
let type_attr =
|
||||
read_attribute(&mut cursor, if is_erased { Endian::Little } else { e }, e)?;
|
||||
let type_attr = read_attribute(&mut cursor, e, e)?;
|
||||
element_type = Some(process_type(&type_attr, e)?);
|
||||
data = &data[cursor.position() as usize..];
|
||||
}
|
||||
@ -2127,9 +2142,16 @@ fn process_enumeration_tag(info: &DwarfInfo, tag: &Tag) -> Result<EnumerationTyp
|
||||
(AttributeKind::ElementList, AttributeValue::Block(data)) => {
|
||||
let mut cursor = Cursor::new(data);
|
||||
while cursor.position() < data.len() as u64 {
|
||||
let value = i32::from_reader(&mut cursor, info.e)?;
|
||||
let value = match byte_size {
|
||||
Some(1) => Some(i8::from_reader(&mut cursor, info.e)? as i32),
|
||||
Some(2) => Some(i16::from_reader(&mut cursor, info.e)? as i32),
|
||||
Some(4) => Some(i32::from_reader(&mut cursor, info.e)?),
|
||||
_ => None,
|
||||
};
|
||||
let name = read_string(&mut cursor)?;
|
||||
members.push(EnumerationMember { name, value });
|
||||
if let Some(value) = value {
|
||||
members.push(EnumerationMember { name, value });
|
||||
}
|
||||
}
|
||||
}
|
||||
(AttributeKind::Member, &AttributeValue::Reference(_key)) => {
|
||||
@ -2456,10 +2478,7 @@ fn process_subroutine_parameter_tag(info: &DwarfInfo, tag: &Tag) -> Result<Subro
|
||||
) => kind = Some(process_type(attr, info.e)?),
|
||||
(AttributeKind::Location, AttributeValue::Block(block)) => {
|
||||
if !block.is_empty() {
|
||||
location = Some(process_variable_location(
|
||||
block,
|
||||
if tag.is_erased { Endian::Little } else { info.e },
|
||||
)?);
|
||||
location = Some(process_variable_location(block, tag.data_endian)?);
|
||||
}
|
||||
}
|
||||
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {
|
||||
@ -2514,10 +2533,7 @@ fn process_local_variable_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineV
|
||||
) => kind = Some(process_type(attr, info.e)?),
|
||||
(AttributeKind::Location, AttributeValue::Block(block)) => {
|
||||
if !block.is_empty() {
|
||||
location = Some(process_variable_location(
|
||||
block,
|
||||
if tag.is_erased { Endian::Little } else { info.e },
|
||||
)?);
|
||||
location = Some(process_variable_location(block, tag.data_endian)?);
|
||||
}
|
||||
}
|
||||
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {
|
||||
|
@ -3,7 +3,7 @@ use itertools::Itertools;
|
||||
|
||||
use crate::obj::ObjInfo;
|
||||
|
||||
pub fn clean_extab(obj: &mut ObjInfo) -> Result<usize> {
|
||||
pub fn clean_extab(obj: &mut ObjInfo, mut padding: impl Iterator<Item = u8>) -> Result<usize> {
|
||||
let (extab_section_index, extab_section) = obj
|
||||
.sections
|
||||
.iter_mut()
|
||||
@ -27,19 +27,25 @@ pub fn clean_extab(obj: &mut ObjInfo) -> Result<usize> {
|
||||
})?;
|
||||
let mut updated = false;
|
||||
for action in &decoded.exception_actions {
|
||||
let section_offset =
|
||||
(symbol.address - extab_section.address) as usize + action.action_offset as usize;
|
||||
let clean_data = action.get_exaction_bytes(true);
|
||||
let orig_data =
|
||||
&mut extab_section.data[section_offset..section_offset + clean_data.len()];
|
||||
if orig_data != clean_data {
|
||||
updated = true;
|
||||
// Check if the current action has padding
|
||||
if let Some(padding_offset) = action.get_struct_padding_offset() {
|
||||
let index = padding_offset as usize;
|
||||
let section_offset = (symbol.address - extab_section.address) as usize
|
||||
+ action.action_offset as usize;
|
||||
let mut clean_data: Vec<u8> = action.get_exaction_bytes(false);
|
||||
// Write the two padding bytes
|
||||
clean_data[index] = padding.next().unwrap_or(0);
|
||||
clean_data[index + 1] = padding.next().unwrap_or(0);
|
||||
|
||||
let orig_data =
|
||||
&mut extab_section.data[section_offset..section_offset + clean_data.len()];
|
||||
orig_data.copy_from_slice(&clean_data);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if updated {
|
||||
tracing::debug!(
|
||||
"Removed uninitialized bytes in {} (extab {:#010X}..{:#010X})",
|
||||
"Replaced uninitialized bytes in {} (extab {:#010X}..{:#010X})",
|
||||
symbol.name,
|
||||
symbol.address,
|
||||
symbol.address + symbol.size
|
||||
|
@ -144,7 +144,7 @@ pub fn touch(path: &Utf8NativePath) -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decompress_if_needed(buf: &[u8]) -> Result<Bytes> {
|
||||
pub fn decompress_if_needed(buf: &[u8]) -> Result<Bytes<'_>> {
|
||||
if buf.len() > 4 {
|
||||
match *array_ref!(buf, 0, 4) {
|
||||
YAZ0_MAGIC => return decompress_yaz0(buf).map(Bytes::Owned),
|
||||
|
@ -200,7 +200,7 @@ impl<'a> RarcView<'a> {
|
||||
}
|
||||
|
||||
/// Get a string from the string table at the given offset.
|
||||
pub fn get_string(&self, offset: u32) -> Result<Cow<str>, String> {
|
||||
pub fn get_string(&self, offset: u32) -> Result<Cow<'_, str>, String> {
|
||||
let name_buf = self.string_table.get(offset as usize..).ok_or_else(|| {
|
||||
format!(
|
||||
"RARC: name offset {} out of bounds (string table size: {})",
|
||||
|
@ -20,6 +20,15 @@ impl From<object::Endianness> for Endian {
|
||||
}
|
||||
}
|
||||
|
||||
impl Endian {
|
||||
pub fn flip(self) -> Self {
|
||||
match self {
|
||||
Endian::Big => Endian::Little,
|
||||
Endian::Little => Endian::Big,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const DYNAMIC_SIZE: usize = 0;
|
||||
|
||||
pub const fn struct_size<const N: usize>(fields: [usize; N]) -> usize {
|
||||
|
@ -992,7 +992,11 @@ fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
|
||||
|
||||
/// Split an object into multiple relocatable objects.
|
||||
#[instrument(level = "debug", skip(obj))]
|
||||
pub fn split_obj(obj: &ObjInfo, module_name: Option<&str>) -> Result<Vec<ObjInfo>> {
|
||||
pub fn split_obj(
|
||||
obj: &ObjInfo,
|
||||
module_name: Option<&str>,
|
||||
globalize_symbols: bool,
|
||||
) -> Result<Vec<ObjInfo>> {
|
||||
let mut objects: Vec<ObjInfo> = vec![];
|
||||
let mut object_symbols: Vec<Vec<Option<SymbolIndex>>> = vec![];
|
||||
let mut name_to_obj: HashMap<String, usize> = HashMap::new();
|
||||
@ -1215,7 +1219,7 @@ pub fn split_obj(obj: &ObjInfo, module_name: Option<&str>) -> Result<Vec<ObjInfo
|
||||
}
|
||||
|
||||
// Update relocations
|
||||
let mut globalize_symbols = vec![];
|
||||
let mut symbols_to_globalize = vec![];
|
||||
for (obj_idx, out_obj) in objects.iter_mut().enumerate() {
|
||||
let symbol_idxs = &mut object_symbols[obj_idx];
|
||||
for (_section_index, section) in out_obj.sections.iter_mut() {
|
||||
@ -1231,7 +1235,7 @@ pub fn split_obj(obj: &ObjInfo, module_name: Option<&str>) -> Result<Vec<ObjInfo
|
||||
|
||||
// If the symbol is local, we'll upgrade the scope to global
|
||||
// and rename it to avoid conflicts
|
||||
if target_sym.flags.is_local() {
|
||||
if globalize_symbols && target_sym.flags.is_local() {
|
||||
let address_str = if obj.module_id == 0 {
|
||||
format!("{:08X}", target_sym.address)
|
||||
} else if let Some(section_index) = target_sym.section {
|
||||
@ -1250,7 +1254,7 @@ pub fn split_obj(obj: &ObjInfo, module_name: Option<&str>) -> Result<Vec<ObjInfo
|
||||
} else {
|
||||
format!("{}_{}", target_sym.name, address_str)
|
||||
};
|
||||
globalize_symbols.push((reloc.target_symbol, new_name));
|
||||
symbols_to_globalize.push((reloc.target_symbol, new_name));
|
||||
}
|
||||
|
||||
symbol_idxs[reloc.target_symbol as usize] = Some(out_sym_idx);
|
||||
@ -1295,16 +1299,18 @@ pub fn split_obj(obj: &ObjInfo, module_name: Option<&str>) -> Result<Vec<ObjInfo
|
||||
}
|
||||
|
||||
// Upgrade local symbols to global if necessary
|
||||
for (obj, symbol_map) in objects.iter_mut().zip(&object_symbols) {
|
||||
for (globalize_idx, new_name) in &globalize_symbols {
|
||||
if let Some(symbol_idx) = symbol_map[*globalize_idx as usize] {
|
||||
let mut symbol = obj.symbols[symbol_idx].clone();
|
||||
symbol.name.clone_from(new_name);
|
||||
if symbol.flags.is_local() {
|
||||
log::debug!("Globalizing {} in {}", symbol.name, obj.name);
|
||||
symbol.flags.set_scope(ObjSymbolScope::Global);
|
||||
if globalize_symbols {
|
||||
for (obj, symbol_map) in objects.iter_mut().zip(&object_symbols) {
|
||||
for (globalize_idx, new_name) in &symbols_to_globalize {
|
||||
if let Some(symbol_idx) = symbol_map[*globalize_idx as usize] {
|
||||
let mut symbol = obj.symbols[symbol_idx].clone();
|
||||
symbol.name.clone_from(new_name);
|
||||
if symbol.flags.is_local() {
|
||||
log::debug!("Globalizing {} in {}", symbol.name, obj.name);
|
||||
symbol.flags.set_scope(ObjSymbolScope::Global);
|
||||
}
|
||||
obj.symbols.replace(symbol_idx, symbol)?;
|
||||
}
|
||||
obj.symbols.replace(symbol_idx, symbol)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,10 +121,10 @@ impl<'a> U8View<'a> {
|
||||
}
|
||||
|
||||
/// Iterate over the nodes in the U8 archive.
|
||||
pub fn iter(&self) -> U8Iter { U8Iter { inner: self, idx: 1 } }
|
||||
pub fn iter(&self) -> U8Iter<'_> { U8Iter { inner: self, idx: 1 } }
|
||||
|
||||
/// Get the name of a node.
|
||||
pub fn get_name(&self, node: U8Node) -> Result<Cow<str>, String> {
|
||||
pub fn get_name(&self, node: U8Node) -> Result<Cow<'_, str>, String> {
|
||||
let name_buf = self.string_table.get(node.name_offset() as usize..).ok_or_else(|| {
|
||||
format!(
|
||||
"U8: name offset {} out of bounds (string table size: {})",
|
||||
|
@ -47,7 +47,7 @@ impl DiscFs {
|
||||
Ok(Self { disc, base, meta, mtime })
|
||||
}
|
||||
|
||||
fn find(&self, path: &Utf8UnixPath) -> VfsResult<DiscNode> {
|
||||
fn find(&self, path: &Utf8UnixPath) -> VfsResult<DiscNode<'_>> {
|
||||
let path = path.as_str().trim_matches('/');
|
||||
let mut split = path.split('/');
|
||||
let mut segment = next_non_empty(&mut split);
|
||||
|
@ -13,7 +13,7 @@ pub struct RarcFs {
|
||||
impl RarcFs {
|
||||
pub fn new(file: Box<dyn VfsFile>) -> io::Result<Self> { Ok(Self { file }) }
|
||||
|
||||
fn view(&mut self) -> io::Result<RarcView> {
|
||||
fn view(&mut self) -> io::Result<RarcView<'_>> {
|
||||
let data = self.file.map()?;
|
||||
RarcView::new(data).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ pub struct U8Fs {
|
||||
impl U8Fs {
|
||||
pub fn new(file: Box<dyn VfsFile>) -> io::Result<Self> { Ok(Self { file }) }
|
||||
|
||||
fn view(&mut self) -> io::Result<U8View> {
|
||||
fn view(&mut self) -> io::Result<U8View<'_>> {
|
||||
let data = self.file.map()?;
|
||||
U8View::new(data).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ impl WadFs {
|
||||
Ok(Self { file, wad, mtime })
|
||||
}
|
||||
|
||||
fn find(&self, path: &str) -> Option<WadFindResult> {
|
||||
fn find(&self, path: &str) -> Option<WadFindResult<'_>> {
|
||||
let filename = path.trim_start_matches('/');
|
||||
if filename.contains('/') {
|
||||
return None;
|
||||
|
Loading…
x
Reference in New Issue
Block a user