21 个版本
0.1.20 | 2024 年 7 月 14 日 |
---|---|
0.1.19 | 2024 年 6 月 3 日 |
0.1.17 | 2024 年 5 月 28 日 |
0.1.15 | 2024 年 4 月 28 日 |
0.1.6 | 2024 年 2 月 26 日 |
#118 in 开发工具
144 每月下载量
用于 wechat-multi
11MB
309K SLoC
指令解码器生成器
关于此项目
本项目包含一个库和工具,可以从 ISA 的某些标准描述中生成基于决策树的指令解码器。对于反汇编器、调试器、模拟器、VMM 和其他类似工具,高效且精确的指令解码是一个相当常见的目标。
要解码的指令越多,任务就越繁琐,错误的可能性就越大。因此,人们可能会考虑从可机器读取的描述中 生成 解码器。
ISA 描述从 JSON 文件中读取(仓库中有一个示例:aarch64.json,有超过 3,000
条指令可以操作),算法假设固定长度的 32 位编码。该文件由 opcodes-lab 仓库中的工具生成。生成的解码器可以通过条件语句(默认)、DFS 表或 BFS 表驱动。在大小和速度之间有一个权衡,可以找到最适合他们的方案。
默认选项在开发用的笔记本电脑上至少提供 250 MiB/sec 的解码速度。
在指令被识别(解码)后,可能需要将其格式化为某种字符串。目前正在添加类似于反汇编器使用的指令格式,预计将在版本 0.2.0
中实现。详细的进展情况总结如下 这里。高级状态是,与 LLVM 和 binutils 匹配的格式化工作适用于以下组中的指令
- 算术
- 数据处理(一些别名可能尚未使用,使用基本指令形式)
- 逻辑
- 位操作(一些别名可能尚未使用,使用基本指令形式)
- 分支(无条件、条件、测试位和分支)
- 条件
- 异常
- 加载/存储
- 移动
- PC 相对
- 系统指令(提示、系统寄存器)
- 浮点
- SIMD, SVE
- SME
当前的工作重点是提供对指令操作数进行结构匹配的能力。
安装工具
cargo install disarm64
作为库使用
cargo add disarm64
cargo add disarm64_defn
在你的酷代码中,可能可以从以下代码片段开始
use disarm64::decoder;
use disarm64_defn::defn::InsnOpcode;
fn neato() {
let insn = decoder::decode(0x11000000).unwrap();
println!("Instruction: {insn:?}");
println!("Formatted: {insn}");
println!("Definition: {:?}", insn.definition());
}
默认情况下,库构建为支持完整的已知指令集。还有一些子集可用,作为 crate 功能以节省编译时间和代码大小
# Other features are `exception` and `system`
disarm64 = { version = "0.1", default_features = false, features = ["load_store"] }
使用工具
解码器
解码器可以从命令行、平面二进制文件、ELF 文件、PE 文件或 Mach-O 文件解码 ARM64 指令
disarm64 --help
Usage: disarm64 [OPTIONS] <COMMAND>
Commands:
insn Instructions to decode (hex 32-bit), can specify multiple instructions separated by commas
bin Flat binary file with instructions to decode, can specify offset and count
elf ELF file with instructions to decode
mach-o Mach-O file with instructions to decode
pe PE file with instructions to decode
help Print this message or the help of the given subcommand(s)
Options:
-v... Log level/verbosity; repeat (-v, -vv, ...) to increase the verbosity
--benchmark <BENCHMARK> Benchmark mode: measure the time to decode the instructions.
This is not a synthetic benchmark, it provides an estimate
of the real-world performance
[possible values: decode, format]
-h, --help Print help
生成器
此工具可以从 ISA 的 JSON 描述生成指令解码器。
disarm64_gen --help
Usage: disarm64_gen [OPTIONS] <DESCRIPTION_JSON>
Arguments:
<DESCRIPTION_JSON>
A JSON file with the description of the instruction set architecture
Options:
-a, --algo <ALGO>
Decoder algorithm style, defaults to conditionals
Possible values:
- cond: Conditionals
- dfs: DFS table-driven
- bfs: BFS table-driven
-f, --feature-sets <FEATURE_SETS>...
Include filter for feature sets, e.g. "v8,simd". Case-insensitive, ignored if not provided
-c, --insn-class <INSN_CLASS>...
Include filter for instruction classes, e.g. "addsub_imm,ldst_pos,exception". Case-insensitive, ignored if not provided
-m, --mnemonic <MNEMONIC>...
Include filter for mnemonics, e.g. "adc,ldp". Case-insensitive, ignored if not provided
-g, --graphviz <GRAPHVIZ>
Output the decision tree to a Graphviz DOT file
-r, --rs-file <RS_FILE>
Generate the decoder implemented in Rust
-t, --test-bin <TEST_BIN>
Generate a test binary
--test-bin-size-limit <TEST_BIN_SIZE_LIMIT>
The size limit of the generated test binary, the default is 64MB
[default: 67108864]
--test-encodings-limit <TEST_ENCODINGS_LIMIT>
The number of test encodings to generate for each instruction, the default is 0x10_000
[default: 65536]
-v...
Log level/verbosity; repeat (-v, -vv, ...) to increase the verbosity
-h, --help
Print help (see a summary with '-h')
指令类和功能集
要了解 ISA 描述中可用的类和功能集,请运行
disarm64_gen ./aarch64.json -c -
或
disarm64_gen ./aarch64.json -f -
示例
生成 Rust 实现的解码器
对于整个已知指令集
disarm64_gen ./aarch64.json -r decoder.rs
如果只需要解码整个指令集的子集,请适当使用过滤器。例如,要生成 V8 加载/存储指令的解码器,请执行
disarm64_gen ./aarch64.json -c ldst_imm10,ldst_imm9,ldst_pos,ldst_regoff,ldst_unpriv,ldst_unscaled,ldstexcl,ldstnapair_offs,ldstpair_indexed,ldstpair_off,loadlit -f v8 -r decoder.rs
使用解码器
解码命令行上的指令
disarm64 -v insn 0x1a000001,0xa,0xa,0xa,0xa
[DEBUG] Decoded instruction: ADDSUB_CARRY(ADC_Rd_Rn_Rm(ADC_Rd_Rn_Rm { rd: 00000001, rn: 00000000, rm: 00000000 }))
[DEBUG] 0x1a000001: Insn { mnemonic: "adc", aliases: [], opcode: 1a000000, mask: 7fe0fc00, class: ADDSUB_CARRY, feature_set: V8, operands: [InsnOperand { kind: Rd, class: INT_REG, qualifiers: [W, X], bit_fields: [BitfieldSpec { bitfield: Rd, lsb: 00000000, width: 00000005 }] }, InsnOperand { kind: Rn, class: INT_REG, qualifiers: [W, X], bit_fields: [BitfieldSpec { bitfield: Rn, lsb: 00000005, width: 00000005 }] }, InsnOperand { kind: Rm, class: INT_REG, qualifiers: [W, X], bit_fields: [BitfieldSpec { bitfield: Rm, lsb: 00000010, width: 00000005 }] }], flags: InsnFlags(HAS_SF_FIELD) }
[INFO ] 0x000000: 1a000001 adc w1, w0, w0
[DEBUG] Decoded instruction: EXCEPTION(UDF_UNDEFINED(UDF_UNDEFINED { imm16_0: 0000000a }))
[DEBUG] 0x00000a: Insn { mnemonic: "udf", aliases: [], opcode: 00000000, mask: ffff0000, class: EXCEPTION, feature_set: V8, operands: [InsnOperand { kind: UNDEFINED, class: IMMEDIATE, qualifiers: [], bit_fields: [BitfieldSpec { bitfield: imm16_0, lsb: 00000000, width: 00000010 }] }], flags: InsnFlags(0x0) }
[INFO ] 0x000004: 0000000a udf #0xa
[DEBUG] Decoded instruction: EXCEPTION(UDF_UNDEFINED(UDF_UNDEFINED { imm16_0: 0000000a }))
[DEBUG] 0x00000a: Insn { mnemonic: "udf", aliases: [], opcode: 00000000, mask: ffff0000, class: EXCEPTION, feature_set: V8, operands: [InsnOperand { kind: UNDEFINED, class: IMMEDIATE, qualifiers: [], bit_fields: [BitfieldSpec { bitfield: imm16_0, lsb: 00000000, width: 00000010 }] }], flags: InsnFlags(0x0) }
[INFO ] 0x000008: 0000000a udf #0xa
[DEBUG] Decoded instruction: EXCEPTION(UDF_UNDEFINED(UDF_UNDEFINED { imm16_0: 0000000a }))
[DEBUG] 0x00000a: Insn { mnemonic: "udf", aliases: [], opcode: 00000000, mask: ffff0000, class: EXCEPTION, feature_set: V8, operands: [InsnOperand { kind: UNDEFINED, class: IMMEDIATE, qualifiers: [], bit_fields: [BitfieldSpec { bitfield: imm16_0, lsb: 00000000, width: 00000010 }] }], flags: InsnFlags(0x0) }
[INFO ] 0x00000c: 0000000a udf #0xa
[DEBUG] Decoded instruction: EXCEPTION(UDF_UNDEFINED(UDF_UNDEFINED { imm16_0: 0000000a }))
[DEBUG] 0x00000a: Insn { mnemonic: "udf", aliases: [], opcode: 00000000, mask: ffff0000, class: EXCEPTION, feature_set: V8, operands: [InsnOperand { kind: UNDEFINED, class: IMMEDIATE, qualifiers: [], bit_fields: [BitfieldSpec { bitfield: imm16_0, lsb: 00000000, width: 00000010 }] }], flags: InsnFlags(0x0) }
[INFO ] 0x000010: 0000000a udf #0xa
可视化决策树
disarm64_gen ./aarch64.json -c exception -g dt-exception.dot
disarm64_gen ./aarch64.json -f v8 -g dt-v8.dot
disarm64_gen ./aarch64.json -c ic_system -g dt-system.dot
disarm64_gen ./aarch64.json -c ldst_imm10,ldst_imm9,ldst_pos,ldst_regoff,ldst_unpriv,ldst_unscaled,ldstexcl,ldstnapair_offs,ldstpair_indexed,ldstpair_off,loadlit -f v8 -g dt-ldst.dot
然后(假设已安装 Graphviz 工具)
dot -Tpng dt-exception.dot -o dt-exception.png
dot -Tpng dt-v8.dot -o dt-v8.png
dot -Tpng dt-system.dot -o dt-system.png
dot -Tpng dt-ldst.dot -o dt-ldst.png
将 dot
文件渲染为 png
图像。圆圈中的数字表示要检查的位;在矩形中,有要检查的指令和操作码。
示例(Aarch64)
- 异常指令:dt-exception.png
- V8 指令(无 SIMD,无别名):dt-v8.png
- 系统指令:dt-system.png
- V8 加载和存储指令:dt-ldst.png
调试输出
disarm64_gen ./aarch64.json -c ldst_pos -m ldr -v
[INFO ] Loading "./aarch64.json"
[INFO ] Including instructions from all feature sets
[INFO ] Including instructions from classes {LDST_POS}
[INFO ] Including instructions with mnemonics {"ldr"}
[DEBUG] instruction Insn { mnemonic: "ldr", opcode: 3d400000, mask: 3f400000, class: LDST_POS, feature_set: V8, operands: {ADDR_UIMM12: InsnOperand { class: ADDRESS, qualifiers: [S_B], bit_fields: [BitfieldSpec { bitfield: Rn, lsb: 5, width: 5 }, BitfieldSpec { bitfield: imm12, lsb: a, width: c }] }, Ft: InsnOperand { class: FP_REG, qualifiers: [S_B], bit_fields: [BitfieldSpec { bitfield: Rt, lsb: 0, width: 5 }] }}, flags: InsnFlags(0x0), index: 37d }
[DEBUG] instruction Insn { mnemonic: "ldr", opcode: b9400000, mask: bfc00000, class: LDST_POS, feature_set: V8, operands: {Rt: InsnOperand { class: INT_REG, qualifiers: [W], bit_fields: [BitfieldSpec { bitfield: Rt, lsb: 0, width: 5 }] }, ADDR_UIMM12: InsnOperand { class: ADDRESS, qualifiers: [S_S], bit_fields: [BitfieldSpec { bitfield: Rn, lsb: 5, width: 5 }, BitfieldSpec { bitfield: imm12, lsb: a, width: c }] }}, flags: InsnFlags(HAS_ADVSIMV_GPRSIZE_IN_Q), index: 382 }
[DEBUG] Classes {LDST_POS}
[DEBUG] Feature sets {V8}
[INFO ] Processed 3323 instructions, skipped 200 aliases, 1 classes, 1 feature sets filtered out 3321 instructions
[INFO ] Loaded 2 instructions
[DEBUG] Building decision tree at depth 1
[DEBUG] mask: 3f400000
[DEBUG] decision bit: 22
[DEBUG] decision mask: 400000
[DEBUG] zero: 0, one: 2
[DEBUG] mask: 3f000000
[DEBUG] decision bit: 24
[DEBUG] decision mask: 1000000
[DEBUG] zero: 0, one: 2
[DEBUG] mask: 3e000000
[DEBUG] decision bit: 25
[DEBUG] decision mask: 2000000
[DEBUG] zero: 2, one: 0
[DEBUG] mask: 3c000000
[DEBUG] decision bit: 26
[DEBUG] decision mask: 4000000
[DEBUG] zero: 1, one: 1
[DEBUG] Building decision tree at depth 2
[DEBUG] One instruction at depth 1
[DEBUG] Building decision tree at depth 2
[DEBUG] One instruction at depth 1
[DEBUG] Decision tree built at depth 0
测试
有关 disarm64 与 LLVM 以及 diasrm64 与 binutils 之间的测试夹具和反汇编差异,请参阅 disarm64_test_data。
ARM A64 ISA 参考材料
相关艺术
此项目没有任何声名鹊起之处。它使用众所周知的算法和方法来生成指令解码器和反汇编器,似乎只有一些微小的变化:从 JSON 文件读取 ISA 描述,并生成无指针、无不安全块和无内存分配的强类型 Rust 解码器。也许,您会喜欢生成指令集的一部分解码器的功能,这可以使代码大小更小。
以下是其他涉及机器指令解码主题的项目
- 毕业设计及其LLVM TableGen分支
- LLVM与TableGen
- Qemu - 快速模拟器
- Unicorn
- Binutils与libopcode
- Radare2
- Rizin
- Binary Ninja ARM64插件
上述内容具有更广泛的范围,其中一些提供各种语言的绑定。
不是以库/API为中心,却是唯一的
尽管只针对x86_64,但仍然非常出色
关于此主题及其密切相关主题的一些论文
- C.S. Collberg. "逆向解释 + 变异分析 = 自动重定位",ACM SIG-PLAN 1997年编程语言设计与实现会议论文集", 1997年,第57-70页。
- C.S. Collberg. "自动推导编译器机器描述",ACM Trans. Program. Lang. Syst.,2002年,第24卷,第4期,第369-408页。
- W.C. Hsieh,D.R. Engler,G. Back. "逆向工程指令编码",通用轨道会议记录:2002年USENIX年度技术会议",2001年,第133-145页。
- R. Krishna,T. Austin. "高效的软件解码器设计",IEEE计算机学会计算机体系结构技术委员会通讯,2001年。
- H. Theiling. "为解码二进制生成决策树",ACM SIGPLAN嵌入式系统语言、编译器和工具研讨会论文集",2001年,第112-120页。
- W. Qin,S. Malik. "为可重定位软件工具包自动合成高效的二进制解码器",第40届年度设计自动化会议论文集",2003年,第764-769页。
法律
所有商标均为各自所有者的财产。本项目的内内容可在MIT许可证的条款下使用。MIT许可证。
依赖项
~3.5–4.5MB
~85K SLoC