├── .gitignore ├── .gitattributes ├── .github └── dependabot.yml ├── CHANGELOG.md ├── Cargo.toml ├── tests └── command.rs ├── LICENSE ├── ci ├── azure-deployment.yml ├── azure-build-and-test.yml └── azure-ci-cd.yml ├── README.md ├── src └── main.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # See GitHub for changelog 2 | 3 | Changelogs for this project are curated from the Git history of its main branch. 4 | You can find them by looking at [the changelog on the `release` branch][cl] or 5 | the project's [GitHub release history][releases]. 6 | 7 | [cl]: https://github.com/pkgw/elfx86exts/blob/release/CHANGELOG.md 8 | [releases]: https://github.com/pkgw/elfx86exts/releases 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2022 Peter Williams 2 | # Licensed under the MIT License. 3 | 4 | [package] 5 | name = "elfx86exts" 6 | version = "0.0.0-dev.0" 7 | authors = ["Peter Williams "] 8 | description = "Decode x86 binaries (ELF or MachO) and print out which instruction set extensions they use." 9 | homepage = "https://github.com/pkgw/elfx86exts" 10 | repository = "https://github.com/pkgw/elfx86exts" 11 | documentation = "https://docs.rs/crate/elfx86exts" 12 | readme = "README.md" 13 | keywords = ["disassembly", "ELF", "MachO", "x86"] 14 | categories = ["command-line-utilities", "development-tools::debugging", "parsing"] 15 | license = "MIT" 16 | edition = "2018" 17 | 18 | [dependencies] 19 | capstone = "^0.13" 20 | clap = { version = "^4", features = ["derive"] } 21 | object = "^0.38" 22 | memmap = "^0.7.0" 23 | 24 | [dev-dependencies] 25 | assert_cmd = "2.1" 26 | escargot = "0.5" 27 | -------------------------------------------------------------------------------- /tests/command.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Peter Williams 2 | // Licensed under the MIT License. 3 | 4 | //! Test the CLI command. 5 | 6 | extern crate assert_cmd; 7 | extern crate escargot; 8 | 9 | use assert_cmd::prelude::*; 10 | use std::process::Command; 11 | 12 | /// Test that the command runs successfully on itself. In principle I think 13 | /// this might be somewhat limiting: there's no reason you couldn't compile 14 | /// this tool on a platform whose executable format is not ELF or Mach-O, and 15 | /// in that case this test would fail. Somehow I'm not very worried about that 16 | /// possibility, though. 17 | #[test] 18 | fn run_on_self() { 19 | let cmd_run = escargot::CargoBuild::new() 20 | .bin("elfx86exts") 21 | .current_release() 22 | .current_target() 23 | .run() 24 | .unwrap(); 25 | let cmd_path = cmd_run.path(); 26 | 27 | let mut cmd = Command::new(cmd_path); 28 | cmd.arg(cmd_path); 29 | 30 | cmd.assert().success(); 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Thomas Park 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /ci/azure-deployment.yml: -------------------------------------------------------------------------------- 1 | # Deployment pipeline, run when all CI tests pass on `master` or `rc`. 2 | 3 | parameters: 4 | - name: rcBuild 5 | type: boolean 6 | default: false 7 | 8 | steps: 9 | - bash: | 10 | cd $(Pipeline.Workspace) 11 | mkdir binary-collection 12 | cp binary-*/* binary-collection/ 13 | displayName: Collect release artifacts 14 | 15 | - publish: $(Pipeline.Workspace)/binary-collection 16 | displayName: Publish release artifacts 17 | artifact: binary-collection 18 | 19 | # if we're on the `rc` branch, the release has been fully vetted and the 20 | # internal artifacts have been gathered -- time to lock in a new `release` 21 | # commit and invoke the full release processes. 22 | 23 | - ${{ if eq(parameters.rcBuild, true) }}: 24 | - checkout: self 25 | 26 | - bash: | 27 | d="$(mktemp -d /tmp/cranko.XXXXXX)" 28 | cd "$d" 29 | curl --proto '=https' --tlsv1.2 -sSf https://pkgw.github.io/cranko/fetch-latest.sh | sh 30 | echo "##vso[task.prependpath]$d" 31 | displayName: Install latest Cranko 32 | 33 | - bash: | 34 | git switch -c release 35 | git pull --ff-only $(Pipeline.Workspace)/git-release/release.bundle 36 | git show 37 | cranko release-workflow tag 38 | displayName: Prepare release commit and tags 39 | 40 | - bash: | 41 | cranko github install-credential-helper 42 | git push --tags origin release:release 43 | displayName: Update release branch 44 | env: 45 | GITHUB_TOKEN: $(GITHUB_TOKEN) 46 | 47 | - bash: | 48 | cranko cargo foreach-released publish 49 | displayName: Publish updated Cargo crates 50 | env: 51 | CARGO_REGISTRY_TOKEN: $(CARGO_REGISTRY_TOKEN) 52 | 53 | - bash: | 54 | cranko github create-releases 55 | cranko github upload-artifacts elfx86exts $(Pipeline.Workspace)/binary-collection/* 56 | displayName: Create GitHub releases 57 | env: 58 | GITHUB_TOKEN: $(GITHUB_TOKEN) 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elfx86exts 2 | 3 | Disassemble a binary and print out which instruction set extensions it uses. 4 | Despite the utterly misleading name, this tool supports ELF and MachO binaries, 5 | and perhaps other formats as well, and has preliminary support for ARM64 as well 6 | as X86/64. It used to be a lot more limited! 7 | 8 | I have no idea what I'm doing here, but it seems to work. There are several 9 | Rust crates that make this pretty easy to do. 10 | 11 | 12 | ## Change Log 13 | 14 | See [the CHANGELOG on the release 15 | branch](https://github.com/pkgw/elfx86exts/blob/release/CHANGELOG.md) for news 16 | about what has changed between releases. 17 | 18 | 19 | ## Installation 20 | 21 | ### Prepackaged 22 | 23 | This tool is installable through a few package managers: 24 | 25 | - [Arch Linux AUR](https://aur.archlinux.org/packages/elfx86exts/) 26 | - [conda-forge](https://anaconda.org/conda-forge/elfx86exts) (Linux only right now) 27 | 28 | If you are interested in packaging `elfx86exts` in a new packaging system, or 29 | have already done so, please submit a PR to add it to this list. 30 | 31 | ### Compiling the Latest Release 32 | 33 | If a package is not available, in most cases it will be straightforward to 34 | build `elfx86exts` yourself. Dependencies are: 35 | 36 | - A [Rust](https://www.rust-lang.org/) toolchain 37 | - The [Capstone](http://www.capstone-engine.org/) disassembly engine 38 | 39 | Both of these dependencies are available through a wide variety of package 40 | managers. Once they’re set up, you don’t even need to check out this 41 | repository to install the latest release. Simply run: 42 | 43 | ``` 44 | cargo install elfx86exts 45 | ``` 46 | 47 | … and the tool will be installed in your Cargo binary directory, usually 48 | `~/.cargo/bin/`. When using this method, you need to add the `--force` flag to 49 | upgrade from one version to the next. 50 | 51 | ### Compiling the Code From Git 52 | 53 | This is hardly any more difficult than the above. Check out this repository, 54 | then run: 55 | 56 | ``` 57 | cargo install --path . 58 | ``` 59 | 60 | To develop the program, use the `cargo build` and `cargo run` commands. For 61 | more information, see 62 | [The Cargo Book](https://doc.rust-lang.org/cargo/index.html). 63 | 64 | 65 | ## Contributions 66 | 67 | Contributions are welcome! Please submit PRs against this repository, or file 68 | issues for discussion. The only important rule is that all participants are 69 | expected to abide by the spirit of a standard 70 | [Contributor Covenant code of conduct](https://www.contributor-covenant.org/). 71 | All contributions will be assumed to be licensed under the terms described 72 | below unless you explicitly state otherwise. 73 | 74 | 75 | ## Licensing 76 | 77 | Licensed under the [MIT License](https://opensource.org/licenses/MIT). 78 | -------------------------------------------------------------------------------- /ci/azure-build-and-test.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: canaryBuild 3 | type: boolean 4 | default: false 5 | - name: primaryBuild 6 | type: boolean 7 | default: false 8 | 9 | steps: 10 | - checkout: self 11 | 12 | - bash: | 13 | set -ex 14 | rustup set profile minimal 15 | rustup component remove --toolchain=$TOOLCHAIN rust-docs || echo "already removed" 16 | rustup update --no-self-update $TOOLCHAIN 17 | rustup default $TOOLCHAIN 18 | # Helpful versions 19 | rustup -V 20 | rustc -Vv 21 | cargo -V 22 | displayName: Setup Rust 23 | 24 | - bash: | 25 | d="$(mktemp -d /tmp/cranko.XXXXXX)" 26 | cd "$d" 27 | curl --proto '=https' --tlsv1.2 -sSf https://pkgw.github.io/cranko/fetch-latest.sh | sh 28 | echo "##vso[task.prependpath]$d" 29 | displayName: Install latest Cranko (not Windows) 30 | condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT')) 31 | 32 | - pwsh: | 33 | $d = Join-Path $Env:Temp cranko-$(New-Guid) 34 | [void][System.IO.Directory]::CreateDirectory($d) 35 | cd $d 36 | [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 37 | iex ((New-Object System.Net.WebClient).DownloadString('https://pkgw.github.io/cranko/fetch-latest.ps1')) 38 | echo "##vso[task.prependpath]$d" 39 | displayName: Install latest Cranko (Windows) 40 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) 41 | 42 | - bash: | 43 | cranko release-workflow apply-versions 44 | displayName: "cranko release-workflow apply-versions" 45 | 46 | - bash: cargo build --all --release 47 | displayName: "cargo build" 48 | 49 | - bash: cargo test --all --release 50 | displayName: "cargo test" 51 | 52 | # For non-canary builds, export artifacts. 53 | 54 | - ${{ if eq(parameters.canaryBuild, false) }}: 55 | - bash: | 56 | git add . 57 | cranko release-workflow commit 58 | git show HEAD 59 | displayName: Make release commit 60 | 61 | - bash: | 62 | artifact_dir="$(Build.ArtifactStagingDirectory)/binary-$TARGET" 63 | mkdir -p "$artifact_dir" 64 | cranko cargo package-released-binaries -t $TARGET $artifact_dir -- build --release 65 | displayName: Package binaries 66 | 67 | - task: PublishPipelineArtifact@1 68 | displayName: Publish packaged binary artifact(s) 69 | inputs: 70 | targetPath: '$(Build.ArtifactStagingDirectory)/binary-$(TARGET)' 71 | artifactName: binary-$(TARGET) 72 | 73 | # If, further, we're the primary build on the `rc` branch, export ours as the 74 | # canonical release commit. 75 | 76 | - ${{ if and(eq(parameters.primaryBuild, true), eq(variables['Build.SourceBranchName'], 'rc')) }}: 77 | - bash: | 78 | artifact_dir="$(Build.ArtifactStagingDirectory)/git-release" 79 | mkdir -p "$artifact_dir" 80 | git bundle create "$artifact_dir/release.bundle" origin/master..HEAD 81 | displayName: "Generate and Bundle release commit" 82 | 83 | - task: PublishPipelineArtifact@1 84 | displayName: Publish git bundle artifact 85 | inputs: 86 | targetPath: '$(Build.ArtifactStagingDirectory)/git-release' 87 | artifactName: git-release 88 | -------------------------------------------------------------------------------- /ci/azure-ci-cd.yml: -------------------------------------------------------------------------------- 1 | # Main pipeline spec for CI/CD on Azure Pipelines. 2 | 3 | trigger: 4 | branches: 5 | include: 6 | - master 7 | - rc 8 | 9 | # We cannot use Pipeline's matrixing framework because only it works with 10 | # *runtime variables*, while we want to use templates with *parameters* which 11 | # are evaluated at *compile time* only. By coding our matrixing data as 12 | # top-level parameters, we can use them in compile-time template evaluation 13 | # (`${{ }}` expressions) to achieve a matrixing effect. Only a few *variables* 14 | # can be used at compile time: see "Available in templates?" in the table at: 15 | # https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml 16 | # This is why some platform-specific steps use `condition:` terms rather than 17 | # compile-time `${{ if }}:` statements. 18 | parameters: 19 | - name: builds 20 | type: object 21 | default: 22 | - name: linux_stable 23 | vmImage: ubuntu-latest 24 | params: 25 | primaryBuild: true 26 | vars: 27 | TARGET: x86_64-unknown-linux-gnu 28 | TOOLCHAIN: stable 29 | 30 | - name: linux_beta 31 | vmImage: ubuntu-latest 32 | params: 33 | canaryBuild: true 34 | vars: 35 | TARGET: x86_64-unknown-linux-gnu 36 | TOOLCHAIN: beta 37 | 38 | - name: linux_nightly 39 | vmImage: ubuntu-latest 40 | params: 41 | canaryBuild: true 42 | vars: 43 | TARGET: x86_64-unknown-linux-gnu 44 | TOOLCHAIN: nightly 45 | 46 | - name: windows_msvc 47 | vmImage: windows-2019 48 | params: {} 49 | vars: 50 | TARGET: x86_64-pc-windows-msvc 51 | TOOLCHAIN: stable-x86_64-pc-windows-msvc 52 | 53 | - name: windows_gnu 54 | vmImage: windows-2019 55 | params: {} 56 | vars: 57 | TARGET: x86_64-pc-windows-gnu 58 | TOOLCHAIN: stable-x86_64-pc-windows-gnu 59 | 60 | - name: macos 61 | vmImage: macos-13 62 | params: {} 63 | vars: 64 | TARGET: x86_64-apple-darwin 65 | TOOLCHAIN: stable 66 | 67 | stages: 68 | - stage: BuildAndTest 69 | jobs: 70 | - ${{ each build in parameters.builds }}: 71 | - job: ${{ format('build_{0}', build.name) }} 72 | pool: 73 | vmImage: ${{ build.vmImage }} 74 | steps: 75 | - template: azure-build-and-test.yml 76 | parameters: 77 | ${{ insert }}: ${{ build.params }} 78 | variables: 79 | ${{ insert }}: ${{ build.vars }} 80 | 81 | # If all of those succeed and we're not in a pull request, run the deployment 82 | # pipeline. If we're specifically on the `rc` branch, this will finalize any 83 | # releases. 84 | - stage: Deploy 85 | condition: and(succeeded('BuildAndTest'), not(eq(variables['build.reason'], 'PullRequest'))) 86 | jobs: 87 | - deployment: Deploy 88 | environment: Deploy 89 | displayName: Deploy 90 | pool: 91 | vmImage: ubuntu-latest 92 | 93 | variables: 94 | - group: Deployment Credentials 95 | 96 | strategy: 97 | runOnce: 98 | deploy: 99 | steps: 100 | - template: azure-deployment.yml 101 | parameters: 102 | rcBuild: ${{ eq(variables['Build.SourceBranchName'], 'rc') }} 103 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2023 elfx86exts contributors 2 | // Licensed under the MIT License. 3 | 4 | //! elfx86exts helps you understand which instruction set extensions are used 5 | //! by a binary. Despite the misleading name, this crate supports both 6 | //! ELF and MachO binary formats and x86 as well as Arm processor architectures via the 7 | //! [capstone](https://crates.io/crates/capstone) crate. 8 | 9 | use capstone::{Arch as CapArch, Capstone, Mode, NO_EXTRA_MODE}; 10 | use clap::Parser; 11 | use object::{Architecture as ObjArch, Object, ObjectSection, SectionKind}; 12 | use std::{ 13 | cmp, 14 | collections::{HashMap, HashSet}, 15 | fs::File, 16 | path::PathBuf, 17 | }; 18 | 19 | /// These are from capstone/include/x86.h, which is super sketchy since the 20 | /// enum values are not specificied explicitly. 21 | fn describe_group_x86(g: &u8) -> Option<&'static str> { 22 | Some(match *g { 23 | 128 => "VT-x/AMD-V", // https://github.com/aquynh/capstone/blob/master/include/x86.h#L1583 24 | 129 => "3DNow", 25 | 130 => "AES", 26 | 131 => "ADX", 27 | 132 => "AVX", 28 | 133 => "AVX2", 29 | 134 => "AVX512", 30 | 135 => "BMI", 31 | 136 => "BMI2", 32 | 137 => "CMOV", 33 | 138 => "F16C", // line 1593 34 | 139 => "FMA", 35 | 140 => "FMA4", 36 | 141 => "FSGSBASE", 37 | 142 => "HLE", 38 | 143 => "MMX", 39 | 144 => "MODE32", 40 | 145 => "MODE64", 41 | 146 => "RTM", 42 | 147 => "SHA", 43 | 148 => "SSE1", // line 1603 44 | 149 => "SSE2", 45 | 150 => "SSE3", 46 | 151 => "SSE41", 47 | 152 => "SSE42", 48 | 153 => "SSE4A", 49 | 154 => "SSSE3", 50 | 155 => "PCLMUL", 51 | 156 => "XOP", 52 | 157 => "CDI", 53 | 158 => "ERI", // line 1613 54 | 159 => "TBM", 55 | 160 => "16BITMODE", 56 | 161 => "NOT64BITMODE", 57 | 162 => "SGX", 58 | 163 => "DQI", 59 | 164 => "BWI", 60 | 165 => "PFI", 61 | 166 => "VLX", 62 | 167 => "SMAP", 63 | 168 => "NOVLX", // line 1623 64 | _ => { 65 | return None; 66 | } 67 | }) 68 | } 69 | 70 | /// These are from capstone/include/arm64.h 71 | fn describe_group_aarch64(g: &u8) -> Option<&'static str> { 72 | Some(match *g { 73 | 128 => "CRYPTO", // https://github.com/aquynh/capstone/blob/master/include/aarch64.h 74 | 129 => "FPARMV8", // Appears to map to both fp and fp16 instruction sets 75 | 130 => "NEON", 76 | 131 => "CRC", 77 | 132 => "AES", 78 | 133 => "DOTPROD", 79 | 134 => "FULLFP16", 80 | 135 => "LSE", 81 | 136 => "RCPC", 82 | 137 => "RDM", 83 | 138 => "SHA2", 84 | 139 => "SHA3", 85 | 140 => "SM4", 86 | 141 => "SVE", 87 | 142 => "SVE2", 88 | 143 => "SVE2AES", 89 | 144 => "SVE2BitPerm", 90 | 145 => "SVE2SHA3", 91 | 146 => "SV#2SM4", 92 | 147 => "SME", 93 | 148 => "SMEF64", 94 | 149 => "SMEI64", 95 | 150 => "MatMulFP32", 96 | 151 => "MatMulFP64", 97 | 152 => "MatMulInt8", 98 | 153 => "V8_1A", 99 | 154 => "V8_3A", 100 | 155 => "V8_4A", 101 | _ => { 102 | return None; 103 | } 104 | }) 105 | } 106 | 107 | #[derive(Parser, Debug)] 108 | #[command(author, version, about, long_about = None)] 109 | struct Args { 110 | /// The path of the file to analyze 111 | path: PathBuf, 112 | } 113 | 114 | fn main() { 115 | // This list is taken from https://en.wikipedia.org/wiki/List_of_Intel_CPU_microarchitectures 116 | // The ID numbers here are internal elfx86exts identifiers; they have no meaning except 117 | // for their relative ordering. 118 | let cpu_generations: HashMap<&str, u16> = [ 119 | // Intel: 1xx 120 | ("Pentium", 100), 121 | ("Pentium Pro", 101), 122 | ("Pentium III", 102), 123 | ("Pentium 4", 103), 124 | ("Pentium M", 104), 125 | ("Prescott", 105), 126 | ("Intel Core", 106), 127 | ("Penryn", 107), 128 | ("Nehalem", 108), 129 | ("Bonnell", 109), 130 | ("Westmere", 110), 131 | ("Saltwell", 111), 132 | ("Sandy Bridge", 112), 133 | ("Ivy Bridge", 113), 134 | ("Silvermont", 114), 135 | ("Haswell", 115), 136 | ("Broadwell", 116), 137 | ("Airmont", 117), 138 | ("Skylake", 118), 139 | ("Goldmont", 119), 140 | ("Kaby Lake", 120), 141 | ("Coffee Lake", 121), 142 | ("Cannon Lake", 122), 143 | ("Whiskey Lake", 123), 144 | ("Amber Lake", 124), 145 | ("Cascade Lake", 125), 146 | ("Cooper Lake", 126), 147 | ("Ice Lake", 127), 148 | // AMD: 2xx 149 | ("K6-2", 200), 150 | ("Bulldozer", 201), 151 | ("K10", 202), 152 | ("Piledriver", 203), 153 | // Unknown 154 | ("Unknown", 999), 155 | ] 156 | .iter() 157 | .cloned() 158 | .collect(); 159 | 160 | let mut cpu_generations_reverse: HashMap = HashMap::new(); 161 | 162 | for (key, val) in &cpu_generations { 163 | cpu_generations_reverse.insert(*val, key); 164 | } 165 | 166 | // The Intel generation that introduced each instruction set 167 | // This list is based on Googling and Wikipedia reading 168 | // Many of these are approximations, since CPU development isn't strictly linear, and not 169 | // all models of a generation support a given instruction set. 170 | let instrset_to_cpu: HashMap<&str, &str> = [ 171 | ("VT-x/AMD-V", "Intel Core"), // guess; https://en.wikipedia.org/wiki/X86_virtualization 172 | ("3DNow", "K6-2"), // Not supported by Intel CPUs, nor AMD since 2010; https://en.wikipedia.org/wiki/3DNow! 173 | ("AES", "Westmere"), // https://en.wikipedia.org/wiki/AES_instruction_set 174 | ("ADX", "Broadwell"), // https://en.wikipedia.org/wiki/Intel_ADX 175 | ("AVX", "Sandy Bridge"), // https://en.wikipedia.org/wiki/Advanced_Vector_Extensions 176 | ("AVX2", "Haswell"), // https://en.wikipedia.org/wiki/Advanced_Vector_Extensions 177 | ("AVX512", "Unknown"), // It's complicated. https://en.wikipedia.org/wiki/Advanced_Vector_Extensions 178 | ("BMI", "Haswell"), // https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets 179 | ("BMI2", "Haswell"), // https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets 180 | ("CMOV", "Pentium Pro"), // https://en.wikipedia.org/wiki/X86_instruction_listings 181 | ("F16C", "Ivy Bridge"), // https://en.wikipedia.org/wiki/F16C 182 | ("FMA", "Haswell"), // https://en.wikipedia.org/wiki/FMA_instruction_set 183 | ("FMA4", "Bulldozer"), // Not supported by Intel? https://en.wikipedia.org/wiki/FMA_instruction_set 184 | ("FSGSBASE", "Ivy Bridge"), // https://lwn.net/Articles/821723/ 185 | ("HLE", "Haswell"), // Part of TSX - https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions 186 | ("MMX", "Pentium"), // https://en.wikipedia.org/wiki/MMX_(instruction_set) 187 | ("MODE32", "Pentium"), // Assuming all x86 CPUs support 32-bit mode 188 | ("MODE64", "Intel Core"), // I'm assuming this just means x86-64 support 189 | ("RTM", "Haswell"), // Part of TSX - https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions 190 | ("SHA", "Goldmont"), // https://en.wikipedia.org/wiki/Intel_SHA_extensions 191 | ("SSE1", "Pentium III"), // https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions 192 | ("SSE2", "Pentium 4"), // https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions 193 | ("SSE3", "Prescott"), // https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions 194 | ("SSE41", "Penryn"), // https://en.wikipedia.org/wiki/SSE4 195 | ("SSE42", "Nehalem"), // https://en.wikipedia.org/wiki/SSE4 196 | ("SSE4A", "K10"), // AMD-only - https://en.wikipedia.org/wiki/SSE4 197 | ("SSSE3", "Intel Core"), // https://en.wikipedia.org/wiki/Intel_Core_(microarchitecture) 198 | ("PCLMUL", "Intel Core"), // https://software.intel.com/en-us/articles/intel-carry-less-multiplication-instruction-and-its-usage-for-computing-the-gcm-mode/ 199 | ("XOP", "Bulldozer"), // AMD-only - https://en.wikipedia.org/wiki/XOP_instruction_set 200 | ("CDI", "Unknown"), // Knights Landing - https://software.intel.com/en-us/blogs/2013/avx-512-instructions 201 | ("ERI", "Unknown"), // Knights Landing - https://software.intel.com/en-us/blogs/2013/avx-512-instructions 202 | ("TBM", "Piledriver"), // AMD-only - https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets#TBM_(Trailing_Bit_Manipulation) 203 | ("16BITMODE", "Unknown"), 204 | ("NOT64BITMODE", "Unknown"), 205 | ("SGX", "Skylake"), // https://en.wikipedia.org/wiki/Software_Guard_Extensions 206 | ("DQI", "Cannon Lake"), // AVX-512 Doubleword and Quadword Instructions 207 | ("BWI", "Cannon Lake"), // AVX-512 Byte and Word Instructions 208 | ("PFI", "Unknown"), // AVX-512 Prefetch Instructions, implemented by Knights Landing - https://software.intel.com/en-us/blogs/2013/avx-512-instructions 209 | ("VLX", "Cannon Lake"), // AVX-512 Vector Length Extensions 210 | ("SMAP", "Broadwell"), // https://en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention 211 | ("NOVLX", "Unknown"), // References in LLVM sources, associated mostly with AVX and AVX2 when VLX are not available 212 | ] 213 | .iter() 214 | .cloned() 215 | .collect(); 216 | 217 | let args = Args::parse(); 218 | 219 | // Read the file 220 | let f = File::open(&args.path).expect("can't open object file"); 221 | let buf = unsafe { memmap::Mmap::map(&f).expect("can't memmap object file") }; 222 | let obj = object::File::parse(&*buf).expect("can't parse object file"); 223 | let obj_arch = obj.architecture(); 224 | 225 | println!( 226 | "File format and CPU architecture: {:?}, {obj_arch:?}", 227 | obj.format() 228 | ); 229 | 230 | // Figure out the capstone / analysis settings. 231 | // 232 | // Capstone apparently doesn't do any validation, so if we initialize it 233 | // with the wrong architecture, it will just plunge on ahead and parse 234 | // everything as best it can. So in principle instead of exiting early with 235 | // unrecognized arches, we could print a warning and YOLO it. Right now we 236 | // don't do that, but we do try to be generous about accepting any `object` 237 | // architecture that might be consistent with a supported `capstone` 238 | // architecture. 239 | let (cap_arch, mode, describe_group): (CapArch, Mode, fn(&u8) -> Option<&str>) = match obj_arch 240 | { 241 | ObjArch::X86_64 | ObjArch::X86_64_X32 => { 242 | if obj.is_64() { 243 | (CapArch::X86, Mode::Mode64, describe_group_x86) 244 | } else { 245 | (CapArch::X86, Mode::Mode32, describe_group_x86) 246 | } 247 | } 248 | 249 | ObjArch::Aarch64 | ObjArch::Aarch64_Ilp32 => { 250 | (CapArch::ARM64, Mode::Arm, describe_group_aarch64) 251 | } 252 | 253 | _ => { 254 | // This could plausibly be an error exit, but it doesn't seem 255 | // unreasonable to exit with success either. 256 | println!( 257 | "CPU architecture `{obj_arch:?}` of input file `{}` is not currently supported for analysis", 258 | args.path.display() 259 | ); 260 | return; 261 | } 262 | }; 263 | 264 | // Disassemble the file 265 | let mut cs = 266 | Capstone::new_raw(cap_arch, mode, NO_EXTRA_MODE, None).expect("can't initialize capstone"); 267 | cs.set_detail(true) 268 | .expect("can't enable Capstone detail mode"); 269 | cs.set_skipdata(true) 270 | .expect("can't enable Capstone skip data mode"); 271 | 272 | let mut seen_groups = HashSet::new(); 273 | 274 | for sect in obj.sections() { 275 | if sect.kind() != SectionKind::Text { 276 | continue; 277 | } 278 | 279 | let data = sect.data().expect("couldn't get section data"); 280 | let mut offset = 0; 281 | 282 | loop { 283 | let rest = &data[offset..]; 284 | 285 | if rest.is_empty() { 286 | break; 287 | } 288 | 289 | let insns = cs 290 | .disasm_count(rest, 0, 1) 291 | .expect("couldn't disassemble section"); 292 | 293 | for insn in insns.iter() { 294 | offset += insn.bytes().len(); 295 | 296 | let Ok(detail) = cs.insn_detail(insn) else { 297 | continue; 298 | }; 299 | 300 | for group_code in detail.groups() { 301 | if seen_groups.insert(group_code.0) { 302 | if let Some(mnemonic) = insn.mnemonic() { 303 | if let Some(desc) = describe_group(&group_code.0) { 304 | println!("{} ({})", desc, mnemonic); 305 | } 306 | } 307 | } 308 | } 309 | } 310 | } 311 | } 312 | 313 | let mut proc_features = seen_groups 314 | .iter() 315 | .filter_map(describe_group) 316 | .collect::>(); 317 | proc_features.sort(); 318 | 319 | println!( 320 | "Instruction set extensions used: {}", 321 | proc_features.join(", ") 322 | ); 323 | 324 | // For x86 processors, we can map the instruction set features to specific CPU generations 325 | if cap_arch == CapArch::X86 { 326 | let mut max_gen_code = 100; 327 | for group_code in seen_groups.iter() { 328 | if let Some(desc) = describe_group(group_code) { 329 | match instrset_to_cpu.get(desc) { 330 | Some(generation) => match cpu_generations.get(generation) { 331 | Some(gen_code) => { 332 | max_gen_code = cmp::max(max_gen_code, *gen_code); 333 | } 334 | None => unimplemented!(), 335 | }, 336 | None => unimplemented!(), 337 | } 338 | } 339 | } 340 | 341 | match cpu_generations_reverse.get(&max_gen_code) { 342 | Some(generation) => { 343 | println!("CPU Generation: {}", generation); 344 | } 345 | None => unimplemented!(), 346 | } 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "anstream" 13 | version = "0.6.7" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" 16 | dependencies = [ 17 | "anstyle", 18 | "anstyle-parse", 19 | "anstyle-query", 20 | "anstyle-wincon", 21 | "colorchoice", 22 | "utf8parse", 23 | ] 24 | 25 | [[package]] 26 | name = "anstyle" 27 | version = "1.0.8" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 30 | 31 | [[package]] 32 | name = "anstyle-parse" 33 | version = "0.2.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" 36 | dependencies = [ 37 | "utf8parse", 38 | ] 39 | 40 | [[package]] 41 | name = "anstyle-query" 42 | version = "1.0.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 45 | dependencies = [ 46 | "windows-sys", 47 | ] 48 | 49 | [[package]] 50 | name = "anstyle-wincon" 51 | version = "3.0.1" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" 54 | dependencies = [ 55 | "anstyle", 56 | "windows-sys", 57 | ] 58 | 59 | [[package]] 60 | name = "assert_cmd" 61 | version = "2.1.1" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "bcbb6924530aa9e0432442af08bbcafdad182db80d2e560da42a6d442535bf85" 64 | dependencies = [ 65 | "anstyle", 66 | "bstr", 67 | "libc", 68 | "predicates", 69 | "predicates-core", 70 | "predicates-tree", 71 | "wait-timeout", 72 | ] 73 | 74 | [[package]] 75 | name = "bstr" 76 | version = "1.7.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" 79 | dependencies = [ 80 | "memchr", 81 | "regex-automata", 82 | "serde", 83 | ] 84 | 85 | [[package]] 86 | name = "capstone" 87 | version = "0.13.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "015ef5d5ca1743e3f94af9509ba6bd2886523cfee46e48d15c2ef5216fd4ac9a" 90 | dependencies = [ 91 | "capstone-sys", 92 | "libc", 93 | ] 94 | 95 | [[package]] 96 | name = "capstone-sys" 97 | version = "0.17.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0" 100 | dependencies = [ 101 | "cc", 102 | "libc", 103 | ] 104 | 105 | [[package]] 106 | name = "cc" 107 | version = "1.0.83" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 110 | dependencies = [ 111 | "libc", 112 | ] 113 | 114 | [[package]] 115 | name = "cfg-if" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 119 | 120 | [[package]] 121 | name = "clap" 122 | version = "4.5.53" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" 125 | dependencies = [ 126 | "clap_builder", 127 | "clap_derive", 128 | ] 129 | 130 | [[package]] 131 | name = "clap_builder" 132 | version = "4.5.53" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" 135 | dependencies = [ 136 | "anstream", 137 | "anstyle", 138 | "clap_lex", 139 | "strsim", 140 | ] 141 | 142 | [[package]] 143 | name = "clap_derive" 144 | version = "4.5.49" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" 147 | dependencies = [ 148 | "heck", 149 | "proc-macro2", 150 | "quote", 151 | "syn", 152 | ] 153 | 154 | [[package]] 155 | name = "clap_lex" 156 | version = "0.7.4" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 159 | 160 | [[package]] 161 | name = "colorchoice" 162 | version = "1.0.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 165 | 166 | [[package]] 167 | name = "crc32fast" 168 | version = "1.3.2" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 171 | dependencies = [ 172 | "cfg-if", 173 | ] 174 | 175 | [[package]] 176 | name = "difflib" 177 | version = "0.4.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 180 | 181 | [[package]] 182 | name = "either" 183 | version = "1.9.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 186 | 187 | [[package]] 188 | name = "elfx86exts" 189 | version = "0.0.0-dev.0" 190 | dependencies = [ 191 | "assert_cmd", 192 | "capstone", 193 | "clap", 194 | "escargot", 195 | "memmap", 196 | "object", 197 | ] 198 | 199 | [[package]] 200 | name = "escargot" 201 | version = "0.5.15" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "11c3aea32bc97b500c9ca6a72b768a26e558264303d101d3409cf6d57a9ed0cf" 204 | dependencies = [ 205 | "log", 206 | "serde", 207 | "serde_json", 208 | ] 209 | 210 | [[package]] 211 | name = "flate2" 212 | version = "1.0.27" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" 215 | dependencies = [ 216 | "crc32fast", 217 | "miniz_oxide", 218 | ] 219 | 220 | [[package]] 221 | name = "heck" 222 | version = "0.5.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 225 | 226 | [[package]] 227 | name = "itertools" 228 | version = "0.11.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" 231 | dependencies = [ 232 | "either", 233 | ] 234 | 235 | [[package]] 236 | name = "itoa" 237 | version = "1.0.9" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 240 | 241 | [[package]] 242 | name = "libc" 243 | version = "0.2.149" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" 246 | 247 | [[package]] 248 | name = "log" 249 | version = "0.4.20" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 252 | 253 | [[package]] 254 | name = "memchr" 255 | version = "2.6.4" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 258 | 259 | [[package]] 260 | name = "memmap" 261 | version = "0.7.0" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" 264 | dependencies = [ 265 | "libc", 266 | "winapi", 267 | ] 268 | 269 | [[package]] 270 | name = "miniz_oxide" 271 | version = "0.7.1" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 274 | dependencies = [ 275 | "adler", 276 | ] 277 | 278 | [[package]] 279 | name = "object" 280 | version = "0.38.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "b8b28f24bd43920cd8e0bc4f9c6553e8b93221c512cb9a1014987fc89d36f830" 283 | dependencies = [ 284 | "flate2", 285 | "memchr", 286 | "ruzstd", 287 | ] 288 | 289 | [[package]] 290 | name = "predicates" 291 | version = "3.0.4" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" 294 | dependencies = [ 295 | "anstyle", 296 | "difflib", 297 | "itertools", 298 | "predicates-core", 299 | ] 300 | 301 | [[package]] 302 | name = "predicates-core" 303 | version = "1.0.6" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" 306 | 307 | [[package]] 308 | name = "predicates-tree" 309 | version = "1.0.9" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" 312 | dependencies = [ 313 | "predicates-core", 314 | "termtree", 315 | ] 316 | 317 | [[package]] 318 | name = "proc-macro2" 319 | version = "1.0.69" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" 322 | dependencies = [ 323 | "unicode-ident", 324 | ] 325 | 326 | [[package]] 327 | name = "quote" 328 | version = "1.0.33" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 331 | dependencies = [ 332 | "proc-macro2", 333 | ] 334 | 335 | [[package]] 336 | name = "regex-automata" 337 | version = "0.4.1" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" 340 | 341 | [[package]] 342 | name = "ruzstd" 343 | version = "0.8.1" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c" 346 | dependencies = [ 347 | "twox-hash", 348 | ] 349 | 350 | [[package]] 351 | name = "ryu" 352 | version = "1.0.15" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 355 | 356 | [[package]] 357 | name = "serde" 358 | version = "1.0.188" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" 361 | dependencies = [ 362 | "serde_derive", 363 | ] 364 | 365 | [[package]] 366 | name = "serde_derive" 367 | version = "1.0.188" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" 370 | dependencies = [ 371 | "proc-macro2", 372 | "quote", 373 | "syn", 374 | ] 375 | 376 | [[package]] 377 | name = "serde_json" 378 | version = "1.0.107" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" 381 | dependencies = [ 382 | "itoa", 383 | "ryu", 384 | "serde", 385 | ] 386 | 387 | [[package]] 388 | name = "strsim" 389 | version = "0.11.0" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" 392 | 393 | [[package]] 394 | name = "syn" 395 | version = "2.0.38" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" 398 | dependencies = [ 399 | "proc-macro2", 400 | "quote", 401 | "unicode-ident", 402 | ] 403 | 404 | [[package]] 405 | name = "termtree" 406 | version = "0.4.1" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" 409 | 410 | [[package]] 411 | name = "twox-hash" 412 | version = "2.1.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "e7b17f197b3050ba473acf9181f7b1d3b66d1cf7356c6cc57886662276e65908" 415 | 416 | [[package]] 417 | name = "unicode-ident" 418 | version = "1.0.12" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 421 | 422 | [[package]] 423 | name = "utf8parse" 424 | version = "0.2.1" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 427 | 428 | [[package]] 429 | name = "wait-timeout" 430 | version = "0.2.0" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 433 | dependencies = [ 434 | "libc", 435 | ] 436 | 437 | [[package]] 438 | name = "winapi" 439 | version = "0.3.9" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 442 | dependencies = [ 443 | "winapi-i686-pc-windows-gnu", 444 | "winapi-x86_64-pc-windows-gnu", 445 | ] 446 | 447 | [[package]] 448 | name = "winapi-i686-pc-windows-gnu" 449 | version = "0.4.0" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 452 | 453 | [[package]] 454 | name = "winapi-x86_64-pc-windows-gnu" 455 | version = "0.4.0" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 458 | 459 | [[package]] 460 | name = "windows-sys" 461 | version = "0.48.0" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 464 | dependencies = [ 465 | "windows-targets", 466 | ] 467 | 468 | [[package]] 469 | name = "windows-targets" 470 | version = "0.48.5" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 473 | dependencies = [ 474 | "windows_aarch64_gnullvm", 475 | "windows_aarch64_msvc", 476 | "windows_i686_gnu", 477 | "windows_i686_msvc", 478 | "windows_x86_64_gnu", 479 | "windows_x86_64_gnullvm", 480 | "windows_x86_64_msvc", 481 | ] 482 | 483 | [[package]] 484 | name = "windows_aarch64_gnullvm" 485 | version = "0.48.5" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 488 | 489 | [[package]] 490 | name = "windows_aarch64_msvc" 491 | version = "0.48.5" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 494 | 495 | [[package]] 496 | name = "windows_i686_gnu" 497 | version = "0.48.5" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 500 | 501 | [[package]] 502 | name = "windows_i686_msvc" 503 | version = "0.48.5" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 506 | 507 | [[package]] 508 | name = "windows_x86_64_gnu" 509 | version = "0.48.5" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 512 | 513 | [[package]] 514 | name = "windows_x86_64_gnullvm" 515 | version = "0.48.5" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 518 | 519 | [[package]] 520 | name = "windows_x86_64_msvc" 521 | version = "0.48.5" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 524 | --------------------------------------------------------------------------------