diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c8564e9..f6df264 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -71,7 +71,6 @@ jobs: test: name: Test - if: 'false' # No tests yet strategy: matrix: platform: [ ubuntu-latest, windows-latest, macos-latest ] @@ -227,6 +226,25 @@ jobs: ${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe if-no-files-found: error + build-wasm: + name: Build objdiff-wasm + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + targets: wasm32-wasip2 + - name: Cache Rust workspace + uses: Swatinem/rust-cache@v2 + - name: Install dependencies + working-directory: objdiff-wasm + run: npm install + - name: Build + working-directory: objdiff-wasm + run: npm run build + release: name: Release if: startsWith(github.ref, 'refs/tags/') diff --git a/Cargo.lock b/Cargo.lock index 86fb307..7342b47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,6 +159,12 @@ dependencies = [ "thiserror 2.0.11", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -208,12 +214,23 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener", + "event-listener 5.4.0", "event-listener-strategy", "futures-core", "pin-project-lite", ] +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -226,6 +243,19 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "flate2", + "futures-core", + "futures-io", + "memchr", + "pin-project-lite", +] + [[package]] name = "async-executor" version = "1.13.1" @@ -250,6 +280,21 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + [[package]] name = "async-io" version = "2.4.0" @@ -275,7 +320,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -297,14 +342,14 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener", + "event-listener 5.4.0", "futures-lite", "rustix", "tracing", @@ -339,6 +384,46 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "async-std" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-tar" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a42f905d4f623faf634bbd1e001e84e0efc24694afa64be9ad239bf6ca49e1f8" +dependencies = [ + "async-std", + "filetime", + "libc", + "pin-project", + "redox_syscall 0.2.16", +] + [[package]] name = "async-task" version = "4.7.1" @@ -443,6 +528,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block2" version = "0.5.1" @@ -458,7 +552,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-task", "futures-io", "futures-lite", @@ -669,16 +763,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - [[package]] name = "const_format" version = "0.2.34" @@ -770,6 +854,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -829,6 +922,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cursor-icon" version = "1.1.0" @@ -893,6 +996,25 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs" version = "5.0.1" @@ -1138,9 +1260,9 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml", + "toml 0.8.19", "vswhom", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -1158,6 +1280,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "endi" version = "1.1.0" @@ -1285,6 +1416,12 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "event-listener" version = "5.4.0" @@ -1302,7 +1439,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener", + "event-listener 5.4.0", "pin-project-lite", ] @@ -1494,6 +1631,20 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -1558,6 +1709,7 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -1569,6 +1721,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -1632,6 +1794,18 @@ dependencies = [ "serde", ] +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "glow" version = "0.14.2" @@ -1773,6 +1947,25 @@ dependencies = [ "bitflags 2.8.0", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.7" @@ -1784,7 +1977,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.2.0", "indexmap", "slab", "tokio", @@ -1836,6 +2029,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.2.0" @@ -1847,6 +2051,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -1854,7 +2069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.2.0", ] [[package]] @@ -1865,8 +2080,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1876,6 +2091,36 @@ version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.5.2" @@ -1885,9 +2130,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", @@ -1896,6 +2141,20 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.5" @@ -1903,15 +2162,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http", - "hyper", + "http 1.2.0", + "hyper 1.5.2", "hyper-util", - "rustls", + "rustls 0.23.21", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.1", "tower-service", - "webpki-roots", + "webpki-roots 0.26.7", ] [[package]] @@ -1922,7 +2181,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.5.2", "hyper-util", "native-tls", "tokio", @@ -1939,9 +2198,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.2", "pin-project-lite", "socket2", "tokio", @@ -2171,6 +2430,19 @@ dependencies = [ "libc", ] +[[package]] +name = "insta" +version = "1.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86" +dependencies = [ + "console", + "linked-hash-map", + "once_cell", + "pin-project", + "similar 2.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "instability" version = "0.3.7" @@ -2224,6 +2496,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -2308,6 +2589,15 @@ dependencies = [ "libc", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -2318,10 +2608,10 @@ dependencies = [ ] [[package]] -name = "leb128" -version = "0.2.5" +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" @@ -2360,6 +2650,12 @@ dependencies = [ "redox_syscall 0.5.8", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -2393,6 +2689,9 @@ name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +dependencies = [ + "value-bag", +] [[package]] name = "lru" @@ -2942,7 +3241,7 @@ dependencies = [ [[package]] name = "objdiff-cli" -version = "2.7.1" +version = "3.0.0-alpha.1" dependencies = [ "anyhow", "argp", @@ -2965,7 +3264,7 @@ dependencies = [ [[package]] name = "objdiff-core" -version = "2.7.1" +version = "3.0.0-alpha.1" dependencies = [ "anyhow", "arm-attr", @@ -2979,12 +3278,15 @@ dependencies = [ "globset", "heck", "iced-x86", + "insta", + "itertools 0.14.0", "log", "memmap2", "msvc-demangler", "notify", "notify-debouncer-full", "num-traits", + "objdiff-core", "object", "pbjson", "pbjson-build", @@ -2995,35 +3297,32 @@ dependencies = [ "prost-build", "quote", "rabbitizer", - "reqwest", + "regex", + "reqwest 0.12.12", "self_update", "semver", "serde", "serde_json", "shell-escape", - "similar", - "spin", + "similar 2.7.0 (git+https://github.com/encounter/similar.git?branch=no_std)", "strum", "syn", - "talc", "tempfile", "time", "typed-path", "unarm", "winapi", - "wit-bindgen", "yaxpeax-arch", "yaxpeax-arm", ] [[package]] name = "objdiff-gui" -version = "2.7.1" +version = "3.0.0-alpha.1" dependencies = [ "anyhow", "bytes", "cfg-if", - "console_error_panic_hook", "const_format", "cwdemangle", "cwextab", @@ -3052,10 +3351,22 @@ dependencies = [ "tauri-winres", "time", "tracing-subscriber", - "tracing-wasm", "typed-path", "wgpu", "winapi", + "winit", +] + +[[package]] +name = "objdiff-wasm" +version = "3.0.0-alpha.1" +dependencies = [ + "log", + "objdiff-core", + "regex", + "talc", + "wit-bindgen", + "wit-deps", ] [[package]] @@ -3245,7 +3556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eea3058763d6e656105d1403cb04e0a41b7bbac6362d413e7c33be0c32279c9" dependencies = [ "heck", - "itertools", + "itertools 0.13.0", "prost", "prost-types", ] @@ -3398,7 +3709,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit", + "toml_edit 0.22.22", ] [[package]] @@ -3433,7 +3744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" dependencies = [ "heck", - "itertools", + "itertools 0.13.0", "log", "multimap", "once_cell", @@ -3453,7 +3764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ "anyhow", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", "syn", @@ -3509,7 +3820,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.0", - "rustls", + "rustls 0.23.21", "socket2", "thiserror 2.0.11", "tokio", @@ -3527,7 +3838,7 @@ dependencies = [ "rand", "ring", "rustc-hash 2.1.0", - "rustls", + "rustls 0.23.21", "rustls-pki-types", "slab", "thiserror 2.0.11", @@ -3621,7 +3932,7 @@ dependencies = [ "crossterm", "indoc", "instability", - "itertools", + "itertools 0.13.0", "lru", "paste", "strum", @@ -3656,6 +3967,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -3735,6 +4055,49 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-rustls 0.24.1", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots 0.25.4", + "winreg 0.50.0", +] + [[package]] name = "reqwest" version = "0.12.12" @@ -3746,12 +4109,12 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", - "hyper", - "hyper-rustls", + "hyper 1.5.2", + "hyper-rustls 0.27.5", "hyper-tls", "hyper-util", "ipnet", @@ -3764,23 +4127,23 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls", - "rustls-pemfile", + "rustls 0.23.21", + "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.26.1", "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.26.7", "windows-registry", ] @@ -3884,6 +4247,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.23.21" @@ -3893,11 +4268,20 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -3916,6 +4300,16 @@ dependencies = [ "web-time", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.102.8" @@ -3969,6 +4363,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -4009,12 +4426,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d832c086ece0dacc29fb2947bb4219b8f6e12fe9e40b7108f9e57c4224e47b5c" dependencies = [ - "hyper", + "hyper 1.5.2", "indicatif", "log", "quick-xml 0.37.2", "regex", - "reqwest", + "reqwest 0.12.12", "self-replace", "semver", "serde_json", @@ -4092,6 +4509,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -4149,6 +4577,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "similar" version = "2.7.0" @@ -4250,9 +4684,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "spirv" @@ -4275,6 +4706,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + [[package]] name = "strsim" version = "0.11.1" @@ -4329,6 +4766,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -4349,6 +4792,27 @@ dependencies = [ "syn", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "talc" version = "4.4.2" @@ -4371,7 +4835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff389a3b73a46a4537e17dfce42061c4f0c31be9fbb4f87e49150b94e3e26377" dependencies = [ "embed-resource", - "toml", + "toml 0.8.19", ] [[package]] @@ -4480,6 +4944,31 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinystr" version = "0.7.6" @@ -4530,13 +5019,34 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls", + "rustls 0.23.21", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", "tokio", ] @@ -4548,11 +5058,25 @@ checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", ] +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + [[package]] name = "toml" version = "0.8.19" @@ -4562,7 +5086,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.22", ] [[package]] @@ -4574,6 +5098,19 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.22" @@ -4584,7 +5121,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.24", ] [[package]] @@ -4596,7 +5133,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tower-layer", "tower-service", @@ -4675,17 +5212,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "tracing-wasm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" -dependencies = [ - "tracing", - "tracing-subscriber", - "wasm-bindgen", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -4713,6 +5239,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41713888c5ccfd99979fcd1afd47b71652e331b3d4a0e19d30769e80fec76cce" +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "uds_windows" version = "1.1.0" @@ -4754,7 +5286,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools", + "itertools 0.13.0", "unicode-segmentation", "unicode-width 0.1.14", ] @@ -4819,6 +5351,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "value-bag" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" + [[package]] name = "vcpkg" version = "0.2.15" @@ -4949,19 +5487,19 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.224.0" +version = "0.225.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7249cf8cb0c6b9cb42bce90c0a5feb276fbf963fa385ff3d818ab3d90818ed6" +checksum = "6f7eac0445cac73bcf09e6a97f83248d64356dccf9f2b100199769b6b42464e5" dependencies = [ - "leb128", + "leb128fmt", "wasmparser", ] [[package]] name = "wasm-metadata" -version = "0.224.0" +version = "0.225.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d13d93febc749413cb6f327e4fdba8c84e4d03bd69fcc4a220c66f113c8de1" +checksum = "f1d20d0bf2c73c32a5114cf35a5c10ccf9f9aa37a3a2c0114b3e11cbf6faac12" dependencies = [ "anyhow", "indexmap", @@ -4975,10 +5513,23 @@ dependencies = [ ] [[package]] -name = "wasmparser" -version = "0.224.0" +name = "wasm-streams" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65881a664fdd43646b647bb27bf186ab09c05bf56779d40aed4c6dce47d423f5" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.225.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e5456165f81e64cb9908a0fe9b9d852c2c74582aa3fe2be3c2da57f937d3ae" dependencies = [ "bitflags 2.8.0", "hashbrown", @@ -5133,6 +5684,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webpki-roots" version = "0.26.7" @@ -5616,6 +6173,7 @@ dependencies = [ "raw-window-handle", "redox_syscall 0.4.1", "rustix", + "sctk-adwaita", "smithay-client-toolkit", "smol_str", "tracing", @@ -5634,6 +6192,15 @@ dependencies = [ "xkbcommon-dl", ] +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.6.24" @@ -5643,6 +6210,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winreg" version = "0.52.0" @@ -5664,9 +6241,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b550e454e4cce8984398539a94a0226511e1f295b14afdc8f08b4e2e2ff9de3a" +checksum = "e4dd9a372b25d6f35456b0a730d2adabeb0c4878066ba8f8089800349be6ecb5" dependencies = [ "wit-bindgen-rt", "wit-bindgen-rust-macro", @@ -5674,9 +6251,9 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e2f98d49960a416074c5d72889f810ed3032a32ffef5e4760094426fefbfe8" +checksum = "f108fa9b77a346372858b30c11ea903680e7e2b9d820b1a5883e9d530bf51c7e" dependencies = [ "anyhow", "heck", @@ -5685,18 +6262,18 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6f8d372a2d4a1227f2556e051cc24b2a5f15768d53451c84ff91e2527139e3" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.8.0", ] [[package]] name = "wit-bindgen-rust" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc49091f84e4f2ace078bbc86082b57e667b9e789baece4b1184e0963382b6e" +checksum = "e5ba5b852e976d35dbf6cb745746bf1bd4fc26782bab1e0c615fc71a7d8aac05" dependencies = [ "anyhow", "heck", @@ -5710,9 +6287,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3545a699dc9d72298b2064ce71b771fc10fc6b757d29306b1e54a4283a75abba" +checksum = "401529c9af9304a20ed99fa01799e467b7d37727126f0c9a958895471268ad7a" dependencies = [ "anyhow", "prettyplease", @@ -5725,9 +6302,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.224.0" +version = "0.225.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad555ab4f4e676474df746d937823c7279c2d6dd36c3e97a61db893d4ef64ee5" +checksum = "2505c917564c1d74774563bbcd3e4f8c216a6508050862fd5f449ee56e3c5125" dependencies = [ "anyhow", "bitflags 2.8.0", @@ -5743,10 +6320,35 @@ dependencies = [ ] [[package]] -name = "wit-parser" -version = "0.224.0" +name = "wit-deps" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e2925a7365d2c6709ae17bdbb5777ffd8154fd70906b413fc01b75f0dba59e" +checksum = "c8148a419968e66a49be6cf233438b5675f05d43b36aef3f089948bac0c4ca32" +dependencies = [ + "anyhow", + "async-compression", + "async-tar", + "async-trait", + "directories", + "futures", + "hex", + "reqwest 0.11.27", + "serde", + "sha2", + "tokio", + "tokio-stream", + "tokio-util", + "toml 0.7.8", + "tracing", + "url", + "urlencoding", +] + +[[package]] +name = "wit-parser" +version = "0.225.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebefaa234e47224f10ce60480c5bfdece7497d0f3b87a12b41ff39e5c8377a78" dependencies = [ "anyhow", "id-arena", @@ -5925,7 +6527,7 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener", + "event-listener 5.4.0", "futures-core", "futures-util", "hex", @@ -5937,7 +6539,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow", + "winnow 0.6.24", "xdg-home", "zbus_macros", "zbus_names", @@ -5967,7 +6569,7 @@ checksum = "519629a3f80976d89c575895b05677cbc45eaf9f70d62a364d819ba646409cc8" dependencies = [ "serde", "static_assertions", - "winnow", + "winnow 0.6.24", "zvariant", ] @@ -6052,7 +6654,7 @@ dependencies = [ "serde", "static_assertions", "url", - "winnow", + "winnow 0.6.24", "zvariant_derive", "zvariant_utils", ] @@ -6081,5 +6683,5 @@ dependencies = [ "serde", "static_assertions", "syn", - "winnow", + "winnow 0.6.24", ] diff --git a/Cargo.toml b/Cargo.toml index c2f6e65..a5fbf0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "objdiff-cli", "objdiff-core", "objdiff-gui", + "objdiff-wasm", ] resolver = "2" @@ -13,7 +14,7 @@ strip = "debuginfo" codegen-units = 1 [workspace.package] -version = "2.7.1" +version = "3.0.0-alpha.1" authors = ["Luke Street "] edition = "2021" license = "MIT OR Apache-2.0" diff --git a/objdiff-cli/Cargo.toml b/objdiff-cli/Cargo.toml index 268d1e9..6530ea7 100644 --- a/objdiff-cli/Cargo.toml +++ b/objdiff-cli/Cargo.toml @@ -18,7 +18,7 @@ argp = "0.4" crossterm = "0.28" enable-ansi-support = "0.2" memmap2 = "0.9" -objdiff-core = { path = "../objdiff-core", features = ["all"] } +objdiff-core = { path = "../objdiff-core", features = ["ppc", "std", "config", "dwarf", "bindings", "serde"] } prost = "0.13" ratatui = "0.29" rayon = "1.10" diff --git a/objdiff-cli/src/cmd/diff.rs b/objdiff-cli/src/cmd/diff.rs index 592723a..175e616 100644 --- a/objdiff-cli/src/cmd/diff.rs +++ b/objdiff-cli/src/cmd/diff.rs @@ -30,16 +30,15 @@ use objdiff_core::{ path::{check_path_buf, platform_path, platform_path_serde_option}, ProjectConfig, ProjectObject, ProjectObjectMetadata, }, - diff, diff::{ - ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig, ObjDiff, + self, ConfigEnum, ConfigPropertyId, ConfigPropertyKind, DiffObjConfig, MappingConfig, + ObjectDiff, }, jobs::{ objdiff::{start_build, ObjDiffConfig}, Job, JobQueue, JobResult, }, - obj, - obj::ObjInfo, + obj::{self, Object}, }; use ratatui::prelude::*; use typed_path::{Utf8PlatformPath, Utf8PlatformPathBuf}; @@ -239,7 +238,7 @@ fn run_oneshot( }) .transpose()?; let result = - diff::diff_objs(&diff_config, &mapping_config, target.as_ref(), base.as_ref(), None)?; + diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?; let left = target.as_ref().and_then(|o| result.left.as_ref().map(|d| (o, d))); let right = base.as_ref().and_then(|o| result.right.as_ref().map(|d| (o, d))); write_output(&DiffResult::new(left, right), Some(output), output_format)?; @@ -253,9 +252,9 @@ pub struct AppState { pub project_config: Option, pub target_path: Option, pub base_path: Option, - pub left_obj: Option<(ObjInfo, ObjDiff)>, - pub right_obj: Option<(ObjInfo, ObjDiff)>, - pub prev_obj: Option<(ObjInfo, ObjDiff)>, + pub left_obj: Option<(Object, ObjectDiff)>, + pub right_obj: Option<(Object, ObjectDiff)>, + pub prev_obj: Option<(Object, ObjectDiff)>, pub reload_time: Option, pub time_format: Vec>, pub watcher: Option, diff --git a/objdiff-cli/src/cmd/report.rs b/objdiff-cli/src/cmd/report.rs index 17eb51b..b20c033 100644 --- a/objdiff-cli/src/cmd/report.rs +++ b/objdiff-cli/src/cmd/report.rs @@ -10,7 +10,7 @@ use objdiff_core::{ }, config::path::platform_path, diff, obj, - obj::{ObjSectionKind, ObjSymbolFlags}, + obj::{SectionKind, SymbolFlag}, }; use prost::Message; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; @@ -181,7 +181,7 @@ fn report_object( }) .transpose()?; let result = - diff::diff_objs(&diff_config, &mapping_config, target.as_ref(), base.as_ref(), None)?; + diff::diff_objs(target.as_ref(), base.as_ref(), None, &diff_config, &mapping_config)?; let metadata = ReportUnitMetadata { complete: object.metadata.complete, @@ -200,7 +200,9 @@ fn report_object( let obj = target.as_ref().or(base.as_ref()).unwrap(); let obj_diff = result.left.as_ref().or(result.right.as_ref()).unwrap(); - for (section, section_diff) in obj.sections.iter().zip(&obj_diff.sections) { + for ((section_idx, section), section_diff) in + obj.sections.iter().enumerate().zip(&obj_diff.sections) + { let section_match_percent = section_diff.match_percent.unwrap_or_else(|| { // Support cases where we don't have a target object, // assume complete means 100% match @@ -221,23 +223,26 @@ fn report_object( }); match section.kind { - ObjSectionKind::Data | ObjSectionKind::Bss => { + SectionKind::Data | SectionKind::Bss => { measures.total_data += section.size; if section_match_percent == 100.0 { measures.matched_data += section.size; } continue; } - ObjSectionKind::Code => (), + _ => {} } - for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) { - if symbol.size == 0 || symbol.flags.0.contains(ObjSymbolFlags::Hidden) { + for (symbol, symbol_diff) in obj.symbols.iter().zip(&obj_diff.symbols) { + if symbol.section != Some(section_idx) + || symbol.size == 0 + || symbol.flags.contains(SymbolFlag::Hidden) + { continue; } if let Some(existing_functions) = &mut existing_functions { - if (symbol.flags.0.contains(ObjSymbolFlags::Global) - || symbol.flags.0.contains(ObjSymbolFlags::Weak)) + if (symbol.flags.contains(SymbolFlag::Global) + || symbol.flags.contains(SymbolFlag::Weak)) && !existing_functions.insert(symbol.name.clone()) { continue; diff --git a/objdiff-cli/src/main.rs b/objdiff-cli/src/main.rs index e0e35f9..bcd5c24 100644 --- a/objdiff-cli/src/main.rs +++ b/objdiff-cli/src/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::too_many_arguments)] + mod argp_version; mod cmd; mod util; diff --git a/objdiff-cli/src/views/function_diff.rs b/objdiff-cli/src/views/function_diff.rs index a300c08..41963c8 100644 --- a/objdiff-cli/src/views/function_diff.rs +++ b/objdiff-cli/src/views/function_diff.rs @@ -4,10 +4,10 @@ use anyhow::{bail, Result}; use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers, MouseButton, MouseEventKind}; use objdiff_core::{ diff::{ - display::{display_diff, DiffText, HighlightKind}, - FunctionRelocDiffs, ObjDiff, ObjInsDiffKind, ObjSymbolDiff, + display::{display_row, DiffText, HighlightKind}, + DiffObjConfig, FunctionRelocDiffs, InstructionDiffKind, ObjectDiff, SymbolDiff, }, - obj::{ObjInfo, ObjSectionKind, ObjSymbol, SymbolRef}, + obj::Object, }; use ratatui::{ prelude::*, @@ -30,9 +30,9 @@ pub struct FunctionDiffUi { pub scroll_state_y: ScrollbarState, pub per_page: usize, pub num_rows: usize, - pub left_sym: Option, - pub right_sym: Option, - pub prev_sym: Option, + pub left_sym: Option, + pub right_sym: Option, + pub prev_sym: Option, pub open_options: bool, pub three_way: bool, } @@ -82,8 +82,8 @@ impl UiView for FunctionDiffUi { f.render_widget(line_l, header_chunks[0]); let mut line_r = Line::default(); - if let Some(percent) = - get_symbol(state.right_obj.as_ref(), self.right_sym).and_then(|(_, d)| d.match_percent) + if let Some(percent) = get_symbol(state.right_obj.as_ref(), self.right_sym) + .and_then(|(_, _, d)| d.match_percent) { line_r.spans.push(Span::styled( format!("{:.2}% ", percent), @@ -108,13 +108,17 @@ impl UiView for FunctionDiffUi { let mut left_text = None; let mut left_highlight = None; let mut max_width = 0; - if let Some((symbol, symbol_diff)) = get_symbol(state.left_obj.as_ref(), self.left_sym) { + if let Some((obj, symbol_idx, symbol_diff)) = + get_symbol(state.left_obj.as_ref(), self.left_sym) + { let mut text = Text::default(); let rect = content_chunks[0].inner(Margin::new(0, 1)); left_highlight = self.print_sym( &mut text, - symbol, + obj, + symbol_idx, symbol_diff, + &state.diff_obj_config, rect, &self.left_highlight, result, @@ -127,13 +131,17 @@ impl UiView for FunctionDiffUi { let mut right_text = None; let mut right_highlight = None; let mut margin_text = None; - if let Some((symbol, symbol_diff)) = get_symbol(state.right_obj.as_ref(), self.right_sym) { + if let Some((obj, symbol_idx, symbol_diff)) = + get_symbol(state.right_obj.as_ref(), self.right_sym) + { let mut text = Text::default(); let rect = content_chunks[2].inner(Margin::new(0, 1)); right_highlight = self.print_sym( &mut text, - symbol, + obj, + symbol_idx, symbol_diff, + &state.diff_obj_config, rect, &self.right_highlight, result, @@ -152,14 +160,17 @@ impl UiView for FunctionDiffUi { let mut prev_text = None; let mut prev_margin_text = None; if self.three_way { - if let Some((symbol, symbol_diff)) = get_symbol(state.prev_obj.as_ref(), self.prev_sym) + if let Some((obj, symbol_idx, symbol_diff)) = + get_symbol(state.prev_obj.as_ref(), self.prev_sym) { let mut text = Text::default(); let rect = content_chunks[4].inner(Margin::new(0, 1)); self.print_sym( &mut text, - symbol, + obj, + symbol_idx, symbol_diff, + &state.diff_obj_config, rect, &self.right_highlight, result, @@ -437,9 +448,11 @@ impl UiView for FunctionDiffUi { get_symbol(state.left_obj.as_ref(), left_sym), get_symbol(state.right_obj.as_ref(), right_sym), ) { - (Some((_l, ld)), Some((_r, rd))) => ld.instructions.len().max(rd.instructions.len()), - (Some((_l, ld)), None) => ld.instructions.len(), - (None, Some((_r, rd))) => rd.instructions.len(), + (Some((_l, _ls, ld)), Some((_r, _rs, rd))) => { + ld.instruction_rows.len().max(rd.instruction_rows.len()) + } + (Some((_l, _ls, ld)), None) => ld.instruction_rows.len(), + (None, Some((_r, _rs, rd))) => rd.instruction_rows.len(), (None, None) => bail!("Symbol not found: {}", self.symbol_name), }; self.left_sym = left_sym; @@ -482,52 +495,51 @@ impl FunctionDiffUi { self.scroll_y += self.per_page / if half { 2 } else { 1 }; } - #[allow(clippy::too_many_arguments)] fn print_sym( &self, out: &mut Text<'static>, - symbol: &ObjSymbol, - symbol_diff: &ObjSymbolDiff, + obj: &Object, + symbol_index: usize, + symbol_diff: &SymbolDiff, + diff_config: &DiffObjConfig, rect: Rect, highlight: &HighlightKind, result: &EventResult, only_changed: bool, ) -> Option { - let base_addr = symbol.address; let mut new_highlight = None; - for (y, ins_diff) in symbol_diff - .instructions + for (y, ins_row) in symbol_diff + .instruction_rows .iter() .skip(self.scroll_y) .take(rect.height as usize) .enumerate() { - if only_changed && ins_diff.kind == ObjInsDiffKind::None { + if only_changed && ins_row.kind == InstructionDiffKind::None { out.lines.push(Line::default()); continue; } let mut sx = rect.x; let sy = rect.y + y as u16; let mut line = Line::default(); - display_diff(ins_diff, base_addr, |text| -> Result<()> { + display_row(obj, symbol_index, ins_row, diff_config, |text, diff_idx| { let label_text; - let mut base_color = match ins_diff.kind { - ObjInsDiffKind::None - | ObjInsDiffKind::OpMismatch - | ObjInsDiffKind::ArgMismatch => Color::Gray, - ObjInsDiffKind::Replace => Color::Cyan, - ObjInsDiffKind::Delete => Color::Red, - ObjInsDiffKind::Insert => Color::Green, + let mut base_color = match ins_row.kind { + InstructionDiffKind::None + | InstructionDiffKind::OpMismatch + | InstructionDiffKind::ArgMismatch => Color::Gray, + InstructionDiffKind::Replace => Color::Cyan, + InstructionDiffKind::Delete => Color::Red, + InstructionDiffKind::Insert => Color::Green, }; + if let Some(idx) = diff_idx.get() { + base_color = COLOR_ROTATION[idx as usize % COLOR_ROTATION.len()]; + } let mut pad_to = 0; match text { DiffText::Basic(text) => { label_text = text.to_string(); } - DiffText::BasicColor(s, idx) => { - label_text = s.to_string(); - base_color = COLOR_ROTATION[idx % COLOR_ROTATION.len()]; - } DiffText::Line(num) => { label_text = format!("{num} "); base_color = Color::DarkGray; @@ -539,41 +551,31 @@ impl FunctionDiffUi { } DiffText::Opcode(mnemonic, _op) => { label_text = mnemonic.to_string(); - if ins_diff.kind == ObjInsDiffKind::OpMismatch { + if ins_row.kind == InstructionDiffKind::OpMismatch { base_color = Color::Blue; } pad_to = 8; } - DiffText::Argument(arg, diff) => { + DiffText::Argument(arg) => { label_text = arg.to_string(); - if let Some(diff) = diff { - base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()] - } } - DiffText::BranchDest(addr, diff) => { + DiffText::BranchDest(addr) => { label_text = format!("{addr:x}"); - if let Some(diff) = diff { - base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()] - } } - DiffText::Symbol(sym, diff) => { + DiffText::Symbol(sym) => { let name = sym.demangled_name.as_ref().unwrap_or(&sym.name); label_text = name.clone(); - if let Some(diff) = diff { - base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()] - } else { + if diff_idx.is_none() { base_color = Color::White; } } - DiffText::Addend(addend, diff) => { + DiffText::Addend(addend) => { label_text = match addend.cmp(&0i64) { Ordering::Greater => format!("+{:#x}", addend), Ordering::Less => format!("-{:#x}", -addend), _ => "".to_string(), }; - if let Some(diff) = diff { - base_color = COLOR_ROTATION[diff.idx % COLOR_ROTATION.len()] - } else { + if diff_idx.is_none() { base_color = Color::White; } } @@ -612,12 +614,13 @@ impl FunctionDiffUi { new_highlight } - fn print_margin(&self, out: &mut Text, symbol: &ObjSymbolDiff, rect: Rect) { - for ins_diff in symbol.instructions.iter().skip(self.scroll_y).take(rect.height as usize) { - if ins_diff.kind != ObjInsDiffKind::None { - out.lines.push(Line::raw(match ins_diff.kind { - ObjInsDiffKind::Delete => "<", - ObjInsDiffKind::Insert => ">", + fn print_margin(&self, out: &mut Text, symbol: &SymbolDiff, rect: Rect) { + for ins_row in symbol.instruction_rows.iter().skip(self.scroll_y).take(rect.height as usize) + { + if ins_row.kind != InstructionDiffKind::None { + out.lines.push(Line::raw(match ins_row.kind { + InstructionDiffKind::Delete => "<", + InstructionDiffKind::Insert => ">", _ => "|", })); } else { @@ -649,23 +652,18 @@ pub fn match_percent_color(match_percent: f32) -> Color { #[inline] fn get_symbol( - obj: Option<&(ObjInfo, ObjDiff)>, - sym: Option, -) -> Option<(&ObjSymbol, &ObjSymbolDiff)> { + obj: Option<&(Object, ObjectDiff)>, + sym: Option, +) -> Option<(&Object, usize, &SymbolDiff)> { let (obj, diff) = obj?; let sym = sym?; - Some((obj.section_symbol(sym).1, diff.symbol_diff(sym))) + Some((obj, sym, &diff.symbols[sym])) } -fn find_function(obj: &ObjInfo, name: &str) -> Option { - for (section_idx, section) in obj.sections.iter().enumerate() { - if section.kind != ObjSectionKind::Code { - continue; - } - for (symbol_idx, symbol) in section.symbols.iter().enumerate() { - if symbol.name == name { - return Some(SymbolRef { section_idx, symbol_idx }); - } +fn find_function(obj: &Object, name: &str) -> Option { + for (symbol_idx, symbol) in obj.symbols.iter().enumerate() { + if symbol.name == name { + return Some(symbol_idx); } } None diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index 3614c78..20f52dd 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -12,9 +12,6 @@ A local diffing tool for decompilation projects. """ documentation = "https://docs.rs/objdiff-core" -[lib] -crate-type = ["cdylib", "rlib"] - [features] default = ["std"] all = [ @@ -25,11 +22,11 @@ all = [ "dwarf", "serde", # Architectures + "arm", + "arm64", "mips", "ppc", "x86", - "arm", - "arm64", ] # Implicit, used to check if any arch is enabled any-arch = [ @@ -41,6 +38,7 @@ any-arch = [ "dep:prettyplease", "dep:proc-macro2", "dep:quote", + "dep:regex", "dep:similar", "dep:strum", "dep:syn", @@ -113,14 +111,6 @@ arm64 = [ "dep:yaxpeax-arch", "dep:yaxpeax-arm", ] -wasm = [ - "any-arch", - "bindings", - "dep:log", - "dep:talc", - "dep:spin", - "dep:wit-bindgen", -] [package.metadata.docs.rs] features = ["all"] @@ -140,6 +130,8 @@ serde = { version = "1.0", default-features = false, features = ["derive"], opti similar = { version = "2.7", default-features = false, optional = true, git = "https://github.com/encounter/similar.git", branch = "no_std" } strum = { version = "0.26", default-features = false, features = ["derive"], optional = true } typed-path = { version = "0.10", default-features = false, optional = true } +regex = { version = "1.11", default-features = false, features = [], optional = true } +itertools = { version = "0.14", default-features = false, features = ["use_alloc"] } # config globset = { version = "0.4", default-features = false, optional = true } @@ -170,13 +162,6 @@ arm-attr = { version = "0.2", optional = true } yaxpeax-arch = { version = "0.3", default-features = false, optional = true } yaxpeax-arm = { version = "0.3", default-features = false, optional = true } -# wasm -#console_error_panic_hook = { version = "0.1", optional = true } -#console_log = { version = "1.0", optional = true } -talc = { version = "4.4", optional = true } -spin = { version = "0.9", optional = true } -wit-bindgen = { version = "0.38", default-features = false, features = ["macros"], optional = true } - # build notify = { version = "8.0.0", optional = true } notify-debouncer-full = { version = "0.5.0", optional = true } @@ -207,3 +192,21 @@ quote = { version = "1.0", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0" } syn = { version = "2.0", optional = true } + +# Enable all features for tests +[dev-dependencies] +objdiff-core = { path = ".", features = [ + # Features + "bindings", + "build", + "config", + "dwarf", + "serde", + # Architectures + "mips", + "ppc", +# "x86", + "arm", + "arm64", +] } +insta = "1.42.1" diff --git a/objdiff-core/build.rs b/objdiff-core/build.rs index 0b307b4..fdd4e63 100644 --- a/objdiff-core/build.rs +++ b/objdiff-core/build.rs @@ -1,11 +1,12 @@ #[cfg(feature = "any-arch")] mod config_gen; -fn main() { +fn main() -> Result<(), Box> { #[cfg(feature = "bindings")] compile_protos(); #[cfg(feature = "any-arch")] config_gen::generate_diff_config(); + Ok(()) } #[cfg(feature = "bindings")] @@ -18,7 +19,7 @@ fn compile_protos() { .map(|m| m.modified().unwrap()) .unwrap_or(std::time::SystemTime::UNIX_EPOCH); let mut run_protoc = false; - let proto_files = vec![root.join("diff.proto"), root.join("report.proto")]; + let proto_files = vec![root.join("report.proto")]; for proto_file in &proto_files { println!("cargo:rerun-if-changed={}", proto_file.display()); let mtime = match std::fs::metadata(proto_file) { diff --git a/objdiff-core/config-schema.json b/objdiff-core/config-schema.json index 1cc24e9..1ac8be5 100644 --- a/objdiff-core/config-schema.json +++ b/objdiff-core/config-schema.json @@ -39,6 +39,13 @@ "name": "Combine data sections", "description": "Combines data sections with equal names." }, + { + "id": "combineTextSections", + "type": "boolean", + "default": false, + "name": "Combine text sections", + "description": "Combines all text sections into one." + }, { "id": "arm.archVersion", "type": "choice", @@ -213,7 +220,8 @@ "properties": [ "functionRelocDiffs", "spaceBetweenArgs", - "combineDataSections" + "combineDataSections", + "combineTextSections" ] }, { diff --git a/objdiff-core/protos/diff.proto b/objdiff-core/protos/diff.proto index 1a0ba97..c1ad7df 100644 --- a/objdiff-core/protos/diff.proto +++ b/objdiff-core/protos/diff.proto @@ -87,11 +87,11 @@ message Relocation { } message RelocationTarget { - Symbol symbol = 1; + uint32 symbol_index = 1; int64 addend = 2; } -message InstructionDiff { +message InstructionDiffRow { DiffKind diff_kind = 1; optional Instruction instruction = 2; optional InstructionBranchFrom branch_from = 3; @@ -122,17 +122,12 @@ message InstructionBranchTo { uint32 branch_index = 2; } -message SymbolRef { - optional uint32 section_index = 1; - uint32 symbol_index = 2; -} - message SymbolDiff { Symbol symbol = 1; - repeated InstructionDiff instructions = 2; + repeated InstructionDiffRow instruction_rows = 2; optional float match_percent = 3; - // The symbol ref in the _other_ object that this symbol was diffed against - optional SymbolRef target = 5; + // The symbol index in the _other_ object that this symbol was diffed against + optional uint32 target_symbol = 5; } message DataDiff { @@ -147,7 +142,7 @@ message SectionDiff { SectionKind kind = 2; uint64 size = 3; uint64 address = 4; - repeated SymbolDiff symbols = 5; + reserved 5; repeated DataDiff data = 6; optional float match_percent = 7; } @@ -157,11 +152,11 @@ enum SectionKind { SECTION_TEXT = 1; SECTION_DATA = 2; SECTION_BSS = 3; - SECTION_COMMON = 4; } message ObjectDiff { repeated SectionDiff sections = 1; + repeated SymbolDiff symbols = 2; } message DiffResult { diff --git a/objdiff-core/protos/proto_descriptor.bin b/objdiff-core/protos/proto_descriptor.bin index b8cbfb6..9312c29 100644 Binary files a/objdiff-core/protos/proto_descriptor.bin and b/objdiff-core/protos/proto_descriptor.bin differ diff --git a/objdiff-core/src/arch/arm.rs b/objdiff-core/src/arch/arm.rs index c7f5698..c4aa54d 100644 --- a/objdiff-core/src/arch/arm.rs +++ b/objdiff-core/src/arch/arm.rs @@ -3,41 +3,37 @@ use alloc::{ collections::BTreeMap, format, string::{String, ToString}, - vec, vec::Vec, }; +use core::ops::Range; use anyhow::{bail, Result}; use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs}; -use object::{ - elf::{self, SHT_ARM_ATTRIBUTES}, - Endian, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionKind, - Symbol, SymbolKind, -}; -use unarm::{ - args::{Argument, OffsetImm, OffsetReg, Register}, - parse::{ArmVersion, ParseMode, Parser}, - DisplayOptions, ParseFlags, ParsedIns, RegNames, -}; +use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _}; +use unarm::{args, arm, thumb}; use crate::{ - arch::{ObjArch, ProcessCodeResult}, - diff::{ArmArchVersion, ArmR9Usage, DiffObjConfig}, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, + arch::Arch, + diff::{display::InstructionPart, ArmArchVersion, ArmR9Usage, DiffObjConfig}, + obj::{ + InstructionArg, InstructionArgValue, InstructionRef, RelocationFlags, ResolvedRelocation, + ScannedInstruction, SymbolFlag, SymbolFlagSet, SymbolKind, + }, }; -pub struct ObjArchArm { +#[derive(Debug)] +pub struct ArchArm { /// Maps section index, to list of disasm modes (arm, thumb or data) sorted by address disasm_modes: BTreeMap>, - detected_version: Option, + detected_version: Option, endianness: object::Endianness, } -impl ObjArchArm { - pub fn new(file: &File) -> Result { +impl ArchArm { + pub fn new(file: &object::File) -> Result { let endianness = file.endianness(); match file { - File::Elf32(_) => { + object::File::Elf32(_) => { let disasm_modes = Self::elf_get_mapping_symbols(file); let detected_version = Self::elf_detect_arm_version(file)?; Ok(Self { disasm_modes, detected_version, endianness }) @@ -46,10 +42,11 @@ impl ObjArchArm { } } - fn elf_detect_arm_version(file: &File) -> Result> { + fn elf_detect_arm_version(file: &object::File) -> Result> { // Check ARM attributes if let Some(arm_attrs) = file.sections().find(|s| { - s.kind() == SectionKind::Elf(SHT_ARM_ATTRIBUTES) && s.name() == Ok(".ARM.attributes") + s.kind() == object::SectionKind::Elf(elf::SHT_ARM_ATTRIBUTES) + && s.name() == Ok(".ARM.attributes") }) { let attr_data = arm_attrs.uncompressed_data()?; let build_attrs = BuildAttrs::new(&attr_data, match file.endianness() { @@ -70,9 +67,9 @@ impl ObjArchArm { } }); match cpu_arch { - Some(CpuArch::V4T) => return Ok(Some(ArmVersion::V4T)), - Some(CpuArch::V5TE) => return Ok(Some(ArmVersion::V5Te)), - Some(CpuArch::V6K) => return Ok(Some(ArmVersion::V6K)), + Some(CpuArch::V4T) => return Ok(Some(unarm::ArmVersion::V4T)), + Some(CpuArch::V5TE) => return Ok(Some(unarm::ArmVersion::V5Te)), + Some(CpuArch::V6K) => return Ok(Some(unarm::ArmVersion::V6K)), Some(arch) => bail!("ARM arch {} not supported", arch), None => {} }; @@ -82,9 +79,9 @@ impl ObjArchArm { Ok(None) } - fn elf_get_mapping_symbols(file: &File) -> BTreeMap> { + fn elf_get_mapping_symbols(file: &object::File) -> BTreeMap> { file.sections() - .filter(|s| s.kind() == SectionKind::Text) + .filter(|s| s.kind() == object::SectionKind::Text) .map(|s| { let index = s.index(); let mut mapping_symbols: Vec<_> = file @@ -97,32 +94,95 @@ impl ObjArchArm { }) .collect() } -} -impl ObjArch for ObjArchArm { - fn symbol_address(&self, symbol: &Symbol) -> u64 { - let address = symbol.address(); - if symbol.kind() == SymbolKind::Text { - address & !1 - } else { - address + fn endian(&self) -> unarm::Endian { + match self.endianness { + object::Endianness::Little => unarm::Endian::Little, + object::Endianness::Big => unarm::Endian::Big, } } - fn process_code( + fn parse_flags(&self, diff_config: &DiffObjConfig) -> unarm::ParseFlags { + unarm::ParseFlags { + ual: diff_config.arm_unified_syntax, + version: match diff_config.arm_arch_version { + ArmArchVersion::Auto => self.detected_version.unwrap_or(unarm::ArmVersion::V5Te), + ArmArchVersion::V4t => unarm::ArmVersion::V4T, + ArmArchVersion::V5te => unarm::ArmVersion::V5Te, + ArmArchVersion::V6k => unarm::ArmVersion::V6K, + }, + } + } + + fn display_options(&self, diff_config: &DiffObjConfig) -> unarm::DisplayOptions { + unarm::DisplayOptions { + reg_names: unarm::RegNames { + av_registers: diff_config.arm_av_registers, + r9_use: match diff_config.arm_r9_usage { + ArmR9Usage::GeneralPurpose => unarm::R9Use::GeneralPurpose, + ArmR9Usage::Sb => unarm::R9Use::Pid, + ArmR9Usage::Tr => unarm::R9Use::Tls, + }, + explicit_stack_limit: diff_config.arm_sl_usage, + frame_pointer: diff_config.arm_fp_usage, + ip: diff_config.arm_ip_usage, + }, + } + } + + fn parse_ins_ref( + &self, + ins_ref: InstructionRef, + code: &[u8], + diff_config: &DiffObjConfig, + ) -> Result<(unarm::Ins, unarm::ParsedIns)> { + let code = match (self.endianness, ins_ref.size) { + (object::Endianness::Little, 2) => u16::from_le_bytes([code[0], code[1]]) as u32, + (object::Endianness::Little, 4) => { + u32::from_le_bytes([code[0], code[1], code[2], code[3]]) + } + (object::Endianness::Big, 2) => u16::from_be_bytes([code[0], code[1]]) as u32, + (object::Endianness::Big, 4) => { + u32::from_be_bytes([code[0], code[1], code[2], code[3]]) + } + _ => bail!("Invalid instruction size {}", ins_ref.size), + }; + let (ins, parsed_ins) = if ins_ref.opcode == u16::MAX { + let mut args = args::Arguments::default(); + args[0] = args::Argument::UImm(code); + let mnemonic = if ins_ref.size == 4 { ".word" } else { ".hword" }; + (unarm::Ins::Data, unarm::ParsedIns { mnemonic, args }) + } else if ins_ref.opcode & (1 << 15) != 0 { + let ins = arm::Ins { code, op: arm::Opcode::from(ins_ref.opcode as u8) }; + let parsed = ins.parse(&self.parse_flags(diff_config)); + (unarm::Ins::Arm(ins), parsed) + } else { + let ins = thumb::Ins { code, op: thumb::Opcode::from(ins_ref.opcode as u8) }; + let parsed = ins.parse(&self.parse_flags(diff_config)); + if ins.is_half_bl() { + todo!("Combine thumb BL instructions"); + } else { + (unarm::Ins::Thumb(ins), parsed) + } + }; + Ok((ins, parsed_ins)) + } +} + +impl Arch for ArchArm { + fn scan_instructions( &self, address: u64, code: &[u8], section_index: usize, - relocations: &[ObjReloc], - line_info: &BTreeMap, - config: &DiffObjConfig, - ) -> Result { + diff_config: &DiffObjConfig, + ) -> Result> { let start_addr = address as u32; let end_addr = start_addr + code.len() as u32; // Mapping symbols decide what kind of data comes after it. $a for ARM code, $t for Thumb code and $d for data. - let fallback_mappings = [DisasmMode { address: start_addr, mapping: ParseMode::Arm }]; + let fallback_mappings = + [DisasmMode { address: start_addr, mapping: unarm::ParseMode::Arm }]; let mapping_symbols = self .disasm_modes .get(§ion_index) @@ -138,39 +198,14 @@ impl ObjArch for ObjArchArm { let mut next_mapping = mappings_iter.next(); let ins_count = code.len() / first_mapping.instruction_size(start_addr); - let mut ops = Vec::::with_capacity(ins_count); - let mut insts = Vec::::with_capacity(ins_count); + let mut ops = Vec::::with_capacity(ins_count); - let version = match config.arm_arch_version { - ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te), - ArmArchVersion::V4t => ArmVersion::V4T, - ArmArchVersion::V5te => ArmVersion::V5Te, - ArmArchVersion::V6k => ArmVersion::V6K, - }; - let endian = match self.endianness { - object::Endianness::Little => unarm::Endian::Little, - object::Endianness::Big => unarm::Endian::Big, - }; + let endian = self.endian(); + let parse_flags = self.parse_flags(diff_config); + let mut parser = unarm::Parser::new(first_mapping, start_addr, endian, parse_flags, code); - let parse_flags = ParseFlags { ual: config.arm_unified_syntax, version }; - - let mut parser = Parser::new(first_mapping, start_addr, endian, parse_flags, code); - - let display_options = DisplayOptions { - reg_names: RegNames { - av_registers: config.arm_av_registers, - r9_use: match config.arm_r9_usage { - ArmR9Usage::GeneralPurpose => unarm::R9Use::GeneralPurpose, - ArmR9Usage::Sb => unarm::R9Use::Pid, - ArmR9Usage::Tr => unarm::R9Use::Tls, - }, - explicit_stack_limit: config.arm_sl_usage, - frame_pointer: config.arm_fp_usage, - ip: config.arm_ip_usage, - }, - }; - - while let Some((address, ins, parsed_ins)) = parser.next() { + while let Some((address, ins, _parsed_ins)) = parser.next() { + let size = parser.mode.instruction_size(address); if let Some(next) = next_mapping { let next_address = parser.address; if next_address >= next.address { @@ -179,82 +214,95 @@ impl ObjArch for ObjArchArm { next_mapping = mappings_iter.next(); } } - let line = line_info.range(..=address as u64).last().map(|(_, &b)| b); - - let reloc = relocations.iter().find(|r| (r.address as u32 & !1) == address).cloned(); - - let mut reloc_arg = None; - if let Some(reloc) = &reloc { - match reloc.flags { - // Calls - RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 } - | RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 } - | RelocationFlags::Elf { r_type: elf::R_ARM_PC24 } - | RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 } - | RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => { - reloc_arg = parsed_ins - .args - .iter() - .rposition(|a| matches!(a, Argument::BranchDest(_))); - } - // Data - RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => { - reloc_arg = - parsed_ins.args.iter().rposition(|a| matches!(a, Argument::UImm(_))); - } - _ => (), + let (opcode, branch_dest) = match ins { + unarm::Ins::Arm(x) => { + let opcode = x.op as u16 | (1 << 15); + let branch_dest = match x.op { + arm::Opcode::B | arm::Opcode::Bl => { + address.checked_add_signed(x.field_branch_offset()) + } + arm::Opcode::BlxI => address.checked_add_signed(x.field_blx_offset()), + _ => None, + }; + (opcode, branch_dest) } + unarm::Ins::Thumb(x) => { + let opcode = x.op as u16; + let branch_dest = match x.op { + thumb::Opcode::B | thumb::Opcode::Bl => { + address.checked_add_signed(x.field_branch_offset_8()) + } + thumb::Opcode::BLong => { + address.checked_add_signed(x.field_branch_offset_11()) + } + _ => None, + }; + (opcode, branch_dest) + } + unarm::Ins::Data => (u16::MAX, None), }; - - let (args, branch_dest) = if reloc.is_some() && parser.mode == ParseMode::Data { - (vec![ObjInsArg::Reloc], None) - } else { - push_args(&parsed_ins, config, reloc_arg, address, display_options)? - }; - - ops.push(ins.opcode_id()); - insts.push(ObjIns { - address: address as u64, - size: (parser.address - address) as u8, - op: ins.opcode_id(), - mnemonic: Cow::Borrowed(parsed_ins.mnemonic), - args, - reloc, - branch_dest, - line, - formatted: parsed_ins.display(display_options).to_string(), - orig: None, + ops.push(ScannedInstruction { + ins_ref: InstructionRef { address: address as u64, size: size as u8, opcode }, + branch_dest: branch_dest.map(|x| x as u64), }); } - Ok(ProcessCodeResult { ops, insts }) + Ok(ops) + } + + fn display_instruction( + &self, + ins_ref: InstructionRef, + code: &[u8], + relocation: Option, + _function_range: Range, + _section_index: usize, + diff_config: &DiffObjConfig, + cb: &mut dyn FnMut(InstructionPart) -> Result<()>, + ) -> Result<()> { + let (ins, parsed_ins) = self.parse_ins_ref(ins_ref, code, diff_config)?; + cb(InstructionPart::Opcode(Cow::Borrowed(parsed_ins.mnemonic), ins_ref.opcode))?; + if ins == unarm::Ins::Data && relocation.is_some() { + cb(InstructionPart::Arg(InstructionArg::Reloc))?; + } else { + push_args( + &parsed_ins, + relocation, + ins_ref.address as u32, + self.display_options(diff_config), + cb, + )?; + } + Ok(()) } fn implcit_addend( &self, - _file: &File<'_>, - section: &ObjSection, + _file: &object::File<'_>, + section: &object::Section, address: u64, - reloc: &Relocation, + _relocation: &object::Relocation, + flags: RelocationFlags, ) -> Result { + let section_data = section.data()?; let address = address as usize; - Ok(match reloc.flags() { + Ok(match flags { // ARM calls - RelocationFlags::Elf { r_type: elf::R_ARM_PC24 } - | RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 } - | RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => { - let data = section.data[address..address + 4].try_into()?; + RelocationFlags::Elf(elf::R_ARM_PC24) + | RelocationFlags::Elf(elf::R_ARM_XPC25) + | RelocationFlags::Elf(elf::R_ARM_CALL) => { + let data = section_data[address..address + 4].try_into()?; let addend = self.endianness.read_i32_bytes(data); let imm24 = addend & 0xffffff; (imm24 << 2) << 8 >> 8 } // Thumb calls - RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 } - | RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 } => { - let data = section.data[address..address + 2].try_into()?; + RelocationFlags::Elf(elf::R_ARM_THM_PC22) + | RelocationFlags::Elf(elf::R_ARM_THM_XPC22) => { + let data = section_data[address..address + 2].try_into()?; let high = self.endianness.read_i16_bytes(data) as i32; - let data = section.data[address + 2..address + 4].try_into()?; + let data = section_data[address + 2..address + 4].try_into()?; let low = self.endianness.read_i16_bytes(data) as i32; let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff); @@ -262,8 +310,8 @@ impl ObjArch for ObjArchArm { } // Data - RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => { - let data = section.data[address..address + 4].try_into()?; + RelocationFlags::Elf(elf::R_ARM_ABS32) => { + let data = section_data[address..address + 4].try_into()?; self.endianness.read_i32_bytes(data) } @@ -283,7 +331,7 @@ impl ObjArch for ObjArchArm { fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { match flags { - RelocationFlags::Elf { r_type } => match r_type { + RelocationFlags::Elf(r_type) => match r_type { elf::R_ARM_ABS32 => 4, elf::R_ARM_REL32 => 4, elf::R_ARM_ABS16 => 2, @@ -293,48 +341,67 @@ impl ObjArch for ObjArchArm { _ => 1, } } + + fn symbol_address(&self, address: u64, kind: SymbolKind) -> u64 { + if kind == SymbolKind::Function { + address & !1 + } else { + address + } + } + + fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet { + let mut flags = SymbolFlagSet::default(); + if DisasmMode::from_symbol(symbol).is_some() { + flags |= SymbolFlag::Hidden; + } + flags + } } #[derive(Clone, Copy, Debug)] struct DisasmMode { address: u32, - mapping: ParseMode, + mapping: unarm::ParseMode, } impl DisasmMode { - fn from_symbol<'a>(sym: &Symbol<'a, '_, &'a [u8]>) -> Option { - if let Ok(name) = sym.name() { - ParseMode::from_mapping_symbol(name) - .map(|mapping| DisasmMode { address: sym.address() as u32, mapping }) - } else { - None - } + fn from_symbol<'a>(sym: &object::Symbol<'a, '_, &'a [u8]>) -> Option { + sym.name() + .ok() + .and_then(unarm::ParseMode::from_mapping_symbol) + .map(|mapping| DisasmMode { address: sym.address() as u32, mapping }) } } fn push_args( - parsed_ins: &ParsedIns, - config: &DiffObjConfig, - reloc_arg: Option, + parsed_ins: &unarm::ParsedIns, + relocation: Option, cur_addr: u32, - display_options: DisplayOptions, -) -> Result<(Vec, Option)> { - let mut args = vec![]; - let mut branch_dest = None; + display_options: unarm::DisplayOptions, + mut arg_cb: impl FnMut(InstructionPart) -> Result<()>, +) -> Result<()> { + let reloc_arg = find_reloc_arg(parsed_ins, relocation); let mut writeback = false; let mut deref = false; for (i, arg) in parsed_ins.args_iter().enumerate() { // Emit punctuation before separator if deref { match arg { - Argument::OffsetImm(OffsetImm { post_indexed: true, value: _ }) - | Argument::OffsetReg(OffsetReg { add: _, post_indexed: true, reg: _ }) - | Argument::CoOption(_) => { + args::Argument::OffsetImm(args::OffsetImm { post_indexed: true, value: _ }) + | args::Argument::OffsetReg(args::OffsetReg { + add: _, + post_indexed: true, + reg: _, + }) + | args::Argument::CoOption(_) => { deref = false; - args.push(ObjInsArg::PlainText("]".into())); + arg_cb(InstructionPart::Basic("]"))?; if writeback { writeback = false; - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into()))); + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque("!".into()), + )))?; } } _ => {} @@ -342,117 +409,179 @@ fn push_args( } if i > 0 { - args.push(ObjInsArg::PlainText(config.separator().into())); + arg_cb(InstructionPart::Separator)?; } if reloc_arg == Some(i) { - args.push(ObjInsArg::Reloc); + arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; } else { match arg { - Argument::None => {} - Argument::Reg(reg) => { + args::Argument::None => {} + args::Argument::Reg(reg) => { if reg.deref { deref = true; - args.push(ObjInsArg::PlainText("[".into())); + arg_cb(InstructionPart::Basic("["))?; } - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - reg.reg.display(display_options.reg_names).to_string().into(), - ))); + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque( + reg.reg.display(display_options.reg_names).to_string().into(), + ), + )))?; if reg.writeback { if reg.deref { writeback = true; } else { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into()))); + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque("!".into()), + )))?; } } } - Argument::RegList(reg_list) => { - args.push(ObjInsArg::PlainText("{".into())); + args::Argument::RegList(reg_list) => { + arg_cb(InstructionPart::Basic("{"))?; let mut first = true; for i in 0..16 { if (reg_list.regs & (1 << i)) != 0 { if !first { - args.push(ObjInsArg::PlainText(config.separator().into())); + arg_cb(InstructionPart::Separator)?; } - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - Register::parse(i) - .display(display_options.reg_names) - .to_string() - .into(), - ))); + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque( + args::Register::parse(i) + .display(display_options.reg_names) + .to_string() + .into(), + ), + )))?; first = false; } } - args.push(ObjInsArg::PlainText("}".into())); + arg_cb(InstructionPart::Basic("}"))?; if reg_list.user_mode { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("^".to_string().into()))); + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque("^".into()), + )))?; } } - Argument::UImm(value) | Argument::CoOpcode(value) | Argument::SatImm(value) => { - args.push(ObjInsArg::PlainText("#".into())); - args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64))); + args::Argument::UImm(value) + | args::Argument::CoOpcode(value) + | args::Argument::SatImm(value) => { + arg_cb(InstructionPart::Basic("#"))?; + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Unsigned(*value as u64), + )))?; } - Argument::SImm(value) - | Argument::OffsetImm(OffsetImm { post_indexed: _, value }) => { - args.push(ObjInsArg::PlainText("#".into())); - args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(*value as i64))); + args::Argument::SImm(value) + | args::Argument::OffsetImm(args::OffsetImm { post_indexed: _, value }) => { + arg_cb(InstructionPart::Basic("#"))?; + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Signed(*value as i64), + )))?; } - Argument::BranchDest(value) => { + args::Argument::BranchDest(value) => { let dest = cur_addr.wrapping_add_signed(*value) as u64; - args.push(ObjInsArg::BranchDest(dest)); - branch_dest = Some(dest); + arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(dest)))?; } - Argument::CoOption(value) => { - args.push(ObjInsArg::PlainText("{".into())); - args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64))); - args.push(ObjInsArg::PlainText("}".into())); + args::Argument::CoOption(value) => { + arg_cb(InstructionPart::Basic("{"))?; + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Unsigned(*value as u64), + )))?; + arg_cb(InstructionPart::Basic("}"))?; } - Argument::CoprocNum(value) => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(format!("p{}", value).into()))); + args::Argument::CoprocNum(value) => { + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque(format!("p{}", value).into()), + )))?; } - Argument::ShiftImm(shift) => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into()))); - args.push(ObjInsArg::PlainText(" #".into())); - args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(shift.imm as u64))); + args::Argument::ShiftImm(shift) => { + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque(shift.op.to_string().into()), + )))?; + arg_cb(InstructionPart::Basic(" #"))?; + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Unsigned(shift.imm as u64), + )))?; } - Argument::ShiftReg(shift) => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(shift.op.to_string().into()))); - args.push(ObjInsArg::PlainText(" ".into())); - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - shift.reg.display(display_options.reg_names).to_string().into(), - ))); + args::Argument::ShiftReg(shift) => { + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque(shift.op.to_string().into()), + )))?; + arg_cb(InstructionPart::Basic(" "))?; + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque( + shift.reg.display(display_options.reg_names).to_string().into(), + ), + )))?; } - Argument::OffsetReg(offset) => { + args::Argument::OffsetReg(offset) => { if !offset.add { - args.push(ObjInsArg::PlainText("-".into())); + arg_cb(InstructionPart::Basic("-"))?; } - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - offset.reg.display(display_options.reg_names).to_string().into(), - ))); + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque( + offset.reg.display(display_options.reg_names).to_string().into(), + ), + )))?; } - Argument::CpsrMode(mode) => { - args.push(ObjInsArg::PlainText("#".into())); - args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(mode.mode as u64))); + args::Argument::CpsrMode(mode) => { + arg_cb(InstructionPart::Basic("#"))?; + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Unsigned(mode.mode as u64), + )))?; if mode.writeback { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into()))); + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque("!".into()), + )))?; } } - Argument::CoReg(_) - | Argument::StatusReg(_) - | Argument::StatusMask(_) - | Argument::Shift(_) - | Argument::CpsrFlags(_) - | Argument::Endian(_) => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - arg.display(display_options, None).to_string().into(), - ))), + args::Argument::CoReg(_) + | args::Argument::StatusReg(_) + | args::Argument::StatusMask(_) + | args::Argument::Shift(_) + | args::Argument::CpsrFlags(_) + | args::Argument::Endian(_) => { + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque( + arg.display(display_options, None).to_string().into(), + ), + )))?; + } } } } if deref { - args.push(ObjInsArg::PlainText("]".into())); + arg_cb(InstructionPart::Basic("]"))?; if writeback { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into()))); + arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque( + "!".into(), + ))))?; } } - Ok((args, branch_dest)) + Ok(()) +} + +fn find_reloc_arg( + parsed_ins: &unarm::ParsedIns, + relocation: Option, +) -> Option { + if let Some(resolved) = relocation { + match resolved.relocation.flags { + // Calls + RelocationFlags::Elf(elf::R_ARM_THM_XPC22) + | RelocationFlags::Elf(elf::R_ARM_THM_PC22) + | RelocationFlags::Elf(elf::R_ARM_PC24) + | RelocationFlags::Elf(elf::R_ARM_XPC25) + | RelocationFlags::Elf(elf::R_ARM_CALL) => { + parsed_ins.args.iter().rposition(|a| matches!(a, args::Argument::BranchDest(_))) + } + // Data + RelocationFlags::Elf(elf::R_ARM_ABS32) => { + parsed_ins.args.iter().rposition(|a| matches!(a, args::Argument::UImm(_))) + } + _ => None, + } + } else { + None + } } diff --git a/objdiff-core/src/arch/arm64.rs b/objdiff-core/src/arch/arm64.rs index 971cc45..465c3bb 100644 --- a/objdiff-core/src/arch/arm64.rs +++ b/objdiff-core/src/arch/arm64.rs @@ -1,57 +1,56 @@ use alloc::{ borrow::Cow, - collections::BTreeMap, format, string::{String, ToString}, - vec, vec::Vec, }; -use core::cmp::Ordering; +use core::{cmp::Ordering, ops::Range}; use anyhow::{bail, Result}; -use object::{elf, File, Relocation, RelocationFlags}; -use yaxpeax_arch::{Arch, Decoder, Reader, U8Reader}; +use object::elf; +use yaxpeax_arch::{Arch as YaxpeaxArch, Decoder, Reader, U8Reader}; use yaxpeax_arm::armv8::a64::{ ARMv8, DecodeError, InstDecoder, Instruction, Opcode, Operand, SIMDSizeCode, ShiftStyle, SizeCode, }; use crate::{ - arch::{ObjArch, ProcessCodeResult}, - diff::DiffObjConfig, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, + arch::Arch, + diff::{display::InstructionPart, DiffObjConfig}, + obj::{ + InstructionArg, InstructionArgValue, InstructionRef, RelocationFlags, ResolvedRelocation, + ScannedInstruction, + }, }; -pub struct ObjArchArm64 {} +#[derive(Debug)] +pub struct ArchArm64 {} -impl ObjArchArm64 { - pub fn new(_file: &File) -> Result { Ok(Self {}) } +impl ArchArm64 { + pub fn new(_file: &object::File) -> Result { Ok(Self {}) } } -impl ObjArch for ObjArchArm64 { - fn process_code( +impl Arch for ArchArm64 { + fn scan_instructions( &self, address: u64, code: &[u8], - section_index: usize, - relocations: &[ObjReloc], - line_info: &BTreeMap, - config: &DiffObjConfig, - ) -> Result { + _section_index: usize, + _diff_config: &DiffObjConfig, + ) -> Result> { let start_address = address; - let end_address = address + code.len() as u64; - let ins_count = code.len() / 4; - - let mut ops = Vec::with_capacity(ins_count); - let mut insts = Vec::with_capacity(ins_count); + let mut ops = Vec::::with_capacity(code.len() / 4); let mut reader = U8Reader::new(code); let decoder = InstDecoder::default(); let mut ins = Instruction::default(); loop { // This is ridiculous... - let address = - start_address + as Reader<::Address, ::Word>>::total_offset(&mut reader); + let offset = as Reader< + ::Address, + ::Word, + >>::total_offset(&mut reader); + let address = start_address + offset; match decoder.decode_into(&mut ins, &mut reader) { Ok(()) => {} Err(e) => match e { @@ -59,94 +58,182 @@ impl ObjArch for ObjArchArm64 { DecodeError::InvalidOpcode | DecodeError::InvalidOperand | DecodeError::IncompleteDecoder => { - ops.push(u16::MAX); - insts.push(ObjIns { - address, - size: 4, - op: u16::MAX, - mnemonic: Cow::Borrowed(""), - args: vec![], - reloc: None, + ops.push(ScannedInstruction { + ins_ref: InstructionRef { address, size: 4, opcode: u16::MAX }, branch_dest: None, - line: None, - formatted: "".to_string(), - orig: None, }); continue; } }, } - let line = line_info.range(..=address).last().map(|(_, &b)| b); - let reloc = relocations.iter().find(|r| (r.address & !3) == address).cloned(); - - let mut args = vec![]; - let mut ctx = DisplayCtx { - address, - section_index, - start_address, - end_address, - reloc: reloc.as_ref(), - config, - branch_dest: None, - }; - // Simplify instruction and process args - let mnemonic = display_instruction(&mut args, &ins, &mut ctx); - - // Format the instruction without simplification - let mut orig = ins.opcode.to_string(); - for (i, o) in ins.operands.iter().enumerate() { - if let Operand::Nothing = o { - break; - } - if i == 0 { - orig.push(' '); - } else { - orig.push_str(", "); - } - orig.push_str(o.to_string().as_str()); - } - - if let Some(reloc) = &reloc { - if !args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) { - args.push(ObjInsArg::PlainText(Cow::Borrowed(" "))); - log::warn!( - "Unhandled ARM64 relocation {:?}: {} @ {:#X}", - reloc.flags, - orig, - address - ); - } - }; - - let op = opcode_to_u16(ins.opcode); - ops.push(op); - let branch_dest = ctx.branch_dest; - insts.push(ObjIns { - address, - size: 4, - op, - mnemonic: Cow::Borrowed(mnemonic), - args, - reloc, - branch_dest, - line, - formatted: ins.to_string(), - orig: Some(orig), - }); + let opcode = opcode_to_u16(ins.opcode); + let ins_ref = InstructionRef { address, size: 4, opcode }; + let branch_dest = branch_dest(ins_ref, &code[offset as usize..offset as usize + 4]); + ops.push(ScannedInstruction { ins_ref, branch_dest }); } - Ok(ProcessCodeResult { ops, insts }) + Ok(ops) } + fn display_instruction( + &self, + ins_ref: InstructionRef, + code: &[u8], + relocation: Option, + function_range: Range, + _section_index: usize, + diff_config: &DiffObjConfig, + cb: &mut dyn FnMut(InstructionPart) -> Result<()>, + ) -> Result<()> { + let mut reader = U8Reader::new(code); + let decoder = InstDecoder::default(); + let mut ins = Instruction::default(); + if decoder.decode_into(&mut ins, &mut reader).is_err() { + cb(InstructionPart::Opcode(Cow::Borrowed(""), u16::MAX))?; + return Ok(()); + } + + let mut ctx = DisplayCtx { + address: ins_ref.address, + section_index: 0, + start_address: function_range.start, + end_address: function_range.end, + reloc: relocation, + config: diff_config, + }; + + let mut display_args = Vec::with_capacity(16); + let mnemonic = display_instruction(&mut |ret| display_args.push(ret), &ins, &mut ctx); + cb(InstructionPart::Opcode(Cow::Borrowed(mnemonic), ins_ref.opcode))?; + for arg in display_args { + cb(arg)?; + } + Ok(()) + } + + // fn process_code( + // &self, + // address: u64, + // code: &[u8], + // section_index: usize, + // relocations: &[ObjReloc], + // line_info: &BTreeMap, + // config: &DiffObjConfig, + // ) -> Result { + // let start_address = address; + // let end_address = address + code.len() as u64; + // let ins_count = code.len() / 4; + // + // let mut ops = Vec::with_capacity(ins_count); + // let mut insts = Vec::with_capacity(ins_count); + // + // let mut reader = U8Reader::new(code); + // let decoder = InstDecoder::default(); + // let mut ins = Instruction::default(); + // loop { + // // This is ridiculous... + // let address = start_address + // + as Reader< + // ::Address, + // ::Word, + // >>::total_offset(&mut reader); + // match decoder.decode_into(&mut ins, &mut reader) { + // Ok(()) => {} + // Err(e) => match e { + // DecodeError::ExhaustedInput => break, + // DecodeError::InvalidOpcode + // | DecodeError::InvalidOperand + // | DecodeError::IncompleteDecoder => { + // ops.push(u16::MAX); + // insts.push(ObjIns { + // address, + // size: 4, + // op: u16::MAX, + // mnemonic: Cow::Borrowed(""), + // args: vec![], + // reloc: None, + // branch_dest: None, + // line: None, + // formatted: "".to_string(), + // orig: None, + // }); + // continue; + // } + // }, + // } + // + // let line = line_info.range(..=address).last().map(|(_, &b)| b); + // let reloc = relocations.iter().find(|r| (r.address & !3) == address).cloned(); + // + // let mut args = vec![]; + // let mut ctx = DisplayCtx { + // address, + // section_index, + // start_address, + // end_address, + // reloc: reloc.as_ref(), + // config, + // branch_dest: None, + // }; + // // Simplify instruction and process args + // let mnemonic = display_instruction(&mut args, &ins, &mut ctx); + // + // // Format the instruction without simplification + // let mut orig = ins.opcode.to_string(); + // for (i, o) in ins.operands.iter().enumerate() { + // if let Operand::Nothing = o { + // break; + // } + // if i == 0 { + // orig.push(' '); + // } else { + // orig.push_str(", "); + // } + // orig.push_str(o.to_string().as_str()); + // } + // + // if let Some(reloc) = &reloc { + // if !args.iter().any(|a| matches!(a, InstructionArg::Reloc)) { + // push_arg(args, InstructionArg::PlainText(Cow::Borrowed(" "))); + // log::warn!( + // "Unhandled ARM64 relocation {:?}: {} @ {:#X}", + // reloc.flags, + // orig, + // address + // ); + // } + // }; + // + // let op = opcode_to_u16(ins.opcode); + // ops.push(op); + // let branch_dest = ctx.branch_dest; + // insts.push(ObjIns { + // address, + // size: 4, + // op, + // mnemonic: Cow::Borrowed(mnemonic), + // args, + // reloc, + // branch_dest, + // line, + // formatted: ins.to_string(), + // orig: Some(orig), + // }); + // } + // + // Ok(ProcessCodeResult { ops, insts }) + // } + fn implcit_addend( &self, - _file: &File<'_>, - _section: &ObjSection, + _file: &object::File<'_>, + _section: &object::Section, address: u64, - reloc: &Relocation, + _relocation: &object::Relocation, + flags: RelocationFlags, ) -> Result { - bail!("Unsupported ARM64 implicit relocation {:#x}:{:?}", address, reloc.flags()) + bail!("Unsupported ARM64 implicit relocation {:#x}:{:?}", address, flags) } fn demangle(&self, name: &str) -> Option { @@ -157,25 +244,21 @@ impl ObjArch for ObjArchArm64 { fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> { match flags { - RelocationFlags::Elf { r_type: elf::R_AARCH64_ADR_PREL_PG_HI21 } => { + RelocationFlags::Elf(elf::R_AARCH64_ADR_PREL_PG_HI21) => { Cow::Borrowed("R_AARCH64_ADR_PREL_PG_HI21") } - RelocationFlags::Elf { r_type: elf::R_AARCH64_ADD_ABS_LO12_NC } => { + RelocationFlags::Elf(elf::R_AARCH64_ADD_ABS_LO12_NC) => { Cow::Borrowed("R_AARCH64_ADD_ABS_LO12_NC") } - RelocationFlags::Elf { r_type: elf::R_AARCH64_JUMP26 } => { - Cow::Borrowed("R_AARCH64_JUMP26") - } - RelocationFlags::Elf { r_type: elf::R_AARCH64_CALL26 } => { - Cow::Borrowed("R_AARCH64_CALL26") - } - RelocationFlags::Elf { r_type: elf::R_AARCH64_LDST32_ABS_LO12_NC } => { + RelocationFlags::Elf(elf::R_AARCH64_JUMP26) => Cow::Borrowed("R_AARCH64_JUMP26"), + RelocationFlags::Elf(elf::R_AARCH64_CALL26) => Cow::Borrowed("R_AARCH64_CALL26"), + RelocationFlags::Elf(elf::R_AARCH64_LDST32_ABS_LO12_NC) => { Cow::Borrowed("R_AARCH64_LDST32_ABS_LO12_NC") } - RelocationFlags::Elf { r_type: elf::R_AARCH64_ADR_GOT_PAGE } => { + RelocationFlags::Elf(elf::R_AARCH64_ADR_GOT_PAGE) => { Cow::Borrowed("R_AARCH64_ADR_GOT_PAGE") } - RelocationFlags::Elf { r_type: elf::R_AARCH64_LD64_GOT_LO12_NC } => { + RelocationFlags::Elf(elf::R_AARCH64_LD64_GOT_LO12_NC) => { Cow::Borrowed("R_AARCH64_LD64_GOT_LO12_NC") } _ => Cow::Owned(format!("<{flags:?}>")), @@ -184,7 +267,7 @@ impl ObjArch for ObjArchArm64 { fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { match flags { - RelocationFlags::Elf { r_type } => match r_type { + RelocationFlags::Elf(r_type) => match r_type { elf::R_AARCH64_ABS64 => 8, elf::R_AARCH64_ABS32 => 4, elf::R_AARCH64_ABS16 => 2, @@ -198,25 +281,51 @@ impl ObjArch for ObjArchArm64 { } } +fn branch_dest(ins_ref: InstructionRef, code: &[u8]) -> Option { + const OPCODE_B: u16 = opcode_to_u16(Opcode::B); + const OPCODE_BL: u16 = opcode_to_u16(Opcode::BL); + const OPCODE_BCC: u16 = opcode_to_u16(Opcode::Bcc(0)); + const OPCODE_CBZ: u16 = opcode_to_u16(Opcode::CBZ); + const OPCODE_CBNZ: u16 = opcode_to_u16(Opcode::CBNZ); + const OPCODE_TBZ: u16 = opcode_to_u16(Opcode::TBZ); + const OPCODE_TBNZ: u16 = opcode_to_u16(Opcode::TBNZ); + + let word = u32::from_le_bytes(code.try_into().ok()?); + match ins_ref.opcode { + OPCODE_B | OPCODE_BL => { + let offset = ((word & 0x03ff_ffff) << 2) as i32; + let extended_offset = (offset << 4) >> 4; + ins_ref.address.checked_add_signed(extended_offset as i64) + } + OPCODE_BCC | OPCODE_CBZ | OPCODE_CBNZ => { + let offset = (word as i32 & 0x00ff_ffe0) >> 3; + let extended_offset = (offset << 11) >> 11; + ins_ref.address.checked_add_signed(extended_offset as i64) + } + OPCODE_TBZ | OPCODE_TBNZ => { + let offset = (word as i32 & 0x0007_ffe0) >> 3; + let extended_offset = (offset << 16) >> 16; + ins_ref.address.checked_add_signed(extended_offset as i64) + } + _ => None, + } +} + struct DisplayCtx<'a> { address: u64, section_index: usize, start_address: u64, end_address: u64, - reloc: Option<&'a ObjReloc>, + reloc: Option>, config: &'a DiffObjConfig, - branch_dest: Option, } // Source: https://github.com/iximeow/yaxpeax-arm/blob/716a6e3fc621f5fe3300f3309e56943b8e1e65ad/src/armv8/a64.rs#L317 // License: 0BSD // Reworked for more structured output. The library only gives us a Display impl, and no way to // capture any of this information, so it needs to be reimplemented here. -fn display_instruction( - args: &mut Vec, - ins: &Instruction, - ctx: &mut DisplayCtx, -) -> &'static str { +fn display_instruction(args: &mut Cb, ins: &Instruction, ctx: &mut DisplayCtx) -> &'static str +where Cb: FnMut(InstructionPart) { let mnemonic = match ins.opcode { Opcode::Invalid => return "", Opcode::UDF => "udf", @@ -2025,12 +2134,14 @@ fn condition_code(cond: u8) -> &'static str { } #[inline] -fn push_register(args: &mut Vec, size: SizeCode, reg: u16, sp: bool) { +fn push_register(args: &mut Cb, size: SizeCode, reg: u16, sp: bool) +where Cb: FnMut(InstructionPart) { push_opaque(args, reg_name(size, reg, sp)); } #[inline] -fn push_shift(args: &mut Vec, style: ShiftStyle, amount: u8) { +fn push_shift(args: &mut Cb, style: ShiftStyle, amount: u8) +where Cb: FnMut(InstructionPart) { push_opaque(args, shift_style(style)); if amount != 0 { push_plain(args, " "); @@ -2039,11 +2150,13 @@ fn push_shift(args: &mut Vec, style: ShiftStyle, amount: u8) { } #[inline] -fn push_condition_code(args: &mut Vec, cond: u8) { +fn push_condition_code(args: &mut Cb, cond: u8) +where Cb: FnMut(InstructionPart) { push_opaque(args, condition_code(cond)); } -fn push_barrier(args: &mut Vec, option: u8) { +fn push_barrier(args: &mut Cb, option: u8) +where Cb: FnMut(InstructionPart) { match option { 0b0001 => push_opaque(args, "oshld"), 0b0010 => push_opaque(args, "oshst"), @@ -2062,89 +2175,104 @@ fn push_barrier(args: &mut Vec, option: u8) { } #[inline] -fn push_opaque(args: &mut Vec, text: &'static str) { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(Cow::Borrowed(text)))); +fn push_opaque(args: &mut Cb, text: &'static str) +where Cb: FnMut(InstructionPart) { + push_arg(args, InstructionArg::Value(InstructionArgValue::Opaque(Cow::Borrowed(text)))); } #[inline] -fn push_plain(args: &mut Vec, text: &'static str) { - args.push(ObjInsArg::PlainText(Cow::Borrowed(text))); +fn push_plain(args: &mut Cb, text: &'static str) +where Cb: FnMut(InstructionPart) { + args(InstructionPart::Basic(text)); } #[inline] -fn push_separator(args: &mut Vec, config: &DiffObjConfig) { - args.push(ObjInsArg::PlainText(Cow::Borrowed(config.separator()))); +fn push_separator(args: &mut Cb, _config: &DiffObjConfig) +where Cb: FnMut(InstructionPart) { + args(InstructionPart::Separator); } #[inline] -fn push_unsigned(args: &mut Vec, v: u64) { +fn push_unsigned(args: &mut Cb, v: u64) +where Cb: FnMut(InstructionPart) { push_plain(args, "#"); - args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(v))); + push_arg(args, InstructionArg::Value(InstructionArgValue::Unsigned(v))); } #[inline] -fn push_signed(args: &mut Vec, v: i64) { +fn push_signed(args: &mut Cb, v: i64) +where Cb: FnMut(InstructionPart) { push_plain(args, "#"); - args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(v))); + push_arg(args, InstructionArg::Value(InstructionArgValue::Signed(v))); +} + +#[inline] +fn push_arg(args: &mut Cb, arg: InstructionArg) +where Cb: FnMut(InstructionPart) { + args(InstructionPart::Arg(arg)); } /// Relocations that appear in Operand::PCOffset. -fn is_pc_offset_reloc(reloc: Option<&ObjReloc>) -> Option<&ObjReloc> { - if let Some(reloc) = reloc { - if let RelocationFlags::Elf { - r_type: - elf::R_AARCH64_ADR_PREL_PG_HI21 - | elf::R_AARCH64_JUMP26 - | elf::R_AARCH64_CALL26 - | elf::R_AARCH64_ADR_GOT_PAGE, - } = reloc.flags +fn is_pc_offset_reloc(reloc: Option) -> Option { + if let Some(resolved) = reloc { + if let RelocationFlags::Elf( + elf::R_AARCH64_ADR_PREL_PG_HI21 + | elf::R_AARCH64_JUMP26 + | elf::R_AARCH64_CALL26 + | elf::R_AARCH64_ADR_GOT_PAGE, + ) = resolved.relocation.flags { - return Some(reloc); + return Some(resolved); } } None } /// Relocations that appear in Operand::Immediate. -fn is_imm_reloc(reloc: Option<&ObjReloc>) -> bool { - matches!(reloc, Some(reloc) if matches!(reloc.flags, RelocationFlags::Elf { - r_type: elf::R_AARCH64_ADD_ABS_LO12_NC, - })) +fn is_imm_reloc(resolved: Option) -> bool { + resolved.is_some_and(|r| { + matches!(r.relocation.flags, RelocationFlags::Elf(elf::R_AARCH64_ADD_ABS_LO12_NC)) + }) } /// Relocations that appear in Operand::RegPreIndex/RegPostIndex. -fn is_reg_index_reloc(reloc: Option<&ObjReloc>) -> bool { - matches!(reloc, Some(reloc) if matches!(reloc.flags, RelocationFlags::Elf { - r_type: elf::R_AARCH64_LDST32_ABS_LO12_NC | elf::R_AARCH64_LD64_GOT_LO12_NC, - })) +fn is_reg_index_reloc(resolved: Option) -> bool { + resolved.is_some_and(|r| { + matches!( + r.relocation.flags, + RelocationFlags::Elf( + elf::R_AARCH64_LDST32_ABS_LO12_NC | elf::R_AARCH64_LD64_GOT_LO12_NC + ) + ) + }) } -fn push_operand(args: &mut Vec, o: &Operand, ctx: &mut DisplayCtx) { +fn push_operand(args: &mut Cb, o: &Operand, ctx: &mut DisplayCtx) +where Cb: FnMut(InstructionPart) { match o { Operand::Nothing => unreachable!(), Operand::PCOffset(off) => { - if let Some(reloc) = is_pc_offset_reloc(ctx.reloc) { - let target_address = reloc.target.address.checked_add_signed(reloc.addend); - if reloc.target.orig_section_index == Some(ctx.section_index) + if let Some(resolved) = is_pc_offset_reloc(ctx.reloc) { + let target_address = + resolved.symbol.address.checked_add_signed(resolved.relocation.addend); + if resolved.symbol.section == Some(ctx.section_index) && matches!(target_address, Some(addr) if addr > ctx.start_address && addr < ctx.end_address) { let dest = target_address.unwrap(); push_plain(args, "$"); - args.push(ObjInsArg::BranchDest(dest)); - ctx.branch_dest = Some(dest); + push_arg(args, InstructionArg::BranchDest(dest)); } else { - args.push(ObjInsArg::Reloc); + push_arg(args, InstructionArg::Reloc); } } else { let dest = ctx.address.saturating_add_signed(*off); push_plain(args, "$"); - args.push(ObjInsArg::BranchDest(dest)); - ctx.branch_dest = Some(dest); + push_arg(args, InstructionArg::BranchDest(dest)); } } Operand::Immediate(imm) => { if is_imm_reloc(ctx.reloc) { - args.push(ObjInsArg::Reloc); + push_arg(args, InstructionArg::Reloc); } else { push_unsigned(args, *imm as u64); } @@ -2249,7 +2377,7 @@ fn push_operand(args: &mut Vec, o: &Operand, ctx: &mut DisplayCtx) { push_register(args, SizeCode::X, *reg, true); if is_reg_index_reloc(ctx.reloc) { push_separator(args, ctx.config); - args.push(ObjInsArg::Reloc); + push_arg(args, InstructionArg::Reloc); } else if *offset != 0 || *wback_bit { push_separator(args, ctx.config); push_signed(args, *offset as i64); @@ -2265,7 +2393,7 @@ fn push_operand(args: &mut Vec, o: &Operand, ctx: &mut DisplayCtx) { push_plain(args, "]"); push_separator(args, ctx.config); if is_reg_index_reloc(ctx.reloc) { - args.push(ObjInsArg::Reloc); + push_arg(args, InstructionArg::Reloc); } else { push_signed(args, *offset as i64); } @@ -2276,10 +2404,13 @@ fn push_operand(args: &mut Vec, o: &Operand, ctx: &mut DisplayCtx) { push_plain(args, "]"); push_separator(args, ctx.config); // TODO does 31 have to be handled separate? - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(Cow::Owned(format!( - "x{}", - offset_reg - ))))); + push_arg( + args, + InstructionArg::Value(InstructionArgValue::Opaque(Cow::Owned(format!( + "x{}", + offset_reg + )))), + ); } // Fall back to original logic Operand::SIMDRegister(_, _) @@ -2293,13 +2424,16 @@ fn push_operand(args: &mut Vec, o: &Operand, ctx: &mut DisplayCtx) { | Operand::SystemReg(_) | Operand::ControlReg(_) | Operand::PstateField(_) => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(Cow::Owned(o.to_string())))); + push_arg( + args, + InstructionArg::Value(InstructionArgValue::Opaque(Cow::Owned(o.to_string()))), + ); } } } // Opcode is #[repr(u16)], but the tuple variants negate that, so we have to do this instead. -fn opcode_to_u16(opcode: Opcode) -> u16 { +const fn opcode_to_u16(opcode: Opcode) -> u16 { match opcode { Opcode::Invalid => u16::MAX, Opcode::UDF => 0, diff --git a/objdiff-core/src/arch/mips.rs b/objdiff-core/src/arch/mips.rs index ebe7aa1..bc128ca 100644 --- a/objdiff-core/src/arch/mips.rs +++ b/objdiff-core/src/arch/mips.rs @@ -1,25 +1,27 @@ -use alloc::{borrow::Cow, collections::BTreeMap, format, string::ToString, vec::Vec}; +use alloc::{borrow::Cow, format, string::ToString, vec::Vec}; +use core::ops::Range; use anyhow::{bail, Result}; -use object::{ - elf, Endian, Endianness, File, FileFlags, Object, ObjectSection, ObjectSymbol, Relocation, - RelocationFlags, RelocationTarget, -}; +use object::{elf, Endian as _, Object as _, ObjectSection as _, ObjectSymbol as _}; use rabbitizer::{ abi::Abi, operands::{ValuedOperand, IU16}, registers_meta::Register, - Instruction, InstructionDisplayFlags, InstructionFlags, IsaExtension, IsaVersion, Vram, + IsaExtension, IsaVersion, Vram, }; use crate::{ - arch::{ObjArch, ProcessCodeResult}, - diff::{DiffObjConfig, MipsAbi, MipsInstrCategory}, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, + arch::Arch, + diff::{display::InstructionPart, DiffObjConfig, MipsAbi, MipsInstrCategory}, + obj::{ + InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, + ResolvedRelocation, ScannedInstruction, + }, }; -pub struct ObjArchMips { - pub endianness: Endianness, +#[derive(Debug)] +pub struct ArchMips { + pub endianness: object::Endianness, pub abi: Abi, pub isa_extension: Option, pub ri_gp_value: i32, @@ -33,13 +35,13 @@ const EF_MIPS_MACH_5900: u32 = 0x00920000; const R_MIPS15_S3: u32 = 119; -impl ObjArchMips { - pub fn new(object: &File) -> Result { +impl ArchMips { + pub fn new(object: &object::File) -> Result { let mut abi = Abi::O32; let mut isa_extension = None; match object.flags() { - FileFlags::None => {} - FileFlags::Elf { e_flags, .. } => { + object::FileFlags::None => {} + object::FileFlags::Elf { e_flags, .. } => { abi = match e_flags & EF_MIPS_ABI { elf::EF_MIPS_ABI_O32 | elf::EF_MIPS_ABI_O64 => Abi::O32, elf::EF_MIPS_ABI_EABI32 | elf::EF_MIPS_ABI_EABI64 => Abi::N32, @@ -73,19 +75,9 @@ impl ObjArchMips { Ok(Self { endianness: object.endianness(), abi, isa_extension, ri_gp_value }) } -} -impl ObjArch for ObjArchMips { - fn process_code( - &self, - address: u64, - code: &[u8], - section_index: usize, - relocations: &[ObjReloc], - line_info: &BTreeMap, - config: &DiffObjConfig, - ) -> Result { - let isa_extension = match config.mips_instr_category { + fn instruction_flags(&self, diff_config: &DiffObjConfig) -> rabbitizer::InstructionFlags { + let isa_extension = match diff_config.mips_instr_category { MipsInstrCategory::Auto => self.isa_extension, MipsInstrCategory::Cpu => None, MipsInstrCategory::Rsp => Some(IsaExtension::RSP), @@ -93,158 +85,105 @@ impl ObjArch for ObjArchMips { MipsInstrCategory::R4000allegrex => Some(IsaExtension::R4000ALLEGREX), MipsInstrCategory::R5900 => Some(IsaExtension::R5900), }; - let instruction_flags = match isa_extension { - Some(extension) => InstructionFlags::new_extension(extension), - None => InstructionFlags::new_isa(IsaVersion::MIPS_III, None), + match isa_extension { + Some(extension) => rabbitizer::InstructionFlags::new_extension(extension), + None => rabbitizer::InstructionFlags::new_isa(IsaVersion::MIPS_III, None), } - .with_abi(match config.mips_abi { + .with_abi(match diff_config.mips_abi { MipsAbi::Auto => self.abi, MipsAbi::O32 => Abi::O32, MipsAbi::N32 => Abi::N32, MipsAbi::N64 => Abi::N64, - }); - let display_flags = InstructionDisplayFlags::default().with_unknown_instr_comment(false); + }) + } + fn instruction_display_flags( + &self, + _diff_config: &DiffObjConfig, + ) -> rabbitizer::InstructionDisplayFlags { + rabbitizer::InstructionDisplayFlags::default().with_unknown_instr_comment(false) + } + + fn parse_ins_ref( + &self, + ins_ref: InstructionRef, + code: &[u8], + diff_config: &DiffObjConfig, + ) -> Result { + Ok(rabbitizer::Instruction::new( + self.endianness.read_u32_bytes(code.try_into()?), + Vram::new(ins_ref.address as u32), + self.instruction_flags(diff_config), + )) + } +} + +impl Arch for ArchMips { + fn scan_instructions( + &self, + address: u64, + code: &[u8], + _section_index: usize, + diff_config: &DiffObjConfig, + ) -> Result> { + let instruction_flags = self.instruction_flags(diff_config); let start_address = address; - let end_address = address + code.len() as u64; - let ins_count = code.len() / 4; - let mut ops = Vec::::with_capacity(ins_count); - let mut insts = Vec::::with_capacity(ins_count); + let mut ops = Vec::::with_capacity(code.len() / 4); let mut cur_addr = start_address as u32; for chunk in code.chunks_exact(4) { - let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr); let code = self.endianness.read_u32_bytes(chunk.try_into()?); - let instruction = Instruction::new(code, Vram::new(cur_addr), instruction_flags); - - let formatted = instruction.display(&display_flags, None::<&str>, 0).to_string(); - let op = instruction.opcode() as u16; - ops.push(op); - - let mnemonic = instruction.opcode().name(); - let mut branch_dest = instruction.get_branch_offset_generic().map(|a| a.inner() as u64); - - let operands = instruction.valued_operands_iter(); - - let mut args = Vec::with_capacity(6); - for (idx, op) in operands.enumerate() { - if idx > 0 { - args.push(ObjInsArg::PlainText(config.separator().into())); - } - - match op { - ValuedOperand::core_immediate(imm) => { - if let Some(reloc) = reloc { - push_reloc(&mut args, reloc)?; - } else { - args.push(ObjInsArg::Arg(match imm { - IU16::Integer(s) => ObjInsArgValue::Signed(s as i64), - IU16::Unsigned(u) => ObjInsArgValue::Unsigned(u as u64), - })); - } - } - ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => { - if let Some(reloc) = reloc { - // If the relocation target is within the current function, we can - // convert it into a relative branch target. Note that we check - // target_address > start_address instead of >= so that recursive - // tail calls are not considered branch targets. - let target_address = - reloc.target.address.checked_add_signed(reloc.addend); - if reloc.target.orig_section_index == Some(section_index) - && matches!(target_address, Some(addr) if addr > start_address && addr < end_address) - { - let target_address = target_address.unwrap(); - args.push(ObjInsArg::BranchDest(target_address)); - branch_dest = Some(target_address); - } else { - push_reloc(&mut args, reloc)?; - branch_dest = None; - } - } else if let Some(branch_dest) = branch_dest { - args.push(ObjInsArg::BranchDest(branch_dest)); - } else { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - op.display(&instruction, &display_flags, None::<&str>) - .to_string() - .into(), - ))); - } - } - ValuedOperand::core_immediate_base(imm, base) => { - if let Some(reloc) = reloc { - push_reloc(&mut args, reloc)?; - } else { - args.push(ObjInsArg::Arg(match imm { - IU16::Integer(s) => ObjInsArgValue::Signed(s as i64), - IU16::Unsigned(u) => ObjInsArgValue::Unsigned(u as u64), - })); - } - args.push(ObjInsArg::PlainText("(".into())); - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - base.either_name(instruction.flags().abi(), display_flags.named_gpr()) - .into(), - ))); - args.push(ObjInsArg::PlainText(")".into())); - } - // ValuedOperand::r5900_immediate15(..) => match reloc { - // Some(reloc) - // if reloc.flags == RelocationFlags::Elf { r_type: R_MIPS15_S3 } => - // { - // push_reloc(&mut args, reloc)?; - // } - // _ => { - // args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - // op.disassemble(&instruction, None).into(), - // ))); - // } - // }, - _ => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - op.display(&instruction, &display_flags, None::<&str>) - .to_string() - .into(), - ))); - } - } - } - let line = line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b); - insts.push(ObjIns { - address: cur_addr as u64, - size: 4, - op, - mnemonic: Cow::Borrowed(mnemonic), - args, - reloc: reloc.cloned(), + let vram = Vram::new(cur_addr); + let instruction = rabbitizer::Instruction::new(code, vram, instruction_flags); + let opcode = instruction.opcode() as u16; + let branch_dest = + instruction.get_branch_offset_generic().map(|o| (vram + o).inner() as u64); + ops.push(ScannedInstruction { + ins_ref: InstructionRef { address, size: 4, opcode }, branch_dest, - line, - formatted, - orig: None, }); cur_addr += 4; } - Ok(ProcessCodeResult { ops, insts }) + Ok(ops) + } + + fn display_instruction( + &self, + ins_ref: InstructionRef, + code: &[u8], + relocation: Option, + function_range: Range, + section_index: usize, + diff_config: &DiffObjConfig, + cb: &mut dyn FnMut(InstructionPart) -> Result<()>, + ) -> Result<()> { + let instruction = self.parse_ins_ref(ins_ref, code, diff_config)?; + let display_flags = self.instruction_display_flags(diff_config); + let opcode = instruction.opcode(); + cb(InstructionPart::Opcode(Cow::Borrowed(opcode.name()), opcode as u16))?; + push_args(&instruction, relocation, function_range, section_index, &display_flags, cb)?; + Ok(()) } fn implcit_addend( &self, - file: &File<'_>, - section: &ObjSection, + file: &object::File<'_>, + section: &object::Section, address: u64, - reloc: &Relocation, + reloc: &object::Relocation, + flags: RelocationFlags, ) -> Result { - let data = section.data[address as usize..address as usize + 4].try_into()?; - let addend = self.endianness.read_u32_bytes(data); - Ok(match reloc.flags() { - RelocationFlags::Elf { r_type: elf::R_MIPS_32 } => addend as i64, - RelocationFlags::Elf { r_type: elf::R_MIPS_26 } => ((addend & 0x03FFFFFF) << 2) as i64, - RelocationFlags::Elf { r_type: elf::R_MIPS_HI16 } => { - ((addend & 0x0000FFFF) << 16) as i32 as i64 + let data = section.data()?; + let code = data[address as usize..address as usize + 4].try_into()?; + let addend = self.endianness.read_u32_bytes(code); + Ok(match flags { + RelocationFlags::Elf(elf::R_MIPS_32) => addend as i64, + RelocationFlags::Elf(elf::R_MIPS_26) => ((addend & 0x03FFFFFF) << 2) as i64, + RelocationFlags::Elf(elf::R_MIPS_HI16) => ((addend & 0x0000FFFF) << 16) as i32 as i64, + RelocationFlags::Elf(elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16) => { + (addend & 0x0000FFFF) as i16 as i64 } - RelocationFlags::Elf { - r_type: elf::R_MIPS_LO16 | elf::R_MIPS_GOT16 | elf::R_MIPS_CALL16, - } => (addend & 0x0000FFFF) as i16 as i64, - RelocationFlags::Elf { r_type: elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL } => { - let RelocationTarget::Symbol(idx) = reloc.target() else { + RelocationFlags::Elf(elf::R_MIPS_GPREL16 | elf::R_MIPS_LITERAL) => { + let object::RelocationTarget::Symbol(idx) = reloc.target() else { bail!("Unsupported R_MIPS_GPREL16 relocation against a non-symbol"); }; let sym = file.symbol_by_index(idx)?; @@ -257,15 +196,15 @@ impl ObjArch for ObjArchMips { (addend & 0x0000FFFF) as i16 as i64 } } - RelocationFlags::Elf { r_type: elf::R_MIPS_PC16 } => 0, // PC-relative relocation - RelocationFlags::Elf { r_type: R_MIPS15_S3 } => ((addend & 0x001FFFC0) >> 3) as i64, + RelocationFlags::Elf(elf::R_MIPS_PC16) => 0, // PC-relative relocation + RelocationFlags::Elf(R_MIPS15_S3) => ((addend & 0x001FFFC0) >> 3) as i64, flags => bail!("Unsupported MIPS implicit relocation {flags:?}"), }) } fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> { match flags { - RelocationFlags::Elf { r_type } => match r_type { + RelocationFlags::Elf(r_type) => match r_type { elf::R_MIPS_32 => Cow::Borrowed("R_MIPS_32"), elf::R_MIPS_26 => Cow::Borrowed("R_MIPS_26"), elf::R_MIPS_HI16 => Cow::Borrowed("R_MIPS_HI16"), @@ -284,7 +223,7 @@ impl ObjArch for ObjArchMips { fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { match flags { - RelocationFlags::Elf { r_type } => match r_type { + RelocationFlags::Elf(r_type) => match r_type { elf::R_MIPS_16 => 2, elf::R_MIPS_32 => 4, _ => 1, @@ -294,40 +233,139 @@ impl ObjArch for ObjArchMips { } } -fn push_reloc(args: &mut Vec, reloc: &ObjReloc) -> Result<()> { +fn push_args( + instruction: &rabbitizer::Instruction, + relocation: Option, + function_range: Range, + section_index: usize, + display_flags: &rabbitizer::InstructionDisplayFlags, + mut arg_cb: impl FnMut(InstructionPart) -> Result<()>, +) -> Result<()> { + let operands = instruction.valued_operands_iter(); + for (idx, op) in operands.enumerate() { + if idx > 0 { + arg_cb(InstructionPart::Separator)?; + } + + match op { + ValuedOperand::core_immediate(imm) => { + if let Some(resolved) = relocation { + push_reloc(resolved.relocation, &mut arg_cb)?; + } else { + arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm { + IU16::Integer(s) => InstructionArgValue::Signed(s as i64), + IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64), + })))?; + } + } + ValuedOperand::core_label(..) | ValuedOperand::core_branch_target_label(..) => { + if let Some(resolved) = relocation { + // If the relocation target is within the current function, we can + // convert it into a relative branch target. Note that we check + // target_address > start_address instead of >= so that recursive + // tail calls are not considered branch targets. + let target_address = + resolved.symbol.address.checked_add_signed(resolved.relocation.addend); + if resolved.symbol.section == Some(section_index) + && target_address.is_some_and(|addr| { + addr > function_range.start && addr < function_range.end + }) + { + // TODO move this logic up a level + let target_address = target_address.unwrap(); + arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(target_address)))?; + } else { + push_reloc(resolved.relocation, &mut arg_cb)?; + } + } else if let Some(branch_dest) = instruction + .get_branch_offset_generic() + .map(|o| (instruction.vram() + o).inner() as u64) + { + arg_cb(InstructionPart::Arg(InstructionArg::BranchDest(branch_dest)))?; + } else { + arg_cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque( + op.display(instruction, display_flags, None::<&str>) + .to_string() + .into(), + ), + )))?; + } + } + ValuedOperand::core_immediate_base(imm, base) => { + if let Some(resolved) = relocation { + push_reloc(resolved.relocation, &mut arg_cb)?; + } else { + arg_cb(InstructionPart::Arg(InstructionArg::Value(match imm { + IU16::Integer(s) => InstructionArgValue::Signed(s as i64), + IU16::Unsigned(u) => InstructionArgValue::Unsigned(u as u64), + })))?; + } + arg_cb(InstructionPart::Basic("("))?; + arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque( + base.either_name(instruction.flags().abi(), display_flags.named_gpr()).into(), + ))))?; + arg_cb(InstructionPart::Basic(")"))?; + } + // ValuedOperand::r5900_immediate15(..) => match relocation { + // Some(resolved) + // if resolved.relocation.flags == RelocationFlags::Elf(R_MIPS15_S3) => + // { + // push_reloc(&resolved.relocation, &mut arg_cb, &mut plain_cb)?; + // } + // _ => { + // arg_cb(InstructionArg::Value(InstructionArgValue::Opaque( + // op.disassemble(&instruction, None).into(), + // )))?; + // } + // }, + _ => { + arg_cb(InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque( + op.display(instruction, display_flags, None::<&str>).to_string().into(), + ))))?; + } + } + } + Ok(()) +} + +fn push_reloc( + reloc: &Relocation, + mut arg_cb: impl FnMut(InstructionPart) -> Result<()>, +) -> Result<()> { match reloc.flags { - RelocationFlags::Elf { r_type } => match r_type { + RelocationFlags::Elf(r_type) => match r_type { elf::R_MIPS_HI16 => { - args.push(ObjInsArg::PlainText("%hi(".into())); - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".into())); + arg_cb(InstructionPart::Basic("%hi("))?; + arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; + arg_cb(InstructionPart::Basic(")"))?; } elf::R_MIPS_LO16 => { - args.push(ObjInsArg::PlainText("%lo(".into())); - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".into())); + arg_cb(InstructionPart::Basic("%lo("))?; + arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; + arg_cb(InstructionPart::Basic(")"))?; } elf::R_MIPS_GOT16 => { - args.push(ObjInsArg::PlainText("%got(".into())); - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".into())); + arg_cb(InstructionPart::Basic("%got("))?; + arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; + arg_cb(InstructionPart::Basic(")"))?; } elf::R_MIPS_CALL16 => { - args.push(ObjInsArg::PlainText("%call16(".into())); - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".into())); + arg_cb(InstructionPart::Basic("%call16("))?; + arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; + arg_cb(InstructionPart::Basic(")"))?; } elf::R_MIPS_GPREL16 => { - args.push(ObjInsArg::PlainText("%gp_rel(".into())); - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(")".into())); + arg_cb(InstructionPart::Basic("%gp_rel("))?; + arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; + arg_cb(InstructionPart::Basic(")"))?; } elf::R_MIPS_32 | elf::R_MIPS_26 | elf::R_MIPS_LITERAL | elf::R_MIPS_PC16 | R_MIPS15_S3 => { - args.push(ObjInsArg::Reloc); + arg_cb(InstructionPart::Arg(InstructionArg::Reloc))?; } _ => bail!("Unsupported ELF MIPS relocation type {r_type}"), }, diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index ba06dd4..a3eafc9 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -1,13 +1,16 @@ -use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, format, string::String, vec::Vec}; -use core::ffi::CStr; +use alloc::{borrow::Cow, boxed::Box, format, string::String, vec, vec::Vec}; +use core::{ffi::CStr, fmt, fmt::Debug, ops::Range}; use anyhow::{bail, Result}; use byteorder::ByteOrder; -use object::{Architecture, File, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol}; +use object::{File, Relocation, Section}; use crate::{ - diff::DiffObjConfig, - obj::{ObjIns, ObjReloc, ObjSection}, + diff::{display::InstructionPart, DiffObjConfig}, + obj::{ + InstructionRef, ParsedInstruction, RelocationFlags, ResolvedRelocation, ScannedInstruction, + SymbolFlagSet, SymbolKind, + }, util::ReallySigned, }; @@ -35,8 +38,8 @@ pub enum DataType { String, } -impl std::fmt::Display for DataType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for DataType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { DataType::Int8 => write!(f, "Int8"), DataType::Int16 => write!(f, "Int16"), @@ -154,23 +157,76 @@ impl DataType { } } -pub trait ObjArch: Send + Sync { - fn process_code( +pub trait Arch: Send + Sync + Debug { + /// Generate a list of instructions references (offset, size, opcode) from the given code. + /// + /// The opcode IDs are used to generate the initial diff. Implementations should do as little + /// parsing as possible here: just enough to identify the base instruction opcode, size, and + /// possible branch destination (for visual representation). As needed, instructions are parsed + /// via `process_instruction` to compare their arguments. + fn scan_instructions( &self, address: u64, code: &[u8], section_index: usize, - relocations: &[ObjReloc], - line_info: &BTreeMap, - config: &DiffObjConfig, - ) -> Result; + diff_config: &DiffObjConfig, + ) -> Result>; + + /// Parse an instruction to gather its mnemonic and arguments for more detailed comparison. + /// + /// This is called only when we need to compare the arguments of an instruction. + fn process_instruction( + &self, + ins_ref: InstructionRef, + code: &[u8], + relocation: Option, + function_range: Range, + section_index: usize, + diff_config: &DiffObjConfig, + ) -> Result { + let mut mnemonic = None; + let mut args = Vec::with_capacity(8); + self.display_instruction( + ins_ref, + code, + relocation, + function_range, + section_index, + diff_config, + &mut |part| { + match part { + InstructionPart::Opcode(m, _) => mnemonic = Some(m), + InstructionPart::Arg(arg) => args.push(arg), + _ => {} + } + Ok(()) + }, + )?; + Ok(ParsedInstruction { ins_ref, mnemonic: mnemonic.unwrap_or_default(), args }) + } + + /// Format an instruction for display. + /// + /// Implementations should call the callback for each part of the instruction: usually the + /// mnemonic and arguments, plus any separators and visual formatting. + fn display_instruction( + &self, + ins_ref: InstructionRef, + code: &[u8], + relocation: Option, + function_range: Range, + section_index: usize, + diff_config: &DiffObjConfig, + cb: &mut dyn FnMut(InstructionPart) -> Result<()>, + ) -> Result<()>; fn implcit_addend( &self, - file: &File<'_>, - section: &ObjSection, + file: &object::File<'_>, + section: &object::Section, address: u64, - reloc: &Relocation, + relocation: &object::Relocation, + flags: RelocationFlags, ) -> Result; fn demangle(&self, _name: &str) -> Option { None } @@ -179,9 +235,20 @@ pub trait ObjArch: Send + Sync { fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize; - fn symbol_address(&self, symbol: &Symbol) -> u64 { symbol.address() } + fn symbol_address(&self, address: u64, _kind: SymbolKind) -> u64 { address } - fn guess_data_type(&self, _instruction: &ObjIns) -> Option { None } + fn extra_symbol_flags(&self, _symbol: &object::Symbol) -> SymbolFlagSet { + SymbolFlagSet::default() + } + + fn guess_data_type( + &self, + _ins_ref: InstructionRef, + _code: &[u8], + _relocation: Option, + ) -> Option { + None + } fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec { vec![format!("Bytes: {:#x?}", bytes)] @@ -191,58 +258,113 @@ pub trait ObjArch: Send + Sync { vec![format!("{:#?}", bytes)] } - fn display_ins_data_labels(&self, ins: &ObjIns) -> Vec { - let Some(reloc) = ins.reloc.as_ref() else { - return Vec::new(); - }; - if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize { - return self - .guess_data_type(ins) - .map(|ty| { - self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..]) - }) - .unwrap_or_default(); - } + fn display_ins_data_labels( + &self, + _ins_ref: InstructionRef, + _code: &[u8], + _relocation: Option, + ) -> Vec { + // TODO + // let Some(reloc) = relocation else { + // return Vec::new(); + // }; + // if reloc.relocation.addend >= 0 && reloc.symbol.bytes.len() > reloc.relocation.addend as usize { + // return self + // .guess_data_type(ins) + // .map(|ty| { + // self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..]) + // }) + // .unwrap_or_default(); + // } Vec::new() } - fn display_ins_data_literals(&self, ins: &ObjIns) -> Vec { - let Some(reloc) = ins.reloc.as_ref() else { - return Vec::new(); - }; - if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize { - return self - .guess_data_type(ins) - .map(|ty| { - self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..]) - }) - .unwrap_or_default(); - } + fn display_ins_data_literals( + &self, + _ins_ref: InstructionRef, + _code: &[u8], + _relocation: Option, + ) -> Vec { + // TODO + // let Some(reloc) = ins.reloc.as_ref() else { + // return Vec::new(); + // }; + // if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize { + // return self + // .guess_data_type(ins) + // .map(|ty| { + // self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..]) + // }) + // .unwrap_or_default(); + // } Vec::new() } - - // Downcast methods - #[cfg(feature = "ppc")] - fn ppc(&self) -> Option<&ppc::ObjArchPpc> { None } } -pub struct ProcessCodeResult { - pub ops: Vec, - pub insts: Vec, -} - -pub fn new_arch(object: &File) -> Result> { +pub fn new_arch(object: &object::File) -> Result> { + use object::Object as _; Ok(match object.architecture() { #[cfg(feature = "ppc")] - Architecture::PowerPc => Box::new(ppc::ObjArchPpc::new(object)?), + object::Architecture::PowerPc => Box::new(ppc::ArchPpc::new(object)?), #[cfg(feature = "mips")] - Architecture::Mips => Box::new(mips::ObjArchMips::new(object)?), + object::Architecture::Mips => Box::new(mips::ArchMips::new(object)?), #[cfg(feature = "x86")] - Architecture::I386 | Architecture::X86_64 => Box::new(x86::ObjArchX86::new(object)?), + object::Architecture::I386 | object::Architecture::X86_64 => { + Box::new(x86::ArchX86::new(object)?) + } #[cfg(feature = "arm")] - Architecture::Arm => Box::new(arm::ObjArchArm::new(object)?), + object::Architecture::Arm => Box::new(arm::ArchArm::new(object)?), #[cfg(feature = "arm64")] - Architecture::Aarch64 => Box::new(arm64::ObjArchArm64::new(object)?), + object::Architecture::Aarch64 => Box::new(arm64::ArchArm64::new(object)?), arch => bail!("Unsupported architecture: {arch:?}"), }) } + +#[derive(Debug, Default)] +pub struct ArchDummy {} + +impl ArchDummy { + pub fn new() -> Box { Box::new(Self {}) } +} + +impl Arch for ArchDummy { + fn scan_instructions( + &self, + _address: u64, + _code: &[u8], + _section_index: usize, + _diff_config: &DiffObjConfig, + ) -> Result> { + Ok(Vec::new()) + } + + fn display_instruction( + &self, + _ins_ref: InstructionRef, + _code: &[u8], + _relocation: Option, + _function_range: Range, + _section_index: usize, + _diff_config: &DiffObjConfig, + _cb: &mut dyn FnMut(InstructionPart) -> Result<()>, + ) -> Result<()> { + Ok(()) + } + + fn implcit_addend( + &self, + _file: &File<'_>, + _section: &Section, + _address: u64, + _relocation: &Relocation, + _flags: RelocationFlags, + ) -> Result { + Ok(0) + } + + fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> { + format!("{flags:?}").into() + } + + fn get_reloc_byte_size(&self, _flags: RelocationFlags) -> usize { 0 } +} diff --git a/objdiff-core/src/arch/ppc.rs b/objdiff-core/src/arch/ppc.rs index c1d37e0..306f6e5 100644 --- a/objdiff-core/src/arch/ppc.rs +++ b/objdiff-core/src/arch/ppc.rs @@ -1,187 +1,189 @@ use alloc::{ borrow::Cow, - collections::{BTreeMap, BTreeSet}, + collections::BTreeMap, format, string::{String, ToString}, vec, vec::Vec, }; +use core::ops::Range; use anyhow::{bail, ensure, Result}; use byteorder::BigEndian; use cwextab::{decode_extab, ExceptionTableData}; -use object::{ - elf, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, RelocationTarget, - Symbol, SymbolKind, -}; -use ppc750cl::{Argument, Arguments, Ins, InsIter, Opcode, ParsedIns, GPR}; +use flagset::Flags; +use object::{elf, Object as _, ObjectSection as _, ObjectSymbol as _}; use crate::{ - arch::{DataType, ObjArch, ProcessCodeResult}, - diff::DiffObjConfig, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection, ObjSymbol}, + arch::{Arch, DataType}, + diff::{display::InstructionPart, DiffObjConfig}, + obj::{ + InstructionArg, InstructionArgValue, InstructionRef, Relocation, RelocationFlags, + ResolvedRelocation, ScannedInstruction, Symbol, SymbolFlag, SymbolFlagSet, + }, }; // Relative relocation, can be Simm, Offset or BranchDest -fn is_relative_arg(arg: &Argument) -> bool { - matches!(arg, Argument::Simm(_) | Argument::Offset(_) | Argument::BranchDest(_)) +fn is_relative_arg(arg: &ppc750cl::Argument) -> bool { + matches!( + arg, + ppc750cl::Argument::Simm(_) + | ppc750cl::Argument::Offset(_) + | ppc750cl::Argument::BranchDest(_) + ) } // Relative or absolute relocation, can be Uimm, Simm or Offset -fn is_rel_abs_arg(arg: &Argument) -> bool { - matches!(arg, Argument::Uimm(_) | Argument::Simm(_) | Argument::Offset(_)) +fn is_rel_abs_arg(arg: &ppc750cl::Argument) -> bool { + matches!( + arg, + ppc750cl::Argument::Uimm(_) | ppc750cl::Argument::Simm(_) | ppc750cl::Argument::Offset(_) + ) } -fn is_offset_arg(arg: &Argument) -> bool { matches!(arg, Argument::Offset(_)) } +fn is_offset_arg(arg: &ppc750cl::Argument) -> bool { matches!(arg, ppc750cl::Argument::Offset(_)) } -pub struct ObjArchPpc { +#[derive(Debug)] +pub struct ArchPpc { /// Exception info pub extab: Option>, } -impl ObjArchPpc { - pub fn new(file: &File) -> Result { Ok(Self { extab: decode_exception_info(file)? }) } +impl ArchPpc { + pub fn new(file: &object::File) -> Result { + Ok(Self { extab: decode_exception_info(file)? }) + } + + fn find_reloc_arg( + &self, + ins: &ppc750cl::ParsedIns, + resolved: Option, + ) -> Option { + match resolved?.relocation.flags { + RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => Some(1), + RelocationFlags::Elf(elf::R_PPC_REL24 | elf::R_PPC_REL14) => { + ins.args.iter().rposition(is_relative_arg) + } + RelocationFlags::Elf( + elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO, + ) => ins.args.iter().rposition(is_rel_abs_arg), + _ => None, + } + } } -impl ObjArch for ObjArchPpc { - fn process_code( +impl Arch for ArchPpc { + fn scan_instructions( &self, address: u64, code: &[u8], _section_index: usize, - relocations: &[ObjReloc], - line_info: &BTreeMap, - config: &DiffObjConfig, - ) -> Result { + _diff_config: &DiffObjConfig, + ) -> Result> { + ensure!(code.len() & 3 == 0, "Code length must be a multiple of 4"); let ins_count = code.len() / 4; - let mut ops = Vec::::with_capacity(ins_count); - let mut insts = Vec::::with_capacity(ins_count); - let fake_pool_reloc_for_addr = - generate_fake_pool_reloc_for_addr_mapping(address, code, relocations); - for (cur_addr, mut ins) in InsIter::new(code, address as u32) { - let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr); - if let Some(reloc) = reloc { - // Zero out relocations - ins.code = match reloc.flags { - RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => ins.code & !0x1FFFFF, - RelocationFlags::Elf { r_type: elf::R_PPC_REL24 } => ins.code & !0x3FFFFFC, - RelocationFlags::Elf { r_type: elf::R_PPC_REL14 } => ins.code & !0xFFFC, - RelocationFlags::Elf { - r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO, - } => ins.code & !0xFFFF, - _ => ins.code, + let mut insts = Vec::::with_capacity(ins_count); + for (cur_addr, ins) in ppc750cl::InsIter::new(code, address as u32) { + insts.push(ScannedInstruction { + ins_ref: InstructionRef { + address: cur_addr as u64, + size: 4, + opcode: u8::from(ins.op) as u16, + }, + branch_dest: ins.branch_dest(cur_addr).map(u64::from), + }); + } + Ok(insts) + } + + fn display_instruction( + &self, + ins_ref: InstructionRef, + code: &[u8], + relocation: Option, + _function_range: Range, + _section_index: usize, + _diff_config: &DiffObjConfig, + cb: &mut dyn FnMut(InstructionPart) -> Result<()>, + ) -> Result<()> { + let mut code = u32::from_be_bytes(code.try_into()?); + if let Some(resolved) = relocation { + code = zero_reloc(code, resolved.relocation); + } + let op = ppc750cl::Opcode::from(ins_ref.opcode as u8); + let ins = ppc750cl::Ins { code, op }.simplified(); + + cb(InstructionPart::Opcode(Cow::Borrowed(ins.mnemonic), ins_ref.opcode))?; + + let reloc_arg = self.find_reloc_arg(&ins, relocation); + + let mut writing_offset = false; + for (idx, arg) in ins.args_iter().enumerate() { + if idx > 0 && !writing_offset { + cb(InstructionPart::Separator)?; + } + + if reloc_arg == Some(idx) { + let resolved = relocation.unwrap(); + display_reloc(resolved, cb)?; + // For @sda21, we can omit the register argument + if matches!(resolved.relocation.flags, RelocationFlags::Elf(elf::R_PPC_EMB_SDA21)) + // Sanity check: the next argument should be r0 + && matches!(ins.args.get(idx + 1), Some(ppc750cl::Argument::GPR(ppc750cl::GPR(0)))) + { + break; + } + } else { + match arg { + ppc750cl::Argument::Simm(simm) => { + cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Signed(simm.0 as i64), + )))?; + } + ppc750cl::Argument::Uimm(uimm) => { + cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Unsigned(uimm.0 as u64), + )))?; + } + ppc750cl::Argument::Offset(offset) => { + cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Signed(offset.0 as i64), + )))?; + } + ppc750cl::Argument::BranchDest(dest) => { + let dest = (ins_ref.address as u32).wrapping_add_signed(dest.0) as u64; + cb(InstructionPart::Arg(InstructionArg::BranchDest(dest)))?; + } + _ => { + cb(InstructionPart::Arg(InstructionArg::Value( + InstructionArgValue::Opaque(arg.to_string().into()), + )))?; + } }; } - let orig = ins.basic().to_string(); - let simplified = ins.simplified(); - let formatted = simplified.to_string(); - - let mut reloc_arg = None; - if let Some(reloc) = reloc { - match reloc.flags { - RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 } => { - reloc_arg = Some(1); - } - RelocationFlags::Elf { r_type: elf::R_PPC_REL24 | elf::R_PPC_REL14 } => { - reloc_arg = simplified.args.iter().rposition(is_relative_arg); - } - RelocationFlags::Elf { - r_type: elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO, - } => { - reloc_arg = simplified.args.iter().rposition(is_rel_abs_arg); - } - _ => {} - } + if writing_offset { + cb(InstructionPart::Basic(")"))?; + writing_offset = false; } - - let mut args = vec![]; - let mut branch_dest = None; - let mut writing_offset = false; - for (idx, arg) in simplified.args_iter().enumerate() { - if idx > 0 && !writing_offset { - args.push(ObjInsArg::PlainText(config.separator().into())); - } - - if reloc_arg == Some(idx) { - let reloc = reloc.unwrap(); - push_reloc(&mut args, reloc)?; - // For @sda21, we can omit the register argument - if matches!(reloc.flags, RelocationFlags::Elf { r_type: elf::R_PPC_EMB_SDA21 }) - // Sanity check: the next argument should be r0 - && matches!(simplified.args.get(idx + 1), Some(Argument::GPR(GPR(0)))) - { - break; - } - } else { - match arg { - Argument::Simm(simm) => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(simm.0 as i64))); - } - Argument::Uimm(uimm) => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(uimm.0 as u64))); - } - Argument::Offset(offset) => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(offset.0 as i64))); - } - Argument::BranchDest(dest) => { - let dest = cur_addr.wrapping_add_signed(dest.0) as u64; - args.push(ObjInsArg::BranchDest(dest)); - branch_dest = Some(dest); - } - _ => { - args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque( - arg.to_string().into(), - ))); - } - }; - } - - if writing_offset { - args.push(ObjInsArg::PlainText(")".into())); - writing_offset = false; - } - if is_offset_arg(arg) { - args.push(ObjInsArg::PlainText("(".into())); - writing_offset = true; - } + if is_offset_arg(arg) { + cb(InstructionPart::Basic("("))?; + writing_offset = true; } - - if reloc.is_none() { - if let Some(fake_pool_reloc) = fake_pool_reloc_for_addr.get(&cur_addr) { - // If this instruction has a fake pool relocation, show it as a fake argument - // at the end of the line. - args.push(ObjInsArg::PlainText(" ".into())); - push_reloc(&mut args, fake_pool_reloc)?; - } - } - - ops.push(ins.op as u16); - let line = line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b); - insts.push(ObjIns { - address: cur_addr as u64, - size: 4, - mnemonic: Cow::Borrowed(simplified.mnemonic), - args, - reloc: reloc.or(fake_pool_reloc_for_addr.get(&cur_addr)).cloned(), - op: ins.op as u16, - branch_dest, - line, - formatted, - orig: Some(orig), - }); } - Ok(ProcessCodeResult { ops, insts }) + + Ok(()) } fn implcit_addend( &self, - _file: &File<'_>, - _section: &ObjSection, + _file: &object::File<'_>, + _section: &object::Section, address: u64, - reloc: &Relocation, + _relocation: &object::Relocation, + flags: RelocationFlags, ) -> Result { - bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, reloc.flags()) + bail!("Unsupported PPC implicit relocation {:#x}:{:?}", address, flags) } fn demangle(&self, name: &str) -> Option { @@ -190,7 +192,7 @@ impl ObjArch for ObjArchPpc { fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> { match flags { - RelocationFlags::Elf { r_type } => match r_type { + RelocationFlags::Elf(r_type) => match r_type { elf::R_PPC_NONE => Cow::Borrowed("R_PPC_NONE"), // We use this for fake pool relocs elf::R_PPC_ADDR16_LO => Cow::Borrowed("R_PPC_ADDR16_LO"), elf::R_PPC_ADDR16_HI => Cow::Borrowed("R_PPC_ADDR16_HI"), @@ -206,9 +208,17 @@ impl ObjArch for ObjArchPpc { } } + fn extra_symbol_flags(&self, symbol: &object::Symbol) -> SymbolFlagSet { + if self.extab.as_ref().is_some_and(|extab| extab.contains_key(&symbol.index().0)) { + SymbolFlag::HasExtra.into() + } else { + SymbolFlag::none() + } + } + fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { match flags { - RelocationFlags::Elf { r_type } => match r_type { + RelocationFlags::Elf(r_type) => match r_type { elf::R_PPC_ADDR32 => 4, elf::R_PPC_UADDR32 => 4, _ => 1, @@ -217,12 +227,17 @@ impl ObjArch for ObjArchPpc { } } - fn guess_data_type(&self, instruction: &ObjIns) -> Option { - if instruction.reloc.as_ref().is_some_and(|r| r.target.name.starts_with("@stringBase")) { + fn guess_data_type( + &self, + ins_ref: InstructionRef, + _code: &[u8], + relocation: Option, + ) -> Option { + if relocation.is_some_and(|r| r.symbol.name.starts_with("@stringBase")) { return Some(DataType::String); } - guess_data_type_from_load_store_inst_op(Opcode::from(instruction.op as u8)) + guess_data_type_from_load_store_inst_op(ppc750cl::Opcode::from(ins_ref.opcode as u8)) } fn display_data_labels(&self, ty: DataType, bytes: &[u8]) -> Vec { @@ -232,47 +247,62 @@ impl ObjArch for ObjArchPpc { fn display_data_literals(&self, ty: DataType, bytes: &[u8]) -> Vec { ty.display_literals::(bytes) } - - fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) } } -impl ObjArchPpc { - pub fn extab_for_symbol(&self, symbol: &ObjSymbol) -> Option<&ExceptionInfo> { - symbol.original_index.and_then(|i| self.extab.as_ref()?.get(&i)) +impl ArchPpc { + pub fn extab_for_symbol(&self, _symbol: &Symbol) -> Option<&ExceptionInfo> { + // TODO + // symbol.original_index.and_then(|i| self.extab.as_ref()?.get(&i)) + None } } -fn push_reloc(args: &mut Vec, reloc: &ObjReloc) -> Result<()> { +fn zero_reloc(code: u32, reloc: &Relocation) -> u32 { match reloc.flags { - RelocationFlags::Elf { r_type } => match r_type { + RelocationFlags::Elf(elf::R_PPC_EMB_SDA21) => code & !0x1FFFFF, + RelocationFlags::Elf(elf::R_PPC_REL24) => code & !0x3FFFFFC, + RelocationFlags::Elf(elf::R_PPC_REL14) => code & !0xFFFC, + RelocationFlags::Elf( + elf::R_PPC_ADDR16_HI | elf::R_PPC_ADDR16_HA | elf::R_PPC_ADDR16_LO, + ) => code & !0xFFFF, + _ => code, + } +} + +fn display_reloc( + resolved: ResolvedRelocation, + cb: &mut dyn FnMut(InstructionPart) -> Result<()>, +) -> Result<()> { + match resolved.relocation.flags { + RelocationFlags::Elf(r_type) => match r_type { elf::R_PPC_ADDR16_LO => { - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText("@l".into())); + cb(InstructionPart::Arg(InstructionArg::Reloc))?; + cb(InstructionPart::Basic("@l"))?; } elf::R_PPC_ADDR16_HI => { - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText("@h".into())); + cb(InstructionPart::Arg(InstructionArg::Reloc))?; + cb(InstructionPart::Basic("@h"))?; } elf::R_PPC_ADDR16_HA => { - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText("@ha".into())); + cb(InstructionPart::Arg(InstructionArg::Reloc))?; + cb(InstructionPart::Basic("@ha"))?; } elf::R_PPC_EMB_SDA21 => { - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText("@sda21".into())); + cb(InstructionPart::Arg(InstructionArg::Reloc))?; + cb(InstructionPart::Basic("@sda21"))?; } elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 | elf::R_PPC_REL24 | elf::R_PPC_REL14 => { - args.push(ObjInsArg::Reloc); + cb(InstructionPart::Arg(InstructionArg::Reloc))?; } elf::R_PPC_NONE => { // Fake pool relocation. - args.push(ObjInsArg::PlainText("<".into())); - args.push(ObjInsArg::Reloc); - args.push(ObjInsArg::PlainText(">".into())); + cb(InstructionPart::Basic("<"))?; + cb(InstructionPart::Arg(InstructionArg::Reloc))?; + cb(InstructionPart::Basic(">"))?; } - _ => bail!("Unsupported ELF PPC relocation type {r_type}"), + _ => cb(InstructionPart::Arg(InstructionArg::Reloc))?, }, - flags => bail!("Unsupported PPC relocation kind: {flags:?}"), + _ => cb(InstructionPart::Arg(InstructionArg::Reloc))?, }; Ok(()) } @@ -292,7 +322,9 @@ pub struct ExceptionInfo { pub dtors: Vec, } -fn decode_exception_info(file: &File<'_>) -> Result>> { +fn decode_exception_info( + file: &object::File<'_>, +) -> Result>> { let Some(extab_section) = file.section_by_name("extab") else { return Ok(None); }; @@ -301,13 +333,14 @@ fn decode_exception_info(file: &File<'_>) -> Result>(); + let extab_relocations = + extab_section.relocations().collect::>(); let extabindex_relocations = - extabindex_section.relocations().collect::>(); + extabindex_section.relocations().collect::>(); for extabindex in file.symbols().filter(|symbol| { symbol.section_index() == Some(extabindex_section.index()) - && symbol.kind() == SymbolKind::Data + && symbol.kind() == object::SymbolKind::Data }) { if extabindex.size() != 12 { log::warn!("Invalid extabindex entry size {}", extabindex.size()); @@ -380,16 +413,16 @@ fn decode_exception_info(file: &File<'_>) -> Result( - file: &'file File<'data>, - relocation: &Relocation, -) -> Result>> { + file: &'file object::File<'data>, + relocation: &object::Relocation, +) -> Result>> { let addend = relocation.addend(); match relocation.target() { - RelocationTarget::Symbol(idx) => { + object::RelocationTarget::Symbol(idx) => { ensure!(addend == 0, "Symbol relocations must have zero addend"); Ok(Some(file.symbol_by_index(idx)?)) } - RelocationTarget::Section(idx) => { + object::RelocationTarget::Section(idx) => { ensure!(addend >= 0, "Section relocations must have non-negative addend"); let addend = addend as u64; Ok(file @@ -400,13 +433,14 @@ fn relocation_symbol<'data, 'file>( } } -fn make_symbol_ref(symbol: &Symbol) -> Result { +fn make_symbol_ref(symbol: &object::Symbol) -> Result { let name = symbol.name()?.to_string(); let demangled_name = cwdemangle::demangle(&name, &cwdemangle::DemangleOptions::default()); Ok(ExtabSymbolRef { original_index: symbol.index().0, name, demangled_name }) } -fn guess_data_type_from_load_store_inst_op(inst_op: Opcode) -> Option { +fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option { + use ppc750cl::Opcode; match inst_op { Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8), Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16), @@ -427,10 +461,12 @@ fn guess_data_type_from_load_store_inst_op(inst_op: Opcode) -> Option // Given an instruction, determine if it could accessing data at the address in a register. // If so, return the offset added to the register's address, the register containing that address, // and (optionally) which destination register the address is being copied into. +#[expect(unused)] fn get_offset_and_addr_gpr_for_possible_pool_reference( - opcode: Opcode, - simplified: &ParsedIns, -) -> Option<(i16, GPR, Option)> { + opcode: ppc750cl::Opcode, + simplified: &ppc750cl::ParsedIns, +) -> Option<(i16, ppc750cl::GPR, Option)> { + use ppc750cl::{Argument, Opcode}; let args = &simplified.args; if guess_data_type_from_load_store_inst_op(opcode).is_some() { match (args[1], args[2]) { @@ -483,7 +519,9 @@ fn get_offset_and_addr_gpr_for_possible_pool_reference( // Remove the relocation we're keeping track of in a particular register when an instruction reuses // that register to hold some other value, unrelated to pool relocation addresses. -fn clear_overwritten_gprs(ins: Ins, gpr_pool_relocs: &mut BTreeMap) { +#[expect(unused)] +fn clear_overwritten_gprs(ins: ppc750cl::Ins, gpr_pool_relocs: &mut BTreeMap) { + use ppc750cl::{Argument, Arguments, Opcode}; let mut def_args = Arguments::default(); ins.parse_defs(&mut def_args); for arg in def_args { @@ -501,249 +539,250 @@ fn clear_overwritten_gprs(ins: Ins, gpr_pool_relocs: &mut BTreeMap } } -// We create a fake relocation for an instruction, vaguely simulating what the actual relocation -// might have looked like if it wasn't pooled. This is so minimal changes are needed to display -// pooled accesses vs non-pooled accesses. We set the relocation type to R_PPC_NONE to indicate that -// there isn't really a relocation here, as copying the pool relocation's type wouldn't make sense. -// Also, if this instruction is accessing the middle of a symbol instead of the start, we add an -// addend to indicate that. -fn make_fake_pool_reloc(offset: i16, cur_addr: u32, pool_reloc: &ObjReloc) -> Option { - let offset_from_pool = pool_reloc.addend + offset as i64; - let target_address = pool_reloc.target.address.checked_add_signed(offset_from_pool)?; - let target; - let addend; - if pool_reloc.target.orig_section_index.is_some() { - // If the target symbol is within this current object, then we also need to create a fake - // target symbol to go inside our fake relocation. This is because we don't have access to - // list of all symbols in this section, so we can't find the real symbol within the pool - // based on its address yet. Instead we make a placeholder that has the correct - // `orig_section_index` and `address` fields, and then later on when this information is - // displayed to the user, we can find the real symbol by searching through the object's - // section's symbols for one that contains this address. - target = ObjSymbol { - name: "".to_string(), - demangled_name: None, - address: target_address, - section_address: 0, - size: 0, - size_known: false, - kind: Default::default(), - flags: Default::default(), - orig_section_index: pool_reloc.target.orig_section_index, - virtual_address: None, - original_index: None, - bytes: vec![], - }; - // The addend is also fake because we don't know yet if the `target_address` here is the exact - // start of the symbol or if it's in the middle of it. - addend = 0; - } else { - // But if the target symbol is in a different object (extern), then we simply copy the pool - // relocation's target. This is because it won't be possible to locate the actual symbol - // later on based only off of an offset without knowing the object or section it's in. And - // doing that for external symbols would also be unnecessary, because when the compiler - // generates an instruction that accesses an external "pool" plus some offset, that won't be - // a normal pool that contains other symbols within it that we want to display. It will be - // something like a vtable for a class with multiple inheritance (for example, dCcD_Cyl in - // The Wind Waker). So just showing that vtable symbol plus an addend to represent the - // offset into it works fine in this case, no fake symbol to hold an address is necessary. - target = pool_reloc.target.clone(); - addend = pool_reloc.addend; - }; - Some(ObjReloc { - flags: RelocationFlags::Elf { r_type: elf::R_PPC_NONE }, - address: cur_addr as u64, - target, - addend, - }) -} - -// Searches through all instructions in a function, determining which registers have the addresses -// of pooled data relocations in them, finding which instructions load data from those addresses, -// and constructing a mapping of the address of that instruction to a "fake pool relocation" that -// simulates what that instruction's relocation would look like if data hadn't been pooled. -// This method tries to follow the function's proper control flow. It keeps track of a queue of -// states it hasn't traversed yet, where each state holds an instruction address and a HashMap of -// which registers hold which pool relocations at that point. -// When a conditional or unconditional branch is encountered, the destination of the branch is added -// to the queue. Conditional branches will traverse both the path where the branch is taken and the -// one where it's not. Unconditional branches only follow the branch, ignoring any code immediately -// after the branch instruction. -// Limitations: This method cannot read jump tables. This is because the jump tables are located in -// the .data section, but ObjArch.process_code only has access to the .text section. In order to -// work around this limitation and avoid completely missing most code inside switch statements that -// use jump tables, we instead guess that any parts of a function we missed were switch cases, and -// traverse them as if the last `bctr` before that address had branched there. This should be fairly -// accurate in practice - in testing the only instructions it seems to miss are double branches that -// the compiler generates in error which can never be reached during normal execution anyway. -fn generate_fake_pool_reloc_for_addr_mapping( - func_address: u64, - code: &[u8], - relocations: &[ObjReloc], -) -> BTreeMap { - let mut visited_ins_addrs = BTreeSet::new(); - let mut pool_reloc_for_addr = BTreeMap::new(); - let mut ins_iters_with_gpr_state = - vec![(InsIter::new(code, func_address as u32), BTreeMap::new())]; - let mut gpr_state_at_bctr = BTreeMap::new(); - while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() { - for (cur_addr, ins) in ins_iter { - if visited_ins_addrs.contains(&cur_addr) { - // Avoid getting stuck in an infinite loop when following looping branches. - break; - } - visited_ins_addrs.insert(cur_addr); - - let simplified = ins.simplified(); - - // First handle traversing the function's control flow. - let mut branch_dest = None; - for arg in simplified.args_iter() { - if let Argument::BranchDest(dest) = arg { - let dest = cur_addr.wrapping_add_signed(dest.0); - branch_dest = Some(dest); - break; - } - } - if let Some(branch_dest) = branch_dest { - if branch_dest >= func_address as u32 - && (branch_dest - func_address as u32) < code.len() as u32 - { - let dest_offset_into_func = branch_dest - func_address as u32; - let dest_code_slice = &code[dest_offset_into_func as usize..]; - match ins.op { - Opcode::Bc => { - // Conditional branch. - // Add the branch destination to the queue to do later. - ins_iters_with_gpr_state.push(( - InsIter::new(dest_code_slice, branch_dest), - gpr_pool_relocs.clone(), - )); - // Then continue on with the current iterator. - } - Opcode::B => { - if simplified.mnemonic != "bl" { - // Unconditional branch. - // Add the branch destination to the queue. - ins_iters_with_gpr_state.push(( - InsIter::new(dest_code_slice, branch_dest), - gpr_pool_relocs.clone(), - )); - // Break out of the current iterator so we can do the newly added one. - break; - } - } - _ => unreachable!(), - } - } - } - if let Opcode::Bcctr = ins.op { - if simplified.mnemonic == "bctr" { - // Unconditional branch to count register. - // Likely a jump table. - gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone()); - } - } - - // Then handle keeping track of which GPR contains which pool relocation. - let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr); - if let Some(reloc) = reloc { - // This instruction has a real relocation, so it may be a pool load we want to keep - // track of. - let args = &simplified.args; - match (ins.op, args[0], args[1], args[2]) { - ( - // `lis` + `addi` - Opcode::Addi, - Argument::GPR(addr_dst_gpr), - Argument::GPR(_addr_src_gpr), - Argument::Simm(_simm), - ) => { - gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); - } - ( - // `lis` + `ori` - Opcode::Ori, - Argument::GPR(addr_dst_gpr), - Argument::GPR(_addr_src_gpr), - Argument::Uimm(_uimm), - ) => { - gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); - } - (Opcode::B, _, _, _) => { - if simplified.mnemonic == "bl" { - // When encountering a function call, clear any active pool relocations from - // the volatile registers (r0, r3-r12), but not the nonvolatile registers. - gpr_pool_relocs.remove(&0); - for gpr in 3..12 { - gpr_pool_relocs.remove(&gpr); - } - } - } - _ => { - clear_overwritten_gprs(ins, &mut gpr_pool_relocs); - } - } - } else if let Some((offset, addr_src_gpr, addr_dst_gpr)) = - get_offset_and_addr_gpr_for_possible_pool_reference(ins.op, &simplified) - { - // This instruction doesn't have a real relocation, so it may be a reference to one of - // the already-loaded pools. - if let Some(pool_reloc) = gpr_pool_relocs.get(&addr_src_gpr.0) { - if let Some(fake_pool_reloc) = - make_fake_pool_reloc(offset, cur_addr, pool_reloc) - { - pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc); - } - if let Some(addr_dst_gpr) = addr_dst_gpr { - // If the address of the pool relocation got copied into another register, we - // need to keep track of it in that register too as future instructions may - // reference the symbol indirectly via this new register, instead of the - // register the symbol's address was originally loaded into. - // For example, the start of the function might `lis` + `addi` the start of the - // ...data pool into r25, and then later the start of a loop will `addi` r25 - // with the offset within the .data section of an array variable into r21. - // Then the body of the loop will `lwzx` one of the array elements from r21. - let mut new_reloc = pool_reloc.clone(); - new_reloc.addend += offset as i64; - gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc); - } else { - clear_overwritten_gprs(ins, &mut gpr_pool_relocs); - } - } else { - clear_overwritten_gprs(ins, &mut gpr_pool_relocs); - } - } else { - clear_overwritten_gprs(ins, &mut gpr_pool_relocs); - } - } - - // Finally, if we're about to finish the outer loop and don't have any more control flow to - // follow, we check if there are any instruction addresses in this function that we missed. - // If so, and if there were any `bctr` instructions before those points in this function, - // then we try to traverse those missing spots as switch cases. - if ins_iters_with_gpr_state.is_empty() { - let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32) - .step_by(4) - .filter(|addr| !visited_ins_addrs.contains(addr)); - for unseen_addr in unseen_addrs { - let prev_bctr_gpr_state = gpr_state_at_bctr - .iter() - .filter(|(&addr, _)| addr < unseen_addr) - .min_by_key(|(&addr, _)| addr) - .map(|(_, gpr_state)| gpr_state); - if let Some(gpr_pool_relocs) = prev_bctr_gpr_state { - let dest_offset_into_func = unseen_addr - func_address as u32; - let dest_code_slice = &code[dest_offset_into_func as usize..]; - ins_iters_with_gpr_state.push(( - InsIter::new(dest_code_slice, unseen_addr), - gpr_pool_relocs.clone(), - )); - break; - } - } - } - } - - pool_reloc_for_addr -} +// TODO +// // We create a fake relocation for an instruction, vaguely simulating what the actual relocation +// // might have looked like if it wasn't pooled. This is so minimal changes are needed to display +// // pooled accesses vs non-pooled accesses. We set the relocation type to R_PPC_NONE to indicate that +// // there isn't really a relocation here, as copying the pool relocation's type wouldn't make sense. +// // Also, if this instruction is accessing the middle of a symbol instead of the start, we add an +// // addend to indicate that. +// fn make_fake_pool_reloc(offset: i16, cur_addr: u32, pool_reloc: &Relocation) -> Option { +// let offset_from_pool = pool_reloc.addend + offset as i64; +// let target_address = pool_reloc.sy.address.checked_add_signed(offset_from_pool)?; +// let target; +// let addend; +// if pool_reloc.target.orig_section_index.is_some() { +// // If the target symbol is within this current object, then we also need to create a fake +// // target symbol to go inside our fake relocation. This is because we don't have access to +// // list of all symbols in this section, so we can't find the real symbol within the pool +// // based on its address yet. Instead we make a placeholder that has the correct +// // `orig_section_index` and `address` fields, and then later on when this information is +// // displayed to the user, we can find the real symbol by searching through the object's +// // section's symbols for one that contains this address. +// target = ObjSymbol { +// name: "".to_string(), +// demangled_name: None, +// address: target_address, +// section_address: 0, +// size: 0, +// size_known: false, +// kind: Default::default(), +// flags: Default::default(), +// orig_section_index: pool_reloc.target.orig_section_index, +// virtual_address: None, +// original_index: None, +// bytes: vec![], +// }; +// // The addend is also fake because we don't know yet if the `target_address` here is the exact +// // start of the symbol or if it's in the middle of it. +// addend = 0; +// } else { +// // But if the target symbol is in a different object (extern), then we simply copy the pool +// // relocation's target. This is because it won't be possible to locate the actual symbol +// // later on based only off of an offset without knowing the object or section it's in. And +// // doing that for external symbols would also be unnecessary, because when the compiler +// // generates an instruction that accesses an external "pool" plus some offset, that won't be +// // a normal pool that contains other symbols within it that we want to display. It will be +// // something like a vtable for a class with multiple inheritance (for example, dCcD_Cyl in +// // The Wind Waker). So just showing that vtable symbol plus an addend to represent the +// // offset into it works fine in this case, no fake symbol to hold an address is necessary. +// target = pool_reloc.target.clone(); +// addend = pool_reloc.addend; +// }; +// Some(ObjReloc { +// flags: RelocationFlags::Elf { r_type: elf::R_PPC_NONE }, +// address: cur_addr as u64, +// target, +// addend, +// }) +// } +// +// // Searches through all instructions in a function, determining which registers have the addresses +// // of pooled data relocations in them, finding which instructions load data from those addresses, +// // and constructing a mapping of the address of that instruction to a "fake pool relocation" that +// // simulates what that instruction's relocation would look like if data hadn't been pooled. +// // This method tries to follow the function's proper control flow. It keeps track of a queue of +// // states it hasn't traversed yet, where each state holds an instruction address and a HashMap of +// // which registers hold which pool relocations at that point. +// // When a conditional or unconditional branch is encountered, the destination of the branch is added +// // to the queue. Conditional branches will traverse both the path where the branch is taken and the +// // one where it's not. Unconditional branches only follow the branch, ignoring any code immediately +// // after the branch instruction. +// // Limitations: This method cannot read jump tables. This is because the jump tables are located in +// // the .data section, but ObjArch.process_code only has access to the .text section. In order to +// // work around this limitation and avoid completely missing most code inside switch statements that +// // use jump tables, we instead guess that any parts of a function we missed were switch cases, and +// // traverse them as if the last `bctr` before that address had branched there. This should be fairly +// // accurate in practice - in testing the only instructions it seems to miss are double branches that +// // the compiler generates in error which can never be reached during normal execution anyway. +// fn generate_fake_pool_reloc_for_addr_mapping( +// func_address: u64, +// code: &[u8], +// relocations: &[ObjReloc], +// ) -> BTreeMap { +// let mut visited_ins_addrs = BTreeSet::new(); +// let mut pool_reloc_for_addr = BTreeMap::new(); +// let mut ins_iters_with_gpr_state = +// vec![(InsIter::new(code, func_address as u32), BTreeMap::new())]; +// let mut gpr_state_at_bctr = BTreeMap::new(); +// while let Some((ins_iter, mut gpr_pool_relocs)) = ins_iters_with_gpr_state.pop() { +// for (cur_addr, ins) in ins_iter { +// if visited_ins_addrs.contains(&cur_addr) { +// // Avoid getting stuck in an infinite loop when following looping branches. +// break; +// } +// visited_ins_addrs.insert(cur_addr); +// +// let simplified = ins.simplified(); +// +// // First handle traversing the function's control flow. +// let mut branch_dest = None; +// for arg in simplified.args_iter() { +// if let Argument::BranchDest(dest) = arg { +// let dest = cur_addr.wrapping_add_signed(dest.0); +// branch_dest = Some(dest); +// break; +// } +// } +// if let Some(branch_dest) = branch_dest { +// if branch_dest >= func_address as u32 +// && (branch_dest - func_address as u32) < code.len() as u32 +// { +// let dest_offset_into_func = branch_dest - func_address as u32; +// let dest_code_slice = &code[dest_offset_into_func as usize..]; +// match ins.op { +// Opcode::Bc => { +// // Conditional branch. +// // Add the branch destination to the queue to do later. +// ins_iters_with_gpr_state.push(( +// InsIter::new(dest_code_slice, branch_dest), +// gpr_pool_relocs.clone(), +// )); +// // Then continue on with the current iterator. +// } +// Opcode::B => { +// if simplified.mnemonic != "bl" { +// // Unconditional branch. +// // Add the branch destination to the queue. +// ins_iters_with_gpr_state.push(( +// InsIter::new(dest_code_slice, branch_dest), +// gpr_pool_relocs.clone(), +// )); +// // Break out of the current iterator so we can do the newly added one. +// break; +// } +// } +// _ => unreachable!(), +// } +// } +// } +// if let Opcode::Bcctr = ins.op { +// if simplified.mnemonic == "bctr" { +// // Unconditional branch to count register. +// // Likely a jump table. +// gpr_state_at_bctr.insert(cur_addr, gpr_pool_relocs.clone()); +// } +// } +// +// // Then handle keeping track of which GPR contains which pool relocation. +// let reloc = relocations.iter().find(|r| (r.address as u32 & !3) == cur_addr); +// if let Some(reloc) = reloc { +// // This instruction has a real relocation, so it may be a pool load we want to keep +// // track of. +// let args = &simplified.args; +// match (ins.op, args[0], args[1], args[2]) { +// ( +// // `lis` + `addi` +// Opcode::Addi, +// Argument::GPR(addr_dst_gpr), +// Argument::GPR(_addr_src_gpr), +// Argument::Simm(_simm), +// ) => { +// gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); +// } +// ( +// // `lis` + `ori` +// Opcode::Ori, +// Argument::GPR(addr_dst_gpr), +// Argument::GPR(_addr_src_gpr), +// Argument::Uimm(_uimm), +// ) => { +// gpr_pool_relocs.insert(addr_dst_gpr.0, reloc.clone()); +// } +// (Opcode::B, _, _, _) => { +// if simplified.mnemonic == "bl" { +// // When encountering a function call, clear any active pool relocations from +// // the volatile registers (r0, r3-r12), but not the nonvolatile registers. +// gpr_pool_relocs.remove(&0); +// for gpr in 3..12 { +// gpr_pool_relocs.remove(&gpr); +// } +// } +// } +// _ => { +// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); +// } +// } +// } else if let Some((offset, addr_src_gpr, addr_dst_gpr)) = +// get_offset_and_addr_gpr_for_possible_pool_reference(ins.op, &simplified) +// { +// // This instruction doesn't have a real relocation, so it may be a reference to one of +// // the already-loaded pools. +// if let Some(pool_reloc) = gpr_pool_relocs.get(&addr_src_gpr.0) { +// if let Some(fake_pool_reloc) = +// make_fake_pool_reloc(offset, cur_addr, pool_reloc) +// { +// pool_reloc_for_addr.insert(cur_addr, fake_pool_reloc); +// } +// if let Some(addr_dst_gpr) = addr_dst_gpr { +// // If the address of the pool relocation got copied into another register, we +// // need to keep track of it in that register too as future instructions may +// // reference the symbol indirectly via this new register, instead of the +// // register the symbol's address was originally loaded into. +// // For example, the start of the function might `lis` + `addi` the start of the +// // ...data pool into r25, and then later the start of a loop will `addi` r25 +// // with the offset within the .data section of an array variable into r21. +// // Then the body of the loop will `lwzx` one of the array elements from r21. +// let mut new_reloc = pool_reloc.clone(); +// new_reloc.addend += offset as i64; +// gpr_pool_relocs.insert(addr_dst_gpr.0, new_reloc); +// } else { +// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); +// } +// } else { +// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); +// } +// } else { +// clear_overwritten_gprs(ins, &mut gpr_pool_relocs); +// } +// } +// +// // Finally, if we're about to finish the outer loop and don't have any more control flow to +// // follow, we check if there are any instruction addresses in this function that we missed. +// // If so, and if there were any `bctr` instructions before those points in this function, +// // then we try to traverse those missing spots as switch cases. +// if ins_iters_with_gpr_state.is_empty() { +// let unseen_addrs = (func_address as u32..func_address as u32 + code.len() as u32) +// .step_by(4) +// .filter(|addr| !visited_ins_addrs.contains(addr)); +// for unseen_addr in unseen_addrs { +// let prev_bctr_gpr_state = gpr_state_at_bctr +// .iter() +// .filter(|(&addr, _)| addr < unseen_addr) +// .min_by_key(|(&addr, _)| addr) +// .map(|(_, gpr_state)| gpr_state); +// if let Some(gpr_pool_relocs) = prev_bctr_gpr_state { +// let dest_offset_into_func = unseen_addr - func_address as u32; +// let dest_code_slice = &code[dest_offset_into_func as usize..]; +// ins_iters_with_gpr_state.push(( +// InsIter::new(dest_code_slice, unseen_addr), +// gpr_pool_relocs.clone(), +// )); +// break; +// } +// } +// } +// } +// +// pool_reloc_for_addr +// } diff --git a/objdiff-core/src/arch/x86.rs b/objdiff-core/src/arch/x86.rs index b94e73a..5ad6e23 100644 --- a/objdiff-core/src/arch/x86.rs +++ b/objdiff-core/src/arch/x86.rs @@ -7,33 +7,93 @@ use alloc::{ vec, vec::Vec, }; +use std::ops::Range; use anyhow::{anyhow, bail, ensure, Result}; use iced_x86::{ - Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind, - GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, - PrefixKind, Register, + Decoder, DecoderOptions, DecoratorKind, FormatterOutput, FormatterTextKind, GasFormatter, + Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind, PrefixKind, + Register, }; -use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags}; +use object::{pe, Endian as _, Object as _, ObjectSection as _}; use crate::{ - arch::{ObjArch, ProcessCodeResult}, - diff::{DiffObjConfig, X86Formatter}, - obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection}, + arch::Arch, + diff::{display::InstructionPart, DiffObjConfig, X86Formatter}, + obj::{ + InstructionArg, InstructionArgValue, InstructionRef, ParsedInstruction, RelocationFlags, + ResolvedRelocation, ScannedInstruction, + }, }; -pub struct ObjArchX86 { +#[derive(Debug)] +pub struct ArchX86 { bits: u32, - endianness: Endianness, + endianness: object::Endianness, } -impl ObjArchX86 { - pub fn new(object: &File) -> Result { +impl ArchX86 { + pub fn new(object: &object::File) -> Result { Ok(Self { bits: if object.is_64() { 64 } else { 32 }, endianness: object.endianness() }) } + + fn formatter(&self, diff_config: &DiffObjConfig) -> Box { + let mut formatter: Box = match diff_config.x86_formatter { + X86Formatter::Intel => Box::new(IntelFormatter::new()), + X86Formatter::Gas => Box::new(GasFormatter::new()), + X86Formatter::Nasm => Box::new(NasmFormatter::new()), + X86Formatter::Masm => Box::new(MasmFormatter::new()), + }; + formatter.options_mut().set_space_after_operand_separator(diff_config.space_between_args); + formatter + } } -impl ObjArch for ObjArchX86 { +impl Arch for ArchX86 { + fn scan_instructions( + &self, + address: u64, + code: &[u8], + _section_index: usize, + _diff_config: &DiffObjConfig, + ) -> Result> { + let mut out = Vec::with_capacity(code.len() / 2); + let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE); + let mut instruction = Instruction::default(); + while decoder.can_decode() { + decoder.decode_out(&mut instruction); + // TODO is this right? + let branch_dest = match instruction.op0_kind() { + OpKind::NearBranch16 => Some(instruction.near_branch16() as u64), + OpKind::NearBranch32 => Some(instruction.near_branch32() as u64), + OpKind::NearBranch64 => Some(instruction.near_branch64()), + _ => None, + }; + out.push(ScannedInstruction { + ins_ref: InstructionRef { + address: instruction.ip(), + size: instruction.len() as u8, + opcode: instruction.mnemonic() as u16, + }, + branch_dest, + }); + } + Ok(out) + } + + fn display_instruction( + &self, + ins_ref: InstructionRef, + code: &[u8], + relocation: Option, + function_range: Range, + section_index: usize, + diff_config: &DiffObjConfig, + cb: &mut dyn FnMut(InstructionPart) -> Result<()>, + ) -> Result<()> { + todo!() + } + fn process_code( &self, address: u64, @@ -45,13 +105,7 @@ impl ObjArch for ObjArchX86 { ) -> Result { let mut result = ProcessCodeResult { ops: Vec::new(), insts: Vec::new() }; let mut decoder = Decoder::with_ip(self.bits, code, address, DecoderOptions::NONE); - let mut formatter: Box = match config.x86_formatter { - X86Formatter::Intel => Box::new(IntelFormatter::new()), - X86Formatter::Gas => Box::new(GasFormatter::new()), - X86Formatter::Nasm => Box::new(NasmFormatter::new()), - X86Formatter::Masm => Box::new(MasmFormatter::new()), - }; - formatter.options_mut().set_space_after_operand_separator(config.space_between_args); + let mut formatter = self.formatter(config); let mut output = InstructionFormatterOutput { formatted: String::new(), @@ -101,10 +155,12 @@ impl ObjArch for ObjArchX86 { output.ins.formatted.clone_from(&output.formatted); // Make sure we've put the relocation somewhere in the instruction - if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) { + if reloc.is_some() + && !output.ins.args.iter().any(|a| matches!(a, InstructionArg::Reloc)) + { let mut found = replace_arg( OpKind::Memory, - ObjInsArg::Reloc, + InstructionArg::Reloc, &mut output.ins.args, &instruction, &output.ins_operands, @@ -112,7 +168,7 @@ impl ObjArch for ObjArchX86 { if !found { found = replace_arg( OpKind::Immediate32, - ObjInsArg::Reloc, + InstructionArg::Reloc, &mut output.ins.args, &instruction, &output.ins_operands, @@ -120,7 +176,9 @@ impl ObjArch for ObjArchX86 { } ensure!(found, "x86: Failed to find operand for Absolute relocation"); } - if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) { + if reloc.is_some() + && !output.ins.args.iter().any(|a| matches!(a, InstructionArg::Reloc)) + { bail!("Failed to find relocation in instruction"); } @@ -136,14 +194,15 @@ impl ObjArch for ObjArchX86 { fn implcit_addend( &self, - _file: &File<'_>, - section: &ObjSection, + _file: &object::File<'_>, + section: &object::Section, address: u64, - reloc: &Relocation, + _relocation: &object::Relocation, + flags: RelocationFlags, ) -> Result { - match reloc.flags() { - RelocationFlags::Coff { typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 } => { - let data = section.data[address as usize..address as usize + 4].try_into()?; + match flags { + RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) => { + let data = section.data()[address as usize..address as usize + 4].try_into()?; Ok(self.endianness.read_i32_bytes(data) as i64) } flags => bail!("Unsupported x86 implicit relocation {flags:?}"), @@ -162,7 +221,7 @@ impl ObjArch for ObjArchX86 { fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str> { match flags { - RelocationFlags::Coff { typ } => match typ { + RelocationFlags::Coff(typ) => match typ { pe::IMAGE_REL_I386_DIR32 => Cow::Borrowed("IMAGE_REL_I386_DIR32"), pe::IMAGE_REL_I386_REL32 => Cow::Borrowed("IMAGE_REL_I386_REL32"), _ => Cow::Owned(format!("<{flags:?}>")), @@ -173,7 +232,7 @@ impl ObjArch for ObjArchX86 { fn get_reloc_byte_size(&self, flags: RelocationFlags) -> usize { match flags { - RelocationFlags::Coff { typ } => match typ { + RelocationFlags::Coff(typ) => match typ { pe::IMAGE_REL_I386_DIR16 => 2, pe::IMAGE_REL_I386_REL16 => 2, pe::IMAGE_REL_I386_DIR32 => 4, @@ -187,8 +246,8 @@ impl ObjArch for ObjArchX86 { fn replace_arg( from: OpKind, - to: ObjInsArg, - args: &mut [ObjInsArg], + to: InstructionArg, + args: &mut [InstructionArg], instruction: &Instruction, ins_operands: &[Option], ) -> Result { @@ -213,7 +272,7 @@ fn replace_arg( struct InstructionFormatterOutput { formatted: String, - ins: ObjIns, + ins: ParsedInstruction, error: Option, ins_operands: Vec>, } @@ -223,11 +282,13 @@ impl InstructionFormatterOutput { // The formatter writes the '-' operator and then gives us a negative value, // so convert it to a positive value to avoid double negatives if value < 0 - && matches!(self.ins.args.last(), Some(ObjInsArg::Arg(ObjInsArgValue::Opaque(v))) if v == "-") + && matches!(self.ins.args.last(), Some(InstructionArg::Value(InstructionArgValue::Opaque(v))) if v == "-") { - self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value.wrapping_abs()))); + self.ins + .args + .push(InstructionArg::Value(InstructionArgValue::Signed(value.wrapping_abs()))); } else { - self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Signed(value))); + self.ins.args.push(InstructionArg::Value(InstructionArgValue::Signed(value))); } } } @@ -242,10 +303,12 @@ impl FormatterOutput for InstructionFormatterOutput { self.ins_operands.push(None); match kind { FormatterTextKind::Text | FormatterTextKind::Punctuation => { - self.ins.args.push(ObjInsArg::PlainText(text.to_string().into())); + self.ins.args.push(InstructionArg::PlainText(text.to_string().into())); } FormatterTextKind::Keyword | FormatterTextKind::Operator => { - self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into()))); + self.ins.args.push(InstructionArg::Value(InstructionArgValue::Opaque( + text.to_string().into(), + ))); } _ => { if self.error.is_none() { @@ -258,12 +321,13 @@ impl FormatterOutput for InstructionFormatterOutput { fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) { self.formatted.push_str(text); self.ins_operands.push(None); - self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into()))); + self.ins + .args + .push(InstructionArg::Value(InstructionArgValue::Opaque(text.to_string().into()))); } fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) { self.formatted.push_str(text); - // TODO: can iced-x86 guarantee 'static here? self.ins.mnemonic = Cow::Owned(text.to_string()); } @@ -284,10 +348,11 @@ impl FormatterOutput for InstructionFormatterOutput { match kind { FormatterTextKind::LabelAddress => { if let Some(reloc) = self.ins.reloc.as_ref() { - if matches!(reloc.flags, RelocationFlags::Coff { - typ: pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32 - }) { - self.ins.args.push(ObjInsArg::Reloc); + if matches!( + reloc.flags, + RelocationFlags::Coff(pe::IMAGE_REL_I386_DIR32 | pe::IMAGE_REL_I386_REL32) + ) { + self.ins.args.push(InstructionArg::Reloc); return; } else if self.error.is_none() { self.error = Some(anyhow!( @@ -296,16 +361,14 @@ impl FormatterOutput for InstructionFormatterOutput { )); } } - self.ins.args.push(ObjInsArg::BranchDest(value)); + self.ins.args.push(InstructionArg::BranchDest(value)); self.ins.branch_dest = Some(value); return; } FormatterTextKind::FunctionAddress => { if let Some(reloc) = self.ins.reloc.as_ref() { - if matches!(reloc.flags, RelocationFlags::Coff { - typ: pe::IMAGE_REL_I386_REL32 - }) { - self.ins.args.push(ObjInsArg::Reloc); + if matches!(reloc.flags, RelocationFlags::Coff(pe::IMAGE_REL_I386_REL32)) { + self.ins.args.push(InstructionArg::Reloc); return; } else if self.error.is_none() { self.error = Some(anyhow!( @@ -332,7 +395,7 @@ impl FormatterOutput for InstructionFormatterOutput { self.push_signed(value as i64); } NumberKind::UInt8 | NumberKind::UInt16 | NumberKind::UInt32 | NumberKind::UInt64 => { - self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(value))); + self.ins.args.push(InstructionArg::Value(InstructionArgValue::Unsigned(value))); } } } @@ -347,7 +410,7 @@ impl FormatterOutput for InstructionFormatterOutput { ) { self.formatted.push_str(text); self.ins_operands.push(instruction_operand); - self.ins.args.push(ObjInsArg::PlainText(text.to_string().into())); + self.ins.args.push(InstructionArg::PlainText(text.to_string().into())); } fn write_register( @@ -360,6 +423,8 @@ impl FormatterOutput for InstructionFormatterOutput { ) { self.formatted.push_str(text); self.ins_operands.push(instruction_operand); - self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string().into()))); + self.ins + .args + .push(InstructionArg::Value(InstructionArgValue::Opaque(text.to_string().into()))); } } diff --git a/objdiff-core/src/bindings/diff.rs b/objdiff-core/src/bindings/diff.rs index f702dbe..959ec8c 100644 --- a/objdiff-core/src/bindings/diff.rs +++ b/objdiff-core/src/bindings/diff.rs @@ -1,18 +1,6 @@ #![allow(clippy::needless_lifetimes)] // Generated serde code -use alloc::string::ToString; - -use crate::{ - diff::{ - ObjDataDiff, ObjDataDiffKind, ObjDiff, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, - ObjInsDiff, ObjInsDiffKind, ObjSectionDiff, ObjSymbolDiff, - }, - obj, - obj::{ - ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSectionKind, ObjSymbol, - ObjSymbolFlagSet, ObjSymbolFlags, - }, -}; +use crate::{diff, obj}; // Protobuf diff types include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs")); @@ -20,242 +8,235 @@ include!(concat!(env!("OUT_DIR"), "/objdiff.diff.rs")); include!(concat!(env!("OUT_DIR"), "/objdiff.diff.serde.rs")); impl DiffResult { - pub fn new(left: Option<(&ObjInfo, &ObjDiff)>, right: Option<(&ObjInfo, &ObjDiff)>) -> Self { + pub fn new( + _left: Option<(&obj::Object, &diff::ObjectDiff)>, + _right: Option<(&obj::Object, &diff::ObjectDiff)>, + ) -> Self { Self { - left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)), - right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)), + // TODO + // left: left.map(|(obj, diff)| ObjectDiff::new(obj, diff)), + // right: right.map(|(obj, diff)| ObjectDiff::new(obj, diff)), + left: None, + right: None, } } } -impl ObjectDiff { - pub fn new(obj: &ObjInfo, diff: &ObjDiff) -> Self { - Self { - sections: diff - .sections - .iter() - .enumerate() - .map(|(i, d)| SectionDiff::new(obj, i, d)) - .collect(), - } - } -} - -impl SectionDiff { - pub fn new(obj: &ObjInfo, section_index: usize, section_diff: &ObjSectionDiff) -> Self { - let section = &obj.sections[section_index]; - let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect(); - let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect(); - // TODO: section_diff.reloc_diff - Self { - name: section.name.to_string(), - kind: SectionKind::from(section.kind) as i32, - size: section.size, - address: section.address, - symbols, - data, - match_percent: section_diff.match_percent, - } - } -} - -impl From for SectionKind { - fn from(value: ObjSectionKind) -> Self { - match value { - ObjSectionKind::Code => SectionKind::SectionText, - ObjSectionKind::Data => SectionKind::SectionData, - ObjSectionKind::Bss => SectionKind::SectionBss, - // TODO common - } - } -} - -impl From for SymbolRef { - fn from(value: obj::SymbolRef) -> Self { - Self { - section_index: if value.section_idx == obj::SECTION_COMMON { - None - } else { - Some(value.section_idx as u32) - }, - symbol_index: value.symbol_idx as u32, - } - } -} - -impl SymbolDiff { - pub fn new(object: &ObjInfo, symbol_diff: &ObjSymbolDiff) -> Self { - let (_section, symbol) = object.section_symbol(symbol_diff.symbol_ref); - let instructions = symbol_diff - .instructions - .iter() - .map(|ins_diff| InstructionDiff::new(object, ins_diff)) - .collect(); - Self { - symbol: Some(Symbol::new(symbol)), - instructions, - match_percent: symbol_diff.match_percent, - target: symbol_diff.target_symbol.map(SymbolRef::from), - } - } -} - -impl DataDiff { - pub fn new(_object: &ObjInfo, data_diff: &ObjDataDiff) -> Self { - Self { - kind: DiffKind::from(data_diff.kind) as i32, - data: data_diff.data.clone(), - size: data_diff.len as u64, - } - } -} - -impl Symbol { - pub fn new(value: &ObjSymbol) -> Self { - Self { - name: value.name.to_string(), - demangled_name: value.demangled_name.clone(), - address: value.address, - size: value.size, - flags: symbol_flags(value.flags), - } - } -} - -fn symbol_flags(value: ObjSymbolFlagSet) -> u32 { - let mut flags = 0u32; - if value.0.contains(ObjSymbolFlags::Global) { - flags |= SymbolFlag::SymbolGlobal as u32; - } - if value.0.contains(ObjSymbolFlags::Local) { - flags |= SymbolFlag::SymbolLocal as u32; - } - if value.0.contains(ObjSymbolFlags::Weak) { - flags |= SymbolFlag::SymbolWeak as u32; - } - if value.0.contains(ObjSymbolFlags::Common) { - flags |= SymbolFlag::SymbolCommon as u32; - } - if value.0.contains(ObjSymbolFlags::Hidden) { - flags |= SymbolFlag::SymbolHidden as u32; - } - flags -} - -impl Instruction { - pub fn new(object: &ObjInfo, instruction: &ObjIns) -> Self { - Self { - address: instruction.address, - size: instruction.size as u32, - opcode: instruction.op as u32, - mnemonic: instruction.mnemonic.to_string(), - formatted: instruction.formatted.clone(), - arguments: instruction.args.iter().map(Argument::new).collect(), - relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)), - branch_dest: instruction.branch_dest, - line_number: instruction.line, - original: instruction.orig.clone(), - } - } -} - -impl Argument { - pub fn new(value: &ObjInsArg) -> Self { - Self { - value: Some(match value { - ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()), - ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)), - ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}), - ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest), - }), - } - } -} - -impl ArgumentValue { - pub fn new(value: &ObjInsArgValue) -> Self { - Self { - value: Some(match value { - ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v), - ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v), - ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()), - }), - } - } -} - -impl Relocation { - pub fn new(object: &ObjInfo, reloc: &ObjReloc) -> Self { - Self { - r#type: match reloc.flags { - object::RelocationFlags::Elf { r_type } => r_type, - object::RelocationFlags::MachO { r_type, .. } => r_type as u32, - object::RelocationFlags::Coff { typ } => typ as u32, - object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32, - _ => unreachable!(), - }, - type_name: object.arch.display_reloc(reloc.flags).into_owned(), - target: Some(RelocationTarget { - symbol: Some(Symbol::new(&reloc.target)), - addend: reloc.addend, - }), - } - } -} - -impl InstructionDiff { - pub fn new(object: &ObjInfo, instruction_diff: &ObjInsDiff) -> Self { - Self { - instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)), - diff_kind: DiffKind::from(instruction_diff.kind) as i32, - branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new), - branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new), - arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(), - } - } -} - -impl ArgumentDiff { - pub fn new(value: &Option) -> Self { - Self { diff_index: value.as_ref().map(|v| v.idx as u32) } - } -} - -impl From for DiffKind { - fn from(value: ObjInsDiffKind) -> Self { - match value { - ObjInsDiffKind::None => DiffKind::DiffNone, - ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch, - ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch, - ObjInsDiffKind::Replace => DiffKind::DiffReplace, - ObjInsDiffKind::Delete => DiffKind::DiffDelete, - ObjInsDiffKind::Insert => DiffKind::DiffInsert, - } - } -} - -impl From for DiffKind { - fn from(value: ObjDataDiffKind) -> Self { - match value { - ObjDataDiffKind::None => DiffKind::DiffNone, - ObjDataDiffKind::Replace => DiffKind::DiffReplace, - ObjDataDiffKind::Delete => DiffKind::DiffDelete, - ObjDataDiffKind::Insert => DiffKind::DiffInsert, - } - } -} - -impl InstructionBranchFrom { - pub fn new(value: &ObjInsBranchFrom) -> Self { - Self { - instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(), - branch_index: value.branch_idx as u32, - } - } -} - -impl InstructionBranchTo { - pub fn new(value: &ObjInsBranchTo) -> Self { - Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 } - } -} +// impl ObjectDiff { +// pub fn new(obj: &obj::Object, diff: &diff::ObjectDiff) -> Self { +// Self { +// sections: diff +// .sections +// .iter() +// .enumerate() +// .map(|(i, d)| SectionDiff::new(obj, i, d)) +// .collect(), +// } +// } +// } +// +// impl SectionDiff { +// pub fn new(obj: &obj::Object, section_index: usize, section_diff: &diff::SectionDiff) -> Self { +// let section = &obj.sections[section_index]; +// let symbols = section_diff.symbols.iter().map(|d| SymbolDiff::new(obj, d)).collect(); +// let data = section_diff.data_diff.iter().map(|d| DataDiff::new(obj, d)).collect(); +// // TODO: section_diff.reloc_diff +// Self { +// name: section.name.to_string(), +// kind: SectionKind::from(section.kind) as i32, +// size: section.size, +// address: section.address, +// symbols, +// data, +// match_percent: section_diff.match_percent, +// } +// } +// } +// +// impl From for SectionKind { +// fn from(value: obj::SectionKind) -> Self { +// match value { +// obj::SectionKind::Code => SectionKind::SectionText, +// obj::SectionKind::Data => SectionKind::SectionData, +// obj::SectionKind::Bss => SectionKind::SectionBss, +// // TODO common +// } +// } +// } +// +// impl SymbolDiff { +// pub fn new(object: &obj::Object, symbol_diff: &diff::SymbolDiff) -> Self { +// let symbol = object.symbols[symbol_diff.symbol_index]; +// let instructions = symbol_diff +// .instruction_rows +// .iter() +// .map(|ins_diff| InstructionDiff::new(object, ins_diff)) +// .collect(); +// Self { +// symbol: Some(Symbol::new(symbol)), +// instructions, +// match_percent: symbol_diff.match_percent, +// target: symbol_diff.target_symbol.map(SymbolRef::from), +// } +// } +// } +// +// impl DataDiff { +// pub fn new(_object: &obj::Object, data_diff: &diff::DataDiff) -> Self { +// Self { +// kind: DiffKind::from(data_diff.kind) as i32, +// data: data_diff.data.clone(), +// size: data_diff.len as u64, +// } +// } +// } +// +// impl Symbol { +// pub fn new(value: &ObjSymbol) -> Self { +// Self { +// name: value.name.to_string(), +// demangled_name: value.demangled_name.clone(), +// address: value.address, +// size: value.size, +// flags: symbol_flags(value.flags), +// } +// } +// } +// +// fn symbol_flags(value: ObjSymbolFlagSet) -> u32 { +// let mut flags = 0u32; +// if value.0.contains(ObjSymbolFlags::Global) { +// flags |= SymbolFlag::SymbolGlobal as u32; +// } +// if value.0.contains(ObjSymbolFlags::Local) { +// flags |= SymbolFlag::SymbolLocal as u32; +// } +// if value.0.contains(ObjSymbolFlags::Weak) { +// flags |= SymbolFlag::SymbolWeak as u32; +// } +// if value.0.contains(ObjSymbolFlags::Common) { +// flags |= SymbolFlag::SymbolCommon as u32; +// } +// if value.0.contains(ObjSymbolFlags::Hidden) { +// flags |= SymbolFlag::SymbolHidden as u32; +// } +// flags +// } +// +// impl Instruction { +// pub fn new(object: &obj::Object, instruction: &ObjIns) -> Self { +// Self { +// address: instruction.address, +// size: instruction.size as u32, +// opcode: instruction.op as u32, +// mnemonic: instruction.mnemonic.to_string(), +// formatted: instruction.formatted.clone(), +// arguments: instruction.args.iter().map(Argument::new).collect(), +// relocation: instruction.reloc.as_ref().map(|reloc| Relocation::new(object, reloc)), +// branch_dest: instruction.branch_dest, +// line_number: instruction.line, +// original: instruction.orig.clone(), +// } +// } +// } +// +// impl Argument { +// pub fn new(value: &ObjInsArg) -> Self { +// Self { +// value: Some(match value { +// ObjInsArg::PlainText(s) => argument::Value::PlainText(s.to_string()), +// ObjInsArg::Arg(v) => argument::Value::Argument(ArgumentValue::new(v)), +// ObjInsArg::Reloc => argument::Value::Relocation(ArgumentRelocation {}), +// ObjInsArg::BranchDest(dest) => argument::Value::BranchDest(*dest), +// }), +// } +// } +// } +// +// impl ArgumentValue { +// pub fn new(value: &ObjInsArgValue) -> Self { +// Self { +// value: Some(match value { +// ObjInsArgValue::Signed(v) => argument_value::Value::Signed(*v), +// ObjInsArgValue::Unsigned(v) => argument_value::Value::Unsigned(*v), +// ObjInsArgValue::Opaque(v) => argument_value::Value::Opaque(v.to_string()), +// }), +// } +// } +// } +// +// impl Relocation { +// pub fn new(object: &obj::Object, reloc: &ObjReloc) -> Self { +// Self { +// r#type: match reloc.flags { +// object::RelocationFlags::Elf { r_type } => r_type, +// object::RelocationFlags::MachO { r_type, .. } => r_type as u32, +// object::RelocationFlags::Coff { typ } => typ as u32, +// object::RelocationFlags::Xcoff { r_rtype, .. } => r_rtype as u32, +// _ => unreachable!(), +// }, +// type_name: object.arch.display_reloc(reloc.flags).into_owned(), +// target: Some(RelocationTarget { +// symbol: Some(Symbol::new(&reloc.target)), +// addend: reloc.addend, +// }), +// } +// } +// } +// +// impl InstructionDiff { +// pub fn new(object: &obj::Object, instruction_diff: &ObjInsDiff) -> Self { +// Self { +// instruction: instruction_diff.ins.as_ref().map(|ins| Instruction::new(object, ins)), +// diff_kind: DiffKind::from(instruction_diff.kind) as i32, +// branch_from: instruction_diff.branch_from.as_ref().map(InstructionBranchFrom::new), +// branch_to: instruction_diff.branch_to.as_ref().map(InstructionBranchTo::new), +// arg_diff: instruction_diff.arg_diff.iter().map(ArgumentDiff::new).collect(), +// } +// } +// } +// +// impl ArgumentDiff { +// pub fn new(value: &Option) -> Self { +// Self { diff_index: value.as_ref().map(|v| v.idx as u32) } +// } +// } +// +// impl From for DiffKind { +// fn from(value: ObjInsDiffKind) -> Self { +// match value { +// ObjInsDiffKind::None => DiffKind::DiffNone, +// ObjInsDiffKind::OpMismatch => DiffKind::DiffOpMismatch, +// ObjInsDiffKind::ArgMismatch => DiffKind::DiffArgMismatch, +// ObjInsDiffKind::Replace => DiffKind::DiffReplace, +// ObjInsDiffKind::Delete => DiffKind::DiffDelete, +// ObjInsDiffKind::Insert => DiffKind::DiffInsert, +// } +// } +// } +// +// impl From for DiffKind { +// fn from(value: ObjDataDiffKind) -> Self { +// match value { +// ObjDataDiffKind::None => DiffKind::DiffNone, +// ObjDataDiffKind::Replace => DiffKind::DiffReplace, +// ObjDataDiffKind::Delete => DiffKind::DiffDelete, +// ObjDataDiffKind::Insert => DiffKind::DiffInsert, +// } +// } +// } +// +// impl InstructionBranchFrom { +// pub fn new(value: &ObjInsBranchFrom) -> Self { +// Self { +// instruction_index: value.ins_idx.iter().map(|&x| x as u32).collect(), +// branch_index: value.branch_idx as u32, +// } +// } +// } +// +// impl InstructionBranchTo { +// pub fn new(value: &ObjInsBranchTo) -> Self { +// Self { instruction_index: value.ins_idx as u32, branch_index: value.branch_idx as u32 } +// } +// } diff --git a/objdiff-core/src/diff/code.rs b/objdiff-core/src/diff/code.rs index b52d0d5..65c53b8 100644 --- a/objdiff-core/src/diff/code.rs +++ b/objdiff-core/src/diff/code.rs @@ -1,428 +1,561 @@ use alloc::{ - collections::BTreeMap, + collections::{btree_map, BTreeMap}, string::{String, ToString}, vec, vec::Vec, }; -use anyhow::{anyhow, Result}; -use similar::{capture_diff_slices, Algorithm}; +use anyhow::{anyhow, ensure, Result}; -use super::FunctionRelocDiffs; -use crate::{ - arch::ProcessCodeResult, - diff::{ - DiffObjConfig, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff, ObjInsDiffKind, - ObjSymbolDiff, - }, - obj::{ - ObjInfo, ObjIns, ObjInsArg, ObjReloc, ObjSection, ObjSymbol, ObjSymbolFlags, ObjSymbolKind, - SymbolRef, - }, +use super::{ + DiffObjConfig, FunctionRelocDiffs, InstructionArgDiffIndex, InstructionBranchFrom, + InstructionBranchTo, InstructionDiffKind, InstructionDiffRow, SymbolDiff, +}; +use crate::obj::{ + InstructionArg, InstructionArgValue, InstructionRef, Object, ResolvedRelocation, + ScannedInstruction, SymbolFlag, SymbolKind, }; -pub fn process_code_symbol( - obj: &ObjInfo, - symbol_ref: SymbolRef, - config: &DiffObjConfig, -) -> Result { - let (section, symbol) = obj.section_symbol(symbol_ref); - let section = section.ok_or_else(|| anyhow!("Code symbol section not found"))?; - let code = §ion.data - [symbol.section_address as usize..(symbol.section_address + symbol.size) as usize]; - let mut res = obj.arch.process_code( - symbol.address, - code, - section.orig_index, - §ion.relocations, - §ion.line_info, - config, - )?; - - for inst in res.insts.iter_mut() { - if let Some(reloc) = &mut inst.reloc { - if reloc.target.size == 0 && reloc.target.name.is_empty() { - // Fake target symbol we added as a placeholder. We need to find the real one. - if let Some(real_target) = - find_symbol_matching_fake_symbol_in_sections(&reloc.target, &obj.sections) - { - reloc.addend = (reloc.target.address - real_target.address) as i64; - reloc.target = real_target; - } - } - } +pub fn no_diff_code( + obj: &Object, + symbol_idx: usize, + diff_config: &DiffObjConfig, +) -> Result { + let symbol = &obj.symbols[symbol_idx]; + let section_index = symbol.section.ok_or_else(|| anyhow!("Missing section for symbol"))?; + let section = &obj.sections[section_index]; + let data = section.data_range(symbol.address, symbol.size as usize).ok_or_else(|| { + anyhow!( + "Symbol data out of bounds: {:#x}..{:#x}", + symbol.address, + symbol.address + symbol.size + ) + })?; + let ops = obj.arch.scan_instructions(symbol.address, data, section_index, diff_config)?; + let mut instruction_rows = Vec::::new(); + for i in &ops { + instruction_rows + .push(InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() }); } - - Ok(res) + resolve_branches(obj, section_index, &ops, &mut instruction_rows); + Ok(SymbolDiff { target_symbol: None, match_percent: None, diff_score: None, instruction_rows }) } -pub fn no_diff_code(out: &ProcessCodeResult, symbol_ref: SymbolRef) -> Result { - let mut diff = Vec::::new(); - for i in &out.insts { - diff.push(ObjInsDiff { - ins: Some(i.clone()), - kind: ObjInsDiffKind::None, - ..Default::default() - }); - } - resolve_branches(&mut diff); - Ok(ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: diff, match_percent: None }) -} +const PENALTY_IMM_DIFF: u64 = 1; +const PENALTY_REG_DIFF: u64 = 5; +const PENALTY_REPLACE: u64 = 60; +const PENALTY_INSERT_DELETE: u64 = 100; pub fn diff_code( - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left_out: &ProcessCodeResult, - right_out: &ProcessCodeResult, - left_symbol_ref: SymbolRef, - right_symbol_ref: SymbolRef, - config: &DiffObjConfig, -) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> { - let mut left_diff = Vec::::new(); - let mut right_diff = Vec::::new(); - diff_instructions(&mut left_diff, &mut right_diff, left_out, right_out)?; + left_obj: &Object, + right_obj: &Object, + left_symbol_idx: usize, + right_symbol_idx: usize, + diff_config: &DiffObjConfig, +) -> Result<(SymbolDiff, SymbolDiff)> { + let left_symbol = &left_obj.symbols[left_symbol_idx]; + let right_symbol = &right_obj.symbols[right_symbol_idx]; + let left_section = left_symbol + .section + .and_then(|i| left_obj.sections.get(i)) + .ok_or_else(|| anyhow!("Missing section for symbol"))?; + let right_section = right_symbol + .section + .and_then(|i| right_obj.sections.get(i)) + .ok_or_else(|| anyhow!("Missing section for symbol"))?; + let left_data = left_section + .data_range(left_symbol.address, left_symbol.size as usize) + .ok_or_else(|| { + anyhow!( + "Symbol data out of bounds: {:#x}..{:#x}", + left_symbol.address, + left_symbol.address + left_symbol.size + ) + })?; + let right_data = right_section + .data_range(right_symbol.address, right_symbol.size as usize) + .ok_or_else(|| { + anyhow!( + "Symbol data out of bounds: {:#x}..{:#x}", + right_symbol.address, + right_symbol.address + right_symbol.size + ) + })?; - resolve_branches(&mut left_diff); - resolve_branches(&mut right_diff); + let left_section_idx = left_symbol.section.unwrap(); + let right_section_idx = right_symbol.section.unwrap(); + let left_ops = left_obj.arch.scan_instructions( + left_symbol.address, + left_data, + left_section_idx, + diff_config, + )?; + let right_ops = left_obj.arch.scan_instructions( + right_symbol.address, + right_data, + right_section_idx, + diff_config, + )?; + let (mut left_rows, mut right_rows) = diff_instructions(&left_ops, &right_ops)?; + resolve_branches(left_obj, left_section_idx, &left_ops, &mut left_rows); + resolve_branches(right_obj, right_section_idx, &right_ops, &mut right_rows); - let mut diff_state = InsDiffState::default(); - for (left, right) in left_diff.iter_mut().zip(right_diff.iter_mut()) { - let result = compare_ins(config, left_obj, right_obj, left, right, &mut diff_state)?; - left.kind = result.kind; - right.kind = result.kind; - left.arg_diff = result.left_args_diff; - right.arg_diff = result.right_args_diff; + let mut diff_state = InstructionDiffState::default(); + for (left_row, right_row) in left_rows.iter_mut().zip(right_rows.iter_mut()) { + let result = diff_instruction( + left_obj, + right_obj, + left_symbol_idx, + right_symbol_idx, + left_row.ins_ref.as_ref(), + right_row.ins_ref.as_ref(), + left_row, + right_row, + diff_config, + &mut diff_state, + )?; + left_row.kind = result.kind; + right_row.kind = result.kind; + left_row.arg_diff = result.left_args_diff; + right_row.arg_diff = result.right_args_diff; } - let total = left_out.insts.len().max(right_out.insts.len()); - let percent = if diff_state.diff_count >= total { - 0.0 + let max_score = left_ops.len() as u64 * PENALTY_INSERT_DELETE; + let diff_score = diff_state.diff_score.min(max_score); + let match_percent = if max_score == 0 { + 100.0 } else { - ((total - diff_state.diff_count) as f32 / total as f32) * 100.0 + ((1.0 - (diff_score as f64 / max_score as f64)) * 100.0) as f32 }; Ok(( - ObjSymbolDiff { - symbol_ref: left_symbol_ref, - target_symbol: Some(right_symbol_ref), - instructions: left_diff, - match_percent: Some(percent), + SymbolDiff { + target_symbol: Some(right_symbol_idx), + match_percent: Some(match_percent), + diff_score: Some((diff_score, max_score)), + instruction_rows: left_rows, }, - ObjSymbolDiff { - symbol_ref: right_symbol_ref, - target_symbol: Some(left_symbol_ref), - instructions: right_diff, - match_percent: Some(percent), + SymbolDiff { + target_symbol: Some(left_symbol_idx), + match_percent: Some(match_percent), + diff_score: Some((diff_score, max_score)), + instruction_rows: right_rows, }, )) } fn diff_instructions( - left_diff: &mut Vec, - right_diff: &mut Vec, - left_code: &ProcessCodeResult, - right_code: &ProcessCodeResult, -) -> Result<()> { - let ops = capture_diff_slices(Algorithm::Patience, &left_code.ops, &right_code.ops); + left_insts: &[ScannedInstruction], + right_insts: &[ScannedInstruction], +) -> Result<(Vec, Vec)> { + let left_ops = left_insts.iter().map(|i| i.ins_ref.opcode).collect::>(); + let right_ops = right_insts.iter().map(|i| i.ins_ref.opcode).collect::>(); + let ops = similar::capture_diff_slices(similar::Algorithm::Patience, &left_ops, &right_ops); if ops.is_empty() { - left_diff.extend( - left_code - .insts - .iter() - .map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }), - ); - right_diff.extend( - right_code - .insts - .iter() - .map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }), - ); - return Ok(()); + ensure!(left_insts.len() == right_insts.len()); + let left_diff = left_insts + .iter() + .map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() }) + .collect(); + let right_diff = right_insts + .iter() + .map(|i| InstructionDiffRow { ins_ref: Some(i.ins_ref), ..Default::default() }) + .collect(); + return Ok((left_diff, right_diff)); } + let row_count = ops + .iter() + .map(|op| match *op { + similar::DiffOp::Equal { len, .. } => len, + similar::DiffOp::Delete { old_len, .. } => old_len, + similar::DiffOp::Insert { new_len, .. } => new_len, + similar::DiffOp::Replace { old_len, new_len, .. } => old_len.max(new_len), + }) + .sum(); + let mut left_diff = Vec::::with_capacity(row_count); + let mut right_diff = Vec::::with_capacity(row_count); for op in ops { let (_tag, left_range, right_range) = op.as_tag_tuple(); let len = left_range.len().max(right_range.len()); - left_diff.extend( - left_code.insts[left_range.clone()] - .iter() - .map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }), - ); - right_diff.extend( - right_code.insts[right_range.clone()] - .iter() - .map(|i| ObjInsDiff { ins: Some(i.clone()), ..Default::default() }), - ); + left_diff.extend(left_range.clone().map(|i| InstructionDiffRow { + ins_ref: Some(left_insts[i].ins_ref), + ..Default::default() + })); + right_diff.extend(right_range.clone().map(|i| InstructionDiffRow { + ins_ref: Some(right_insts[i].ins_ref), + ..Default::default() + })); if left_range.len() < len { - left_diff.extend((left_range.len()..len).map(|_| ObjInsDiff::default())); + left_diff.extend((left_range.len()..len).map(|_| InstructionDiffRow::default())); } if right_range.len() < len { - right_diff.extend((right_range.len()..len).map(|_| ObjInsDiff::default())); + right_diff.extend((right_range.len()..len).map(|_| InstructionDiffRow::default())); } } - - Ok(()) + Ok((left_diff, right_diff)) } -fn resolve_branches(vec: &mut [ObjInsDiff]) { - let mut branch_idx = 0usize; +fn arg_to_string(arg: &InstructionArg, reloc: Option<&ResolvedRelocation>) -> String { + match arg { + InstructionArg::Value(arg) => arg.to_string(), + InstructionArg::Reloc => { + reloc.as_ref().map_or_else(|| "".to_string(), |r| r.symbol.name.clone()) + } + InstructionArg::BranchDest(arg) => arg.to_string(), + } +} + +fn resolve_branches( + obj: &Object, + section_index: usize, + ops: &[ScannedInstruction], + rows: &mut [InstructionDiffRow], +) { + let section = &obj.sections[section_index]; + let mut branch_idx = 0u32; // Map addresses to indices - let mut addr_map = BTreeMap::::new(); - for (i, ins_diff) in vec.iter().enumerate() { - if let Some(ins) = &ins_diff.ins { - addr_map.insert(ins.address, i); + let mut addr_map = BTreeMap::::new(); + for (i, ins_diff) in rows.iter().enumerate() { + if let Some(ins) = ins_diff.ins_ref { + addr_map.insert(ins.address, i as u32); } } // Generate branches - let mut branches = BTreeMap::::new(); - for (i, ins_diff) in vec.iter_mut().enumerate() { - if let Some(ins) = &ins_diff.ins { - if let Some(ins_idx) = ins.branch_dest.and_then(|a| addr_map.get(&a)) { - if let Some(branch) = branches.get_mut(ins_idx) { - ins_diff.branch_to = - Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx: branch.branch_idx }); - branch.ins_idx.push(i); - } else { - ins_diff.branch_to = Some(ObjInsBranchTo { ins_idx: *ins_idx, branch_idx }); - branches.insert(*ins_idx, ObjInsBranchFrom { ins_idx: vec![i], branch_idx }); + let mut branches = BTreeMap::::new(); + for ((i, ins_diff), ins) in + rows.iter_mut().enumerate().filter(|(_, row)| row.ins_ref.is_some()).zip(ops) + { + let branch_dest = if let Some(resolved) = section.relocation_at(ins.ins_ref.address, obj) { + if resolved.symbol.section == Some(section_index) { + // If the relocation target is in the same section, use it as the branch destination + resolved.symbol.address.checked_add_signed(resolved.relocation.addend) + } else { + None + } + } else { + ins.branch_dest + }; + if let Some(ins_idx) = branch_dest.and_then(|a| addr_map.get(&a).copied()) { + match branches.entry(ins_idx) { + btree_map::Entry::Vacant(e) => { + ins_diff.branch_to = Some(InstructionBranchTo { ins_idx, branch_idx }); + e.insert(InstructionBranchFrom { ins_idx: vec![i as u32], branch_idx }); branch_idx += 1; } + btree_map::Entry::Occupied(e) => { + let branch = e.into_mut(); + ins_diff.branch_to = + Some(InstructionBranchTo { ins_idx, branch_idx: branch.branch_idx }); + branch.ins_idx.push(i as u32); + } } } } // Store branch from for (i, branch) in branches { - vec[i].branch_from = Some(branch); + rows[i as usize].branch_from = Some(branch); } } -pub fn address_eq(left: &ObjReloc, right: &ObjReloc) -> bool { - if right.target.size == 0 && left.target.size != 0 { +pub(crate) fn address_eq(left: &ResolvedRelocation, right: &ResolvedRelocation) -> bool { + if right.symbol.size == 0 && left.symbol.size != 0 { // The base relocation is against a pool but the target relocation isn't. // This can happen in rare cases where the compiler will generate a pool+addend relocation // in the base's data, but the one detected in the target is direct with no addend. // Just check that the final address is the same so these count as a match. - left.target.address as i64 + left.addend == right.target.address as i64 + right.addend + left.symbol.address as i64 + left.relocation.addend + == right.symbol.address as i64 + right.relocation.addend } else { // But otherwise, if the compiler isn't using a pool, we're more strict and check that the // target symbol address and relocation addend both match exactly. - left.target.address == right.target.address && left.addend == right.addend + left.symbol.address == right.symbol.address + && left.relocation.addend == right.relocation.addend } } -pub fn section_name_eq( - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left_orig_section_index: usize, - right_orig_section_index: usize, +pub(crate) fn section_name_eq( + left_obj: &Object, + right_obj: &Object, + left_section_index: usize, + right_section_index: usize, ) -> bool { - let Some(left_section) = - left_obj.sections.iter().find(|s| s.orig_index == left_orig_section_index) - else { - return false; - }; - let Some(right_section) = - right_obj.sections.iter().find(|s| s.orig_index == right_orig_section_index) - else { - return false; - }; - left_section.name == right_section.name + left_obj.sections.get(left_section_index).is_some_and(|left_section| { + right_obj + .sections + .get(right_section_index) + .is_some_and(|right_section| left_section.name == right_section.name) + }) } fn reloc_eq( - config: &DiffObjConfig, - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left_ins: Option<&ObjIns>, - right_ins: Option<&ObjIns>, + left_obj: &Object, + right_obj: &Object, + left_reloc: Option, + right_reloc: Option, + diff_config: &DiffObjConfig, ) -> bool { - let (Some(left_ins), Some(right_ins)) = (left_ins, right_ins) else { - return false; + let relax_reloc_diffs = diff_config.function_reloc_diffs == FunctionRelocDiffs::None; + let (left_reloc, right_reloc) = match (left_reloc, right_reloc) { + (Some(left_reloc), Some(right_reloc)) => (left_reloc, right_reloc), + // If relocations are relaxed, match if left is missing a reloc + (None, Some(_)) => return relax_reloc_diffs, + (None, None) => return true, + _ => return false, }; - let (Some(left), Some(right)) = (&left_ins.reloc, &right_ins.reloc) else { - return false; - }; - if left.flags != right.flags { + if left_reloc.relocation.flags != right_reloc.relocation.flags { return false; } - if config.function_reloc_diffs == FunctionRelocDiffs::None { + if relax_reloc_diffs { return true; } - let symbol_name_addend_matches = - left.target.name == right.target.name && left.addend == right.addend; - match (&left.target.orig_section_index, &right.target.orig_section_index) { + let symbol_name_addend_matches = left_reloc.symbol.name == right_reloc.symbol.name + && left_reloc.relocation.addend == right_reloc.relocation.addend; + match (&left_reloc.symbol.section, &right_reloc.symbol.section) { (Some(sl), Some(sr)) => { - // Match if section and name+addend or address match + // Match if section and name or address match section_name_eq(left_obj, right_obj, *sl, *sr) - && (config.function_reloc_diffs == FunctionRelocDiffs::DataValue + && (diff_config.function_reloc_diffs == FunctionRelocDiffs::DataValue || symbol_name_addend_matches - || address_eq(left, right)) - && (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress - || left.target.kind != ObjSymbolKind::Object - || left_obj.arch.display_ins_data_labels(left_ins) - == left_obj.arch.display_ins_data_labels(right_ins)) + || address_eq(&left_reloc, &right_reloc)) + && ( + diff_config.function_reloc_diffs == FunctionRelocDiffs::NameAddress + || left_reloc.symbol.kind != SymbolKind::Object + // TODO + // || left_obj.arch.display_ins_data_labels(left_ins) + // == left_obj.arch.display_ins_data_labels(right_ins)) + ) } (Some(_), None) => false, (None, Some(_)) => { // Match if possibly stripped weak symbol - symbol_name_addend_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak) + symbol_name_addend_matches && right_reloc.symbol.flags.contains(SymbolFlag::Weak) } (None, None) => symbol_name_addend_matches, } } fn arg_eq( - config: &DiffObjConfig, - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left: &ObjInsArg, - right: &ObjInsArg, - left_diff: &ObjInsDiff, - right_diff: &ObjInsDiff, + left_obj: &Object, + right_obj: &Object, + left_row: &InstructionDiffRow, + right_row: &InstructionDiffRow, + left_arg: &InstructionArg, + right_arg: &InstructionArg, + left_reloc: Option, + right_reloc: Option, + diff_config: &DiffObjConfig, ) -> bool { - match left { - ObjInsArg::PlainText(l) => match right { - ObjInsArg::PlainText(r) => l == r, - _ => false, - }, - ObjInsArg::Arg(l) => match right { - ObjInsArg::Arg(r) => l.loose_eq(r), + match left_arg { + InstructionArg::Value(l) => match right_arg { + InstructionArg::Value(r) => l.loose_eq(r), // If relocations are relaxed, match if left is a constant and right is a reloc // Useful for instances where the target object is created without relocations - ObjInsArg::Reloc => config.function_reloc_diffs == FunctionRelocDiffs::None, + InstructionArg::Reloc => diff_config.function_reloc_diffs == FunctionRelocDiffs::None, _ => false, }, - ObjInsArg::Reloc => { - matches!(right, ObjInsArg::Reloc) - && reloc_eq( - config, - left_obj, - right_obj, - left_diff.ins.as_ref(), - right_diff.ins.as_ref(), - ) + InstructionArg::Reloc => { + matches!(right_arg, InstructionArg::Reloc) + && reloc_eq(left_obj, right_obj, left_reloc, right_reloc, diff_config) } - ObjInsArg::BranchDest(_) => match right { + InstructionArg::BranchDest(_) => match right_arg { // Compare dest instruction idx after diffing - ObjInsArg::BranchDest(_) => { - left_diff.branch_to.as_ref().map(|b| b.ins_idx) - == right_diff.branch_to.as_ref().map(|b| b.ins_idx) + InstructionArg::BranchDest(_) => { + left_row.branch_to.as_ref().map(|b| b.ins_idx) + == right_row.branch_to.as_ref().map(|b| b.ins_idx) } // If relocations are relaxed, match if left is a constant and right is a reloc // Useful for instances where the target object is created without relocations - ObjInsArg::Reloc => config.function_reloc_diffs == FunctionRelocDiffs::None, + InstructionArg::Reloc => diff_config.function_reloc_diffs == FunctionRelocDiffs::None, _ => false, }, } } #[derive(Default)] -struct InsDiffState { - diff_count: usize, - left_arg_idx: usize, - right_arg_idx: usize, - left_args_idx: BTreeMap, - right_args_idx: BTreeMap, +struct InstructionDiffState { + diff_score: u64, + left_arg_idx: u32, + right_arg_idx: u32, + left_args_idx: BTreeMap, + right_args_idx: BTreeMap, } #[derive(Default)] -struct InsDiffResult { - kind: ObjInsDiffKind, - left_args_diff: Vec>, - right_args_diff: Vec>, +struct InstructionDiffResult { + kind: InstructionDiffKind, + left_args_diff: Vec, + right_args_diff: Vec, } -fn compare_ins( - config: &DiffObjConfig, - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left: &ObjInsDiff, - right: &ObjInsDiff, - state: &mut InsDiffState, -) -> Result { - let mut result = InsDiffResult::default(); - if let (Some(left_ins), Some(right_ins)) = (&left.ins, &right.ins) { - // Count only non-PlainText args - let left_args_count = left_ins.iter_args().count(); - let right_args_count = right_ins.iter_args().count(); - if left_args_count != right_args_count || left_ins.op != right_ins.op { - // Totally different op - result.kind = ObjInsDiffKind::Replace; - state.diff_count += 1; - return Ok(result); +impl InstructionDiffResult { + #[inline] + const fn new(kind: InstructionDiffKind) -> Self { + Self { kind, left_args_diff: Vec::new(), right_args_diff: Vec::new() } + } +} + +fn diff_instruction( + left_obj: &Object, + right_obj: &Object, + left_symbol_idx: usize, + right_symbol_idx: usize, + l: Option<&InstructionRef>, + r: Option<&InstructionRef>, + left_row: &InstructionDiffRow, + right_row: &InstructionDiffRow, + diff_config: &DiffObjConfig, + state: &mut InstructionDiffState, +) -> Result { + let (l, r) = match (l, r) { + (Some(l), Some(r)) => (l, r), + (Some(_), None) => { + state.diff_score += PENALTY_INSERT_DELETE; + return Ok(InstructionDiffResult::new(InstructionDiffKind::Delete)); } + (None, Some(_)) => { + state.diff_score += PENALTY_INSERT_DELETE; + return Ok(InstructionDiffResult::new(InstructionDiffKind::Insert)); + } + (None, None) => return Ok(InstructionDiffResult::new(InstructionDiffKind::None)), + }; + + // If opcodes don't match, replace + if l.opcode != r.opcode { + state.diff_score += PENALTY_REPLACE; + return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace)); + } + + let left_symbol = &left_obj.symbols[left_symbol_idx]; + let right_symbol = &right_obj.symbols[right_symbol_idx]; + let left_section = left_symbol + .section + .and_then(|i| left_obj.sections.get(i)) + .ok_or_else(|| anyhow!("Missing section for symbol"))?; + let right_section = right_symbol + .section + .and_then(|i| right_obj.sections.get(i)) + .ok_or_else(|| anyhow!("Missing section for symbol"))?; + + // Resolve relocations + let left_reloc = left_section.relocation_at(l.address, left_obj); + let right_reloc = right_section.relocation_at(r.address, right_obj); + + // Compare instruction data + let left_data = left_section.data_range(l.address, l.size as usize).ok_or_else(|| { + anyhow!( + "Instruction data out of bounds: {:#x}..{:#x}", + l.address, + l.address + l.size as u64 + ) + })?; + let right_data = right_section.data_range(r.address, r.size as usize).ok_or_else(|| { + anyhow!( + "Instruction data out of bounds: {:#x}..{:#x}", + r.address, + r.address + r.size as u64 + ) + })?; + if left_data != right_data { + // If data doesn't match, process instructions and compare args + let left_ins = left_obj.arch.process_instruction( + *l, + left_data, + left_reloc, + left_symbol.address..left_symbol.address + left_symbol.size, + left_symbol.section.unwrap(), + diff_config, + )?; + let right_ins = left_obj.arch.process_instruction( + *r, + right_data, + right_reloc, + right_symbol.address..right_symbol.address + right_symbol.size, + right_symbol.section.unwrap(), + diff_config, + )?; + if left_ins.args.len() != right_ins.args.len() { + state.diff_score += PENALTY_REPLACE; + return Ok(InstructionDiffResult::new(InstructionDiffKind::Replace)); + } + let mut result = InstructionDiffResult::new(InstructionDiffKind::None); if left_ins.mnemonic != right_ins.mnemonic { - // Same op but different mnemonic, still cmp args - result.kind = ObjInsDiffKind::OpMismatch; - state.diff_count += 1; + state.diff_score += PENALTY_REG_DIFF; + result.kind = InstructionDiffKind::OpMismatch; } - for (a, b) in left_ins.iter_args().zip(right_ins.iter_args()) { - if arg_eq(config, left_obj, right_obj, a, b, left, right) { - result.left_args_diff.push(None); - result.right_args_diff.push(None); + for (a, b) in left_ins.args.iter().zip(right_ins.args.iter()) { + if arg_eq( + left_obj, + right_obj, + left_row, + right_row, + a, + b, + left_reloc, + right_reloc, + diff_config, + ) { + result.left_args_diff.push(InstructionArgDiffIndex::NONE); + result.right_args_diff.push(InstructionArgDiffIndex::NONE); } else { - if result.kind == ObjInsDiffKind::None { - result.kind = ObjInsDiffKind::ArgMismatch; - state.diff_count += 1; + state.diff_score += if let InstructionArg::Value( + InstructionArgValue::Signed(_) | InstructionArgValue::Unsigned(_), + ) = a + { + PENALTY_IMM_DIFF + } else { + PENALTY_REG_DIFF + }; + if result.kind == InstructionDiffKind::None { + result.kind = InstructionDiffKind::ArgMismatch; } - let a_str = match a { - ObjInsArg::PlainText(arg) => arg.to_string(), - ObjInsArg::Arg(arg) => arg.to_string(), - ObjInsArg::Reloc => left_ins - .reloc - .as_ref() - .map_or_else(|| "".to_string(), |r| r.target.name.clone()), - ObjInsArg::BranchDest(arg) => arg.to_string(), + let a_str = arg_to_string(a, left_reloc.as_ref()); + let a_diff = match state.left_args_idx.entry(a_str) { + btree_map::Entry::Vacant(e) => { + let idx = state.left_arg_idx; + state.left_arg_idx = idx + 1; + e.insert(idx); + idx + } + btree_map::Entry::Occupied(e) => *e.get(), }; - let a_diff = if let Some(idx) = state.left_args_idx.get(&a_str) { - ObjInsArgDiff { idx: *idx } - } else { - let idx = state.left_arg_idx; - state.left_args_idx.insert(a_str, idx); - state.left_arg_idx += 1; - ObjInsArgDiff { idx } + let b_str = arg_to_string(b, right_reloc.as_ref()); + let b_diff = match state.right_args_idx.entry(b_str) { + btree_map::Entry::Vacant(e) => { + let idx = state.right_arg_idx; + state.right_arg_idx = idx + 1; + e.insert(idx); + idx + } + btree_map::Entry::Occupied(e) => *e.get(), }; - let b_str = match b { - ObjInsArg::PlainText(arg) => arg.to_string(), - ObjInsArg::Arg(arg) => arg.to_string(), - ObjInsArg::Reloc => right_ins - .reloc - .as_ref() - .map_or_else(|| "".to_string(), |r| r.target.name.clone()), - ObjInsArg::BranchDest(arg) => arg.to_string(), - }; - let b_diff = if let Some(idx) = state.right_args_idx.get(&b_str) { - ObjInsArgDiff { idx: *idx } - } else { - let idx = state.right_arg_idx; - state.right_args_idx.insert(b_str, idx); - state.right_arg_idx += 1; - ObjInsArgDiff { idx } - }; - result.left_args_diff.push(Some(a_diff)); - result.right_args_diff.push(Some(b_diff)); + result.left_args_diff.push(InstructionArgDiffIndex::new(a_diff)); + result.right_args_diff.push(InstructionArgDiffIndex::new(b_diff)); } } - } else if left.ins.is_some() { - result.kind = ObjInsDiffKind::Delete; - state.diff_count += 1; - } else { - result.kind = ObjInsDiffKind::Insert; - state.diff_count += 1; + return Ok(result); } - Ok(result) + + // Compare relocations + if !reloc_eq(left_obj, right_obj, left_reloc, right_reloc, diff_config) { + state.diff_score += PENALTY_REG_DIFF; + return Ok(InstructionDiffResult::new(InstructionDiffKind::ArgMismatch)); + } + + Ok(InstructionDiffResult::new(InstructionDiffKind::None)) } -fn find_symbol_matching_fake_symbol_in_sections( - fake_symbol: &ObjSymbol, - sections: &[ObjSection], -) -> Option { - let orig_section_index = fake_symbol.orig_section_index?; - let section = sections.iter().find(|s| s.orig_index == orig_section_index)?; - let real_symbol = section - .symbols - .iter() - .find(|s| s.size > 0 && (s.address..s.address + s.size).contains(&fake_symbol.address))?; - Some(real_symbol.clone()) -} +// TODO +// fn find_symbol_matching_fake_symbol_in_sections( +// fake_symbol: &ObjSymbol, +// sections: &[ObjSection], +// ) -> Option { +// let orig_section_index = fake_symbol.orig_section_index?; +// let section = sections.iter().find(|s| s.orig_index == orig_section_index)?; +// let real_symbol = section +// .symbols +// .iter() +// .find(|s| s.size > 0 && (s.address..s.address + s.size).contains(&fake_symbol.address))?; +// Some(real_symbol.clone()) +// } diff --git a/objdiff-core/src/diff/data.rs b/objdiff-core/src/diff/data.rs index f53f727..e56e75b 100644 --- a/objdiff-core/src/diff/data.rs +++ b/objdiff-core/src/diff/data.rs @@ -4,119 +4,130 @@ use core::{cmp::Ordering, ops::Range}; use anyhow::{anyhow, Result}; use similar::{capture_diff_slices, get_diff_ratio, Algorithm}; -use super::code::{address_eq, section_name_eq}; -use crate::{ - diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff, ObjSectionDiff, ObjSymbolDiff}, - obj::{ObjInfo, ObjReloc, ObjSection, ObjSymbolFlags, SymbolRef}, +use super::{ + code::{address_eq, section_name_eq}, + DataDiff, DataDiffKind, DataRelocationDiff, ObjectDiff, SectionDiff, SymbolDiff, }; +use crate::obj::{Object, Relocation, ResolvedRelocation, SymbolFlag, SymbolKind}; pub fn diff_bss_symbol( - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left_symbol_ref: SymbolRef, - right_symbol_ref: SymbolRef, -) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> { - let (_, left_symbol) = left_obj.section_symbol(left_symbol_ref); - let (_, right_symbol) = right_obj.section_symbol(right_symbol_ref); + left_obj: &Object, + right_obj: &Object, + left_symbol_ref: usize, + right_symbol_ref: usize, +) -> Result<(SymbolDiff, SymbolDiff)> { + let left_symbol = &left_obj.symbols[left_symbol_ref]; + let right_symbol = &right_obj.symbols[right_symbol_ref]; let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 }; Ok(( - ObjSymbolDiff { - symbol_ref: left_symbol_ref, + SymbolDiff { target_symbol: Some(right_symbol_ref), - instructions: vec![], match_percent: Some(percent), + diff_score: None, + instruction_rows: vec![], }, - ObjSymbolDiff { - symbol_ref: right_symbol_ref, + SymbolDiff { target_symbol: Some(left_symbol_ref), - instructions: vec![], match_percent: Some(percent), + diff_score: None, + instruction_rows: vec![], }, )) } -pub fn no_diff_symbol(_obj: &ObjInfo, symbol_ref: SymbolRef) -> ObjSymbolDiff { - ObjSymbolDiff { symbol_ref, target_symbol: None, instructions: vec![], match_percent: None } -} - -fn reloc_eq(left_obj: &ObjInfo, right_obj: &ObjInfo, left: &ObjReloc, right: &ObjReloc) -> bool { - if left.flags != right.flags { +fn reloc_eq( + left_obj: &Object, + right_obj: &Object, + left: &ResolvedRelocation, + right: &ResolvedRelocation, +) -> bool { + if left.relocation.flags != right.relocation.flags { return false; } let symbol_name_addend_matches = - left.target.name == right.target.name && left.addend == right.addend; - match (&left.target.orig_section_index, &right.target.orig_section_index) { + left.symbol.name == right.symbol.name && left.relocation.addend == right.relocation.addend; + match (left.symbol.section, right.symbol.section) { (Some(sl), Some(sr)) => { // Match if section and name+addend or address match - section_name_eq(left_obj, right_obj, *sl, *sr) + section_name_eq(left_obj, right_obj, sl, sr) && (symbol_name_addend_matches || address_eq(left, right)) } (Some(_), None) => false, (None, Some(_)) => { // Match if possibly stripped weak symbol - symbol_name_addend_matches && right.target.flags.0.contains(ObjSymbolFlags::Weak) + symbol_name_addend_matches && right.symbol.flags.contains(SymbolFlag::Weak) } (None, None) => symbol_name_addend_matches, } } +#[inline] +fn resolve_relocation<'obj>( + obj: &'obj Object, + reloc: &'obj Relocation, +) -> ResolvedRelocation<'obj> { + let symbol = &obj.symbols[reloc.target_symbol]; + ResolvedRelocation { relocation: reloc, symbol } +} + /// Compares relocations contained with a certain data range. -/// The ObjDataDiffKind for each diff will either be `None`` (if the relocation matches), +/// The DataDiffKind for each diff will either be `None`` (if the relocation matches), /// or `Replace` (if a relocation was changed, added, or removed). /// `Insert` and `Delete` are not used when a relocation is added or removed to avoid confusing diffs /// where it looks like the bytes themselves were changed but actually only the relocations changed. -fn diff_data_relocs_for_range( - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left: &ObjSection, - right: &ObjSection, +fn diff_data_relocs_for_range<'left, 'right>( + left_obj: &'left Object, + right_obj: &'right Object, + left_section_idx: usize, + right_section_idx: usize, left_range: Range, right_range: Range, -) -> Vec<(ObjDataDiffKind, Option, Option)> { +) -> Vec<(DataDiffKind, Option>, Option>)> { + let left_section = &left_obj.sections[left_section_idx]; + let right_section = &right_obj.sections[right_section_idx]; let mut diffs = Vec::new(); - for left_reloc in left.relocations.iter() { + for left_reloc in left_section.relocations.iter() { if !left_range.contains(&(left_reloc.address as usize)) { continue; } let left_offset = left_reloc.address as usize - left_range.start; - let Some(right_reloc) = right.relocations.iter().find(|r| { + let left_reloc = resolve_relocation(left_obj, left_reloc); + let Some(right_reloc) = right_section.relocations.iter().find(|r| { if !right_range.contains(&(r.address as usize)) { return false; } let right_offset = r.address as usize - right_range.start; right_offset == left_offset }) else { - diffs.push((ObjDataDiffKind::Delete, Some(left_reloc.clone()), None)); + diffs.push((DataDiffKind::Delete, Some(left_reloc), None)); continue; }; - if reloc_eq(left_obj, right_obj, left_reloc, right_reloc) { - diffs.push(( - ObjDataDiffKind::None, - Some(left_reloc.clone()), - Some(right_reloc.clone()), - )); + let right_reloc = resolve_relocation(right_obj, right_reloc); + if reloc_eq(left_obj, right_obj, &left_reloc, &right_reloc) { + diffs.push((DataDiffKind::None, Some(left_reloc), Some(right_reloc))); } else { diffs.push(( - ObjDataDiffKind::Replace, - Some(left_reloc.clone()), - Some(right_reloc.clone()), + DataDiffKind::Replace, + Some(left_reloc), + Some(right_reloc), )); } } - for right_reloc in right.relocations.iter() { + for right_reloc in right_section.relocations.iter() { if !right_range.contains(&(right_reloc.address as usize)) { continue; } let right_offset = right_reloc.address as usize - right_range.start; - let Some(_) = left.relocations.iter().find(|r| { + let right_reloc = resolve_relocation(right_obj, right_reloc); + let Some(_) = left_section.relocations.iter().find(|r| { if !left_range.contains(&(r.address as usize)) { return false; } let left_offset = r.address as usize - left_range.start; left_offset == right_offset }) else { - diffs.push((ObjDataDiffKind::Insert, None, Some(right_reloc.clone()))); + diffs.push((DataDiffKind::Insert, None, Some(right_reloc))); continue; }; // No need to check the cases for relocations being deleted or matching again. @@ -127,81 +138,103 @@ fn diff_data_relocs_for_range( /// Compare the data sections of two object files. pub fn diff_data_section( - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left: &ObjSection, - right: &ObjSection, - left_section_diff: &ObjSectionDiff, - right_section_diff: &ObjSectionDiff, -) -> Result<(ObjSectionDiff, ObjSectionDiff)> { - let left_max = - left.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(left.size); - let right_max = - right.symbols.iter().map(|s| s.section_address + s.size).max().unwrap_or(0).min(right.size); - let left_data = &left.data[..left_max as usize]; - let right_data = &right.data[..right_max as usize]; + left_obj: &Object, + right_obj: &Object, + left_diff: &ObjectDiff, + right_diff: &ObjectDiff, + left_section_idx: usize, + right_section_idx: usize, +) -> Result<(SectionDiff, SectionDiff)> { + let left_section = &left_obj.sections[left_section_idx]; + let right_section = &right_obj.sections[right_section_idx]; + let left_max = left_obj + .symbols + .iter() + .filter_map(|s| { + if s.section != Some(left_section_idx) || s.kind == SymbolKind::Section { + return None; + } + s.address.checked_sub(left_section.address).map(|a| a + s.size) + }) + .max() + .unwrap_or(0) + .min(left_section.size); + let right_max = right_obj + .symbols + .iter() + .filter_map(|s| { + if s.section != Some(right_section_idx) || s.kind == SymbolKind::Section { + return None; + } + s.address.checked_sub(right_section.address).map(|a| a + s.size) + }) + .max() + .unwrap_or(0) + .min(right_section.size); + let left_data = &left_section.data[..left_max as usize]; + let right_data = &right_section.data[..right_max as usize]; let ops = capture_diff_slices(Algorithm::Patience, left_data, right_data); let match_percent = get_diff_ratio(&ops, left_data.len(), right_data.len()) * 100.0; - let mut left_diff = Vec::::new(); - let mut right_diff = Vec::::new(); + let mut left_data_diff = Vec::::new(); + let mut right_data_diff = Vec::::new(); for op in ops { let (tag, left_range, right_range) = op.as_tag_tuple(); let left_len = left_range.len(); let right_len = right_range.len(); let mut len = left_len.max(right_len); let kind = match tag { - similar::DiffTag::Equal => ObjDataDiffKind::None, - similar::DiffTag::Delete => ObjDataDiffKind::Delete, - similar::DiffTag::Insert => ObjDataDiffKind::Insert, + similar::DiffTag::Equal => DataDiffKind::None, + similar::DiffTag::Delete => DataDiffKind::Delete, + similar::DiffTag::Insert => DataDiffKind::Insert, similar::DiffTag::Replace => { // Ensure replacements are equal length len = left_len.min(right_len); - ObjDataDiffKind::Replace + DataDiffKind::Replace } }; - let left_data = &left.data[left_range]; - let right_data = &right.data[right_range]; - left_diff.push(ObjDataDiff { + let left_data = &left_section.data[left_range]; + let right_data = &right_section.data[right_range]; + left_data_diff.push(DataDiff { data: left_data[..len.min(left_data.len())].to_vec(), kind, len, ..Default::default() }); - right_diff.push(ObjDataDiff { + right_data_diff.push(DataDiff { data: right_data[..len.min(right_data.len())].to_vec(), kind, len, ..Default::default() }); - if kind == ObjDataDiffKind::Replace { + if kind == DataDiffKind::Replace { match left_len.cmp(&right_len) { Ordering::Less => { let len = right_len - left_len; - left_diff.push(ObjDataDiff { + left_data_diff.push(DataDiff { data: vec![], - kind: ObjDataDiffKind::Insert, + kind: DataDiffKind::Insert, len, ..Default::default() }); - right_diff.push(ObjDataDiff { + right_data_diff.push(DataDiff { data: right_data[left_len..right_len].to_vec(), - kind: ObjDataDiffKind::Insert, + kind: DataDiffKind::Insert, len, ..Default::default() }); } Ordering::Greater => { let len = left_len - right_len; - left_diff.push(ObjDataDiff { + left_data_diff.push(DataDiff { data: left_data[right_len..left_len].to_vec(), - kind: ObjDataDiffKind::Delete, + kind: DataDiffKind::Delete, len, ..Default::default() }); - right_diff.push(ObjDataDiff { + right_data_diff.push(DataDiff { data: vec![], - kind: ObjDataDiffKind::Delete, + kind: DataDiffKind::Delete, len, ..Default::default() }); @@ -216,28 +249,36 @@ pub fn diff_data_section( for (diff_kind, left_reloc, right_reloc) in diff_data_relocs_for_range( left_obj, right_obj, - left, - right, + left_section_idx, + right_section_idx, 0..left_max as usize, 0..right_max as usize, ) { if let Some(left_reloc) = left_reloc { - let len = left_obj.arch.get_reloc_byte_size(left_reloc.flags); - let range = left_reloc.address as usize..left_reloc.address as usize + len; - left_reloc_diffs.push(ObjDataRelocDiff { reloc: left_reloc, kind: diff_kind, range }); + let len = left_obj.arch.get_reloc_byte_size(left_reloc.relocation.flags); + let range = left_reloc.relocation.address as usize + ..left_reloc.relocation.address as usize + len; + left_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range }); } if let Some(right_reloc) = right_reloc { - let len = right_obj.arch.get_reloc_byte_size(right_reloc.flags); - let range = right_reloc.address as usize..right_reloc.address as usize + len; - right_reloc_diffs.push(ObjDataRelocDiff { reloc: right_reloc, kind: diff_kind, range }); + let len = right_obj.arch.get_reloc_byte_size(right_reloc.relocation.flags); + let range = right_reloc.relocation.address as usize + ..right_reloc.relocation.address as usize + len; + right_reloc_diffs.push(DataRelocationDiff { kind: diff_kind, range }); } } - let (mut left_section_diff, mut right_section_diff) = - diff_generic_section(left, right, left_section_diff, right_section_diff)?; - let all_left_relocs_match = left_reloc_diffs.iter().all(|d| d.kind == ObjDataDiffKind::None); - left_section_diff.data_diff = left_diff; - right_section_diff.data_diff = right_diff; + let (mut left_section_diff, mut right_section_diff) = diff_generic_section( + left_obj, + right_obj, + left_diff, + right_diff, + left_section_idx, + right_section_idx, + )?; + let all_left_relocs_match = left_reloc_diffs.iter().all(|d| d.kind == DataDiffKind::None); + left_section_diff.data_diff = left_data_diff; + right_section_diff.data_diff = right_data_diff; left_section_diff.reloc_diff = left_reloc_diffs; right_section_diff.reloc_diff = right_reloc_diffs; if all_left_relocs_match { @@ -254,29 +295,58 @@ pub fn diff_data_section( } pub fn diff_data_symbol( - left_obj: &ObjInfo, - right_obj: &ObjInfo, - left_symbol_ref: SymbolRef, - right_symbol_ref: SymbolRef, -) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> { - let (left_section, left_symbol) = left_obj.section_symbol(left_symbol_ref); - let (right_section, right_symbol) = right_obj.section_symbol(right_symbol_ref); + left_obj: &Object, + right_obj: &Object, + left_symbol_idx: usize, + right_symbol_idx: usize, +) -> Result<(SymbolDiff, SymbolDiff)> { + let left_symbol = &left_obj.symbols[left_symbol_idx]; + let right_symbol = &right_obj.symbols[right_symbol_idx]; - let left_section = left_section.ok_or_else(|| anyhow!("Data symbol section not found"))?; - let right_section = right_section.ok_or_else(|| anyhow!("Data symbol section not found"))?; + let left_section_idx = + left_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?; + let right_section_idx = + right_symbol.section.ok_or_else(|| anyhow!("Data symbol section not found"))?; - let left_range = left_symbol.section_address as usize - ..(left_symbol.section_address + left_symbol.size) as usize; - let right_range = right_symbol.section_address as usize - ..(right_symbol.section_address + right_symbol.size) as usize; + let left_section = &left_obj.sections[left_section_idx]; + let right_section = &right_obj.sections[right_section_idx]; + + let left_start = left_symbol + .address + .checked_sub(left_section.address) + .ok_or_else(|| anyhow!("Symbol address out of section bounds"))?; + let right_start = right_symbol + .address + .checked_sub(right_section.address) + .ok_or_else(|| anyhow!("Symbol address out of section bounds"))?; + let left_end = left_start + left_symbol.size; + if left_end > left_section.size { + return Err(anyhow!( + "Symbol {} size out of section bounds ({} > {})", + left_symbol.name, + left_end, + left_section.size + )); + } + let right_end = right_start + right_symbol.size; + if right_end > right_section.size { + return Err(anyhow!( + "Symbol {} size out of section bounds ({} > {})", + right_symbol.name, + right_end, + right_section.size + )); + } + let left_range = left_start as usize..left_end as usize; + let right_range = right_start as usize..right_end as usize; let left_data = &left_section.data[left_range.clone()]; let right_data = &right_section.data[right_range.clone()]; let reloc_diffs = diff_data_relocs_for_range( left_obj, right_obj, - left_section, - right_section, + left_section_idx, + right_section_idx, left_range, right_range, ); @@ -291,11 +361,15 @@ pub fn diff_data_symbol( for (diff_kind, left_reloc, right_reloc) in reloc_diffs { let reloc_diff_len = match (left_reloc, right_reloc) { (None, None) => unreachable!(), - (None, Some(right_reloc)) => right_obj.arch.get_reloc_byte_size(right_reloc.flags), - (Some(left_reloc), _) => left_obj.arch.get_reloc_byte_size(left_reloc.flags), + (None, Some(right_reloc)) => { + right_obj.arch.get_reloc_byte_size(right_reloc.relocation.flags) + } + (Some(left_reloc), _) => { + left_obj.arch.get_reloc_byte_size(left_reloc.relocation.flags) + } }; total_reloc_bytes += reloc_diff_len; - if diff_kind == ObjDataDiffKind::None { + if diff_kind == DataDiffKind::None { matching_reloc_bytes += reloc_diff_len; } } @@ -315,17 +389,17 @@ pub fn diff_data_symbol( let match_percent = match_ratio * 100.0; Ok(( - ObjSymbolDiff { - symbol_ref: left_symbol_ref, - target_symbol: Some(right_symbol_ref), - instructions: vec![], + SymbolDiff { + target_symbol: Some(right_symbol_idx), match_percent: Some(match_percent), + diff_score: None, + instruction_rows: vec![], }, - ObjSymbolDiff { - symbol_ref: right_symbol_ref, - target_symbol: Some(left_symbol_ref), - instructions: vec![], + SymbolDiff { + target_symbol: Some(left_symbol_idx), match_percent: Some(match_percent), + diff_score: None, + instruction_rows: vec![], }, )) } @@ -333,69 +407,89 @@ pub fn diff_data_symbol( /// Compares a section of two object files. /// This essentially adds up the match percentage of each symbol in the section. pub fn diff_generic_section( - left: &ObjSection, - _right: &ObjSection, - left_diff: &ObjSectionDiff, - _right_diff: &ObjSectionDiff, -) -> Result<(ObjSectionDiff, ObjSectionDiff)> { - let match_percent = if left_diff.symbols.iter().all(|d| d.match_percent == Some(100.0)) { + left_obj: &Object, + _right_obj: &Object, + left_diff: &ObjectDiff, + _right_diff: &ObjectDiff, + left_section_idx: usize, + _right_section_idx: usize, +) -> Result<(SectionDiff, SectionDiff)> { + let match_percent = if left_obj + .symbols + .iter() + .enumerate() + .filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section) + .map(|(i, _)| &left_diff.symbols[i]) + .all(|d| d.match_percent == Some(100.0)) + { 100.0 // Avoid fp precision issues } else { - left.symbols + let (matched, total) = left_obj + .symbols .iter() - .zip(left_diff.symbols.iter()) - .map(|(s, d)| d.match_percent.unwrap_or(0.0) * s.size as f32) - .sum::() - / left.size as f32 + .enumerate() + .filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section) + .map(|(i, s)| (s, &left_diff.symbols[i])) + .fold((0.0, 0.0), |(matched, total), (s, d)| { + (matched + d.match_percent.unwrap_or(0.0) * s.size as f32, total + s.size as f32) + }); + if total == 0.0 { + 100.0 + } else { + matched / total + } }; Ok(( - ObjSectionDiff { - symbols: vec![], - data_diff: vec![], - reloc_diff: vec![], - match_percent: Some(match_percent), - }, - ObjSectionDiff { - symbols: vec![], - data_diff: vec![], - reloc_diff: vec![], - match_percent: Some(match_percent), - }, + SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] }, + SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] }, )) } /// Compare the addresses and sizes of each symbol in the BSS sections. pub fn diff_bss_section( - left: &ObjSection, - right: &ObjSection, - left_diff: &ObjSectionDiff, - right_diff: &ObjSectionDiff, -) -> Result<(ObjSectionDiff, ObjSectionDiff)> { - let left_sizes = left.symbols.iter().map(|s| (s.section_address, s.size)).collect::>(); - let right_sizes = right.symbols.iter().map(|s| (s.section_address, s.size)).collect::>(); + left_obj: &Object, + right_obj: &Object, + left_diff: &ObjectDiff, + right_diff: &ObjectDiff, + left_section_idx: usize, + right_section_idx: usize, +) -> Result<(SectionDiff, SectionDiff)> { + let left_section = &left_obj.sections[left_section_idx]; + let left_sizes = left_obj + .symbols + .iter() + .enumerate() + .filter(|(_, s)| s.section == Some(left_section_idx) && s.kind != SymbolKind::Section) + .filter_map(|(_, s)| s.address.checked_sub(left_section.address).map(|a| (a, s.size))) + .collect::>(); + let right_section = &right_obj.sections[right_section_idx]; + let right_sizes = right_obj + .symbols + .iter() + .enumerate() + .filter(|(_, s)| s.section == Some(right_section_idx) && s.kind != SymbolKind::Section) + .filter_map(|(_, s)| s.address.checked_sub(right_section.address).map(|a| (a, s.size))) + .collect::>(); let ops = capture_diff_slices(Algorithm::Patience, &left_sizes, &right_sizes); let mut match_percent = get_diff_ratio(&ops, left_sizes.len(), right_sizes.len()) * 100.0; // Use the highest match percent between two options: // - Left symbols matching right symbols by name // - Diff of the addresses and sizes of each symbol - let (generic_diff, _) = diff_generic_section(left, right, left_diff, right_diff)?; + let (generic_diff, _) = diff_generic_section( + left_obj, + right_obj, + left_diff, + right_diff, + left_section_idx, + right_section_idx, + )?; if generic_diff.match_percent.unwrap_or(-1.0) > match_percent { match_percent = generic_diff.match_percent.unwrap(); } Ok(( - ObjSectionDiff { - symbols: vec![], - data_diff: vec![], - reloc_diff: vec![], - match_percent: Some(match_percent), - }, - ObjSectionDiff { - symbols: vec![], - data_diff: vec![], - reloc_diff: vec![], - match_percent: Some(match_percent), - }, + SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] }, + SectionDiff { match_percent: Some(match_percent), data_diff: vec![], reloc_diff: vec![] }, )) } diff --git a/objdiff-core/src/diff/display.rs b/objdiff-core/src/diff/display.rs index 559ef69..6a9bcae 100644 --- a/objdiff-core/src/diff/display.rs +++ b/objdiff-core/src/diff/display.rs @@ -1,16 +1,28 @@ -use alloc::string::{String, ToString}; +use alloc::{ + borrow::Cow, + collections::BTreeSet, + format, + string::{String, ToString}, + vec::Vec, +}; +use core::cmp::Ordering; + +use anyhow::Result; +use itertools::Itertools; +use regex::Regex; use crate::{ - diff::{ObjInsArgDiff, ObjInsDiff}, - obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol}, + diff::{DiffObjConfig, InstructionArgDiffIndex, InstructionDiffRow, ObjectDiff, SymbolDiff}, + obj::{ + InstructionArg, InstructionArgValue, Object, SectionFlag, SectionKind, Symbol, SymbolFlag, + SymbolKind, + }, }; #[derive(Debug, Copy, Clone)] pub enum DiffText<'a> { /// Basic text Basic(&'a str), - /// Colored text - BasicColor(&'a str, usize), /// Line number Line(u32), /// Instruction address @@ -18,13 +30,13 @@ pub enum DiffText<'a> { /// Instruction mnemonic Opcode(&'a str, u16), /// Instruction argument - Argument(&'a ObjInsArgValue, Option<&'a ObjInsArgDiff>), + Argument(&'a InstructionArgValue), /// Branch destination - BranchDest(u64, Option<&'a ObjInsArgDiff>), + BranchDest(u64), /// Symbol name - Symbol(&'a ObjSymbol, Option<&'a ObjInsArgDiff>), + Symbol(&'a Symbol), /// Relocation addend - Addend(i64, Option<&'a ObjInsArgDiff>), + Addend(i64), /// Number of spaces Spacing(usize), /// End of line @@ -36,83 +48,113 @@ pub enum HighlightKind { #[default] None, Opcode(u16), - Arg(ObjInsArgValue), + Argument(InstructionArgValue), Symbol(String), Address(u64), } -pub fn display_diff( - ins_diff: &ObjInsDiff, - base_addr: u64, - mut cb: impl FnMut(DiffText) -> Result<(), E>, -) -> Result<(), E> { - let Some(ins) = &ins_diff.ins else { - cb(DiffText::Eol)?; - return Ok(()); - }; - if let Some(line) = ins.line { - cb(DiffText::Line(line))?; - } - cb(DiffText::Address(ins.address - base_addr))?; - if let Some(branch) = &ins_diff.branch_from { - cb(DiffText::BasicColor(" ~> ", branch.branch_idx))?; - } else { - cb(DiffText::Spacing(4))?; - } - cb(DiffText::Opcode(&ins.mnemonic, ins.op))?; - let mut arg_diff_idx = 0; // non-PlainText index - for (i, arg) in ins.args.iter().enumerate() { - if i == 0 { - cb(DiffText::Spacing(1))?; - } - let diff = ins_diff.arg_diff.get(arg_diff_idx).and_then(|o| o.as_ref()); - match arg { - ObjInsArg::PlainText(s) => { - cb(DiffText::Basic(s))?; - } - ObjInsArg::Arg(v) => { - cb(DiffText::Argument(v, diff))?; - arg_diff_idx += 1; - } - ObjInsArg::Reloc => { - display_reloc_name(ins.reloc.as_ref().unwrap(), &mut cb, diff)?; - arg_diff_idx += 1; - } - ObjInsArg::BranchDest(dest) => { - if let Some(dest) = dest.checked_sub(base_addr) { - cb(DiffText::BranchDest(dest, diff))?; - } else { - cb(DiffText::Basic(""))?; - } - arg_diff_idx += 1; - } - } - } - if let Some(branch) = &ins_diff.branch_to { - cb(DiffText::BasicColor(" ~>", branch.branch_idx))?; - } - cb(DiffText::Eol)?; - Ok(()) +pub enum InstructionPart { + Basic(&'static str), + Opcode(Cow<'static, str>, u16), + Arg(InstructionArg), + Separator, } -fn display_reloc_name( - reloc: &ObjReloc, - mut cb: impl FnMut(DiffText) -> Result<(), E>, - diff: Option<&ObjInsArgDiff>, -) -> Result<(), E> { - cb(DiffText::Symbol(&reloc.target, diff))?; - cb(DiffText::Addend(reloc.addend, diff)) +pub fn display_row( + obj: &Object, + symbol_index: usize, + ins_row: &InstructionDiffRow, + diff_config: &DiffObjConfig, + mut cb: impl FnMut(DiffText, InstructionArgDiffIndex) -> Result<()>, +) -> Result<()> { + let Some(ins_ref) = ins_row.ins_ref else { + cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?; + return Ok(()); + }; + let symbol = &obj.symbols[symbol_index]; + let Some(section_index) = symbol.section else { + cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?; + return Ok(()); + }; + let section = &obj.sections[section_index]; + let Some(data) = section.data_range(ins_ref.address, ins_ref.size as usize) else { + cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?; + return Ok(()); + }; + if let Some(line) = section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b) { + cb(DiffText::Line(line), InstructionArgDiffIndex::NONE)?; + } + cb( + DiffText::Address(ins_ref.address.saturating_sub(symbol.address)), + InstructionArgDiffIndex::NONE, + )?; + if let Some(branch) = &ins_row.branch_from { + cb(DiffText::Basic(" ~> "), InstructionArgDiffIndex::new(branch.branch_idx))?; + } else { + cb(DiffText::Spacing(4), InstructionArgDiffIndex::NONE)?; + } + let mut arg_idx = 0; + let relocation = section.relocation_at(ins_ref.address, obj); + obj.arch.display_instruction( + ins_ref, + data, + relocation, + symbol.address..symbol.address + symbol.size, + section_index, + diff_config, + &mut |part| match part { + InstructionPart::Basic(text) => { + cb(DiffText::Basic(text), InstructionArgDiffIndex::NONE) + } + InstructionPart::Opcode(mnemonic, opcode) => { + cb(DiffText::Opcode(mnemonic.as_ref(), opcode), InstructionArgDiffIndex::NONE) + } + InstructionPart::Arg(arg) => { + let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default(); + arg_idx += 1; + match arg { + InstructionArg::Value(ref value) => cb(DiffText::Argument(value), diff_index), + InstructionArg::Reloc => { + let resolved = relocation.unwrap(); + cb(DiffText::Symbol(resolved.symbol), diff_index)?; + if resolved.relocation.addend != 0 { + cb(DiffText::Addend(resolved.relocation.addend), diff_index)?; + } + Ok(()) + } + InstructionArg::BranchDest(dest) => { + if let Some(addr) = dest.checked_sub(symbol.address) { + cb(DiffText::BranchDest(addr), diff_index) + } else { + cb( + DiffText::Argument(&InstructionArgValue::Opaque(Cow::Borrowed( + "", + ))), + diff_index, + ) + } + } + } + } + InstructionPart::Separator => { + cb(DiffText::Basic(diff_config.separator()), InstructionArgDiffIndex::NONE) + } + }, + )?; + if let Some(branch) = &ins_row.branch_to { + cb(DiffText::Basic(" ~>"), InstructionArgDiffIndex::new(branch.branch_idx))?; + } + cb(DiffText::Eol, InstructionArgDiffIndex::NONE)?; + Ok(()) } impl PartialEq> for HighlightKind { fn eq(&self, other: &DiffText) -> bool { match (self, other) { (HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b, - (HighlightKind::Arg(a), DiffText::Argument(b, _)) => a.loose_eq(b), - (HighlightKind::Symbol(a), DiffText::Symbol(b, _)) => a == &b.name, - (HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b, _)) => { - a == b - } + (HighlightKind::Argument(a), DiffText::Argument(b)) => a.loose_eq(b), + (HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name, + (HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b, _ => false, } } @@ -126,10 +168,245 @@ impl From> for HighlightKind { fn from(value: DiffText<'_>) -> Self { match value { DiffText::Opcode(_, op) => HighlightKind::Opcode(op), - DiffText::Argument(arg, _) => HighlightKind::Arg(arg.clone()), - DiffText::Symbol(sym, _) => HighlightKind::Symbol(sym.name.to_string()), - DiffText::Address(addr) | DiffText::BranchDest(addr, _) => HighlightKind::Address(addr), + DiffText::Argument(arg) => HighlightKind::Argument(arg.clone()), + DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()), + DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(addr), _ => HighlightKind::None, } } } + +pub enum ContextMenuItem { + Copy { value: String, label: Option }, + Navigate { label: String }, +} + +pub enum HoverItemColor { + Normal, // Gray + Emphasized, // White + Special, // Blue +} + +pub struct HoverItem { + pub text: String, + pub color: HoverItemColor, +} + +pub fn symbol_context(_obj: &Object, symbol: &Symbol) -> Vec { + let mut out = Vec::new(); + if let Some(name) = &symbol.demangled_name { + out.push(ContextMenuItem::Copy { value: name.clone(), label: None }); + } + out.push(ContextMenuItem::Copy { value: symbol.name.clone(), label: None }); + if let Some(address) = symbol.virtual_address { + out.push(ContextMenuItem::Copy { + value: format!("{:#x}", address), + label: Some("virtual address".to_string()), + }); + } + // if let Some(_extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) { + // out.push(ContextMenuItem::Navigate { label: "Decode exception table".to_string() }); + // } + out +} + +pub fn symbol_hover(_obj: &Object, symbol: &Symbol) -> Vec { + let mut out = Vec::new(); + out.push(HoverItem { + text: format!("Name: {}", symbol.name), + color: HoverItemColor::Emphasized, + }); + out.push(HoverItem { + text: format!("Address: {:x}", symbol.address), + color: HoverItemColor::Emphasized, + }); + if symbol.flags.contains(SymbolFlag::SizeInferred) { + out.push(HoverItem { + text: format!("Size: {:x} (inferred)", symbol.size), + color: HoverItemColor::Emphasized, + }); + } else { + out.push(HoverItem { + text: format!("Size: {:x}", symbol.size), + color: HoverItemColor::Emphasized, + }); + } + if let Some(address) = symbol.virtual_address { + out.push(HoverItem { + text: format!("Virtual address: {:#x}", address), + color: HoverItemColor::Special, + }); + } + // if let Some(extab) = obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) { + // out.push(HoverItem { + // text: format!("extab symbol: {}", extab.etb_symbol.name), + // color: HoverItemColor::Special, + // }); + // out.push(HoverItem { + // text: format!("extabindex symbol: {}", extab.eti_symbol.name), + // color: HoverItemColor::Special, + // }); + // } + out +} + +#[derive(Copy, Clone)] +pub enum SymbolFilter<'a> { + None, + Search(&'a Regex), + Mapping(usize, Option<&'a Regex>), +} + +fn symbol_matches_filter( + symbol: &Symbol, + diff: &SymbolDiff, + filter: SymbolFilter<'_>, + show_hidden_symbols: bool, +) -> bool { + // Ignore absolute symbols + if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) { + return false; + } + if !show_hidden_symbols && symbol.flags.contains(SymbolFlag::Hidden) { + return false; + } + match filter { + SymbolFilter::None => true, + SymbolFilter::Search(regex) => { + regex.is_match(&symbol.name) + || symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s)) + } + SymbolFilter::Mapping(symbol_ref, regex) => { + diff.target_symbol == Some(symbol_ref) + && regex.is_none_or(|r| { + r.is_match(&symbol.name) + || symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s)) + }) + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct SectionDisplaySymbol { + pub symbol: usize, + pub is_mapping_symbol: bool, +} + +#[derive(Debug, Clone)] +pub struct SectionDisplay { + pub id: String, + pub name: String, + pub size: u64, + pub match_percent: Option, + pub symbols: Vec, +} + +pub fn display_sections( + obj: &Object, + diff: &ObjectDiff, + filter: SymbolFilter<'_>, + show_hidden_symbols: bool, + show_mapped_symbols: bool, + reverse_fn_order: bool, +) -> Vec { + let mut mapping = BTreeSet::new(); + let is_mapping_symbol = if let SymbolFilter::Mapping(_, _) = filter { + for mapping_diff in &diff.mapping_symbols { + let symbol = &obj.symbols[mapping_diff.symbol_index]; + if !symbol_matches_filter( + symbol, + &mapping_diff.symbol_diff, + filter, + show_hidden_symbols, + ) { + continue; + } + if !show_mapped_symbols { + let symbol_diff = &diff.symbols[mapping_diff.symbol_index]; + if symbol_diff.target_symbol.is_some() { + continue; + } + } + mapping.insert((symbol.section, mapping_diff.symbol_index)); + } + true + } else { + for (symbol_idx, (symbol, symbol_diff)) in obj.symbols.iter().zip(&diff.symbols).enumerate() + { + if !symbol_matches_filter(symbol, symbol_diff, filter, show_hidden_symbols) { + continue; + } + mapping.insert((symbol.section, symbol_idx)); + } + false + }; + let num_sections = mapping.iter().map(|(section_idx, _)| *section_idx).dedup().count(); + let mut sections = Vec::with_capacity(num_sections); + for (section_idx, group) in &mapping.iter().chunk_by(|(section_idx, _)| *section_idx) { + let mut symbols = group + .map(|&(_, symbol)| SectionDisplaySymbol { symbol, is_mapping_symbol }) + .collect::>(); + if let Some(section_idx) = section_idx { + let section = &obj.sections[section_idx]; + if section.kind == SectionKind::Unknown || section.flags.contains(SectionFlag::Hidden) { + // Skip unknown and hidden sections + continue; + } + let section_diff = &diff.sections[section_idx]; + if section.kind == SectionKind::Code && reverse_fn_order { + symbols.sort_by(|a, b| { + let a_symbol = &obj.symbols[a.symbol]; + let b_symbol = &obj.symbols[b.symbol]; + symbol_sort_reverse(a_symbol, b_symbol) + }); + } else { + symbols.sort_by(|a, b| { + let a_symbol = &obj.symbols[a.symbol]; + let b_symbol = &obj.symbols[b.symbol]; + symbol_sort(a_symbol, b_symbol) + }); + } + sections.push(SectionDisplay { + id: section.id.clone(), + name: if section.flags.contains(SectionFlag::Combined) { + format!("{} [combined]", section.name) + } else { + section.name.clone() + }, + size: section.size, + match_percent: section_diff.match_percent, + symbols, + }); + } else { + // Don't sort, preserve order of absolute symbols + sections.push(SectionDisplay { + id: ".comm".to_string(), + name: ".comm".to_string(), + size: 0, + match_percent: None, + symbols, + }); + } + } + sections.sort_by(|a, b| a.id.cmp(&b.id)); + sections +} + +fn section_symbol_sort(a: &Symbol, b: &Symbol) -> Ordering { + if a.kind == SymbolKind::Section { + if b.kind != SymbolKind::Section { + return Ordering::Less; + } + } else if b.kind == SymbolKind::Section { + return Ordering::Greater; + } + Ordering::Equal +} + +fn symbol_sort(a: &Symbol, b: &Symbol) -> Ordering { + section_symbol_sort(a, b).then(a.address.cmp(&b.address)).then(a.size.cmp(&b.size)) +} + +fn symbol_sort_reverse(a: &Symbol, b: &Symbol) -> Ordering { + section_symbol_sort(a, b).then(b.address.cmp(&a.address)).then(b.size.cmp(&a.size)) +} diff --git a/objdiff-core/src/diff/mod.rs b/objdiff-core/src/diff/mod.rs index 1f86547..117d383 100644 --- a/objdiff-core/src/diff/mod.rs +++ b/objdiff-core/src/diff/mod.rs @@ -4,21 +4,19 @@ use alloc::{ vec, vec::Vec, }; -use core::ops::Range; +use core::{num::NonZeroU32, ops::Range}; use anyhow::Result; use crate::{ diff::{ - code::{diff_code, no_diff_code, process_code_symbol}, + code::{diff_code, no_diff_code}, data::{ diff_bss_section, diff_bss_symbol, diff_data_section, diff_data_symbol, - diff_generic_section, no_diff_symbol, + diff_generic_section, }, }, - obj::{ - ObjInfo, ObjIns, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef, SECTION_COMMON, - }, + obj::{InstructionRef, Object, SectionKind, Symbol, SymbolFlag}, }; pub mod code; @@ -38,47 +36,44 @@ impl DiffObjConfig { } #[derive(Debug, Clone)] -pub struct ObjSectionDiff { - pub symbols: Vec, - pub data_diff: Vec, - pub reloc_diff: Vec, +pub struct SectionDiff { + // pub target_section: Option, pub match_percent: Option, -} - -impl ObjSectionDiff { - fn merge(&mut self, other: ObjSectionDiff) { - // symbols ignored - self.data_diff = other.data_diff; - self.reloc_diff = other.reloc_diff; - self.match_percent = other.match_percent; - } + pub data_diff: Vec, + pub reloc_diff: Vec, } #[derive(Debug, Clone, Default)] -pub struct ObjSymbolDiff { - /// The symbol ref this object - pub symbol_ref: SymbolRef, - /// The symbol ref in the _other_ object that this symbol was diffed against - pub target_symbol: Option, - pub instructions: Vec, +pub struct SymbolDiff { + /// The symbol index in the _other_ object that this symbol was diffed against + pub target_symbol: Option, pub match_percent: Option, + pub diff_score: Option<(u64, u64)>, + pub instruction_rows: Vec, } #[derive(Debug, Clone, Default)] -pub struct ObjInsDiff { - pub ins: Option, +pub struct MappingSymbolDiff { + pub symbol_index: usize, + pub symbol_diff: SymbolDiff, +} + +#[derive(Debug, Clone, Default)] +pub struct InstructionDiffRow { + /// Instruction reference + pub ins_ref: Option, /// Diff kind - pub kind: ObjInsDiffKind, - /// Branches from instruction - pub branch_from: Option, + pub kind: InstructionDiffKind, + /// Branches from instruction(s) + pub branch_from: Option, /// Branches to instruction - pub branch_to: Option, - /// Arg diffs (only contains non-PlainText args) - pub arg_diff: Vec>, + pub branch_to: Option, + /// Arg diffs + pub arg_diff: Vec, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] -pub enum ObjInsDiffKind { +pub enum InstructionDiffKind { #[default] None, OpMismatch, @@ -89,22 +84,21 @@ pub enum ObjInsDiffKind { } #[derive(Debug, Clone, Default)] -pub struct ObjDataDiff { +pub struct DataDiff { pub data: Vec, - pub kind: ObjDataDiffKind, + pub kind: DataDiffKind, pub len: usize, pub symbol: String, } #[derive(Debug, Clone)] -pub struct ObjDataRelocDiff { - pub reloc: ObjReloc, - pub kind: ObjDataDiffKind, +pub struct DataRelocationDiff { + pub kind: DataDiffKind, pub range: Range, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] -pub enum ObjDataDiffKind { +pub enum DataDiffKind { #[default] None, Replace, @@ -112,127 +106,102 @@ pub enum ObjDataDiffKind { Insert, } -#[derive(Debug, Copy, Clone)] -pub struct ObjInsArgDiff { - /// Incrementing index for coloring - pub idx: usize, +/// Index of the argument diff for coloring. +#[repr(transparent)] +#[derive(Debug, Copy, Clone, Default)] +pub struct InstructionArgDiffIndex(pub Option); + +impl InstructionArgDiffIndex { + pub const NONE: Self = Self(None); + + #[inline(always)] + pub fn new(idx: u32) -> Self { + Self(Some(unsafe { NonZeroU32::new_unchecked(idx.saturating_add(1)) })) + } + + #[inline(always)] + pub fn get(&self) -> Option { self.0.map(|idx| idx.get() - 1) } + + #[inline(always)] + pub fn is_some(&self) -> bool { self.0.is_some() } + + #[inline(always)] + pub fn is_none(&self) -> bool { self.0.is_none() } } #[derive(Debug, Clone)] -pub struct ObjInsBranchFrom { +pub struct InstructionBranchFrom { /// Source instruction indices - pub ins_idx: Vec, + pub ins_idx: Vec, /// Incrementing index for coloring - pub branch_idx: usize, + pub branch_idx: u32, } #[derive(Debug, Clone)] -pub struct ObjInsBranchTo { +pub struct InstructionBranchTo { /// Target instruction index - pub ins_idx: usize, + pub ins_idx: u32, /// Incrementing index for coloring - pub branch_idx: usize, + pub branch_idx: u32, } -#[derive(Default)] -pub struct ObjDiff { +#[derive(Debug, Default)] +pub struct ObjectDiff { + /// A list of all symbol diffs in the object. + pub symbols: Vec, /// A list of all section diffs in the object. - pub sections: Vec, - /// Common BSS symbols don't live in a section, so they're stored separately. - pub common: Vec, + pub sections: Vec, /// If `selecting_left` or `selecting_right` is set, this is the list of symbols /// that are being mapped to the other object. - pub mapping_symbols: Vec, + pub mapping_symbols: Vec, } -impl ObjDiff { - pub fn new_from_obj(obj: &ObjInfo) -> Self { +impl ObjectDiff { + pub fn new_from_obj(obj: &Object) -> Self { let mut result = Self { + symbols: Vec::with_capacity(obj.symbols.len()), sections: Vec::with_capacity(obj.sections.len()), - common: Vec::with_capacity(obj.common.len()), mapping_symbols: vec![], }; - for (section_idx, section) in obj.sections.iter().enumerate() { - let mut symbols = Vec::with_capacity(section.symbols.len()); - for (symbol_idx, _) in section.symbols.iter().enumerate() { - symbols.push(ObjSymbolDiff { - symbol_ref: SymbolRef { section_idx, symbol_idx }, - target_symbol: None, - instructions: vec![], - match_percent: None, - }); - } - result.sections.push(ObjSectionDiff { - symbols, - data_diff: vec![ObjDataDiff { - data: section.data.clone(), - kind: ObjDataDiffKind::None, - len: section.data.len(), - symbol: section.name.clone(), - }], - reloc_diff: vec![], + for _ in obj.symbols.iter() { + result.symbols.push(SymbolDiff { + target_symbol: None, match_percent: None, + diff_score: None, + instruction_rows: vec![], }); } - for (symbol_idx, _) in obj.common.iter().enumerate() { - result.common.push(ObjSymbolDiff { - symbol_ref: SymbolRef { section_idx: SECTION_COMMON, symbol_idx }, - target_symbol: None, - instructions: vec![], + for _ in obj.sections.iter() { + result.sections.push(SectionDiff { + // target_section: None, match_percent: None, + data_diff: vec![], + reloc_diff: vec![], }); } result } - - #[inline] - pub fn section_diff(&self, section_idx: usize) -> &ObjSectionDiff { - &self.sections[section_idx] - } - - #[inline] - pub fn section_diff_mut(&mut self, section_idx: usize) -> &mut ObjSectionDiff { - &mut self.sections[section_idx] - } - - #[inline] - pub fn symbol_diff(&self, symbol_ref: SymbolRef) -> &ObjSymbolDiff { - if symbol_ref.section_idx == SECTION_COMMON { - &self.common[symbol_ref.symbol_idx] - } else { - &self.section_diff(symbol_ref.section_idx).symbols[symbol_ref.symbol_idx] - } - } - - #[inline] - pub fn symbol_diff_mut(&mut self, symbol_ref: SymbolRef) -> &mut ObjSymbolDiff { - if symbol_ref.section_idx == SECTION_COMMON { - &mut self.common[symbol_ref.symbol_idx] - } else { - &mut self.section_diff_mut(symbol_ref.section_idx).symbols[symbol_ref.symbol_idx] - } - } } -#[derive(Default)] +#[derive(Debug, Default)] pub struct DiffObjsResult { - pub left: Option, - pub right: Option, - pub prev: Option, + pub left: Option, + pub right: Option, + pub prev: Option, } pub fn diff_objs( + left: Option<&Object>, + right: Option<&Object>, + prev: Option<&Object>, diff_config: &DiffObjConfig, mapping_config: &MappingConfig, - left: Option<&ObjInfo>, - right: Option<&ObjInfo>, - prev: Option<&ObjInfo>, ) -> Result { let symbol_matches = matching_symbols(left, right, prev, mapping_config)?; let section_matches = matching_sections(left, right)?; - let mut left = left.map(|p| (p, ObjDiff::new_from_obj(p))); - let mut right = right.map(|p| (p, ObjDiff::new_from_obj(p))); - let mut prev = prev.map(|p| (p, ObjDiff::new_from_obj(p))); + let mut left = left.map(|p| (p, ObjectDiff::new_from_obj(p))); + let mut right = right.map(|p| (p, ObjectDiff::new_from_obj(p))); + let mut prev = prev.map(|p| (p, ObjectDiff::new_from_obj(p))); for symbol_match in symbol_matches { match symbol_match { @@ -245,87 +214,76 @@ pub fn diff_objs( let (left_obj, left_out) = left.as_mut().unwrap(); let (right_obj, right_out) = right.as_mut().unwrap(); match section_kind { - ObjSectionKind::Code => { - let left_code = - process_code_symbol(left_obj, left_symbol_ref, diff_config)?; - let right_code = - process_code_symbol(right_obj, right_symbol_ref, diff_config)?; + SectionKind::Code => { let (left_diff, right_diff) = diff_code( left_obj, right_obj, - &left_code, - &right_code, left_symbol_ref, right_symbol_ref, diff_config, )?; - *left_out.symbol_diff_mut(left_symbol_ref) = left_diff; - *right_out.symbol_diff_mut(right_symbol_ref) = right_diff; + left_out.symbols[left_symbol_ref] = left_diff; + right_out.symbols[right_symbol_ref] = right_diff; if let Some(prev_symbol_ref) = prev_symbol_ref { - let (prev_obj, prev_out) = prev.as_mut().unwrap(); - let prev_code = - process_code_symbol(prev_obj, prev_symbol_ref, diff_config)?; + let (_prev_obj, prev_out) = prev.as_mut().unwrap(); let (_, prev_diff) = diff_code( left_obj, right_obj, - &right_code, - &prev_code, right_symbol_ref, prev_symbol_ref, diff_config, )?; - *prev_out.symbol_diff_mut(prev_symbol_ref) = prev_diff; + prev_out.symbols[prev_symbol_ref] = prev_diff; } } - ObjSectionKind::Data => { + SectionKind::Data => { let (left_diff, right_diff) = diff_data_symbol( left_obj, right_obj, left_symbol_ref, right_symbol_ref, )?; - *left_out.symbol_diff_mut(left_symbol_ref) = left_diff; - *right_out.symbol_diff_mut(right_symbol_ref) = right_diff; + left_out.symbols[left_symbol_ref] = left_diff; + right_out.symbols[right_symbol_ref] = right_diff; } - ObjSectionKind::Bss => { + SectionKind::Bss | SectionKind::Common => { let (left_diff, right_diff) = diff_bss_symbol( left_obj, right_obj, left_symbol_ref, right_symbol_ref, )?; - *left_out.symbol_diff_mut(left_symbol_ref) = left_diff; - *right_out.symbol_diff_mut(right_symbol_ref) = right_diff; + left_out.symbols[left_symbol_ref] = left_diff; + right_out.symbols[right_symbol_ref] = right_diff; } + SectionKind::Unknown => unreachable!(), } } SymbolMatch { left: Some(left_symbol_ref), right: None, prev: _, section_kind } => { let (left_obj, left_out) = left.as_mut().unwrap(); match section_kind { - ObjSectionKind::Code => { - let code = process_code_symbol(left_obj, left_symbol_ref, diff_config)?; - *left_out.symbol_diff_mut(left_symbol_ref) = - no_diff_code(&code, left_symbol_ref)?; + SectionKind::Code => { + left_out.symbols[left_symbol_ref] = + no_diff_code(left_obj, left_symbol_ref, diff_config)?; } - ObjSectionKind::Data | ObjSectionKind::Bss => { - *left_out.symbol_diff_mut(left_symbol_ref) = - no_diff_symbol(left_obj, left_symbol_ref); + SectionKind::Data | SectionKind::Bss | SectionKind::Common => { + // Nothing needs to be done } + SectionKind::Unknown => unreachable!(), } } SymbolMatch { left: None, right: Some(right_symbol_ref), prev: _, section_kind } => { let (right_obj, right_out) = right.as_mut().unwrap(); match section_kind { - ObjSectionKind::Code => { - let code = process_code_symbol(right_obj, right_symbol_ref, diff_config)?; - *right_out.symbol_diff_mut(right_symbol_ref) = - no_diff_code(&code, right_symbol_ref)?; + SectionKind::Code => { + right_out.symbols[right_symbol_ref] = + no_diff_code(right_obj, right_symbol_ref, diff_config)?; } - ObjSectionKind::Data | ObjSectionKind::Bss => { - *right_out.symbol_diff_mut(right_symbol_ref) = - no_diff_symbol(right_obj, right_symbol_ref); + SectionKind::Data | SectionKind::Bss | SectionKind::Common => { + // Nothing needs to be done } + SectionKind::Unknown => unreachable!(), } } SymbolMatch { left: None, right: None, .. } => { @@ -343,47 +301,44 @@ pub fn diff_objs( { let (left_obj, left_out) = left.as_mut().unwrap(); let (right_obj, right_out) = right.as_mut().unwrap(); - let left_section = &left_obj.sections[left_section_idx]; - let right_section = &right_obj.sections[right_section_idx]; match section_kind { - ObjSectionKind::Code => { - let left_section_diff = left_out.section_diff(left_section_idx); - let right_section_diff = right_out.section_diff(right_section_idx); + SectionKind::Code => { let (left_diff, right_diff) = diff_generic_section( - left_section, - right_section, - left_section_diff, - right_section_diff, + left_obj, + right_obj, + left_out, + right_out, + left_section_idx, + right_section_idx, )?; - left_out.section_diff_mut(left_section_idx).merge(left_diff); - right_out.section_diff_mut(right_section_idx).merge(right_diff); + left_out.sections[left_section_idx] = left_diff; + right_out.sections[right_section_idx] = right_diff; } - ObjSectionKind::Data => { - let left_section_diff = left_out.section_diff(left_section_idx); - let right_section_diff = right_out.section_diff(right_section_idx); + SectionKind::Data => { let (left_diff, right_diff) = diff_data_section( left_obj, right_obj, - left_section, - right_section, - left_section_diff, - right_section_diff, + left_out, + right_out, + left_section_idx, + right_section_idx, )?; - left_out.section_diff_mut(left_section_idx).merge(left_diff); - right_out.section_diff_mut(right_section_idx).merge(right_diff); + left_out.sections[left_section_idx] = left_diff; + right_out.sections[right_section_idx] = right_diff; } - ObjSectionKind::Bss => { - let left_section_diff = left_out.section_diff(left_section_idx); - let right_section_diff = right_out.section_diff(right_section_idx); + SectionKind::Bss | SectionKind::Common => { let (left_diff, right_diff) = diff_bss_section( - left_section, - right_section, - left_section_diff, - right_section_diff, + left_obj, + right_obj, + left_out, + right_out, + left_section_idx, + right_section_idx, )?; - left_out.section_diff_mut(left_section_idx).merge(left_diff); - right_out.section_diff_mut(right_section_idx).merge(right_diff); + left_out.sections[left_section_idx] = left_diff; + right_out.sections[right_section_idx] = right_diff; } + SectionKind::Unknown => unreachable!(), } } } @@ -410,54 +365,46 @@ pub fn diff_objs( /// symbols in the other object that match the selected symbol's section and kind. This allows /// us to display match percentages for all symbols in the other object that could be selected. fn generate_mapping_symbols( - base_obj: &ObjInfo, + base_obj: &Object, base_name: &str, - target_obj: &ObjInfo, - target_out: &mut ObjDiff, + target_obj: &Object, + target_out: &mut ObjectDiff, config: &DiffObjConfig, ) -> Result<()> { let Some(base_symbol_ref) = symbol_ref_by_name(base_obj, base_name) else { return Ok(()); }; - let (base_section, _base_symbol) = base_obj.section_symbol(base_symbol_ref); - let Some(base_section) = base_section else { - return Ok(()); - }; - let base_code = match base_section.kind { - ObjSectionKind::Code => Some(process_code_symbol(base_obj, base_symbol_ref, config)?), - _ => None, - }; - for (target_section_index, target_section) in - target_obj.sections.iter().enumerate().filter(|(_, s)| s.kind == base_section.kind) - { - for (target_symbol_index, _target_symbol) in target_section.symbols.iter().enumerate() { - let target_symbol_ref = - SymbolRef { section_idx: target_section_index, symbol_idx: target_symbol_index }; - match base_section.kind { - ObjSectionKind::Code => { - let target_code = process_code_symbol(target_obj, target_symbol_ref, config)?; - let (left_diff, _right_diff) = diff_code( - target_obj, - base_obj, - &target_code, - base_code.as_ref().unwrap(), - target_symbol_ref, - base_symbol_ref, - config, - )?; - target_out.mapping_symbols.push(left_diff); - } - ObjSectionKind::Data => { - let (left_diff, _right_diff) = - diff_data_symbol(target_obj, base_obj, target_symbol_ref, base_symbol_ref)?; - target_out.mapping_symbols.push(left_diff); - } - ObjSectionKind::Bss => { - let (left_diff, _right_diff) = - diff_bss_symbol(target_obj, base_obj, target_symbol_ref, base_symbol_ref)?; - target_out.mapping_symbols.push(left_diff); - } + let base_section_kind = symbol_section_kind(base_obj, &base_obj.symbols[base_symbol_ref]); + for (target_symbol_index, target_symbol) in target_obj.symbols.iter().enumerate() { + if symbol_section_kind(target_obj, target_symbol) != base_section_kind { + continue; + } + match base_section_kind { + SectionKind::Code => { + let (left_diff, _right_diff) = + diff_code(target_obj, base_obj, target_symbol_index, base_symbol_ref, config)?; + target_out.mapping_symbols.push(MappingSymbolDiff { + symbol_index: target_symbol_index, + symbol_diff: left_diff, + }); } + SectionKind::Data => { + let (left_diff, _right_diff) = + diff_data_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?; + target_out.mapping_symbols.push(MappingSymbolDiff { + symbol_index: target_symbol_index, + symbol_diff: left_diff, + }); + } + SectionKind::Bss | SectionKind::Common => { + let (left_diff, _right_diff) = + diff_bss_symbol(target_obj, base_obj, target_symbol_index, base_symbol_ref)?; + target_out.mapping_symbols.push(MappingSymbolDiff { + symbol_index: target_symbol_index, + symbol_diff: left_diff, + }); + } + SectionKind::Unknown => {} } } Ok(()) @@ -465,17 +412,17 @@ fn generate_mapping_symbols( #[derive(Copy, Clone, Eq, PartialEq)] struct SymbolMatch { - left: Option, - right: Option, - prev: Option, - section_kind: ObjSectionKind, + left: Option, + right: Option, + prev: Option, + section_kind: SectionKind, } #[derive(Copy, Clone, Eq, PartialEq)] struct SectionMatch { left: Option, right: Option, - section_kind: ObjSectionKind, + section_kind: SectionKind, } #[derive(Debug, Clone, Default)] @@ -489,23 +436,16 @@ pub struct MappingConfig { pub selecting_right: Option, } -fn symbol_ref_by_name(obj: &ObjInfo, name: &str) -> Option { - for (section_idx, section) in obj.sections.iter().enumerate() { - for (symbol_idx, symbol) in section.symbols.iter().enumerate() { - if symbol.name == name { - return Some(SymbolRef { section_idx, symbol_idx }); - } - } - } - None +fn symbol_ref_by_name(obj: &Object, name: &str) -> Option { + obj.symbols.iter().position(|s| s.name == name) } fn apply_symbol_mappings( - left: &ObjInfo, - right: &ObjInfo, + left: &Object, + right: &Object, mapping_config: &MappingConfig, - left_used: &mut BTreeSet, - right_used: &mut BTreeSet, + left_used: &mut BTreeSet, + right_used: &mut BTreeSet, matches: &mut Vec, ) -> Result<()> { // If we're selecting a symbol to use as a comparison, mark it as used @@ -523,47 +463,57 @@ fn apply_symbol_mappings( // Apply manual symbol mappings for (left_name, right_name) in &mapping_config.mappings { - let Some(left_symbol) = symbol_ref_by_name(left, left_name) else { + let Some(left_symbol_index) = symbol_ref_by_name(left, left_name) else { continue; }; - if left_used.contains(&left_symbol) { + if left_used.contains(&left_symbol_index) { continue; } - let Some(right_symbol) = symbol_ref_by_name(right, right_name) else { + let Some(right_symbol_index) = symbol_ref_by_name(right, right_name) else { continue; }; - if right_used.contains(&right_symbol) { + if right_used.contains(&right_symbol_index) { continue; } - let left_section = &left.sections[left_symbol.section_idx]; - let right_section = &right.sections[right_symbol.section_idx]; - if left_section.kind != right_section.kind { + let left_section_kind = left + .symbols + .get(left_symbol_index) + .and_then(|s| s.section) + .and_then(|section_index| left.sections.get(section_index)) + .map_or(SectionKind::Unknown, |s| s.kind); + let right_section_kind = right + .symbols + .get(right_symbol_index) + .and_then(|s| s.section) + .and_then(|section_index| right.sections.get(section_index)) + .map_or(SectionKind::Unknown, |s| s.kind); + if left_section_kind != right_section_kind { log::warn!( "Symbol section kind mismatch: {} ({:?}) vs {} ({:?})", left_name, - left_section.kind, + left_section_kind, right_name, - right_section.kind + right_section_kind ); continue; } matches.push(SymbolMatch { - left: Some(left_symbol), - right: Some(right_symbol), + left: Some(left_symbol_index), + right: Some(right_symbol_index), prev: None, // TODO - section_kind: left_section.kind, + section_kind: left_section_kind, }); - left_used.insert(left_symbol); - right_used.insert(right_symbol); + left_used.insert(left_symbol_index); + right_used.insert(right_symbol_index); } Ok(()) } /// Find matching symbols between each object. fn matching_symbols( - left: Option<&ObjInfo>, - right: Option<&ObjInfo>, - prev: Option<&ObjInfo>, + left: Option<&Object>, + right: Option<&Object>, + prev: Option<&Object>, mappings: &MappingConfig, ) -> Result> { let mut matches = Vec::new(); @@ -580,34 +530,19 @@ fn matching_symbols( &mut matches, )?; } - for (section_idx, section) in left.sections.iter().enumerate() { - for (symbol_idx, symbol) in section.symbols.iter().enumerate() { - let symbol_ref = SymbolRef { section_idx, symbol_idx }; - if left_used.contains(&symbol_ref) { - continue; - } - let symbol_match = SymbolMatch { - left: Some(symbol_ref), - right: find_symbol(right, symbol, section, Some(&right_used)), - prev: find_symbol(prev, symbol, section, None), - section_kind: section.kind, - }; - matches.push(symbol_match); - if let Some(right) = symbol_match.right { - right_used.insert(right); - } + for (symbol_idx, symbol) in left.symbols.iter().enumerate() { + let section_kind = symbol_section_kind(left, symbol); + if section_kind == SectionKind::Unknown { + continue; } - } - for (symbol_idx, symbol) in left.common.iter().enumerate() { - let symbol_ref = SymbolRef { section_idx: SECTION_COMMON, symbol_idx }; - if left_used.contains(&symbol_ref) { + if left_used.contains(&symbol_idx) { continue; } let symbol_match = SymbolMatch { - left: Some(symbol_ref), - right: find_common_symbol(right, symbol), - prev: find_common_symbol(prev, symbol), - section_kind: ObjSectionKind::Bss, + left: Some(symbol_idx), + right: find_symbol(right, left, symbol, Some(&right_used)), + prev: find_symbol(prev, left, symbol, None), + section_kind, }; matches.push(symbol_match); if let Some(right) = symbol_match.right { @@ -616,83 +551,84 @@ fn matching_symbols( } } if let Some(right) = right { - for (section_idx, section) in right.sections.iter().enumerate() { - for (symbol_idx, symbol) in section.symbols.iter().enumerate() { - let symbol_ref = SymbolRef { section_idx, symbol_idx }; - if right_used.contains(&symbol_ref) { - continue; - } - matches.push(SymbolMatch { - left: None, - right: Some(symbol_ref), - prev: find_symbol(prev, symbol, section, None), - section_kind: section.kind, - }); + for (symbol_idx, symbol) in right.symbols.iter().enumerate() { + let section_kind = symbol_section_kind(right, symbol); + if section_kind == SectionKind::Unknown { + continue; } - } - for (symbol_idx, symbol) in right.common.iter().enumerate() { - let symbol_ref = SymbolRef { section_idx: SECTION_COMMON, symbol_idx }; - if right_used.contains(&symbol_ref) { + if right_used.contains(&symbol_idx) { continue; } matches.push(SymbolMatch { left: None, - right: Some(symbol_ref), - prev: find_common_symbol(prev, symbol), - section_kind: ObjSectionKind::Bss, + right: Some(symbol_idx), + prev: find_symbol(prev, right, symbol, None), + section_kind, }); } } Ok(matches) } -fn unmatched_symbols<'section, 'used>( - section: &'section ObjSection, - section_idx: usize, - used: Option<&'used BTreeSet>, -) -> impl Iterator + 'used +fn unmatched_symbols<'obj, 'used>( + obj: &'obj Object, + used: Option<&'used BTreeSet>, +) -> impl Iterator + 'used where - 'section: 'used, + 'obj: 'used, { - section.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| { + obj.symbols.iter().enumerate().filter(move |&(symbol_idx, _)| { // Skip symbols that have already been matched - !used.map(|u| u.contains(&SymbolRef { section_idx, symbol_idx })).unwrap_or(false) + !used.is_some_and(|u| u.contains(&symbol_idx)) }) } +fn symbol_section<'obj>(obj: &'obj Object, symbol: &Symbol) -> Option<(&'obj str, SectionKind)> { + if let Some(section) = symbol.section.and_then(|section_idx| obj.sections.get(section_idx)) { + Some((section.name.as_str(), section.kind)) + } else if symbol.flags.contains(SymbolFlag::Common) { + Some((".comm", SectionKind::Common)) + } else { + None + } +} + +fn symbol_section_kind(obj: &Object, symbol: &Symbol) -> SectionKind { + match symbol.section { + Some(section_index) => obj.sections[section_index].kind, + None if symbol.flags.contains(SymbolFlag::Common) => SectionKind::Common, + None => SectionKind::Unknown, + } +} + fn find_symbol( - obj: Option<&ObjInfo>, - in_symbol: &ObjSymbol, - in_section: &ObjSection, - used: Option<&BTreeSet>, -) -> Option { + obj: Option<&Object>, + in_obj: &Object, + in_symbol: &Symbol, + used: Option<&BTreeSet>, +) -> Option { let obj = obj?; + let (section_name, section_kind) = symbol_section(in_obj, in_symbol)?; // Try to find an exact name match - for (section_idx, section) in obj.sections.iter().enumerate() { - if section.kind != in_section.kind { - continue; - } - if let Some((symbol_idx, _)) = unmatched_symbols(section, section_idx, used) - .find(|(_, symbol)| symbol.name == in_symbol.name) - { - return Some(SymbolRef { section_idx, symbol_idx }); - } + if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| { + symbol.name == in_symbol.name && symbol_section_kind(obj, symbol) == section_kind + }) { + return Some(symbol_idx); } // Match compiler-generated symbols against each other (e.g. @251 -> @60) // If they are at the same address in the same section if in_symbol.name.starts_with('@') - && matches!(in_section.kind, ObjSectionKind::Data | ObjSectionKind::Bss) + && matches!(section_kind, SectionKind::Data | SectionKind::Bss) { - if let Some((section_idx, section)) = - obj.sections.iter().enumerate().find(|(_, s)| s.name == in_section.name) - { - if let Some((symbol_idx, _)) = - unmatched_symbols(section, section_idx, used).find(|(_, symbol)| { - symbol.address == in_symbol.address && symbol.name.starts_with('@') - }) - { - return Some(SymbolRef { section_idx, symbol_idx }); - } + if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| { + let Some(section_index) = symbol.section else { + return false; + }; + symbol.name.starts_with('@') + && symbol.address == in_symbol.address + && obj.sections[section_index].name == section_name + }) { + return Some(symbol_idx); } } // Match Metrowerks symbol$1234 against symbol$2345 @@ -700,41 +636,29 @@ fn find_symbol( if !suffix.chars().all(char::is_numeric) { return None; } - for (section_idx, section) in obj.sections.iter().enumerate() { - if section.kind != in_section.kind { - continue; + if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|&(_, symbol)| { + if let Some((p, s)) = symbol.name.split_once('$') { + prefix == p + && s.chars().all(char::is_numeric) + && symbol_section_kind(obj, symbol) == section_kind + } else { + false } - if let Some((symbol_idx, _)) = - unmatched_symbols(section, section_idx, used).find(|&(_, symbol)| { - if let Some((p, s)) = symbol.name.split_once('$') { - prefix == p && s.chars().all(char::is_numeric) - } else { - false - } - }) - { - return Some(SymbolRef { section_idx, symbol_idx }); - } - } - } - None -} - -fn find_common_symbol(obj: Option<&ObjInfo>, in_symbol: &ObjSymbol) -> Option { - let obj = obj?; - for (symbol_idx, symbol) in obj.common.iter().enumerate() { - if symbol.name == in_symbol.name { - return Some(SymbolRef { section_idx: SECTION_COMMON, symbol_idx }); + }) { + return Some(symbol_idx); } } None } /// Find matching sections between each object. -fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result> { +fn matching_sections(left: Option<&Object>, right: Option<&Object>) -> Result> { let mut matches = Vec::new(); if let Some(left) = left { for (section_idx, section) in left.sections.iter().enumerate() { + if section.kind == SectionKind::Unknown { + continue; + } matches.push(SectionMatch { left: Some(section_idx), right: find_section(right, §ion.name, section.kind), @@ -744,6 +668,9 @@ fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result< } if let Some(right) = right { for (section_idx, section) in right.sections.iter().enumerate() { + if section.kind == SectionKind::Unknown { + continue; + } if matches.iter().any(|m| m.right == Some(section_idx)) { continue; } @@ -757,14 +684,6 @@ fn matching_sections(left: Option<&ObjInfo>, right: Option<&ObjInfo>) -> Result< Ok(matches) } -fn find_section(obj: Option<&ObjInfo>, name: &str, section_kind: ObjSectionKind) -> Option { - for (section_idx, section) in obj?.sections.iter().enumerate() { - if section.kind != section_kind { - continue; - } - if section.name == name { - return Some(section_idx); - } - } - None +fn find_section(obj: Option<&Object>, name: &str, section_kind: SectionKind) -> Option { + obj?.sections.iter().position(|s| s.kind == section_kind && s.name == name) } diff --git a/objdiff-core/src/jobs/objdiff.rs b/objdiff-core/src/jobs/objdiff.rs index 897fa80..8d0d0d9 100644 --- a/objdiff-core/src/jobs/objdiff.rs +++ b/objdiff-core/src/jobs/objdiff.rs @@ -6,9 +6,9 @@ use typed_path::Utf8PlatformPathBuf; use crate::{ build::{run_make, BuildConfig, BuildStatus}, - diff::{diff_objs, DiffObjConfig, MappingConfig, ObjDiff}, + diff::{diff_objs, DiffObjConfig, MappingConfig, ObjectDiff}, jobs::{start_job, update_status, Job, JobContext, JobResult, JobState}, - obj::{read, ObjInfo}, + obj::{read, Object}, }; pub struct ObjDiffConfig { @@ -24,8 +24,8 @@ pub struct ObjDiffConfig { pub struct ObjDiffResult { pub first_status: BuildStatus, pub second_status: BuildStatus, - pub first_obj: Option<(ObjInfo, ObjDiff)>, - pub second_obj: Option<(ObjInfo, ObjDiff)>, + pub first_obj: Option<(Object, ObjectDiff)>, + pub second_obj: Option<(Object, ObjectDiff)>, pub time: OffsetDateTime, } @@ -170,11 +170,11 @@ fn run_build( update_status(context, "Performing diff".to_string(), step_idx, total, &cancel)?; step_idx += 1; let result = diff_objs( - &config.diff_obj_config, - &config.mapping_config, first_obj.as_ref(), second_obj.as_ref(), None, + &config.diff_obj_config, + &config.mapping_config, )?; update_status(context, "Complete".to_string(), step_idx, total, &cancel)?; diff --git a/objdiff-core/src/lib.rs b/objdiff-core/src/lib.rs index 263f17c..f2c2fb0 100644 --- a/objdiff-core/src/lib.rs +++ b/objdiff-core/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::too_many_arguments)] #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; @@ -17,5 +18,3 @@ pub mod jobs; pub mod obj; #[cfg(feature = "any-arch")] pub mod util; -#[cfg(feature = "wasm")] -pub mod wasm; diff --git a/objdiff-core/src/obj/mod.rs b/objdiff-core/src/obj/mod.rs index 21b213e..67aad97 100644 --- a/objdiff-core/src/obj/mod.rs +++ b/objdiff-core/src/obj/mod.rs @@ -1,23 +1,30 @@ pub mod read; pub mod split_meta; -use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec::Vec}; -use core::fmt; +use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec, vec::Vec}; +use core::{fmt, num::NonZeroU32}; use flagset::{flags, FlagSet}; -use object::RelocationFlags; -use split_meta::SplitMeta; -use crate::{arch::ObjArch, util::ReallySigned}; +use crate::{ + arch::{Arch, ArchDummy}, + obj::split_meta::SplitMeta, + util::ReallySigned, +}; -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum ObjSectionKind { +#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] +pub enum SectionKind { + #[default] + Unknown = -1, Code, Data, Bss, + Common, } + flags! { - pub enum ObjSymbolFlags: u8 { + #[derive(Hash)] + pub enum SymbolFlag: u8 { Global, Local, Weak, @@ -26,105 +33,152 @@ flags! { /// Has extra data associated with the symbol /// (e.g. exception table entry) HasExtra, + /// Symbol size was missing and was inferred + SizeInferred, } } -#[derive(Debug, Copy, Clone, Default, PartialEq)] -pub struct ObjSymbolFlagSet(pub FlagSet); -#[derive(Debug, Clone)] -pub struct ObjSection { +pub type SymbolFlagSet = FlagSet; + +flags! { + #[derive(Hash)] + pub enum SectionFlag: u8 { + /// Section combined from multiple input sections + Combined, + Hidden, + } +} + +pub type SectionFlagSet = FlagSet; + +#[derive(Debug, Clone, Default)] +pub struct Section { + /// Unique section ID + pub id: String, pub name: String, - pub kind: ObjSectionKind, pub address: u64, pub size: u64, - pub data: Vec, - pub orig_index: usize, - pub symbols: Vec, - pub relocations: Vec, - pub virtual_address: Option, + pub kind: SectionKind, + pub data: SectionData, + pub flags: SectionFlagSet, + pub relocations: Vec, /// Line number info (.line or .debug_line section) pub line_info: BTreeMap, + /// Original virtual address (from .note.split section) + pub virtual_address: Option, +} + +#[derive(Clone, Default)] +#[repr(transparent)] +pub struct SectionData(pub Vec); + +impl core::ops::Deref for SectionData { + type Target = Vec; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl fmt::Debug for SectionData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("SectionData").field(&self.0.len()).finish() + } +} + +impl Section { + pub fn data_range(&self, address: u64, size: usize) -> Option<&[u8]> { + let start = self.address; + let end = start + self.size; + if address >= start && address + size as u64 <= end { + let offset = (address - start) as usize; + Some(&self.data[offset..offset + size]) + } else { + None + } + } + + pub fn relocation_at<'obj>( + &'obj self, + address: u64, + obj: &'obj Object, + ) -> Option> { + self.relocations.binary_search_by_key(&address, |r| r.address).ok().and_then(|i| { + let relocation = self.relocations.get(i)?; + let symbol = obj.symbols.get(relocation.target_symbol)?; + Some(ResolvedRelocation { relocation, symbol }) + }) + } } #[derive(Debug, Clone, Eq, PartialEq)] -pub enum ObjInsArgValue { +pub enum InstructionArgValue { Signed(i64), Unsigned(u64), Opaque(Cow<'static, str>), } -impl ObjInsArgValue { - pub fn loose_eq(&self, other: &ObjInsArgValue) -> bool { +impl InstructionArgValue { + pub fn loose_eq(&self, other: &InstructionArgValue) -> bool { match (self, other) { - (ObjInsArgValue::Signed(a), ObjInsArgValue::Signed(b)) => a == b, - (ObjInsArgValue::Unsigned(a), ObjInsArgValue::Unsigned(b)) => a == b, - (ObjInsArgValue::Signed(a), ObjInsArgValue::Unsigned(b)) - | (ObjInsArgValue::Unsigned(b), ObjInsArgValue::Signed(a)) => *a as u64 == *b, - (ObjInsArgValue::Opaque(a), ObjInsArgValue::Opaque(b)) => a == b, + (InstructionArgValue::Signed(a), InstructionArgValue::Signed(b)) => a == b, + (InstructionArgValue::Unsigned(a), InstructionArgValue::Unsigned(b)) => a == b, + (InstructionArgValue::Signed(a), InstructionArgValue::Unsigned(b)) + | (InstructionArgValue::Unsigned(b), InstructionArgValue::Signed(a)) => *a as u64 == *b, + (InstructionArgValue::Opaque(a), InstructionArgValue::Opaque(b)) => a == b, _ => false, } } } -impl fmt::Display for ObjInsArgValue { +impl fmt::Display for InstructionArgValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ObjInsArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)), - ObjInsArgValue::Unsigned(v) => write!(f, "{:#x}", v), - ObjInsArgValue::Opaque(v) => write!(f, "{}", v), - } - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum ObjInsArg { - PlainText(Cow<'static, str>), - Arg(ObjInsArgValue), - Reloc, - BranchDest(u64), -} - -impl ObjInsArg { - #[inline] - pub fn is_plain_text(&self) -> bool { matches!(self, ObjInsArg::PlainText(_)) } - - pub fn loose_eq(&self, other: &ObjInsArg) -> bool { - match (self, other) { - (ObjInsArg::Arg(a), ObjInsArg::Arg(b)) => a.loose_eq(b), - (ObjInsArg::Reloc, ObjInsArg::Reloc) => true, - (ObjInsArg::BranchDest(a), ObjInsArg::BranchDest(b)) => a == b, - _ => false, + InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)), + InstructionArgValue::Unsigned(v) => write!(f, "{:#x}", v), + InstructionArgValue::Opaque(v) => write!(f, "{}", v), } } } #[derive(Debug, Clone)] -pub struct ObjIns { - pub address: u64, - pub size: u8, - pub op: u16, - pub mnemonic: Cow<'static, str>, - pub args: Vec, - pub reloc: Option, - pub branch_dest: Option, - /// Line number - pub line: Option, - /// Formatted instruction - pub formatted: String, - /// Original (unsimplified) instruction - pub orig: Option, +pub enum InstructionArg { + Value(InstructionArgValue), + Reloc, + BranchDest(u64), } -impl ObjIns { - /// Iterate over non-PlainText arguments. - #[inline] - pub fn iter_args(&self) -> impl DoubleEndedIterator { - self.args.iter().filter(|a| !a.is_plain_text()) +impl InstructionArg { + pub fn loose_eq(&self, other: &InstructionArg) -> bool { + match (self, other) { + (InstructionArg::Value(a), InstructionArg::Value(b)) => a.loose_eq(b), + (InstructionArg::Reloc, InstructionArg::Reloc) => true, + (InstructionArg::BranchDest(a), InstructionArg::BranchDest(b)) => a == b, + _ => false, + } } } +#[derive(Copy, Clone, Debug)] +pub struct InstructionRef { + pub address: u64, + pub size: u8, + pub opcode: u16, +} + +#[derive(Copy, Clone, Debug)] +pub struct ScannedInstruction { + pub ins_ref: InstructionRef, + pub branch_dest: Option, +} + +#[derive(Debug, Clone)] +pub struct ParsedInstruction { + pub ins_ref: InstructionRef, + pub mnemonic: Cow<'static, str>, + pub args: Vec, +} + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] -pub enum ObjSymbolKind { +pub enum SymbolKind { #[default] Unknown, Function, @@ -132,60 +186,65 @@ pub enum ObjSymbolKind { Section, } -#[derive(Debug, Clone, PartialEq)] -pub struct ObjSymbol { +#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)] +pub struct Symbol { pub name: String, pub demangled_name: Option, pub address: u64, - pub section_address: u64, pub size: u64, - pub size_known: bool, - pub kind: ObjSymbolKind, - pub flags: ObjSymbolFlagSet, - pub orig_section_index: Option, + pub kind: SymbolKind, + pub section: Option, + pub flags: SymbolFlagSet, + /// Alignment (from Metrowerks .comment section) + pub align: Option, /// Original virtual address (from .note.split section) pub virtual_address: Option, - /// Original index in object symbol table - pub original_index: Option, - pub bytes: Vec, } -pub struct ObjInfo { - pub arch: Box, - pub path: Option, - #[cfg(feature = "std")] - pub timestamp: Option, - pub sections: Vec, - /// Common BSS symbols - pub common: Vec, +#[derive(Debug)] +pub struct Object { + pub arch: Box, + pub symbols: Vec, + pub sections: Vec
, /// Split object metadata (.note.split section) pub split_meta: Option, + #[cfg(feature = "std")] + pub path: Option, + #[cfg(feature = "std")] + pub timestamp: Option, } -#[derive(Debug, Clone, PartialEq)] -pub struct ObjReloc { +impl Default for Object { + fn default() -> Self { + Self { + arch: ArchDummy::new(), + symbols: vec![], + sections: vec![], + split_meta: None, + #[cfg(feature = "std")] + path: None, + #[cfg(feature = "std")] + timestamp: None, + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct Relocation { pub flags: RelocationFlags, pub address: u64, - pub target: ObjSymbol, + pub target_symbol: usize, pub addend: i64, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -pub struct SymbolRef { - pub section_idx: usize, - pub symbol_idx: usize, +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum RelocationFlags { + Elf(u32), + Coff(u16), } -pub const SECTION_COMMON: usize = usize::MAX - 1; - -impl ObjInfo { - pub fn section_symbol(&self, symbol_ref: SymbolRef) -> (Option<&ObjSection>, &ObjSymbol) { - if symbol_ref.section_idx == SECTION_COMMON { - let symbol = &self.common[symbol_ref.symbol_idx]; - return (None, symbol); - } - let section = &self.sections[symbol_ref.section_idx]; - let symbol = §ion.symbols[symbol_ref.symbol_idx]; - (Some(section), symbol) - } +#[derive(Clone, Copy)] +pub struct ResolvedRelocation<'a> { + pub relocation: &'a Relocation, + pub symbol: &'a Symbol, } diff --git a/objdiff-core/src/obj/read.rs b/objdiff-core/src/obj/read.rs index ed0904c..8133bed 100644 --- a/objdiff-core/src/obj/read.rs +++ b/objdiff-core/src/obj/read.rs @@ -1,134 +1,140 @@ use alloc::{ - collections::{BTreeMap, BTreeSet}, + collections::BTreeMap, format, string::{String, ToString}, - vec, vec::Vec, }; -use anyhow::{anyhow, bail, ensure, Context, Result}; -use flagset::Flags; -use object::{ - endian::LittleEndian as LE, - pe::{ImageAuxSymbolFunctionBeginEnd, ImageLinenumber}, - read::coff::{CoffFile, CoffHeader, ImageSymbol}, - BinaryFormat, File, Object, ObjectSection, ObjectSymbol, RelocationTarget, Section, - SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolScope, -}; +use anyhow::{bail, ensure, Context, Result}; +use object::{Object as _, ObjectSection as _, ObjectSymbol as _}; use crate::{ - arch::{new_arch, ObjArch}, + arch::{new_arch, Arch}, diff::DiffObjConfig, obj::{ split_meta::{SplitMeta, SPLITMETA_SECTION}, - ObjInfo, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, - ObjSymbolKind, + Object, Relocation, RelocationFlags, Section, SectionData, SectionFlag, SectionKind, + Symbol, SymbolFlag, SymbolKind, }, util::{read_u16, read_u32}, }; -fn to_obj_section_kind(kind: SectionKind) -> Option { - match kind { - SectionKind::Text => Some(ObjSectionKind::Code), - SectionKind::Data | SectionKind::ReadOnlyData => Some(ObjSectionKind::Data), - SectionKind::UninitializedData => Some(ObjSectionKind::Bss), - _ => None, +fn map_section_kind(section: &object::Section) -> SectionKind { + match section.kind() { + object::SectionKind::Text => SectionKind::Code, + object::SectionKind::Data | object::SectionKind::ReadOnlyData => SectionKind::Data, + object::SectionKind::UninitializedData => SectionKind::Bss, + _ => SectionKind::Unknown, } } -fn to_obj_symbol( - arch: &dyn ObjArch, - obj_file: &File<'_>, - symbol: &Symbol<'_, '_>, +fn map_symbol( + arch: &dyn Arch, + file: &object::File, + symbol: &object::Symbol, split_meta: Option<&SplitMeta>, -) -> Result { - let mut name = symbol.name().context("Failed to process symbol name")?; - if name.is_empty() { - log::warn!("Found empty sym: {symbol:?}"); - name = "?"; +) -> Result { + let mut name = symbol.name().context("Failed to process symbol name")?.to_string(); + let size = symbol.size(); + if let (object::SymbolKind::Section, Some(section)) = + (symbol.kind(), symbol.section_index().and_then(|i| file.section_by_index(i).ok())) + { + let section_name = section.name().context("Failed to process section name")?; + name = format!("[{}]", section_name); + // size = section.size(); } - let mut flags = ObjSymbolFlagSet(ObjSymbolFlags::none()); + + let mut flags = arch.extra_symbol_flags(symbol); if symbol.is_global() { - flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Global); + flags |= SymbolFlag::Global; } if symbol.is_local() { - flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Local); + flags |= SymbolFlag::Local; } if symbol.is_common() { - flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Common); + flags |= SymbolFlag::Common; } if symbol.is_weak() { - flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak); + flags |= SymbolFlag::Weak; } - if obj_file.format() == BinaryFormat::Elf && symbol.scope() == SymbolScope::Linkage { - flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Hidden); - } - #[cfg(feature = "ppc")] - if arch.ppc().and_then(|a| a.extab.as_ref()).is_some_and(|e| e.contains_key(&symbol.index().0)) + if file.format() == object::BinaryFormat::Elf && symbol.scope() == object::SymbolScope::Linkage { - flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::HasExtra); + flags |= SymbolFlag::Hidden; } - let address = arch.symbol_address(symbol); - let section_address = if let Some(section) = - symbol.section_index().and_then(|idx| obj_file.section_by_index(idx).ok()) - { - address - section.address() - } else { - address + + let kind = match symbol.kind() { + object::SymbolKind::Text => SymbolKind::Function, + object::SymbolKind::Data => SymbolKind::Object, + object::SymbolKind::Section => SymbolKind::Section, + _ => SymbolKind::Unknown, }; - let demangled_name = arch.demangle(name); + let address = arch.symbol_address(symbol.address(), kind); + let demangled_name = arch.demangle(&name); // Find the virtual address for the symbol if available let virtual_address = split_meta .and_then(|m| m.virtual_addresses.as_ref()) .and_then(|v| v.get(symbol.index().0).cloned()); + let section = symbol.section_index().map(|i| map_section_index(file, i)); - let bytes = symbol - .section_index() - .and_then(|idx| obj_file.section_by_index(idx).ok()) - .and_then(|section| section.data().ok()) - .and_then(|data| { - data.get(section_address as usize..(section_address + symbol.size()) as usize) - }) - .unwrap_or(&[]); - - let kind = match symbol.kind() { - SymbolKind::Text => ObjSymbolKind::Function, - SymbolKind::Data => ObjSymbolKind::Object, - SymbolKind::Section => ObjSymbolKind::Section, - _ => ObjSymbolKind::Unknown, - }; - - Ok(ObjSymbol { - name: name.to_string(), + Ok(Symbol { + name, demangled_name, address, - section_address, - size: symbol.size(), - size_known: symbol.size() != 0, + size, kind, + section, flags, - orig_section_index: symbol.section_index().map(|i| i.0), + align: None, // TODO parse .comment virtual_address, - original_index: Some(symbol.index().0), - bytes: bytes.to_vec(), }) } -fn filter_sections(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Result> { - let mut result = Vec::::new(); +fn map_section_index(file: &object::File, idx: object::SectionIndex) -> usize { + match file.format() { + object::BinaryFormat::Elf => idx.0 - 1, + _ => idx.0, + } +} + +fn map_symbol_index(file: &object::File, idx: object::SymbolIndex) -> usize { + match file.format() { + object::BinaryFormat::Elf => idx.0 - 1, + _ => idx.0, + } +} + +fn map_symbols( + arch: &dyn Arch, + obj_file: &object::File, + split_meta: Option<&SplitMeta>, +) -> Result> { + let mut symbols = Vec::::with_capacity(obj_file.symbols().count()); + for symbol in obj_file.symbols() { + symbols.push(map_symbol(arch, obj_file, &symbol, split_meta)?); + } + Ok(symbols) +} + +fn map_sections( + arch: &dyn Arch, + obj_file: &object::File, + split_meta: Option<&SplitMeta>, +) -> Result> { + let mut section_names = BTreeMap::::new(); + let mut result = Vec::
::with_capacity(obj_file.sections().count()); for section in obj_file.sections() { - if section.size() == 0 { - continue; - } - let Some(kind) = to_obj_section_kind(section.kind()) else { - continue; - }; let name = section.name().context("Failed to process section name")?; - let data = section.uncompressed_data().context("Failed to read section data")?; + let kind = map_section_kind(§ion); + let data = if kind == SectionKind::Unknown { + // Don't need to read data for unknown sections + Vec::new() + } else { + section.uncompressed_data().context("Failed to read section data")?.into_owned() + }; // Find the virtual address for the section symbol if available let section_symbol = obj_file.symbols().find(|s| { - s.kind() == SymbolKind::Section && s.section_index() == Some(section.index()) + s.kind() == object::SymbolKind::Section && s.section_index() == Some(section.index()) }); let virtual_address = section_symbol.and_then(|s| { split_meta @@ -136,111 +142,54 @@ fn filter_sections(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Resul .and_then(|v| v.get(s.index().0).cloned()) }); - result.push(ObjSection { + let relocations = map_relocations(arch, obj_file, §ion)?; + + let unique_id = section_names.entry(name.to_string()).or_insert(0); + let id = format!("{}-{}", name, unique_id); + *unique_id += 1; + + result.push(Section { + id, name: name.to_string(), - kind, address: section.address(), size: section.size(), - data: data.to_vec(), - orig_index: section.index().0, - symbols: Vec::new(), - relocations: Vec::new(), + kind, + data: SectionData(data), + flags: Default::default(), + relocations, virtual_address, line_info: Default::default(), }); } - result.sort_by(|a, b| a.name.cmp(&b.name)); Ok(result) } -fn symbols_by_section( - arch: &dyn ObjArch, - obj_file: &File<'_>, - section: &ObjSection, - section_symbols: &[Symbol<'_, '_>], - split_meta: Option<&SplitMeta>, - name_counts: &mut BTreeMap, -) -> Result> { - let mut result = Vec::::new(); - for symbol in section_symbols { - if symbol.kind() == SymbolKind::Section { - continue; - } - if symbol.is_local() && section.kind == ObjSectionKind::Code { - // TODO strip local syms in diff? - let name = symbol.name().context("Failed to process symbol name")?; - if symbol.size() == 0 || name.starts_with("lbl_") { - continue; - } - } - result.push(to_obj_symbol(arch, obj_file, symbol, split_meta)?); - } - result.sort_by(|a, b| a.address.cmp(&b.address).then(a.size.cmp(&b.size))); - let mut iter = result.iter_mut().peekable(); - while let Some(symbol) = iter.next() { - if symbol.size == 0 { - if let Some(next_symbol) = iter.peek() { - symbol.size = next_symbol.address - symbol.address; - } else { - symbol.size = (section.address + section.size) - symbol.address; - } - // Set symbol kind if we ended up with a non-zero size - if symbol.kind == ObjSymbolKind::Unknown && symbol.size > 0 { - symbol.kind = match section.kind { - ObjSectionKind::Code => ObjSymbolKind::Function, - ObjSectionKind::Data | ObjSectionKind::Bss => ObjSymbolKind::Object, - }; - } - } - } - if result.is_empty() { - // Dummy symbol for empty sections - *name_counts.entry(section.name.clone()).or_insert(0) += 1; - let current_count: u32 = *name_counts.get(§ion.name).unwrap(); - result.push(ObjSymbol { - name: if current_count > 1 { - format!("[{} ({})]", section.name, current_count) - } else { - format!("[{}]", section.name) - }, - demangled_name: None, - address: 0, - section_address: 0, - size: section.size, - size_known: true, - kind: match section.kind { - ObjSectionKind::Code => ObjSymbolKind::Function, - ObjSectionKind::Data | ObjSectionKind::Bss => ObjSymbolKind::Object, - }, - flags: Default::default(), - orig_section_index: Some(section.orig_index), - virtual_address: None, - original_index: None, - bytes: Vec::new(), - }); - } - Ok(result) -} - -fn common_symbols( - arch: &dyn ObjArch, - obj_file: &File<'_>, - split_meta: Option<&SplitMeta>, -) -> Result> { - obj_file - .symbols() - .filter(Symbol::is_common) - .map(|symbol| to_obj_symbol(arch, obj_file, &symbol, split_meta)) - .collect::>>() -} +// result.sort_by(|a, b| a.address.cmp(&b.address).then(a.size.cmp(&b.size))); +// let mut iter = result.iter_mut().peekable(); +// while let Some(symbol) = iter.next() { +// if symbol.size == 0 { +// if let Some(next_symbol) = iter.peek() { +// symbol.size = next_symbol.address - symbol.address; +// } else { +// symbol.size = (section.address + section.size) - symbol.address; +// } +// // Set symbol kind if we ended up with a non-zero size +// if symbol.kind == ObjSymbolKind::Unknown && symbol.size > 0 { +// symbol.kind = match section.kind { +// ObjSectionKind::Code => ObjSymbolKind::Function, +// ObjSectionKind::Data | ObjSectionKind::Bss => ObjSymbolKind::Object, +// }; +// } +// } +// } const LOW_PRIORITY_SYMBOLS: &[&str] = &["__gnu_compiled_c", "__gnu_compiled_cplusplus", "gcc2_compiled."]; fn best_symbol<'r, 'data, 'file>( - symbols: &'r [Symbol<'data, 'file>], + symbols: &'r [object::Symbol<'data, 'file>], address: u64, -) -> Option<&'r Symbol<'data, 'file>> { +) -> Option { let mut closest_symbol_index = match symbols.binary_search_by_key(&address, |s| s.address()) { Ok(index) => Some(index), Err(index) => index.checked_sub(1), @@ -253,12 +202,12 @@ fn best_symbol<'r, 'data, 'file>( } closest_symbol_index = prev_index; } - let mut best_symbol: Option<&'r Symbol<'data, 'file>> = None; + let mut best_symbol: Option<&'r object::Symbol<'data, 'file>> = None; for symbol in symbols.iter().skip(closest_symbol_index) { if symbol.address() > address { break; } - if symbol.kind() == SymbolKind::Section + if symbol.kind() == object::SymbolKind::Section || (symbol.size() > 0 && (symbol.address() + symbol.size()) <= address) { continue; @@ -274,118 +223,91 @@ fn best_symbol<'r, 'data, 'file>( best_symbol = Some(symbol); } } - best_symbol + best_symbol.map(|s| s.index()) } -fn find_section_symbol( - arch: &dyn ObjArch, - obj_file: &File<'_>, - section: &Section, - section_symbols: &[Symbol<'_, '_>], - address: u64, - split_meta: Option<&SplitMeta>, -) -> Result { - if let Some(symbol) = best_symbol(section_symbols, address) { - return to_obj_symbol(arch, obj_file, symbol, split_meta); - } - // Fallback to section symbol - Ok(ObjSymbol { - name: section.name()?.to_string(), - demangled_name: None, - address: section.address(), - section_address: 0, - size: 0, - size_known: false, - kind: ObjSymbolKind::Section, - flags: Default::default(), - orig_section_index: Some(section.index().0), - virtual_address: None, - original_index: None, - bytes: Vec::new(), - }) -} - -fn relocations_by_section( - arch: &dyn ObjArch, - obj_file: &File<'_>, - section: &ObjSection, - section_symbols: &[Vec>], - split_meta: Option<&SplitMeta>, -) -> Result> { - let obj_section = obj_file.section_by_index(SectionIndex(section.orig_index))?; - let mut relocations = Vec::::new(); +fn map_relocations( + arch: &dyn Arch, + obj_file: &object::File, + obj_section: &object::Section, +) -> Result> { + let mut relocations = Vec::::with_capacity(obj_section.relocations().count()); + let mut ordered_symbols = None; for (address, reloc) in obj_section.relocations() { - let symbol = match reloc.target() { - RelocationTarget::Symbol(idx) => { + let target_symbol = match reloc.target() { + object::RelocationTarget::Symbol(idx) => { if idx.0 == u32::MAX as usize { // ??? continue; } - let Ok(symbol) = obj_file.symbol_by_index(idx) else { - log::warn!( - "Failed to locate relocation {:#x} target symbol {}", - address, - idx.0 - ); - continue; + // If the target is a section symbol, try to resolve a better symbol as the target + let idx = if let Some(section_symbol) = obj_file + .symbol_by_index(idx) + .ok() + .take_if(|s| s.kind() == object::SymbolKind::Section) + { + let section_index = + section_symbol.section_index().context("Section symbol without section")?; + let ordered_symbols = ordered_symbols.get_or_insert_with(|| { + let mut vec = obj_file + .symbols() + .filter(|s| { + s.section_index() == Some(section_index) + && s.kind() != object::SymbolKind::Section + }) + .collect::>(); + vec.sort_by(|a, b| { + a.address().cmp(&b.address()).then(a.size().cmp(&b.size())) + }); + vec + }); + best_symbol( + ordered_symbols, + section_symbol.address().wrapping_add_signed(reloc.addend()), + ) + .unwrap_or(idx) + } else { + idx }; - symbol + map_symbol_index(obj_file, idx) } - RelocationTarget::Absolute => { - log::warn!("Ignoring absolute relocation @ {}:{:#x}", section.name, address); + object::RelocationTarget::Absolute => { + let section_name = obj_section.name()?; + log::warn!("Ignoring absolute relocation @ {}:{:#x}", section_name, address); continue; } _ => bail!("Unhandled relocation target: {:?}", reloc.target()), }; - let flags = reloc.flags(); // TODO validate reloc here? - let mut addend = if reloc.has_implicit_addend() { - arch.implcit_addend(obj_file, section, address, &reloc)? + let flags = match reloc.flags() { + object::RelocationFlags::Elf { r_type } => RelocationFlags::Elf(r_type), + object::RelocationFlags::Coff { typ } => RelocationFlags::Coff(typ), + flags => { + bail!("Unhandled relocation flags: {:?}", flags); + } + }; + // TODO validate reloc here? + let addend = if reloc.has_implicit_addend() { + arch.implcit_addend(obj_file, obj_section, address, &reloc, flags)? } else { reloc.addend() }; - let target = match symbol.kind() { - SymbolKind::Text | SymbolKind::Data | SymbolKind::Label | SymbolKind::Unknown => { - to_obj_symbol(arch, obj_file, &symbol, split_meta)? - } - SymbolKind::Section => { - ensure!(addend >= 0, "Negative addend in section reloc: {addend}"); - let section_index = symbol - .section_index() - .ok_or_else(|| anyhow!("Section symbol {symbol:?} has no section index"))?; - let section = obj_file.section_by_index(section_index)?; - let symbol = find_section_symbol( - arch, - obj_file, - §ion, - §ion_symbols[section_index.0], - addend as u64, - split_meta, - )?; - // Adjust addend to be relative to the selected symbol - addend = (symbol.address - section.address()) as i64; - symbol - } - kind => bail!("Unhandled relocation symbol type {kind:?}"), - }; - relocations.push(ObjReloc { flags, address, target, addend }); + relocations.push(Relocation { address, flags, target_symbol, addend }); } Ok(relocations) } -fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection], obj_data: &[u8]) -> Result<()> { +fn parse_line_info( + obj_file: &object::File, + sections: &mut [Section], + obj_data: &[u8], +) -> Result<()> { // DWARF 1.1 if let Some(section) = obj_file.section_by_name(".line") { let data = section.uncompressed_data()?; let mut reader: &[u8] = data.as_ref(); - let mut text_sections = obj_file.sections().filter(|s| s.kind() == SectionKind::Text); + let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code); while !reader.is_empty() { - let text_section_index = text_sections - .next() - .ok_or_else(|| anyhow!("Next text section not found for line info"))? - .index() - .0; - let mut section_data = reader; let size = read_u32(obj_file, &mut section_data)? as usize; if size > reader.len() { @@ -393,13 +315,9 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection], obj_data: &[u8]) } (section_data, reader) = reader.split_at(size); + section_data = §ion_data[4..]; // Skip the size field let base_address = read_u32(obj_file, &mut section_data)? as u64; - let Some(out_section) = - sections.iter_mut().find(|s| s.orig_index == text_section_index) - else { - // Skip line info for sections we filtered out - continue; - }; + let out_section = text_sections.next().context("No text section for line info")?; while !section_data.is_empty() { let line_number = read_u32(obj_file, &mut section_data)?; let statement_pos = read_u16(obj_file, &mut section_data)?; @@ -435,12 +353,8 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection], obj_data: &[u8]) if let Some(header) = iter.next().map_err(gimli_error)? { let unit = dwarf.unit(header).map_err(gimli_error)?; if let Some(program) = unit.line_program.clone() { - let mut text_sections = - obj_file.sections().filter(|s| s.kind() == SectionKind::Text); - let section_index = text_sections.next().map(|s| s.index().0); - let mut lines = section_index - .and_then(|index| sections.iter_mut().find(|s| s.orig_index == index)) - .map(|s| &mut s.line_info); + let mut text_sections = sections.iter_mut().filter(|s| s.kind == SectionKind::Code); + let mut lines = text_sections.next().map(|section| &mut section.line_info); let mut rows = program.rows(); while let Some((_header, row)) = rows.next_row().map_err(gimli_error)? { @@ -450,10 +364,7 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection], obj_data: &[u8]) if row.end_sequence() { // The next row is the start of a new sequence, which means we must // advance to the next .text section. - let section_index = text_sections.next().map(|s| s.index().0); - lines = section_index - .and_then(|index| sections.iter_mut().find(|s| s.orig_index == index)) - .map(|s| &mut s.line_info); + lines = text_sections.next().map(|section| &mut section.line_info); } } } @@ -464,14 +375,22 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection], obj_data: &[u8]) } // COFF - if let File::Coff(coff) = obj_file { - line_info_coff(coff, sections, obj_data)?; + if let object::File::Coff(coff) = obj_file { + parse_line_info_coff(coff, sections, obj_data)?; } Ok(()) } -fn line_info_coff(coff: &CoffFile, sections: &mut [ObjSection], obj_data: &[u8]) -> Result<()> { +fn parse_line_info_coff( + coff: &object::coff::CoffFile, + sections: &mut [Section], + obj_data: &[u8], +) -> Result<()> { + use object::{ + coff::{CoffHeader as _, ImageSymbol as _}, + endian::LittleEndian as LE, + }; let symbol_table = coff.coff_header().symbols(obj_data)?; // Enumerate over all sections. @@ -486,17 +405,19 @@ fn line_info_coff(coff: &CoffFile, sections: &mut [ObjSection], obj_data: &[u8]) // Find this section in our out_section. If it's not in out_section, // skip it. - let Some(out_section) = sections.iter_mut().find(|s| s.orig_index == sect.index().0) else { + let Some(out_section) = sections.get_mut(sect.index().0) else { continue; }; // Turn the line numbers into an ImageLinenumber slice. - let Some(linenums) = - &obj_data.get(ptr_linenums..ptr_linenums + num_linenums * size_of::()) - else { + let Some(linenums) = &obj_data.get( + ptr_linenums..ptr_linenums + num_linenums * size_of::(), + ) else { continue; }; - let Ok(linenums) = object::pod::slice_from_all_bytes::(linenums) else { + let Ok(linenums) = + object::pod::slice_from_all_bytes::(linenums) + else { continue; }; @@ -528,10 +449,12 @@ fn line_info_coff(coff: &CoffFile, sections: &mut [ObjSection], obj_data: &[u8]) // for logging purposes, but also to acquire its Function // Auxillary Record, which tells us where to find our .bf symbol. let symtable_entry = linenum.symbol_table_index_or_virtual_address.get(LE); - let Ok(symbol) = symbol_table.symbol(SymbolIndex(symtable_entry as usize)) else { + let Ok(symbol) = symbol_table.symbol(object::SymbolIndex(symtable_entry as usize)) + else { continue; }; - let Ok(aux_fun) = symbol_table.aux_function(SymbolIndex(symtable_entry as usize)) + let Ok(aux_fun) = + symbol_table.aux_function(object::SymbolIndex(symtable_entry as usize)) else { continue; }; @@ -543,7 +466,7 @@ fn line_info_coff(coff: &CoffFile, sections: &mut [ObjSection], obj_data: &[u8]) continue; } let Ok(bf_symbol) = - symbol_table.symbol(SymbolIndex(aux_fun.tag_index.get(LE) as usize)) + symbol_table.symbol(object::SymbolIndex(aux_fun.tag_index.get(LE) as usize)) else { continue; }; @@ -555,8 +478,8 @@ fn line_info_coff(coff: &CoffFile, sections: &mut [ObjSection], obj_data: &[u8]) // Get the Function Begin/End Auxillary Record associated with // our .bf symbol, where we'll fine the linenumber of the start // of our function. - let Ok(bf_aux) = symbol_table.get::( - SymbolIndex(aux_fun.tag_index.get(LE) as usize), + let Ok(bf_aux) = symbol_table.get::( + object::SymbolIndex(aux_fun.tag_index.get(LE) as usize), 1, ) else { continue; @@ -581,172 +504,176 @@ fn line_info_coff(coff: &CoffFile, sections: &mut [ObjSection], obj_data: &[u8]) Ok(()) } -fn update_combined_symbol(symbol: ObjSymbol, address_change: i64) -> Result { - Ok(ObjSymbol { - name: symbol.name, - demangled_name: symbol.demangled_name, - address: (symbol.address as i64 + address_change).try_into()?, - section_address: (symbol.section_address as i64 + address_change).try_into()?, - size: symbol.size, - size_known: symbol.size_known, - kind: symbol.kind, - flags: symbol.flags, - orig_section_index: symbol.orig_section_index, - virtual_address: if let Some(virtual_address) = symbol.virtual_address { - Some((virtual_address as i64 + address_change).try_into()?) - } else { - None - }, - original_index: symbol.original_index, - bytes: symbol.bytes, - }) -} - -fn combine_sections(section: ObjSection, combine: ObjSection) -> Result { - let mut data = section.data; - data.extend(combine.data); - - let address_change: i64 = (section.address + section.size) as i64 - combine.address as i64; - let mut symbols = section.symbols; - for symbol in combine.symbols { - symbols.push(update_combined_symbol(symbol, address_change)?); - } - - let mut relocations = section.relocations; - for reloc in combine.relocations { - relocations.push(ObjReloc { - flags: reloc.flags, - address: (reloc.address as i64 + address_change).try_into()?, - target: reloc.target, // TODO: Should be updated? - addend: reloc.addend, - }); - } - - let mut line_info = section.line_info; - for (addr, line) in combine.line_info { - let key = (addr as i64 + address_change).try_into()?; - line_info.insert(key, line); - } - - Ok(ObjSection { - name: section.name, - kind: section.kind, - address: section.address, - size: section.size + combine.size, - data, - orig_index: section.orig_index, - symbols, - relocations, - virtual_address: section.virtual_address, - line_info, - }) -} - -fn combine_data_sections(sections: &mut Vec) -> Result<()> { - let names_to_combine: BTreeSet<_> = sections - .iter() - .filter(|s| s.kind == ObjSectionKind::Data) - .map(|s| s.name.clone()) - .collect(); - - for name in names_to_combine { - // Take section with lowest index - let (mut section_index, _) = sections - .iter() - .enumerate() - .filter(|(_, s)| s.name == name) - .min_by_key(|(_, s)| s.orig_index) - // Should not happen - .context("No combine section found with name")?; - let mut section = sections.remove(section_index); - - // Remove equally named sections - let mut combines = vec![]; - for i in (0..sections.len()).rev() { - if sections[i].name != name || sections[i].orig_index == section.orig_index { - continue; +fn combine_sections( + sections: &mut [Section], + symbols: &mut [Symbol], + config: &DiffObjConfig, +) -> Result<()> { + let mut data_sections = BTreeMap::>::new(); + let mut text_sections = Vec::::new(); + for (i, section) in sections.iter().enumerate() { + match section.kind { + SectionKind::Data | SectionKind::Bss => { + data_sections.entry(section.name.clone()).or_default().push(i); } - combines.push(sections.remove(i)); - if i < section_index { - section_index -= 1; + SectionKind::Code => { + text_sections.push(i); } + _ => {} } - - // Combine sections ordered by index - combines.sort_unstable_by_key(|c| c.orig_index); - for combine in combines { - section = combine_sections(section, combine)?; + } + if config.combine_data_sections { + for (_, section_indices) in data_sections { + do_combine_sections(sections, symbols, §ion_indices)?; } - sections.insert(section_index, section); + } + if config.combine_text_sections { + do_combine_sections(sections, symbols, &text_sections)?; } Ok(()) } +fn do_combine_sections( + sections: &mut [Section], + symbols: &mut [Symbol], + section_indices: &[usize], +) -> Result<()> { + if section_indices.len() < 2 { + return Ok(()); + } + let first_section_idx = section_indices[0]; + + // Calculate the new offset for each section + let mut offsets = Vec::::with_capacity(section_indices.len()); + let mut current_offset = 0; + let mut data_size = 0; + let mut num_relocations = 0; + for &i in section_indices { + let section = §ions[i]; + if section.address != 0 { + bail!("Section {} ({}) has non-zero address", i, section.name); + } + offsets.push(current_offset); + current_offset += section.size; + data_size += section.data.len(); + num_relocations += section.relocations.len(); + } + if data_size > 0 { + ensure!(data_size == current_offset as usize, "Data size mismatch"); + } + + // Combine section data + let mut data = Vec::::with_capacity(data_size); + let mut relocations = Vec::::with_capacity(num_relocations); + let mut line_info = BTreeMap::::new(); + for (&i, &offset) in section_indices.iter().zip(&offsets) { + let section = &mut sections[i]; + section.size = 0; + data.append(&mut section.data.0); + section.relocations.iter_mut().for_each(|r| r.address += offset); + relocations.append(&mut section.relocations); + line_info.append(&mut section.line_info.iter().map(|(&a, &l)| (a + offset, l)).collect()); + section.line_info.clear(); + if offset > 0 { + section.flags |= SectionFlag::Hidden; + } + } + { + let first_section = &mut sections[first_section_idx]; + first_section.size = current_offset; + first_section.data = SectionData(data); + first_section.flags |= SectionFlag::Combined; + first_section.relocations = relocations; + first_section.line_info = line_info; + } + + // Find all section symbols for the merged sections + let mut section_symbols = symbols + .iter() + .enumerate() + .filter(|&(_, s)| { + s.kind == SymbolKind::Section && s.section.is_some_and(|i| section_indices.contains(&i)) + }) + .map(|(i, _)| i) + .collect::>(); + section_symbols.sort_by_key(|&i| symbols[i].section.unwrap()); + let target_section_symbol = section_symbols.first().copied(); + + // Adjust symbol addresses and section indices + for symbol in symbols.iter_mut() { + let Some(section_index) = symbol.section else { + continue; + }; + let Some(merge_index) = section_indices.iter().position(|&i| i == section_index) else { + continue; + }; + symbol.address += offsets[merge_index]; + symbol.section = Some(first_section_idx); + } + + // Adjust relocations to section symbols + for relocation in sections.iter_mut().flat_map(|s| s.relocations.iter_mut()) { + let target_symbol = &symbols[relocation.target_symbol]; + if target_symbol.kind != SymbolKind::Section { + continue; + } + if !target_symbol.section.is_some_and(|i| section_indices.contains(&i)) { + continue; + } + // The section symbol's address will have the offset applied + relocation.target_symbol = target_section_symbol.context("No target section symbol")?; + relocation.addend = relocation + .addend + .checked_add_unsigned(target_symbol.address) + .context("Relocation addend overflow")?; + } + + // Reset section symbols + for (i, &symbol_index) in section_symbols.iter().enumerate() { + let symbol = &mut symbols[symbol_index]; + symbol.address = 0; + if i > 0 { + // Remove the section symbol + symbol.kind = SymbolKind::Unknown; + symbol.section = None; + } + } + + Ok(()) +} + #[cfg(feature = "std")] -pub fn read(obj_path: &std::path::Path, config: &DiffObjConfig) -> Result { +pub fn read(obj_path: &std::path::Path, config: &DiffObjConfig) -> Result { let (data, timestamp) = { let file = std::fs::File::open(obj_path)?; let timestamp = filetime::FileTime::from_last_modification_time(&file.metadata()?); (unsafe { memmap2::Mmap::map(&file) }?, timestamp) }; let mut obj = parse(&data, config)?; - obj.path = Some(obj_path.to_string_lossy().into_owned()); + obj.path = Some(obj_path.to_path_buf()); obj.timestamp = Some(timestamp); Ok(obj) } -pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result { - let obj_file = File::parse(data)?; +pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result { + let obj_file = object::File::parse(data)?; let arch = new_arch(&obj_file)?; - let split_meta = split_meta(&obj_file)?; - - // Create sorted symbol list for each section - let mut section_symbols = Vec::with_capacity(obj_file.sections().count()); - for section in obj_file.sections() { - let mut symbols = obj_file - .symbols() - .filter(|s| s.section_index() == Some(section.index())) - .collect::>(); - symbols.sort_by_key(|s| s.address()); - let section_index = section.index().0; - if section_index >= section_symbols.len() { - section_symbols.resize_with(section_index + 1, Vec::new); - } - section_symbols[section_index] = symbols; + let split_meta = parse_split_meta(&obj_file)?; + let mut symbols = map_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?; + let mut sections = map_sections(arch.as_ref(), &obj_file, split_meta.as_ref())?; + parse_line_info(&obj_file, &mut sections, data)?; + if config.combine_data_sections || config.combine_text_sections { + combine_sections(&mut sections, &mut symbols, config)?; } - - let mut sections = filter_sections(&obj_file, split_meta.as_ref())?; - let mut section_name_counts: BTreeMap = BTreeMap::new(); - for section in &mut sections { - section.symbols = symbols_by_section( - arch.as_ref(), - &obj_file, - section, - §ion_symbols[section.orig_index], - split_meta.as_ref(), - &mut section_name_counts, - )?; - section.relocations = relocations_by_section( - arch.as_ref(), - &obj_file, - section, - §ion_symbols, - split_meta.as_ref(), - )?; - } - if config.combine_data_sections { - combine_data_sections(&mut sections)?; - } - line_info(&obj_file, &mut sections, data)?; - let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?; - Ok(ObjInfo { + Ok(Object { arch, + symbols, + sections, + split_meta, + #[cfg(feature = "std")] path: None, #[cfg(feature = "std")] timestamp: None, - sections, - common, - split_meta, }) } @@ -756,16 +683,128 @@ pub fn has_function(obj_path: &std::path::Path, symbol_name: &str) -> Result) -> Result> { +fn parse_split_meta(obj_file: &object::File) -> Result> { Ok(if let Some(section) = obj_file.section_by_name(SPLITMETA_SECTION) { Some(SplitMeta::from_section(section, obj_file.endianness(), obj_file.is_64())?) } else { None }) } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_combine_sections() { + let mut sections = vec![ + Section { + id: ".text-0".to_string(), + name: ".text".to_string(), + size: 8, + kind: SectionKind::Code, + data: SectionData(vec![0; 8]), + relocations: vec![ + Relocation { + address: 0, + flags: RelocationFlags::Elf(0), + target_symbol: 0, + addend: 0, + }, + Relocation { + address: 2, + flags: RelocationFlags::Elf(0), + target_symbol: 1, + addend: 0, + }, + Relocation { + address: 4, + flags: RelocationFlags::Elf(0), + target_symbol: 3, + addend: 2, + }, + ], + ..Default::default() + }, + Section { + id: ".data-0".to_string(), + name: ".data".to_string(), + size: 4, + kind: SectionKind::Data, + data: SectionData(vec![1, 2, 3, 4]), + relocations: vec![Relocation { + address: 0, + flags: RelocationFlags::Elf(0), + target_symbol: 2, + addend: 0, + }], + line_info: [(0, 1)].into_iter().collect(), + ..Default::default() + }, + Section { + id: ".data-1".to_string(), + name: ".data".to_string(), + size: 4, + kind: SectionKind::Data, + data: SectionData(vec![5, 6, 7, 8]), + relocations: vec![Relocation { + address: 0, + flags: RelocationFlags::Elf(0), + target_symbol: 2, + addend: 0, + }], + ..Default::default() + }, + Section { + id: ".data-2".to_string(), + name: ".data".to_string(), + size: 4, + kind: SectionKind::Data, + data: SectionData(vec![9, 10, 11, 12]), + line_info: [(0, 2)].into_iter().collect(), + ..Default::default() + }, + ]; + let mut symbols = vec![ + Symbol { + name: ".data".to_string(), + address: 0, + kind: SymbolKind::Section, + section: Some(2), + ..Default::default() + }, + Symbol { + name: "symbol".to_string(), + address: 0, + kind: SymbolKind::Object, + size: 4, + section: Some(2), + ..Default::default() + }, + Symbol { + name: "function".to_string(), + address: 0, + size: 8, + kind: SymbolKind::Function, + section: Some(0), + ..Default::default() + }, + Symbol { + name: ".data".to_string(), + address: 0, + kind: SymbolKind::Section, + section: Some(3), + ..Default::default() + }, + ]; + do_combine_sections(&mut sections, &mut symbols, &[1, 2, 3]).unwrap(); + assert_eq!(sections[1].data.0, (1..=12).collect::>()); + insta::assert_debug_snapshot!((sections, symbols)); + } +} diff --git a/objdiff-core/src/obj/snapshots/objdiff_core__obj__read__test__combine_sections.snap b/objdiff-core/src/obj/snapshots/objdiff_core__obj__read__test__combine_sections.snap new file mode 100644 index 0000000..ca8dc9c --- /dev/null +++ b/objdiff-core/src/obj/snapshots/objdiff_core__obj__read__test__combine_sections.snap @@ -0,0 +1,161 @@ +--- +source: objdiff-core/src/obj/read.rs +expression: "(sections, symbols)" +--- +( + [ + Section { + id: ".text-0", + name: ".text", + address: 0, + size: 8, + kind: Code, + data: SectionData( + 8, + ), + flags: FlagSet(), + relocations: [ + Relocation { + flags: Elf( + 0, + ), + address: 0, + target_symbol: 0, + addend: 4, + }, + Relocation { + flags: Elf( + 0, + ), + address: 2, + target_symbol: 1, + addend: 0, + }, + Relocation { + flags: Elf( + 0, + ), + address: 4, + target_symbol: 0, + addend: 10, + }, + ], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".data-0", + name: ".data", + address: 0, + size: 12, + kind: Data, + data: SectionData( + 12, + ), + flags: FlagSet(Combined), + relocations: [ + Relocation { + flags: Elf( + 0, + ), + address: 0, + target_symbol: 2, + addend: 0, + }, + Relocation { + flags: Elf( + 0, + ), + address: 4, + target_symbol: 2, + addend: 0, + }, + ], + line_info: { + 0: 1, + 8: 2, + }, + virtual_address: None, + }, + Section { + id: ".data-1", + name: ".data", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".data-2", + name: ".data", + address: 0, + size: 0, + kind: Data, + data: SectionData( + 0, + ), + flags: FlagSet(Hidden), + relocations: [], + line_info: {}, + virtual_address: None, + }, + ], + [ + Symbol { + name: ".data", + demangled_name: None, + address: 0, + size: 0, + kind: Section, + section: Some( + 1, + ), + flags: FlagSet(), + align: None, + virtual_address: None, + }, + Symbol { + name: "symbol", + demangled_name: None, + address: 4, + size: 4, + kind: Object, + section: Some( + 1, + ), + flags: FlagSet(), + align: None, + virtual_address: None, + }, + Symbol { + name: "function", + demangled_name: None, + address: 0, + size: 8, + kind: Function, + section: Some( + 0, + ), + flags: FlagSet(), + align: None, + virtual_address: None, + }, + Symbol { + name: ".data", + demangled_name: None, + address: 0, + size: 0, + kind: Unknown, + section: None, + flags: FlagSet(), + align: None, + virtual_address: None, + }, + ], +) diff --git a/objdiff-core/src/util.rs b/objdiff-core/src/util.rs index b8a32b3..42783ee 100644 --- a/objdiff-core/src/util.rs +++ b/objdiff-core/src/util.rs @@ -1,7 +1,7 @@ use alloc::format; use core::fmt; -use anyhow::Result; +use anyhow::{ensure, Result}; use num_traits::PrimInt; use object::{Endian, Object}; @@ -27,17 +27,15 @@ impl fmt::UpperHex for ReallySigned { } pub fn read_u32(obj_file: &object::File, reader: &mut &[u8]) -> Result { - if reader.len() < 4 { - return Err(anyhow::anyhow!("Not enough bytes to read u32")); - } + ensure!(reader.len() >= 4, "Not enough bytes to read u32"); let value = u32::from_ne_bytes(reader[..4].try_into()?); + *reader = &reader[4..]; Ok(obj_file.endianness().read_u32(value)) } pub fn read_u16(obj_file: &object::File, reader: &mut &[u8]) -> Result { - if reader.len() < 2 { - return Err(anyhow::anyhow!("Not enough bytes to read u16")); - } + ensure!(reader.len() >= 2, "Not enough bytes to read u16"); let value = u16::from_ne_bytes(reader[..2].try_into()?); + *reader = &reader[2..]; Ok(obj_file.endianness().read_u16(value)) } diff --git a/objdiff-core/src/wasm/api.rs b/objdiff-core/src/wasm/api.rs deleted file mode 100644 index a8b5209..0000000 --- a/objdiff-core/src/wasm/api.rs +++ /dev/null @@ -1,99 +0,0 @@ -use alloc::{ - format, - str::FromStr, - string::{String, ToString}, - vec::Vec, -}; -use core::cell::RefCell; - -use prost::Message; - -use crate::{bindings::diff::DiffResult, diff, obj}; - -wit_bindgen::generate!({ - world: "api", -}); - -use exports::objdiff::core::diff::{ - DiffConfigBorrow, Guest as GuestTypes, GuestDiffConfig, GuestObject, Object, ObjectBorrow, -}; - -struct Component; - -impl Guest for Component { - fn init() -> Result<(), String> { - // console_error_panic_hook::set_once(); - // #[cfg(debug_assertions)] - // console_log::init_with_level(log::Level::Debug).map_err(|e| e.to_string())?; - // #[cfg(not(debug_assertions))] - // console_log::init_with_level(log::Level::Info).map_err(|e| e.to_string())?; - Ok(()) - } - - fn version() -> String { env!("CARGO_PKG_VERSION").to_string() } -} - -#[repr(transparent)] -struct ResourceDiffConfig(RefCell); - -impl GuestTypes for Component { - type DiffConfig = ResourceDiffConfig; - type Object = obj::ObjInfo; - - fn run_diff( - left: Option, - right: Option, - diff_config: DiffConfigBorrow, - ) -> Result, String> { - let diff_config = diff_config.get::().0.borrow(); - let result = run_diff_internal( - left.as_ref().map(|o| o.get()), - right.as_ref().map(|o| o.get()), - &diff_config, - &diff::MappingConfig::default(), - ) - .map_err(|e| e.to_string())?; - Ok(result.encode_to_vec()) - } -} - -impl GuestDiffConfig for ResourceDiffConfig { - fn new() -> Self { Self(RefCell::new(diff::DiffObjConfig::default())) } - - fn set_property(&self, key: String, value: String) -> Result<(), String> { - let id = diff::ConfigPropertyId::from_str(&key) - .map_err(|_| format!("Invalid property key {:?}", key))?; - self.0 - .borrow_mut() - .set_property_value_str(id, &value) - .map_err(|_| format!("Invalid property value {:?}", value)) - } - - fn get_property(&self, key: String) -> Result { - let id = diff::ConfigPropertyId::from_str(&key) - .map_err(|_| format!("Invalid property key {:?}", key))?; - Ok(self.0.borrow().get_property_value(id).to_string()) - } -} - -impl GuestObject for obj::ObjInfo { - fn parse(data: Vec, diff_config: DiffConfigBorrow) -> Result { - let diff_config = diff_config.get::().0.borrow(); - obj::read::parse(&data, &diff_config).map(|o| Object::new(o)).map_err(|e| e.to_string()) - } -} - -fn run_diff_internal( - left: Option<&obj::ObjInfo>, - right: Option<&obj::ObjInfo>, - diff_config: &diff::DiffObjConfig, - mapping_config: &diff::MappingConfig, -) -> anyhow::Result { - log::debug!("Running diff with config: {:?}", diff_config); - let result = diff::diff_objs(diff_config, mapping_config, left, right, None)?; - let left = left.and_then(|o| result.left.as_ref().map(|d| (o, d))); - let right = right.and_then(|o| result.right.as_ref().map(|d| (o, d))); - Ok(DiffResult::new(left, right)) -} - -export!(Component); diff --git a/objdiff-core/src/wasm/mod.rs b/objdiff-core/src/wasm/mod.rs deleted file mode 100644 index 894901f..0000000 --- a/objdiff-core/src/wasm/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod api; - -#[cfg(not(feature = "std"))] -mod cabi_realloc; - -#[cfg(not(feature = "std"))] -static mut ARENA: [u8; 10000] = [0; 10000]; - -#[cfg(not(feature = "std"))] -#[global_allocator] -static ALLOCATOR: talc::Talck, talc::ClaimOnOom> = talc::Talc::new(unsafe { - talc::ClaimOnOom::new(talc::Span::from_array(core::ptr::addr_of!(ARENA) as *mut [u8; 10000])) -}) -.lock(); - -#[cfg(not(feature = "std"))] -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } diff --git a/objdiff-core/tests/arch_ppc.rs b/objdiff-core/tests/arch_ppc.rs new file mode 100644 index 0000000..12a62dd --- /dev/null +++ b/objdiff-core/tests/arch_ppc.rs @@ -0,0 +1,79 @@ +use objdiff_core::{ + diff::{self, display}, + obj, + obj::SectionKind, +}; + +mod common; + +#[test] +#[cfg(feature = "ppc")] +fn read_ppc() { + let diff_config = diff::DiffObjConfig::default(); + let obj = obj::read::parse(include_object!("data/ppc/IObj.o"), &diff_config).unwrap(); + insta::assert_debug_snapshot!(obj); + let symbol_idx = + obj.symbols.iter().position(|s| s.name == "Type2Text__10SObjectTagFUi").unwrap(); + let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap(); + insta::assert_debug_snapshot!(diff.instruction_rows); + let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config); + insta::assert_snapshot!(output); +} + +#[test] +#[cfg(feature = "ppc")] +fn read_dwarf1_line_info() { + let diff_config = diff::DiffObjConfig::default(); + let obj = obj::read::parse(include_object!("data/ppc/m_Do_hostIO.o"), &diff_config).unwrap(); + let line_infos = obj + .sections + .iter() + .filter(|s| s.kind == SectionKind::Code) + .map(|s| s.line_info.clone()) + .collect::>(); + insta::assert_debug_snapshot!(line_infos); +} + +#[test] +#[cfg(feature = "ppc")] +fn diff_ppc() { + let diff_config = diff::DiffObjConfig::default(); + let mapping_config = diff::MappingConfig::default(); + let target_obj = + obj::read::parse(include_object!("data/ppc/CDamageVulnerability_target.o"), &diff_config) + .unwrap(); + let base_obj = + obj::read::parse(include_object!("data/ppc/CDamageVulnerability_base.o"), &diff_config) + .unwrap(); + let diff = + diff::diff_objs(Some(&target_obj), Some(&base_obj), None, &diff_config, &mapping_config) + .unwrap(); + + let target_diff = diff.left.as_ref().unwrap(); + let base_diff = diff.right.as_ref().unwrap(); + let sections_display = display::display_sections( + &target_obj, + &target_diff, + display::SymbolFilter::None, + false, + false, + true, + ); + insta::assert_debug_snapshot!(sections_display); + + let target_symbol_idx = target_obj + .symbols + .iter() + .position(|s| s.name == "WeaponHurts__20CDamageVulnerabilityCFRC11CWeaponModei") + .unwrap(); + let target_symbol_diff = &target_diff.symbols[target_symbol_idx]; + let base_symbol_idx = base_obj + .symbols + .iter() + .position(|s| s.name == "WeaponHurts__20CDamageVulnerabilityCFRC11CWeaponModei") + .unwrap(); + let base_symbol_diff = &base_diff.symbols[base_symbol_idx]; + assert_eq!(target_symbol_diff.target_symbol, Some(base_symbol_idx)); + assert_eq!(base_symbol_diff.target_symbol, Some(target_symbol_idx)); + insta::assert_debug_snapshot!((target_symbol_diff, base_symbol_diff)); +} diff --git a/objdiff-core/tests/arch_x86.rs b/objdiff-core/tests/arch_x86.rs new file mode 100644 index 0000000..55605fb --- /dev/null +++ b/objdiff-core/tests/arch_x86.rs @@ -0,0 +1,17 @@ +use objdiff_core::{diff, obj}; + +mod common; + +#[test] +#[cfg(feature = "x86")] +fn read_x86() { + let diff_config = diff::DiffObjConfig::default(); + let obj = obj::read::parse(include_object!("data/x86/rtest.obj"), &diff_config).unwrap(); + insta::assert_debug_snapshot!(obj); + let symbol_idx = + obj.symbols.iter().position(|s| s.name == "Type2Text__10SObjectTagFUi").unwrap(); + let diff = diff::code::no_diff_code(&obj, symbol_idx, &diff_config).unwrap(); + insta::assert_debug_snapshot!(diff.instruction_rows); + let output = common::display_diff(&obj, &diff, symbol_idx, &diff_config); + insta::assert_snapshot!(output); +} diff --git a/objdiff-core/tests/common.rs b/objdiff-core/tests/common.rs new file mode 100644 index 0000000..b5fb408 --- /dev/null +++ b/objdiff-core/tests/common.rs @@ -0,0 +1,57 @@ +use objdiff_core::{ + diff::{DiffObjConfig, SymbolDiff}, + obj::Object, +}; + +pub fn display_diff( + obj: &Object, + diff: &SymbolDiff, + symbol_idx: usize, + diff_config: &DiffObjConfig, +) -> String { + let mut output = String::new(); + for row in &diff.instruction_rows { + output.push('['); + let mut separator = false; + objdiff_core::diff::display::display_row( + &obj, + symbol_idx, + row, + &diff_config, + |text, diff_idx| { + if separator { + output.push_str(", "); + } else { + separator = true; + } + output.push_str(&format!("({:?}, {:?})", text, diff_idx.get())); + Ok(()) + }, + ) + .unwrap(); + output.push_str("]\n"); + } + output +} + +#[repr(C)] +pub struct AlignedAs { + pub _align: [Align; 0], + pub bytes: Bytes, +} + +#[macro_export] +macro_rules! include_bytes_align_as { + ($align_ty:ty, $path:literal) => {{ + static ALIGNED: &common::AlignedAs<$align_ty, [u8]> = + &common::AlignedAs { _align: [], bytes: *include_bytes!($path) }; + &ALIGNED.bytes + }}; +} + +#[macro_export] +macro_rules! include_object { + ($path:literal) => { + include_bytes_align_as!(u32, $path) + }; +} diff --git a/objdiff-core/tests/data/ppc/CDamageVulnerability_base.o b/objdiff-core/tests/data/ppc/CDamageVulnerability_base.o new file mode 100644 index 0000000..b7feb3f Binary files /dev/null and b/objdiff-core/tests/data/ppc/CDamageVulnerability_base.o differ diff --git a/objdiff-core/tests/data/ppc/CDamageVulnerability_target.o b/objdiff-core/tests/data/ppc/CDamageVulnerability_target.o new file mode 100644 index 0000000..484255a Binary files /dev/null and b/objdiff-core/tests/data/ppc/CDamageVulnerability_target.o differ diff --git a/objdiff-core/tests/data/ppc/IObj.o b/objdiff-core/tests/data/ppc/IObj.o new file mode 100644 index 0000000..05cbe50 Binary files /dev/null and b/objdiff-core/tests/data/ppc/IObj.o differ diff --git a/objdiff-core/tests/data/ppc/m_Do_hostIO.o b/objdiff-core/tests/data/ppc/m_Do_hostIO.o new file mode 100644 index 0000000..fd726c1 Binary files /dev/null and b/objdiff-core/tests/data/ppc/m_Do_hostIO.o differ diff --git a/objdiff-core/tests/data/x86/rtest.obj b/objdiff-core/tests/data/x86/rtest.obj new file mode 100644 index 0000000..d06b426 Binary files /dev/null and b/objdiff-core/tests/data/x86/rtest.obj differ diff --git a/objdiff-core/tests/data/x86/staticdebug.obj b/objdiff-core/tests/data/x86/staticdebug.obj new file mode 100644 index 0000000..75afda1 Binary files /dev/null and b/objdiff-core/tests/data/x86/staticdebug.obj differ diff --git a/objdiff-core/tests/snapshots/arch_ppc__diff_ppc-2.snap b/objdiff-core/tests/snapshots/arch_ppc__diff_ppc-2.snap new file mode 100644 index 0000000..81b58a9 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_ppc__diff_ppc-2.snap @@ -0,0 +1,4870 @@ +--- +source: objdiff-core/tests/arch_ppc.rs +expression: "(target_symbol_diff, base_symbol_diff)" +--- +( + SymbolDiff { + target_symbol: Some( + 17, + ), + match_percent: Some( + 98.92086, + ), + diff_score: Some( + ( + 150, + 13900, + ), + ), + instruction_rows: [ + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 632, + size: 4, + opcode: 160, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 636, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 640, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 5, + branch_idx: 0, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 644, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 648, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 7, + branch_idx: 1, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 652, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 2, + ], + branch_idx: 0, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 656, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 660, + size: 4, + opcode: 162, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 4, + ], + branch_idx: 1, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 664, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 668, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 12, + branch_idx: 2, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 672, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 676, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 680, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 9, + ], + branch_idx: 2, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 684, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 688, + size: 4, + opcode: 109, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 692, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 37, + branch_idx: 3, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 696, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 700, + size: 4, + opcode: 36, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 704, + size: 4, + opcode: 129, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 708, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 712, + size: 4, + opcode: 128, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 716, + size: 4, + opcode: 150, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 720, + size: 4, + opcode: 128, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 724, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 728, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 26, + branch_idx: 4, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 732, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 736, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 24, + ], + branch_idx: 4, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 740, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 31, + branch_idx: 5, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 744, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 748, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 31, + branch_idx: 5, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 752, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 756, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 27, + 29, + ], + branch_idx: 5, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 760, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 45, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 764, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 768, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 45, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 772, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 776, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 45, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 780, + size: 4, + opcode: 36, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 15, + ], + branch_idx: 3, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 784, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 788, + size: 4, + opcode: 129, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 792, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 796, + size: 4, + opcode: 150, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 800, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 804, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 45, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 808, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 812, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 32, + 34, + 36, + 43, + ], + branch_idx: 6, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 816, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 81, + branch_idx: 7, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 820, + size: 4, + opcode: 68, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 824, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 828, + size: 4, + opcode: 160, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 832, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 72, + branch_idx: 8, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 836, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 840, + size: 4, + opcode: 36, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 844, + size: 4, + opcode: 129, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 848, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 852, + size: 4, + opcode: 128, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 856, + size: 4, + opcode: 150, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 860, + size: 4, + opcode: 128, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 864, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 868, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 61, + branch_idx: 9, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 872, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 876, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 59, + ], + branch_idx: 9, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 880, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 66, + branch_idx: 10, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 884, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 888, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 66, + branch_idx: 10, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 892, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 896, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 62, + 64, + ], + branch_idx: 10, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 900, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 904, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 908, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 912, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 916, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 920, + size: 4, + opcode: 36, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 50, + ], + branch_idx: 8, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 924, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 928, + size: 4, + opcode: 129, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 932, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 936, + size: 4, + opcode: 150, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 940, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 944, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 948, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 952, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 956, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 46, + ], + branch_idx: 7, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 960, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 67, + 69, + 71, + 78, + 80, + ], + branch_idx: 11, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 964, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 118, + branch_idx: 12, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 968, + size: 4, + opcode: 68, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 972, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 976, + size: 4, + opcode: 160, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 980, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 109, + branch_idx: 13, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 984, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 988, + size: 4, + opcode: 36, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 992, + size: 4, + opcode: 129, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + Some( + 1, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 996, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1000, + size: 4, + opcode: 128, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 4, + ), + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1004, + size: 4, + opcode: 150, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1008, + size: 4, + opcode: 128, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1012, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1016, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 98, + branch_idx: 14, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1020, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1024, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 96, + ], + branch_idx: 14, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1028, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 103, + branch_idx: 15, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1032, + size: 4, + opcode: 38, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1036, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 103, + branch_idx: 15, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1040, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 4, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1044, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 99, + 101, + ], + branch_idx: 15, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 4, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1048, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1052, + size: 4, + opcode: 38, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1056, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1060, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1064, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1068, + size: 4, + opcode: 36, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 87, + ], + branch_idx: 13, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1072, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1076, + size: 4, + opcode: 129, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + Some( + 1, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1080, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1084, + size: 4, + opcode: 150, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1088, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1092, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1096, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1100, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1104, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 83, + ], + branch_idx: 12, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1108, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 104, + 106, + 108, + 115, + 117, + ], + branch_idx: 16, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1112, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1116, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 127, + branch_idx: 17, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1120, + size: 4, + opcode: 162, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1124, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1128, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 127, + branch_idx: 17, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1132, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1136, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 137, + branch_idx: 18, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1140, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 121, + 124, + ], + branch_idx: 17, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1144, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 132, + branch_idx: 19, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1148, + size: 4, + opcode: 162, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1152, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1156, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 137, + branch_idx: 18, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1160, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 128, + ], + branch_idx: 19, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1164, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1168, + size: 4, + opcode: 162, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1172, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1176, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1180, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 126, + 131, + ], + branch_idx: 18, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1184, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + ], + }, + SymbolDiff { + target_symbol: Some( + 7, + ), + match_percent: Some( + 98.92086, + ), + diff_score: Some( + ( + 150, + 13900, + ), + ), + instruction_rows: [ + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 632, + size: 4, + opcode: 160, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 636, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 640, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 5, + branch_idx: 0, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 644, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 648, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 7, + branch_idx: 1, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 652, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 2, + ], + branch_idx: 0, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 656, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 660, + size: 4, + opcode: 162, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 4, + ], + branch_idx: 1, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 664, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 668, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 12, + branch_idx: 2, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 672, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 676, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 680, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 9, + ], + branch_idx: 2, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 684, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 688, + size: 4, + opcode: 109, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 692, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 37, + branch_idx: 3, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 696, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 700, + size: 4, + opcode: 36, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 704, + size: 4, + opcode: 129, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 708, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 712, + size: 4, + opcode: 128, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 716, + size: 4, + opcode: 150, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 720, + size: 4, + opcode: 128, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 724, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 728, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 26, + branch_idx: 4, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 732, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 736, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 24, + ], + branch_idx: 4, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 740, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 31, + branch_idx: 5, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 744, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 748, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 31, + branch_idx: 5, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 752, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 756, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 27, + 29, + ], + branch_idx: 5, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 760, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 45, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 764, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 768, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 45, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 772, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 776, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 45, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 780, + size: 4, + opcode: 36, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 15, + ], + branch_idx: 3, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 784, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 788, + size: 4, + opcode: 129, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 792, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 796, + size: 4, + opcode: 150, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 800, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 804, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 45, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 808, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 812, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 32, + 34, + 36, + 43, + ], + branch_idx: 6, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 816, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 81, + branch_idx: 7, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 820, + size: 4, + opcode: 68, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 824, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 828, + size: 4, + opcode: 160, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 832, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 72, + branch_idx: 8, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 836, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 840, + size: 4, + opcode: 36, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 844, + size: 4, + opcode: 129, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 848, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 852, + size: 4, + opcode: 128, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 856, + size: 4, + opcode: 150, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 860, + size: 4, + opcode: 128, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 864, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 868, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 61, + branch_idx: 9, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 872, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 876, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 59, + ], + branch_idx: 9, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 880, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 66, + branch_idx: 10, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 884, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 888, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 66, + branch_idx: 10, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 892, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 896, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 62, + 64, + ], + branch_idx: 10, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 900, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 904, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 908, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 912, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 916, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 920, + size: 4, + opcode: 36, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 50, + ], + branch_idx: 8, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 924, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 928, + size: 4, + opcode: 129, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 932, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 936, + size: 4, + opcode: 150, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 940, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 944, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 948, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 952, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 82, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 956, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 46, + ], + branch_idx: 7, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 960, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 67, + 69, + 71, + 78, + 80, + ], + branch_idx: 11, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 964, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 118, + branch_idx: 12, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 968, + size: 4, + opcode: 68, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 972, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 976, + size: 4, + opcode: 160, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 980, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 109, + branch_idx: 13, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 984, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 988, + size: 4, + opcode: 36, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 992, + size: 4, + opcode: 129, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + Some( + 1, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 996, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1000, + size: 4, + opcode: 128, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 4, + ), + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1004, + size: 4, + opcode: 150, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1008, + size: 4, + opcode: 128, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1012, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1016, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 98, + branch_idx: 14, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1020, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1024, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 96, + ], + branch_idx: 14, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1028, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 103, + branch_idx: 15, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1032, + size: 4, + opcode: 38, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1036, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 103, + branch_idx: 15, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1040, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 4, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1044, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 99, + 101, + ], + branch_idx: 15, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 4, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1048, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1052, + size: 4, + opcode: 38, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1056, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1060, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1064, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1068, + size: 4, + opcode: 36, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 87, + ], + branch_idx: 13, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 1, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1072, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1076, + size: 4, + opcode: 129, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + Some( + 1, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1080, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1084, + size: 4, + opcode: 150, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1088, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1092, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1096, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1100, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 119, + branch_idx: 16, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1104, + size: 4, + opcode: 41, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 83, + ], + branch_idx: 12, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1108, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 104, + 106, + 108, + 115, + 117, + ], + branch_idx: 16, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1112, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1116, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 127, + branch_idx: 17, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1120, + size: 4, + opcode: 162, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1124, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1128, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 127, + branch_idx: 17, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1132, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: None, + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 3, + ), + ), + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1136, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 137, + branch_idx: 18, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1140, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 121, + 124, + ], + branch_idx: 17, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1144, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 132, + branch_idx: 19, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1148, + size: 4, + opcode: 162, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1152, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1156, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 137, + branch_idx: 18, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1160, + size: 4, + opcode: 60, + }, + ), + kind: ArgMismatch, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 128, + ], + branch_idx: 19, + }, + ), + branch_to: None, + arg_diff: [ + InstructionArgDiffIndex( + None, + ), + InstructionArgDiffIndex( + Some( + 2, + ), + ), + InstructionArgDiffIndex( + None, + ), + ], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1164, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1168, + size: 4, + opcode: 162, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1172, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1176, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1180, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 126, + 131, + ], + branch_idx: 18, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 1184, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + ], + }, +) diff --git a/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap b/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap new file mode 100644 index 0000000..97cbc82 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_ppc__diff_ppc.snap @@ -0,0 +1,90 @@ +--- +source: objdiff-core/tests/arch_ppc.rs +expression: sections_display +--- +[ + SectionDisplay { + id: ".comm", + name: ".comm", + size: 0, + match_percent: None, + symbols: [ + SectionDisplaySymbol { + symbol: 11, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 12, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 13, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 14, + is_mapping_symbol: false, + }, + ], + }, + SectionDisplay { + id: ".ctors-0", + name: ".ctors", + size: 4, + match_percent: Some( + 100.0, + ), + symbols: [ + SectionDisplaySymbol { + symbol: 2, + is_mapping_symbol: false, + }, + ], + }, + SectionDisplay { + id: ".text-0", + name: ".text", + size: 3060, + match_percent: Some( + 59.02353, + ), + symbols: [ + SectionDisplaySymbol { + symbol: 1, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 3, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 10, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 9, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 8, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 7, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 6, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 5, + is_mapping_symbol: false, + }, + SectionDisplaySymbol { + symbol: 4, + is_mapping_symbol: false, + }, + ], + }, +] diff --git a/objdiff-core/tests/snapshots/arch_ppc__read_dwarf1_line_info.snap b/objdiff-core/tests/snapshots/arch_ppc__read_dwarf1_line_info.snap new file mode 100644 index 0000000..3bc8577 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_ppc__read_dwarf1_line_info.snap @@ -0,0 +1,40 @@ +--- +source: objdiff-core/tests/arch_ppc.rs +expression: line_infos +--- +[ + { + 0: 13, + 4: 16, + 32: 17, + 44: 18, + 60: 20, + 76: 21, + 84: 23, + 92: 25, + 108: 26, + 124: 27, + 136: 28, + 144: 29, + 152: 31, + 164: 34, + 184: 35, + 212: 39, + 228: 40, + 236: 41, + 260: 43, + 288: 44, + 292: 45, + 300: 48, + 436: 0, + }, + { + 0: 48, + 132: 35, + 244: 26, + 304: 22, + 312: 23, + 316: 24, + 320: 0, + }, +] diff --git a/objdiff-core/tests/snapshots/arch_ppc__read_ppc-2.snap b/objdiff-core/tests/snapshots/arch_ppc__read_ppc-2.snap new file mode 100644 index 0000000..b56c053 --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_ppc__read_ppc-2.snap @@ -0,0 +1,1008 @@ +--- +source: objdiff-core/tests/arch_ppc.rs +expression: diff.instruction_rows +--- +[ + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 0, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 4, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 8, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 5, + branch_idx: 0, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 12, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 16, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 8, + branch_idx: 1, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 20, + size: 4, + opcode: 42, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 2, + ], + branch_idx: 0, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 24, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 28, + size: 4, + opcode: 94, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 32, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 4, + ], + branch_idx: 1, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 36, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 40, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 44, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 14, + branch_idx: 2, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 48, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 52, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 17, + branch_idx: 3, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 56, + size: 4, + opcode: 42, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 11, + ], + branch_idx: 2, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 60, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 64, + size: 4, + opcode: 94, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 68, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 13, + ], + branch_idx: 3, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 72, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 76, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 80, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 84, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 24, + branch_idx: 4, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 88, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 92, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 27, + branch_idx: 5, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 96, + size: 4, + opcode: 42, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 21, + ], + branch_idx: 4, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 100, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 104, + size: 4, + opcode: 94, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 108, + size: 4, + opcode: 60, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 23, + ], + branch_idx: 5, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 112, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 116, + size: 4, + opcode: 38, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 120, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 124, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 34, + branch_idx: 6, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 128, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 132, + size: 4, + opcode: 45, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 37, + branch_idx: 7, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 136, + size: 4, + opcode: 42, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 31, + ], + branch_idx: 6, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 140, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 144, + size: 4, + opcode: 94, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 148, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 33, + ], + branch_idx: 7, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 152, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 156, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 160, + size: 4, + opcode: 42, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 164, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 168, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 172, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 176, + size: 4, + opcode: 162, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 180, + size: 4, + opcode: 94, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 184, + size: 4, + opcode: 66, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 188, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 49, + branch_idx: 8, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 192, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 196, + size: 4, + opcode: 163, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 47, + ], + branch_idx: 8, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 200, + size: 4, + opcode: 94, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 204, + size: 4, + opcode: 66, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 208, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 54, + branch_idx: 9, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 212, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 216, + size: 4, + opcode: 163, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 52, + ], + branch_idx: 9, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 220, + size: 4, + opcode: 94, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 224, + size: 4, + opcode: 66, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 228, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 59, + branch_idx: 10, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 232, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 236, + size: 4, + opcode: 163, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 57, + ], + branch_idx: 10, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 240, + size: 4, + opcode: 94, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 244, + size: 4, + opcode: 66, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 248, + size: 4, + opcode: 43, + }, + ), + kind: None, + branch_from: None, + branch_to: Some( + InstructionBranchTo { + ins_idx: 64, + branch_idx: 11, + }, + ), + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 252, + size: 4, + opcode: 166, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 256, + size: 4, + opcode: 41, + }, + ), + kind: None, + branch_from: Some( + InstructionBranchFrom { + ins_idx: [ + 62, + ], + branch_idx: 11, + }, + ), + branch_to: None, + arg_diff: [], + }, + InstructionDiffRow { + ins_ref: Some( + InstructionRef { + address: 260, + size: 4, + opcode: 47, + }, + ), + kind: None, + branch_from: None, + branch_to: None, + arg_diff: [], + }, +] diff --git a/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap b/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap new file mode 100644 index 0000000..44e4bce --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_ppc__read_ppc-3.snap @@ -0,0 +1,70 @@ +--- +source: objdiff-core/tests/arch_ppc.rs +expression: output +--- +[(Address(0), None), (Spacing(4), None), (Opcode("srwi", 60), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("24")), None), (Eol, None)] +[(Address(4), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] +[(Address(8), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(20), None), (Basic(" ~>"), Some(0)), (Eol, None)] +[(Address(12), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] +[(Address(16), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(32), None), (Basic(" ~>"), Some(1)), (Eol, None)] +[(Address(20), None), (Basic(" ~> "), Some(0)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] +[(Address(24), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] +[(Address(28), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r0")), None), (Eol, None)] +[(Address(32), None), (Basic(" ~> "), Some(1)), (Opcode("extrwi", 60), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Eol, None)] +[(Address(36), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] +[(Address(40), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] +[(Address(44), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(56), None), (Basic(" ~>"), Some(2)), (Eol, None)] +[(Address(48), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] +[(Address(52), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(68), None), (Basic(" ~>"), Some(3)), (Eol, None)] +[(Address(56), None), (Basic(" ~> "), Some(2)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] +[(Address(60), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] +[(Address(64), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r5")), None), (Eol, None)] +[(Address(68), None), (Basic(" ~> "), Some(3)), (Opcode("extrwi", 60), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("8")), None), (Basic(", "), None), (Argument(Opaque("16")), None), (Eol, None)] +[(Address(72), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] +[(Address(76), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] +[(Address(80), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r4")), None), (Basic(")"), None), (Eol, None)] +[(Address(84), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(96), None), (Basic(" ~>"), Some(4)), (Eol, None)] +[(Address(88), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] +[(Address(92), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(108), None), (Basic(" ~>"), Some(5)), (Eol, None)] +[(Address(96), None), (Basic(" ~> "), Some(4)), (Opcode("lis", 42), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] +[(Address(100), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] +[(Address(104), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r5")), None), (Eol, None)] +[(Address(108), None), (Basic(" ~> "), Some(5)), (Opcode("clrlwi", 60), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("24")), None), (Eol, None)] +[(Address(112), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] +[(Address(116), None), (Spacing(4), None), (Opcode("cmpwi", 38), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] +[(Address(120), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(2)), None), (Basic("("), None), (Argument(Opaque("r3")), None), (Basic(")"), None), (Eol, None)] +[(Address(124), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(136), None), (Basic(" ~>"), Some(6)), (Eol, None)] +[(Address(128), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(-1)), None), (Eol, None)] +[(Address(132), None), (Spacing(4), None), (Opcode("b", 45), None), (BranchDest(148), None), (Basic(" ~>"), Some(7)), (Eol, None)] +[(Address(136), None), (Basic(" ~> "), Some(6)), (Opcode("lis", 42), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] +[(Address(140), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] +[(Address(144), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Eol, None)] +[(Address(148), None), (Basic(" ~> "), Some(7)), (Opcode("li", 41), None), (Argument(Opaque("r5")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] +[(Address(152), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] +[(Address(156), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(3)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(160), None), (Spacing(4), None), (Opcode("lis", 42), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(0)), None), (Eol, None)] +[(Address(164), None), (Spacing(4), None), (Opcode("addi", 41), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Eol, None)] +[(Address(168), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(4)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(172), None), (Spacing(4), None), (Opcode("li", 41), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(45)), None), (Eol, None)] +[(Address(176), None), (Spacing(4), None), (Opcode("lbz", 162), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] +[(Address(180), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)] +[(Address(184), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)] +[(Address(188), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(196), None), (Basic(" ~>"), Some(8)), (Eol, None)] +[(Address(192), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(196), None), (Basic(" ~> "), Some(8)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(200), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)] +[(Address(204), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)] +[(Address(208), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(216), None), (Basic(" ~>"), Some(9)), (Eol, None)] +[(Address(212), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(216), None), (Basic(" ~> "), Some(9)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(220), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)] +[(Address(224), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)] +[(Address(228), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(236), None), (Basic(" ~>"), Some(10)), (Eol, None)] +[(Address(232), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(236), None), (Basic(" ~> "), Some(10)), (Opcode("lbzu", 163), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Signed(1)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(240), None), (Spacing(4), None), (Opcode("lbzx", 94), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r4")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Eol, None)] +[(Address(244), None), (Spacing(4), None), (Opcode("andi.", 66), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Argument(Unsigned(220)), None), (Eol, None)] +[(Address(248), None), (Spacing(4), None), (Opcode("bne", 43), None), (BranchDest(256), None), (Basic(" ~>"), Some(11)), (Eol, None)] +[(Address(252), None), (Spacing(4), None), (Opcode("stb", 166), None), (Argument(Opaque("r0")), None), (Basic(", "), None), (Argument(Signed(0)), None), (Basic("("), None), (Argument(Opaque("r5")), None), (Basic(")"), None), (Eol, None)] +[(Address(256), None), (Basic(" ~> "), Some(11)), (Opcode("li", 41), None), (Argument(Opaque("r3")), None), (Basic(", "), None), (Symbol(Symbol { name: "text$52", demangled_name: None, address: 8, size: 5, kind: Object, section: Some(2), flags: FlagSet(Local), align: None, virtual_address: Some(2153420056) }), None), (Basic("@sda21"), None), (Eol, None)] +[(Address(260), None), (Spacing(4), None), (Opcode("blr", 47), None), (Eol, None)] diff --git a/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap b/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap new file mode 100644 index 0000000..c1c445d --- /dev/null +++ b/objdiff-core/tests/snapshots/arch_ppc__read_ppc.snap @@ -0,0 +1,489 @@ +--- +source: objdiff-core/tests/arch_ppc.rs +expression: obj +--- +Object { + arch: ArchPpc { + extab: None, + }, + symbols: [ + Symbol { + name: "IObj.cpp", + demangled_name: None, + address: 0, + size: 0, + kind: Unknown, + section: None, + flags: FlagSet(Local), + align: None, + virtual_address: Some( + 0, + ), + }, + Symbol { + name: "[.text]", + demangled_name: None, + address: 0, + size: 0, + kind: Section, + section: Some( + 0, + ), + flags: FlagSet(Local), + align: None, + virtual_address: Some( + 2150895620, + ), + }, + Symbol { + name: "[.ctors]", + demangled_name: None, + address: 0, + size: 0, + kind: Section, + section: Some( + 1, + ), + flags: FlagSet(Local), + align: None, + virtual_address: Some( + 2151461704, + ), + }, + Symbol { + name: "[.sbss]", + demangled_name: None, + address: 0, + size: 0, + kind: Section, + section: Some( + 2, + ), + flags: FlagSet(Local), + align: None, + virtual_address: Some( + 2153420048, + ), + }, + Symbol { + name: "__sinit_IObj_cpp", + demangled_name: None, + address: 264, + size: 20, + kind: Function, + section: Some( + 0, + ), + flags: FlagSet(Local), + align: None, + virtual_address: Some( + 2150895884, + ), + }, + Symbol { + name: "text$52", + demangled_name: None, + address: 8, + size: 5, + kind: Object, + section: Some( + 2, + ), + flags: FlagSet(Local), + align: None, + virtual_address: Some( + 2153420056, + ), + }, + Symbol { + name: "Type2Text__10SObjectTagFUi", + demangled_name: Some( + "SObjectTag::Type2Text(unsigned int)", + ), + address: 0, + size: 264, + kind: Function, + section: Some( + 0, + ), + flags: FlagSet(Global), + align: None, + virtual_address: Some( + 2150895620, + ), + }, + Symbol { + name: "gkInvalidObjectTag", + demangled_name: None, + address: 0, + size: 8, + kind: Object, + section: Some( + 2, + ), + flags: FlagSet(Global), + align: None, + virtual_address: Some( + 2153420048, + ), + }, + Symbol { + name: "__upper_map", + demangled_name: None, + address: 0, + size: 0, + kind: Unknown, + section: None, + flags: FlagSet(Global), + align: None, + virtual_address: Some( + 0, + ), + }, + Symbol { + name: "__ctype_map", + demangled_name: None, + address: 0, + size: 0, + kind: Unknown, + section: None, + flags: FlagSet(Global), + align: None, + virtual_address: Some( + 0, + ), + }, + ], + sections: [ + Section { + id: ".text-0", + name: ".text", + address: 0, + size: 284, + kind: Code, + data: SectionData( + 284, + ), + flags: FlagSet(), + relocations: [ + Relocation { + flags: Elf( + 6, + ), + address: 22, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Elf( + 4, + ), + address: 26, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Elf( + 109, + ), + address: 36, + target_symbol: 5, + addend: 0, + }, + Relocation { + flags: Elf( + 6, + ), + address: 58, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Elf( + 4, + ), + address: 62, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Elf( + 109, + ), + address: 72, + target_symbol: 5, + addend: 0, + }, + Relocation { + flags: Elf( + 6, + ), + address: 98, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Elf( + 4, + ), + address: 102, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Elf( + 109, + ), + address: 112, + target_symbol: 5, + addend: 0, + }, + Relocation { + flags: Elf( + 6, + ), + address: 138, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Elf( + 4, + ), + address: 142, + target_symbol: 8, + addend: 0, + }, + Relocation { + flags: Elf( + 109, + ), + address: 148, + target_symbol: 5, + addend: 0, + }, + Relocation { + flags: Elf( + 6, + ), + address: 162, + target_symbol: 9, + addend: 0, + }, + Relocation { + flags: Elf( + 4, + ), + address: 166, + target_symbol: 9, + addend: 0, + }, + Relocation { + flags: Elf( + 109, + ), + address: 176, + target_symbol: 5, + addend: 0, + }, + Relocation { + flags: Elf( + 109, + ), + address: 256, + target_symbol: 5, + addend: 0, + }, + Relocation { + flags: Elf( + 109, + ), + address: 268, + target_symbol: 7, + addend: 0, + }, + Relocation { + flags: Elf( + 109, + ), + address: 272, + target_symbol: 7, + addend: 0, + }, + ], + line_info: {}, + virtual_address: Some( + 2150895620, + ), + }, + Section { + id: ".ctors-0", + name: ".ctors", + address: 0, + size: 4, + kind: Data, + data: SectionData( + 4, + ), + flags: FlagSet(), + relocations: [ + Relocation { + flags: Elf( + 1, + ), + address: 0, + target_symbol: 4, + addend: 0, + }, + ], + line_info: {}, + virtual_address: Some( + 2151461704, + ), + }, + Section { + id: ".sbss-0", + name: ".sbss", + address: 0, + size: 16, + kind: Bss, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: Some( + 2153420048, + ), + }, + Section { + id: ".rela.text-0", + name: ".rela.text", + address: 0, + size: 216, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".rela.ctors-0", + name: ".rela.ctors", + address: 0, + size: 12, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".symtab-0", + name: ".symtab", + address: 0, + size: 176, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".strtab-0", + name: ".strtab", + address: 0, + size: 105, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".shstrtab-0", + name: ".shstrtab", + address: 0, + size: 77, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".comment-0", + name: ".comment", + address: 0, + size: 132, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + Section { + id: ".note.split-0", + name: ".note.split", + address: 0, + size: 152, + kind: Unknown, + data: SectionData( + 0, + ), + flags: FlagSet(), + relocations: [], + line_info: {}, + virtual_address: None, + }, + ], + split_meta: Some( + SplitMeta { + generator: Some( + "decomp-toolkit 1.4.0", + ), + module_name: Some( + "main", + ), + module_id: Some( + 0, + ), + virtual_addresses: Some( + [ + 0, + 0, + 2150895620, + 2151461704, + 2153420048, + 2150895884, + 2153420056, + 2150895620, + 2153420048, + 0, + 0, + ], + ), + }, + ), + path: None, + timestamp: None, +} diff --git a/objdiff-core/wit/objdiff.wit b/objdiff-core/wit/objdiff.wit deleted file mode 100644 index 73007b2..0000000 --- a/objdiff-core/wit/objdiff.wit +++ /dev/null @@ -1,29 +0,0 @@ -package objdiff:core; - -interface diff { - resource diff-config { - constructor(); - set-property: func(id: string, value: string) -> result<_, string>; - get-property: func(id: string) -> result; - } - - resource object { - parse: static func( - data: list, - config: borrow, - ) -> result; - } - - run-diff: func( - left: option>, - right: option>, - config: borrow, - ) -> result, string>; -} - -world api { - export diff; - - export init: func() -> result<_, string>; - export version: func() -> string; -} diff --git a/objdiff-gui/Cargo.toml b/objdiff-gui/Cargo.toml index 9c44842..28ee962 100644 --- a/objdiff-gui/Cargo.toml +++ b/objdiff-gui/Cargo.toml @@ -38,7 +38,7 @@ float-ord = "0.3" font-kit = "0.14" globset = { version = "0.4", features = ["serde1"] } log = "0.4" -objdiff-core = { path = "../objdiff-core", features = ["all"] } +objdiff-core = { path = "../objdiff-core", features = ["ppc", "arm", "arm64", "mips", "std", "config", "dwarf", "bindings", "serde", "build"] } open = "5.3" png = "0.17" pollster = "0.4" @@ -52,6 +52,8 @@ shell-escape = "0.1" strum = { version = "0.26", features = ["derive"] } time = { version = "0.3", features = ["formatting", "local-offset"] } typed-path = "0.10" +winit = { version = "0.30", features = ["wayland-csd-adwaita"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } # Keep version in sync with egui [dependencies.eframe] @@ -81,15 +83,6 @@ winapi = "0.3" [target.'cfg(unix)'.dependencies] exec = "0.3" -# native: -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tracing-subscriber = { version = "0.3", features = ["env-filter"] } - -# web: -[target.'cfg(target_arch = "wasm32")'.dependencies] -console_error_panic_hook = "0.1" -tracing-wasm = "0.2" - [build-dependencies] anyhow = "1.0" diff --git a/objdiff-gui/src/app.rs b/objdiff-gui/src/app.rs index a38bf9e..a58af1b 100644 --- a/objdiff-gui/src/app.rs +++ b/objdiff-gui/src/app.rs @@ -783,7 +783,8 @@ impl eframe::App for App { let mut action = None; egui::CentralPanel::default().show(ctx, |ui| { - action = diff_view_ui(ui, diff_state, appearance); + let state = state.read().unwrap(); + action = diff_view_ui(ui, diff_state, appearance, &state.config.diff_obj_config); }); project_window(ctx, state, show_project_config, config_state, appearance); diff --git a/objdiff-gui/src/app_config.rs b/objdiff-gui/src/app_config.rs index 0f26876..257b2bb 100644 --- a/objdiff-gui/src/app_config.rs +++ b/objdiff-gui/src/app_config.rs @@ -273,6 +273,7 @@ impl DiffObjConfigV1 { }, space_between_args: self.space_between_args, combine_data_sections: self.combine_data_sections, + combine_text_sections: false, x86_formatter: self.x86_formatter, mips_abi: self.mips_abi, mips_instr_category: self.mips_instr_category, diff --git a/objdiff-gui/src/main.rs b/objdiff-gui/src/main.rs index 6312855..e9e1e3e 100644 --- a/objdiff-gui/src/main.rs +++ b/objdiff-gui/src/main.rs @@ -1,3 +1,4 @@ +#![allow(clippy::too_many_arguments)] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release mod app; @@ -37,8 +38,6 @@ fn load_icon() -> Result { const APP_NAME: &str = "objdiff"; -// When compiling natively: -#[cfg(not(target_arch = "wasm32"))] fn main() -> ExitCode { // Log to stdout (if you run with `RUST_LOG=debug`). tracing_subscriber::fmt() @@ -229,21 +228,3 @@ fn run_eframe( }), ) } - -// when compiling to web using trunk. -#[cfg(target_arch = "wasm32")] -fn main() { - // Make sure panics are logged using `console.error`. - console_error_panic_hook::set_once(); - - // Redirect tracing to console.log and friends: - tracing_wasm::set_as_global_default(); - - let web_options = eframe::WebOptions::default(); - eframe::start_web( - "the_canvas_id", // hardcode it - web_options, - Box::new(|cc| Box::new(eframe_template::TemplateApp::new(cc))), - ) - .expect("failed to start eframe"); -} diff --git a/objdiff-gui/src/views/data_diff.rs b/objdiff-gui/src/views/data_diff.rs index 6ee91ee..c33e87f 100644 --- a/objdiff-gui/src/views/data_diff.rs +++ b/objdiff-gui/src/views/data_diff.rs @@ -1,13 +1,9 @@ -use std::{ - cmp::{min, Ordering}, - default::Default, - mem::take, -}; +use std::{cmp::min, default::Default, mem::take}; use egui::{text::LayoutJob, Label, Sense, Widget}; use objdiff_core::{ - diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff}, - obj::ObjInfo, + diff::{DataDiff, DataDiffKind, DataRelocationDiff}, + obj::Object, }; use crate::views::{appearance::Appearance, write_text}; @@ -16,108 +12,110 @@ pub(crate) const BYTES_PER_ROW: usize = 16; fn data_row_hover_ui( ui: &mut egui::Ui, - obj: &ObjInfo, - diffs: &[(ObjDataDiff, Vec)], - appearance: &Appearance, + _obj: &Object, + _diffs: &[(DataDiff, Vec)], + _appearance: &Appearance, ) { ui.scope(|ui| { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs); - let mut prev_reloc = None; - for reloc_diff in reloc_diffs { - let reloc = &reloc_diff.reloc; - if prev_reloc == Some(reloc) { - // Avoid showing consecutive duplicate relocations. - // We do this because a single relocation can span across multiple diffs if the - // bytes in the relocation changed (e.g. first byte is added, second is unchanged). - continue; - } - prev_reloc = Some(reloc); - - let color = get_color_for_diff_kind(reloc_diff.kind, appearance); - - // TODO: Most of this code is copy-pasted from ins_hover_ui. - // Try to separate this out into a shared function. - ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags))); - ui.label(format!("Relocation address: {:x}", reloc.address)); - let addend_str = match reloc.addend.cmp(&0i64) { - Ordering::Greater => format!("+{:x}", reloc.addend), - Ordering::Less => format!("-{:x}", -reloc.addend), - _ => "".to_string(), - }; - ui.colored_label(color, format!("Name: {}{}", reloc.target.name, addend_str)); - if let Some(orig_section_index) = reloc.target.orig_section_index { - if let Some(section) = - obj.sections.iter().find(|s| s.orig_index == orig_section_index) - { - ui.colored_label(color, format!("Section: {}", section.name)); - } - ui.colored_label( - color, - format!("Address: {:x}{}", reloc.target.address, addend_str), - ); - ui.colored_label(color, format!("Size: {:x}", reloc.target.size)); - if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {} - } else { - ui.colored_label(color, "Extern".to_string()); - } - } + // TODO + // let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs); + // let mut prev_reloc = None; + // for reloc_diff in reloc_diffs { + // let reloc = &reloc_diff.reloc; + // if prev_reloc == Some(reloc) { + // // Avoid showing consecutive duplicate relocations. + // // We do this because a single relocation can span across multiple diffs if the + // // bytes in the relocation changed (e.g. first byte is added, second is unchanged). + // continue; + // } + // prev_reloc = Some(reloc); + // + // let color = get_color_for_diff_kind(reloc_diff.kind, appearance); + // + // // TODO: Most of this code is copy-pasted from ins_hover_ui. + // // Try to separate this out into a shared function. + // ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags))); + // ui.label(format!("Relocation address: {:x}", reloc.address)); + // let addend_str = match reloc.addend.cmp(&0i64) { + // Ordering::Greater => format!("+{:x}", reloc.addend), + // Ordering::Less => format!("-{:x}", -reloc.addend), + // _ => "".to_string(), + // }; + // ui.colored_label(color, format!("Name: {}{}", reloc.target.name, addend_str)); + // if let Some(orig_section_index) = reloc.target.orig_section_index { + // if let Some(section) = + // obj.sections.iter().find(|s| s.orig_index == orig_section_index) + // { + // ui.colored_label(color, format!("Section: {}", section.name)); + // } + // ui.colored_label( + // color, + // format!("Address: {:x}{}", reloc.target.address, addend_str), + // ); + // ui.colored_label(color, format!("Size: {:x}", reloc.target.size)); + // if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {} + // } else { + // ui.colored_label(color, "Extern".to_string()); + // } + // } }); } -fn data_row_context_menu(ui: &mut egui::Ui, diffs: &[(ObjDataDiff, Vec)]) { +fn data_row_context_menu(ui: &mut egui::Ui, _diffs: &[(DataDiff, Vec)]) { ui.scope(|ui| { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs); - let mut prev_reloc = None; - for reloc_diff in reloc_diffs { - let reloc = &reloc_diff.reloc; - if prev_reloc == Some(reloc) { - // Avoid showing consecutive duplicate relocations. - // We do this because a single relocation can span across multiple diffs if the - // bytes in the relocation changed (e.g. first byte is added, second is unchanged). - continue; - } - prev_reloc = Some(reloc); - - // TODO: This code is copy-pasted from ins_context_menu. - // Try to separate this out into a shared function. - if let Some(name) = &reloc.target.demangled_name { - if ui.button(format!("Copy \"{name}\"")).clicked() { - ui.output_mut(|output| output.copied_text.clone_from(name)); - ui.close_menu(); - } - } - if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() { - ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name)); - ui.close_menu(); - } - } + // TODO + // let reloc_diffs = diffs.iter().flat_map(|(_, reloc_diffs)| reloc_diffs); + // let mut prev_reloc = None; + // for reloc_diff in reloc_diffs { + // let reloc = &reloc_diff.reloc; + // if prev_reloc == Some(reloc) { + // // Avoid showing consecutive duplicate relocations. + // // We do this because a single relocation can span across multiple diffs if the + // // bytes in the relocation changed (e.g. first byte is added, second is unchanged). + // continue; + // } + // prev_reloc = Some(reloc); + // + // // TODO: This code is copy-pasted from ins_context_menu. + // // Try to separate this out into a shared function. + // if let Some(name) = &reloc.target.demangled_name { + // if ui.button(format!("Copy \"{name}\"")).clicked() { + // ui.output_mut(|output| output.copied_text.clone_from(name)); + // ui.close_menu(); + // } + // } + // if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() { + // ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name)); + // ui.close_menu(); + // } + // } }); } -fn get_color_for_diff_kind(diff_kind: ObjDataDiffKind, appearance: &Appearance) -> egui::Color32 { +fn get_color_for_diff_kind(diff_kind: DataDiffKind, appearance: &Appearance) -> egui::Color32 { match diff_kind { - ObjDataDiffKind::None => appearance.text_color, - ObjDataDiffKind::Replace => appearance.replace_color, - ObjDataDiffKind::Delete => appearance.delete_color, - ObjDataDiffKind::Insert => appearance.insert_color, + DataDiffKind::None => appearance.text_color, + DataDiffKind::Replace => appearance.replace_color, + DataDiffKind::Delete => appearance.delete_color, + DataDiffKind::Insert => appearance.insert_color, } } pub(crate) fn data_row_ui( ui: &mut egui::Ui, - obj: Option<&ObjInfo>, + obj: Option<&Object>, address: usize, - diffs: &[(ObjDataDiff, Vec)], + diffs: &[(DataDiff, Vec)], appearance: &Appearance, ) { if diffs.iter().any(|(dd, rds)| { - dd.kind != ObjDataDiffKind::None || rds.iter().any(|rd| rd.kind != ObjDataDiffKind::None) + dd.kind != DataDiffKind::None || rds.iter().any(|rd| rd.kind != DataDiffKind::None) }) { ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color); } @@ -145,7 +143,7 @@ pub(crate) fn data_row_ui( for byte in &diff.data { let mut byte_color = base_color; if let Some(reloc_diff) = reloc_diffs.iter().find(|reloc_diff| { - reloc_diff.kind != ObjDataDiffKind::None + reloc_diff.kind != DataDiffKind::None && reloc_diff.range.contains(&cur_addr_actual) }) { byte_color = get_color_for_diff_kind(reloc_diff.kind, appearance); @@ -200,11 +198,11 @@ pub(crate) fn data_row_ui( } pub(crate) fn split_diffs( - diffs: &[ObjDataDiff], - reloc_diffs: &[ObjDataRelocDiff], -) -> Vec)>> { - let mut split_diffs = Vec::)>>::new(); - let mut row_diffs = Vec::<(ObjDataDiff, Vec)>::new(); + diffs: &[DataDiff], + reloc_diffs: &[DataRelocationDiff], +) -> Vec)>> { + let mut split_diffs = Vec::)>>::new(); + let mut row_diffs = Vec::<(DataDiff, Vec)>::new(); // The offset shown on the side of the GUI, shifted by insertions/deletions. let mut cur_addr = 0usize; // The offset into the actual bytes of the section on this side, ignoring differences. @@ -216,7 +214,7 @@ pub(crate) fn split_diffs( let mut remaining_in_row = BYTES_PER_ROW - (cur_addr % BYTES_PER_ROW); let len = min(remaining_len, remaining_in_row); - let data_diff = ObjDataDiff { + let data_diff = DataDiff { data: if diff.data.is_empty() { Vec::new() } else { @@ -226,7 +224,7 @@ pub(crate) fn split_diffs( len, symbol: String::new(), // TODO }; - let row_reloc_diffs: Vec = if diff.data.is_empty() { + let row_reloc_diffs: Vec = if diff.data.is_empty() { Vec::new() } else { let diff_range = cur_addr_actual + cur_len..cur_addr_actual + cur_len + len; diff --git a/objdiff-gui/src/views/diff.rs b/objdiff-gui/src/views/diff.rs index 886a617..4c4938b 100644 --- a/objdiff-gui/src/views/diff.rs +++ b/objdiff-gui/src/views/diff.rs @@ -1,8 +1,8 @@ use egui::{Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget}; use objdiff_core::{ build::BuildStatus, - diff::{ObjDiff, ObjSectionDiff, ObjSymbolDiff}, - obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, SymbolRef}, + diff::{display::SymbolFilter, DiffObjConfig, ObjectDiff, SectionDiff, SymbolDiff}, + obj::{Object, Section, SectionKind, Symbol}, }; use time::format_description; @@ -16,30 +16,30 @@ use crate::{ function_diff::{asm_col_ui, FunctionDiffContext}, symbol_diff::{ match_color_for_symbol, symbol_list_ui, DiffViewAction, DiffViewNavigation, - DiffViewState, SymbolDiffContext, SymbolFilter, SymbolRefByName, View, + DiffViewState, SymbolDiffContext, SymbolRefByName, View, }, }, }; #[derive(Clone, Copy)] enum SelectedSymbol { - Symbol(SymbolRef), + Symbol(usize), Section(usize), } #[derive(Clone, Copy)] struct DiffColumnContext<'a> { status: &'a BuildStatus, - obj: Option<&'a (ObjInfo, ObjDiff)>, - section: Option<(&'a ObjSection, &'a ObjSectionDiff)>, - symbol: Option<(&'a ObjSymbol, &'a ObjSymbolDiff)>, + obj: Option<&'a (Object, ObjectDiff)>, + section: Option<(&'a Section, &'a SectionDiff, usize)>, + symbol: Option<(&'a Symbol, &'a SymbolDiff, usize)>, } impl<'a> DiffColumnContext<'a> { pub fn new( view: View, status: &'a BuildStatus, - obj: Option<&'a (ObjInfo, ObjDiff)>, + obj: Option<&'a (Object, ObjectDiff)>, selected_symbol: Option<&SymbolRefByName>, ) -> Self { let selected_symbol = match view { @@ -57,15 +57,18 @@ impl<'a> DiffColumnContext<'a> { }; let (section, symbol) = match (obj, selected_symbol) { (Some((obj, obj_diff)), Some(SelectedSymbol::Symbol(symbol_ref))) => { - let (section, symbol) = obj.section_symbol(symbol_ref); + let symbol = &obj.symbols[symbol_ref]; ( - section.map(|s| (s, obj_diff.section_diff(symbol_ref.section_idx))), - Some((symbol, obj_diff.symbol_diff(symbol_ref))), + symbol.section.map(|section_idx| { + (&obj.sections[section_idx], &obj_diff.sections[section_idx], section_idx) + }), + Some((symbol, &obj_diff.symbols[symbol_ref], symbol_ref)), ) } - (Some((obj, obj_diff)), Some(SelectedSymbol::Section(section_idx))) => { - (Some((&obj.sections[section_idx], obj_diff.section_diff(section_idx))), None) - } + (Some((obj, obj_diff)), Some(SelectedSymbol::Section(section_idx))) => ( + Some((&obj.sections[section_idx], &obj_diff.sections[section_idx], section_idx)), + None, + ), _ => (None, None), }; Self { status, obj, section, symbol } @@ -77,8 +80,8 @@ impl<'a> DiffColumnContext<'a> { #[inline] pub fn id(&self) -> Option<&str> { self.symbol - .map(|(symbol, _)| symbol.name.as_str()) - .or_else(|| self.section.map(|(section, _)| section.name.as_str())) + .map(|(symbol, _, _)| symbol.name.as_str()) + .or_else(|| self.section.map(|(section, _, _)| section.name.as_str())) } } @@ -87,6 +90,7 @@ pub fn diff_view_ui( ui: &mut Ui, state: &DiffViewState, appearance: &Appearance, + diff_config: &DiffObjConfig, ) -> Option { let mut ret = None; let Some(result) = &state.build else { @@ -113,12 +117,15 @@ pub fn diff_view_ui( right_symbol: state.symbol_state.right_symbol.clone(), }; let mut navigation = current_navigation.clone(); - if let Some((_symbol, symbol_diff)) = left_ctx.symbol { + if let Some((_symbol, symbol_diff, _symbol_idx)) = left_ctx.symbol { // If a matching symbol appears, select it if !right_ctx.has_symbol() { if let Some(target_symbol_ref) = symbol_diff.target_symbol { - let (target_section, target_symbol) = - right_ctx.obj.unwrap().0.section_symbol(target_symbol_ref); + let (right_obj, _) = right_ctx.obj.unwrap(); + let target_symbol = &right_obj.symbols[target_symbol_ref]; + let target_section = target_symbol + .section + .and_then(|section_idx| right_obj.sections.get(section_idx)); navigation.right_symbol = Some(SymbolRefByName::new(target_symbol, target_section)); } } @@ -129,12 +136,15 @@ pub fn diff_view_ui( // Clear selection if symbol goes missing navigation.left_symbol = None; } - if let Some((_symbol, symbol_diff)) = right_ctx.symbol { + if let Some((_symbol, symbol_diff, _symbol_idx)) = right_ctx.symbol { // If a matching symbol appears, select it if !left_ctx.has_symbol() { if let Some(target_symbol_ref) = symbol_diff.target_symbol { - let (target_section, target_symbol) = - left_ctx.obj.unwrap().0.section_symbol(target_symbol_ref); + let (left_obj, _) = left_ctx.obj.unwrap(); + let target_symbol = &left_obj.symbols[target_symbol_ref]; + let target_section = target_symbol + .section + .and_then(|section_idx| left_obj.sections.get(section_idx)); navigation.left_symbol = Some(SymbolRefByName::new(target_symbol, target_section)); } } @@ -170,7 +180,7 @@ pub fn diff_view_ui( ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff())); } - if let Some((symbol, _)) = left_ctx.symbol { + if let Some((symbol, _, _)) = left_ctx.symbol { ui.separator(); if ui .add_enabled( @@ -210,13 +220,13 @@ pub fn diff_view_ui( .color(appearance.replace_color), ); } - } else if let Some((symbol, _)) = left_ctx.symbol { + } else if let Some((symbol, _, _)) = left_ctx.symbol { ui.label( RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name)) .font(appearance.code_font.clone()) .color(appearance.highlight_color), ); - } else if let Some((section, _)) = left_ctx.section { + } else if let Some((section, _, _)) = left_ctx.section { ui.label( RichText::new(section.name.clone()) .font(appearance.code_font.clone()) @@ -331,13 +341,13 @@ pub fn diff_view_ui( .color(appearance.replace_color), ); } - } else if let Some((symbol, _)) = right_ctx.symbol { + } else if let Some((symbol, _, _)) = right_ctx.symbol { ui.label( RichText::new(symbol.demangled_name.as_deref().unwrap_or(&symbol.name)) .font(appearance.code_font.clone()) .color(appearance.highlight_color), ); - } else if let Some((section, _)) = right_ctx.section { + } else if let Some((section, _, _)) = right_ctx.section { ui.label( RichText::new(section.name.clone()) .font(appearance.code_font.clone()) @@ -359,16 +369,29 @@ pub fn diff_view_ui( // Third row ui.horizontal(|ui| { - if let Some((_, symbol_diff)) = right_ctx.symbol { + if let Some((_, symbol_diff, _symbol_idx)) = right_ctx.symbol { + let mut needs_separator = false; if let Some(match_percent) = symbol_diff.match_percent { - ui.label( - RichText::new(format!("{:.0}%", match_percent.floor())) + let response = ui.label( + RichText::new(format!("{:.2}%", match_percent)) .font(appearance.code_font.clone()) .color(match_color_for_symbol(match_percent, appearance)), ); + if let Some((diff_score, max_score)) = symbol_diff.diff_score { + response.on_hover_ui_at_pointer(|ui| { + ui.label( + RichText::new(format!("Score: {}/{}", diff_score, max_score)) + .font(appearance.code_font.clone()) + .color(appearance.text_color), + ); + }); + } + needs_separator = true; } if state.current_view == View::FunctionDiff && left_ctx.has_symbol() { - ui.separator(); + if needs_separator { + ui.separator(); + } if ui .button("Change base") .on_hover_text_at_pointer( @@ -413,17 +436,17 @@ pub fn diff_view_ui( View::FunctionDiff, Some((left_obj, left_diff)), Some((right_obj, right_diff)), - Some((_, left_symbol_diff)), - Some((_, right_symbol_diff)), + Some((_, left_symbol_diff, left_symbol_idx)), + Some((_, right_symbol_diff, right_symbol_idx)), ) = (state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.symbol, right_ctx.symbol) { // Joint diff view hotkeys::check_scroll_hotkeys(ui, true); - if left_symbol_diff.instructions.len() != right_symbol_diff.instructions.len() { + if left_symbol_diff.instruction_rows.len() != right_symbol_diff.instruction_rows.len() { ui.label("Instruction count mismatch"); return; } - let instructions_len = left_symbol_diff.instructions.len(); + let instructions_len = left_symbol_diff.instruction_rows.len(); render_table( ui, available_width, @@ -437,10 +460,11 @@ pub fn diff_view_ui( FunctionDiffContext { obj: left_obj, diff: left_diff, - symbol_ref: Some(left_symbol_diff.symbol_ref), + symbol_ref: Some(left_symbol_idx), }, appearance, &state.function_state, + diff_config, column, ) { ret = Some(action); @@ -451,10 +475,11 @@ pub fn diff_view_ui( FunctionDiffContext { obj: right_obj, diff: right_diff, - symbol_ref: Some(right_symbol_diff.symbol_ref), + symbol_ref: Some(right_symbol_idx), }, appearance, &state.function_state, + diff_config, column, ) { ret = Some(action); @@ -469,8 +494,8 @@ pub fn diff_view_ui( View::DataDiff, Some((left_obj, _left_diff)), Some((right_obj, _right_diff)), - Some((_left_section, left_section_diff)), - Some((_right_section, right_section_diff)), + Some((_left_section, left_section_diff, _left_symbol_idx)), + Some((_right_section, right_section_diff, _right_symbol_idx)), ) = (state.current_view, left_ctx.obj, right_ctx.obj, left_ctx.section, right_ctx.section) { @@ -522,6 +547,7 @@ pub fn diff_view_ui( right_ctx, available_width, open_sections.0, + diff_config, ) { ret = Some(action); } @@ -535,6 +561,7 @@ pub fn diff_view_ui( left_ctx, available_width, open_sections.1, + diff_config, ) { ret = Some(action); } @@ -547,7 +574,6 @@ pub fn diff_view_ui( } #[must_use] -#[allow(clippy::too_many_arguments)] fn diff_col_ui( ui: &mut Ui, state: &DiffViewState, @@ -557,14 +583,15 @@ fn diff_col_ui( other_ctx: DiffColumnContext, available_width: f32, open_sections: Option, + diff_config: &DiffObjConfig, ) -> Option { let mut ret = None; if !ctx.status.success { build_log_ui(ui, ctx.status, appearance); } else if let Some((obj, diff)) = ctx.obj { - if let Some((_symbol, symbol_diff)) = ctx.symbol { + if let Some((_symbol, symbol_diff, symbol_idx)) = ctx.symbol { hotkeys::check_scroll_hotkeys(ui, false); - let ctx = FunctionDiffContext { obj, diff, symbol_ref: Some(symbol_diff.symbol_ref) }; + let ctx = FunctionDiffContext { obj, diff, symbol_ref: Some(symbol_idx) }; if state.current_view == View::ExtabDiff { extab_ui(ui, ctx, appearance, column); } else { @@ -573,11 +600,16 @@ fn diff_col_ui( available_width / 2.0, 1, appearance.code_font.size, - symbol_diff.instructions.len(), + symbol_diff.instruction_rows.len(), |row, column| { - if let Some(action) = - asm_col_ui(row, ctx, appearance, &state.function_state, column) - { + if let Some(action) = asm_col_ui( + row, + ctx, + appearance, + &state.function_state, + diff_config, + column, + ) { ret = Some(action); } if row.response().clicked() { @@ -586,7 +618,7 @@ fn diff_col_ui( }, ); } - } else if let Some((_section, section_diff)) = ctx.section { + } else if let Some((_section, section_diff, _section_idx)) = ctx.section { hotkeys::check_scroll_hotkeys(ui, false); let total_bytes = section_diff.data_diff.iter().fold(0usize, |accum, item| accum + item.len); @@ -610,8 +642,8 @@ fn diff_col_ui( }, ); } else if let ( - Some((other_section, _other_section_diff)), - Some((other_symbol, other_symbol_diff)), + Some((other_section, _other_section_diff, _other_section_idx)), + Some((other_symbol, _other_symbol_diff, other_symbol_idx)), ) = (other_ctx.section, other_ctx.symbol) { if let Some(action) = symbol_list_ui( @@ -619,7 +651,7 @@ fn diff_col_ui( SymbolDiffContext { obj, diff }, None, &state.symbol_state, - SymbolFilter::Mapping(other_symbol_diff.symbol_ref, None), + SymbolFilter::Mapping(other_symbol_idx, None), appearance, column, open_sections, @@ -634,7 +666,7 @@ fn diff_col_ui( ) => { ret = Some(DiffViewAction::SetMapping( match other_section.kind { - ObjSectionKind::Code => View::FunctionDiff, + SectionKind::Code => View::FunctionDiff, _ => View::SymbolDiff, }, left_symbol_ref, @@ -650,7 +682,7 @@ fn diff_col_ui( ) => { ret = Some(DiffViewAction::SetMapping( match other_section.kind { - ObjSectionKind::Code => View::FunctionDiff, + SectionKind::Code => View::FunctionDiff, _ => View::SymbolDiff, }, SymbolRefByName::new(other_symbol, Some(other_section)), @@ -725,17 +757,10 @@ fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) { }); } -fn find_symbol(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option { - for (section_idx, section) in obj.sections.iter().enumerate() { - for (symbol_idx, symbol) in section.symbols.iter().enumerate() { - if symbol.name == selected_symbol.symbol_name { - return Some(SymbolRef { section_idx, symbol_idx }); - } - } - } - None +fn find_symbol(obj: &Object, selected_symbol: &SymbolRefByName) -> Option { + obj.symbols.iter().position(|symbol| symbol.name == selected_symbol.symbol_name) } -fn find_section(obj: &ObjInfo, section_name: &str) -> Option { +fn find_section(obj: &Object, section_name: &str) -> Option { obj.sections.iter().position(|section| section.name == section_name) } diff --git a/objdiff-gui/src/views/extab_diff.rs b/objdiff-gui/src/views/extab_diff.rs index 9065cac..f077d2b 100644 --- a/objdiff-gui/src/views/extab_diff.rs +++ b/objdiff-gui/src/views/extab_diff.rs @@ -1,7 +1,7 @@ use egui::ScrollArea; use objdiff_core::{ arch::ppc::ExceptionInfo, - obj::{ObjInfo, ObjSymbol}, + obj::{Object, Symbol}, }; use crate::views::{appearance::Appearance, function_diff::FunctionDiffContext}; @@ -26,14 +26,16 @@ fn decode_extab(extab: &ExceptionInfo) -> String { text } -fn find_extab_entry<'a>(obj: &'a ObjInfo, symbol: &ObjSymbol) -> Option<&'a ExceptionInfo> { - obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) +fn find_extab_entry<'a>(_obj: &'a Object, _symbol: &Symbol) -> Option<&'a ExceptionInfo> { + // TODO + // obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) + None } fn extab_text_ui( ui: &mut egui::Ui, ctx: FunctionDiffContext<'_>, - symbol: &ObjSymbol, + symbol: &Symbol, appearance: &Appearance, ) -> Option<()> { if let Some(extab_entry) = find_extab_entry(ctx.obj, symbol) { @@ -56,8 +58,8 @@ pub(crate) fn extab_ui( ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - if let Some((_section, symbol)) = - ctx.symbol_ref.map(|symbol_ref| ctx.obj.section_symbol(symbol_ref)) + if let Some(symbol) = + ctx.symbol_ref.and_then(|symbol_ref| ctx.obj.symbols.get(symbol_ref)) { extab_text_ui(ui, ctx, symbol, appearance); } diff --git a/objdiff-gui/src/views/frame_history.rs b/objdiff-gui/src/views/frame_history.rs index 82015d0..0fe924e 100644 --- a/objdiff-gui/src/views/frame_history.rs +++ b/objdiff-gui/src/views/frame_history.rs @@ -61,14 +61,9 @@ impl FrameHistory { ); egui::warn_if_debug_build(ui); - if !cfg!(target_arch = "wasm32") { - egui::CollapsingHeader::new("📊 CPU usage history").default_open(false).show( - ui, - |ui| { - self.graph(ui); - }, - ); - } + egui::CollapsingHeader::new("📊 CPU usage history").default_open(false).show(ui, |ui| { + self.graph(ui); + }); } fn graph(&mut self, ui: &mut egui::Ui) -> egui::Response { diff --git a/objdiff-gui/src/views/function_diff.rs b/objdiff-gui/src/views/function_diff.rs index 8981af2..2080209 100644 --- a/objdiff-gui/src/views/function_diff.rs +++ b/objdiff-gui/src/views/function_diff.rs @@ -4,10 +4,14 @@ use egui::{text::LayoutJob, Label, Response, Sense, Widget}; use egui_extras::TableRow; use objdiff_core::{ diff::{ - display::{display_diff, DiffText, HighlightKind}, - ObjDiff, ObjInsDiff, ObjInsDiffKind, + display::{display_row, DiffText, HighlightKind}, + DiffObjConfig, InstructionArgDiffIndex, InstructionDiffKind, InstructionDiffRow, + ObjectDiff, + }, + obj::{ + InstructionArg, InstructionArgValue, InstructionRef, Object, ParsedInstruction, + ResolvedRelocation, Section, Symbol, }, - obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, ObjSymbol, SymbolRef}, }; use crate::views::{appearance::Appearance, symbol_diff::DiffViewAction}; @@ -63,43 +67,94 @@ impl FunctionViewState { } } +#[expect(unused)] +#[derive(Clone, Copy)] +pub struct ResolvedInstructionRef<'obj> { + pub symbol: &'obj Symbol, + pub section_idx: usize, + pub section: &'obj Section, + pub data: &'obj [u8], + pub relocation: Option>, +} + +fn resolve_instruction_ref( + obj: &Object, + symbol_idx: usize, + ins_ref: InstructionRef, +) -> Option { + let symbol = &obj.symbols[symbol_idx]; + let section_idx = symbol.section?; + let section = &obj.sections[section_idx]; + let offset = ins_ref.address.checked_sub(section.address)?; + let data = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?; + let relocation = section.relocation_at(ins_ref.address, obj); + Some(ResolvedInstructionRef { symbol, section, section_idx, data, relocation }) +} + +fn resolve_instruction<'obj>( + obj: &'obj Object, + symbol_idx: usize, + ins_ref: InstructionRef, + diff_config: &DiffObjConfig, +) -> Option<(ResolvedInstructionRef<'obj>, ParsedInstruction)> { + let resolved = resolve_instruction_ref(obj, symbol_idx, ins_ref)?; + let ins = obj + .arch + .process_instruction( + ins_ref, + resolved.data, + resolved.relocation, + resolved.symbol.address..resolved.symbol.address + resolved.symbol.size, + resolved.section_idx, + diff_config, + ) + .ok()?; + Some((resolved, ins)) +} + fn ins_hover_ui( ui: &mut egui::Ui, - obj: &ObjInfo, - section: &ObjSection, - ins: &ObjIns, - symbol: &ObjSymbol, + obj: &Object, + symbol_idx: usize, + ins_ref: InstructionRef, + diff_config: &DiffObjConfig, appearance: &Appearance, ) { + let Some(( + ResolvedInstructionRef { symbol, section_idx: _, section: _, data, relocation }, + ins, + )) = resolve_instruction(obj, symbol_idx, ins_ref, diff_config) + else { + ui.colored_label(appearance.delete_color, "Failed to resolve instruction"); + return; + }; + ui.scope(|ui| { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - let offset = ins.address - section.address; - ui.label(format!( - "{:02x?}", - §ion.data[offset as usize..(offset + ins.size as u64) as usize] - )); + ui.label(format!("{:02x?}", data)); if let Some(virtual_address) = symbol.virtual_address { - let offset = ins.address - symbol.address; + let offset = ins_ref.address - symbol.address; ui.colored_label( appearance.replace_color, format!("Virtual address: {:#x}", virtual_address + offset), ); } - if let Some(orig) = &ins.orig { - ui.label(format!("Original: {}", orig)); - } + // TODO + // if let Some(orig) = &ins.orig { + // ui.label(format!("Original: {}", orig)); + // } for arg in &ins.args { - if let ObjInsArg::Arg(arg) = arg { + if let InstructionArg::Value(arg) = arg { match arg { - ObjInsArgValue::Signed(v) => { + InstructionArgValue::Signed(v) => { ui.label(format!("{arg} == {v}")); } - ObjInsArgValue::Unsigned(v) => { + InstructionArgValue::Unsigned(v) => { ui.label(format!("{arg} == {v}")); } _ => {} @@ -107,66 +162,76 @@ fn ins_hover_ui( } } - if let Some(reloc) = &ins.reloc { - ui.label(format!("Relocation type: {}", obj.arch.display_reloc(reloc.flags))); - let addend_str = match reloc.addend.cmp(&0i64) { - Ordering::Greater => format!("+{:x}", reloc.addend), - Ordering::Less => format!("-{:x}", -reloc.addend), + if let Some(resolved) = relocation { + ui.label(format!( + "Relocation type: {}", + obj.arch.display_reloc(resolved.relocation.flags) + )); + let addend_str = match resolved.relocation.addend.cmp(&0i64) { + Ordering::Greater => format!("+{:x}", resolved.relocation.addend), + Ordering::Less => format!("-{:x}", -resolved.relocation.addend), _ => "".to_string(), }; ui.colored_label( appearance.highlight_color, - format!("Name: {}{}", reloc.target.name, addend_str), + format!("Name: {}{}", resolved.symbol.name, addend_str), ); - if let Some(orig_section_index) = reloc.target.orig_section_index { - if let Some(section) = - obj.sections.iter().find(|s| s.orig_index == orig_section_index) - { - ui.colored_label( - appearance.highlight_color, - format!("Section: {}", section.name), - ); - } + if let Some(orig_section_index) = resolved.symbol.section { + let section = &obj.sections[orig_section_index]; + ui.colored_label(appearance.highlight_color, format!("Section: {}", section.name)); ui.colored_label( appearance.highlight_color, - format!("Address: {:x}{}", reloc.target.address, addend_str), + format!("Address: {:x}{}", resolved.symbol.address, addend_str), ); ui.colored_label( appearance.highlight_color, - format!("Size: {:x}", reloc.target.size), + format!("Size: {:x}", resolved.symbol.size), ); - for label in obj.arch.display_ins_data_labels(ins) { - ui.colored_label(appearance.highlight_color, label); - } + // TODO + // for label in obj.arch.display_ins_data_labels(ins) { + // ui.colored_label(appearance.highlight_color, label); + // } } else { ui.colored_label(appearance.highlight_color, "Extern".to_string()); } } - if let Some(decoded) = rlwinmdec::decode(&ins.formatted) { - ui.colored_label(appearance.highlight_color, decoded.trim()); - } + // TODO + // if let Some(decoded) = rlwinmdec::decode(&ins.formatted) { + // ui.colored_label(appearance.highlight_color, decoded.trim()); + // } }); } fn ins_context_menu( ui: &mut egui::Ui, - obj: &ObjInfo, - section: &ObjSection, - ins: &ObjIns, - symbol: &ObjSymbol, + obj: &Object, + symbol_idx: usize, + ins_ref: InstructionRef, + diff_config: &DiffObjConfig, + appearance: &Appearance, ) { + let Some(( + ResolvedInstructionRef { symbol, section_idx: _, section: _, data, relocation }, + ins, + )) = resolve_instruction(obj, symbol_idx, ins_ref, diff_config) + else { + ui.colored_label(appearance.delete_color, "Failed to resolve instruction"); + return; + }; + ui.scope(|ui| { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - if ui.button(format!("Copy \"{}\"", ins.formatted)).clicked() { - ui.output_mut(|output| output.copied_text.clone_from(&ins.formatted)); - ui.close_menu(); - } + // TODO + // if ui.button(format!("Copy \"{}\"", ins.formatted)).clicked() { + // ui.output_mut(|output| output.copied_text.clone_from(&ins.formatted)); + // ui.close_menu(); + // } let mut hex_string = "0x".to_string(); - for byte in §ion.data[ins.address as usize..(ins.address + ins.size as u64) as usize] { + for byte in data { hex_string.push_str(&format!("{:02x}", byte)); } if ui.button(format!("Copy \"{hex_string}\" (instruction bytes)")).clicked() { @@ -175,7 +240,7 @@ fn ins_context_menu( } if let Some(virtual_address) = symbol.virtual_address { - let offset = ins.address - symbol.address; + let offset = ins_ref.address - symbol.address; let offset_string = format!("{:#x}", virtual_address + offset); if ui.button(format!("Copy \"{offset_string}\" (virtual address)")).clicked() { ui.output_mut(|output| output.copied_text = offset_string); @@ -184,9 +249,9 @@ fn ins_context_menu( } for arg in &ins.args { - if let ObjInsArg::Arg(arg) = arg { + if let InstructionArg::Value(arg) = arg { match arg { - ObjInsArgValue::Signed(v) => { + InstructionArgValue::Signed(v) => { if ui.button(format!("Copy \"{arg}\"")).clicked() { ui.output_mut(|output| output.copied_text = arg.to_string()); ui.close_menu(); @@ -196,7 +261,7 @@ fn ins_context_menu( ui.close_menu(); } } - ObjInsArgValue::Unsigned(v) => { + InstructionArgValue::Unsigned(v) => { if ui.button(format!("Copy \"{arg}\"")).clicked() { ui.output_mut(|output| output.copied_text = arg.to_string()); ui.close_menu(); @@ -210,21 +275,23 @@ fn ins_context_menu( } } } - if let Some(reloc) = &ins.reloc { - for literal in obj.arch.display_ins_data_literals(ins) { - if ui.button(format!("Copy \"{literal}\"")).clicked() { - ui.output_mut(|output| output.copied_text.clone_from(&literal)); - ui.close_menu(); - } - } - if let Some(name) = &reloc.target.demangled_name { + + if let Some(resolved) = relocation { + // TODO + // for literal in obj.arch.display_ins_data_literals(ins) { + // if ui.button(format!("Copy \"{literal}\"")).clicked() { + // ui.output_mut(|output| output.copied_text.clone_from(&literal)); + // ui.close_menu(); + // } + // } + if let Some(name) = &resolved.symbol.demangled_name { if ui.button(format!("Copy \"{name}\"")).clicked() { ui.output_mut(|output| output.copied_text.clone_from(name)); ui.close_menu(); } } - if ui.button(format!("Copy \"{}\"", reloc.target.name)).clicked() { - ui.output_mut(|output| output.copied_text.clone_from(&reloc.target.name)); + if ui.button(format!("Copy \"{}\"", resolved.symbol.name)).clicked() { + ui.output_mut(|output| output.copied_text.clone_from(&resolved.symbol.name)); ui.close_menu(); } } @@ -232,11 +299,11 @@ fn ins_context_menu( } #[must_use] -#[expect(clippy::too_many_arguments)] fn diff_text_ui( ui: &mut egui::Ui, text: DiffText<'_>, - ins_diff: &ObjInsDiff, + diff: InstructionArgDiffIndex, + ins_diff: &InstructionDiffRow, appearance: &Appearance, ins_view_state: &FunctionViewState, column: usize, @@ -246,22 +313,18 @@ fn diff_text_ui( let mut ret = None; let label_text; let mut base_color = match ins_diff.kind { - ObjInsDiffKind::None | ObjInsDiffKind::OpMismatch | ObjInsDiffKind::ArgMismatch => { - appearance.text_color - } - ObjInsDiffKind::Replace => appearance.replace_color, - ObjInsDiffKind::Delete => appearance.delete_color, - ObjInsDiffKind::Insert => appearance.insert_color, + InstructionDiffKind::None + | InstructionDiffKind::OpMismatch + | InstructionDiffKind::ArgMismatch => appearance.text_color, + InstructionDiffKind::Replace => appearance.replace_color, + InstructionDiffKind::Delete => appearance.delete_color, + InstructionDiffKind::Insert => appearance.insert_color, }; let mut pad_to = 0; match text { DiffText::Basic(text) => { label_text = text.to_string(); } - DiffText::BasicColor(s, idx) => { - label_text = s.to_string(); - base_color = appearance.diff_colors[idx % appearance.diff_colors.len()]; - } DiffText::Line(num) => { label_text = num.to_string(); base_color = appearance.deemphasized_text_color; @@ -273,43 +336,29 @@ fn diff_text_ui( } DiffText::Opcode(mnemonic, _op) => { label_text = mnemonic.to_string(); - if ins_diff.kind == ObjInsDiffKind::OpMismatch { + if ins_diff.kind == InstructionDiffKind::OpMismatch { base_color = appearance.replace_color; } pad_to = 8; } - DiffText::Argument(arg, diff) => { + DiffText::Argument(arg) => { label_text = arg.to_string(); - if let Some(diff) = diff { - base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()] - } } - DiffText::BranchDest(addr, diff) => { + DiffText::BranchDest(addr) => { label_text = format!("{addr:x}"); - if let Some(diff) = diff { - base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()] - } } - DiffText::Symbol(sym, diff) => { + DiffText::Symbol(sym) => { let name = sym.demangled_name.as_ref().unwrap_or(&sym.name); label_text = name.clone(); - if let Some(diff) = diff { - base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()] - } else { - base_color = appearance.emphasized_text_color; - } + base_color = appearance.emphasized_text_color; } - DiffText::Addend(addend, diff) => { + DiffText::Addend(addend) => { label_text = match addend.cmp(&0i64) { Ordering::Greater => format!("+{:#x}", addend), Ordering::Less => format!("-{:#x}", -addend), _ => "".to_string(), }; - if let Some(diff) = diff { - base_color = appearance.diff_colors[diff.idx % appearance.diff_colors.len()] - } else { - base_color = appearance.emphasized_text_color; - } + base_color = appearance.emphasized_text_color; } DiffText::Spacing(n) => { ui.add_space(n as f32 * space_width); @@ -319,6 +368,9 @@ fn diff_text_ui( label_text = "\n".to_string(); } } + if let Some(diff_idx) = diff.get() { + base_color = appearance.diff_colors[diff_idx as usize % appearance.diff_colors.len()]; + } let len = label_text.len(); let highlight = *ins_view_state.highlight(column) == text; @@ -341,24 +393,27 @@ fn diff_text_ui( #[must_use] fn asm_row_ui( ui: &mut egui::Ui, - ins_diff: &ObjInsDiff, - symbol: &ObjSymbol, + obj: &Object, + ins_diff: &InstructionDiffRow, + symbol_idx: usize, appearance: &Appearance, ins_view_state: &FunctionViewState, + diff_config: &DiffObjConfig, column: usize, response_cb: impl Fn(Response) -> Response, ) -> Option { let mut ret = None; ui.spacing_mut().item_spacing.x = 0.0; ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - if ins_diff.kind != ObjInsDiffKind::None { + if ins_diff.kind != InstructionDiffKind::None { ui.painter().rect_filled(ui.available_rect_before_wrap(), 0.0, ui.visuals().faint_bg_color); } let space_width = ui.fonts(|f| f.glyph_width(&appearance.code_font, ' ')); - display_diff(ins_diff, symbol.address, |text| { + display_row(obj, symbol_idx, ins_diff, diff_config, |text, diff| { if let Some(action) = diff_text_ui( ui, text, + diff, ins_diff, appearance, ins_view_state, @@ -368,7 +423,7 @@ fn asm_row_ui( ) { ret = Some(action); } - Ok::<_, ()>(()) + Ok(()) }) .unwrap(); ret @@ -380,27 +435,36 @@ pub(crate) fn asm_col_ui( ctx: FunctionDiffContext<'_>, appearance: &Appearance, ins_view_state: &FunctionViewState, + diff_config: &DiffObjConfig, column: usize, ) -> Option { let mut ret = None; let symbol_ref = ctx.symbol_ref?; - let (section, symbol) = ctx.obj.section_symbol(symbol_ref); - let section = section?; - let ins_diff = &ctx.diff.symbol_diff(symbol_ref).instructions[row.index()]; + let ins_row = &ctx.diff.symbols[symbol_ref].instruction_rows[row.index()]; let response_cb = |response: Response| { - if let Some(ins) = &ins_diff.ins { - response.context_menu(|ui| ins_context_menu(ui, ctx.obj, section, ins, symbol)); + if let Some(ins_ref) = ins_row.ins_ref { + response.context_menu(|ui| { + ins_context_menu(ui, ctx.obj, symbol_ref, ins_ref, diff_config, appearance) + }); response.on_hover_ui_at_pointer(|ui| { - ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance) + ins_hover_ui(ui, ctx.obj, symbol_ref, ins_ref, diff_config, appearance) }) } else { response } }; let (_, response) = row.col(|ui| { - if let Some(action) = - asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state, column, response_cb) - { + if let Some(action) = asm_row_ui( + ui, + ctx.obj, + ins_row, + symbol_ref, + appearance, + ins_view_state, + diff_config, + column, + response_cb, + ) { ret = Some(action); } }); @@ -410,7 +474,7 @@ pub(crate) fn asm_col_ui( #[derive(Clone, Copy)] pub struct FunctionDiffContext<'a> { - pub obj: &'a ObjInfo, - pub diff: &'a ObjDiff, - pub symbol_ref: Option, + pub obj: &'a Object, + pub diff: &'a ObjectDiff, + pub symbol_ref: Option, } diff --git a/objdiff-gui/src/views/symbol_diff.rs b/objdiff-gui/src/views/symbol_diff.rs index 0b5295c..bc77498 100644 --- a/objdiff-gui/src/views/symbol_diff.rs +++ b/objdiff-gui/src/views/symbol_diff.rs @@ -1,16 +1,19 @@ -use std::{collections::BTreeMap, mem::take, ops::Bound}; +use std::mem::take; use egui::{ style::ScrollAnimation, text::LayoutJob, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea, SelectableLabel, Ui, Widget, }; use objdiff_core::{ - arch::ObjArch, - diff::{display::HighlightKind, ObjDiff, ObjSymbolDiff}, - jobs::{create_scratch::CreateScratchResult, objdiff::ObjDiffResult, Job, JobQueue, JobResult}, - obj::{ - ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags, SymbolRef, SECTION_COMMON, + diff::{ + display::{ + display_sections, symbol_context, symbol_hover, ContextMenuItem, HighlightKind, + HoverItem, HoverItemColor, SectionDisplay, SymbolFilter, + }, + ObjectDiff, SymbolDiff, }, + jobs::{create_scratch::CreateScratchResult, objdiff::ObjDiffResult, Job, JobQueue, JobResult}, + obj::{Object, Section, SectionKind, Symbol, SymbolFlag}, }; use regex::{Regex, RegexBuilder}; @@ -28,7 +31,7 @@ pub struct SymbolRefByName { } impl SymbolRefByName { - pub fn new(symbol: &ObjSymbol, section: Option<&ObjSection>) -> Self { + pub fn new(symbol: &Symbol, section: Option<&Section>) -> Self { Self { symbol_name: symbol.name.clone(), section_name: section.map(|s| s.name.clone()) } } } @@ -50,7 +53,7 @@ pub enum DiffViewAction { /// Navigate to a new diff view Navigate(DiffViewNavigation), /// Set the highlighted symbols in the symbols view, optionally scrolling them into view. - SetSymbolHighlight(Option, Option, bool), + SetSymbolHighlight(Option, Option, bool), /// Set the symbols view search filter SetSearch(String), /// Submit the current function to decomp.me @@ -88,15 +91,17 @@ impl DiffViewNavigation { pub fn with_symbols( view: View, other_ctx: Option>, - symbol: &ObjSymbol, - section: &ObjSection, - symbol_diff: &ObjSymbolDiff, + symbol: &Symbol, + section: &Section, + symbol_diff: &SymbolDiff, column: usize, ) -> Self { let symbol1 = Some(SymbolRefByName::new(symbol, Some(section))); let symbol2 = symbol_diff.target_symbol.and_then(|symbol_ref| { other_ctx.map(|ctx| { - let (section, symbol) = ctx.obj.section_symbol(symbol_ref); + let symbol = &ctx.obj.symbols[symbol_ref]; + let section = + symbol.section.and_then(|section_idx| ctx.obj.sections.get(section_idx)); SymbolRefByName::new(symbol, section) }) }); @@ -107,7 +112,7 @@ impl DiffViewNavigation { } } - pub fn data_diff(section: &ObjSection, column: usize) -> Self { + pub fn data_diff(section: &Section, column: usize) -> Self { let symbol = Some(SymbolRefByName { symbol_name: "".to_string(), section_name: Some(section.name.clone()), @@ -143,7 +148,7 @@ pub struct DiffViewState { #[derive(Default)] pub struct SymbolViewState { - pub highlighted_symbol: (Option, Option), + pub highlighted_symbol: (Option, Option), pub autoscroll_to_highlighted_symbols: bool, pub left_symbol: Option, pub right_symbol: Option, @@ -365,9 +370,9 @@ fn symbol_context_menu_ui( ui: &mut Ui, ctx: SymbolDiffContext<'_>, other_ctx: Option>, - symbol: &ObjSymbol, - symbol_diff: &ObjSymbolDiff, - section: Option<&ObjSection>, + symbol: &Symbol, + symbol_diff: &SymbolDiff, + section: Option<&Section>, column: usize, ) -> Option { let mut ret = None; @@ -375,37 +380,37 @@ fn symbol_context_menu_ui( ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - if let Some(name) = &symbol.demangled_name { - if ui.button(format!("Copy \"{name}\"")).clicked() { - ui.output_mut(|output| output.copied_text.clone_from(name)); - ui.close_menu(); + for item in symbol_context(ctx.obj, symbol) { + match item { + ContextMenuItem::Copy { value, label } => { + let label = if let Some(extra) = label { + format!("Copy \"{value}\" ({extra})") + } else { + format!("Copy \"{value}\"") + }; + if ui.button(label).clicked() { + ui.output_mut(|output| output.copied_text = value); + ui.close_menu(); + } + } + ContextMenuItem::Navigate { label } => { + if ui.button(label).clicked() { + // TODO other navigation + ret = Some(DiffViewNavigation::with_symbols( + View::ExtabDiff, + other_ctx, + symbol, + section.unwrap(), + symbol_diff, + column, + )); + ui.close_menu(); + } + } } } - if ui.button(format!("Copy \"{}\"", symbol.name)).clicked() { - ui.output_mut(|output| output.copied_text.clone_from(&symbol.name)); - ui.close_menu(); - } - if let Some(address) = symbol.virtual_address { - if ui.button(format!("Copy \"{:#x}\" (virtual address)", address)).clicked() { - ui.output_mut(|output| output.copied_text = format!("{:#x}", address)); - ui.close_menu(); - } - } - if let Some(section) = section { - let has_extab = - ctx.obj.arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)).is_some(); - if has_extab && ui.button("Decode exception table").clicked() { - ret = Some(DiffViewNavigation::with_symbols( - View::ExtabDiff, - other_ctx, - symbol, - section, - symbol_diff, - column, - )); - ui.close_menu(); - } + if let Some(section) = section { if ui.button("Map symbol").clicked() { let symbol_ref = SymbolRefByName::new(symbol, Some(section)); if column == 0 { @@ -428,54 +433,41 @@ fn symbol_context_menu_ui( ret } -fn symbol_hover_ui(ui: &mut Ui, arch: &dyn ObjArch, symbol: &ObjSymbol, appearance: &Appearance) { +fn symbol_hover_ui( + ui: &mut Ui, + ctx: SymbolDiffContext<'_>, + symbol: &Symbol, + appearance: &Appearance, +) { ui.scope(|ui| { ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - ui.colored_label(appearance.highlight_color, format!("Name: {}", symbol.name)); - ui.colored_label(appearance.highlight_color, format!("Address: {:x}", symbol.address)); - if symbol.size_known { - ui.colored_label(appearance.highlight_color, format!("Size: {:x}", symbol.size)); - } else { - ui.colored_label( - appearance.highlight_color, - format!("Size: {:x} (assumed)", symbol.size), - ); - } - if let Some(address) = symbol.virtual_address { - ui.colored_label(appearance.replace_color, format!("Virtual address: {:#x}", address)); - } - if let Some(extab) = arch.ppc().and_then(|ppc| ppc.extab_for_symbol(symbol)) { - ui.colored_label( - appearance.highlight_color, - format!("extab symbol: {}", &extab.etb_symbol.name), - ); - ui.colored_label( - appearance.highlight_color, - format!("extabindex symbol: {}", &extab.eti_symbol.name), - ); + for HoverItem { text, color } in symbol_hover(ctx.obj, symbol) { + let color = match color { + HoverItemColor::Normal => appearance.text_color, + HoverItemColor::Emphasized => appearance.highlight_color, + HoverItemColor::Special => appearance.replace_color, + }; + ui.colored_label(color, text); } }); } #[must_use] -#[expect(clippy::too_many_arguments)] fn symbol_ui( ui: &mut Ui, ctx: SymbolDiffContext<'_>, other_ctx: Option>, - symbol: &ObjSymbol, - symbol_diff: &ObjSymbolDiff, - section: Option<&ObjSection>, + symbol: &Symbol, + symbol_diff: &SymbolDiff, + symbol_idx: usize, + section: Option<&Section>, state: &SymbolViewState, appearance: &Appearance, column: usize, ) -> Option { let mut ret = None; - if symbol.flags.0.contains(ObjSymbolFlags::Hidden) && !state.show_hidden_symbols { - return ret; - } let mut job = LayoutJob::default(); let name: &str = if let Some(demangled) = &symbol.demangled_name { demangled } else { &symbol.name }; @@ -483,24 +475,24 @@ fn symbol_ui( if let Some(sym_ref) = if column == 0 { state.highlighted_symbol.0 } else { state.highlighted_symbol.1 } { - selected = symbol_diff.symbol_ref == sym_ref; + selected = symbol_idx == sym_ref; } - if !symbol.flags.0.is_empty() { + if !symbol.flags.is_empty() { write_text("[", appearance.text_color, &mut job, appearance.code_font.clone()); - if symbol.flags.0.contains(ObjSymbolFlags::Common) { + if symbol.flags.contains(SymbolFlag::Common) { write_text("c", appearance.replace_color, &mut job, appearance.code_font.clone()); - } else if symbol.flags.0.contains(ObjSymbolFlags::Global) { + } else if symbol.flags.contains(SymbolFlag::Global) { write_text("g", appearance.insert_color, &mut job, appearance.code_font.clone()); - } else if symbol.flags.0.contains(ObjSymbolFlags::Local) { + } else if symbol.flags.contains(SymbolFlag::Local) { write_text("l", appearance.text_color, &mut job, appearance.code_font.clone()); } - if symbol.flags.0.contains(ObjSymbolFlags::Weak) { + if symbol.flags.contains(SymbolFlag::Weak) { write_text("w", appearance.text_color, &mut job, appearance.code_font.clone()); } - if symbol.flags.0.contains(ObjSymbolFlags::HasExtra) { + if symbol.flags.contains(SymbolFlag::HasExtra) { write_text("e", appearance.text_color, &mut job, appearance.code_font.clone()); } - if symbol.flags.0.contains(ObjSymbolFlags::Hidden) { + if symbol.flags.contains(SymbolFlag::Hidden) { write_text( "h", appearance.deemphasized_text_color, @@ -521,9 +513,9 @@ fn symbol_ui( write_text(") ", appearance.text_color, &mut job, appearance.code_font.clone()); } write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone()); - let response = SelectableLabel::new(selected, job).ui(ui).on_hover_ui_at_pointer(|ui| { - symbol_hover_ui(ui, ctx.obj.arch.as_ref(), symbol, appearance) - }); + let response = SelectableLabel::new(selected, job) + .ui(ui) + .on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, ctx, symbol, appearance)); response.context_menu(|ui| { if let Some(result) = symbol_context_menu_ui(ui, ctx, other_ctx, symbol, symbol_diff, section, column) @@ -542,7 +534,7 @@ fn symbol_ui( if response.clicked() || (selected && hotkeys::enter_pressed(ui.ctx())) { if let Some(section) = section { match section.kind { - ObjSectionKind::Code => { + SectionKind::Code => { ret = Some(DiffViewAction::Navigate(DiffViewNavigation::with_symbols( View::FunctionDiff, other_ctx, @@ -552,62 +544,56 @@ fn symbol_ui( column, ))); } - ObjSectionKind::Data => { + SectionKind::Data => { ret = Some(DiffViewAction::Navigate(DiffViewNavigation::data_diff( section, column, ))); } - ObjSectionKind::Bss => {} + _ => {} } } } else if response.hovered() { ret = Some(if column == 0 { - DiffViewAction::SetSymbolHighlight( - Some(symbol_diff.symbol_ref), - symbol_diff.target_symbol, - false, - ) + DiffViewAction::SetSymbolHighlight(Some(symbol_idx), symbol_diff.target_symbol, false) } else { - DiffViewAction::SetSymbolHighlight( - symbol_diff.target_symbol, - Some(symbol_diff.symbol_ref), - false, - ) + DiffViewAction::SetSymbolHighlight(symbol_diff.target_symbol, Some(symbol_idx), false) }); } ret } -fn symbol_matches_filter( - symbol: &ObjSymbol, - diff: &ObjSymbolDiff, - filter: SymbolFilter<'_>, -) -> bool { - match filter { - SymbolFilter::None => true, - SymbolFilter::Search(regex) => { - regex.is_match(&symbol.name) - || symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s)) - } - SymbolFilter::Mapping(symbol_ref, regex) => { - diff.target_symbol == Some(symbol_ref) - && regex.is_none_or(|r| { - r.is_match(&symbol.name) - || symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s)) - }) - } - } +fn find_prev_symbol(section_display: &[SectionDisplay], current: usize) -> Option { + section_display + .iter() + .flat_map(|s| s.symbols.iter()) + .rev() + .skip_while(|s| s.symbol != current) + .nth(1) + .map(|s| s.symbol) + // Wrap around to the last symbol if we're at the beginning of the list + .or_else(|| find_last_symbol(section_display)) } -#[derive(Copy, Clone)] -pub enum SymbolFilter<'a> { - None, - Search(&'a Regex), - Mapping(SymbolRef, Option<&'a Regex>), +fn find_next_symbol(section_display: &[SectionDisplay], current: usize) -> Option { + section_display + .iter() + .flat_map(|s| s.symbols.iter()) + .skip_while(|s| s.symbol != current) + .nth(1) + .map(|s| s.symbol) + // Wrap around to the first symbol if we're at the end of the list + .or_else(|| find_first_symbol(section_display)) +} + +fn find_first_symbol(section_display: &[SectionDisplay]) -> Option { + section_display.iter().flat_map(|s| s.symbols.iter()).next().map(|s| s.symbol) +} + +fn find_last_symbol(section_display: &[SectionDisplay]) -> Option { + section_display.iter().flat_map(|s| s.symbols.iter()).next_back().map(|s| s.symbol) } #[must_use] -#[expect(clippy::too_many_arguments)] pub fn symbol_list_ui( ui: &mut Ui, ctx: SymbolDiffContext<'_>, @@ -620,41 +606,20 @@ pub fn symbol_list_ui( ) -> Option { let mut ret = None; ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| { - let mut mapping = BTreeMap::new(); + let mut show_mapped_symbols = state.show_mapped_symbols; if let SymbolFilter::Mapping(_, _) = filter { - let mut show_mapped_symbols = state.show_mapped_symbols; if ui.checkbox(&mut show_mapped_symbols, "Show mapped symbols").changed() { ret = Some(DiffViewAction::SetShowMappedSymbols(show_mapped_symbols)); } - for mapping_diff in &ctx.diff.mapping_symbols { - let symbol = ctx.obj.section_symbol(mapping_diff.symbol_ref).1; - if !symbol_matches_filter(symbol, mapping_diff, filter) { - continue; - } - if !show_mapped_symbols { - let symbol_diff = ctx.diff.symbol_diff(mapping_diff.symbol_ref); - if symbol_diff.target_symbol.is_some() { - continue; - } - } - mapping.insert(mapping_diff.symbol_ref, mapping_diff); - } - } else { - for (symbol, diff) in ctx.obj.common.iter().zip(&ctx.diff.common) { - if !symbol_matches_filter(symbol, diff, filter) { - continue; - } - mapping.insert(diff.symbol_ref, diff); - } - for (section, section_diff) in ctx.obj.sections.iter().zip(&ctx.diff.sections) { - for (symbol, symbol_diff) in section.symbols.iter().zip(§ion_diff.symbols) { - if !symbol_matches_filter(symbol, symbol_diff, filter) { - continue; - } - mapping.insert(symbol_diff.symbol_ref, symbol_diff); - } - } } + let section_display = display_sections( + ctx.obj, + ctx.diff, + filter, + state.show_hidden_symbols, + show_mapped_symbols, + state.reverse_fn_order, + ); hotkeys::check_scroll_hotkeys(ui, false); @@ -669,14 +634,11 @@ pub fn symbol_list_ui( } else { None }; - if let Some(mut up) = up { - if state.reverse_fn_order { - up = !up; - } + if let Some(up) = up { new_key_value_to_highlight = if up { - mapping.range(..sym_ref).next_back() + find_prev_symbol(§ion_display, sym_ref) } else { - mapping.range((Bound::Excluded(sym_ref), Bound::Unbounded)).next() + find_next_symbol(§ion_display, sym_ref) }; }; } else { @@ -685,26 +647,15 @@ pub fn symbol_list_ui( // we do when a symbol is highlighted. This is so that if only one column has a symbol // highlighted, that one takes precedence over the one with nothing highlighted. if hotkeys::up_pressed(ui.ctx()) || hotkeys::down_pressed(ui.ctx()) { - new_key_value_to_highlight = if state.reverse_fn_order { - mapping.last_key_value() - } else { - mapping.first_key_value() - }; + new_key_value_to_highlight = find_first_symbol(§ion_display); } } - if let Some((new_sym_ref, new_symbol_diff)) = new_key_value_to_highlight { + if let Some(new_sym_ref) = new_key_value_to_highlight { + let target_symbol = ctx.diff.symbols[new_sym_ref].target_symbol; ret = Some(if column == 0 { - DiffViewAction::SetSymbolHighlight( - Some(*new_sym_ref), - new_symbol_diff.target_symbol, - true, - ) + DiffViewAction::SetSymbolHighlight(Some(new_sym_ref), target_symbol, true) } else { - DiffViewAction::SetSymbolHighlight( - new_symbol_diff.target_symbol, - Some(*new_sym_ref), - true, - ) + DiffViewAction::SetSymbolHighlight(target_symbol, Some(new_sym_ref), true) }); } @@ -712,44 +663,21 @@ pub fn symbol_list_ui( ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); - // Skip sections with all symbols filtered out - if mapping.keys().any(|symbol_ref| symbol_ref.section_idx == SECTION_COMMON) { - CollapsingHeader::new(".comm").default_open(true).show(ui, |ui| { - for (symbol_ref, symbol_diff) in mapping - .iter() - .filter(|(symbol_ref, _)| symbol_ref.section_idx == SECTION_COMMON) - { - let symbol = ctx.obj.section_symbol(*symbol_ref).1; - if let Some(result) = symbol_ui( - ui, - ctx, - other_ctx, - symbol, - symbol_diff, - None, - state, - appearance, - column, - ) { - ret = Some(result); - } - } - }); - } - - for ((section_index, section), section_diff) in - ctx.obj.sections.iter().enumerate().zip(&ctx.diff.sections) - { - // Skip sections with all symbols filtered out - if !mapping.keys().any(|symbol_ref| symbol_ref.section_idx == section_index) { - continue; - } + for section_display in section_display { let mut header = LayoutJob::simple_singleline( - format!("{} ({:x})", section.name, section.size), + section_display.name.clone(), appearance.code_font.clone(), Color32::PLACEHOLDER, ); - if let Some(match_percent) = section_diff.match_percent { + if section_display.size > 0 { + write_text( + &format!(" ({:x})", section_display.size), + Color32::PLACEHOLDER, + &mut header, + appearance.code_font.clone(), + ); + } + if let Some(match_percent) = section_display.match_percent { write_text( " (", Color32::PLACEHOLDER, @@ -770,50 +698,38 @@ pub fn symbol_list_ui( ); } CollapsingHeader::new(header) - .id_salt(Id::new(section.name.clone()).with(section.orig_index)) + .id_salt(Id::new(§ion_display.id)) .default_open(true) .open(open_sections) .show(ui, |ui| { - if section.kind == ObjSectionKind::Code && state.reverse_fn_order { - for (symbol, symbol_diff) in mapping - .iter() - .filter(|(symbol_ref, _)| symbol_ref.section_idx == section_index) - .rev() - { - let symbol = ctx.obj.section_symbol(*symbol).1; - if let Some(result) = symbol_ui( - ui, - ctx, - other_ctx, - symbol, - symbol_diff, - Some(section), - state, - appearance, - column, - ) { - ret = Some(result); - } - } - } else { - for (symbol, symbol_diff) in mapping - .iter() - .filter(|(symbol_ref, _)| symbol_ref.section_idx == section_index) - { - let symbol = ctx.obj.section_symbol(*symbol).1; - if let Some(result) = symbol_ui( - ui, - ctx, - other_ctx, - symbol, - symbol_diff, - Some(section), - state, - appearance, - column, - ) { - ret = Some(result); - } + for symbol_display in §ion_display.symbols { + let symbol = &ctx.obj.symbols[symbol_display.symbol]; + let section = symbol + .section + .and_then(|section_idx| ctx.obj.sections.get(section_idx)); + let symbol_diff = if symbol_display.is_mapping_symbol { + ctx.diff + .mapping_symbols + .iter() + .find(|d| d.symbol_index == symbol_display.symbol) + .map(|d| &d.symbol_diff) + .unwrap() + } else { + &ctx.diff.symbols[symbol_display.symbol] + }; + if let Some(result) = symbol_ui( + ui, + ctx, + other_ctx, + symbol, + symbol_diff, + symbol_display.symbol, + section, + state, + appearance, + column, + ) { + ret = Some(result); } } }); @@ -825,6 +741,6 @@ pub fn symbol_list_ui( #[derive(Copy, Clone)] pub struct SymbolDiffContext<'a> { - pub obj: &'a ObjInfo, - pub diff: &'a ObjDiff, + pub obj: &'a Object, + pub diff: &'a ObjectDiff, } diff --git a/objdiff-wasm/Cargo.toml b/objdiff-wasm/Cargo.toml new file mode 100644 index 0000000..fbeb676 --- /dev/null +++ b/objdiff-wasm/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "objdiff-wasm" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +readme = "../README.md" +description = """ +A local diffing tool for decompilation projects. +""" +publish = false +build = "build.rs" + +[lib] +crate-type = ["cdylib"] + +[features] +default = ["std"] +std = ["objdiff-core/std"] + +[dependencies] +log = { version = "0.4", default-features = false } +regex = { version = "1.11", default-features = false } + +[dependencies.objdiff-core] +path = "../objdiff-core" +default-features = false +features = ["arm", "arm64", "mips", "ppc", "dwarf"] + +[target.'cfg(target_family = "wasm")'.dependencies] +talc = { version = "4.4", default-features = false, features = ["lock_api"] } + +[target.'cfg(target_os = "wasi")'.dependencies] +wit-bindgen = { version = "0.39", default-features = false, features = ["macros"] } + +[build-dependencies] +wit-deps = "0.5" diff --git a/objdiff-wasm/biome.json b/objdiff-wasm/biome.json new file mode 100644 index 0000000..5e6252d --- /dev/null +++ b/objdiff-wasm/biome.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", + "organizeImports": { + "enabled": true + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "formatter": { + "indentStyle": "space" + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "a11y": { + "all": false + }, + "suspicious": { + "noExplicitAny": "off" + } + } + } +} diff --git a/objdiff-wasm/build.rs b/objdiff-wasm/build.rs new file mode 100644 index 0000000..8181dc1 --- /dev/null +++ b/objdiff-wasm/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), Box> { + wit_deps::lock_sync!()?; + Ok(()) +} diff --git a/objdiff-wasm/eslint.config.js b/objdiff-wasm/eslint.config.js deleted file mode 100644 index 2019c35..0000000 --- a/objdiff-wasm/eslint.config.js +++ /dev/null @@ -1,28 +0,0 @@ -import globals from "globals"; -import pluginJs from "@eslint/js"; -import tseslint from "typescript-eslint"; - -export default [ - {files: ["**/*.{js,mjs,cjs,ts}"]}, - {languageOptions: {globals: globals.browser}}, - pluginJs.configs.recommended, - ...tseslint.configs.recommended, - { - rules: { - "semi": [2, "always"], - "@typescript-eslint/no-unused-vars": [ - "error", - // https://typescript-eslint.io/rules/no-unused-vars/#benefits-over-typescript - { - "args": "all", - "argsIgnorePattern": "^_", - "caughtErrors": "all", - "caughtErrorsIgnorePattern": "^_", - "destructuredArrayIgnorePattern": "^_", - "varsIgnorePattern": "^_", - "ignoreRestSiblings": true - }, - ], - } - }, -]; \ No newline at end of file diff --git a/objdiff-wasm/lib/wasi-logging.ts b/objdiff-wasm/lib/wasi-logging.ts new file mode 100644 index 0000000..2e5486a --- /dev/null +++ b/objdiff-wasm/lib/wasi-logging.ts @@ -0,0 +1,25 @@ +import type { WasiLoggingLogging010Draft as logging } from '../pkg/objdiff'; + +export const log: typeof logging.log = (level, context, message) => { + const msg = `[${context}] ${message}`; + switch (level) { + case 'trace': + console.trace(msg); + break; + case 'debug': + console.debug(msg); + break; + case 'info': + console.info(msg); + break; + case 'warn': + console.warn(msg); + break; + case 'error': + console.error(msg); + break; + case 'critical': + console.error(msg); + break; + } +}; diff --git a/objdiff-wasm/package-lock.json b/objdiff-wasm/package-lock.json index 0c2be3d..fe515b3 100644 --- a/objdiff-wasm/package-lock.json +++ b/objdiff-wasm/package-lock.json @@ -1,644 +1,406 @@ { "name": "objdiff-wasm", - "version": "2.0.0", + "version": "2.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "objdiff-wasm", - "version": "2.0.0", + "version": "2.7.1", "license": "MIT OR Apache-2.0", - "dependencies": { - "@protobuf-ts/runtime": "^2.9.4" - }, "devDependencies": { - "@eslint/js": "^9.9.0", - "@protobuf-ts/plugin": "^2.9.4", - "@types/node": "^22.4.1", - "esbuild": "^0.23.1", - "eslint": "^9.9.0", - "globals": "^15.9.0", - "tsup": "^8.2.4", - "typescript-eslint": "^8.2.0" + "@biomejs/biome": "^1.9.3", + "@bytecodealliance/jco": "^1.9.1", + "@rslib/core": "^0.4.1", + "typescript": "^5.7.2" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", - "cpu": [ - "ppc64" - ], + "node_modules/@biomejs/biome": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", + "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "hasInstallScript": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, "engines": { - "node": ">=18" + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.4", + "@biomejs/cli-darwin-x64": "1.9.4", + "@biomejs/cli-linux-arm64": "1.9.4", + "@biomejs/cli-linux-arm64-musl": "1.9.4", + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", + "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=18" + "node": ">=14.21.3" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", + "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=18" + "node": ">=14.21.3" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", + "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", "optional": true, "os": [ - "freebsd" + "linux" ], "engines": { - "node": ">=18" + "node": ">=14.21.3" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", + "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", + "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=18" + "node": ">=14.21.3" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", + "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", + "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", "optional": true, "os": [ - "linux" + "win32" ], "engines": { - "node": ">=18" + "node": ">=14.21.3" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", + "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", "cpu": [ - "ia32" + "x64" ], "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", "optional": true, "os": [ - "linux" + "win32" ], "engines": { - "node": ">=18" + "node": ">=14.21.3" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "node_modules/@bytecodealliance/componentize-js": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@bytecodealliance/componentize-js/-/componentize-js-0.15.1.tgz", + "integrity": "sha512-bTQT+uwWNeyFXRiV6cp+5ERUKC2g6lyiMoeMys2/yg8IcWPwq+3btV1Pj/q0ueAwyiIsuQ//c+peMHPrNTmHOg==", + "dev": true, + "workspaces": [ + "." + ], + "dependencies": { + "@bytecodealliance/jco": "^1.8.1", + "@bytecodealliance/weval": "^0.3.3", + "@bytecodealliance/wizer": "^7.0.5", + "es-module-lexer": "^1.5.4" + } + }, + "node_modules/@bytecodealliance/jco": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.9.1.tgz", + "integrity": "sha512-Xmd1iw2OrWhlLOPZraFTBuE1AGfMSQVqKzswmq3k1vQ5B0EJe8O1CFG/UJeRXwbq1fxHHS9DTtlfAZiTeOdLWQ==", + "dev": true, + "license": "(Apache-2.0 WITH LLVM-exception)", + "workspaces": [ + "packages/preview2-shim" + ], + "dependencies": { + "@bytecodealliance/componentize-js": "^0.15.0", + "@bytecodealliance/preview2-shim": "^0.17.1", + "binaryen": "^120.0.0", + "chalk-template": "^1", + "commander": "^12", + "mkdirp": "^3", + "ora": "^8", + "terser": "^5" + }, + "bin": { + "jco": "src/jco.js" + } + }, + "node_modules/@bytecodealliance/preview2-shim": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.1.tgz", + "integrity": "sha512-h1qLL0TN5KXk/zagY2BtbZuDX6xYjz4Br9RZXEa0ID4UpiPc0agUMhTdz9r89G4vX5SU/tqBg1A6UNv2+DJ5pg==", + "dev": true, + "license": "(Apache-2.0 WITH LLVM-exception)" + }, + "node_modules/@bytecodealliance/weval": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@bytecodealliance/weval/-/weval-0.3.3.tgz", + "integrity": "sha512-hrQI47O1l3ilFscixu0uuSJTj5tbQW0QmCATQWWNW0E8wJxbKH4yo8y57O5gqpRSKk/T+da1sH/GJNrnGHTFNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@napi-rs/lzma": "^1.1.2", + "decompress": "^4.2.1", + "decompress-tar": "^4.1.1", + "decompress-unzip": "^4.0.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@bytecodealliance/wizer": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer/-/wizer-7.0.5.tgz", + "integrity": "sha512-xIbLzKxmUNaPwDWorcGtdxh1mcgDiXI8fe9KiDaSICKfCl9VtUKVyXIc3ix+VpwFczBbdhek+TlMiiCf+9lpOQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "wizer": "wizer.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@bytecodealliance/wizer-darwin-arm64": "7.0.5", + "@bytecodealliance/wizer-darwin-x64": "7.0.5", + "@bytecodealliance/wizer-linux-arm64": "7.0.5", + "@bytecodealliance/wizer-linux-s390x": "7.0.5", + "@bytecodealliance/wizer-linux-x64": "7.0.5", + "@bytecodealliance/wizer-win32-x64": "7.0.5" + } + }, + "node_modules/@bytecodealliance/wizer-darwin-arm64": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-darwin-arm64/-/wizer-darwin-arm64-7.0.5.tgz", + "integrity": "sha512-Tp0SgVQR568SVPvSfyWDT00yL4ry/w9FS2qy8ZwaP0EauYyjFSZojj6mESX6x9fpYkEnQdprgfdvhw5h1hTwCQ==", "cpu": [ - "loong64" + "arm64" ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "linux" + "darwin" ], - "engines": { - "node": ">=18" + "bin": { + "wizer-darwin-arm64": "wizer" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "node_modules/@bytecodealliance/wizer-darwin-x64": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-darwin-x64/-/wizer-darwin-x64-7.0.5.tgz", + "integrity": "sha512-HYmG5Q9SpQJnqR7kimb5J3VAh6E62b30GLG/E+6doS/UwNhSpSmYjaggVfuJvgFDbUxsnD1l36qZny0xMwxikA==", "cpu": [ - "mips64el" + "x64" ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "linux" + "darwin" ], - "engines": { - "node": ">=18" + "bin": { + "wizer-darwin-x64": "wizer" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "node_modules/@bytecodealliance/wizer-linux-arm64": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-arm64/-/wizer-linux-arm64-7.0.5.tgz", + "integrity": "sha512-01qqaiIWrYXPt2bjrfiluSSOmUL/PMjPtJlYa/XqZgK75g3RVn3sRkSflwoCXtXMRbHdb03qNrJ9w81+F17kvA==", "cpu": [ - "ppc64" + "arm64" ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "bin": { + "wizer-linux-arm64": "wizer" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "node_modules/@bytecodealliance/wizer-linux-s390x": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-s390x/-/wizer-linux-s390x-7.0.5.tgz", + "integrity": "sha512-smGfD4eJou81g6yDlV7MCRoKgKlqd4SQL00pHxQGrNfUPnfYKhZ4z80N9J9T2B++uo2FM14BFilsRrV5UevKlA==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "bin": { + "wizer-linux-s390x": "wizer" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "node_modules/@bytecodealliance/wizer-linux-x64": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-x64/-/wizer-linux-x64-7.0.5.tgz", + "integrity": "sha512-lxMb25jLd6n+hhjPhlqRBnBdGRumKkcEavqJ3p4OAtjr6pEPdbSfSVmYDt9LnvtqmqQSnUCtFRRr5J2BmQ3SkQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "bin": { + "wizer-linux-x64": "wizer" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "node_modules/@bytecodealliance/wizer-win32-x64": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-win32-x64/-/wizer-win32-x64-7.0.5.tgz", + "integrity": "sha512-eUY9a82HR20qIfyEffWdJZj7k4GH2wGaZpr70dinDy8Q648LeQayL0Z6FW5nApoezjy+CIBj0Mv+rHUASV9Jzw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "win32" ], - "engines": { - "node": ">=18" + "bin": { + "wizer-win32-x64": "wizer" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", - "cpu": [ - "ia32" - ], + "node_modules/@emnapi/core": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz", + "integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@emnapi/wasi-threads": "1.0.1", + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", - "cpu": [ - "x64" - ], + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", - "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.4", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", - "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { + "node_modules/@emnapi/wasi-threads": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", + "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" + "tslib": "^2.4.0" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "license": "MIT", "dependencies": { @@ -670,6 +432,17 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", @@ -688,142 +461,202 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@microsoft/api-extractor": { + "version": "7.50.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.50.0.tgz", + "integrity": "sha512-Ds/PHTiVzuENQsmXrJKkSdfgNkr/SDG/2rDef0AWl3BchAnXdO7gXaYsAkNx4gWiC4OngNA3fQfd3+BcQxP1DQ==", "dev": true, "license": "MIT", "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@protobuf-ts/plugin": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin/-/plugin-2.9.4.tgz", - "integrity": "sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==", - "dev": true, - "license": "Apache-2.0", + "peer": true, "dependencies": { - "@protobuf-ts/plugin-framework": "^2.9.4", - "@protobuf-ts/protoc": "^2.9.4", - "@protobuf-ts/runtime": "^2.9.4", - "@protobuf-ts/runtime-rpc": "^2.9.4", - "typescript": "^3.9" + "@microsoft/api-extractor-model": "7.30.3", + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.11.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.15.0", + "@rushstack/ts-command-line": "4.23.5", + "lodash": "~4.17.15", + "minimatch": "~3.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.7.2" }, "bin": { - "protoc-gen-dump": "bin/protoc-gen-dump", - "protoc-gen-ts": "bin/protoc-gen-ts" + "api-extractor": "bin/api-extractor" } }, - "node_modules/@protobuf-ts/plugin-framework": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin-framework/-/plugin-framework-2.9.4.tgz", - "integrity": "sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==", + "node_modules/@microsoft/api-extractor-model": { + "version": "7.30.3", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.30.3.tgz", + "integrity": "sha512-yEAvq0F78MmStXdqz9TTT4PZ05Xu5R8nqgwI5xmUmQjWBQ9E6R2n8HB/iZMRciG4rf9iwI2mtuQwIzDXBvHn1w==", "dev": true, - "license": "(Apache-2.0 AND BSD-3-Clause)", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@protobuf-ts/runtime": "^2.9.4", - "typescript": "^3.9" + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.11.0" } }, - "node_modules/@protobuf-ts/plugin-framework/node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", + "optional": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, - "node_modules/@protobuf-ts/plugin/node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "node_modules/@microsoft/tsdoc": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } + "license": "MIT", + "optional": true, + "peer": true }, - "node_modules/@protobuf-ts/protoc": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.9.4.tgz", - "integrity": "sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==", + "node_modules/@microsoft/tsdoc-config": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.1.tgz", + "integrity": "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==", "dev": true, - "license": "Apache-2.0", - "bin": { - "protoc": "protoc.js" - } - }, - "node_modules/@protobuf-ts/runtime": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.9.4.tgz", - "integrity": "sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==", - "license": "(Apache-2.0 AND BSD-3-Clause)" - }, - "node_modules/@protobuf-ts/runtime-rpc": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.9.4.tgz", - "integrity": "sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==", - "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@protobuf-ts/runtime": "^2.9.4" + "@microsoft/tsdoc": "0.15.1", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", - "integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", + "node_modules/@module-federation/error-codes": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.8.4.tgz", + "integrity": "sha512-55LYmrDdKb4jt+qr8qE8U3al62ZANp3FhfVaNPOaAmdTh0jHdD8M3yf5HKFlr5xVkVO4eV/F/J2NCfpbh+pEXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@module-federation/runtime": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.8.4.tgz", + "integrity": "sha512-yZeZ7z2Rx4gv/0E97oLTF3V6N25vglmwXGgoeju/W2YjsFvWzVtCDI7zRRb0mJhU6+jmSM8jP1DeQGbea/AiZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.8.4", + "@module-federation/sdk": "0.8.4" + } + }, + "node_modules/@module-federation/runtime-tools": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.8.4.tgz", + "integrity": "sha512-fjVOsItJ1u5YY6E9FnS56UDwZgqEQUrWFnouRiPtK123LUuqUI9FH4redZoKWlE1PB0ir1Z3tnqy8eFYzPO38Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@module-federation/runtime": "0.8.4", + "@module-federation/webpack-bundler-runtime": "0.8.4" + } + }, + "node_modules/@module-federation/sdk": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.8.4.tgz", + "integrity": "sha512-waABomIjg/5m1rPDBWYG4KUhS5r7OUUY7S+avpaVIY/tkPWB3ibRDKy2dNLLAMaLKq0u+B1qIdEp4NIWkqhqpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "isomorphic-rslog": "0.0.6" + } + }, + "node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.8.4.tgz", + "integrity": "sha512-HggROJhvHPUX7uqBD/XlajGygMNM1DG0+4OAkk8MBQe4a18QzrRNzZt6XQbRTSG4OaEoyRWhQHvYD3Yps405tQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@module-federation/runtime": "0.8.4", + "@module-federation/sdk": "0.8.4" + } + }, + "node_modules/@napi-rs/lzma": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma/-/lzma-1.4.1.tgz", + "integrity": "sha512-5f8K9NHjwHjZKGm3SS+7CFxXQhz8rbg2umBm/9g0xQRXBdYEI31N5z1ACuk9bmBQOusXAq9CArGfs/ZQso2rUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/lzma-android-arm-eabi": "1.4.1", + "@napi-rs/lzma-android-arm64": "1.4.1", + "@napi-rs/lzma-darwin-arm64": "1.4.1", + "@napi-rs/lzma-darwin-x64": "1.4.1", + "@napi-rs/lzma-freebsd-x64": "1.4.1", + "@napi-rs/lzma-linux-arm-gnueabihf": "1.4.1", + "@napi-rs/lzma-linux-arm64-gnu": "1.4.1", + "@napi-rs/lzma-linux-arm64-musl": "1.4.1", + "@napi-rs/lzma-linux-ppc64-gnu": "1.4.1", + "@napi-rs/lzma-linux-riscv64-gnu": "1.4.1", + "@napi-rs/lzma-linux-s390x-gnu": "1.4.1", + "@napi-rs/lzma-linux-x64-gnu": "1.4.1", + "@napi-rs/lzma-linux-x64-musl": "1.4.1", + "@napi-rs/lzma-wasm32-wasi": "1.4.1", + "@napi-rs/lzma-win32-arm64-msvc": "1.4.1", + "@napi-rs/lzma-win32-ia32-msvc": "1.4.1", + "@napi-rs/lzma-win32-x64-msvc": "1.4.1" + } + }, + "node_modules/@napi-rs/lzma-android-arm-eabi": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-android-arm-eabi/-/lzma-android-arm-eabi-1.4.1.tgz", + "integrity": "sha512-yenreSpZ9IrqppJOiWDqWMmja7XtSgio9LhtxYwgdILmy/OJTe/mlTYv+FhJBf7hIV9Razu5eBuEa3zKri81IA==", "cpu": [ "arm" ], @@ -832,12 +665,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", - "integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", + "node_modules/@napi-rs/lzma-android-arm64": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-android-arm64/-/lzma-android-arm64-1.4.1.tgz", + "integrity": "sha512-piutVBz5B1TNxXeEjub0n/IKI6dMaXPPRbVSXuc4gnZgzcihNDUh68vcLZgYd+IMiACZvBxvx2O3t5nthtph3A==", "cpu": [ "arm64" ], @@ -846,12 +682,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", - "integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", + "node_modules/@napi-rs/lzma-darwin-arm64": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-darwin-arm64/-/lzma-darwin-arm64-1.4.1.tgz", + "integrity": "sha512-sDfOhQQFqV8lGbpgJN9DqNLBPR7QOfYjcWUv8FOGPaVP1LPJDnrc5uCpRWWEa2zIKmTiO8P9xzIl0TDzrYmghg==", "cpu": [ "arm64" ], @@ -860,12 +699,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", - "integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", + "node_modules/@napi-rs/lzma-darwin-x64": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-darwin-x64/-/lzma-darwin-x64-1.4.1.tgz", + "integrity": "sha512-S5/RbC6EP4QkYy2xhxbfm48ZD9FkysfpWY4Slve0nj5RGGsHvcJBg2Pi69jrTPB/zLKz2SUa0i+RfUt9zvZNaw==", "cpu": [ "x64" ], @@ -874,12 +716,32 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", - "integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", + "node_modules/@napi-rs/lzma-freebsd-x64": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-freebsd-x64/-/lzma-freebsd-x64-1.4.1.tgz", + "integrity": "sha512-4AFnq6aZnclwameSBkDWu5Ftb8y4GwvVXeQXJKbN7hf7O5GG/8QpQB1R1NJw2QORUhpKwjAQUpbkTyhL2GFWWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-arm-gnueabihf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm-gnueabihf/-/lzma-linux-arm-gnueabihf-1.4.1.tgz", + "integrity": "sha512-j5rL1YRIm6rWmmGAvN6DPX6QuRjvFGB93xJ7DTRB47GXW4zHekXae6ivowjJ95vT4Iz4hSWkZbuwAy95eFrWRA==", "cpu": [ "arm" ], @@ -888,26 +750,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", - "integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", - "cpu": [ - "arm" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", - "integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", + "node_modules/@napi-rs/lzma-linux-arm64-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm64-gnu/-/lzma-linux-arm64-gnu-1.4.1.tgz", + "integrity": "sha512-1XdFGKyTS9m+VrRQYs9uz+ToHf4Jwm0ejHU48k9lT9MPl8jSqzKdVtFzZBPzronHteSynBfKmUq0+HeWmjrsOQ==", "cpu": [ "arm64" ], @@ -916,12 +767,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", - "integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", + "node_modules/@napi-rs/lzma-linux-arm64-musl": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm64-musl/-/lzma-linux-arm64-musl-1.4.1.tgz", + "integrity": "sha512-9d09tYS0/rBwIk1QTcO2hMZEB/ZpsG2+uFW5am1RHElSWMclObirB1An7b6AMDJcRvcomkOg2GZ9COzrvHKwEA==", "cpu": [ "arm64" ], @@ -930,12 +784,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", - "integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", + "node_modules/@napi-rs/lzma-linux-ppc64-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-ppc64-gnu/-/lzma-linux-ppc64-gnu-1.4.1.tgz", + "integrity": "sha512-UzEkmsgoJ3IOGIRb6kBzNiw+ThUpiighop7dVYfSqlF5juGzwf7YewC57RGn4FoJCvadOCrSm5VikAcgrwVgAw==", "cpu": [ "ppc64" ], @@ -944,12 +801,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", - "integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", + "node_modules/@napi-rs/lzma-linux-riscv64-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-riscv64-gnu/-/lzma-linux-riscv64-gnu-1.4.1.tgz", + "integrity": "sha512-9dUKlZ1PdwxTaFF+j3oc+xjlk9nqFwo1NWWOH30uwjl4Rm5Gkv+Fx0pHrzu4kR/iVA+oyQqa9/2uDYnGSTijBA==", "cpu": [ "riscv64" ], @@ -958,12 +818,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", - "integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", + "node_modules/@napi-rs/lzma-linux-s390x-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-s390x-gnu/-/lzma-linux-s390x-gnu-1.4.1.tgz", + "integrity": "sha512-MOVXUWJSLLCJDCCAlGa39sh7nv9XjvXzCf7QJus7rD8Ciz0mpXNXF9mg0ji7/MZ7pZlKPlXjXDnpVCfFdSEaFQ==", "cpu": [ "s390x" ], @@ -972,12 +835,253 @@ "optional": true, "os": [ "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.4.1.tgz", + "integrity": "sha512-Sxu7aJxU1sDbUTqjqLVDV3DCOAlbsFKvmuCN/S5uXBJd1IF2wJ9jK3NbFzfqTAo5Hudx8Y7kOb6+3K+fYPI1KQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-musl": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-musl/-/lzma-linux-x64-musl-1.4.1.tgz", + "integrity": "sha512-4I3BeKBQJSE5gF2/VTEv7wCLLjhapeutbCGpZPmDiLHZ74rm9edmNXAlKpdjADQ4YDLJ2GIBzttvwLXkJ9U+cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-wasm32-wasi": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-wasm32-wasi/-/lzma-wasm32-wasi-1.4.1.tgz", + "integrity": "sha512-s32HdKqQWbohf6DGWpG9YMODaBdbKJ++JpNr6Ii7821sKf4h/o+p8IRFTOaWdmdJdllEWlRirnd5crA29VivJQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@napi-rs/lzma-win32-arm64-msvc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-arm64-msvc/-/lzma-win32-arm64-msvc-1.4.1.tgz", + "integrity": "sha512-ISz+v7ML5mKnjEZ7Kk4Z1BIn411r/fz3tDy9j5yDnwQI0MgTsUQFrIQElGUpULWYs2aYc6EZ9PhECbLBfSjh7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-win32-ia32-msvc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-ia32-msvc/-/lzma-win32-ia32-msvc-1.4.1.tgz", + "integrity": "sha512-3WKuCpZBrd7Jrw+h1jSu5XAsRWepMJu0sYuRoA4Y4Cwfu9gI7p5Z5Bc510nfjg7M7xvdpkI4UoW2WY7kBFRYrQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-win32-x64-msvc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-x64-msvc/-/lzma-win32-x64-msvc-1.4.1.tgz", + "integrity": "sha512-0ixRo5z1zFXdh62hlrTV+QCTKHK0te5NHKaExOluhtcc6AdpMmpslvM9JhUxNHI+zM46w/DmmcvcOtqsaTmHgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.6.tgz", + "integrity": "sha512-z8YVS3XszxFTO73iwvFDNpQIzdMmSDTP/mB3E/ucR37V3Sx57hSExcXyMoNwaucWxnsWf4xfbZv0iZ30jr0M4Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.3.1", + "@emnapi/runtime": "^1.3.1", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@rsbuild/core": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-1.2.9.tgz", + "integrity": "sha512-Y3b4Y7lGIvNPywspP38deHkp/EEkTXrJEHeX1K5yz8U/94PkWvPlDebjjiSvmI6TT+9iVzsq22qDlBEdDuJZhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rspack/core": "1.2.3", + "@rspack/lite-tapable": "~1.0.1", + "@swc/helpers": "^0.5.15", + "core-js": "~3.40.0" + }, + "bin": { + "rsbuild": "bin/rsbuild.js" + }, + "engines": { + "node": ">=16.7.0" + } + }, + "node_modules/@rslib/core": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@rslib/core/-/core-0.4.1.tgz", + "integrity": "sha512-qYzXqmyJsYhrorxUnXz1UNaLlkun/4HPXfDmvkrvFJ/vbQTLZom66a9uMQfkluRish3kJNumYNwtgfxBzn6ZBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rsbuild/core": "~1.2.4", + "rsbuild-plugin-dts": "0.4.1", + "tinyglobby": "^0.2.10" + }, + "bin": { + "rslib": "bin/rslib.js" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@rspack/binding": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.2.3.tgz", + "integrity": "sha512-enpOXZPQOJO800wdWcR7H5Dx5UZfwkaT0D0xsHD53WbpI09Z2KJbLX7I/i1FLLy3K1KQTB+2FIHLVdRikasXZA==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "@rspack/binding-darwin-arm64": "1.2.3", + "@rspack/binding-darwin-x64": "1.2.3", + "@rspack/binding-linux-arm64-gnu": "1.2.3", + "@rspack/binding-linux-arm64-musl": "1.2.3", + "@rspack/binding-linux-x64-gnu": "1.2.3", + "@rspack/binding-linux-x64-musl": "1.2.3", + "@rspack/binding-win32-arm64-msvc": "1.2.3", + "@rspack/binding-win32-ia32-msvc": "1.2.3", + "@rspack/binding-win32-x64-msvc": "1.2.3" + } + }, + "node_modules/@rspack/binding-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-xuwYzhPgNCr4BtKXCU3xe4249TFsXAZglIlbxv8Qs3PeIarrZMRddcqH2zUXi+nJavNw3yN12sCYEzk1f+O4FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" ] }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", - "integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", + "node_modules/@rspack/binding-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", + "integrity": "sha512-afiIN8elcrO2EtO27UN0qyZqu5FXGUdclud56DrhvEfnWS3GGxJEdjA8XUYVXkfCYakdXHucIJKlkkgaAjEvHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rspack/binding-linux-arm64-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", + "integrity": "sha512-K2u/fPUmKujlKSWL3q2zaUu8/6ZK/bOGKcqJSib8jdanQQ/GFKwKtPAFOOa/vvqbzhDocqKOobFR10FhgJqCHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-arm64-musl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", + "integrity": "sha512-mgovdzGb6cH9hQsjTyzDbfZWCPhTcoHcLro1P7UbiqcLPMDJp/k3Io9xV2/EJhaDA1aynIdq7XfY0fuk4+6Irw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-x64-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", + "integrity": "sha512-542lwJzB1RMGuVdBdA3cOWTlmL9okpOppHUBWcNCjmJM+9zTI+0jwjVe8HaqOqtuR8XzNsoCwT9QonU/GLcuhg==", "cpu": [ "x64" ], @@ -988,10 +1092,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", - "integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", + "node_modules/@rspack/binding-linux-x64-musl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", + "integrity": "sha512-dJromiREDcTWqzfCOI5y1IVoYmUnCv7vCp63AEq0+13fJJdk7+pcNN3VV2jOKpk9VECSvjg1c01wl+UzXAXFMw==", "cpu": [ "x64" ], @@ -1002,10 +1106,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", - "integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", + "node_modules/@rspack/binding-win32-arm64-msvc": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", + "integrity": "sha512-S8ZKddMMQDGy8jx/R0i2m1XrmfY2CpI+t6lIEpsuZuKUR4MbOGKN2DuL4MDnT3m8JaYvC8ihsvQjBXQCy3SNxQ==", "cpu": [ "arm64" ], @@ -1016,10 +1120,10 @@ "win32" ] }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", - "integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", + "node_modules/@rspack/binding-win32-ia32-msvc": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.2.3.tgz", + "integrity": "sha512-74lqSMKQJcJcgfFaxm+G9YVJSl2KK9/v4fRoMsWApztNy2qNgee+UguNBCOU6JLa3rVSj8Z5OVVDtJkGFrSvVg==", "cpu": [ "ia32" ], @@ -1030,10 +1134,10 @@ "win32" ] }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", - "integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", + "node_modules/@rspack/binding-win32-x64-msvc": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", + "integrity": "sha512-fcU532PgFdd5Bil8jwQW0Dcb/80oM6V0qSstGIxZ4M77t4t8e/PcukXfORTL71FfNQ64Rd4Dp6XRl1NHNJVxeg==", "cpu": [ "x64" ], @@ -1044,230 +1148,173 @@ "win32" ] }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.1.tgz", - "integrity": "sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg==", + "node_modules/@rspack/core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.2.3.tgz", + "integrity": "sha512-BFgdUYf05/hjjY9Nlwq8DpWaRJN5w2kTl8ZJi20SRL60oAx+ZD2ABT+fsPhBiFSmfTZDdvGGIq5e3vfRzoIuqg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", - "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/type-utils": "8.2.0", - "@typescript-eslint/utils": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "@module-federation/runtime-tools": "0.8.4", + "@rspack/binding": "1.2.3", + "@rspack/lite-tapable": "1.0.1", + "caniuse-lite": "^1.0.30001616" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.0.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" + "@rspack/tracing": "^1.x", + "@swc/helpers": ">=0.5.1" }, "peerDependenciesMeta": { - "typescript": { + "@rspack/tracing": { + "optional": true + }, + "@swc/helpers": { "optional": true } } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", - "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", + "node_modules/@rspack/lite-tapable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.0.1.tgz", + "integrity": "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/typescript-estree": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", - "debug": "^4.3.4" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.0.0" + } + }, + "node_modules/@rushstack/node-core-library": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.11.0.tgz", + "integrity": "sha512-I8+VzG9A0F3nH2rLpPd7hF8F7l5Xb7D+ldrWVZYegXM6CsKkvWc670RlgK3WX8/AseZfXA/vVrh0bpXe2Y2UDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "@types/node": "*" }, "peerDependenciesMeta": { - "typescript": { + "@types/node": { "optional": true } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", - "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", - "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", + "node_modules/@rushstack/rig-package": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.2.0", - "@typescript-eslint/utils": "8.2.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", - "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", + "node_modules/@rushstack/terminal": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.0.tgz", + "integrity": "sha512-vXQPRQ+vJJn4GVqxkwRe+UGgzNxdV8xuJZY2zem46Y0p3tlahucH9/hPmLGj2i9dQnUBFiRnoM9/KW7PYw8F4Q==", "dev": true, "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", - "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", - "dev": true, - "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", - "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/typescript-estree": "8.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@rushstack/node-core-library": "5.11.0", + "supports-color": "~8.1.1" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", - "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", + "node_modules/@rushstack/ts-command-line": { + "version": "4.23.5", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.5.tgz", + "integrity": "sha512-jg70HfoK44KfSP3MTiL5rxsZH7X1ktX3cZs9Sl8eDu1/LxJSbPsh0MOFRC710lIuYYSgxWjI5AjbCBAl7u3RxA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@typescript-eslint/types": "8.2.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@rushstack/terminal": "0.15.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "dev": true, "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "dependencies": { + "tslib": "^2.8.0" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", "bin": { @@ -1277,26 +1324,18 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -1304,10 +1343,47 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -1317,55 +1393,16 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "optional": true, + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" } }, "node_modules/balanced-match": { @@ -1373,166 +1410,214 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/binaryen": { + "version": "120.0.0", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-120.0.0.tgz", + "integrity": "sha512-MaNC1qW5ubk5S7MNNxNpAb9ivKp6TAf8CDknRk4XeCC2wkrpdaubK10S1CAwUXaDDF54gZLtk6opzbEA9VTtJw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "license": "Apache-2.0", + "bin": { + "wasm-as": "bin/wasm-as", + "wasm-ctor-eval": "bin/wasm-ctor-eval", + "wasm-dis": "bin/wasm-dis", + "wasm-merge": "bin/wasm-merge", + "wasm-metadce": "bin/wasm-metadce", + "wasm-opt": "bin/wasm-opt", + "wasm-reduce": "bin/wasm-reduce", + "wasm-shell": "bin/wasm-shell", + "wasm2js": "bin/wasm2js" } }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" } }, - "node_modules/bundle-require": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.0.0.tgz", - "integrity": "sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==", + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "dev": true, - "license": "MIT", - "dependencies": { - "load-tsconfig": "^0.2.3" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "esbuild": ">=0.18" - } + "license": "MIT" }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "license": "MIT" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001700", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", + "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/chalk-template": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.0.tgz", + "integrity": "sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "chalk": "^5.2.0" }, "engines": { - "node": ">=8" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">= 8.10.0" + "node": ">=18" }, "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=18" } }, "node_modules/concat-map": { @@ -1540,584 +1625,225 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", - "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", - "dev": true, "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } + "optional": true, + "peer": true }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "node_modules/core-js": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", "dev": true, "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/eslint": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", - "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.17.1", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.9.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.2", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "node": ">=4" } }, - "node_modules/eslint-scope": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", - "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" }, "engines": { - "node": ">=10.13.0" + "node": ">=4" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/espree": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "license": "MIT", "dependencies": { - "acorn": "^8.12.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=4" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" }, "engines": { - "node": ">=0.10" + "node": ">=4" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "once": "^1.4.0" } }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, "license": "MIT", "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "peer": true + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "MIT" }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "is-glob": "^4.0.1" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">= 6" + "node": ">=14.14" } }, - "node_modules/globals": { - "version": "15.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", - "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, "license": "MIT", "engines": { @@ -2127,33 +1853,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", "dev": true, "license": "MIT", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, - "license": "MIT" + "license": "ISC" }, "node_modules/has-flag": { "version": "4.0.0", @@ -2161,420 +1880,294 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isomorphic-rslog": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/isomorphic-rslog/-/isomorphic-rslog-0.0.6.tgz", + "integrity": "sha512-HM0q6XqQ93psDlqvuViNs/Ea3hAyGDkIdVAHlrEocjjAwGrs1fZ+EdQjS9eUPacnYB7Y8SoDdSY3H8p3ce205A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.6" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { + "node_modules/mkdirp": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" + "bin": { + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2585,221 +2178,118 @@ "node": ">=0.10.0" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", "dev": true, "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "peer": true }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=0.10.0" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "lilconfig": "^3.1.1" + "pinkie": "^2.0.0" }, "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } + "license": "MIT" }, "node_modules/punycode": { "version": "2.3.1", @@ -2807,14 +2297,119 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rsbuild-plugin-dts": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/rsbuild-plugin-dts/-/rsbuild-plugin-dts-0.4.1.tgz", + "integrity": "sha512-9/GJ/U8vt7pvRJts8l/FX6f5HzT2/0LUlwCrCz4mvqHh7EF4DI/b1TWXehvR7OH5ORE3hQxmPAhW01eaHBT8IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "picocolors": "1.1.1", + "tinyglobby": "^0.2.10" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7", + "@rsbuild/core": "1.x", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -2832,106 +2427,38 @@ ], "license": "MIT" }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", "dev": true, "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", - "integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.5" + "commander": "^2.8.1" }, "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.0", - "@rollup/rollup-android-arm64": "4.21.0", - "@rollup/rollup-darwin-arm64": "4.21.0", - "@rollup/rollup-darwin-x64": "4.21.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.0", - "@rollup/rollup-linux-arm-musleabihf": "4.21.0", - "@rollup/rollup-linux-arm64-gnu": "4.21.0", - "@rollup/rollup-linux-arm64-musl": "4.21.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", - "@rollup/rollup-linux-riscv64-gnu": "4.21.0", - "@rollup/rollup-linux-s390x-gnu": "4.21.0", - "@rollup/rollup-linux-x64-gnu": "4.21.0", - "@rollup/rollup-linux-x64-musl": "4.21.0", - "@rollup/rollup-win32-arm64-msvc": "4.21.0", - "@rollup/rollup-win32-ia32-msvc": "4.21.0", - "@rollup/rollup-win32-x64-msvc": "4.21.0", - "fsevents": "~2.3.2" + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/seek-bzip/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } + "license": "MIT" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" }, @@ -2939,121 +2466,107 @@ "node": ">=10" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "ISC" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", - "dependencies": { - "whatwg-url": "^7.0.0" - }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "safe-buffer": "~5.1.0" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "license": "MIT" }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-ansi": { @@ -3072,38 +2585,14 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "is-natural-number": "^4.0.1" } }, "node_modules/strip-json-comments": { @@ -3112,6 +2601,8 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" }, @@ -3119,197 +2610,156 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" + "node": ">=10" }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, "license": "MIT", "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/tsup": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.2.4.tgz", - "integrity": "sha512-akpCPePnBnC/CXgRrcy72ZSntgIEUa1jN0oJbbvpALWKNOz1B7aM+UVDWGRGIO/T/PZugAESWDJUAb5FD48o8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "bundle-require": "^5.0.0", - "cac": "^6.7.14", - "chokidar": "^3.6.0", - "consola": "^3.2.3", - "debug": "^4.3.5", - "esbuild": "^0.23.0", - "execa": "^5.1.1", - "globby": "^11.1.0", - "joycon": "^3.1.1", - "picocolors": "^1.0.1", - "postcss-load-config": "^6.0.1", - "resolve-from": "^5.0.0", - "rollup": "^4.19.0", - "source-map": "0.8.0-beta.0", - "sucrase": "^3.35.0", - "tree-kill": "^1.2.2" - }, - "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@microsoft/api-extractor": "^7.36.0", - "@swc/core": "^1", - "postcss": "^8.4.12", - "typescript": ">=4.5.0" - }, - "peerDependenciesMeta": { - "@microsoft/api-extractor": { - "optional": true - }, - "@swc/core": { - "optional": true - }, - "postcss": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/terser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.11.tgz", + "integrity": "sha512-32TmKeeKUahv0Go8WmQgiEp9Y21NuxjwjqiRC1nrUB51YacfSwuB44xgXD+HdIppmMRgjQNPdrHyA6vIybYZ+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3318,36 +2768,28 @@ "node": ">=14.17" } }, - "node_modules/typescript-eslint": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.2.0.tgz", - "integrity": "sha512-DmnqaPcML0xYwUzgNbM1XaKXpEb7BShYf2P1tkUmmcl8hyeG7Pj08Er7R9bNy6AufabywzJcOybQAtnD/c9DGw==", + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.2.0", - "@typescript-eslint/parser": "8.2.0", - "@typescript-eslint/utils": "8.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "buffer": "^5.2.1", + "through": "^2.3.8" } }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10.0.0" + } }, "node_modules/uri-js": { "version": "4.4.1", @@ -3355,164 +2797,54 @@ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", + "optional": true, + "peer": true, "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, "license": "MIT" }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } } } diff --git a/objdiff-wasm/package.json b/objdiff-wasm/package.json index 3d466cf..26b136a 100644 --- a/objdiff-wasm/package.json +++ b/objdiff-wasm/package.json @@ -1,6 +1,6 @@ { "name": "objdiff-wasm", - "version": "2.7.1", + "version": "3.0.0-alpha.1", "description": "A local diffing tool for decompilation projects.", "author": { "name": "Luke Street", @@ -12,28 +12,19 @@ "type": "git", "url": "git+https://github.com/encounter/objdiff.git" }, - "files": [ - "dist/*" - ], - "main": "dist/main.js", - "types": "dist/main.d.ts", + "files": ["dist/*"], + "main": "dist/objdiff.js", + "types": "dist/objdiff.d.ts", "scripts": { - "build": "tsup", - "build:all": "npm run build:wasm && npm run build:proto && npm run build", - "build:proto": "protoc --ts_out=gen --ts_opt add_pb_suffix,eslint_disable,ts_nocheck,use_proto_field_name --proto_path=../objdiff-core/protos ../objdiff-core/protos/*.proto", - "build:wasm": "cd ../objdiff-core && wasm-pack build --out-dir ../objdiff-wasm/pkg --target web -- --features arm,arm64,dwarf,config,ppc,x86,wasm" - }, - "dependencies": { - "@protobuf-ts/runtime": "^2.9.4" + "build": "npm run build:wasm && npm run build:transpile && npm run build:lib", + "build:wasm": "cargo +nightly -Zbuild-std=panic_abort,core,alloc -Zbuild-std-features=compiler-builtins-mem build --target wasm32-wasip2 --release --no-default-features", + "build:transpile": "jco transpile ../target/wasm32-wasip2/release/objdiff_wasm.wasm --no-nodejs-compat --no-wasi-shim --no-namespaced-exports --map wasi:logging/logging=./wasi-logging.js -o pkg --name objdiff", + "build:lib": "rslib build" }, "devDependencies": { - "@eslint/js": "^9.9.0", - "@protobuf-ts/plugin": "^2.9.4", - "@types/node": "^22.4.1", - "esbuild": "^0.23.1", - "eslint": "^9.9.0", - "globals": "^15.9.0", - "tsup": "^8.2.4", - "typescript-eslint": "^8.2.0" + "@biomejs/biome": "^1.9.3", + "@bytecodealliance/jco": "^1.9.1", + "@rslib/core": "^0.4.1", + "typescript": "^5.7.2" } } diff --git a/objdiff-wasm/rslib.config.ts b/objdiff-wasm/rslib.config.ts new file mode 100644 index 0000000..ab6957d --- /dev/null +++ b/objdiff-wasm/rslib.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + source: { + entry: { + 'wasi-logging': 'lib/wasi-logging.ts', + }, + }, + lib: [ + { + format: 'esm', + syntax: 'es2022', + }, + ], + output: { + target: 'web', + copy: [{ from: 'pkg' }, { from: '../objdiff-core/config-schema.json' }], + }, +}); diff --git a/objdiff-wasm/src/api.rs b/objdiff-wasm/src/api.rs new file mode 100644 index 0000000..aa05a7f --- /dev/null +++ b/objdiff-wasm/src/api.rs @@ -0,0 +1,321 @@ +use alloc::{ + format, + rc::Rc, + str::FromStr, + string::{String, ToString}, + vec::Vec, +}; +use core::cell::RefCell; + +use objdiff_core::{diff, obj}; +use regex::RegexBuilder; + +use super::logging; + +wit_bindgen::generate!({ + world: "api", + with: { + "wasi:logging/logging@0.1.0-draft": logging::wasi_logging, + }, +}); + +use exports::objdiff::core::{ + diff::Guest as GuestDiff, + diff_types::{ + DiffConfigBorrow, DiffResult, Guest as GuestDiffTypes, GuestDiffConfig, GuestObject, + GuestObjectDiff, Object, ObjectBorrow, ObjectDiff, ObjectDiffBorrow, + }, + display_types::{ + ContextMenuItem, DiffText, DiffTextOpcode, DiffTextSegment, DiffTextSymbol, DisplayConfig, + HoverItem, InstructionDiffKind, InstructionDiffRow, SectionDisplay, SectionDisplaySymbol, + SymbolDisplay, SymbolFilter, SymbolFlags, SymbolKind, SymbolRef, + }, +}; + +struct Component; + +impl Guest for Component { + fn init(level: logging::wasi_logging::Level) { logging::init(level); } + + fn version() -> String { env!("CARGO_PKG_VERSION").to_string() } +} + +#[repr(transparent)] +struct ResourceObject(Rc); + +struct ResourceObjectDiff(Rc, diff::ObjectDiff); + +#[repr(transparent)] +struct ResourceDiffConfig(RefCell); + +impl GuestDiffTypes for Component { + type DiffConfig = ResourceDiffConfig; + type Object = ResourceObject; + type ObjectDiff = ResourceObjectDiff; +} + +impl GuestDiff for Component { + fn run_diff( + left: Option, + right: Option, + diff_config: DiffConfigBorrow, + ) -> Result { + let diff_config = diff_config.get::().0.borrow(); + log::debug!("Running diff with config: {:?}", diff_config); + let result = diff::diff_objs( + left.as_ref().map(|o| o.get::().0.as_ref()), + right.as_ref().map(|o| o.get::().0.as_ref()), + None, + &diff_config, + &diff::MappingConfig::default(), + ) + .map_err(|e| e.to_string())?; + Ok(DiffResult { + left: result.left.map(|d| { + ObjectDiff::new(ResourceObjectDiff( + left.unwrap().get::().0.clone(), + d, + )) + }), + right: result.right.map(|d| { + ObjectDiff::new(ResourceObjectDiff( + right.unwrap().get::().0.clone(), + d, + )) + }), + }) + } + + fn symbol_context(_obj: ObjectBorrow, _symbol: SymbolRef) -> Vec { todo!() } + + fn symbol_hover(_obj: ObjectBorrow, _symbol: SymbolRef) -> Vec { todo!() } + + fn display_sections( + diff: ObjectDiffBorrow, + filter: SymbolFilter, + config: DisplayConfig, + ) -> Vec { + let regex = filter.regex.as_ref().and_then(|s| { + RegexBuilder::new(s).case_insensitive(true).build().ok().or_else(|| { + // Use the string as a literal if the regex fails to compile + let escaped = regex::escape(s); + RegexBuilder::new(&escaped).case_insensitive(true).build().ok() + }) + }); + let filter = if let Some(mapping) = filter.mapping { + diff::display::SymbolFilter::Mapping(mapping as usize, regex.as_ref()) + } else if let Some(regex) = ®ex { + diff::display::SymbolFilter::Search(regex) + } else { + diff::display::SymbolFilter::None + }; + let obj_diff = diff.get::(); + diff::display::display_sections( + obj_diff.0.as_ref(), + &obj_diff.1, + filter, + config.show_hidden_symbols, + config.show_mapped_symbols, + config.reverse_fn_order, + ) + .into_iter() + .map(|d| SectionDisplay { + id: d.id, + name: d.name, + size: d.size, + match_percent: d.match_percent, + symbols: d + .symbols + .into_iter() + .map(|s| SectionDisplaySymbol { + symbol: s.symbol as SymbolRef, + is_mapping_symbol: s.is_mapping_symbol, + }) + .collect(), + }) + .collect() + } + + fn display_symbol( + diff: ObjectDiffBorrow, + symbol_display: SectionDisplaySymbol, + ) -> SymbolDisplay { + let obj_diff = diff.get::(); + let obj = obj_diff.0.as_ref(); + let obj_diff = &obj_diff.1; + let symbol_idx = symbol_display.symbol as usize; + let symbol = &obj.symbols[symbol_idx]; + let symbol_diff = if symbol_display.is_mapping_symbol { + obj_diff + .mapping_symbols + .iter() + .find(|s| s.symbol_index == symbol_idx) + .map(|s| &s.symbol_diff) + .unwrap() + } else { + &obj_diff.symbols[symbol_idx] + }; + SymbolDisplay { + name: symbol.name.clone(), + demangled_name: symbol.demangled_name.clone(), + address: symbol.address, + size: symbol.size, + kind: SymbolKind::from(symbol.kind), + section: symbol.section.map(|s| s as u32), + flags: SymbolFlags::from(symbol.flags), + align: symbol.align.map(|a| a.get()), + virtual_address: symbol.virtual_address, + target_symbol: symbol_diff.target_symbol.map(|s| s as u32), + match_percent: symbol_diff.match_percent, + diff_score: symbol_diff.diff_score, + row_count: symbol_diff.instruction_rows.len() as u32, + } + } + + fn display_instruction_row( + diff: ObjectDiffBorrow, + symbol_display: SectionDisplaySymbol, + row_index: u32, + diff_config: DiffConfigBorrow, + ) -> InstructionDiffRow { + let mut segments = Vec::with_capacity(16); + let obj_diff = diff.get::(); + let obj = obj_diff.0.as_ref(); + let obj_diff = &obj_diff.1; + let symbol_idx = symbol_display.symbol as usize; + let symbol_diff = if symbol_display.is_mapping_symbol { + obj_diff + .mapping_symbols + .iter() + .find(|s| s.symbol_index == symbol_idx) + .map(|s| &s.symbol_diff) + .unwrap() + } else { + &obj_diff.symbols[symbol_idx] + }; + let row = &symbol_diff.instruction_rows[row_index as usize]; + let diff_config = diff_config.get::().0.borrow(); + diff::display::display_row(obj, symbol_idx, row, &diff_config, |text, idx| { + segments.push(DiffTextSegment { text: DiffText::from(text), diff_index: idx.get() }); + Ok(()) + }) + .unwrap(); + InstructionDiffRow { segments, diff_kind: InstructionDiffKind::from(row.kind) } + } +} + +impl From for SymbolKind { + fn from(kind: obj::SymbolKind) -> Self { + match kind { + obj::SymbolKind::Unknown => SymbolKind::Unknown, + obj::SymbolKind::Function => SymbolKind::Function, + obj::SymbolKind::Object => SymbolKind::Object, + obj::SymbolKind::Section => SymbolKind::Section, + } + } +} + +impl From for SymbolFlags { + fn from(flags: obj::SymbolFlagSet) -> SymbolFlags { + let mut out = SymbolFlags::empty(); + for flag in flags { + out |= match flag { + obj::SymbolFlag::Global => SymbolFlags::GLOBAL, + obj::SymbolFlag::Local => SymbolFlags::LOCAL, + obj::SymbolFlag::Weak => SymbolFlags::WEAK, + obj::SymbolFlag::Common => SymbolFlags::COMMON, + obj::SymbolFlag::Hidden => SymbolFlags::HIDDEN, + obj::SymbolFlag::HasExtra => SymbolFlags::HAS_EXTRA, + obj::SymbolFlag::SizeInferred => SymbolFlags::SIZE_INFERRED, + }; + } + out + } +} + +impl From> for DiffText { + fn from(text: diff::display::DiffText) -> Self { + match text { + diff::display::DiffText::Basic(v) => DiffText::Basic(v.to_string()), + diff::display::DiffText::Line(v) => DiffText::Line(v), + diff::display::DiffText::Address(v) => DiffText::Address(v), + diff::display::DiffText::Opcode(n, op) => { + DiffText::Opcode(DiffTextOpcode { mnemonic: n.to_string(), opcode: op }) + } + diff::display::DiffText::Argument(s) => match s { + obj::InstructionArgValue::Signed(v) => DiffText::Signed(*v), + obj::InstructionArgValue::Unsigned(v) => DiffText::Unsigned(*v), + obj::InstructionArgValue::Opaque(v) => DiffText::Opaque(v.to_string()), + }, + diff::display::DiffText::BranchDest(v) => DiffText::BranchDest(v), + diff::display::DiffText::Symbol(s) => DiffText::Symbol(DiffTextSymbol { + name: s.name.clone(), + demangled_name: s.demangled_name.clone(), + }), + diff::display::DiffText::Addend(v) => DiffText::Addend(v), + diff::display::DiffText::Spacing(v) => DiffText::Spacing(v as u32), + diff::display::DiffText::Eol => DiffText::Eol, + } + } +} + +impl From for InstructionDiffKind { + fn from(kind: diff::InstructionDiffKind) -> Self { + match kind { + diff::InstructionDiffKind::None => InstructionDiffKind::None, + diff::InstructionDiffKind::OpMismatch => InstructionDiffKind::OpMismatch, + diff::InstructionDiffKind::ArgMismatch => InstructionDiffKind::ArgMismatch, + diff::InstructionDiffKind::Replace => InstructionDiffKind::Replace, + diff::InstructionDiffKind::Insert => InstructionDiffKind::Insert, + diff::InstructionDiffKind::Delete => InstructionDiffKind::Delete, + } + } +} + +impl GuestDiffConfig for ResourceDiffConfig { + fn new() -> Self { Self(RefCell::new(diff::DiffObjConfig::default())) } + + fn set_property(&self, key: String, value: String) -> Result<(), String> { + let id = diff::ConfigPropertyId::from_str(&key) + .map_err(|_| format!("Invalid property key {:?}", key))?; + self.0 + .borrow_mut() + .set_property_value_str(id, &value) + .map_err(|_| format!("Invalid property value {:?}", value)) + } + + fn get_property(&self, key: String) -> Result { + let id = diff::ConfigPropertyId::from_str(&key) + .map_err(|_| format!("Invalid property key {:?}", key))?; + Ok(self.0.borrow().get_property_value(id).to_string()) + } +} + +impl GuestObject for ResourceObject { + fn parse(data: Vec, diff_config: DiffConfigBorrow) -> Result { + let diff_config = diff_config.get::().0.borrow(); + obj::read::parse(&data, &diff_config) + .map(|o| Object::new(ResourceObject(Rc::new(o)))) + .map_err(|e| e.to_string()) + } +} + +impl GuestObjectDiff for ResourceObjectDiff { + fn find_symbol(&self, name: String, section_name: Option) -> Option { + let obj = self.0.as_ref(); + obj.symbols + .iter() + .position(|s| { + s.name == name + && match section_name.as_deref() { + Some(section_name) => { + s.section.is_some_and(|n| obj.sections[n].name == section_name) + } + None => true, + } + }) + .map(|i| i as SymbolRef) + } +} + +export!(Component); diff --git a/objdiff-core/src/wasm/cabi_realloc.rs b/objdiff-wasm/src/cabi_realloc.rs similarity index 100% rename from objdiff-core/src/wasm/cabi_realloc.rs rename to objdiff-wasm/src/cabi_realloc.rs diff --git a/objdiff-wasm/src/display.ts b/objdiff-wasm/src/display.ts deleted file mode 100644 index b93051a..0000000 --- a/objdiff-wasm/src/display.ts +++ /dev/null @@ -1,107 +0,0 @@ -import {ArgumentValue, InstructionDiff, RelocationTarget} from "../gen/diff_pb"; - -export type DiffText = - DiffTextBasic - | DiffTextBasicColor - | DiffTextAddress - | DiffTextLine - | DiffTextOpcode - | DiffTextArgument - | DiffTextSymbol - | DiffTextBranchDest - | DiffTextSpacing; - -type DiffTextBase = { - diff_index?: number, -}; -export type DiffTextBasic = DiffTextBase & { - type: 'basic', - text: string, -}; -export type DiffTextBasicColor = DiffTextBase & { - type: 'basic_color', - text: string, - index: number, -}; -export type DiffTextAddress = DiffTextBase & { - type: 'address', - address: bigint, -}; -export type DiffTextLine = DiffTextBase & { - type: 'line', - line_number: number, -}; -export type DiffTextOpcode = DiffTextBase & { - type: 'opcode', - mnemonic: string, - opcode: number, -}; -export type DiffTextArgument = DiffTextBase & { - type: 'argument', - value: ArgumentValue, -}; -export type DiffTextSymbol = DiffTextBase & { - type: 'symbol', - target: RelocationTarget, -}; -export type DiffTextBranchDest = DiffTextBase & { - type: 'branch_dest', - address: bigint, -}; -export type DiffTextSpacing = DiffTextBase & { - type: 'spacing', - count: number, -}; - -// Native JavaScript implementation of objdiff_core::diff::display::display_diff -export function displayDiff(diff: InstructionDiff, baseAddr: bigint, cb: (text: DiffText) => void) { - const ins = diff.instruction; - if (!ins) { - return; - } - if (ins.line_number != null) { - cb({type: 'line', line_number: ins.line_number}); - } - cb({type: 'address', address: ins.address - baseAddr}); - if (diff.branch_from) { - cb({type: 'basic_color', text: ' ~> ', index: diff.branch_from.branch_index}); - } else { - cb({type: 'spacing', count: 4}); - } - cb({type: 'opcode', mnemonic: ins.mnemonic, opcode: ins.opcode}); - let arg_diff_idx = 0; // non-PlainText argument index - for (let i = 0; i < ins.arguments.length; i++) { - if (i === 0) { - cb({type: 'spacing', count: 1}); - } - const arg = ins.arguments[i].value; - let diff_index: number | undefined; - if (arg.oneofKind !== 'plain_text') { - diff_index = diff.arg_diff[arg_diff_idx]?.diff_index; - arg_diff_idx++; - } - switch (arg.oneofKind) { - case "plain_text": - cb({type: 'basic', text: arg.plain_text, diff_index}); - break; - case "argument": - cb({type: 'argument', value: arg.argument, diff_index}); - break; - case "relocation": { - const reloc = ins.relocation!; - cb({type: 'symbol', target: reloc.target!, diff_index}); - break; - } - case "branch_dest": - if (arg.branch_dest < baseAddr) { - cb({type: 'basic', text: '', diff_index}); - } else { - cb({type: 'branch_dest', address: arg.branch_dest - baseAddr, diff_index}); - } - break; - } - } - if (diff.branch_to) { - cb({type: 'basic_color', text: ' ~> ', index: diff.branch_to.branch_index}); - } -} diff --git a/objdiff-wasm/src/lib.rs b/objdiff-wasm/src/lib.rs new file mode 100644 index 0000000..ab5d102 --- /dev/null +++ b/objdiff-wasm/src/lib.rs @@ -0,0 +1,14 @@ +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +#[cfg(target_os = "wasi")] +mod api; +#[cfg(target_os = "wasi")] +mod logging; + +#[cfg(all(target_os = "wasi", not(feature = "std")))] +mod cabi_realloc; + +#[cfg(all(target_family = "wasm", not(feature = "std")))] +#[global_allocator] +static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() }; diff --git a/objdiff-wasm/src/logging.rs b/objdiff-wasm/src/logging.rs new file mode 100644 index 0000000..1c003aa --- /dev/null +++ b/objdiff-wasm/src/logging.rs @@ -0,0 +1,52 @@ +wit_bindgen::generate!({ + world: "imports", + path: "wit/deps/logging", +}); + +use alloc::format; + +pub use wasi::logging::logging as wasi_logging; + +struct WasiLogger; + +impl log::Log for WasiLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { metadata.level() <= log::max_level() } + + fn log(&self, record: &log::Record) { + if !self.enabled(record.metadata()) { + return; + } + let level = match record.level() { + log::Level::Error => wasi_logging::Level::Error, + log::Level::Warn => wasi_logging::Level::Warn, + log::Level::Info => wasi_logging::Level::Info, + log::Level::Debug => wasi_logging::Level::Debug, + log::Level::Trace => wasi_logging::Level::Trace, + }; + wasi_logging::log(level, record.target(), &format!("{}", record.args())); + } + + fn flush(&self) {} +} + +static LOGGER: WasiLogger = WasiLogger; + +pub fn init(level: wasi_logging::Level) { + let _ = log::set_logger(&LOGGER); + log::set_max_level(match level { + wasi_logging::Level::Error => log::LevelFilter::Error, + wasi_logging::Level::Warn => log::LevelFilter::Warn, + wasi_logging::Level::Info => log::LevelFilter::Info, + wasi_logging::Level::Debug => log::LevelFilter::Debug, + wasi_logging::Level::Trace => log::LevelFilter::Trace, + wasi_logging::Level::Critical => log::LevelFilter::Off, + }); +} + +#[cfg(not(feature = "std"))] +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + use alloc::string::ToString; + wasi_logging::log(wasi_logging::Level::Critical, "objdiff_core::panic", &info.to_string()); + core::arch::wasm32::unreachable(); +} diff --git a/objdiff-wasm/src/main.ts b/objdiff-wasm/src/main.ts deleted file mode 100644 index f7cd3bb..0000000 --- a/objdiff-wasm/src/main.ts +++ /dev/null @@ -1,132 +0,0 @@ -import {DiffResult} from "../gen/diff_pb"; -import type { - ConfigProperty, - MappingConfig, - SymbolMappings, -} from '../pkg'; -import {AnyHandlerData, InMessage, OutMessage} from './worker'; - -// Export wasm types -export {ConfigProperty, MappingConfig, SymbolMappings}; - -// Export protobuf types -export * from '../gen/diff_pb'; - -// Export display types -export * from './display'; - -interface PromiseCallbacks { - start: number; - resolve: (value: T | PromiseLike) => void; - reject: (reason?: string) => void; -} - -let workerInit = false; -let workerCallbacks: PromiseCallbacks; -const workerReady = new Promise((resolve, reject) => { - workerCallbacks = {start: performance.now(), resolve, reject}; -}); - -export async function initialize(data?: { - workerUrl?: string | URL, - wasmUrl?: string | URL, // Relative to worker URL -}): Promise { - if (workerInit) { - return workerReady; - } - workerInit = true; - let {workerUrl, wasmUrl} = data || {}; - if (!workerUrl) { - try { - // Bundlers will convert this into an asset URL - workerUrl = new URL('./worker.js', import.meta.url); - } catch (_) { - workerUrl = 'worker.js'; - } - } - if (!wasmUrl) { - try { - // Bundlers will convert this into an asset URL - wasmUrl = new URL('./objdiff_core_bg.wasm', import.meta.url); - } catch (_) { - wasmUrl = 'objdiff_core_bg.js'; - } - } - const worker = new Worker(workerUrl, { - name: 'objdiff', - type: 'module', - }); - worker.onmessage = onMessage; - worker.onerror = (event) => { - console.error("Worker error", event); - workerCallbacks.reject("Worker failed to initialize, wrong URL?"); - }; - defer({ - type: 'init', - // URL can't be sent directly - wasmUrl: wasmUrl.toString(), - }, worker).then(() => { - workerCallbacks.resolve(worker); - }, (e) => { - workerCallbacks.reject(e); - }); - return workerReady; -} - -let globalMessageId = 0; -const messageCallbacks = new Map>(); - -function onMessage(event: MessageEvent) { - switch (event.data.type) { - case 'result': { - const {result, error, messageId} = event.data; - const callbacks = messageCallbacks.get(messageId); - if (callbacks) { - const end = performance.now(); - console.debug(`Message ${messageId} took ${end - callbacks.start}ms`); - messageCallbacks.delete(messageId); - if (error != null) { - callbacks.reject(error); - } else { - callbacks.resolve(result as never); - } - } else { - console.warn(`Unknown message ID ${messageId}`); - } - break; - } - } -} - -async function defer(message: AnyHandlerData, worker?: Worker): Promise { - worker = worker || await initialize(); - const messageId = globalMessageId++; - const promise = new Promise((resolve, reject) => { - messageCallbacks.set(messageId, {start: performance.now(), resolve, reject}); - }); - worker.postMessage({ - ...message, - messageId - } as InMessage); - return promise; -} - -export async function runDiff( - left: Uint8Array | null | undefined, - right: Uint8Array | null | undefined, - properties?: ConfigProperty[], - mappingConfig?: MappingConfig, -): Promise { - const data = await defer({ - type: 'run_diff_proto', - left, - right, - properties, - mappingConfig, - }); - const parseStart = performance.now(); - const result = DiffResult.fromBinary(data, {readUnknownField: false}); - const end = performance.now(); - console.debug(`Parsing message took ${end - parseStart}ms`); - return result; -} diff --git a/objdiff-wasm/src/worker.ts b/objdiff-wasm/src/worker.ts deleted file mode 100644 index c4c036b..0000000 --- a/objdiff-wasm/src/worker.ts +++ /dev/null @@ -1,93 +0,0 @@ -import wasmInit, * as exports from '../pkg'; - -const handlers = { - init: init, - run_diff_proto: run_diff_proto, -} as const; -type ExtractData = T extends (arg: infer U) => Promise ? U : never; -type HandlerData = { - [K in keyof typeof handlers]: { type: K } & ExtractData; -}; - -let wasmReady: Promise | null = null; - -async function init({wasmUrl}: { wasmUrl?: string }): Promise { - if (wasmReady != null) { - throw new Error('Already initialized'); - } - wasmReady = wasmInit({module_or_path: wasmUrl}) - .then(() => { - }); - return wasmReady; -} - -async function initIfNeeded() { - if (wasmReady == null) { - await init({}); - } - return wasmReady; -} - -async function run_diff_proto({left, right, properties, mappingConfig}: { - left: Uint8Array | null | undefined, - right: Uint8Array | null | undefined, - properties?: exports.ConfigProperty[], - mappingConfig?: exports.MappingConfig, -}): Promise { - const diffConfig = exports.config_from_properties(properties || []); - const leftObj = left ? exports.parse_object(left, diffConfig) : null; - const rightObj = right ? exports.parse_object(right, diffConfig) : null; - return exports.run_diff(leftObj, rightObj, diffConfig, mappingConfig || {}); -} - -export type AnyHandlerData = HandlerData[keyof HandlerData]; -export type InMessage = AnyHandlerData & { messageId: number }; - -export type OutMessage = { - type: 'result', - result: unknown | null, - error: string | null, - messageId: number, -}; - -self.onmessage = (event: MessageEvent) => { - const data = event.data; - const messageId = data?.messageId; - (async () => { - if (!data) { - throw new Error('No data'); - } - const handler = handlers[data.type]; - if (handler) { - if (data.type !== 'init') { - await initIfNeeded(); - } - const start = performance.now(); - const result = await handler(data as never); - const end = performance.now(); - console.debug(`Worker message ${data.messageId} took ${end - start}ms`); - let transfer: Transferable[] = []; - if (result instanceof Uint8Array) { - console.log("Transferring!", result.byteLength); - transfer = [result.buffer]; - } else { - console.log("Didn't transfer", typeof result); - } - self.postMessage({ - type: 'result', - result: result, - error: null, - messageId, - } as OutMessage, {transfer}); - } else { - throw new Error(`No handler for ${data.type}`); - } - })().catch(error => { - self.postMessage({ - type: 'result', - result: null, - error: error.toString(), - messageId, - } as OutMessage); - }); -}; diff --git a/objdiff-wasm/tsconfig.json b/objdiff-wasm/tsconfig.json index 6f36d92..65c66e9 100644 --- a/objdiff-wasm/tsconfig.json +++ b/objdiff-wasm/tsconfig.json @@ -1,9 +1,17 @@ { "compilerOptions": { - "esModuleInterop": true, - "module": "ES2022", - "moduleResolution": "Node", - "strict": true, + "lib": ["DOM", "ES2023"], "target": "ES2022", - } + "noEmit": true, + "skipLibCheck": true, + "useDefineForClassFields": true, + "module": "ESNext", + "isolatedModules": true, + "resolveJsonModule": true, + "moduleResolution": "Bundler", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "include": ["lib"] } diff --git a/objdiff-wasm/tsup.config.ts b/objdiff-wasm/tsup.config.ts deleted file mode 100644 index 7c89361..0000000 --- a/objdiff-wasm/tsup.config.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {defineConfig} from 'tsup'; -import fs from 'node:fs/promises'; - -export default defineConfig([ - // Build main library - { - entry: ['src/main.ts'], - clean: true, - dts: true, - format: 'esm', - outDir: 'dist', - skipNodeModulesBundle: true, - sourcemap: true, - splitting: false, - target: 'es2022', - }, - // Build web worker - { - entry: ['src/worker.ts'], - clean: true, - dts: true, - format: 'esm', // type: 'module' - minify: true, - outDir: 'dist', - sourcemap: true, - splitting: false, - target: 'es2022', - // https://github.com/egoist/tsup/issues/278 - async onSuccess() { - await fs.copyFile('pkg/objdiff_core_bg.wasm', 'dist/objdiff_core_bg.wasm'); - await fs.copyFile('../objdiff-core/config-schema.json', 'dist/config-schema.json'); - } - } -]); diff --git a/objdiff-wasm/wit/.gitignore b/objdiff-wasm/wit/.gitignore new file mode 100644 index 0000000..6912fef --- /dev/null +++ b/objdiff-wasm/wit/.gitignore @@ -0,0 +1 @@ +deps/ diff --git a/objdiff-wasm/wit/deps.lock b/objdiff-wasm/wit/deps.lock new file mode 100644 index 0000000..84ca3cb --- /dev/null +++ b/objdiff-wasm/wit/deps.lock @@ -0,0 +1,4 @@ +[logging] +url = "https://github.com/WebAssembly/wasi-logging/archive/d31c41d0d9eed81aabe02333d0025d42acf3fb75.tar.gz" +sha256 = "ad81d8b7f7a8ceb729cf551f1d24586f0de9560a43eea57a9bb031d2175804e1" +sha512 = "1687ad9a02ab3e689443e67d1a0605f58fc5dea828d2e4d2c7825c6002714fac9bd4289b1a68b61a37dcca6c3b421f4c8ed4b1e6cc29f6460e0913cf1bf11c04" diff --git a/objdiff-wasm/wit/deps.toml b/objdiff-wasm/wit/deps.toml new file mode 100644 index 0000000..ec4bdc3 --- /dev/null +++ b/objdiff-wasm/wit/deps.toml @@ -0,0 +1 @@ +logging = "https://github.com/WebAssembly/wasi-logging/archive/d31c41d0d9eed81aabe02333d0025d42acf3fb75.tar.gz" diff --git a/objdiff-wasm/wit/objdiff.wit b/objdiff-wasm/wit/objdiff.wit new file mode 100644 index 0000000..38f9adc --- /dev/null +++ b/objdiff-wasm/wit/objdiff.wit @@ -0,0 +1,249 @@ +package objdiff:core; + +use wasi:logging/logging@0.1.0-draft; + +interface diff-types { + resource diff-config { + constructor(); + set-property: func(id: string, value: string) -> result<_, string>; + get-property: func(id: string) -> result; + } + + record mapping-config { + mappings: list>, + selecting-left: option, + selecting-right: option, + } + + resource object { + parse: static func( + data: list, + config: borrow, + ) -> result; + } + + resource object-diff { + find-symbol: func( + name: string, + section-name: option + ) -> option; + } + + record diff-result { + left: option, + right: option, + } +} + +interface display-types { + type symbol-ref = u32; + + record display-config { + show-hidden-symbols: bool, + show-mapped-symbols: bool, + reverse-fn-order: bool, + } + + record symbol-filter { + regex: option, + mapping: option, + } + + record section-display-symbol { + symbol: symbol-ref, + is-mapping-symbol: bool, + } + + record section-display { + id: string, + name: string, + size: u64, + match-percent: option, + symbols: list, + } + + enum symbol-kind { + unknown, + function, + object, + section, + } + + flags symbol-flags { + global, + local, + weak, + common, + hidden, + has-extra, + size-inferred, + } + + record symbol-display { + name: string, + demangled-name: option, + address: u64, + size: u64, + kind: symbol-kind, + section: option, + %flags: symbol-flags, + align: option, + virtual-address: option, + + target-symbol: option, + match-percent: option, + diff-score: option>, + row-count: u32, + } + + record context-menu-item-copy { + value: string, + label: option, + } + + record context-menu-item-navigate { + label: string, + } + + variant context-menu-item { + copy(context-menu-item-copy), + navigate(context-menu-item-navigate), + } + + enum hover-item-color { + normal, + emphasized, + special, + } + + record hover-item { + text: string, + color: hover-item-color, + } + + record diff-text-opcode { + mnemonic: string, + opcode: u16, + } + + record diff-text-symbol { + name: string, + demangled-name: option, + } + + variant diff-text { + // Basic text (not semantically meaningful) + basic(string), + // Line number + line(u32), + // Instruction address + address(u64), + // Instruction mnemonic + opcode(diff-text-opcode), + // Instruction argument (signed) + signed(s64), + // Instruction argument (unsigned) + unsigned(u64), + // Instruction argument (opaque) + opaque(string), + // Instruction argument (branch destination) + branch-dest(u64), + // Relocation target name + symbol(diff-text-symbol), + // Relocation addend + addend(s64), + // Number of spaces + spacing(u32), + // End of line + eol, + } + + record diff-text-segment { + // Text to display + text: diff-text, + // Index for colorization + diff-index: option, + } + + record instruction-diff-row { + // Text segments + segments: list, + // Diff kind + diff-kind: instruction-diff-kind, + } + + enum instruction-diff-kind { + none, + op-mismatch, + arg-mismatch, + replace, + insert, + delete, + } +} + +interface diff { + use diff-types.{ + object, + object-diff, + diff-config, + diff-result + }; + use display-types.{ + section-display-symbol, + section-display, + symbol-ref, + symbol-filter, + symbol-display, + context-menu-item, + hover-item, + display-config, + instruction-diff-row + }; + + run-diff: func( + left: option>, + right: option>, + config: borrow, + ) -> result; + + display-sections: func( + diff: borrow, + filter: symbol-filter, + config: display-config, + ) -> list; + + display-symbol: func( + diff: borrow, + symbol: section-display-symbol, + ) -> symbol-display; + + symbol-context: func( + object: borrow, + symbol: symbol-ref, + ) -> list; + + symbol-hover: func( + object: borrow, + symbol: symbol-ref, + ) -> list; + + display-instruction-row: func( + diff: borrow, + symbol: section-display-symbol, + row-index: u32, + config: borrow, + ) -> instruction-diff-row; +} + +world api { + import logging; + use logging.{level}; + + export diff; + export diff-types; + export display-types; + + export init: func(level: level); + export version: func() -> string; +}