Initial commit
This commit is contained in:
commit
636cbea59c
|
@ -0,0 +1,5 @@
|
|||
[target.aarch64-unknown-linux-gnu]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
|
||||
[target.armv7-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
|
@ -0,0 +1,132 @@
|
|||
name: Build
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
env:
|
||||
CARGO_BIN_NAME: dtk
|
||||
CARGO_TARGET_DIR: target
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTFLAGS: -D warnings
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
- name: Cargo check
|
||||
run: cargo check --all-features
|
||||
- name: Cargo clippy
|
||||
run: cargo clippy --all-features
|
||||
|
||||
deny:
|
||||
name: Deny
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
checks:
|
||||
- advisories
|
||||
- bans licenses sources
|
||||
# Prevent new advisories from failing CI
|
||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
command: check ${{ matrix.checks }}
|
||||
|
||||
test:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Cargo test
|
||||
run: cargo test --release --all-features
|
||||
|
||||
build:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
name: linux-x86_64
|
||||
- platform: ubuntu-latest
|
||||
target: aarch64-unknown-linux-gnu
|
||||
name: linux-aarch64
|
||||
packages: gcc-aarch64-linux-gnu
|
||||
- platform: ubuntu-latest
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
name: linux-armv7l
|
||||
packages: gcc-arm-linux-gnueabihf
|
||||
- platform: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
name: windows-x86_64
|
||||
- platform: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
name: windows-arm64
|
||||
- platform: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
name: macos-x86_64
|
||||
- platform: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
name: macos-arm64
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
if: matrix.packages != ''
|
||||
run: sudo apt-get -y install ${{ matrix.packages }}
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Cargo build
|
||||
run: cargo build --release --all-features --target ${{ matrix.target }} --bin ${{ env.CARGO_BIN_NAME }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.name }}
|
||||
path: |
|
||||
${{ env.CARGO_TARGET_DIR }}/release/${{ env.CARGO_BIN_NAME }}
|
||||
${{ env.CARGO_TARGET_DIR }}/release/${{ env.CARGO_BIN_NAME }}.exe
|
||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release/${{ env.CARGO_BIN_NAME }}
|
||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release/${{ env.CARGO_BIN_NAME }}.exe
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ build ]
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Rename artifacts
|
||||
working-directory: artifacts
|
||||
run: |
|
||||
mkdir ../out
|
||||
for i in */*/release/$CARGO_BIN_NAME*; do
|
||||
mv "$i" "../out/$(sed -E "s/([^/]+)\/[^/]+\/release\/($CARGO_BIN_NAME)/\2-\1/" <<< "$i")"
|
||||
done
|
||||
ls -R ../out
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: out/*
|
|
@ -0,0 +1,2 @@
|
|||
/build
|
||||
/.idea
|
|
@ -0,0 +1,659 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
|
||||
[[package]]
|
||||
name = "argh"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c375edecfd2074d5edcc31396860b6e54b6f928714d0e097b983053fac0cabe3"
|
||||
dependencies = [
|
||||
"argh_derive",
|
||||
"argh_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argh_derive"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa013479b80109a1bf01a039412b0f0013d716f36921226d86c6709032fb7a03"
|
||||
dependencies = [
|
||||
"argh_shared",
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argh_shared"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "149f75bbec1827618262e0855a68f0f9a7f2edc13faebf33c4f16d6725edb6a9"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[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 = "cwdemangle"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e411efa4ed072fa5bdb637c945ea7f618ebd416748cecc255b00968c1db81e68"
|
||||
dependencies = [
|
||||
"argh",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decomp-toolkit"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argh",
|
||||
"base16ct",
|
||||
"cwdemangle",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"memchr",
|
||||
"memmap2",
|
||||
"multimap",
|
||||
"object",
|
||||
"pretty_env_logger",
|
||||
"regex",
|
||||
"sha-1",
|
||||
"topological-sort",
|
||||
"vergen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401"
|
||||
dependencies = [
|
||||
"enum-iterator-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator-derive"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getset"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.13.4+1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "7.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ba753d713ec3844652ad2cb7eb56bc71e34213a14faddac7852a10ba88f61e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"enum-iterator",
|
||||
"getset",
|
||||
"git2",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -0,0 +1,38 @@
|
|||
[package]
|
||||
name = "decomp-toolkit"
|
||||
description = "GameCube/Wii decompilation project tools."
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
build = "build.rs"
|
||||
repository = "https://github.com/encounter/decomp-toolkit"
|
||||
readme = "README.md"
|
||||
categories = ["command-line-utilities"]
|
||||
|
||||
[[bin]]
|
||||
name = "dtk"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.64"
|
||||
argh = "0.1.8"
|
||||
base16ct = "0.1.1"
|
||||
cwdemangle = "0.1.3"
|
||||
hex = "0.4.3"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.17"
|
||||
memchr = "2.5.0"
|
||||
memmap2 = "0.5.7"
|
||||
multimap = "0.8.3"
|
||||
object = { version = "0.30.0", features = ["read_core", "std", "elf"], default-features = false }
|
||||
pretty_env_logger = "0.4.0"
|
||||
regex = "1.6.0"
|
||||
sha-1 = "0.10.0"
|
||||
topological-sort = "0.2.2"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.64"
|
||||
vergen = { version = "7.4.2", features = ["build", "git"], default-features = false }
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2021 Luke Street.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright 2021 Luke Street.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,58 @@
|
|||
# decomp-toolkit [![Build Status]][actions]
|
||||
|
||||
[Build Status]: https://github.com/encounter/decomp-toolkit/actions/workflows/build.yaml/badge.svg
|
||||
[actions]: https://github.com/encounter/decomp-toolkit/actions
|
||||
|
||||
GameCube/Wii decompilation project tools.
|
||||
|
||||
This provides various commands that assist with creating a build system that works
|
||||
across all major platforms without dealing with platform-specific C compilers,
|
||||
UNIX compatibility layers like msys2, or other idiosyncrasies.
|
||||
|
||||
## Commands
|
||||
|
||||
### demangle
|
||||
|
||||
Demangles CodeWarrior C++ symbols. A thin wrapper for [cwdemangle](https://github.com/encounter/cwdemangle).
|
||||
|
||||
```shell
|
||||
$ dtk demangle 'BuildLight__9CGuiLightCFv'
|
||||
CGuiLight::BuildLight() const
|
||||
```
|
||||
|
||||
### elf2dol
|
||||
|
||||
Creates a DOL file from the provided ELF file.
|
||||
|
||||
```shell
|
||||
$ dtk elf2dol input.elf output.dol
|
||||
```
|
||||
|
||||
### map
|
||||
|
||||
Processes CodeWarrior map files and provides information about symbols and TUs.
|
||||
|
||||
```shell
|
||||
$ dtk map entries Game.MAP 'Unit.o'
|
||||
# Outputs all symbols that are referenced by Unit.o
|
||||
# This is useful for finding deduplicated weak functions,
|
||||
# which only show on first use in the link map.
|
||||
|
||||
$ dtk map symbol Game.MAP 'Function__5ClassFv'
|
||||
# Outputs reference information for Function__5ClassFv
|
||||
# CodeWarrior link maps can get very deeply nested,
|
||||
# so this is useful for emitting direct references
|
||||
# in a readable format.
|
||||
```
|
||||
|
||||
### shasum
|
||||
|
||||
Calculate and verify SHA-1 hashes.
|
||||
|
||||
```shell
|
||||
$ dtk shasum baserom.dol
|
||||
949c5ed7368aef547e0b0db1c3678f466e2afbff baserom.dol
|
||||
|
||||
$ dtk shasum -c baserom.sha1
|
||||
baserom.dol: OK
|
||||
```
|
|
@ -0,0 +1,4 @@
|
|||
use anyhow::Result;
|
||||
use vergen::{vergen, Config};
|
||||
|
||||
fn main() -> Result<()> { vergen(Config::default()) }
|
|
@ -0,0 +1,205 @@
|
|||
# This template contains all of the possible sections and their default values
|
||||
|
||||
# Note that all fields that take a lint level have these possible values:
|
||||
# * deny - An error will be produced and the check will fail
|
||||
# * warn - A warning will be produced, but the check will not fail
|
||||
# * allow - No warning or error will be produced, though in some cases a note
|
||||
# will be
|
||||
|
||||
# The values provided in this template are the default values that will be used
|
||||
# when any section or field is not specified in your own configuration
|
||||
|
||||
# If 1 or more target triples (and optionally, target_features) are specified,
|
||||
# only the specified targets will be checked when running `cargo deny check`.
|
||||
# This means, if a particular package is only ever used as a target specific
|
||||
# dependency, such as, for example, the `nix` crate only being used via the
|
||||
# `target_family = "unix"` configuration, that only having windows targets in
|
||||
# this list would mean the nix crate, as well as any of its exclusive
|
||||
# dependencies not shared by any other crates, would be ignored, as the target
|
||||
# list here is effectively saying which targets you are building for.
|
||||
targets = [
|
||||
# The triple can be any string, but only the target triples built in to
|
||||
# rustc (as of 1.40) can be checked against actual config expressions
|
||||
#{ triple = "x86_64-unknown-linux-musl" },
|
||||
# You can also specify which target_features you promise are enabled for a
|
||||
# particular target. target_features are currently not validated against
|
||||
# the actual valid features supported by the target architecture.
|
||||
#{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
|
||||
]
|
||||
|
||||
# This section is considered when running `cargo deny check advisories`
|
||||
# More documentation for the advisories section can be found here:
|
||||
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
|
||||
[advisories]
|
||||
# The path where the advisory database is cloned/fetched into
|
||||
db-path = "~/.cargo/advisory-db"
|
||||
# The url(s) of the advisory databases to use
|
||||
db-urls = ["https://github.com/rustsec/advisory-db"]
|
||||
# The lint level for security vulnerabilities
|
||||
vulnerability = "deny"
|
||||
# The lint level for unmaintained crates
|
||||
unmaintained = "warn"
|
||||
# The lint level for crates that have been yanked from their source registry
|
||||
yanked = "warn"
|
||||
# The lint level for crates with security notices. Note that as of
|
||||
# 2019-12-17 there are no security notice advisories in
|
||||
# https://github.com/rustsec/advisory-db
|
||||
notice = "warn"
|
||||
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
||||
# output a note when they are encountered.
|
||||
ignore = [
|
||||
#"RUSTSEC-0000-0000",
|
||||
]
|
||||
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
|
||||
# lower than the range specified will be ignored. Note that ignored advisories
|
||||
# will still output a note when they are encountered.
|
||||
# * None - CVSS Score 0.0
|
||||
# * Low - CVSS Score 0.1 - 3.9
|
||||
# * Medium - CVSS Score 4.0 - 6.9
|
||||
# * High - CVSS Score 7.0 - 8.9
|
||||
# * Critical - CVSS Score 9.0 - 10.0
|
||||
#severity-threshold =
|
||||
|
||||
# This section is considered when running `cargo deny check licenses`
|
||||
# More documentation for the licenses section can be found here:
|
||||
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
|
||||
[licenses]
|
||||
# The lint level for crates which do not have a detectable license
|
||||
unlicensed = "deny"
|
||||
# List of explictly allowed licenses
|
||||
# See https://spdx.org/licenses/ for list of possible licenses
|
||||
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
|
||||
allow = [
|
||||
"MIT",
|
||||
"Apache-2.0",
|
||||
"BSD-3-Clause",
|
||||
"Unicode-DFS-2016",
|
||||
"0BSD",
|
||||
]
|
||||
# List of explictly disallowed licenses
|
||||
# See https://spdx.org/licenses/ for list of possible licenses
|
||||
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
|
||||
deny = [
|
||||
#"Nokia",
|
||||
]
|
||||
# Lint level for licenses considered copyleft
|
||||
copyleft = "warn"
|
||||
# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
|
||||
# * both - The license will be approved if it is both OSI-approved *AND* FSF
|
||||
# * either - The license will be approved if it is either OSI-approved *OR* FSF
|
||||
# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
|
||||
# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
|
||||
# * neither - This predicate is ignored and the default lint level is used
|
||||
allow-osi-fsf-free = "neither"
|
||||
# Lint level used when no other predicates are matched
|
||||
# 1. License isn't in the allow or deny lists
|
||||
# 2. License isn't copyleft
|
||||
# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
|
||||
default = "deny"
|
||||
# The confidence threshold for detecting a license from license text.
|
||||
# The higher the value, the more closely the license text must be to the
|
||||
# canonical license text of a valid SPDX license file.
|
||||
# [possible values: any between 0.0 and 1.0].
|
||||
confidence-threshold = 0.8
|
||||
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
|
||||
# aren't accepted for every possible crate as with the normal allow list
|
||||
exceptions = [
|
||||
# Each entry is the crate and version constraint, and its specific allow
|
||||
# list
|
||||
#{ allow = ["Zlib"], name = "adler32", version = "*" },
|
||||
]
|
||||
|
||||
# Some crates don't have (easily) machine readable licensing information,
|
||||
# adding a clarification entry for it allows you to manually specify the
|
||||
# licensing information
|
||||
[[licenses.clarify]]
|
||||
# The name of the crate the clarification applies to
|
||||
name = "encoding_rs"
|
||||
# The optional version constraint for the crate
|
||||
#version = "*"
|
||||
# The SPDX expression for the license requirements of the crate
|
||||
expression = "(Apache-2.0 OR MIT) AND BSD-3-Clause"
|
||||
# One or more files in the crate's source used as the "source of truth" for
|
||||
# the license expression. If the contents match, the clarification will be used
|
||||
# when running the license check, otherwise the clarification will be ignored
|
||||
# and the crate will be checked normally, which may produce warnings or errors
|
||||
# depending on the rest of your configuration
|
||||
license-files = [
|
||||
# Each entry is a crate relative path, and the (opaque) hash of its contents
|
||||
{ path = "COPYRIGHT", hash = 0x39f8ad31 }
|
||||
]
|
||||
|
||||
[licenses.private]
|
||||
# If true, ignores workspace crates that aren't published, or are only
|
||||
# published to private registries
|
||||
ignore = false
|
||||
# One or more private registries that you might publish crates to, if a crate
|
||||
# is only published to private registries, and ignore is true, the crate will
|
||||
# not have its license(s) checked
|
||||
registries = [
|
||||
#"https://sekretz.com/registry
|
||||
]
|
||||
|
||||
# This section is considered when running `cargo deny check bans`.
|
||||
# More documentation about the 'bans' section can be found here:
|
||||
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
|
||||
[bans]
|
||||
# Lint level for when multiple versions of the same crate are detected
|
||||
multiple-versions = "warn"
|
||||
# Lint level for when a crate version requirement is `*`
|
||||
wildcards = "allow"
|
||||
# The graph highlighting used when creating dotgraphs for crates
|
||||
# with multiple versions
|
||||
# * lowest-version - The path to the lowest versioned duplicate is highlighted
|
||||
# * simplest-path - The path to the version with the fewest edges is highlighted
|
||||
# * all - Both lowest-version and simplest-path are used
|
||||
highlight = "all"
|
||||
# List of crates that are allowed. Use with care!
|
||||
allow = [
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
]
|
||||
# List of crates to deny
|
||||
deny = [
|
||||
# Each entry the name of a crate and a version range. If version is
|
||||
# not specified, all versions will be matched.
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
#
|
||||
# Wrapper crates can optionally be specified to allow the crate when it
|
||||
# is a direct dependency of the otherwise banned crate
|
||||
#{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
|
||||
]
|
||||
# Certain crates/versions that will be skipped when doing duplicate detection.
|
||||
skip = [
|
||||
#{ name = "ansi_term", version = "=0.11.0" },
|
||||
]
|
||||
# Similarly to `skip` allows you to skip certain crates during duplicate
|
||||
# detection. Unlike skip, it also includes the entire tree of transitive
|
||||
# dependencies starting at the specified crate, up to a certain depth, which is
|
||||
# by default infinite
|
||||
skip-tree = [
|
||||
#{ name = "ansi_term", version = "=0.11.0", depth = 20 },
|
||||
]
|
||||
|
||||
# This section is considered when running `cargo deny check sources`.
|
||||
# More documentation about the 'sources' section can be found here:
|
||||
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
|
||||
[sources]
|
||||
# Lint level for what to happen when a crate from a crate registry that is not
|
||||
# in the allow list is encountered
|
||||
unknown-registry = "warn"
|
||||
# Lint level for what to happen when a crate from a git repository that is not
|
||||
# in the allow list is encountered
|
||||
unknown-git = "warn"
|
||||
# List of URLs for allowed crate registries. Defaults to the crates.io index
|
||||
# if not specified. If it is specified but empty, no registries are allowed.
|
||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||
# List of URLs for allowed Git repositories
|
||||
allow-git = []
|
||||
|
||||
[sources.allow-org]
|
||||
# 1 or more github.com organizations to allow git sources for
|
||||
#github = [""]
|
||||
# 1 or more gitlab.com organizations to allow git sources for
|
||||
#gitlab = [""]
|
||||
# 1 or more bitbucket.org organizations to allow git sources for
|
||||
#bitbucket = [""]
|
|
@ -0,0 +1,8 @@
|
|||
fn_single_line = true
|
||||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Crate"
|
||||
overflow_delimited_expr = true
|
||||
reorder_impl_items = true
|
||||
use_field_init_shorthand = true
|
||||
use_small_heuristics = "Max"
|
||||
where_single_line = true
|
|
@ -0,0 +1,65 @@
|
|||
// From https://gist.github.com/suluke/e0c672492126be0a4f3b4f0e1115d77c
|
||||
//! Extend `argh` to be better integrated with the `cargo` ecosystem
|
||||
//!
|
||||
//! For now, this only adds a --version/-V option which causes early-exit.
|
||||
use argh::{FromArgs, TopLevelCommand};
|
||||
|
||||
struct ArgsOrVersion<T: FromArgs>(T);
|
||||
impl<T> TopLevelCommand for ArgsOrVersion<T> where T: FromArgs {}
|
||||
impl<T> FromArgs for ArgsOrVersion<T>
|
||||
where T: FromArgs
|
||||
{
|
||||
fn from_args(command_name: &[&str], args: &[&str]) -> Result<Self, argh::EarlyExit> {
|
||||
/// Also use argh for catching `--version`-only invocations
|
||||
#[derive(FromArgs)]
|
||||
struct Version {
|
||||
/// print version information and exit
|
||||
#[argh(switch, short = 'V')]
|
||||
pub version: bool,
|
||||
}
|
||||
match Version::from_args(command_name, args) {
|
||||
Ok(v) => {
|
||||
if v.version {
|
||||
Err(argh::EarlyExit {
|
||||
output: format!(
|
||||
"{} {} {}",
|
||||
command_name.first().unwrap_or(&""),
|
||||
env!("VERGEN_BUILD_SEMVER"),
|
||||
env!("VERGEN_GIT_SHA"),
|
||||
),
|
||||
status: Ok(()),
|
||||
})
|
||||
} else {
|
||||
// seems args are empty
|
||||
T::from_args(command_name, args).map(Self)
|
||||
}
|
||||
}
|
||||
Err(exit) => match exit.status {
|
||||
Ok(()) => {
|
||||
// must have been --help
|
||||
let help = match T::from_args(command_name, &["--help"]) {
|
||||
Ok(_) => unreachable!(),
|
||||
Err(exit) => exit.output,
|
||||
};
|
||||
Err(argh::EarlyExit {
|
||||
output: format!(
|
||||
"{} -V, --version print version information and exit",
|
||||
help
|
||||
),
|
||||
status: Ok(()),
|
||||
})
|
||||
}
|
||||
Err(()) => T::from_args(command_name, args).map(Self),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `FromArgs` type from the current process’s `env::args`.
|
||||
///
|
||||
/// This function will exit early from the current process if argument parsing was unsuccessful or if information like `--help` was requested.
|
||||
/// Error messages will be printed to stderr, and `--help` output to stdout.
|
||||
pub fn from_env<T>() -> T
|
||||
where T: TopLevelCommand {
|
||||
argh::from_env::<ArgsOrVersion<T>>().0
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
use anyhow::{Error, Result};
|
||||
use argh::FromArgs;
|
||||
use cwdemangle::{demangle, DemangleOptions};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Demangle a CodeWarrior C++ symbol.
|
||||
#[argh(subcommand, name = "demangle")]
|
||||
pub struct Args {
|
||||
#[argh(positional)]
|
||||
/// symbol to demangle
|
||||
symbol: String,
|
||||
#[argh(switch)]
|
||||
/// disable replacing `(void)` with `()`
|
||||
keep_void: bool,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
let options = DemangleOptions { omit_empty_parameters: !args.keep_void };
|
||||
match demangle(args.symbol.as_str(), &options) {
|
||||
Some(symbol) => {
|
||||
println!("{}", symbol);
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::msg("Failed to demangle symbol")),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::{BufWriter, Seek, SeekFrom, Write},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Error, Result};
|
||||
use argh::FromArgs;
|
||||
use memmap2::MmapOptions;
|
||||
use object::{Architecture, Object, ObjectKind, ObjectSection, SectionKind};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Converts an ELF file to a DOL file.
|
||||
#[argh(subcommand, name = "elf2dol")]
|
||||
pub struct Args {
|
||||
#[argh(positional)]
|
||||
/// path to input ELF
|
||||
elf_file: String,
|
||||
#[argh(positional)]
|
||||
/// path to output DOL
|
||||
dol_file: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DolSection {
|
||||
pub offset: u32,
|
||||
pub address: u32,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DolHeader {
|
||||
pub text_sections: Vec<DolSection>,
|
||||
pub data_sections: Vec<DolSection>,
|
||||
pub bss_address: u32,
|
||||
pub bss_size: u32,
|
||||
pub entry_point: u32,
|
||||
}
|
||||
|
||||
const MAX_TEXT_SECTIONS: usize = 7;
|
||||
const MAX_DATA_SECTIONS: usize = 11;
|
||||
const ZERO_BUF: [u8; 32] = [0u8; 32];
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
let elf_file = File::open(&args.elf_file)
|
||||
.with_context(|| format!("Failed to open ELF file '{}'", args.elf_file))?;
|
||||
let map = unsafe { MmapOptions::new().map(&elf_file) }
|
||||
.with_context(|| format!("Failed to mmap binary: '{}'", args.elf_file))?;
|
||||
let obj_file = object::read::File::parse(&*map)?;
|
||||
match obj_file.architecture() {
|
||||
Architecture::PowerPc => {}
|
||||
arch => return Err(Error::msg(format!("Unexpected architecture: {:?}", arch))),
|
||||
};
|
||||
if obj_file.is_little_endian() {
|
||||
return Err(Error::msg("Expected big endian"));
|
||||
}
|
||||
match obj_file.kind() {
|
||||
ObjectKind::Executable => {}
|
||||
kind => return Err(Error::msg(format!("Unexpected ELF type: {:?}", kind))),
|
||||
}
|
||||
|
||||
let mut out = BufWriter::new(
|
||||
File::create(&args.dol_file)
|
||||
.with_context(|| format!("Failed to create DOL file '{}'", args.dol_file))?,
|
||||
);
|
||||
let mut header = DolHeader { entry_point: obj_file.entry() as u32, ..Default::default() };
|
||||
let mut offset = 0x100u32;
|
||||
|
||||
// Text sections
|
||||
for section in obj_file.sections() {
|
||||
if section.kind() != SectionKind::Text {
|
||||
continue;
|
||||
}
|
||||
let address = section.address() as u32;
|
||||
let size = align32(section.size() as u32);
|
||||
header.text_sections.push(DolSection { offset, address, size });
|
||||
out.seek(SeekFrom::Start(offset as u64))?;
|
||||
write_aligned(&mut out, section.data()?)?;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
// Data sections
|
||||
for section in obj_file.sections() {
|
||||
if section.kind() != SectionKind::Data && section.kind() != SectionKind::ReadOnlyData {
|
||||
continue;
|
||||
}
|
||||
let address = section.address() as u32;
|
||||
let size = align32(section.size() as u32);
|
||||
header.data_sections.push(DolSection { offset, address, size });
|
||||
out.seek(SeekFrom::Start(offset as u64))?;
|
||||
write_aligned(&mut out, section.data()?)?;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
// BSS sections
|
||||
for section in obj_file.sections() {
|
||||
if section.kind() != SectionKind::UninitializedData {
|
||||
continue;
|
||||
}
|
||||
let address = section.address() as u32;
|
||||
let size = section.size() as u32;
|
||||
if header.bss_address == 0 {
|
||||
header.bss_address = address;
|
||||
}
|
||||
header.bss_size = (address + size) - header.bss_address;
|
||||
}
|
||||
|
||||
if header.text_sections.len() > MAX_TEXT_SECTIONS {
|
||||
return Err(Error::msg(format!(
|
||||
"Too many text sections: {} / {}",
|
||||
header.text_sections.len(),
|
||||
MAX_TEXT_SECTIONS
|
||||
)));
|
||||
}
|
||||
if header.data_sections.len() > MAX_DATA_SECTIONS {
|
||||
return Err(Error::msg(format!(
|
||||
"Too many data sections: {} / {}",
|
||||
header.data_sections.len(),
|
||||
MAX_DATA_SECTIONS
|
||||
)));
|
||||
}
|
||||
|
||||
// Offsets
|
||||
out.rewind()?;
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.offset.to_be_bytes())?;
|
||||
}
|
||||
out.seek(SeekFrom::Start(0x1c))?;
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.offset.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// Addresses
|
||||
out.seek(SeekFrom::Start(0x48))?;
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.address.to_be_bytes())?;
|
||||
}
|
||||
out.seek(SeekFrom::Start(0x64))?;
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.address.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// Sizes
|
||||
out.seek(SeekFrom::Start(0x90))?;
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.size.to_be_bytes())?;
|
||||
}
|
||||
out.seek(SeekFrom::Start(0xac))?;
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.size.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// BSS + entry
|
||||
out.seek(SeekFrom::Start(0xd8))?;
|
||||
out.write_all(&header.bss_address.to_be_bytes())?;
|
||||
out.write_all(&header.bss_size.to_be_bytes())?;
|
||||
out.write_all(&header.entry_point.to_be_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn align32(x: u32) -> u32 { (x + 31) & !31 }
|
||||
|
||||
#[inline]
|
||||
fn write_aligned<T: Write>(out: &mut T, bytes: &[u8]) -> std::io::Result<()> {
|
||||
let len = bytes.len() as u32;
|
||||
let padding = align32(len) - len;
|
||||
out.write_all(bytes)?;
|
||||
if padding > 0 {
|
||||
out.write_all(&ZERO_BUF[0..padding as usize])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
use std::{fs::File, io::BufReader};
|
||||
|
||||
use anyhow::{Context, Error, Result};
|
||||
use argh::FromArgs;
|
||||
|
||||
use crate::util::map::{process_map, SymbolEntry, SymbolRef};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Commands for processing CodeWarrior maps.
|
||||
#[argh(subcommand, name = "map")]
|
||||
pub struct Args {
|
||||
#[argh(subcommand)]
|
||||
command: SubCommand,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum SubCommand {
|
||||
Entries(EntriesArgs),
|
||||
Symbol(SymbolArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Displays all entries for a particular TU.
|
||||
#[argh(subcommand, name = "entries")]
|
||||
pub struct EntriesArgs {
|
||||
#[argh(positional)]
|
||||
/// path to input map
|
||||
map_file: String,
|
||||
#[argh(positional)]
|
||||
/// TU to display entries for
|
||||
unit: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Displays all references to a symbol.
|
||||
#[argh(subcommand, name = "symbol")]
|
||||
pub struct SymbolArgs {
|
||||
#[argh(positional)]
|
||||
/// path to input map
|
||||
map_file: String,
|
||||
#[argh(positional)]
|
||||
/// symbol to display references for
|
||||
symbol: String,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Entries(c_args) => entries(c_args),
|
||||
SubCommand::Symbol(c_args) => symbol(c_args),
|
||||
}
|
||||
}
|
||||
|
||||
fn entries(args: EntriesArgs) -> Result<()> {
|
||||
let reader = BufReader::new(
|
||||
File::open(&args.map_file)
|
||||
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
|
||||
);
|
||||
let entries = process_map(reader)?;
|
||||
match entries.unit_entries.get_vec(&args.unit) {
|
||||
Some(vec) => {
|
||||
for symbol_ref in vec {
|
||||
if symbol_ref.name.starts_with('@') {
|
||||
continue;
|
||||
}
|
||||
if let Some(symbol) = entries.symbols.get(symbol_ref) {
|
||||
println!("{}", symbol.demangled.as_ref().unwrap_or(&symbol.name));
|
||||
} else {
|
||||
println!("Symbol not found: {}", symbol_ref.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(Error::msg(format!(
|
||||
"Failed to find entries for TU '{}' in map",
|
||||
args.unit
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn symbol(args: SymbolArgs) -> Result<()> {
|
||||
let reader = BufReader::new(
|
||||
File::open(&args.map_file)
|
||||
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
|
||||
);
|
||||
let entries = process_map(reader)?;
|
||||
let mut opt_ref: Option<(SymbolRef, SymbolEntry)> = None;
|
||||
for (symbol_ref, entry) in &entries.symbols {
|
||||
if symbol_ref.name == args.symbol {
|
||||
if opt_ref.is_some() {
|
||||
return Err(Error::msg(format!("Symbol '{}' found in multiple TUs", args.symbol)));
|
||||
}
|
||||
opt_ref = Some((symbol_ref.clone(), entry.clone()));
|
||||
}
|
||||
}
|
||||
match opt_ref {
|
||||
Some((symbol_ref, symbol)) => {
|
||||
println!("Located symbol {}", symbol.demangled.as_ref().unwrap_or(&symbol.name));
|
||||
if let Some(vec) = entries.entry_references.get_vec(&symbol_ref) {
|
||||
println!("\nReferences:");
|
||||
for x in vec {
|
||||
if let Some(reference) = entries.symbols.get(x) {
|
||||
println!(
|
||||
">>> {} ({:?},{:?}) [{}]",
|
||||
reference.demangled.as_ref().unwrap_or(&reference.name),
|
||||
reference.kind,
|
||||
reference.visibility,
|
||||
reference.unit
|
||||
);
|
||||
} else {
|
||||
println!(">>> {} (NOT FOUND)", x.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(vec) = entries.entry_referenced_from.get_vec(&symbol_ref) {
|
||||
println!("\nReferenced from:");
|
||||
for x in vec {
|
||||
if let Some(reference) = entries.symbols.get(x) {
|
||||
println!(
|
||||
">>> {} ({:?}, {:?}) [{}]",
|
||||
reference.demangled.as_ref().unwrap_or(&reference.name),
|
||||
reference.kind,
|
||||
reference.visibility,
|
||||
reference.unit
|
||||
);
|
||||
} else {
|
||||
println!(">>> {} (NOT FOUND)", x.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("\n");
|
||||
}
|
||||
None => {
|
||||
return Err(Error::msg(format!("Failed to find symbol '{}' in map", args.symbol)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
use anyhow::{Context, Error, Result};
|
||||
use argh::FromArgs;
|
||||
use memchr::memmem;
|
||||
use memmap2::MmapOptions;
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Sets the MetroidBuildInfo tag value in a given binary.
|
||||
#[argh(subcommand, name = "metroidbuildinfo")]
|
||||
pub struct Args {
|
||||
#[argh(positional)]
|
||||
/// path to source binary
|
||||
binary: String,
|
||||
#[argh(positional)]
|
||||
/// path to build info string
|
||||
build_info: String,
|
||||
}
|
||||
|
||||
const BUILD_STRING_MAX: usize = 35;
|
||||
const BUILD_STRING_TAG: &str = "!#$MetroidBuildInfo!#$";
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
let build_string = std::fs::read_to_string(&args.build_info)
|
||||
.with_context(|| format!("Failed to read build info string from '{}'", args.build_info))?;
|
||||
let build_string_trim = build_string.trim_end();
|
||||
if build_string_trim.as_bytes().len() > BUILD_STRING_MAX {
|
||||
return Err(Error::msg(format!(
|
||||
"Build string '{}' is greater than maximum size of {}",
|
||||
build_string_trim, BUILD_STRING_MAX
|
||||
)));
|
||||
}
|
||||
|
||||
let binary_file = std::fs::File::options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(&args.binary)
|
||||
.with_context(|| format!("Failed to open binary for writing: '{}'", args.binary))?;
|
||||
let mut map = unsafe { MmapOptions::new().map_mut(&binary_file) }
|
||||
.with_context(|| format!("Failed to mmap binary: '{}'", args.binary))?;
|
||||
let start = match memmem::find(&map, BUILD_STRING_TAG.as_bytes()) {
|
||||
Some(idx) => idx + BUILD_STRING_TAG.as_bytes().len(),
|
||||
None => return Err(Error::msg("Failed to find build string tag in binary")),
|
||||
};
|
||||
let end = start + build_string_trim.as_bytes().len();
|
||||
map[start..end].copy_from_slice(build_string_trim.as_bytes());
|
||||
map[end] = 0;
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pub(crate) mod demangle;
|
||||
pub(crate) mod elf2dol;
|
||||
pub(crate) mod map;
|
||||
pub(crate) mod metroidbuildinfo;
|
||||
pub(crate) mod shasum;
|
|
@ -0,0 +1,88 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::{BufRead, BufReader, Read},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Error, Result};
|
||||
use argh::FromArgs;
|
||||
use sha1::{Digest, Sha1};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Print or check SHA1 (160-bit) checksums.
|
||||
#[argh(subcommand, name = "shasum")]
|
||||
pub struct Args {
|
||||
#[argh(switch, short = 'c')]
|
||||
/// check SHA sums against given list
|
||||
check: bool,
|
||||
#[argh(positional)]
|
||||
/// path to file
|
||||
file: String,
|
||||
}
|
||||
|
||||
const DEFAULT_BUF_SIZE: usize = 8192;
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
let file =
|
||||
File::open(&args.file).with_context(|| format!("Failed to open file '{}'", args.file))?;
|
||||
if args.check {
|
||||
check(args, file)
|
||||
} else {
|
||||
hash(args, file)
|
||||
}
|
||||
}
|
||||
|
||||
fn check(_args: Args, file: File) -> Result<()> {
|
||||
let reader = BufReader::new(file);
|
||||
let mut mismatches = 0usize;
|
||||
for line in reader.lines() {
|
||||
let line = match line {
|
||||
Ok(line) => line,
|
||||
Err(e) => return Err(Error::msg(format!("File read failed: {}", e))),
|
||||
};
|
||||
let (hash, file_name) =
|
||||
line.split_once(' ').ok_or_else(|| Error::msg(format!("Invalid line: {}", line)))?;
|
||||
let file_name = match file_name.chars().next() {
|
||||
Some(' ') | Some('*') => &file_name[1..],
|
||||
_ => return Err(Error::msg(format!("Invalid line: {}", line))),
|
||||
};
|
||||
let mut hash_bytes = [0u8; 20];
|
||||
hex::decode_to_slice(hash, &mut hash_bytes)
|
||||
.with_context(|| format!("Invalid line: {}", line))?;
|
||||
|
||||
let file = File::open(file_name)
|
||||
.with_context(|| format!("Failed to open file '{}'", file_name))?;
|
||||
let found_hash = file_sha1(file)?;
|
||||
if hash_bytes == found_hash.as_ref() {
|
||||
println!("{}: OK", file_name);
|
||||
} else {
|
||||
println!("{}: FAILED", file_name);
|
||||
mismatches += 1;
|
||||
}
|
||||
}
|
||||
if mismatches != 0 {
|
||||
eprintln!("WARNING: {} computed checksum did NOT match", mismatches);
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn hash(args: Args, file: File) -> Result<()> {
|
||||
let hash = file_sha1(file)?;
|
||||
let mut hash_buf = [0u8; 40];
|
||||
let hash_str = base16ct::lower::encode_str(&hash, &mut hash_buf)
|
||||
.map_err(|e| Error::msg(format!("Failed to encode hash: {}", e)))?;
|
||||
println!("{} {}", hash_str, args.file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_sha1(mut file: File) -> Result<sha1::digest::Output<Sha1>> {
|
||||
let mut buf = [0u8; DEFAULT_BUF_SIZE];
|
||||
let mut hasher = Sha1::new();
|
||||
Ok(loop {
|
||||
let read = file.read(&mut buf).context("File read failed")?;
|
||||
if read == 0 {
|
||||
break hasher.finalize();
|
||||
}
|
||||
hasher.update(&buf[0..read]);
|
||||
})
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
extern crate core;
|
||||
|
||||
use argh::FromArgs;
|
||||
|
||||
mod argh_version;
|
||||
mod cmd;
|
||||
mod util;
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// GameCube/Wii decompilation project tools.
|
||||
struct TopLevel {
|
||||
#[argh(subcommand)]
|
||||
command: SubCommand,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum SubCommand {
|
||||
Demangle(cmd::demangle::Args),
|
||||
Elf2Dol(cmd::elf2dol::Args),
|
||||
Map(cmd::map::Args),
|
||||
MetroidBuildInfo(cmd::metroidbuildinfo::Args),
|
||||
Shasum(cmd::shasum::Args),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: TopLevel = argh_version::from_env();
|
||||
let result = match args.command {
|
||||
SubCommand::Demangle(c_args) => cmd::demangle::run(c_args),
|
||||
SubCommand::Elf2Dol(c_args) => cmd::elf2dol::run(c_args),
|
||||
SubCommand::Map(c_args) => cmd::map::run(c_args),
|
||||
SubCommand::MetroidBuildInfo(c_args) => cmd::metroidbuildinfo::run(c_args),
|
||||
SubCommand::Shasum(c_args) => cmd::shasum::run(c_args),
|
||||
};
|
||||
if let Err(e) = result {
|
||||
eprintln!("{:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,485 @@
|
|||
use std::{
|
||||
collections::{btree_map::Entry, BTreeMap, HashMap},
|
||||
io::BufRead,
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
use anyhow::{Error, Result};
|
||||
use cwdemangle::{demangle, DemangleOptions};
|
||||
use lazy_static::lazy_static;
|
||||
use multimap::MultiMap;
|
||||
use regex::Regex;
|
||||
use topological_sort::TopologicalSort;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum SymbolKind {
|
||||
Function,
|
||||
Object,
|
||||
Section,
|
||||
NoType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum SymbolVisibility {
|
||||
Global,
|
||||
Local,
|
||||
Weak,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SymbolEntry {
|
||||
pub name: String,
|
||||
pub demangled: Option<String>,
|
||||
pub kind: SymbolKind,
|
||||
pub visibility: SymbolVisibility,
|
||||
pub unit: String,
|
||||
pub address: u32,
|
||||
pub size: u32,
|
||||
pub section: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct SymbolRef {
|
||||
pub name: String,
|
||||
pub unit: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SectionOrder {
|
||||
symbol_order: Vec<SymbolRef>,
|
||||
unit_order: Vec<(String, Vec<String>)>,
|
||||
}
|
||||
|
||||
fn is_code_section(section: &str) -> bool { section == ".text" || section == ".init" }
|
||||
|
||||
/// Iterate over the BTreeMap and generate an ordered list of symbols and TUs by address.
|
||||
fn resolve_section_order(
|
||||
address_to_symbol: &BTreeMap<u32, SymbolRef>,
|
||||
symbol_entries: &mut HashMap<SymbolRef, SymbolEntry>,
|
||||
) -> Result<SectionOrder> {
|
||||
let mut ordering = SectionOrder::default();
|
||||
|
||||
let mut last_unit = String::new();
|
||||
let mut unit_override = String::new();
|
||||
let mut last_section = String::new();
|
||||
let mut section_unit_idx = 0usize;
|
||||
for symbol_ref in address_to_symbol.values() {
|
||||
if let Some(symbol) = symbol_entries.get_mut(symbol_ref) {
|
||||
if last_unit != symbol.unit {
|
||||
unit_override.clear();
|
||||
|
||||
if last_section != symbol.section {
|
||||
ordering.unit_order.push((symbol.section.clone(), vec![]));
|
||||
section_unit_idx = ordering.unit_order.len() - 1;
|
||||
last_section = symbol.section.clone();
|
||||
}
|
||||
let unit_order = &mut ordering.unit_order[section_unit_idx];
|
||||
if unit_order.1.contains(&symbol.unit) {
|
||||
// With -common on, .bss is split into two parts. The TU order repeats
|
||||
// at the end with all globally-deduplicated BSS symbols. Once we detect
|
||||
// a duplicate inside of .bss, we create a new section and start again.
|
||||
// TODO the first entry in .comm *could* be a TU without regular .bss
|
||||
if symbol.section == ".bss" {
|
||||
log::debug!(".comm section detected, duplicate {}", symbol.unit);
|
||||
ordering.unit_order.push((".comm".to_string(), vec![symbol.unit.clone()]));
|
||||
section_unit_idx = ordering.unit_order.len() - 1;
|
||||
} else {
|
||||
// Since the map doesn't contain file paths, it's likely that
|
||||
// a TU name conflict is simply a separate file.
|
||||
// TODO need to resolve and split unit in other sections as well
|
||||
unit_override =
|
||||
format!("{}_{}_{:X}", symbol.unit, symbol.section, symbol.address);
|
||||
log::warn!(
|
||||
"TU order conflict: {} exists multiple times in {}. Renaming to {}.",
|
||||
symbol.unit,
|
||||
symbol.section,
|
||||
unit_override,
|
||||
);
|
||||
unit_order.1.push(unit_override.clone());
|
||||
}
|
||||
} else {
|
||||
unit_order.1.push(symbol.unit.clone());
|
||||
}
|
||||
last_unit = symbol.unit.clone();
|
||||
}
|
||||
// For ASM-generated objects, notype,local symbols in .text
|
||||
// are usually local jump labels, and should be ignored.
|
||||
if is_code_section(&symbol.section)
|
||||
&& symbol.size == 0
|
||||
&& symbol.kind == SymbolKind::NoType
|
||||
&& symbol.visibility == SymbolVisibility::Local
|
||||
{
|
||||
// Being named something other than lbl_* could indicate
|
||||
// that it's actually a local function, but let's just
|
||||
// make the user resolve that if necessary.
|
||||
if !symbol.name.starts_with("lbl_") {
|
||||
log::warn!("Skipping local text symbol {}", symbol.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Guess the symbol type if necessary.
|
||||
if symbol.kind == SymbolKind::NoType {
|
||||
if is_code_section(&symbol.section) {
|
||||
symbol.kind = SymbolKind::Function;
|
||||
} else {
|
||||
symbol.kind = SymbolKind::Object;
|
||||
}
|
||||
}
|
||||
// If we're renaming this TU, replace it in the symbol.
|
||||
if !unit_override.is_empty() {
|
||||
symbol.unit = unit_override.clone();
|
||||
}
|
||||
ordering.symbol_order.push(symbol_ref.clone());
|
||||
} else {
|
||||
return Err(Error::msg(format!("Symbol has address but no entry: {:?}", symbol_ref)));
|
||||
}
|
||||
}
|
||||
|
||||
for iter in ordering.symbol_order.windows(2) {
|
||||
let next_address = symbol_entries.get(&iter[1]).unwrap().address;
|
||||
let symbol = symbol_entries.get_mut(&iter[0]).unwrap();
|
||||
// For ASM-generated objects, we need to guess the symbol size.
|
||||
if symbol.size == 0 {
|
||||
symbol.size = next_address - symbol.address;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ordering)
|
||||
}
|
||||
|
||||
/// The ordering of TUs inside of each section represents a directed edge in a DAG.
|
||||
/// We can use a topological sort to determine a valid global TU order.
|
||||
/// There can be ambiguities, but any solution that satisfies the link order
|
||||
/// constraints is considered valid.
|
||||
// TODO account for library ordering
|
||||
#[allow(dead_code)]
|
||||
pub fn resolve_link_order(section_unit_order: &[(String, Vec<String>)]) -> Result<Vec<String>> {
|
||||
let mut global_unit_order = Vec::<String>::new();
|
||||
let mut t_sort = TopologicalSort::<String>::new();
|
||||
for (section, order) in section_unit_order {
|
||||
let mut order: &[String] = order;
|
||||
if (section == ".ctors" || section == ".dtors") && order.len() > 1 {
|
||||
// __init_cpp_exceptions.o has symbols that get ordered to the beginning of
|
||||
// .ctors and .dtors, so our topological sort would fail if we added them.
|
||||
// Always skip the first TU of .ctors and .dtors.
|
||||
order = &order[1..];
|
||||
}
|
||||
for iter in order.windows(2) {
|
||||
t_sort.add_dependency(iter[0].clone(), iter[1].clone());
|
||||
}
|
||||
}
|
||||
for unit in &mut t_sort {
|
||||
global_unit_order.push(unit);
|
||||
}
|
||||
// An incomplete topological sort indicates that a cyclic dependency was encountered.
|
||||
if !t_sort.is_empty() {
|
||||
return Err(Error::msg("Cyclic dependency encountered!"));
|
||||
}
|
||||
// Sanity check, did we get all TUs in the final order?
|
||||
for (_, order) in section_unit_order {
|
||||
for unit in order {
|
||||
if !global_unit_order.contains(unit) {
|
||||
return Err(Error::msg(format!("Failed to find an order for {}", unit)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(global_unit_order)
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref LINK_MAP_START: Regex = Regex::new("^Link map of (.*)$").unwrap();
|
||||
static ref LINK_MAP_ENTRY: Regex = Regex::new(
|
||||
"^\\s*(?P<depth>\\d+)] (?P<sym>.*) \\((?P<type>.*),(?P<vis>.*)\\) found in (?P<tu>.*)$",
|
||||
)
|
||||
.unwrap();
|
||||
static ref LINK_MAP_ENTRY_GENERATED: Regex =
|
||||
Regex::new("^\\s*(?P<depth>\\d+)] (?P<sym>.*) found as linker generated symbol$").unwrap();
|
||||
static ref LINK_MAP_ENTRY_DUPLICATE: Regex =
|
||||
Regex::new("^\\s*(?P<depth>\\d+)] >>> UNREFERENCED DUPLICATE (?P<sym>.*)$").unwrap();
|
||||
static ref SECTION_LAYOUT_START: Regex = Regex::new("^(?P<section>.*) section layout$").unwrap();
|
||||
static ref SECTION_LAYOUT_SYMBOL: Regex = Regex::new(
|
||||
"^\\s*(?P<rom_addr>[0-9A-Fa-f]+|UNUSED)\\s+(?P<size>[0-9A-Fa-f]+)\\s+(?P<addr>[0-9A-Fa-f]+|\\.{8})\\s+(?P<align>\\d+)?\\s*(?P<sym>.*?)(?:\\s+\\(entry of (?P<entry_of>.*?)\\))?\\s+(?P<tu>.*)$",
|
||||
)
|
||||
.unwrap();
|
||||
static ref SECTION_LAYOUT_HEADER: Regex = Regex::new(
|
||||
"^(\\s*Starting\\s+Virtual\\s*|\\s*address\\s+Size\\s+address\\s*|\\s*-----------------------\\s*)$",
|
||||
)
|
||||
.unwrap();
|
||||
static ref MEMORY_MAP_HEADER: Regex = Regex::new("^\\s*Memory map:\\s*$").unwrap();
|
||||
static ref EXTERN_SYMBOL: Regex = Regex::new("^\\s*>>> SYMBOL NOT FOUND: (.*)$").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MapEntries {
|
||||
pub symbols: HashMap<SymbolRef, SymbolEntry>,
|
||||
pub unit_entries: MultiMap<String, SymbolRef>,
|
||||
pub entry_references: MultiMap<SymbolRef, SymbolRef>,
|
||||
pub entry_referenced_from: MultiMap<SymbolRef, SymbolRef>,
|
||||
pub address_to_symbol: BTreeMap<u32, SymbolRef>,
|
||||
pub unit_section_ranges: HashMap<String, Range<u32>>,
|
||||
pub symbol_order: Vec<SymbolRef>,
|
||||
pub unit_order: Vec<(String, Vec<String>)>,
|
||||
}
|
||||
|
||||
pub fn process_map<R: BufRead>(reader: R) -> Result<MapEntries> {
|
||||
let mut entries = MapEntries::default();
|
||||
|
||||
let mut symbol_stack = Vec::<SymbolRef>::new();
|
||||
let mut current_section = String::new();
|
||||
let mut last_name = String::new();
|
||||
let mut last_unit = String::new();
|
||||
let mut has_link_map = false;
|
||||
let mut relative_offset = 0u32;
|
||||
let mut last_section_end = 0u32;
|
||||
for result in reader.lines() {
|
||||
match result {
|
||||
Ok(line) => {
|
||||
if let Some(captures) = LINK_MAP_START.captures(&line) {
|
||||
log::debug!("Entry point: {}", &captures[1]);
|
||||
has_link_map = true;
|
||||
} else if let Some(captures) = LINK_MAP_ENTRY.captures(&line) {
|
||||
if captures["sym"].starts_with('.') {
|
||||
last_name.clear();
|
||||
continue;
|
||||
}
|
||||
let is_duplicate = &captures["sym"] == ">>>";
|
||||
let unit = captures["tu"].trim().to_string();
|
||||
let name = if is_duplicate {
|
||||
if last_name.is_empty() {
|
||||
return Err(Error::msg("Last name empty?"));
|
||||
}
|
||||
last_name.clone()
|
||||
} else {
|
||||
captures["sym"].to_string()
|
||||
};
|
||||
let symbol_ref = SymbolRef { name: name.clone(), unit: unit.clone() };
|
||||
let depth: usize = captures["depth"].parse()?;
|
||||
if depth > symbol_stack.len() {
|
||||
symbol_stack.push(symbol_ref.clone());
|
||||
} else if depth <= symbol_stack.len() {
|
||||
symbol_stack.truncate(depth - 1);
|
||||
symbol_stack.push(symbol_ref.clone());
|
||||
}
|
||||
// println!("Entry: {} ({})", name, tu);
|
||||
let kind = match &captures["type"] {
|
||||
"func" => SymbolKind::Function,
|
||||
"object" => SymbolKind::Object,
|
||||
"section" => SymbolKind::Section,
|
||||
"notype" => SymbolKind::NoType,
|
||||
_ => {
|
||||
return Err(Error::msg(format!(
|
||||
"Unknown symbol type: {}",
|
||||
&captures["type"],
|
||||
)));
|
||||
}
|
||||
};
|
||||
let visibility = match &captures["vis"] {
|
||||
"global" => SymbolVisibility::Global,
|
||||
"local" => SymbolVisibility::Local,
|
||||
"weak" => SymbolVisibility::Weak,
|
||||
_ => {
|
||||
return Err(Error::msg(format!(
|
||||
"Unknown symbol visibility: {}",
|
||||
&captures["vis"],
|
||||
)));
|
||||
}
|
||||
};
|
||||
if !is_duplicate && symbol_stack.len() > 1 {
|
||||
let from = &symbol_stack[symbol_stack.len() - 2];
|
||||
entries.entry_referenced_from.insert(symbol_ref.clone(), from.clone());
|
||||
entries.entry_references.insert(from.clone(), symbol_ref.clone());
|
||||
}
|
||||
let mut should_insert = true;
|
||||
if let Some(symbol) = entries.symbols.get(&symbol_ref) {
|
||||
if symbol.kind != kind {
|
||||
log::warn!(
|
||||
"Kind mismatch for {}: was {:?}, now {:?}",
|
||||
symbol.name,
|
||||
symbol.kind,
|
||||
kind
|
||||
);
|
||||
}
|
||||
if symbol.visibility != visibility {
|
||||
log::warn!(
|
||||
"Visibility mismatch for {}: was {:?}, now {:?}",
|
||||
symbol.name,
|
||||
symbol.visibility,
|
||||
visibility
|
||||
);
|
||||
}
|
||||
entries.unit_entries.insert(unit.clone(), symbol_ref.clone());
|
||||
should_insert = false;
|
||||
}
|
||||
if should_insert {
|
||||
let demangled =
|
||||
demangle(&name, &DemangleOptions { omit_empty_parameters: true });
|
||||
entries.symbols.insert(symbol_ref.clone(), SymbolEntry {
|
||||
name: name.clone(),
|
||||
demangled,
|
||||
kind,
|
||||
visibility,
|
||||
unit: unit.clone(),
|
||||
address: 0,
|
||||
size: 0,
|
||||
section: String::new(),
|
||||
});
|
||||
last_name = name.clone();
|
||||
entries.unit_entries.insert(unit, symbol_ref.clone());
|
||||
}
|
||||
} else if let Some(captures) = LINK_MAP_ENTRY_GENERATED.captures(&line) {
|
||||
let name = captures["sym"].to_string();
|
||||
let demangled =
|
||||
demangle(&name, &DemangleOptions { omit_empty_parameters: true });
|
||||
let symbol_ref =
|
||||
SymbolRef { name: name.clone(), unit: "[generated]".to_string() };
|
||||
entries.symbols.insert(symbol_ref, SymbolEntry {
|
||||
name,
|
||||
demangled,
|
||||
kind: SymbolKind::NoType,
|
||||
visibility: SymbolVisibility::Global,
|
||||
unit: "[generated]".to_string(),
|
||||
address: 0,
|
||||
size: 0,
|
||||
section: String::new(),
|
||||
});
|
||||
} else if line.trim().is_empty()
|
||||
|| LINK_MAP_ENTRY_DUPLICATE.is_match(&line)
|
||||
|| SECTION_LAYOUT_HEADER.is_match(&line)
|
||||
|| EXTERN_SYMBOL.is_match(&line)
|
||||
{
|
||||
// Ignore
|
||||
} else if let Some(captures) = SECTION_LAYOUT_START.captures(&line) {
|
||||
current_section = captures["section"].trim().to_string();
|
||||
last_unit.clear();
|
||||
log::debug!("Processing section layout for {}", current_section);
|
||||
} else if let Some(captures) = SECTION_LAYOUT_SYMBOL.captures(&line) {
|
||||
if captures["rom_addr"].trim() == "UNUSED" {
|
||||
continue;
|
||||
}
|
||||
let sym_name = captures["sym"].trim();
|
||||
let tu = captures["tu"].trim();
|
||||
let mut address = u32::from_str_radix(captures["addr"].trim(), 16)?;
|
||||
let mut size = u32::from_str_radix(captures["size"].trim(), 16)?;
|
||||
|
||||
// For RELs, the each section starts at address 0. For our purposes
|
||||
// we'll create "fake" addresses by simply starting at the end of the
|
||||
// previous section.
|
||||
if last_unit.is_empty() {
|
||||
if address == 0 {
|
||||
relative_offset = last_section_end;
|
||||
} else {
|
||||
relative_offset = 0;
|
||||
}
|
||||
}
|
||||
address += relative_offset;
|
||||
|
||||
// Section symbol (i.e. ".data") indicates section size for a TU
|
||||
if sym_name == current_section {
|
||||
// Skip empty sections
|
||||
if size == 0 {
|
||||
continue;
|
||||
}
|
||||
let end = address + size;
|
||||
entries.unit_section_ranges.insert(tu.to_string(), address..end);
|
||||
last_unit = tu.to_string();
|
||||
last_section_end = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, for ASM-generated objects, the first section symbol in a TU
|
||||
// has the full size of the section.
|
||||
if tu != last_unit {
|
||||
if size == 0 {
|
||||
return Err(Error::msg(format!(
|
||||
"No section size for {} in {}",
|
||||
sym_name, tu
|
||||
)));
|
||||
}
|
||||
let end = address + size;
|
||||
entries.unit_section_ranges.insert(tu.to_string(), address..end);
|
||||
last_unit = tu.to_string();
|
||||
last_section_end = end;
|
||||
|
||||
// Clear it, so that we guess the "real" symbol size later.
|
||||
size = 0;
|
||||
}
|
||||
|
||||
// Ignore ...data.0 and similar
|
||||
if sym_name.starts_with("...") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let symbol_ref = SymbolRef { name: sym_name.to_string(), unit: tu.to_string() };
|
||||
if let Some(symbol) = entries.symbols.get_mut(&symbol_ref) {
|
||||
symbol.address = address;
|
||||
symbol.size = size;
|
||||
symbol.section = current_section.clone();
|
||||
match entries.address_to_symbol.entry(address) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(symbol_ref.clone());
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
log::warn!(
|
||||
"Symbol overridden @ {:X} from {} to {} in {}",
|
||||
symbol.address,
|
||||
entry.get().name,
|
||||
sym_name,
|
||||
tu
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let visibility = if has_link_map {
|
||||
log::warn!(
|
||||
"Symbol not in link map: {} ({}). Type and visibility unknown.",
|
||||
sym_name,
|
||||
tu,
|
||||
);
|
||||
SymbolVisibility::Local
|
||||
} else {
|
||||
SymbolVisibility::Global
|
||||
};
|
||||
entries.symbols.insert(symbol_ref.clone(), SymbolEntry {
|
||||
name: sym_name.to_string(),
|
||||
demangled: None,
|
||||
kind: SymbolKind::NoType,
|
||||
visibility,
|
||||
unit: tu.to_string(),
|
||||
address,
|
||||
size,
|
||||
section: current_section.clone(),
|
||||
});
|
||||
match entries.address_to_symbol.entry(address) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(symbol_ref.clone());
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
log::warn!(
|
||||
"Symbol overridden @ {:X} from {} to {} in {}",
|
||||
address,
|
||||
entry.get().name,
|
||||
sym_name,
|
||||
tu
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if MEMORY_MAP_HEADER.is_match(&line) {
|
||||
// log::debug!("Done");
|
||||
break;
|
||||
} else {
|
||||
todo!("{}", line);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(Error::from(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let section_order = resolve_section_order(&entries.address_to_symbol, &mut entries.symbols)?;
|
||||
entries.symbol_order = section_order.symbol_order;
|
||||
entries.unit_order = section_order.unit_order;
|
||||
|
||||
Ok(entries)
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub(crate) mod map;
|
Loading…
Reference in New Issue