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]]
name = "cc"
version = "1.0.79"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
@ -193,6 +196,49 @@ dependencies = [
"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]]
name = "crypto-common"
version = "0.1.6"
@ -224,7 +270,6 @@ dependencies = [
"byteorder",
"cwdemangle",
"dol",
"env_logger",
"filetime",
"fixedbitset",
"flagset",
@ -240,8 +285,10 @@ dependencies = [
"num_enum",
"object 0.31.1",
"once_cell",
"path-slash",
"petgraph",
"ppc750cl",
"rayon",
"regex",
"rmp-serde",
"serde",
@ -250,6 +297,9 @@ dependencies = [
"serde_yaml",
"sha-1",
"smallvec",
"tracing",
"tracing-attributes",
"tracing-subscriber",
]
[[package]]
@ -278,46 +328,12 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "equivalent"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "filetime"
version = "0.2.21"
@ -327,7 +343,7 @@ dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"windows-sys 0.48.0",
"windows-sys",
]
[[package]]
@ -409,12 +425,9 @@ checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "hermit-abi"
version = "0.2.6"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "hex"
@ -422,12 +435,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "indexmap"
version = "1.9.2"
@ -448,28 +455,6 @@ dependencies = [
"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]]
name = "itertools"
version = "0.11.0"
@ -485,18 +470,18 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "log"
version = "0.4.19"
@ -518,6 +503,15 @@ dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.6.2"
@ -545,6 +539,16 @@ dependencies = [
"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]]
name = "num-traits"
version = "0.2.15"
@ -554,6 +558,16 @@ dependencies = [
"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]]
name = "num_enum"
version = "0.6.1"
@ -602,12 +616,24 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "paste"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
[[package]]
name = "path-slash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
[[package]]
name = "petgraph"
version = "0.6.3"
@ -618,6 +644,12 @@ dependencies = [
"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]]
name = "ppc750cl"
version = "0.2.0"
@ -668,6 +700,28 @@ dependencies = [
"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]]
name = "redox_syscall"
version = "0.2.16"
@ -734,26 +788,18 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "ryu"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.166"
@ -820,6 +866,15 @@ dependencies = [
"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]]
name = "smallvec"
version = "1.11.0"
@ -848,15 +903,6 @@ dependencies = [
"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]]
name = "thiserror"
version = "1.0.37"
@ -877,6 +923,16 @@ dependencies = [
"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]]
name = "toml"
version = "0.5.10"
@ -886,6 +942,64 @@ dependencies = [
"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]]
name = "typenum"
version = "1.15.0"
@ -919,6 +1033,12 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
@ -941,36 +1061,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "windows-sys"
version = "0.48.0"
@ -986,93 +1082,51 @@ version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"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]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "windows_x86_64_msvc"
version = "0.48.0"

View File

@ -29,7 +29,6 @@ base64 = "0.21.2"
byteorder = "1.4.3"
cwdemangle = "0.1.5"
dol = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" }
env_logger = "0.10.0"
filetime = "0.2.21"
fixedbitset = "0.4.2"
flagset = { version = "0.4.3", features = ["serde"] }
@ -45,8 +44,10 @@ multimap = "0.9.0"
num_enum = "0.6.1"
object = { version = "0.31.1", features = ["read_core", "std", "elf", "write_std"], default-features = false }
once_cell = "1.18.0"
path-slash = "0.2.1"
petgraph = "0.6.3"
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" }
rayon = "1.7.0"
regex = "1.9.0"
serde = "1.0.166"
serde_json = "1.0.104"
@ -54,6 +55,9 @@ serde_repr = "0.1.14"
serde_yaml = "0.9.22"
sha-1 = "0.10.1"
smallvec = "1.11.0"
tracing = "0.1.37"
tracing-attributes = "0.1.26"
tracing-subscriber = "0.3.17"
[build-dependencies]
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 {
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 {
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.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(
ObjSymbol {
name: format!("jumptable_{:08X}", addr.address),
name: format!("jumptable_{}", address_str),
demangled_name: None,
address: addr.address as u64,
section: Some(addr.section),

View File

@ -5,7 +5,7 @@ use crate::{
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
obj.sections.iter_mut().filter(|(_, s)| s.kind != ObjSectionKind::Code)
{

View File

@ -151,8 +151,9 @@ impl AnalysisPass for FindRelCtorsDtors {
let possible_sections = obj
.sections
.iter()
.filter(|&(_, section)| {
.filter(|&(index, section)| {
if section.section_known
|| state.known_sections.contains_key(&index)
|| !matches!(section.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData)
|| section.size < 4
{
@ -283,3 +284,40 @@ impl AnalysisPass for FindRelCtorsDtors {
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 { .. } => {
bail!("Conditional jump table unsupported @ {:#010X}", ins_addr);
BranchTarget::JumpTable { address, size } => {
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 ppc750cl::Opcode;
use tracing::{debug_span, info_span};
use tracing_attributes::instrument;
use crate::{
analysis::{
@ -88,8 +90,8 @@ impl Tracker {
}
}
#[instrument(name = "tracker", skip(self, obj))]
pub fn process(&mut self, obj: &ObjInfo) -> Result<()> {
log::debug!("Processing code sections");
self.process_code(obj)?;
for (section_index, section) in obj
.sections
@ -151,6 +153,7 @@ impl Tracker {
) -> Result<ExecCbResult<()>> {
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 _span = debug_span!("ins", addr = %ins_addr, op = ?ins.op).entered();
match result {
StepResult::Continue => {
@ -310,8 +313,20 @@ impl Tracker {
executor.push(addr, branch.vm, true);
}
}
BranchTarget::JumpTable { .. } => {
bail!("Conditional jump table unsupported @ {:#010X}", ins_addr)
BranchTarget::JumpTable { address, size } => {
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_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,
// but we still want to track them.
@ -461,6 +479,7 @@ impl Tracker {
.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<()> {
fn apply_section_name(section: &mut ObjSection, name: &str) {
let module_id = if let Some((_, b)) = section.name.split_once(':') {

View File

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

View File

@ -1,45 +1,55 @@
use std::{
borrow::Cow,
collections::{btree_map::Entry, hash_map, BTreeMap, HashMap},
fs,
fs::{DirBuilder, File},
io::Write,
mem::take,
path::{Path, PathBuf},
time::Instant,
};
use anyhow::{anyhow, bail, Context, Result};
use argp::FromArgs;
use itertools::Itertools;
use memmap2::Mmap;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use tracing::{debug, info, info_span};
use crate::{
analysis::{
cfa::{AnalyzerState, SectionAddress},
objects::{detect_object_boundaries, detect_strings},
pass::{AnalysisPass, FindRelCtorsDtors, FindSaveRestSleds, FindTRKInterruptVectorTable},
objects::{detect_objects, detect_strings},
pass::{
AnalysisPass, FindRelCtorsDtors, FindRelRodataData, FindSaveRestSleds,
FindTRKInterruptVectorTable,
},
signatures::{apply_signatures, apply_signatures_post},
tracker::Tracker,
},
cmd::shasum::file_sha1,
obj::{
ObjDataKind, ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, SymbolIndex,
best_match_for_reloc, ObjDataKind, ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind,
ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, SymbolIndex,
},
util::{
asm::write_asm,
comment::MWComment,
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,
dol::process_dol,
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},
map::apply_map_file,
rel::process_rel,
rso::{process_rso, DOL_SECTION_ABS, DOL_SECTION_NAMES},
split::{is_linker_generated_object, split_obj, update_splits},
yaz0,
},
};
@ -85,6 +95,9 @@ pub struct SplitArgs {
#[argp(switch)]
/// skip updating splits & symbol files (for build systems)
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)]
@ -120,12 +133,52 @@ pub struct ApplyArgs {
#[inline]
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)]
pub struct ProjectConfig {
pub object: PathBuf,
pub hash: Option<String>,
pub splits: Option<PathBuf>,
pub symbols: Option<PathBuf>,
#[serde(flatten)]
pub base: ModuleConfig,
#[serde(with = "path_slash_serde_option", default)]
pub selfile: Option<PathBuf>,
pub selfile_hash: Option<String>,
/// Version of the MW `.comment` section format.
@ -147,34 +200,63 @@ pub struct ProjectConfig {
/// Adds all objects to FORCEFILES in the linker script.
#[serde(default)]
pub auto_force_files: bool,
/// Specifies the start of the common BSS section.
pub common_start: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ModuleConfig {
#[serde(with = "path_slash_serde")]
pub object: PathBuf,
pub hash: Option<String>,
#[serde(with = "path_slash_serde_option", default)]
pub splits: Option<PathBuf>,
#[serde(with = "path_slash_serde_option", default)]
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)]
pub struct OutputUnit {
#[serde(with = "path_slash_serde")]
pub object: PathBuf,
pub name: String,
pub autogenerated: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct OutputModule {
pub name: String,
pub module_id: u32,
#[serde(with = "path_slash_serde")]
pub ldscript: PathBuf,
pub units: Vec<OutputUnit>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct OutputConfig {
pub ldscript: PathBuf,
pub units: Vec<OutputUnit>,
#[serde(flatten)]
pub base: 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);
// 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
.iter()
.map(|r| (obj.module_id, r))
.chain(
modules
.iter()
.flat_map(|(_, obj)| obj.unresolved_relocations.iter().map(|r| (obj.module_id, r))),
)
.chain(modules.iter().flat_map(|(_, (_, obj))| {
obj.unresolved_relocations.iter().map(|r| (obj.module_id, r))
}))
.filter(|(_, r)| r.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)
.ok_or_else(|| anyhow!("Failed to locate REL section {}", rel_reloc.target_section))?;
let target_symbol = obj
let target_symbols = obj
.symbols
.at_section_address(target_section_index, rel_reloc.addend)
.filter(|(_, s)| s.referenced_by(rel_reloc.kind))
.at_most_one()
.map_err(|e| {
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
)
})?;
.collect_vec();
let target_symbol = best_match_for_reloc(target_symbols, rel_reloc.kind);
if let Some((symbol_index, symbol)) = target_symbol {
// Update symbol
@ -427,11 +495,7 @@ fn update_symbols(obj: &mut ObjInfo, modules: &BTreeMap<u32, ObjInfo>) -> Result
Ok(())
}
fn create_relocations(
obj: &mut ObjInfo,
modules: &BTreeMap<u32, ObjInfo>,
dol_obj: &ObjInfo,
) -> Result<()> {
fn create_relocations(obj: &mut ObjInfo, modules: &ModuleMap<'_>, dol_obj: &ObjInfo) -> Result<()> {
log::debug!("Creating relocations for module {}", obj.module_id);
// Resolve all relocations in this module
@ -450,9 +514,10 @@ fn create_relocations(
} else if rel_reloc.module_id == obj.module_id {
&*obj
} else {
modules
&modules
.get(&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 {
@ -469,21 +534,12 @@ fn create_relocations(
)?
};
let Some((symbol_index, symbol)) = target_obj
let target_symbols = target_obj
.symbols
.at_section_address(target_section_index, rel_reloc.addend)
.filter(|(_, s)| s.referenced_by(rel_reloc.kind))
.at_most_one()
.map_err(|e| {
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)
})?
.collect_vec();
let Some((symbol_index, symbol)) = best_match_for_reloc(target_symbols, rel_reloc.kind)
else {
bail!(
"Couldn't find module {} symbol in section {} at {:#010X}",
@ -516,7 +572,7 @@ fn create_relocations(
fn resolve_external_relocations(
obj: &mut ObjInfo,
modules: &BTreeMap<u32, ObjInfo>,
modules: &ModuleMap<'_>,
dol_obj: Option<&ObjInfo>,
) -> Result<()> {
log::debug!("Resolving relocations for module {}", obj.module_id);
@ -540,9 +596,12 @@ fn resolve_external_relocations(
} else if module_id == 0 {
dol_obj.unwrap()
} else {
modules.get(&module_id).ok_or_else(|| {
anyhow!("Failed to locate module {}", reloc.module.unwrap())
})?
&modules
.get(&module_id)
.ok_or_else(|| {
anyhow!("Failed to locate module {}", reloc.module.unwrap())
})?
.1
};
let target_symbol = &target_obj.symbols[reloc.target_symbol];
@ -573,69 +632,49 @@ fn resolve_external_relocations(
Ok(())
}
fn split(args: SplitArgs) -> Result<()> {
log::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)?;
fn decompress_if_needed(map: &Mmap) -> Result<Cow<[u8]>> {
Ok(if map.len() > 4 && map[0..4] == *b"Yaz0" {
Cow::Owned(yaz0::decompress_file(&mut map_reader(map))?)
} else {
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)?;
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.object)?;
dep.push(config.object.clone());
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 {
obj.mw_comment = Some(MWComment::new(comment_version)?);
}
let mut modules = BTreeMap::<u32, ObjInfo>::new();
let mut module_ids = Vec::with_capacity(config.modules.len());
if !config.modules.is_empty() {
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(map_path) = &config.base.map {
apply_map_file(map_path, &mut obj)?;
dep.push(map_path.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());
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.symbols {
dep.push(symbols_path.clone());
if let Some(symbols_path) = &config.base.symbols {
apply_symbols_file(symbols_path, &mut obj)?;
dep.push(symbols_path.clone());
}
// TODO move before symbols?
log::info!("Performing signature analysis");
debug!("Performing signature analysis");
apply_signatures(&mut obj)?;
if !config.quick_analysis {
log::info!("Detecting function boundaries");
let mut state = AnalyzerState::default();
debug!("Detecting function boundaries");
state.detect_functions(&obj)?;
log::info!("Discovered {} functions", state.function_slices.len());
FindTRKInterruptVectorTable::execute(&mut state, &obj)?;
FindSaveRestSleds::execute(&mut state, &obj)?;
@ -649,116 +688,59 @@ fn split(args: SplitArgs) -> Result<()> {
verify_hash(selfile, hash)?;
}
apply_selfile(&mut obj, selfile)?;
dep.push(selfile.clone());
}
Ok((obj, dep))
}
if !modules.is_empty() {
log::info!("Analyzing modules");
fn split_write_obj(
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;
for &module_id in &module_ids {
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);
}
}
debug!("Applying relocations");
tracker.apply(obj, false)?;
if config.detect_objects {
log::info!("Detecting object boundaries");
detect_object_boundaries(&mut obj)?;
for module_obj in modules.values_mut() {
detect_object_boundaries(module_obj)?;
}
debug!("Detecting object boundaries");
detect_objects(obj)?;
}
if config.detect_strings {
log::info!("Detecting strings");
detect_strings(&mut obj)?;
for module_obj in modules.values_mut() {
detect_strings(module_obj)?;
}
debug!("Detecting strings");
detect_strings(obj)?;
}
log::info!("Adjusting splits");
update_splits(&mut obj)?;
for module_obj in modules.values_mut() {
update_splits(module_obj)?;
}
debug!("Adjusting splits");
update_splits(obj, if obj.module_id == 0 { config.common_start } else { None })?;
if !args.no_update {
log::info!("Writing configuration");
if let Some(symbols_path) = &config.symbols {
if !no_update {
debug!("Writing configuration");
if let Some(symbols_path) = &module_config.symbols {
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)?;
}
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)?;
// Create out dirs
touch(&args.out_dir)?;
let asm_dir = args.out_dir.join("asm");
let include_dir = args.out_dir.join("include");
let obj_dir = args.out_dir.join("obj");
DirBuilder::new().recursive(true).create(&include_dir)?;
fs::write(include_dir.join("macros.inc"), include_str!("../../assets/macros.inc"))?;
log::info!("Writing object files");
let mut out_config = OutputConfig::default();
debug!("Writing object files");
let obj_dir = out_dir.join("obj");
let mut out_config = OutputModule {
name: module_config.name().to_string(),
module_id: obj.module_id,
ldscript: out_dir.join("ldscript.lcf"),
units: Vec::with_capacity(split_objs.len()),
};
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
let out_obj = write_elf(split_obj)?;
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)
.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
let ldscript_path = args.out_dir.join("ldscript.lcf");
fs::write(&ldscript_path, generate_ldscript(&obj, config.auto_force_files)?)?;
out_config.ldscript = ldscript_path;
fs::write(&out_config.ldscript, generate_ldscript(&obj, config.auto_force_files)?)?;
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) {
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()))?;
w.flush()?;
}
Ok(out_config)
}
// Split and write modules
for (config, &module_id) in config.modules.iter().zip(&module_ids) {
let obj = modules.get_mut(&module_id).unwrap();
fn load_analyze_rel(
config: &ProjectConfig,
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 asm_dir = out_dir.join("asm");
// let obj_dir = out_dir.join("obj");
let mut dep = vec![module_config.object.clone()];
if let Some(map_path) = &module_config.map {
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");
let filename = config.object.file_name().unwrap().to_str().unwrap();
let out_path = asm_dir.join(asm_path_for_unit(filename));
let mut w = buf_writer(&out_path)?;
write_asm(&mut w, obj)
.with_context(|| format!("Failed to write {}", out_path.display()))?;
w.flush()?;
if let Some(symbols_path) = &module_config.symbols {
apply_symbols_file(symbols_path, &mut module_obj)?;
dep.push(symbols_path.clone());
}
debug!("Analyzing module {}", module_obj.module_id);
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
@ -826,6 +998,8 @@ fn split(args: SplitArgs) -> Result<()> {
// validate(&obj, file, &state)?;
// }
let duration = command_start.elapsed();
info!("Total duration: {}.{:03}s", duration.as_secs(), duration.subsec_millis());
Ok(())
}
@ -980,10 +1154,10 @@ fn diff(args: DiffArgs) -> Result<()> {
.with_context(|| format!("Failed to open config file '{}'", args.config.display()))?;
let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?;
log::info!("Loading {}", config.object.display());
let mut obj = process_dol(&config.object)?;
log::info!("Loading {}", config.base.object.display());
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)?;
}
@ -1116,10 +1290,10 @@ fn apply(args: ApplyArgs) -> Result<()> {
.with_context(|| format!("Failed to open config file '{}'", args.config.display()))?;
let config: ProjectConfig = serde_yaml::from_reader(&mut config_file)?;
log::info!("Loading {}", config.object.display());
let mut obj = process_dol(&config.object)?;
log::info!("Loading {}", config.base.object.display());
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)? {
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(())
}

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};
@ -86,13 +86,11 @@ enum SubCommand {
}
fn main() {
let args: TopLevel = argp_version::from_env();
env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or(args.log_level.to_string()),
)
.format(|f, r| writeln!(f, "[{}] {}", r.level(), r.args()))
.init();
let format = tracing_subscriber::fmt::format().with_target(false).without_time();
tracing_subscriber::fmt().event_format(format).init();
// TODO reimplement log level selection
let args: TopLevel = argp_version::from_env();
let mut result = Ok(());
if let Some(dir) = &args.chdir {
result = std::env::set_current_dir(dir).map_err(|e| {

View File

@ -11,11 +11,11 @@ use std::{
use anyhow::{anyhow, bail, ensure, Result};
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 symbols::{
ObjDataKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope,
ObjSymbols, SymbolIndex,
best_match_for_reloc, ObjDataKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
ObjSymbolScope, ObjSymbols, SymbolIndex,
};
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 {
".init" | ".text" | ".dbgtext" | ".vmtext" => ObjSectionKind::Code,
".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_name = HashMap::<String, Vec<SymbolIndex>>::new();
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 section_idx >= symbols_by_section.len() {
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 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}",
existing.name,
existing.size,
@ -277,9 +276,7 @@ impl ObjSymbols {
pub fn add_direct(&mut self, in_symbol: ObjSymbol) -> Result<SymbolIndex> {
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 section_idx >= self.symbols_by_section.len() {
self.symbols_by_section.resize_with(section_idx + 1, BTreeMap::new);
@ -446,7 +443,7 @@ impl ObjSymbols {
// ensure!(self.obj_kind == ObjKind::Executable);
let mut result = None;
for (_addr, symbol_idxs) in self.indexes_for_range(..=target_addr.address).rev() {
let mut symbols = symbol_idxs
let symbols = symbol_idxs
.iter()
.map(|&idx| (idx, &self.symbols[idx]))
.filter(|(_, sym)| {
@ -454,42 +451,8 @@ impl ObjSymbols {
&& sym.referenced_by(reloc_kind)
})
.collect_vec();
let (symbol_idx, symbol) = if symbols.len() == 1 {
symbols.pop().unwrap()
} 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,
}
let Some((symbol_idx, symbol)) = best_match_for_reloc(symbols, reloc_kind) else {
continue;
};
if symbol.address == target_addr.address as u64 {
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::{
obj::{
ObjDataKind, ObjInfo, ObjKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
ObjSymbolKind, ObjUnit,
ObjDataKind, ObjInfo, ObjKind, ObjSectionKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet,
ObjSymbolFlags, ObjSymbolKind, ObjUnit,
},
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<()> {
// if let Some(demangled_name) = &symbol.demangled_name {
// writeln!(w, "// {demangled_name}")?;
// }
write!(w, "{} = ", symbol.name)?;
let section = symbol.section.and_then(|idx| obj.sections.get(idx));
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, " 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 {
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]
pub fn write_splits_file<P: AsRef<Path>>(path: P, obj: &ObjInfo, all: bool) -> Result<()> {
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<()> {
writeln!(w, "Sections:")?;
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) {
write!(w, "\n{}:", unit.name)?;
@ -350,13 +362,21 @@ struct SplitUnit {
comment_version: Option<u8>,
}
struct SectionDef {
name: String,
kind: Option<ObjSectionKind>,
align: Option<u32>,
}
enum SplitLine {
Unit(SplitUnit),
Section(SplitSection),
UnitSection(SplitSection),
SectionsStart,
Section(SectionDef),
None,
}
fn parse_split_line(line: &str) -> Result<SplitLine> {
fn parse_split_line(line: &str, state: &SplitState) -> Result<SplitLine> {
static UNIT_LINE: Lazy<Regex> =
Lazy::new(|| Regex::new("^\\s*(?P<name>[^\\s:]+)\\s*:\\s*(?P<attrs>.*)$").unwrap());
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) {
parse_unit_line(captures).with_context(|| format!("While parsing split line: '{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 {
Err(anyhow!("Failed to parse split line: '{line}'"))
}
}
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()) {
if let Some((attr, value)) = attr.split_once(':') {
@ -391,7 +416,33 @@ fn parse_unit_line(captures: Captures) -> Result<SplitLine> {
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 {
name: captures["name"].to_string(),
start: 0,
@ -423,27 +474,39 @@ fn parse_section_line(captures: Captures) -> Result<SplitLine> {
}
}
if section.start > 0 && section.end > 0 {
Ok(SplitLine::Section(section))
Ok(SplitLine::UnitSection(section))
} else {
Err(anyhow!("Section '{}' missing start or end address", section.name))
}
}
enum SplitState {
None,
Sections(usize),
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<()> {
enum SplitState {
None,
Unit(String),
}
let mut state = SplitState::None;
for result in r.lines() {
let line = match result {
Ok(line) => line,
Err(e) => return Err(e.into()),
};
let split_line = parse_split_line(&line)?;
let split_line = parse_split_line(&line, &state)?;
match (&mut state, split_line) {
(
SplitState::None | SplitState::Unit(_),
SplitState::None | SplitState::Unit(_) | SplitState::Sections(_),
SplitLine::Unit(SplitUnit { name, comment_version }),
) => {
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);
}
(SplitState::None, SplitLine::Section(SplitSection { name, .. })) => {
(SplitState::None, SplitLine::UnitSection(SplitSection { 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),
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)? {
Some(v) => Ok(v),

View File

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

View File

@ -8,6 +8,7 @@ use anyhow::{anyhow, Context, Result};
use byteorder::ReadBytesExt;
use filetime::{set_file_mtime, FileTime};
use memmap2::{Mmap, MmapOptions};
use path_slash::PathBufExt;
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() {
let line = result?;
if !line.is_empty() {
out.push(PathBuf::from(line));
out.push(PathBuf::from_slash(line));
}
}
} else if path_str.contains('*') {

View File

@ -2,13 +2,18 @@ use std::path::PathBuf;
use anyhow::{bail, Result};
use itertools::Itertools;
use path_slash::PathBufExt;
use crate::obj::ObjInfo;
use crate::obj::{ObjInfo, ObjKind};
#[inline]
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> {
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 stack_size = match (obj.stack_address, obj.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)
}
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 {
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 {
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(unused_mut)]
use std::{
collections::{btree_map, BTreeMap, HashMap, HashSet},
collections::{btree_map, BTreeMap, HashMap},
hash::Hash,
io::BufRead,
mem::replace,
@ -15,10 +15,7 @@ use once_cell::sync::Lazy;
use regex::{Captures, Regex};
use crate::{
obj::{
section_kind_for_section, ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
ObjSymbolKind,
},
obj::{ObjInfo, ObjKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind},
util::file::{map_file, map_reader},
};
@ -65,86 +62,6 @@ struct SectionOrder {
#[inline]
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 {
($name:ident, $str:expr) => {
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_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!(
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_SYMBOL_ENTRY, "^\\s*(?P<name>\\S+)\\s+(?P<addr>[0-9A-Fa-f]+|\\.{0,8})\\s*$");
#[derive(Debug)]
pub struct SectionInfo {
name: String,
address: u32,
size: u32,
file_offset: u32,
pub name: String,
pub address: u32,
pub size: u32,
pub file_offset: u32,
}
#[derive(Default)]
@ -200,11 +118,7 @@ pub struct MapInfo {
pub unit_entries: MultiMap<String, SymbolRef>,
pub entry_references: MultiMap<SymbolRef, SymbolRef>,
pub entry_referenced_from: MultiMap<SymbolRef, SymbolRef>,
// pub address_to_symbol: BTreeMap<u32, SymbolRef>,
// 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 sections: Vec<SectionInfo>,
pub link_map_symbols: HashMap<SymbolRef, SymbolEntry>,
pub section_symbols: HashMap<String, BTreeMap<u32, Vec<SymbolEntry>>>,
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<()> {
// Resolve duplicate TUs
let mut existing = HashSet::new();
for idx in 0..state.units.len() {
let (addr, unit) = &state.units[idx];
// FIXME
if
/*state.current_section == ".bss" ||*/
existing.contains(unit) {
if
/*state.current_section == ".bss" ||*/
&state.units[idx - 1].1 != unit {
let new_name = format!("{unit}_{}_{:010X}", state.current_section, addr);
log::info!("Renaming {unit} to {new_name}");
for idx2 in 0..idx {
let (addr, n_unit) = &state.units[idx2];
if unit == n_unit {
let new_name =
format!("{n_unit}_{}_{:010X}", state.current_section, addr);
log::info!("Renaming 2 {n_unit} to {new_name}");
state.units[idx2].1 = new_name;
break;
}
}
state.units[idx].1 = new_name;
}
} else {
existing.insert(unit.clone());
}
}
// let mut existing = HashSet::new();
// for idx in 0..state.units.len() {
// let (addr, unit) = &state.units[idx];
// // FIXME
// if
// /*state.current_section == ".bss" ||*/
// existing.contains(unit) {
// if
// /*state.current_section == ".bss" ||*/
// &state.units[idx - 1].1 != unit {
// let new_name = format!("{unit}_{}_{:010X}", state.current_section, addr);
// log::info!("Renaming {unit} to {new_name}");
// for idx2 in 0..idx {
// let (addr, n_unit) = &state.units[idx2];
// if unit == n_unit {
// let new_name =
// format!("{n_unit}_{}_{:010X}", state.current_section, addr);
// log::info!("Renaming 2 {n_unit} to {new_name}");
// state.units[idx2].1 = new_name;
// break;
// }
// }
// state.units[idx].1 = new_name;
// }
// } else {
// existing.insert(unit.clone());
// }
// }
if !state.symbols.is_empty() {
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 file_offset = u32::from_str_radix(&captures["offset"], 16)?;
// 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(),
address,
size,
@ -640,12 +554,7 @@ pub fn process_map<R: BufRead>(reader: R) -> Result<MapInfo> {
}
let state = replace(&mut sm.state, ProcessMapState::None);
sm.end_state(state)?;
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)
Ok(sm.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<()> {
for (_section_index, section) in obj.sections.iter_mut() {
if let Some(info) = result.sections.get(&(section.address as u32)) {
let kind = section_kind_for_section(&info.name)?;
if section.section_known {
if section.name != info.name {
log::warn!("Section mismatch: was {}, map says {}", section.name, info.name);
}
if section.kind != kind {
log::warn!(
"Section type mismatch: {} was {:?}, map says {:?}",
info.name,
section.kind,
kind
);
}
for (section_index, section) in obj.sections.iter_mut() {
log::info!("Section {}: {} ({:?})", section_index, section.name, result.sections);
let opt = if obj.kind == ObjKind::Executable {
result.sections.iter().find(|s| s.address == section.address as u32)
} 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);
}
// if section.size != info.size as u64 {
// 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;
if section.address != info.address as u64 {
log::warn!(
"Section address mismatch: was {:#010X}, map says {:#010X}",
section.address,
info.address
);
}
if section.size != info.size as u64 {
log::warn!(
"Section size mismatch: was {:#X}, map says {:#X}",
section.size,
info.size
);
}
section.rename(info.name.clone())?;
} else {
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
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
// log::info!("Section order: {:#?}", section_order);
// obj.link_order = resolve_link_order(&section_order)?;
// for symbol_entry in result.link_map_symbols.values().filter(|s| s.unit.is_none()) {
// 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(())
}
@ -760,7 +656,7 @@ fn add_symbol(obj: &mut ObjInfo, symbol_entry: &SymbolEntry, section: Option<usi
SymbolKind::Section => ObjSymbolKind::Section,
SymbolKind::NoType => ObjSymbolKind::Unknown,
},
align: None,
align: symbol_entry.align,
data_kind: Default::default(),
},
true,

View File

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

View File

@ -6,6 +6,7 @@ use std::{
use anyhow::{anyhow, bail, ensure, Context, Result};
use itertools::Itertools;
use petgraph::{graph::NodeIndex, Graph};
use tracing_attributes::instrument;
use crate::{
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.
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 {
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)
else {
}) else {
return Ok(());
};
log::debug!("Found common BSS start at {:#010X}", common_bss_start);
@ -434,7 +435,7 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
split.end
);
ensure!(
split.end > 0 && split.end > addr,
split.end > 0 && split.end >= addr,
"Invalid split end {} {} {:#010X}..{:#010X}",
split.unit,
section.name,
@ -490,7 +491,8 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
/// - Ensuring extab & extabindex entries are split with their associated function
/// - Creating splits for gaps between existing splits
/// - 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
if let Some((section_index, section)) = obj.sections.by_name("extabindex")? {
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)?;
// Update common BSS splits
update_common_splits(obj)?;
update_common_splits(obj, common_start)?;
// Ensure splits don't overlap symbols or each other
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.
/// There can be ambiguities, but any solution that satisfies the link order
/// constraints is considered valid.
#[instrument(level = "debug", skip(obj))]
fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
#[allow(dead_code)]
#[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>> {
ensure!(obj.kind == ObjKind::Executable, "Expected executable object");
let mut objects: Vec<ObjInfo> = vec![];
let mut object_symbols: Vec<Vec<Option<usize>>> = vec![];
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
// and rename it to avoid conflicts
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) {
target_sym.name.clone()
} else {