Semi-working REL analysis & splitting
This commit is contained in:
parent
3f63f1ef47
commit
a2374e4fa0
|
@ -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"
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
SECTIONS
|
||||
{
|
||||
GROUP:
|
||||
{
|
||||
.init :{}
|
||||
.text :{}
|
||||
.ctors :{}
|
||||
.dtors :{}
|
||||
.rodata :{}
|
||||
.data :{ *(.data) *(extabindex) *(extab) }
|
||||
.bss :{}
|
||||
}
|
||||
}
|
||||
|
||||
FORCEACTIVE
|
||||
{
|
||||
_unresolved
|
||||
_prolog
|
||||
_epilog
|
||||
$FORCEACTIVE
|
||||
}
|
||||
|
||||
FORCEFILES
|
||||
{
|
||||
$FORCEFILES
|
||||
}
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(':') {
|
||||
|
|
|
@ -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 => {
|
||||
|
|
618
src/cmd/dol.rs
618
src/cmd/dol.rs
|
@ -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(())
|
||||
}
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -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| {
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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" => {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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('*') {
|
||||
|
|
|
@ -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", §ion_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")
|
||||
}
|
||||
|
|
284
src/util/map.rs
284
src/util/map.rs
|
@ -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(§ion_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,
|
||||
|
|
|
@ -144,7 +144,7 @@ pub fn apply_symbol(
|
|||
align: None,
|
||||
data_kind: Default::default(),
|
||||
},
|
||||
true,
|
||||
false,
|
||||
)?;
|
||||
Ok(target_symbol_idx)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue