Semi-working REL analysis & splitting

This commit is contained in:
Luke Street 2023-08-25 00:54:29 -04:00
parent 3f63f1ef47
commit a2374e4fa0
21 changed files with 1072 additions and 709 deletions

400
Cargo.lock generated
View File

@ -165,9 +165,12 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -193,6 +196,49 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -224,7 +270,6 @@ dependencies = [
"byteorder", "byteorder",
"cwdemangle", "cwdemangle",
"dol", "dol",
"env_logger",
"filetime", "filetime",
"fixedbitset", "fixedbitset",
"flagset", "flagset",
@ -240,8 +285,10 @@ dependencies = [
"num_enum", "num_enum",
"object 0.31.1", "object 0.31.1",
"once_cell", "once_cell",
"path-slash",
"petgraph", "petgraph",
"ppc750cl", "ppc750cl",
"rayon",
"regex", "regex",
"rmp-serde", "rmp-serde",
"serde", "serde",
@ -250,6 +297,9 @@ dependencies = [
"serde_yaml", "serde_yaml",
"sha-1", "sha-1",
"smallvec", "smallvec",
"tracing",
"tracing-attributes",
"tracing-subscriber",
] ]
[[package]] [[package]]
@ -278,46 +328,12 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "env_logger"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]] [[package]]
name = "filetime" name = "filetime"
version = "0.2.21" version = "0.2.21"
@ -327,7 +343,7 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall",
"windows-sys 0.48.0", "windows-sys",
] ]
[[package]] [[package]]
@ -409,12 +425,9 @@ checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.2.6" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "hex" name = "hex"
@ -422,12 +435,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.2" version = "1.9.2"
@ -448,28 +455,6 @@ dependencies = [
"hashbrown 0.14.0", "hashbrown 0.14.0",
] ]
[[package]]
name = "io-lifetimes"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
dependencies = [
"libc",
"windows-sys 0.42.0",
]
[[package]]
name = "is-terminal"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys 0.42.0",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.11.0" version = "0.11.0"
@ -485,18 +470,18 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.19" version = "0.4.19"
@ -518,6 +503,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.6.2" version = "0.6.2"
@ -545,6 +539,16 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.15"
@ -554,6 +558,16 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.6.1" version = "0.6.1"
@ -602,12 +616,24 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.11" version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
[[package]]
name = "path-slash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
[[package]] [[package]]
name = "petgraph" name = "petgraph"
version = "0.6.3" version = "0.6.3"
@ -618,6 +644,12 @@ dependencies = [
"indexmap 1.9.2", "indexmap 1.9.2",
] ]
[[package]]
name = "pin-project-lite"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
[[package]] [[package]]
name = "ppc750cl" name = "ppc750cl"
version = "0.2.0" version = "0.2.0"
@ -668,6 +700,28 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rayon"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.16" version = "0.2.16"
@ -734,26 +788,18 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
version = "0.36.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.42.0",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.12" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.166" version = "1.0.166"
@ -820,6 +866,15 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.11.0" version = "1.11.0"
@ -848,15 +903,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.37" version = "1.0.37"
@ -877,6 +923,16 @@ dependencies = [
"syn 1.0.107", "syn 1.0.107",
] ]
[[package]]
name = "thread_local"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.5.10" version = "0.5.10"
@ -886,6 +942,64 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
]
[[package]]
name = "tracing-core"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
dependencies = [
"lazy_static",
"log",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.15.0" version = "1.15.0"
@ -919,6 +1033,12 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
@ -941,36 +1061,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm 0.42.0",
"windows_aarch64_msvc 0.42.0",
"windows_i686_gnu 0.42.0",
"windows_i686_msvc 0.42.0",
"windows_x86_64_gnu 0.42.0",
"windows_x86_64_gnullvm 0.42.0",
"windows_x86_64_msvc 0.42.0",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
@ -986,93 +1082,51 @@ version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.48.0", "windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.48.0", "windows_aarch64_msvc",
"windows_i686_gnu 0.48.0", "windows_i686_gnu",
"windows_i686_msvc 0.48.0", "windows_i686_msvc",
"windows_x86_64_gnu 0.48.0", "windows_x86_64_gnu",
"windows_x86_64_gnullvm 0.48.0", "windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.48.0", "windows_x86_64_msvc",
] ]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.48.0" version = "0.48.0"

View File

@ -29,7 +29,6 @@ base64 = "0.21.2"
byteorder = "1.4.3" byteorder = "1.4.3"
cwdemangle = "0.1.5" cwdemangle = "0.1.5"
dol = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" } dol = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" }
env_logger = "0.10.0"
filetime = "0.2.21" filetime = "0.2.21"
fixedbitset = "0.4.2" fixedbitset = "0.4.2"
flagset = { version = "0.4.3", features = ["serde"] } flagset = { version = "0.4.3", features = ["serde"] }
@ -45,8 +44,10 @@ multimap = "0.9.0"
num_enum = "0.6.1" num_enum = "0.6.1"
object = { version = "0.31.1", features = ["read_core", "std", "elf", "write_std"], default-features = false } object = { version = "0.31.1", features = ["read_core", "std", "elf", "write_std"], default-features = false }
once_cell = "1.18.0" once_cell = "1.18.0"
path-slash = "0.2.1"
petgraph = "0.6.3" petgraph = "0.6.3"
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" } ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" }
rayon = "1.7.0"
regex = "1.9.0" regex = "1.9.0"
serde = "1.0.166" serde = "1.0.166"
serde_json = "1.0.104" serde_json = "1.0.104"
@ -54,6 +55,9 @@ serde_repr = "0.1.14"
serde_yaml = "0.9.22" serde_yaml = "0.9.22"
sha-1 = "0.10.1" sha-1 = "0.10.1"
smallvec = "1.11.0" smallvec = "1.11.0"
tracing = "0.1.37"
tracing-attributes = "0.1.26"
tracing-subscriber = "0.3.17"
[build-dependencies] [build-dependencies]
anyhow = { version = "1.0.71", features = ["backtrace"] } anyhow = { version = "1.0.71", features = ["backtrace"] }

View File

@ -0,0 +1,26 @@
SECTIONS
{
GROUP:
{
.init :{}
.text :{}
.ctors :{}
.dtors :{}
.rodata :{}
.data :{ *(.data) *(extabindex) *(extab) }
.bss :{}
}
}
FORCEACTIVE
{
_unresolved
_prolog
_epilog
$FORCEACTIVE
}
FORCEFILES
{
$FORCEFILES
}

View File

@ -24,13 +24,13 @@ pub struct SectionAddress {
impl Debug for SectionAddress { impl Debug for SectionAddress {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{:#010X}", self.section as isize, self.address) write!(f, "{}:{:#X}", self.section as isize, self.address)
} }
} }
impl Display for SectionAddress { impl Display for SectionAddress {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{:#010X}", self.section as isize, self.address) write!(f, "{}:{:#X}", self.section as isize, self.address)
} }
} }
@ -131,9 +131,19 @@ impl AnalyzerState {
section.address, section.address,
section.address + section.size section.address + section.size
); );
let address_str = if obj.module_id == 0 {
format!("{:08X}", addr.address)
} else {
format!(
"{}_{}_{:X}",
obj.module_id,
section.name.trim_start_matches('.'),
addr.address
)
};
obj.add_symbol( obj.add_symbol(
ObjSymbol { ObjSymbol {
name: format!("jumptable_{:08X}", addr.address), name: format!("jumptable_{}", address_str),
demangled_name: None, demangled_name: None,
address: addr.address as u64, address: addr.address as u64,
section: Some(addr.section), section: Some(addr.section),

View File

@ -5,7 +5,7 @@ use crate::{
util::split::is_linker_generated_label, util::split::is_linker_generated_label,
}; };
pub fn detect_object_boundaries(obj: &mut ObjInfo) -> Result<()> { pub fn detect_objects(obj: &mut ObjInfo) -> Result<()> {
for (section_index, section) in for (section_index, section) in
obj.sections.iter_mut().filter(|(_, s)| s.kind != ObjSectionKind::Code) obj.sections.iter_mut().filter(|(_, s)| s.kind != ObjSectionKind::Code)
{ {

View File

@ -151,8 +151,9 @@ impl AnalysisPass for FindRelCtorsDtors {
let possible_sections = obj let possible_sections = obj
.sections .sections
.iter() .iter()
.filter(|&(_, section)| { .filter(|&(index, section)| {
if section.section_known if section.section_known
|| state.known_sections.contains_key(&index)
|| !matches!(section.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData) || !matches!(section.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData)
|| section.size < 4 || section.size < 4
{ {
@ -283,3 +284,40 @@ impl AnalysisPass for FindRelCtorsDtors {
Ok(()) Ok(())
} }
} }
pub struct FindRelRodataData {}
impl AnalysisPass for FindRelRodataData {
fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> {
ensure!(obj.kind == ObjKind::Relocatable);
match (obj.sections.by_name(".rodata")?, obj.sections.by_name(".data")?) {
(None, None) => {}
_ => return Ok(()),
}
let possible_sections = obj
.sections
.iter()
.filter(|&(index, section)| {
!section.section_known
&& !state.known_sections.contains_key(&index)
&& matches!(section.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData)
})
.collect_vec();
if possible_sections.len() != 2 {
log::warn!("Failed to find .rodata and .data");
return Ok(());
}
log::debug!("Found .rodata and .data: {:?}", possible_sections);
let rodata_section_index = possible_sections[0].0;
state.known_sections.insert(rodata_section_index, ".rodata".to_string());
let data_section_index = possible_sections[1].0;
state.known_sections.insert(data_section_index, ".data".to_string());
Ok(())
}
}

View File

@ -309,8 +309,8 @@ impl FunctionSlices {
} }
} }
} }
BranchTarget::JumpTable { .. } => { BranchTarget::JumpTable { address, size } => {
bail!("Conditional jump table unsupported @ {:#010X}", ins_addr); bail!("Conditional jump table unsupported @ {:#010X} -> {:#010X} size {:#X?}", ins_addr, address, size);
} }
} }
} }

View File

@ -5,6 +5,8 @@ use std::{
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use ppc750cl::Opcode; use ppc750cl::Opcode;
use tracing::{debug_span, info_span};
use tracing_attributes::instrument;
use crate::{ use crate::{
analysis::{ analysis::{
@ -88,8 +90,8 @@ impl Tracker {
} }
} }
#[instrument(name = "tracker", skip(self, obj))]
pub fn process(&mut self, obj: &ObjInfo) -> Result<()> { pub fn process(&mut self, obj: &ObjInfo) -> Result<()> {
log::debug!("Processing code sections");
self.process_code(obj)?; self.process_code(obj)?;
for (section_index, section) in obj for (section_index, section) in obj
.sections .sections
@ -151,6 +153,7 @@ impl Tracker {
) -> Result<ExecCbResult<()>> { ) -> Result<ExecCbResult<()>> {
let ExecCbData { executor, vm, result, ins_addr, section: _, ins, block_start: _ } = data; let ExecCbData { executor, vm, result, ins_addr, section: _, ins, block_start: _ } = data;
let is_function_addr = |addr: SectionAddress| addr >= function_start && addr < function_end; let is_function_addr = |addr: SectionAddress| addr >= function_start && addr < function_end;
let _span = debug_span!("ins", addr = %ins_addr, op = ?ins.op).entered();
match result { match result {
StepResult::Continue => { StepResult::Continue => {
@ -310,8 +313,20 @@ impl Tracker {
executor.push(addr, branch.vm, true); executor.push(addr, branch.vm, true);
} }
} }
BranchTarget::JumpTable { .. } => { BranchTarget::JumpTable { address, size } => {
bail!("Conditional jump table unsupported @ {:#010X}", ins_addr) let (entries, _) = uniq_jump_table_entries(
obj,
address,
size,
ins_addr,
function_start,
Some(function_end),
)?;
for target in entries {
if is_function_addr(target) {
executor.push(target, branch.vm.clone_all(), true);
}
}
} }
} }
} }
@ -326,6 +341,9 @@ impl Tracker {
}; };
let function_start = SectionAddress::new(section_index, symbol.address as u32); let function_start = SectionAddress::new(section_index, symbol.address as u32);
let function_end = function_start + symbol.size as u32; let function_end = function_start + symbol.size as u32;
let _span =
info_span!("fn", name = %symbol.name, start = %function_start, end = %function_end)
.entered();
// The compiler can sometimes create impossible-to-reach branches, // The compiler can sometimes create impossible-to-reach branches,
// but we still want to track them. // but we still want to track them.
@ -461,6 +479,7 @@ impl Tracker {
.or_else(|| check_symbol(self.sda_base, "_SDA_BASE_")) .or_else(|| check_symbol(self.sda_base, "_SDA_BASE_"))
} }
#[instrument(name = "apply", skip(self, obj))]
pub fn apply(&self, obj: &mut ObjInfo, replace: bool) -> Result<()> { pub fn apply(&self, obj: &mut ObjInfo, replace: bool) -> Result<()> {
fn apply_section_name(section: &mut ObjSection, name: &str) { fn apply_section_name(section: &mut ObjSection, name: &str) {
let module_id = if let Some((_, b)) = section.name.split_once(':') { let module_id = if let Some((_, b)) = section.name.split_once(':') {

View File

@ -126,9 +126,12 @@ pub fn section_address_for(
let (section_index, _) = obj.sections.at_address(target_addr).ok()?; let (section_index, _) = obj.sections.at_address(target_addr).ok()?;
return Some(SectionAddress::new(section_index, target_addr)); return Some(SectionAddress::new(section_index, target_addr));
} }
// TODO: relative jumps within relocatable objects? if obj.sections[ins_addr.section].contains(target_addr) {
Some(SectionAddress::new(ins_addr.section, target_addr))
} else {
None None
} }
}
impl VM { impl VM {
#[inline] #[inline]
@ -180,11 +183,11 @@ impl VM {
pub fn clone_all(&self) -> Box<Self> { Box::new(self.clone()) } pub fn clone_all(&self) -> Box<Self> { Box::new(self.clone()) }
pub fn step(&mut self, obj: &ObjInfo, ins_addr: SectionAddress, ins: &Ins) -> StepResult { pub fn step(&mut self, obj: &ObjInfo, ins_addr: SectionAddress, ins: &Ins) -> StepResult {
let relocation_target = relocation_target_for(obj, ins_addr, None).ok().flatten(); // let relocation_target = relocation_target_for(obj, ins_addr, None).ok().flatten();
if let Some(_target) = relocation_target { // if let Some(_target) = relocation_target {
let _defs = ins.defs(); // let _defs = ins.defs();
// TODO // // TODO
} // }
match ins.op { match ins.op {
Opcode::Illegal => { Opcode::Illegal => {

View File

@ -1,45 +1,55 @@
use std::{ use std::{
borrow::Cow,
collections::{btree_map::Entry, hash_map, BTreeMap, HashMap}, collections::{btree_map::Entry, hash_map, BTreeMap, HashMap},
fs, fs,
fs::{DirBuilder, File}, fs::{DirBuilder, File},
io::Write, io::Write,
mem::take, mem::take,
path::{Path, PathBuf}, path::{Path, PathBuf},
time::Instant,
}; };
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use argp::FromArgs; use argp::FromArgs;
use itertools::Itertools; use itertools::Itertools;
use memmap2::Mmap;
use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::{debug, info, info_span};
use crate::{ use crate::{
analysis::{ analysis::{
cfa::{AnalyzerState, SectionAddress}, cfa::{AnalyzerState, SectionAddress},
objects::{detect_object_boundaries, detect_strings}, objects::{detect_objects, detect_strings},
pass::{AnalysisPass, FindRelCtorsDtors, FindSaveRestSleds, FindTRKInterruptVectorTable}, pass::{
AnalysisPass, FindRelCtorsDtors, FindRelRodataData, FindSaveRestSleds,
FindTRKInterruptVectorTable,
},
signatures::{apply_signatures, apply_signatures_post}, signatures::{apply_signatures, apply_signatures_post},
tracker::Tracker, tracker::Tracker,
}, },
cmd::shasum::file_sha1, cmd::shasum::file_sha1,
obj::{ obj::{
ObjDataKind, ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, best_match_for_reloc, ObjDataKind, ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind,
ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, SymbolIndex, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, SymbolIndex,
}, },
util::{ util::{
asm::write_asm, asm::write_asm,
comment::MWComment, comment::MWComment,
config::{ config::{
apply_splits, apply_symbols_file, is_auto_symbol, write_splits_file, write_symbols_file, apply_splits_file, apply_symbols_file, is_auto_symbol, write_splits_file,
write_symbols_file,
}, },
dep::DepFile, dep::DepFile,
dol::process_dol, dol::process_dol,
elf::{process_elf, write_elf}, elf::{process_elf, write_elf},
file::{buf_writer, map_file, map_reader, touch}, file::{buf_writer, map_file, map_reader, touch, Reader},
lcf::{asm_path_for_unit, generate_ldscript, obj_path_for_unit}, lcf::{asm_path_for_unit, generate_ldscript, obj_path_for_unit},
map::apply_map_file, map::apply_map_file,
rel::process_rel, rel::process_rel,
rso::{process_rso, DOL_SECTION_ABS, DOL_SECTION_NAMES}, rso::{process_rso, DOL_SECTION_ABS, DOL_SECTION_NAMES},
split::{is_linker_generated_object, split_obj, update_splits}, split::{is_linker_generated_object, split_obj, update_splits},
yaz0,
}, },
}; };
@ -85,6 +95,9 @@ pub struct SplitArgs {
#[argp(switch)] #[argp(switch)]
/// skip updating splits & symbol files (for build systems) /// skip updating splits & symbol files (for build systems)
no_update: bool, no_update: bool,
#[argp(option, short = 'j')]
/// number of threads to use (default: number of logical CPUs)
jobs: Option<usize>,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
@ -120,12 +133,52 @@ pub struct ApplyArgs {
#[inline] #[inline]
fn bool_true() -> bool { true } fn bool_true() -> bool { true }
mod path_slash_serde {
use std::path::PathBuf;
use path_slash::PathBufExt as _;
use serde::{self, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(path: &PathBuf, s: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let path_str = path.to_slash().ok_or_else(|| serde::ser::Error::custom("Invalid path"))?;
s.serialize_str(path_str.as_ref())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<PathBuf, D::Error>
where D: Deserializer<'de> {
String::deserialize(deserializer).map(PathBuf::from_slash)
}
}
mod path_slash_serde_option {
use std::path::PathBuf;
use path_slash::PathBufExt as _;
use serde::{self, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(path: &Option<PathBuf>, s: S) -> Result<S::Ok, S::Error>
where S: Serializer {
if let Some(path) = path {
let path_str =
path.to_slash().ok_or_else(|| serde::ser::Error::custom("Invalid path"))?;
s.serialize_str(path_str.as_ref())
} else {
s.serialize_none()
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where D: Deserializer<'de> {
Ok(Option::deserialize(deserializer)?.map(|s: String| PathBuf::from_slash(s)))
}
}
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectConfig { pub struct ProjectConfig {
pub object: PathBuf, #[serde(flatten)]
pub hash: Option<String>, pub base: ModuleConfig,
pub splits: Option<PathBuf>, #[serde(with = "path_slash_serde_option", default)]
pub symbols: Option<PathBuf>,
pub selfile: Option<PathBuf>, pub selfile: Option<PathBuf>,
pub selfile_hash: Option<String>, pub selfile_hash: Option<String>,
/// Version of the MW `.comment` section format. /// Version of the MW `.comment` section format.
@ -147,34 +200,63 @@ pub struct ProjectConfig {
/// Adds all objects to FORCEFILES in the linker script. /// Adds all objects to FORCEFILES in the linker script.
#[serde(default)] #[serde(default)]
pub auto_force_files: bool, pub auto_force_files: bool,
/// Specifies the start of the common BSS section.
pub common_start: Option<u32>,
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ModuleConfig { pub struct ModuleConfig {
#[serde(with = "path_slash_serde")]
pub object: PathBuf, pub object: PathBuf,
pub hash: Option<String>, pub hash: Option<String>,
#[serde(with = "path_slash_serde_option", default)]
pub splits: Option<PathBuf>, pub splits: Option<PathBuf>,
#[serde(with = "path_slash_serde_option", default)]
pub symbols: Option<PathBuf>, pub symbols: Option<PathBuf>,
#[serde(with = "path_slash_serde_option", default)]
pub map: Option<PathBuf>,
}
impl ModuleConfig {
pub fn file_name(&self) -> Cow<'_, str> {
self.object.file_name().unwrap_or(self.object.as_os_str()).to_string_lossy()
}
pub fn file_prefix(&self) -> Cow<'_, str> {
match self.file_name() {
Cow::Borrowed(s) => {
Cow::Borrowed(s.split_once('.').map(|(prefix, _)| prefix).unwrap_or(&s))
}
Cow::Owned(s) => {
Cow::Owned(s.split_once('.').map(|(prefix, _)| prefix).unwrap_or(&s).to_string())
}
}
}
pub fn name(&self) -> Cow<'_, str> { self.file_prefix() }
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OutputUnit { pub struct OutputUnit {
#[serde(with = "path_slash_serde")]
pub object: PathBuf, pub object: PathBuf,
pub name: String, pub name: String,
pub autogenerated: bool, pub autogenerated: bool,
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct OutputModule { pub struct OutputModule {
pub name: String, pub name: String,
pub module_id: u32,
#[serde(with = "path_slash_serde")]
pub ldscript: PathBuf, pub ldscript: PathBuf,
pub units: Vec<OutputUnit>, pub units: Vec<OutputUnit>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, Default)] #[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct OutputConfig { pub struct OutputConfig {
pub ldscript: PathBuf, #[serde(flatten)]
pub units: Vec<OutputUnit>, pub base: OutputModule,
pub modules: Vec<OutputModule>, pub modules: Vec<OutputModule>,
} }
@ -331,7 +413,9 @@ fn verify_hash<P: AsRef<Path>>(path: P, hash_str: &str) -> Result<()> {
} }
} }
fn update_symbols(obj: &mut ObjInfo, modules: &BTreeMap<u32, ObjInfo>) -> Result<()> { type ModuleMap<'a> = BTreeMap<u32, (&'a ModuleConfig, ObjInfo)>;
fn update_symbols(obj: &mut ObjInfo, modules: &ModuleMap<'_>) -> Result<()> {
log::debug!("Updating symbols for module {}", obj.module_id); log::debug!("Updating symbols for module {}", obj.module_id);
// Find all references to this module from other modules // Find all references to this module from other modules
@ -339,11 +423,9 @@ fn update_symbols(obj: &mut ObjInfo, modules: &BTreeMap<u32, ObjInfo>) -> Result
.unresolved_relocations .unresolved_relocations
.iter() .iter()
.map(|r| (obj.module_id, r)) .map(|r| (obj.module_id, r))
.chain( .chain(modules.iter().flat_map(|(_, (_, obj))| {
modules obj.unresolved_relocations.iter().map(|r| (obj.module_id, r))
.iter() }))
.flat_map(|(_, obj)| obj.unresolved_relocations.iter().map(|r| (obj.module_id, r))),
)
.filter(|(_, r)| r.module_id == obj.module_id) .filter(|(_, r)| r.module_id == obj.module_id)
{ {
if source_module_id == obj.module_id { if source_module_id == obj.module_id {
@ -362,26 +444,12 @@ fn update_symbols(obj: &mut ObjInfo, modules: &BTreeMap<u32, ObjInfo>) -> Result
.get_elf_index(rel_reloc.target_section as usize) .get_elf_index(rel_reloc.target_section as usize)
.ok_or_else(|| anyhow!("Failed to locate REL section {}", rel_reloc.target_section))?; .ok_or_else(|| anyhow!("Failed to locate REL section {}", rel_reloc.target_section))?;
let target_symbol = obj let target_symbols = obj
.symbols .symbols
.at_section_address(target_section_index, rel_reloc.addend) .at_section_address(target_section_index, rel_reloc.addend)
.filter(|(_, s)| s.referenced_by(rel_reloc.kind)) .filter(|(_, s)| s.referenced_by(rel_reloc.kind))
.at_most_one() .collect_vec();
.map_err(|e| { let target_symbol = best_match_for_reloc(target_symbols, rel_reloc.kind);
for (_, symbol) in e {
log::warn!(
"Multiple symbols found for {:#010X}: {}",
rel_reloc.addend,
symbol.name
);
}
anyhow!(
"Multiple symbols found for {:#010X} while checking reloc {} {:?}",
rel_reloc.addend,
source_module_id,
rel_reloc
)
})?;
if let Some((symbol_index, symbol)) = target_symbol { if let Some((symbol_index, symbol)) = target_symbol {
// Update symbol // Update symbol
@ -427,11 +495,7 @@ fn update_symbols(obj: &mut ObjInfo, modules: &BTreeMap<u32, ObjInfo>) -> Result
Ok(()) Ok(())
} }
fn create_relocations( fn create_relocations(obj: &mut ObjInfo, modules: &ModuleMap<'_>, dol_obj: &ObjInfo) -> Result<()> {
obj: &mut ObjInfo,
modules: &BTreeMap<u32, ObjInfo>,
dol_obj: &ObjInfo,
) -> Result<()> {
log::debug!("Creating relocations for module {}", obj.module_id); log::debug!("Creating relocations for module {}", obj.module_id);
// Resolve all relocations in this module // Resolve all relocations in this module
@ -450,9 +514,10 @@ fn create_relocations(
} else if rel_reloc.module_id == obj.module_id { } else if rel_reloc.module_id == obj.module_id {
&*obj &*obj
} else { } else {
modules &modules
.get(&rel_reloc.module_id) .get(&rel_reloc.module_id)
.ok_or_else(|| anyhow!("Failed to locate module {}", rel_reloc.module_id))? .ok_or_else(|| anyhow!("Failed to locate module {}", rel_reloc.module_id))?
.1
}; };
let (target_section_index, _target_section) = if rel_reloc.module_id == 0 { let (target_section_index, _target_section) = if rel_reloc.module_id == 0 {
@ -469,21 +534,12 @@ fn create_relocations(
)? )?
}; };
let Some((symbol_index, symbol)) = target_obj let target_symbols = target_obj
.symbols .symbols
.at_section_address(target_section_index, rel_reloc.addend) .at_section_address(target_section_index, rel_reloc.addend)
.filter(|(_, s)| s.referenced_by(rel_reloc.kind)) .filter(|(_, s)| s.referenced_by(rel_reloc.kind))
.at_most_one() .collect_vec();
.map_err(|e| { let Some((symbol_index, symbol)) = best_match_for_reloc(target_symbols, rel_reloc.kind)
for (_, symbol) in e {
log::warn!(
"Multiple symbols found for {:#010X}: {}",
rel_reloc.addend,
symbol.name
);
}
anyhow!("Multiple symbols found for {:#010X}", rel_reloc.addend)
})?
else { else {
bail!( bail!(
"Couldn't find module {} symbol in section {} at {:#010X}", "Couldn't find module {} symbol in section {} at {:#010X}",
@ -516,7 +572,7 @@ fn create_relocations(
fn resolve_external_relocations( fn resolve_external_relocations(
obj: &mut ObjInfo, obj: &mut ObjInfo,
modules: &BTreeMap<u32, ObjInfo>, modules: &ModuleMap<'_>,
dol_obj: Option<&ObjInfo>, dol_obj: Option<&ObjInfo>,
) -> Result<()> { ) -> Result<()> {
log::debug!("Resolving relocations for module {}", obj.module_id); log::debug!("Resolving relocations for module {}", obj.module_id);
@ -540,9 +596,12 @@ fn resolve_external_relocations(
} else if module_id == 0 { } else if module_id == 0 {
dol_obj.unwrap() dol_obj.unwrap()
} else { } else {
modules.get(&module_id).ok_or_else(|| { &modules
.get(&module_id)
.ok_or_else(|| {
anyhow!("Failed to locate module {}", reloc.module.unwrap()) anyhow!("Failed to locate module {}", reloc.module.unwrap())
})? })?
.1
}; };
let target_symbol = &target_obj.symbols[reloc.target_symbol]; let target_symbol = &target_obj.symbols[reloc.target_symbol];
@ -573,69 +632,49 @@ fn resolve_external_relocations(
Ok(()) Ok(())
} }
fn split(args: SplitArgs) -> Result<()> { fn decompress_if_needed(map: &Mmap) -> Result<Cow<[u8]>> {
log::info!("Loading {}", args.config.display()); Ok(if map.len() > 4 && map[0..4] == *b"Yaz0" {
let mut config_file = File::open(&args.config) Cow::Owned(yaz0::decompress_file(&mut map_reader(map))?)
.with_context(|| format!("Failed to open config file '{}'", args.config.display()))?; } else {
let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?; Cow::Borrowed(map)
})
let out_config_path = args.out_dir.join("config.json");
let mut dep = DepFile::new(out_config_path.clone());
log::info!("Loading {}", config.object.display());
if let Some(hash_str) = &config.hash {
verify_hash(&config.object, hash_str)?;
} }
let mut obj = process_dol(&config.object)?;
dep.push(config.object.clone()); fn load_analyze_dol(config: &ProjectConfig) -> Result<(ObjInfo, Vec<PathBuf>)> {
// log::info!("Loading {}", config.object.display());
if let Some(hash_str) = &config.base.hash {
verify_hash(&config.base.object, hash_str)?;
}
let mut obj = process_dol(&config.base.object)?;
let mut dep = vec![config.base.object.clone()];
if let Some(comment_version) = config.mw_comment_version { if let Some(comment_version) = config.mw_comment_version {
obj.mw_comment = Some(MWComment::new(comment_version)?); obj.mw_comment = Some(MWComment::new(comment_version)?);
} }
let mut modules = BTreeMap::<u32, ObjInfo>::new(); if let Some(map_path) = &config.base.map {
let mut module_ids = Vec::with_capacity(config.modules.len()); apply_map_file(map_path, &mut obj)?;
if !config.modules.is_empty() { dep.push(map_path.clone());
log::info!("Loading {} modules", config.modules.len());
}
for module_config in &config.modules {
log::debug!("Loading {}", module_config.object.display());
if let Some(hash_str) = &module_config.hash {
verify_hash(&module_config.object, hash_str)?;
}
let map = map_file(&module_config.object)?;
let rel_obj = process_rel(map_reader(&map))?;
module_ids.push(rel_obj.module_id);
match modules.entry(rel_obj.module_id) {
Entry::Vacant(e) => e.insert(rel_obj),
Entry::Occupied(_) => bail!("Duplicate module ID {}", obj.module_id),
};
dep.push(module_config.object.clone());
} }
if let Some(splits_path) = &config.splits { if let Some(splits_path) = &config.base.splits {
apply_splits_file(splits_path, &mut obj)?;
dep.push(splits_path.clone()); dep.push(splits_path.clone());
if splits_path.is_file() {
let map = map_file(splits_path)?;
apply_splits(map_reader(&map), &mut obj)?;
}
} }
let mut state = AnalyzerState::default(); if let Some(symbols_path) = &config.base.symbols {
if let Some(symbols_path) = &config.symbols {
dep.push(symbols_path.clone());
apply_symbols_file(symbols_path, &mut obj)?; apply_symbols_file(symbols_path, &mut obj)?;
dep.push(symbols_path.clone());
} }
// TODO move before symbols? // TODO move before symbols?
log::info!("Performing signature analysis"); debug!("Performing signature analysis");
apply_signatures(&mut obj)?; apply_signatures(&mut obj)?;
if !config.quick_analysis { if !config.quick_analysis {
log::info!("Detecting function boundaries"); let mut state = AnalyzerState::default();
debug!("Detecting function boundaries");
state.detect_functions(&obj)?; state.detect_functions(&obj)?;
log::info!("Discovered {} functions", state.function_slices.len());
FindTRKInterruptVectorTable::execute(&mut state, &obj)?; FindTRKInterruptVectorTable::execute(&mut state, &obj)?;
FindSaveRestSleds::execute(&mut state, &obj)?; FindSaveRestSleds::execute(&mut state, &obj)?;
@ -649,116 +688,59 @@ fn split(args: SplitArgs) -> Result<()> {
verify_hash(selfile, hash)?; verify_hash(selfile, hash)?;
} }
apply_selfile(&mut obj, selfile)?; apply_selfile(&mut obj, selfile)?;
dep.push(selfile.clone());
}
Ok((obj, dep))
} }
if !modules.is_empty() { fn split_write_obj(
log::info!("Analyzing modules"); obj: &mut ObjInfo,
config: &ProjectConfig,
module_config: &ModuleConfig,
out_dir: &PathBuf,
no_update: bool,
) -> Result<OutputModule> {
debug!("Performing relocation analysis");
let mut tracker = Tracker::new(obj);
tracker.process(obj)?;
let mut function_count = 0; debug!("Applying relocations");
for &module_id in &module_ids { tracker.apply(obj, false)?;
log::info!("Analyzing module {}", module_id);
let module_obj = modules.get_mut(&module_id).unwrap();
let mut state = AnalyzerState::default();
state.detect_functions(module_obj)?;
function_count += state.function_slices.len();
FindRelCtorsDtors::execute(&mut state, module_obj)?;
state.apply(module_obj)?;
apply_signatures(module_obj)?;
apply_signatures_post(module_obj)?;
}
log::info!("Discovered {} functions in modules", function_count);
// Step 1: For each module, create any missing symbols (referenced from other modules) and set FORCEACTIVE
update_symbols(&mut obj, &modules)?;
for &module_id in &module_ids {
let mut module_obj = modules.remove(&module_id).unwrap();
update_symbols(&mut module_obj, &modules)?;
modules.insert(module_id, module_obj);
}
// Step 2: For each module, create relocations to symbols in other modules
for &module_id in &module_ids {
let mut module_obj = modules.remove(&module_id).unwrap();
create_relocations(&mut module_obj, &modules, &obj)?;
modules.insert(module_id, module_obj);
}
}
log::info!("Performing relocation analysis");
let mut tracker = Tracker::new(&obj);
tracker.process(&obj)?;
log::info!("Applying relocations");
tracker.apply(&mut obj, false)?;
if !modules.is_empty() {
resolve_external_relocations(&mut obj, &modules, None)?;
for &module_id in &module_ids {
let mut module_obj = modules.remove(&module_id).unwrap();
resolve_external_relocations(&mut module_obj, &modules, Some(&obj))?;
let mut tracker = Tracker::new(&module_obj);
tracker.process(&module_obj)?;
tracker.apply(&mut module_obj, false)?;
modules.insert(module_id, module_obj);
}
}
if config.detect_objects { if config.detect_objects {
log::info!("Detecting object boundaries"); debug!("Detecting object boundaries");
detect_object_boundaries(&mut obj)?; detect_objects(obj)?;
for module_obj in modules.values_mut() {
detect_object_boundaries(module_obj)?;
}
} }
if config.detect_strings { if config.detect_strings {
log::info!("Detecting strings"); debug!("Detecting strings");
detect_strings(&mut obj)?; detect_strings(obj)?;
for module_obj in modules.values_mut() {
detect_strings(module_obj)?;
}
} }
log::info!("Adjusting splits"); debug!("Adjusting splits");
update_splits(&mut obj)?; update_splits(obj, if obj.module_id == 0 { config.common_start } else { None })?;
for module_obj in modules.values_mut() {
update_splits(module_obj)?;
}
if !args.no_update { if !no_update {
log::info!("Writing configuration"); debug!("Writing configuration");
if let Some(symbols_path) = &config.symbols { if let Some(symbols_path) = &module_config.symbols {
write_symbols_file(symbols_path, &obj)?; write_symbols_file(symbols_path, &obj)?;
} }
if let Some(splits_path) = &config.splits { if let Some(splits_path) = &module_config.splits {
write_splits_file(splits_path, &obj, false)?; write_splits_file(splits_path, &obj, false)?;
} }
for (config, &module_id) in config.modules.iter().zip(&module_ids) {
let module_obj = modules.get(&module_id).unwrap();
if let Some(symbols_path) = &config.symbols {
write_symbols_file(symbols_path, module_obj)?;
}
if let Some(splits_path) = &config.splits {
write_splits_file(splits_path, module_obj, true)?;
}
}
} }
log::info!("Splitting {} objects", obj.link_order.len()); debug!("Splitting {} objects", obj.link_order.len());
let split_objs = split_obj(&obj)?; let split_objs = split_obj(&obj)?;
// Create out dirs debug!("Writing object files");
touch(&args.out_dir)?; let obj_dir = out_dir.join("obj");
let asm_dir = args.out_dir.join("asm"); let mut out_config = OutputModule {
let include_dir = args.out_dir.join("include"); name: module_config.name().to_string(),
let obj_dir = args.out_dir.join("obj"); module_id: obj.module_id,
DirBuilder::new().recursive(true).create(&include_dir)?; ldscript: out_dir.join("ldscript.lcf"),
fs::write(include_dir.join("macros.inc"), include_str!("../../assets/macros.inc"))?; units: Vec::with_capacity(split_objs.len()),
};
log::info!("Writing object files");
let mut out_config = OutputConfig::default();
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) { for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
let out_obj = write_elf(split_obj)?; let out_obj = write_elf(split_obj)?;
let out_path = obj_dir.join(obj_path_for_unit(&unit.name)); let out_path = obj_dir.join(obj_path_for_unit(&unit.name));
@ -773,18 +755,12 @@ fn split(args: SplitArgs) -> Result<()> {
fs::write(&out_path, out_obj) fs::write(&out_path, out_obj)
.with_context(|| format!("Failed to write '{}'", out_path.display()))?; .with_context(|| format!("Failed to write '{}'", out_path.display()))?;
} }
{
let mut out_file = buf_writer(&out_config_path)?;
serde_json::to_writer_pretty(&mut out_file, &out_config)?;
out_file.flush()?;
}
// Generate ldscript.lcf // Generate ldscript.lcf
let ldscript_path = args.out_dir.join("ldscript.lcf"); fs::write(&out_config.ldscript, generate_ldscript(&obj, config.auto_force_files)?)?;
fs::write(&ldscript_path, generate_ldscript(&obj, config.auto_force_files)?)?;
out_config.ldscript = ldscript_path;
log::info!("Writing disassembly"); debug!("Writing disassembly");
let asm_dir = out_dir.join("asm");
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) { for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
let out_path = asm_dir.join(asm_path_for_unit(&unit.name)); let out_path = asm_dir.join(asm_path_for_unit(&unit.name));
@ -793,24 +769,220 @@ fn split(args: SplitArgs) -> Result<()> {
.with_context(|| format!("Failed to write {}", out_path.display()))?; .with_context(|| format!("Failed to write {}", out_path.display()))?;
w.flush()?; w.flush()?;
} }
Ok(out_config)
}
// Split and write modules fn load_analyze_rel(
for (config, &module_id) in config.modules.iter().zip(&module_ids) { config: &ProjectConfig,
let obj = modules.get_mut(&module_id).unwrap(); module_config: &ModuleConfig,
) -> Result<(ObjInfo, Vec<PathBuf>)> {
debug!("Loading {}", module_config.object.display());
if let Some(hash_str) = &module_config.hash {
verify_hash(&module_config.object, hash_str)?;
}
let map = map_file(&module_config.object)?;
let buf = decompress_if_needed(&map)?;
let mut module_obj = process_rel(Reader::new(&buf))?;
let out_dir = args.out_dir.join(format!("module_{}", module_id)); let mut dep = vec![module_config.object.clone()];
let asm_dir = out_dir.join("asm"); if let Some(map_path) = &module_config.map {
// let obj_dir = out_dir.join("obj"); apply_map_file(map_path, &mut module_obj)?;
dep.push(map_path.clone());
}
log::info!("Processing module {}", module_id); if let Some(splits_path) = &module_config.splits {
apply_splits_file(splits_path, &mut module_obj)?;
dep.push(splits_path.clone());
}
// log::info!("Writing disassembly"); if let Some(symbols_path) = &module_config.symbols {
let filename = config.object.file_name().unwrap().to_str().unwrap(); apply_symbols_file(symbols_path, &mut module_obj)?;
let out_path = asm_dir.join(asm_path_for_unit(filename)); dep.push(symbols_path.clone());
let mut w = buf_writer(&out_path)?; }
write_asm(&mut w, obj)
.with_context(|| format!("Failed to write {}", out_path.display()))?; debug!("Analyzing module {}", module_obj.module_id);
w.flush()?; if !config.quick_analysis {
let mut state = AnalyzerState::default();
state.detect_functions(&module_obj)?;
FindRelCtorsDtors::execute(&mut state, &module_obj)?;
FindRelRodataData::execute(&mut state, &module_obj)?;
state.apply(&mut module_obj)?;
}
apply_signatures(&mut module_obj)?;
apply_signatures_post(&mut module_obj)?;
Ok((module_obj, dep))
}
fn split(args: SplitArgs) -> Result<()> {
if let Some(jobs) = args.jobs {
rayon::ThreadPoolBuilder::new().num_threads(jobs).build_global().unwrap();
}
let command_start = Instant::now();
info!("Loading {}", args.config.display());
let mut config_file = File::open(&args.config)
.with_context(|| format!("Failed to open config file '{}'", args.config.display()))?;
let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?;
let out_config_path = args.out_dir.join("config.json");
let mut dep = DepFile::new(out_config_path.clone());
let module_count = config.modules.len() + 1;
info!(
"Loading and analyzing {} modules (using {} threads)",
module_count,
rayon::current_num_threads()
);
let mut dol_result: Option<Result<(ObjInfo, Vec<PathBuf>)>> = None;
let mut modules_result: Option<Result<Vec<(ObjInfo, Vec<PathBuf>)>>> = None;
let start = Instant::now();
rayon::scope(|s| {
// DOL
s.spawn(|_| {
let _span = info_span!("module", name = %config.base.name()).entered();
dol_result =
Some(load_analyze_dol(&config).with_context(|| {
format!("While loading object '{}'", config.base.file_name())
}));
});
// Modules
s.spawn(|_| {
modules_result = Some(
config
.modules
.par_iter()
.map(|module_config| {
let _span = info_span!("module", name = %module_config.name()).entered();
load_analyze_rel(&config, module_config).with_context(|| {
format!("While loading object '{}'", module_config.file_name())
})
})
.collect(),
);
});
});
let duration = start.elapsed();
let (mut obj, dep_v) = dol_result.unwrap()?;
let mut function_count = obj.symbols.by_kind(ObjSymbolKind::Function).count();
dep.extend(dep_v);
let mut modules = BTreeMap::<u32, (&ModuleConfig, ObjInfo)>::new();
for (idx, (module_obj, dep_v)) in modules_result.unwrap()?.into_iter().enumerate() {
function_count += module_obj.symbols.by_kind(ObjSymbolKind::Function).count();
dep.extend(dep_v);
match modules.entry(module_obj.module_id) {
Entry::Vacant(e) => e.insert((&config.modules[idx], module_obj)),
Entry::Occupied(_) => bail!("Duplicate module ID {}", obj.module_id),
};
}
info!(
"Initial analysis completed in {}.{:03}s (found {} functions)",
duration.as_secs(),
duration.subsec_millis(),
function_count
);
if !modules.is_empty() {
let module_ids = modules.keys().cloned().collect_vec();
// Create any missing symbols (referenced from other modules) and set FORCEACTIVE
update_symbols(&mut obj, &modules)?;
for &module_id in &module_ids {
let (module_config, mut module_obj) = modules.remove(&module_id).unwrap();
update_symbols(&mut module_obj, &modules)?;
modules.insert(module_id, (module_config, module_obj));
}
// Create relocations to symbols in other modules
for &module_id in &module_ids {
let (module_config, mut module_obj) = modules.remove(&module_id).unwrap();
create_relocations(&mut module_obj, &modules, &obj)?;
modules.insert(module_id, (module_config, module_obj));
}
// Replace external relocations with internal ones, creating extern symbols
resolve_external_relocations(&mut obj, &modules, None)?;
for &module_id in &module_ids {
let (module_config, mut module_obj) = modules.remove(&module_id).unwrap();
resolve_external_relocations(&mut module_obj, &modules, Some(&obj))?;
modules.insert(module_id, (module_config, module_obj));
}
}
// Create out dirs
DirBuilder::new().recursive(true).create(&args.out_dir)?;
touch(&args.out_dir)?;
let include_dir = args.out_dir.join("include");
DirBuilder::new().recursive(true).create(&include_dir)?;
fs::write(include_dir.join("macros.inc"), include_str!("../../assets/macros.inc"))?;
info!("Rebuilding relocations and splitting");
let mut dol_result: Option<Result<OutputModule>> = None;
let mut modules_result: Option<Result<Vec<OutputModule>>> = None;
let start = Instant::now();
rayon::scope(|s| {
// DOL
s.spawn(|_| {
let _span =
info_span!("module", name = %config.base.name(), id = obj.module_id).entered();
dol_result = Some(
split_write_obj(&mut obj, &config, &config.base, &args.out_dir, args.no_update)
.with_context(|| {
format!(
"While processing object '{}' (module ID {})",
config.base.file_name(),
obj.module_id
)
}),
);
});
// Modules
s.spawn(|_| {
modules_result = Some(
modules
.par_iter_mut()
.map(|(&module_id, (module_config, module_obj))| {
let _span =
info_span!("module", name = %module_config.name(), id = module_id)
.entered();
let out_dir = args.out_dir.join(module_config.name().as_ref());
split_write_obj(
module_obj,
&config,
module_config,
&out_dir,
args.no_update,
)
.with_context(|| {
format!(
"While processing object '{}' (module ID {})",
module_config.file_name(),
module_id
)
})
})
.collect(),
);
});
});
let duration = start.elapsed();
let out_config = OutputConfig { base: dol_result.unwrap()?, modules: modules_result.unwrap()? };
let mut object_count = out_config.base.units.len();
for module in &out_config.modules {
object_count += module.units.len();
}
info!(
"Splitting completed in {}.{:03}s (wrote {} objects)",
duration.as_secs(),
duration.subsec_millis(),
object_count
);
// Write output config
{
let mut out_file = buf_writer(&out_config_path)?;
serde_json::to_writer_pretty(&mut out_file, &out_config)?;
out_file.flush()?;
} }
// Write dep file // Write dep file
@ -826,6 +998,8 @@ fn split(args: SplitArgs) -> Result<()> {
// validate(&obj, file, &state)?; // validate(&obj, file, &state)?;
// } // }
let duration = command_start.elapsed();
info!("Total duration: {}.{:03}s", duration.as_secs(), duration.subsec_millis());
Ok(()) Ok(())
} }
@ -980,10 +1154,10 @@ fn diff(args: DiffArgs) -> Result<()> {
.with_context(|| format!("Failed to open config file '{}'", args.config.display()))?; .with_context(|| format!("Failed to open config file '{}'", args.config.display()))?;
let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?; let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?;
log::info!("Loading {}", config.object.display()); log::info!("Loading {}", config.base.object.display());
let mut obj = process_dol(&config.object)?; let mut obj = process_dol(&config.base.object)?;
if let Some(symbols_path) = &config.symbols { if let Some(symbols_path) = &config.base.symbols {
apply_symbols_file(symbols_path, &mut obj)?; apply_symbols_file(symbols_path, &mut obj)?;
} }
@ -1116,10 +1290,10 @@ fn apply(args: ApplyArgs) -> Result<()> {
.with_context(|| format!("Failed to open config file '{}'", args.config.display()))?; .with_context(|| format!("Failed to open config file '{}'", args.config.display()))?;
let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?; let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?;
log::info!("Loading {}", config.object.display()); log::info!("Loading {}", config.base.object.display());
let mut obj = process_dol(&config.object)?; let mut obj = process_dol(&config.base.object)?;
if let Some(symbols_path) = &config.symbols { if let Some(symbols_path) = &config.base.symbols {
if !apply_symbols_file(symbols_path, &mut obj)? { if !apply_symbols_file(symbols_path, &mut obj)? {
bail!("Symbols file '{}' does not exist", symbols_path.display()); bail!("Symbols file '{}' does not exist", symbols_path.display());
} }
@ -1260,7 +1434,7 @@ fn apply(args: ApplyArgs) -> Result<()> {
} }
} }
write_symbols_file(config.symbols.as_ref().unwrap(), &obj)?; write_symbols_file(config.base.symbols.as_ref().unwrap(), &obj)?;
Ok(()) Ok(())
} }

View File

@ -1,4 +1,4 @@
use std::{ffi::OsStr, io::Write, path::PathBuf, str::FromStr}; use std::{ffi::OsStr, path::PathBuf, str::FromStr};
use argp::{FromArgValue, FromArgs}; use argp::{FromArgValue, FromArgs};
@ -86,13 +86,11 @@ enum SubCommand {
} }
fn main() { fn main() {
let args: TopLevel = argp_version::from_env(); let format = tracing_subscriber::fmt::format().with_target(false).without_time();
env_logger::Builder::from_env( tracing_subscriber::fmt().event_format(format).init();
env_logger::Env::default().default_filter_or(args.log_level.to_string()), // TODO reimplement log level selection
)
.format(|f, r| writeln!(f, "[{}] {}", r.level(), r.args()))
.init();
let args: TopLevel = argp_version::from_env();
let mut result = Ok(()); let mut result = Ok(());
if let Some(dir) = &args.chdir { if let Some(dir) = &args.chdir {
result = std::env::set_current_dir(dir).map_err(|e| { result = std::env::set_current_dir(dir).map_err(|e| {

View File

@ -11,11 +11,11 @@ use std::{
use anyhow::{anyhow, bail, ensure, Result}; use anyhow::{anyhow, bail, ensure, Result};
pub use relocations::{ObjReloc, ObjRelocKind, ObjRelocations}; pub use relocations::{ObjReloc, ObjRelocKind, ObjRelocations};
pub use sections::{section_kind_for_section, ObjSection, ObjSectionKind, ObjSections}; pub use sections::{ObjSection, ObjSectionKind, ObjSections};
pub use splits::{ObjSplit, ObjSplits}; pub use splits::{ObjSplit, ObjSplits};
pub use symbols::{ pub use symbols::{
ObjDataKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, best_match_for_reloc, ObjDataKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
ObjSymbols, SymbolIndex, ObjSymbolScope, ObjSymbols, SymbolIndex,
}; };
use crate::util::{comment::MWComment, rel::RelReloc}; use crate::util::{comment::MWComment, rel::RelReloc};

View File

@ -206,7 +206,7 @@ impl ObjSection {
} }
} }
pub fn section_kind_for_section(section_name: &str) -> Result<ObjSectionKind> { fn section_kind_for_section(section_name: &str) -> Result<ObjSectionKind> {
Ok(match section_name { Ok(match section_name {
".init" | ".text" | ".dbgtext" | ".vmtext" => ObjSectionKind::Code, ".init" | ".text" | ".dbgtext" | ".vmtext" => ObjSectionKind::Code,
".ctors" | ".dtors" | ".rodata" | ".sdata2" | "extab" | "extabindex" => { ".ctors" | ".dtors" | ".rodata" | ".sdata2" | "extab" | "extabindex" => {

View File

@ -170,9 +170,7 @@ impl ObjSymbols {
let mut symbols_by_section: Vec<BTreeMap<u32, Vec<SymbolIndex>>> = vec![]; let mut symbols_by_section: Vec<BTreeMap<u32, Vec<SymbolIndex>>> = vec![];
let mut symbols_by_name = HashMap::<String, Vec<SymbolIndex>>::new(); let mut symbols_by_name = HashMap::<String, Vec<SymbolIndex>>::new();
for (idx, symbol) in symbols.iter().enumerate() { for (idx, symbol) in symbols.iter().enumerate() {
if obj_kind == ObjKind::Executable {
symbols_by_address.nested_push(symbol.address as u32, idx); symbols_by_address.nested_push(symbol.address as u32, idx);
}
if let Some(section_idx) = symbol.section { if let Some(section_idx) = symbol.section {
if section_idx >= symbols_by_section.len() { if section_idx >= symbols_by_section.len() {
symbols_by_section.resize_with(section_idx + 1, BTreeMap::new); symbols_by_section.resize_with(section_idx + 1, BTreeMap::new);
@ -209,7 +207,8 @@ impl ObjSymbols {
let target_symbol_idx = if let Some((symbol_idx, existing)) = opt { let target_symbol_idx = if let Some((symbol_idx, existing)) = opt {
let size = let size =
if existing.size_known && in_symbol.size_known && existing.size != in_symbol.size { if existing.size_known && in_symbol.size_known && existing.size != in_symbol.size {
log::warn!( // TODO fix and promote back to warning
log::debug!(
"Conflicting size for {}: was {:#X}, now {:#X}", "Conflicting size for {}: was {:#X}, now {:#X}",
existing.name, existing.name,
existing.size, existing.size,
@ -277,9 +276,7 @@ impl ObjSymbols {
pub fn add_direct(&mut self, in_symbol: ObjSymbol) -> Result<SymbolIndex> { pub fn add_direct(&mut self, in_symbol: ObjSymbol) -> Result<SymbolIndex> {
let symbol_idx = self.symbols.len(); let symbol_idx = self.symbols.len();
if self.obj_kind == ObjKind::Executable {
self.symbols_by_address.nested_push(in_symbol.address as u32, symbol_idx); self.symbols_by_address.nested_push(in_symbol.address as u32, symbol_idx);
}
if let Some(section_idx) = in_symbol.section { if let Some(section_idx) = in_symbol.section {
if section_idx >= self.symbols_by_section.len() { if section_idx >= self.symbols_by_section.len() {
self.symbols_by_section.resize_with(section_idx + 1, BTreeMap::new); self.symbols_by_section.resize_with(section_idx + 1, BTreeMap::new);
@ -446,7 +443,7 @@ impl ObjSymbols {
// ensure!(self.obj_kind == ObjKind::Executable); // ensure!(self.obj_kind == ObjKind::Executable);
let mut result = None; let mut result = None;
for (_addr, symbol_idxs) in self.indexes_for_range(..=target_addr.address).rev() { for (_addr, symbol_idxs) in self.indexes_for_range(..=target_addr.address).rev() {
let mut symbols = symbol_idxs let symbols = symbol_idxs
.iter() .iter()
.map(|&idx| (idx, &self.symbols[idx])) .map(|&idx| (idx, &self.symbols[idx]))
.filter(|(_, sym)| { .filter(|(_, sym)| {
@ -454,42 +451,8 @@ impl ObjSymbols {
&& sym.referenced_by(reloc_kind) && sym.referenced_by(reloc_kind)
}) })
.collect_vec(); .collect_vec();
let (symbol_idx, symbol) = if symbols.len() == 1 { let Some((symbol_idx, symbol)) = best_match_for_reloc(symbols, reloc_kind) else {
symbols.pop().unwrap() continue;
} else {
symbols.sort_by_key(|&(_, symbol)| {
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
if !symbol.name.starts_with("..") =>
{
3
}
_ => 1,
},
ObjSymbolKind::Section => -1,
};
if symbol.size > 0 {
rank += 1;
}
-rank
});
match symbols.first() {
Some(&v) => v,
None => continue,
}
}; };
if symbol.address == target_addr.address as u64 { if symbol.address == target_addr.address as u64 {
result = Some((symbol_idx, symbol)); result = Some((symbol_idx, symbol));
@ -549,3 +512,42 @@ impl ObjSymbol {
} }
} }
} }
pub fn best_match_for_reloc(
mut symbols: Vec<(SymbolIndex, &ObjSymbol)>,
reloc_kind: ObjRelocKind,
) -> Option<(SymbolIndex, &ObjSymbol)> {
if symbols.len() == 1 {
return symbols.into_iter().next();
}
symbols.sort_by_key(|&(_, symbol)| {
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
if !symbol.name.starts_with("..") =>
{
3
}
_ => 1,
},
ObjSymbolKind::Section => -1,
};
if symbol.size > 0 {
rank += 1;
}
-rank
});
symbols.into_iter().next()
}

View File

@ -12,8 +12,8 @@ use regex::{Captures, Regex};
use crate::{ use crate::{
obj::{ obj::{
ObjDataKind, ObjInfo, ObjKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjDataKind, ObjInfo, ObjKind, ObjSectionKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet,
ObjSymbolKind, ObjUnit, ObjSymbolFlags, ObjSymbolKind, ObjUnit,
}, },
util::file::{buf_writer, map_file, map_reader}, util::file::{buf_writer, map_file, map_reader},
}; };
@ -163,9 +163,6 @@ pub fn write_symbols<W: Write>(w: &mut W, obj: &ObjInfo) -> Result<()> {
} }
fn write_symbol<W: Write>(w: &mut W, obj: &ObjInfo, symbol: &ObjSymbol) -> Result<()> { fn write_symbol<W: Write>(w: &mut W, obj: &ObjInfo, symbol: &ObjSymbol) -> Result<()> {
// if let Some(demangled_name) = &symbol.demangled_name {
// writeln!(w, "// {demangled_name}")?;
// }
write!(w, "{} = ", symbol.name)?; write!(w, "{} = ", symbol.name)?;
let section = symbol.section.and_then(|idx| obj.sections.get(idx)); let section = symbol.section.and_then(|idx| obj.sections.get(idx));
if let Some(section) = section { if let Some(section) = section {
@ -173,16 +170,6 @@ fn write_symbol<W: Write>(w: &mut W, obj: &ObjInfo, symbol: &ObjSymbol) -> Resul
} }
write!(w, "{:#010X}; //", symbol.address)?; write!(w, "{:#010X}; //", symbol.address)?;
write!(w, " type:{}", symbol_kind_to_str(symbol.kind))?; write!(w, " type:{}", symbol_kind_to_str(symbol.kind))?;
// if let Some(section) = section {
// match section.kind {
// ObjSectionKind::Code => {
// write!(w, " type:function")?;
// }
// ObjSectionKind::Data | ObjSectionKind::ReadOnlyData | ObjSectionKind::Bss => {
// write!(w, " type:object")?;
// }
// }
// }
if symbol.size_known && symbol.size > 0 { if symbol.size_known && symbol.size > 0 {
write!(w, " size:{:#X}", symbol.size)?; write!(w, " size:{:#X}", symbol.size)?;
} }
@ -287,6 +274,27 @@ fn symbol_data_kind_from_str(s: &str) -> Option<ObjDataKind> {
} }
} }
#[inline]
fn section_kind_from_str(s: &str) -> Option<ObjSectionKind> {
match s {
"code" | "text" => Some(ObjSectionKind::Code),
"data" => Some(ObjSectionKind::Data),
"rodata" => Some(ObjSectionKind::ReadOnlyData),
"bss" => Some(ObjSectionKind::Bss),
_ => None,
}
}
#[inline]
fn section_kind_to_str(kind: ObjSectionKind) -> &'static str {
match kind {
ObjSectionKind::Code => "code",
ObjSectionKind::Data => "data",
ObjSectionKind::ReadOnlyData => "rodata",
ObjSectionKind::Bss => "bss",
}
}
#[inline] #[inline]
pub fn write_splits_file<P: AsRef<Path>>(path: P, obj: &ObjInfo, all: bool) -> Result<()> { pub fn write_splits_file<P: AsRef<Path>>(path: P, obj: &ObjInfo, all: bool) -> Result<()> {
let mut w = buf_writer(path)?; let mut w = buf_writer(path)?;
@ -298,7 +306,11 @@ pub fn write_splits_file<P: AsRef<Path>>(path: P, obj: &ObjInfo, all: bool) -> R
pub fn write_splits<W: Write>(w: &mut W, obj: &ObjInfo, all: bool) -> Result<()> { pub fn write_splits<W: Write>(w: &mut W, obj: &ObjInfo, all: bool) -> Result<()> {
writeln!(w, "Sections:")?; writeln!(w, "Sections:")?;
for (_, section) in obj.sections.iter() { for (_, section) in obj.sections.iter() {
write!(w, "\t{:<11} type:{:?} align:{:#X}", section.name, section.kind, section.align)?; write!(w, "\t{:<11} type:{}", section.name, section_kind_to_str(section.kind))?;
if section.align > 0 {
write!(w, " align:{}", section.align)?;
}
writeln!(w)?;
} }
for unit in obj.link_order.iter().filter(|unit| all || !unit.autogenerated) { for unit in obj.link_order.iter().filter(|unit| all || !unit.autogenerated) {
write!(w, "\n{}:", unit.name)?; write!(w, "\n{}:", unit.name)?;
@ -350,13 +362,21 @@ struct SplitUnit {
comment_version: Option<u8>, comment_version: Option<u8>,
} }
struct SectionDef {
name: String,
kind: Option<ObjSectionKind>,
align: Option<u32>,
}
enum SplitLine { enum SplitLine {
Unit(SplitUnit), Unit(SplitUnit),
Section(SplitSection), UnitSection(SplitSection),
SectionsStart,
Section(SectionDef),
None, None,
} }
fn parse_split_line(line: &str) -> Result<SplitLine> { fn parse_split_line(line: &str, state: &SplitState) -> Result<SplitLine> {
static UNIT_LINE: Lazy<Regex> = static UNIT_LINE: Lazy<Regex> =
Lazy::new(|| Regex::new("^\\s*(?P<name>[^\\s:]+)\\s*:\\s*(?P<attrs>.*)$").unwrap()); Lazy::new(|| Regex::new("^\\s*(?P<name>[^\\s:]+)\\s*:\\s*(?P<attrs>.*)$").unwrap());
static SECTION_LINE: Lazy<Regex> = static SECTION_LINE: Lazy<Regex> =
@ -368,14 +388,19 @@ fn parse_split_line(line: &str) -> Result<SplitLine> {
} else if let Some(captures) = UNIT_LINE.captures(line) { } else if let Some(captures) = UNIT_LINE.captures(line) {
parse_unit_line(captures).with_context(|| format!("While parsing split line: '{line}'")) parse_unit_line(captures).with_context(|| format!("While parsing split line: '{line}'"))
} else if let Some(captures) = SECTION_LINE.captures(line) { } else if let Some(captures) = SECTION_LINE.captures(line) {
parse_section_line(captures).with_context(|| format!("While parsing split line: '{line}'")) parse_section_line(captures, state)
.with_context(|| format!("While parsing split line: '{line}'"))
} else { } else {
Err(anyhow!("Failed to parse split line: '{line}'")) Err(anyhow!("Failed to parse split line: '{line}'"))
} }
} }
fn parse_unit_line(captures: Captures) -> Result<SplitLine> { fn parse_unit_line(captures: Captures) -> Result<SplitLine> {
let mut unit = SplitUnit { name: captures["name"].to_string(), comment_version: None }; let name = &captures["name"];
if name == "Sections" {
return Ok(SplitLine::SectionsStart);
}
let mut unit = SplitUnit { name: name.to_string(), comment_version: None };
for attr in captures["attrs"].split(' ').filter(|&s| !s.is_empty()) { for attr in captures["attrs"].split(' ').filter(|&s| !s.is_empty()) {
if let Some((attr, value)) = attr.split_once(':') { if let Some((attr, value)) = attr.split_once(':') {
@ -391,7 +416,33 @@ fn parse_unit_line(captures: Captures) -> Result<SplitLine> {
Ok(SplitLine::Unit(unit)) Ok(SplitLine::Unit(unit))
} }
fn parse_section_line(captures: Captures) -> Result<SplitLine> { fn parse_section_line(captures: Captures, state: &SplitState) -> Result<SplitLine> {
if matches!(state, SplitState::Sections(_)) {
let name = &captures["name"];
let mut section = SectionDef { name: name.to_string(), kind: None, align: None };
for attr in captures["attrs"].split(' ').filter(|&s| !s.is_empty()) {
if let Some((attr, value)) = attr.split_once(':') {
match attr {
"type" => {
section.kind = Some(
section_kind_from_str(value)
.ok_or_else(|| anyhow!("Unknown section type '{}'", value))?,
);
}
"align" => {
section.align = Some(u32::from_str(value)?);
}
_ => bail!("Unknown section attribute '{attr}'"),
}
} else {
bail!("Unknown section attribute '{attr}'");
}
}
return Ok(SplitLine::Section(section));
}
let mut section = SplitSection { let mut section = SplitSection {
name: captures["name"].to_string(), name: captures["name"].to_string(),
start: 0, start: 0,
@ -423,27 +474,39 @@ fn parse_section_line(captures: Captures) -> Result<SplitLine> {
} }
} }
if section.start > 0 && section.end > 0 { if section.start > 0 && section.end > 0 {
Ok(SplitLine::Section(section)) Ok(SplitLine::UnitSection(section))
} else { } else {
Err(anyhow!("Section '{}' missing start or end address", section.name)) Err(anyhow!("Section '{}' missing start or end address", section.name))
} }
} }
pub fn apply_splits<R: BufRead>(r: R, obj: &mut ObjInfo) -> Result<()> {
enum SplitState { enum SplitState {
None, None,
Sections(usize),
Unit(String), Unit(String),
} }
pub fn apply_splits_file<P: AsRef<Path>>(path: P, obj: &mut ObjInfo) -> Result<bool> {
Ok(if path.as_ref().is_file() {
let map = map_file(path)?;
apply_splits(map_reader(&map), obj)?;
true
} else {
false
})
}
pub fn apply_splits<R: BufRead>(r: R, obj: &mut ObjInfo) -> Result<()> {
let mut state = SplitState::None; let mut state = SplitState::None;
for result in r.lines() { for result in r.lines() {
let line = match result { let line = match result {
Ok(line) => line, Ok(line) => line,
Err(e) => return Err(e.into()), Err(e) => return Err(e.into()),
}; };
let split_line = parse_split_line(&line)?; let split_line = parse_split_line(&line, &state)?;
match (&mut state, split_line) { match (&mut state, split_line) {
( (
SplitState::None | SplitState::Unit(_), SplitState::None | SplitState::Unit(_) | SplitState::Sections(_),
SplitLine::Unit(SplitUnit { name, comment_version }), SplitLine::Unit(SplitUnit { name, comment_version }),
) => { ) => {
obj.link_order.push(ObjUnit { obj.link_order.push(ObjUnit {
@ -453,12 +516,36 @@ pub fn apply_splits<R: BufRead>(r: R, obj: &mut ObjInfo) -> Result<()> {
}); });
state = SplitState::Unit(name); state = SplitState::Unit(name);
} }
(SplitState::None, SplitLine::Section(SplitSection { name, .. })) => { (SplitState::None, SplitLine::UnitSection(SplitSection { name, .. })) => {
bail!("Section {} defined outside of unit", name); bail!("Section {} defined outside of unit", name);
} }
(SplitState::None | SplitState::Unit(_), SplitLine::SectionsStart) => {
state = SplitState::Sections(0);
}
(SplitState::Sections(index), SplitLine::Section(SectionDef { name, kind, align })) => {
let Some(obj_section) = obj.sections.get_mut(*index) else {
bail!(
"Section out of bounds: {} (index {}), object has {} sections",
name,
index,
obj.sections.count()
);
};
if let Err(_) = obj_section.rename(name.clone()) {
// Manual section
obj_section.kind =
kind.ok_or_else(|| anyhow!("Section '{}' missing type", name))?;
obj_section.name = name;
obj_section.section_known = true;
}
if let Some(align) = align {
obj_section.align = align as u64;
}
*index += 1;
}
( (
SplitState::Unit(unit), SplitState::Unit(unit),
SplitLine::Section(SplitSection { name, start, end, align, common, rename }), SplitLine::UnitSection(SplitSection { name, start, end, align, common, rename }),
) => { ) => {
let (section_index, _) = match obj.sections.by_name(&name)? { let (section_index, _) = match obj.sections.by_name(&name)? {
Some(v) => Ok(v), Some(v) => Ok(v),

View File

@ -1,5 +1,7 @@
use std::{io::Write, path::PathBuf}; use std::{io::Write, path::PathBuf};
use path_slash::PathBufExt;
pub struct DepFile { pub struct DepFile {
pub name: PathBuf, pub name: PathBuf,
pub dependencies: Vec<PathBuf>, pub dependencies: Vec<PathBuf>,
@ -10,10 +12,12 @@ impl DepFile {
pub fn push(&mut self, dependency: PathBuf) { self.dependencies.push(dependency); } pub fn push(&mut self, dependency: PathBuf) { self.dependencies.push(dependency); }
pub fn extend(&mut self, dependencies: Vec<PathBuf>) { self.dependencies.extend(dependencies); }
pub fn write<W: Write>(&self, w: &mut W) -> std::io::Result<()> { pub fn write<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
write!(w, "{}:", self.name.display())?; write!(w, "{}:", self.name.to_slash_lossy())?;
for dep in &self.dependencies { for dep in &self.dependencies {
write!(w, " \\\n {}", dep.display())?; write!(w, " \\\n {}", dep.to_slash_lossy())?;
} }
Ok(()) Ok(())
} }

View File

@ -8,6 +8,7 @@ use anyhow::{anyhow, Context, Result};
use byteorder::ReadBytesExt; use byteorder::ReadBytesExt;
use filetime::{set_file_mtime, FileTime}; use filetime::{set_file_mtime, FileTime};
use memmap2::{Mmap, MmapOptions}; use memmap2::{Mmap, MmapOptions};
use path_slash::PathBufExt;
use crate::util::{rarc, rarc::Node, yaz0}; use crate::util::{rarc, rarc::Node, yaz0};
@ -80,7 +81,7 @@ pub fn process_rsp(files: &[PathBuf]) -> Result<Vec<PathBuf>> {
for result in reader.lines() { for result in reader.lines() {
let line = result?; let line = result?;
if !line.is_empty() { if !line.is_empty() {
out.push(PathBuf::from(line)); out.push(PathBuf::from_slash(line));
} }
} }
} else if path_str.contains('*') { } else if path_str.contains('*') {

View File

@ -2,13 +2,18 @@ use std::path::PathBuf;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use itertools::Itertools; use itertools::Itertools;
use path_slash::PathBufExt;
use crate::obj::ObjInfo; use crate::obj::{ObjInfo, ObjKind};
#[inline] #[inline]
const fn align_up(value: u32, align: u32) -> u32 { (value + (align - 1)) & !(align - 1) } const fn align_up(value: u32, align: u32) -> u32 { (value + (align - 1)) & !(align - 1) }
pub fn generate_ldscript(obj: &ObjInfo, auto_force_files: bool) -> Result<String> { pub fn generate_ldscript(obj: &ObjInfo, auto_force_files: bool) -> Result<String> {
if obj.kind == ObjKind::Relocatable {
return generate_ldscript_partial(obj, auto_force_files);
}
let origin = obj.sections.iter().map(|(_, s)| s.address).min().unwrap(); let origin = obj.sections.iter().map(|(_, s)| s.address).min().unwrap();
let stack_size = match (obj.stack_address, obj.stack_end) { let stack_size = match (obj.stack_address, obj.stack_end) {
(Some(stack_address), Some(stack_end)) => stack_address - stack_end, (Some(stack_address), Some(stack_end)) => stack_address - stack_end,
@ -76,10 +81,38 @@ pub fn generate_ldscript(obj: &ObjInfo, auto_force_files: bool) -> Result<String
Ok(out) Ok(out)
} }
pub fn generate_ldscript_partial(obj: &ObjInfo, auto_force_files: bool) -> Result<String> {
let section_defs =
obj.sections.iter().map(|(_, s)| format!("{} :{{}}", s.name)).join("\n ");
let mut force_files = Vec::with_capacity(obj.link_order.len());
for unit in &obj.link_order {
let obj_path = obj_path_for_unit(&unit.name);
force_files.push(obj_path.file_name().unwrap().to_str().unwrap().to_string());
}
let mut force_active = vec![];
for symbol in obj.symbols.iter() {
if symbol.flags.is_force_active() && symbol.flags.is_global() {
force_active.push(symbol.name.clone());
}
}
let mut out = include_str!("../../assets/ldscript_partial.lcf")
.replace("$SECTIONS", &section_defs)
.replace("$FORCEACTIVE", &force_active.join("\n "));
out = if auto_force_files {
out.replace("$FORCEFILES", &force_files.join("\n "))
} else {
out.replace("$FORCEFILES", "")
};
Ok(out)
}
pub fn obj_path_for_unit(unit: &str) -> PathBuf { pub fn obj_path_for_unit(unit: &str) -> PathBuf {
PathBuf::from(unit).with_extension("").with_extension("o") PathBuf::from_slash(unit).with_extension("").with_extension("o")
} }
pub fn asm_path_for_unit(unit: &str) -> PathBuf { pub fn asm_path_for_unit(unit: &str) -> PathBuf {
PathBuf::from(unit).with_extension("").with_extension("s") PathBuf::from_slash(unit).with_extension("").with_extension("s")
} }

View File

@ -1,7 +1,7 @@
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_mut)] #![allow(unused_mut)]
use std::{ use std::{
collections::{btree_map, BTreeMap, HashMap, HashSet}, collections::{btree_map, BTreeMap, HashMap},
hash::Hash, hash::Hash,
io::BufRead, io::BufRead,
mem::replace, mem::replace,
@ -15,10 +15,7 @@ use once_cell::sync::Lazy;
use regex::{Captures, Regex}; use regex::{Captures, Regex};
use crate::{ use crate::{
obj::{ obj::{ObjInfo, ObjKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind},
section_kind_for_section, ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
ObjSymbolKind,
},
util::file::{map_file, map_reader}, util::file::{map_file, map_reader},
}; };
@ -65,86 +62,6 @@ struct SectionOrder {
#[inline] #[inline]
fn is_code_section(section: &str) -> bool { matches!(section, ".text" | ".init") } fn is_code_section(section: &str) -> bool { matches!(section, ".text" | ".init") }
/// Iterate over the BTreeMap and generate an ordered list of symbols and TUs by address.
fn resolve_section_order(
_address_to_symbol: &BTreeMap<u32, SymbolRef>,
symbol_entries: &mut HashMap<SymbolRef, SymbolEntry>,
) -> Result<SectionOrder> {
let ordering = SectionOrder::default();
// let mut last_unit = String::new();
// let mut last_section = String::new();
// let mut section_unit_idx = 0usize;
// for symbol_ref in address_to_symbol.values() {
// if let Some(symbol) = symbol_entries.get_mut(symbol_ref) {
// if last_unit != symbol.unit {
// if last_section != symbol.section {
// ordering.unit_order.push((symbol.section.clone(), vec![]));
// section_unit_idx = ordering.unit_order.len() - 1;
// last_section = symbol.section.clone();
// }
// let unit_order = &mut ordering.unit_order[section_unit_idx];
// if unit_order.1.contains(&symbol.unit) {
// // With -common on, .bss is split into two parts. The TU order repeats
// // at the end with all globally-deduplicated BSS symbols. Once we detect
// // a duplicate inside of .bss, we create a new section and start again.
// // TODO the first entry in .comm *could* be a TU without regular .bss
// if symbol.section == ".bss" {
// log::debug!(".comm section detected, duplicate {}", symbol.unit);
// ordering.unit_order.push((".comm".to_string(), vec![symbol.unit.clone()]));
// section_unit_idx = ordering.unit_order.len() - 1;
// } else {
// bail!(
// "TU order conflict: {} exists multiple times in {}.",
// symbol.unit, symbol.section,
// );
// }
// } else {
// unit_order.1.push(symbol.unit.clone());
// }
// last_unit = symbol.unit.clone();
// }
// // For ASM-generated objects, notype,local symbols in .text
// // are usually local jump labels, and should be ignored.
// if is_code_section(&symbol.section)
// && symbol.size == 0
// && symbol.kind == SymbolKind::NoType
// && symbol.visibility == SymbolVisibility::Local
// {
// // Being named something other than lbl_* could indicate
// // that it's actually a local function, but let's just
// // make the user resolve that if necessary.
// if !symbol.name.starts_with("lbl_") {
// log::warn!("Skipping local text symbol {}", symbol.name);
// }
// continue;
// }
// // Guess the symbol type if necessary.
// if symbol.kind == SymbolKind::NoType {
// if is_code_section(&symbol.section) {
// symbol.kind = SymbolKind::Function;
// } else {
// symbol.kind = SymbolKind::Object;
// }
// }
// ordering.symbol_order.push(symbol_ref.clone());
// } else {
// bail!("Symbol has address but no entry: {symbol_ref:?}");
// }
// }
for iter in ordering.symbol_order.windows(2) {
let next_address = symbol_entries.get(&iter[1]).unwrap().address;
let symbol = symbol_entries.get_mut(&iter[0]).unwrap();
// For ASM-generated objects, we need to guess the symbol size.
if symbol.size == 0 {
symbol.size = next_address - symbol.address;
}
}
Ok(ordering)
}
macro_rules! static_regex { macro_rules! static_regex {
($name:ident, $str:expr) => { ($name:ident, $str:expr) => {
static $name: Lazy<Regex> = Lazy::new(|| Regex::new($str).unwrap()); static $name: Lazy<Regex> = Lazy::new(|| Regex::new($str).unwrap());
@ -171,7 +88,7 @@ static_regex!(LINK_MAP_EXTERN_SYMBOL, "^\\s*>>> SYMBOL NOT FOUND: (.*)$");
static_regex!(SECTION_LAYOUT_START, "^(?P<section>.*) section layout$"); static_regex!(SECTION_LAYOUT_START, "^(?P<section>.*) section layout$");
static_regex!( static_regex!(
SECTION_LAYOUT_SYMBOL, SECTION_LAYOUT_SYMBOL,
"^\\s*(?P<rom_addr>[0-9A-Fa-f]+|UNUSED)\\s+(?P<size>[0-9A-Fa-f]+)\\s+(?P<addr>[0-9A-Fa-f]{8}|\\.{8})\\s+(?P<offset>[0-9A-Fa-f]{8}|\\.{8})\\s+(?P<align>\\d+)?\\s*(?P<sym>.*?)(?:\\s+\\(entry of (?P<entry_of>.*?)\\))?\\s+(?P<tu>.*)$" "^\\s*(?P<rom_addr>[0-9A-Fa-f]+|UNUSED)\\s+(?P<size>[0-9A-Fa-f]+)\\s+(?P<addr>[0-9A-Fa-f]{8}|\\.{8})(?:\\s+(?P<offset>[0-9A-Fa-f]{8}|\\.{8}))?\\s+(?P<align>\\d+)?\\s*(?P<sym>.*?)(?:\\s+\\(entry of (?P<entry_of>.*?)\\))?\\s+(?P<tu>.*)$"
); );
static_regex!( static_regex!(
SECTION_LAYOUT_HEADER, SECTION_LAYOUT_HEADER,
@ -187,11 +104,12 @@ static_regex!(MEMORY_MAP_ENTRY, "^\\s*(?P<section>\\S+)\\s+(?P<addr>[0-9A-Fa-f]+
static_regex!(LINKER_SYMBOLS_START, "^\\s*Linker generated symbols:\\s*$"); static_regex!(LINKER_SYMBOLS_START, "^\\s*Linker generated symbols:\\s*$");
static_regex!(LINKER_SYMBOL_ENTRY, "^\\s*(?P<name>\\S+)\\s+(?P<addr>[0-9A-Fa-f]+|\\.{0,8})\\s*$"); static_regex!(LINKER_SYMBOL_ENTRY, "^\\s*(?P<name>\\S+)\\s+(?P<addr>[0-9A-Fa-f]+|\\.{0,8})\\s*$");
#[derive(Debug)]
pub struct SectionInfo { pub struct SectionInfo {
name: String, pub name: String,
address: u32, pub address: u32,
size: u32, pub size: u32,
file_offset: u32, pub file_offset: u32,
} }
#[derive(Default)] #[derive(Default)]
@ -200,11 +118,7 @@ pub struct MapInfo {
pub unit_entries: MultiMap<String, SymbolRef>, pub unit_entries: MultiMap<String, SymbolRef>,
pub entry_references: MultiMap<SymbolRef, SymbolRef>, pub entry_references: MultiMap<SymbolRef, SymbolRef>,
pub entry_referenced_from: MultiMap<SymbolRef, SymbolRef>, pub entry_referenced_from: MultiMap<SymbolRef, SymbolRef>,
// pub address_to_symbol: BTreeMap<u32, SymbolRef>, pub sections: Vec<SectionInfo>,
// pub unit_section_ranges: HashMap<String, HashMap<String, Range<u32>>>,
// pub symbol_order: Vec<SymbolRef>,
// pub unit_order: Vec<(String, Vec<String>)>,
pub sections: BTreeMap<u32, SectionInfo>,
pub link_map_symbols: HashMap<SymbolRef, SymbolEntry>, pub link_map_symbols: HashMap<SymbolRef, SymbolEntry>,
pub section_symbols: HashMap<String, BTreeMap<u32, Vec<SymbolEntry>>>, pub section_symbols: HashMap<String, BTreeMap<u32, Vec<SymbolEntry>>>,
pub section_units: HashMap<String, Vec<(u32, String)>>, pub section_units: HashMap<String, Vec<(u32, String)>>,
@ -442,34 +356,34 @@ impl StateMachine {
fn end_section_layout(mut state: SectionLayoutState, entries: &mut MapInfo) -> Result<()> { fn end_section_layout(mut state: SectionLayoutState, entries: &mut MapInfo) -> Result<()> {
// Resolve duplicate TUs // Resolve duplicate TUs
let mut existing = HashSet::new(); // let mut existing = HashSet::new();
for idx in 0..state.units.len() { // for idx in 0..state.units.len() {
let (addr, unit) = &state.units[idx]; // let (addr, unit) = &state.units[idx];
// FIXME // // FIXME
if // if
/*state.current_section == ".bss" ||*/ // /*state.current_section == ".bss" ||*/
existing.contains(unit) { // existing.contains(unit) {
if // if
/*state.current_section == ".bss" ||*/ // /*state.current_section == ".bss" ||*/
&state.units[idx - 1].1 != unit { // &state.units[idx - 1].1 != unit {
let new_name = format!("{unit}_{}_{:010X}", state.current_section, addr); // let new_name = format!("{unit}_{}_{:010X}", state.current_section, addr);
log::info!("Renaming {unit} to {new_name}"); // log::info!("Renaming {unit} to {new_name}");
for idx2 in 0..idx { // for idx2 in 0..idx {
let (addr, n_unit) = &state.units[idx2]; // let (addr, n_unit) = &state.units[idx2];
if unit == n_unit { // if unit == n_unit {
let new_name = // let new_name =
format!("{n_unit}_{}_{:010X}", state.current_section, addr); // format!("{n_unit}_{}_{:010X}", state.current_section, addr);
log::info!("Renaming 2 {n_unit} to {new_name}"); // log::info!("Renaming 2 {n_unit} to {new_name}");
state.units[idx2].1 = new_name; // state.units[idx2].1 = new_name;
break; // break;
} // }
} // }
state.units[idx].1 = new_name; // state.units[idx].1 = new_name;
} // }
} else { // } else {
existing.insert(unit.clone()); // existing.insert(unit.clone());
} // }
} // }
if !state.symbols.is_empty() { if !state.symbols.is_empty() {
entries.section_symbols.insert(state.current_section.clone(), state.symbols); entries.section_symbols.insert(state.current_section.clone(), state.symbols);
} }
@ -590,7 +504,7 @@ impl StateMachine {
let size = u32::from_str_radix(&captures["size"], 16)?; let size = u32::from_str_radix(&captures["size"], 16)?;
let file_offset = u32::from_str_radix(&captures["offset"], 16)?; let file_offset = u32::from_str_radix(&captures["offset"], 16)?;
// log::info!("Memory map entry: {section} {address:#010X} {size:#010X} {file_offset:#010X}"); // log::info!("Memory map entry: {section} {address:#010X} {size:#010X} {file_offset:#010X}");
entries.sections.insert(address, SectionInfo { entries.sections.push(SectionInfo {
name: section.to_string(), name: section.to_string(),
address, address,
size, size,
@ -640,12 +554,7 @@ pub fn process_map<R: BufRead>(reader: R) -> Result<MapInfo> {
} }
let state = replace(&mut sm.state, ProcessMapState::None); let state = replace(&mut sm.state, ProcessMapState::None);
sm.end_state(state)?; sm.end_state(state)?;
Ok(sm.result)
let entries = sm.result;
// let section_order = resolve_section_order(&entries.address_to_symbol, &mut entries.symbols)?;
// entries.symbol_order = section_order.symbol_order;
// entries.unit_order = section_order.unit_order;
Ok(entries)
} }
pub fn apply_map_file<P: AsRef<Path>>(path: P, obj: &mut ObjInfo) -> Result<()> { pub fn apply_map_file<P: AsRef<Path>>(path: P, obj: &mut ObjInfo) -> Result<()> {
@ -655,44 +564,32 @@ pub fn apply_map_file<P: AsRef<Path>>(path: P, obj: &mut ObjInfo) -> Result<()>
} }
pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> { pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
for (_section_index, section) in obj.sections.iter_mut() { for (section_index, section) in obj.sections.iter_mut() {
if let Some(info) = result.sections.get(&(section.address as u32)) { log::info!("Section {}: {} ({:?})", section_index, section.name, result.sections);
let kind = section_kind_for_section(&info.name)?; let opt = if obj.kind == ObjKind::Executable {
if section.section_known { result.sections.iter().find(|s| s.address == section.address as u32)
if section.name != info.name { } else {
result.sections.iter().filter(|s| s.size > 0).nth(section_index)
};
if let Some(info) = opt {
if section.section_known && section.name != info.name {
log::warn!("Section mismatch: was {}, map says {}", section.name, info.name); log::warn!("Section mismatch: was {}, map says {}", section.name, info.name);
} }
if section.kind != kind { if section.address != info.address as u64 {
log::warn!( log::warn!(
"Section type mismatch: {} was {:?}, map says {:?}", "Section address mismatch: was {:#010X}, map says {:#010X}",
info.name, section.address,
section.kind, info.address
kind
); );
} }
if section.size != info.size as u64 {
log::warn!(
"Section size mismatch: was {:#X}, map says {:#X}",
section.size,
info.size
);
} }
// if section.size != info.size as u64 { section.rename(info.name.clone())?;
// log::warn!(
// "Section size mismatch: {} was {:#X}, map says {:#X}",
// info.name,
// section.size,
// info.size
// );
// }
// if section.file_offset != info.file_offset as u64 {
// log::warn!(
// "Section file offset mismatch: {} was {:#X}, map says {:#X}",
// info.name,
// section.file_offset,
// info.file_offset
// );
// }
section.name = info.name.clone();
section.kind = kind;
// section.size = info.size as u64;
// section.file_offset = info.file_offset as u64;
// section.original_address = info.address as u64;
section.section_known = true;
} else { } else {
log::warn!("Section {} @ {:#010X} not found in map", section.name, section.address); log::warn!("Section {} @ {:#010X} not found in map", section.name, section.address);
} }
@ -708,33 +605,32 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
} }
} }
// Add absolute symbols // Add absolute symbols
for symbol_entry in result.link_map_symbols.values().filter(|s| s.unit.is_none()) {
add_symbol(obj, symbol_entry, None)?;
}
// Add splits
let mut section_order: Vec<(String, Vec<String>)> = Vec::new();
for (section, unit_order) in &result.section_units {
let mut units = Vec::new();
let mut existing = HashSet::new();
for (_addr, unit) in unit_order {
let unit = unit.clone();
if !existing.contains(&unit) {
units.push(unit.clone());
existing.insert(unit.clone());
}
// obj.splits.nested_push(*addr, ObjSplit {
// unit,
// end: 0, // TODO?
// align: None,
// common: false, // TODO?
// autogenerated: false,
// });
}
section_order.push((section.clone(), units));
}
// TODO // TODO
// log::info!("Section order: {:#?}", section_order); // for symbol_entry in result.link_map_symbols.values().filter(|s| s.unit.is_none()) {
// obj.link_order = resolve_link_order(&section_order)?; // add_symbol(obj, symbol_entry, None)?;
// }
// Add splits
for (section_name, unit_order) in &result.section_units {
let (_, section) = obj
.sections
.iter_mut()
.find(|(_, s)| s.name == *section_name)
.ok_or_else(|| anyhow!("Failed to locate section '{}'", section_name))?;
let mut iter = unit_order.iter().peekable();
while let Some((addr, unit)) = iter.next() {
let next = iter
.peek()
.map(|(addr, _)| *addr)
.unwrap_or_else(|| (section.address + section.size) as u32);
section.splits.push(*addr, ObjSplit {
unit: unit.clone(),
end: next,
align: None,
common: false,
autogenerated: false,
});
}
}
Ok(()) Ok(())
} }
@ -760,7 +656,7 @@ fn add_symbol(obj: &mut ObjInfo, symbol_entry: &SymbolEntry, section: Option<usi
SymbolKind::Section => ObjSymbolKind::Section, SymbolKind::Section => ObjSymbolKind::Section,
SymbolKind::NoType => ObjSymbolKind::Unknown, SymbolKind::NoType => ObjSymbolKind::Unknown,
}, },
align: None, align: symbol_entry.align,
data_kind: Default::default(), data_kind: Default::default(),
}, },
true, true,

View File

@ -144,7 +144,7 @@ pub fn apply_symbol(
align: None, align: None,
data_kind: Default::default(), data_kind: Default::default(),
}, },
true, false,
)?; )?;
Ok(target_symbol_idx) Ok(target_symbol_idx)
} }

View File

@ -6,6 +6,7 @@ use std::{
use anyhow::{anyhow, bail, ensure, Context, Result}; use anyhow::{anyhow, bail, ensure, Context, Result};
use itertools::Itertools; use itertools::Itertools;
use petgraph::{graph::NodeIndex, Graph}; use petgraph::{graph::NodeIndex, Graph};
use tracing_attributes::instrument;
use crate::{ use crate::{
analysis::{cfa::SectionAddress, read_address, read_u32}, analysis::{cfa::SectionAddress, read_address, read_u32},
@ -400,13 +401,13 @@ fn create_gap_splits(obj: &mut ObjInfo) -> Result<()> {
} }
/// Ensures that all .bss splits following a common split are also marked as common. /// Ensures that all .bss splits following a common split are also marked as common.
fn update_common_splits(obj: &mut ObjInfo) -> Result<()> { fn update_common_splits(obj: &mut ObjInfo, common_start: Option<u32>) -> Result<()> {
let Some((bss_section_index, bss_section)) = obj.sections.by_name(".bss")? else { let Some((bss_section_index, bss_section)) = obj.sections.by_name(".bss")? else {
return Ok(()); return Ok(());
}; };
let Some(common_bss_start) = let Some(common_bss_start) = common_start.or_else(|| {
bss_section.splits.iter().find(|(_, split)| split.common).map(|(addr, _)| addr) bss_section.splits.iter().find(|(_, split)| split.common).map(|(addr, _)| addr)
else { }) else {
return Ok(()); return Ok(());
}; };
log::debug!("Found common BSS start at {:#010X}", common_bss_start); log::debug!("Found common BSS start at {:#010X}", common_bss_start);
@ -434,7 +435,7 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
split.end split.end
); );
ensure!( ensure!(
split.end > 0 && split.end > addr, split.end > 0 && split.end >= addr,
"Invalid split end {} {} {:#010X}..{:#010X}", "Invalid split end {} {} {:#010X}..{:#010X}",
split.unit, split.unit,
section.name, section.name,
@ -490,7 +491,8 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
/// - Ensuring extab & extabindex entries are split with their associated function /// - Ensuring extab & extabindex entries are split with their associated function
/// - Creating splits for gaps between existing splits /// - Creating splits for gaps between existing splits
/// - Resolving a new object link order /// - Resolving a new object link order
pub fn update_splits(obj: &mut ObjInfo) -> Result<()> { #[instrument(level = "debug", skip(obj))]
pub fn update_splits(obj: &mut ObjInfo, common_start: Option<u32>) -> Result<()> {
// Create splits for extab and extabindex entries // Create splits for extab and extabindex entries
if let Some((section_index, section)) = obj.sections.by_name("extabindex")? { if let Some((section_index, section)) = obj.sections.by_name("extabindex")? {
let start = SectionAddress::new(section_index, section.address as u32); let start = SectionAddress::new(section_index, section.address as u32);
@ -519,7 +521,7 @@ pub fn update_splits(obj: &mut ObjInfo) -> Result<()> {
create_gap_splits(obj)?; create_gap_splits(obj)?;
// Update common BSS splits // Update common BSS splits
update_common_splits(obj)?; update_common_splits(obj, common_start)?;
// Ensure splits don't overlap symbols or each other // Ensure splits don't overlap symbols or each other
validate_splits(obj)?; validate_splits(obj)?;
@ -534,6 +536,7 @@ pub fn update_splits(obj: &mut ObjInfo) -> Result<()> {
/// We can use a topological sort to determine a valid global TU order. /// We can use a topological sort to determine a valid global TU order.
/// There can be ambiguities, but any solution that satisfies the link order /// There can be ambiguities, but any solution that satisfies the link order
/// constraints is considered valid. /// constraints is considered valid.
#[instrument(level = "debug", skip(obj))]
fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> { fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -619,10 +622,9 @@ fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
} }
} }
/// Split an executable object into relocatable objects. /// Split an object into multiple relocatable objects.
#[instrument(level = "debug", skip(obj))]
pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> { pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
ensure!(obj.kind == ObjKind::Executable, "Expected executable object");
let mut objects: Vec<ObjInfo> = vec![]; let mut objects: Vec<ObjInfo> = vec![];
let mut object_symbols: Vec<Vec<Option<usize>>> = vec![]; let mut object_symbols: Vec<Vec<Option<usize>>> = vec![];
let mut name_to_obj: HashMap<String, usize> = HashMap::new(); let mut name_to_obj: HashMap<String, usize> = HashMap::new();
@ -834,7 +836,19 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
// If the symbol is local, we'll upgrade the scope to global // If the symbol is local, we'll upgrade the scope to global
// and rename it to avoid conflicts // and rename it to avoid conflicts
if target_sym.flags.is_local() { if target_sym.flags.is_local() {
let address_str = format!("{:08X}", target_sym.address); let address_str = if obj.module_id == 0 {
format!("{:08X}", target_sym.address)
} else if let Some(section_index) = target_sym.section {
let target_section = &obj.sections[section_index];
format!(
"{}_{}_{:X}",
obj.module_id,
target_section.name.trim_start_matches('.'),
target_sym.address
)
} else {
bail!("Local symbol {} has no section", target_sym.name);
};
let new_name = if target_sym.name.ends_with(&address_str) { let new_name = if target_sym.name.ends_with(&address_str) {
target_sym.name.clone() target_sym.name.clone()
} else { } else {