├── .gitignore ├── CHANGELOG ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── build.rs ├── ffi ├── .cargo │ └── config ├── .gitignore ├── Cargo.toml ├── README.md ├── long_mode │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── multiarch │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── long_mode.rs │ │ └── protected_mode.rs ├── protected_mode │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── real_mode │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── rust-toolchain └── src │ ├── long_mode.rs │ ├── protected_mode.rs │ ├── real_mode.rs │ └── write_sink.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ ├── decode_does_not_panic.rs │ ├── display_c_does_not_panic.rs │ ├── display_does_not_panic.rs │ ├── displaysink_used_correctly.rs │ ├── does_not_decode_invalid_registers.rs │ ├── instruction_text_buffer_size_ok.rs │ └── small_reg_is_always_old_bank_if_possible.rs ├── goodfile ├── src ├── isa_settings.rs ├── lib.rs ├── long_mode │ ├── display.rs │ ├── evex.rs │ ├── evex │ │ └── generated.rs │ ├── isa_settings.rs │ ├── mod.rs │ ├── uarch.rs │ └── vex.rs ├── protected_mode │ ├── display.rs │ ├── evex.rs │ ├── isa_settings.rs │ ├── mod.rs │ ├── uarch.rs │ └── vex.rs ├── real_mode │ ├── display.rs │ ├── evex.rs │ ├── isa_settings.rs │ ├── mod.rs │ ├── uarch.rs │ └── vex.rs └── shared │ ├── evex.in │ └── generated_evex.in └── test ├── bench.rs ├── lib_test.rs ├── long_mode ├── descriptions.rs ├── display.rs ├── evex_generated.rs ├── mod.rs ├── opcode.rs ├── operand.rs ├── regspec.rs └── reuse_test.rs ├── protected_mode ├── display.rs ├── evex_generated.rs ├── mod.rs ├── opcode.rs ├── operand.rs └── regspec.rs ├── real_mode ├── mod.rs ├── opcode.rs └── operand.rs └── test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | ## 2.1.0 2 | 3 | * `InstDecoder` for all execution modes now has many more feature flags. 4 | * `InstDecoder::default` still constructs a decoder that decodes all supported instructions. 5 | * more ISA extensions can be individually selected for decoding support, though note 6 | not all feature flags are fully implemented. in such cases, instructions will be decoded 7 | even when their corresponding extension is not selected. 8 | * added uarch-specific decoders for Zen 2, Zen 3, Zen 4, and Zen 5 9 | * removed 3DNow support from AMD uarch-specific decoders after K10 10 | 11 | ## 2.0.0 12 | 13 | * upgrade to `yaxpeax-arch 0.3.1`, which brings with it a deprecation of the 14 | `Colorize` and `ShowContextual` traits. 15 | * because common use of yaxpeax-x86 involves using both this crate and 16 | `yaxpeax-arch`, moving to a newer major version of `yaxpeax-arch` is a major 17 | version bump of `yaxpeax-x86` as well. so, 2.0.0! 18 | 19 | changes: 20 | 21 | * `Operand` variants have had their naming made more consistent. 22 | - many variants starting with "Reg" actually describe a memory access. they 23 | now begin with "Mem" instead. 24 | - several variants mentioned "Scale" in their name, but not "Index", even 25 | though they use an index register. they now do. 26 | - several variants mentioned their constituent parts out of order. for 27 | example, "RegIndexBaseScaleDisp", even though the parts were specified as 28 | base, then index, then scale, then displacement. these names have been 29 | adjusted to reflect the order of their fields, which is roughly the order 30 | those fields are shown when printed. 31 | - `DisplacementU*` operands have always been access to memory at the absolute 32 | address they specify. their names are now `AbsoluteU*` 33 | * `Operand`, across the board, now uses struct-style enum variants, rather than tuple-style. 34 | * the two changes together mean an operand that was 35 | `RegIndexBaseScaleDisp(reg, reg, u8, i32)` 36 | is now 37 | `MemBaseIndexScaleDisp { base, index, scale, disp }` 38 | and similar for other variants. 39 | * two operand kinds, and their masked variants, were never actually constructed, and have been deleted. 40 | - long ago yaxpeax-x86 returned different Operand variants when an index 41 | register was used with scale 1, to hint that no scaling actually occurred. 42 | this was eventually changed to return a scaling Operand variant with 43 | scale==1, but the old variants remained. 44 | - RegIndexBase has been removed 45 | - RegIndexBaseDisp has been removed 46 | * `Prefixes::selects_cs()` has been moved to `Prefixes::cs()`, and the old 47 | useless functions are no more. `inst.prefixes().cs()` is finally a reasonable 48 | way to determine if an instruction reads or writes through the cs prefix. 49 | 50 | fixes: 51 | 52 | * fix 32-bit call/jmp not respecting 66 prefix if set - such cases use 16-bit 53 | operands, but decoded as if they used 32-bit operands. 54 | 55 | ## 1.2.2 56 | 57 | * fix `hreset` reporting two operands, with a second operand of `Nothing`. 58 | this is not exactly a *bug*, there is in fact no second operand and libraries 59 | probably ought to handle `Nothing` identically to having no operand reported 60 | at all. but `yaxpeax-x86` intends to not report superfluous operands. 61 | 62 | ## 1.2.1 63 | 64 | * fix incorrect register numbers used in `RegSpec::r12()` and `RegSpec::r13()` 65 | and smaller-size variants (thank you @tokatoka!) 66 | * fix synonyms of registers from `rex.w` on byte-size operands with register 67 | number <4 68 | * adjust misleading docs on `register_class::B` and `register_class::RB`: being 69 | in register class `B` does not imply that there was no `rex.w` on the 70 | instruction - `rex.w` may have been present but the register number may have 71 | been <4 such that `B` was an appropriate register class anyway. 72 | * fix missing memory access size text for 128-bit keylocker instructions (would 73 | have text like "aesdec128kl xmm0, BUG [rcx]" rather than "..., m384b [rcx]". 74 | instructions are otherwise decoded correctly.) 75 | * fix incorrect register selection for `vpmovm2*` with `rex.b` set (would select 76 | registers "k8" through "k15", but should be masked down to "k0".."k7".) 77 | * fix incorrect register selection for `vpbroadcastm{b2q,w2d}` with `rex.b` 78 | set. basically the same bug as `vpmovm2*`; would select registers "k8".."k15", 79 | but should have been masked to "k0".."k7". 80 | * fix incorrect register selection for `vpmov*2m` with `rex.r` set. similar to 81 | above, except 64-bit only: in 32- and 16-bit modes, this case was and continues 82 | to be a `bound` instruction. in 64-bit mode, this would disassemble as a 83 | `vpmov*2m` with nonsense `k8..k15` selected. to real hardware, this bit 84 | sequence is an invalid instruction, and so it is now invalid to yaxpeax-x86 85 | as well. 86 | * fix incorrect `RegisterBank` names in `long_mode`. in a previous 87 | reorganization register bank numbers were changed and the names were updated to 88 | match for `protected_mode` and `real_mode`, but `long_mode` was overlooked. 89 | 90 | ## 1.2.0 91 | * fix incorrect old yaxpeax-arch version selection for ffi crates 92 | * address incorrect behavior of `Prefixes::cs()` 93 | * this "getter" would in fact set the selected segment to `cs`. it has never 94 | actually returned a `bool`, like other selector-checking methods on 95 | `Prefixes`. 96 | * add `Opcode::is_jcc`, `Opcode::is_setcc`, and `Opcode::is_cmovcc` helpers 97 | * discovered that `jna` and `jnb` are named what they are, instead of 98 | `jbe`/`jae` like their `setcc` an `cmovcc` counterparts. sorry. these will 99 | become `jbe` and `jae` in 2.x. 100 | * fix incorrect decode of a0/a1/a2/a3 mov register when rex.b is set 101 | (rex.b would select register 8, but the register is unconditionally A) 102 | * fix incorrect handling of some variants of gather instructions, 103 | vpgatherdq, vpgatherqq, vgatherdps, vgatherdpd, vgatherqps, vgatherqpd. 104 | errors were any of: 105 | * reporting qword loads when loads are dword-wide 106 | * reporting dword loads when loads are qword-wide 107 | * reporting ymm register sizes when sizes are actually xmm 108 | * reporting xmm register sizes when sizes are actually ymm 109 | * reporting index register as ymm when it is actually xmm 110 | * extended support for some newer instructions (pconfig, movdir64b) from just 111 | 64-bit to all decode modes 112 | * fix cvttsd2si/cvtsd2si reporting xmm registers as destinations in 64-bit mode 113 | * fix inconsistently-reported memory access size of vcvt{,t}{sd,si} across 114 | encodings and bitness 115 | * in some cases, instructions loading a single-precision float reported 8-byte loads 116 | * in some cases, instructions loading a double-precision float reported 4-byte loads 117 | * fix register sizes for lar/lsl 118 | * 16 bits are read from the source register, but x86 docs state that the 119 | source register is written as 16-bit, 32-bit, or 64-bit, as prefixes dictate. 120 | memory is always written as `word [addr]`, which was correct before and 121 | remains the case. 122 | 123 | ## 1.1.5 124 | * fix several typos across crate docs - thank you Bruce! (aka github user waywardmonkeys) 125 | * optimizations (mostly code motion) for hot codepaths 126 | - large `match`-based decode tables have been outlined to 256-entry arrays. 127 | this makes for slicely nicer inlining in `read_with_annotations`. 128 | - vex/evex decoding in 64-bit decoding now shares more code. this seems to 129 | aid code cache friendliness when prefixes must be read. 130 | - added a fast path for operand reading for the more-likely cases of 131 | [64-bit]: {0x66,rex}{,0x0f-} 132 | [32-bit]: {0x66}{,0x0f-} 133 | [16-bit]: {0x66}{,0x0f-} 134 | 135 | in particular, this avoids checking for instruction length overflows and 136 | some bounds checks when we aren't handling a pessimal case of many-prefixed 137 | instructions. if an instruction has multiple prefixes, decoders fall back 138 | to normal read-in-a-loop-until-length-limit-reached decoding. 139 | * `Makefile` at the crate root now exercises `yaxpeax-x86` builds and tests under: 140 | - default features (fmt, std) 141 | - no-std + fmt 142 | - no-std and no-fmt 143 | * fix several issues prohibiting builds of the crate with no-std +fmt 144 | configurations; the required Display impl for field annotation would simply not 145 | compile in no-fmt builds. it is now a minimal implementation to comply with the 146 | goals of `no-fmt`: "avoid as much extra code and data for formatting 147 | instructions as possible". two dozen bytes for a likely-DCE'd codepath should 148 | be ok. 149 | * adjust test structure so that exhaustive tests can be `#[ignored]` and 150 | explicitly run anyway for completeness. this means the ignored at&t tests now 151 | are both ignored and appear to succeed when run. 152 | * support `9a` encoding of `callf` with absolute segment/address operand 153 | - this encoding is only present in 32-bit and 16-bit modes 154 | 155 | ## 1.1.4 156 | * fix reachable unreachable under `DisplayStyle::C` in 64-, 32-, and 16-bit modes 157 | * add fuzz target to cover `DisplayStyle::C` formatter for 64-, 32-, and 16-bit modes 158 | 159 | ## 1.1.3 160 | * fix reachable unsoundness via `RegSpec` helper functions 161 | - helpers should only permit creating valid `RegSpec` structs, but three 162 | helpers would permit out-of-range register numbers; `mask` registers in 16- 163 | and 32-bit modes, and `rex byte` registers in 64-bit modes. 164 | - when displaying an invalid `RegSpec`, for some out-of-range mask registers, 165 | the displayed register name could be chosen as arbitrary const data 166 | interpreted as a pointer/length pair 167 | * fix incorrect (non-present!) memory size for f30f1e-style `nop`. 168 | - this would decode without error, but produce an instruction with memory 169 | operand and memory size of `0`. if formatted, yaxpeax-x86 panics. 170 | * add in-tree `cargo fuzz` targets for decoding and displaying instructions. 171 | neither of these operations should ever panic. 172 | * fix incorrect disassembler used in x86-16 doc comments 173 | 174 | and thank you to @5225225 (https://github.com/5225225) for the bug reports 175 | handled in 1.1.2 and 1.1.3, as well as the nudge to start using `cargo fuzz`. 176 | 177 | ## 1.1.2 178 | * fix panic when evex instructions with compressed displacements are decoded in 179 | debug builds 180 | 181 | ## 1.1.1 182 | * support `endbr64` and `endbr32` 183 | - these are interpretations of `nop` (`0f1e` wide nop), so the only issue 184 | with for users <1.1.1 will be `yaxpeax-86` decoding `nop` instead. 185 | * export `InstructionDisplayer` 186 | this fixes an issue where crate docs would have dead links for the return 187 | value of public APIs. it also allows callers to write name of what 188 | `display_with` returns in their code, which hopefully isn't too useful but 189 | shouldn't be impossible. 190 | 191 | ## 1.1.0 192 | 193 | * implement `AnnotatingDecoder` from `yaxpeax-arch=0.2.6` and later. 194 | this is a relatively involved addition. for rustc reasons, there are several 195 | additional `inline(always)` attributes applied to keep non-annotating decoder 196 | calls yielding the same generated code (and performance) as before. 197 | 198 | annotations are produced for much but not all of 16-, 32-, and 64-bit x86, 199 | describing prefixes, opcodes, operand encoding, and for more common 200 | instructions, operand encoding as well. descriptions provided are described 201 | by the `FieldDescription` struct in all architectures. `id` generally matches 202 | some kind of parse order for the instruction, typically the order that 203 | `yaxpeax-x86` considers bit fields in decoding an instruction. prefixes will 204 | have lower id than opcodes, opcodes will have lower id than operands, 205 | immediates will have the highest id due to being last values read in an 206 | instruction. 207 | 208 | between prefixes, opcodes, and operands, "Boundary" field descriptions are 209 | reported as a hint to library clients that a logical grouping of descriptions 210 | has ended. 211 | 212 | * `pub const fn` builders for all general-purpose registers, segment registers, and ip/flags registers. 213 | - this corrects a spotty and inconsistent set of builders filled in on-demand. 214 | * `DisplayStyle::Intel` now shows relative offsets as `$+0xXX`, rather than `0xXX`. 215 | - this corrects an ambiguity with instructions like `jz 0x1234`, where `jz` 216 | on x86 is _only_ relative branches, but the displayed syntax is ambiguous 217 | about being a relative or absolute address. 218 | - `DisplayStyle::Intel` is how `impl Display for Instruction` works, so 219 | typical `Display` use is also fixed. 220 | * `push`, `pop`, `call`, and `ret` now report `mem_size` in all cases. 221 | - earlier, these instructions only reported a `mem_size` if their operand was a memory access. 222 | - for `call`, in 32- and 16-bit modes the reported memory size may describe 223 | the *read*, not the corresponding write of pushing `{e}ip` to the stack. 224 | documentation has been added to `mem_size` more specifically describing 225 | this circumstance. 226 | * correct `rex.b` incorrectly applying to the `*ax` register - `4f91` is `xchg rax, r9`, not `xchg r8, r9`. 227 | * correct `nop` incorrectly ignoring `rex.b` - `4190` is `xchg rax, r8`, not `nop`. 228 | * `DisplayStyle::C` now has rules to nicely display `jCC`, `jmp`, `call`, `loop*`, and `j*cxz` instructions. 229 | 230 | ## 1.0.4 231 | 232 | in 64-, 32-, and 16-bit modes: 233 | * fix incorrect decoding of `scas`; memory access is through `*di` not `*si`. 234 | * fix incorrect segment register for `scas` memory operand; `es` segment is always used. 235 | * fix incorrect decoding of some 67-prefixed string instructions: `movs`, `scas`, `lods`, `stos`, `cmps`. 236 | - a 67-prefix selects an alternate addressing mode. in 64-bit mode, this 237 | selects 32-bit registers for addressing, 32-bit selects 16-bit registers, 238 | and 16-bit selects 32-bit registers. the decoder had ignored the 67 prefix 239 | on these instructions. 240 | 241 | in 32- and 16-bit modes: 242 | * fix incorrect decoding of 16-bit memory accesses with modrm where mod=00 and mmm=110. 243 | - the memory access from this modrm is a disp16 memory access, which the 244 | decoder reports. the decoder would then not read the subsequent 16-bit 245 | displacement. this would typically result in a `Displacement(0)` operand, 246 | and incorrect following instructions. 247 | 248 | ## 1.0.3 249 | 250 | * fix a few broken doc links, added example of yaxpeax-x86 usage through yaxpeax-arch traits 251 | 252 | ## 1.0.2 253 | * remove a stale line from README 254 | 255 | ## 1.0.1 256 | * fix a broken docs link in README 257 | 258 | ## 1.0.0 259 | * `avx512` support 260 | * `avx2` support 261 | * `avx` support 262 | * real-mode (x86_16) support 263 | * ffi-friendly packaging of decoders for 16-bit, 32-bit, and multi-arch uses 264 | * added `Instruction::mem_size()` to query the size of an instruction's memory access 265 | * `xacquire`/`xrelease` support 266 | * `AMD` `sev_snp` support 267 | * `pconfig`/Total Memory Encryption support 268 | * `Intel` `keylocker` support 269 | * removed `MOVSX_b`, `MOVSX_w`, `MOVZX_b`, and `MOVZX_w` - these differentiations are now described by `mem_size` and the `MOVSX`/`MOVZX` opcodes generally 270 | * `PartialEq` impl for `Instruction` and all contained structures 271 | * expose more details of an instruction's prefixes, and which prefixes are actually present on the instruction 272 | 273 | ## 0.2.2 274 | * fix rendering error in `ShowContextual` impl with `&[Option]` overrides 275 | - would in some circumstances incorrectly print stale data if an `Instruction` was reused for decoding. 276 | no impact on logical correctness, but certainly made for awkward disassembly in some cases. 277 | 278 | ## 0.2.1 279 | * update `yaxpeax-arch` dep to 0.0.5 280 | - no external-facing changes for this, but `yaxpeax-arch 0.0.5` uses `crossterm` for cross-platform terminal coloring 281 | * clean up a few warnings that made it into 0.2.0? 282 | 283 | ## 0.2.0 284 | ### features! 285 | * fuzz against mishegos and fix many bugs that made obvious 286 | - duplicate and redundant f2, f3, and 66 prefixes on 0f-type opcodes are now 287 | handled "right", assuming xed as a source of truth. almost all of these cases 288 | are undefined by the intel and AMD manuals, but it seems unlikely that 289 | capstone is correct with respect to cpu interpretation while xed is 290 | incorrect. 291 | * public `enum`s are now `#[non_exhaustive]`. these are `Operand`, `Opcode`, and `DecodeError`. 292 | - `Operand` is not expected to vary, but might. 293 | - `Opcode` will grow new variants for every extension. 294 | - `DecodeError` will probably not change, but no guarantees. 295 | * add a notion of display styles for instructions, see `Instruction::display_with`. currently there are two styles: 296 | - `DisplayStyle::Intel` produces intel-like syntax for instructions 297 | - `DisplayStyle::C` produces C pseudocode-ish syntax for instructions 298 | - as an example, `xor eax, [rax]` is rendered as `eax ^= [rax]`. 299 | - `DisplayStyle::Att` is one potential future style, but not yet implemented 300 | * `fmt`-related code and the `display` module are now optional on the `fmt` feature 301 | - this is to support minimal builds for decoders in non-formatting circumstances. `yaxpeax-x86` long mode is still 65kb there. 302 | * improved packaging of ffi-friendly bindings in `ffi/` 303 | - architectures have standalone libraries for each of `long_mode`, `protected_mode`, and `real_mode` (last still to be implemented) 304 | - improved `ffi/` build instructions to describe how to build minimal-size `.so` and `.a` archives for linking 305 | * `ffi/multiarch` is intended to be a single package for all architectures, but currently does not fulfil this role 306 | 307 | ### decode fixes 308 | * segment prefixes (`cs`, `ds`, `ss`, `es`) are now properly ignored in long mode 309 | * `lock xchg` now decodes correctly (operands were in reversed order and so memory "destinations" were treated as memory sources) 310 | * some missing sse instructions are now supported (`blendps`, `blendpd`, `pclmulqdq`) 311 | * some missing avx instructions are now supported (`vorpd`, `vorps`, `vandpd`, `vandps`, `vandnpd`, `vandnps`, `vpmaxuw`) 312 | * prefetch instructions with register operand are interpreted as `nop`, not `#UD` 313 | * `mov` to control or debug registers that are statically known to `#UD` now produce `DecodeError::InvalidOperand` 314 | * `salc` is now rejected. it was accepted on a whim, and i am fickle. 315 | - realistically, this should be behind a decoder flag and accepted by 316 | default, but this makes fuzzing somewhat easier and isn't an instruction 317 | you'd expect to see in a modern x86 binary. see the summary at the end of 318 | this section for some thoughts on decoder flags... 319 | 320 | ### new ISA extension support! 321 | * 3dnow is 3d-supported-now 322 | * `sse4a` is now supported 323 | * `gfni` extensions are now supported 324 | * `ptwrite` extensions are now supported 325 | * `cet` (`c`ontrol-flow `e`nforcement `t`echnology) extensions are now supported 326 | * `invpcid` extensions are now supported 327 | * `tdx` extensions are now supported 328 | * `waitpkg` extensions are now supported 329 | * `uintr` extensions are now supported 330 | * `tsxldtrk` extensions are now supported 331 | * `ud0`, `ud1`, and `ud2` are now supported - `ud2e` which was never real, is actually `ud1` 332 | * `movdir` extensions are now supported 333 | * `key locker` extensions are now supported 334 | * `enqcmd` extensions are now supported 335 | 336 | ### architecture support 337 | * all above changes apply both to `long_mode` and `protected_mode`. 338 | `protected_mode` may accept some 64bit-only instructions incorrectly. beware. 339 | 340 | ### thoughts? 341 | folks, i'm out of feature bits in `InstDecoder`. there are too dang many x86 342 | extensions. in the happy case, rustc knows that the provided decoder always 343 | compares equal to `InstDecoder::default()` so it can be made an arbitrarily 344 | large byte array. but i continue to be in awe of how much they put in the 345 | computer. as things stand, new extensions are not categorized into 346 | `InstDecoder` flags, but since there is a way to implement additional bits 347 | without causing overhead in the happy path, this feature will probably continue 348 | being supported in the future. 349 | 350 | ### unsafe 351 | there are still a handful of `unsafe {}` uses in `yaxpeax-x86` for performance 352 | reasons. i tried removing arms in matches by making the last meaningful arm, 353 | often something like: 354 | ``` 355 | 8 => { /* handle 8-byte operand */ } 356 | ``` 357 | into a general catchall arm and deleting the `unreachable_unchecked`, like: 358 | ``` 359 | _ => { /* handle 8-byte operand */ } 360 | ``` 361 | but this also caused a ~5% regression in performance. this makes sense, since 362 | `unreachable_unchecked` is stronger in saying that other values (`3`, `5`, `6`, 363 | `7`, ...) will _not_ occur, but `_` doesn't disallow them and likely produces 364 | jump tables for no good reason. maybe this can be solved some other way, one 365 | day... 366 | 367 | ## 0.1.5 368 | * fix several issues around 0f01* opcode decoding; 369 | - AMD-only `monitorx`, `mwaitx`, `clzero`, and `rdpru` are now supported 370 | - `swapgs` is invalid in non-64-bit modes 371 | - `rdpkru` and `wrpkru` were incorrectly decoded when modrm bits were not `11` 372 | * small performance tweaks. read_imm_signed is now inline(always) and some 373 | pre-decode initialization is a bit better-packed 374 | * `xchg {r,e,}ax, reg` was not supported! it's supported now. 375 | 376 | ## 0.1.4 377 | * [long mode only]: fix decoding of rex-prefixed modrm+sib operands selecting index 0b100 and base 0b101 378 | - for memory operands with a base, index, and displacement either 379 | the wrong base would be selected (register number ignored, so only 380 | `*ax` or `r8*` would be reported), or yaxpeax-x86 would report a 381 | base register is present when it is not (`RegIndexBaseScaleDisp` 382 | when the operand is actually `RegScaleDisp`) 383 | 384 | thank you to Evan Johnson for catching and reporting this bug! 385 | 386 | ## 0.1.3 387 | * fix 0x80-opcode instructions not having an opcode 388 | - this meant that for example `lock xorb [rax], 0` would decode as invalid 389 | 390 | ## 0.1.2 391 | * expose constructors for `RegSpec` in both `long_mode` and `protected_mode` 392 | * expose a const `RegSpec::RIP` 393 | - most useful for matching `Operand::RegDisp(RegSpec::RIP, disp)` in patterns, really 394 | 395 | ## 0.1.1 396 | * add `long_mode::register_class` and `protected_mode::register_class` where 397 | `RegisterClass` constants for each register class are defined. 398 | - without these, the only way to distinguish register classes would be string compares. bad. sorry! 399 | 400 | ## 0.1.0 401 | * port `long_mode` improvements to `protected_mode` decoder 402 | - real mode will wait until another day 403 | * support undocumented instruction `salc` 404 | * fix segment registers being numbered wrong 405 | - this is relevant only for mov to/from segments 406 | * support x86_32 `push ``/`pop ` 407 | * support x86_32 `pusha`/`popa` 408 | * support x86_32 BCD instructions 409 | - for `aam`/`aad`, the undocumented `amx` and `adx` forms are used in all cases, including when the base is 10 410 | * begin some proper documentation for public items 411 | 412 | /!\ BREAKING CHANGES /!\ 413 | * `RegisterBank` is no longer public. `RegisterClass` should be a suitable 414 | replacement, accessible via `reg.class()`, with the register class name 415 | available by `reg.class().name`, and size available by `reg.class().width()`. 416 | `reg.width()` still works, and just forwards to `reg.class().width()`. 417 | * the field `opcode` of `Instruction` is no longer public. it can now be accessed by `inst.opcode()`. 418 | 419 | ## 0.0.15 420 | 421 | * the `x87` instruction set is now fully supported 422 | - textual disassembly differs slightly from other decoders in that 423 | yaxpeax-x86 does not prefer using `st` in place of `st(0)` 424 | * do not decode `into` in 64-bit mode 425 | * support `vmread`, `vmwrite` 426 | * support `iret`/`iretd`/`iretq` 427 | * support `enter` 428 | * support `cmc` and `int1` 429 | * support `loopz`, `loopnz`, `jecxz` 430 | * support `maskmovq`, `movnti`, and `movntq` 431 | - this brings full support to non-vex-coded x86 instructions 432 | * reject excessively-long instructions 433 | * reject reg-reg encodings where instructions forbid those operands 434 | * correctly handle `mov [0xoffset], ax` and `mov ax, [0xoffset]` 435 | - offset had been read with incorrect size 436 | * `vpsrlw`, `vpermq`, `vpminsq`, `vpsrlq`, `vextractf128`, `vinserti128` 437 | * reorganize likely decoding paths for a smidge more speed 438 | 439 | ## 0.0.14 440 | 441 | * `netburst` supported `cmpxchg16b` from its first x86_64 incarnation. since no 442 | uarch in `long_mode` had declared `cmpxchg16b` support, no uarch-specific Intel 443 | decoder supported `cmpxchg16b`. 444 | 445 | ## 0.0.13 446 | 447 | * the Intel microarchitecture is named `Penryn`, not `Peryn`. 448 | 449 | ## 0.0.12 450 | 451 | * fix improper decode of `sib` memory operand when `rex.x` is set and index is `0b100` 452 | - functionally: instructions which should have had a memory operand like 453 | `[rax + r12 + disp]` were missing `r12` 454 | * add instruction set extensions: `SHA`, `BMI1`, `BMI2`, `XSAVE`, `RDRAND`, 455 | `RDSEED`, `CMPXCHG{8,16}B` `ADX`, `SVM`, `MOVBE`, `PREFETCHW`, `TSX`, and 456 | `F16C` 457 | * add `RDFSBASE`, `RDGSBASE`, `WRFSBASE`, `WRGSBASE` 458 | * builders for per-uarch x86_64 instruction decoders, see `yaxpeax_x86::long_mode::uarch::{intel, amd}` 459 | * builders for per-uarch x86_32 instruction decoders, see `yaxpeax_x86::protected_mode::uarch::{intel, amd}` 460 | 461 | ## 0.0.11 462 | 463 | * fix mis-named 'cbd' instruction, which should be 'cwd' 464 | * add `Operand::width` to query the width of an x86 access 465 | - this is wrong for many memory operands, which require deeper changes 466 | * bump `yaxpeax-arch` to 0.0.4, which yields a breaking change in `Self::Unit` of `LengthedInstruction 467 | * `Prefixes::rep` is now public, allowing users to query if a decoded instruction has a rep prefix 468 | 469 | ## 0.0.10 470 | 471 | same as 0.0.9, but with a warning fixed. 472 | 473 | ## 0.0.9 474 | 475 | added `protected_mode` for 32-bit instruction decoding. BCD instructions not yet supported. 476 | 477 | ## 0.0.8 478 | 479 | same as 0.0.7, but with a readme in the crates.io page. 480 | 481 | ## 0.0.7 482 | 483 | `sse` and `sse2` support are mostly complete. 484 | `jmp reg` erroneously decoded to 32-bit registers without `rex.w`. 485 | `callf` could erroneously decode as having a register operand. 486 | more comprehensive, if yet insufficiently tested, avx decoding. 487 | support `vmclear` and `vmxon`, vmx still incomplete. 488 | 489 | ## 0.0.6 490 | 491 | addressing modes using a sib byte with displacement != 0 were wrongly reported as having no displacement. 492 | 493 | ## 0.0.5 494 | 495 | history basically starts here. 496 | 497 | * impl Ord and PartialOrd on RegSpec and RegisterBank 498 | * `RegSpec::name` to get `&'static str` labels for registers 499 | * support `in` and `out` instructions 500 | 501 | ## 0.0.4 - 0.0.2 502 | 503 | seriously stop, just don't use these versions 504 | 505 | just bumps to use newer `yaxpeax-arch` since this is all wildly unstable 506 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "yaxpeax-x86" 4 | version = "2.0.0" 5 | authors = [ "iximeow " ] 6 | license = "0BSD" 7 | repository = "http://git.iximeow.net/yaxpeax-x86/" 8 | description = "x86 decoders for the yaxpeax project" 9 | readme = "README.md" 10 | edition = "2018" 11 | 12 | [dependencies] 13 | yaxpeax-arch = { version = "0.3.1", default-features = false, features = [] } 14 | "num-traits" = { version = "0.2", default-features = false } 15 | "serde" = { version = "1.0", optional = true } 16 | "serde_json" = { version = "1.0", optional = true } 17 | "serde_derive" = { version = "1.0", optional = true } 18 | "cfg-if" = "1.0.0" 19 | 20 | [dev-dependencies] 21 | rand = "0.8.4" 22 | 23 | [[test]] 24 | name = "test" 25 | path = "test/test.rs" 26 | 27 | [[bench]] 28 | name = "bench" 29 | path = "test/bench.rs" 30 | 31 | [profile.bench] 32 | opt-level = 3 33 | lto = true 34 | 35 | [profile.release] 36 | opt-level = 3 37 | lto = true 38 | 39 | [features] 40 | default = ["std", "colors", "use-serde", "fmt"] 41 | 42 | # opt-in for some apis that are really much nicer with String 43 | std = ["alloc", "yaxpeax-arch/std"] 44 | 45 | # opt-in for some formatting-related helpers that require performing allocation 46 | # 47 | # this should only be useful with `fmt` currently, but in the future there could 48 | # be other `fmt`-independent code gated on `alloc`. 49 | alloc = ["yaxpeax-arch/alloc"] 50 | 51 | # feature for formatting instructions and their components 52 | fmt = [] 53 | 54 | use-serde = ["yaxpeax-arch/use-serde", "serde", "serde_derive"] 55 | 56 | colors = ["yaxpeax-arch/colors"] 57 | 58 | # This enables some capstone benchmarks over the same 59 | # instruction bytes used to bench this code. 60 | capstone_bench = [] 61 | 62 | # this disables a lot of inlining to make it easier for me to measure 63 | # likelihood of codepaths for typical instruction sequences 64 | profiling = [] 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 iximeow 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 11 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: test-fast test-exhaustive 2 | 3 | test-fast: test-std test-no-std test-fmt-no-std 4 | 5 | test-exhaustive: 6 | cargo test -- --ignored 7 | cargo test --no-default-features -- --ignored 8 | 9 | test-std: 10 | cargo test 11 | test-no-std: 12 | cargo test --no-default-features 13 | test-fmt-no-std: 14 | cargo test --no-default-features --features "fmt" 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## yaxpeax-x86 2 | 3 | [![crate](https://img.shields.io/crates/v/yaxpeax-x86.svg?logo=rust)](https://crates.io/crates/yaxpeax-x86) 4 | [![documentation](https://docs.rs/yaxpeax-x86/badge.svg)](https://docs.rs/yaxpeax-x86) 5 | 6 | x86 decoders implemented as part of the yaxpeax project, implementing traits provided by `yaxpeax-arch`. 7 | 8 | Rust users of this library will either want to use the [quick and dirty APIs](https://docs.rs/yaxpeax-x86/latest/yaxpeax_x86/long_mode/struct.InstDecoder.html#method.decode_slice), or more [generic decode interfaces](https://docs.rs/yaxpeax-arch/latest/yaxpeax_arch/trait.Decoder.html#method.decode) from `yaxpeax-arch` - appropriate when mixing `yaxpeax-x86` usage with other `yaxpeax` decoders, such as `yaxpeax-arm`. examples of both styles are provided [in the documentation](https://docs.rs/yaxpeax-x86/). 9 | 10 | the `ffi/` directory provides a repackaging of `yaxpeax-x86` suitable for use by non-Rust callers, such as C or C++. see the `examples` directory for FFI usage of this library. 11 | 12 | ### features 13 | 14 | * `#[no_std]` 15 | * configurable instruction set extensions 16 | * very fast 17 | * pretty small? 18 | 19 | ### `#[no_std]` 20 | the decoders provided by `yaxpeax-x86` are designed to be usable in a `no_std` setting, and does so by default. to build `yaxpeax_x86` without `std`, add the parameter `default-features = false` to your `yaxpeax-x86` dependency; the [ffi packaging](https://git.iximeow.net/yaxpeax-x86/tree/ffi) of `yaxpeax_x86` does this and builds without the Rust standard library as well. serde can be enabled without `std`, but json serialization/deserialization [need some careful attention](https://serde.rs/no-std.html) in that mode. as well as the `colors` feature to render instructions with default (eg terminal-friendly) syntax highlighting. 21 | 22 | ### instruction set extensions 23 | `yaxpeax-x86` decoders provide the option to specify what [instruction set extensions](http://git.iximeow.net/yaxpeax-x86/tree/src/long_mode/mod.rs#n1297) are eligible when decoding, to support decoding x86 instructions as understood by a particular microarchitecture. the default impls of decoders in `yaxpeax_x86` take an optimistic approach to decoding and assumes all feature sets are available, as well as accepting both intel-specific and amd-specific quirks around undefined encodings. 24 | 25 | yaxpeax-x86 decodes long-mode (`amd64`/`x86_64`), protected-mode (`x86`/`x86_32`), and real-mode (`x86_16`) instructions. the most part, ISA extensions decode equivalently across modes; this is the full list of extensions that are supported: 26 | 27 | `3dnow`, `sse`\*, `sse2`\*, `sse3`, `ssse3`, `sse4.1`, `sse4.2`, `sse4a`, `avx`, `avx2`, `avx512`\*\*, `syscall`, `cmpxchg16b`, `fma3`, `aesni`, `popcnt`, `rdrand`, `xsave`, `sgx`, `monitor`, `movbe`, `sgx`, `bmi1`, `bmi2`, `invpcid`, `mpx`, `adx`, `clflushopt`, `pcommit`, `sha`, `gfni`, `pclmulqdq`, `rdtscp`, `abm`, `xop`, `skinit`, `tbm`, `svm`, `f16c`, `fma4`, `tsx`, `enqcmd`, `uintr`, `keylocker`, `store_direct`, `cet`, `sev/snp` 28 | 29 | \*: `sse`, and `sse2` are non-optional in `x86_64`, so it is not permitted to construct a decoder that rejects them. `x86_32` and `x86_16` could have features to reject these instructions for true `8086` and `i386` compatibility, but currently do not. 30 | 31 | \*\*: `avx512` is fully supported, but decoders rejecting subgroups of the `avx512` family are not. if you need granular `avx512` compatibility controls, please file an issue. 32 | 33 | ### very fast 34 | when hooked up to [`disas-bench`](https://github.com/iximeow/disas-bench#results), `yaxpeax_x86::long_mode` has shown roughly 250mb/s decode throughput and on some hardware is the fastest software x86 decoder available. the likely path through the decoder, through `::decode_into``, is an average of 58 cycles on a zen2 core. 35 | 36 | while there is an in-repo benchmark, i've decided it's so unrealistic as to be unuseful, and prefer `disas-bench` until it can be made more informative. 37 | 38 | ### pretty small? 39 | `yaxpeax_x86::long_mode` built on its own is around 143kb of code and data. with data for instruction formatting, this grows to 249kb. while code size can be shrunk some, most of the crate is a few lookup tables - the hot path through `yaxpeax-x86`'s decode logic stays in functions coming out to ~5 kilobytes of code, and lots of supporting logic for less likely instructions. 40 | 41 | `yaxpeax_x86` may be the smallest library for tasks focused entirely on decoding and instruction formatting, but this crate doesn't have extensive testing to that end. 42 | 43 | ### mirrors 44 | 45 | the canonical copy of `yaxpeax-x86` is at [https://git.iximeow.net/yaxpeax-x86/](https://git.iximeow.net/yaxpeax-x86/). 46 | 47 | `yaxpeax-x86` is also mirrored on GitHub at [https://www.github.com/iximeow/yaxpeax-x86](https://www.github.com/iximeow/yaxpeax-x86). 48 | 49 | ### unsafety 50 | `yaxpeax_x86` makes regular use of `unsafe { unreachable_unchecked(); }` and occasional use of `unsafe { _.get_unchecked() }` for purely performance reasons. `yaxpeax_x86` is fuzzed via `mishegos` and has passed multiple days of fuzzing without issue. 51 | 52 | ### changelog 53 | a changelog across crate versions is maintained in the `CHANGELOG` file located in the repo, as well as [online](https://git.iximeow.net/yaxpeax-x86/tree/CHANGELOG). 54 | 55 | ### contributing 56 | unfortunately, pushing commits to the canonical repo at `git.iximeow.net` is impossible. if you'd like to contribute - thank you! - please send patches to emails iximeow has committed under or by opening PRs against the [GitHub mirror](https://www.github.com/iximeow/yaxpeax-x86). both remotes are kept in sync. 57 | 58 | ### see also 59 | 60 | [`iced`](https://github.com/0xd4d/iced) is another very good `x86_64` decoder, also written in rust. it provides additional information about instruction semantics as part of the crate, as well as the ability to re-encode instructions. 61 | 62 | [`disas-bench`](https://github.com/athre0z/disas-bench), a handy benchmark of several `x86_64` decoders including `yaxpeax-x86`. 63 | 64 | [`mishegos`](https://github.com/trailofbits/mishegos/), a differential fuzzer that has made testing the correctness of `yaxpeax-x86` _much_ easier. 65 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | #[cfg(feature="capstone_bench")] 3 | { 4 | println!("cargo:rustc-link-search=/usr/lib/"); 5 | println!("cargo:rustc-link-lib=capstone"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ffi/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = [ 3 | "-C", 4 | "link-args=-nostdlib", 5 | ] 6 | -------------------------------------------------------------------------------- /ffi/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.dev] 2 | lto = "fat" 3 | panic = "abort" 4 | 5 | [profile.release] 6 | lto = "fat" 7 | panic = "abort" 8 | 9 | [workspace] 10 | members = [ 11 | "multiarch", 12 | "long_mode", 13 | "protected_mode", 14 | "real_mode", 15 | ] 16 | -------------------------------------------------------------------------------- /ffi/README.md: -------------------------------------------------------------------------------- 1 | # `ffi`/ 2 | 3 | this directory includes (relatively small) repackagings of `yaxpeax-x86` for use from non-Rust callers. these bindings are intended to be expanded on an as-needed basis, so if they are insufficient, please file an issue or contact the maintainers. 4 | 5 | # building 6 | for minimal size, ffi crates' `rustflags` includes `-C link-args=-nostdlib`. to avoid conflicts with the native toolchain, this motivates cross-compiling for the native target with `--target x86_64-unknown-linux-gnu`. 7 | 8 | then, to remove extra `eh_frame` information from core, rebuild core with panic=abort by `-Z build-std` to cargo. in total, a build command for `yaxpeax_x86_ffi*` looks like: 9 | 10 | ``` 11 | cargo build -Z build-std --release --no-default-features --target x86_64-unknown-linux-gnu 12 | ``` 13 | 14 | where you must replace `x86_64-unknown-linux-gnu` with the target system the build artifacts will be used on. `rustup show` should give a good indication of the local target, and of course you can cross-compile to other architectures and targets if so inclined - `yaxpeax-x86` should have no specific dependencies on any architecture or target. 15 | 16 | because even builds for the current host are technically cross-compiles with the above command, build artifacts will be in `target//{release,debug}/`, instead of the typical `target/{release,debug>/`. for example, when i build these crates the artifacts end up like this: 17 | 18 | ``` 19 | target/x86_64-unknown-linux-gnu/ 20 | ├── [ 177] CACHEDIR.TAG 21 | └── [4.0K] release 22 | ├── [4.0K] build 23 | ├── [ 12K] deps 24 | ├── [4.0K] examples 25 | ├── [4.0K] incremental 26 | ├── [730K] libyaxpeax_x86_ffi_long_mode.a 27 | ├── [ 909] libyaxpeax_x86_ffi_long_mode.d 28 | ├── [143K] libyaxpeax_x86_ffi_long_mode.so 29 | ├── [1.2M] libyaxpeax_x86_ffi_multiarch.a 30 | ├── [1.0K] libyaxpeax_x86_ffi_multiarch.d 31 | ├── [360K] libyaxpeax_x86_ffi_multiarch.so 32 | ├── [714K] libyaxpeax_x86_ffi_protected_mode.a 33 | ├── [ 929] libyaxpeax_x86_ffi_protected_mode.d 34 | ├── [126K] libyaxpeax_x86_ffi_protected_mode.so 35 | ├── [715K] libyaxpeax_x86_ffi_real_mode.a 36 | └── [ 909] libyaxpeax_x86_ffi_real_mode.d 37 | 38 | 5 directories, 12 files 39 | ``` 40 | -------------------------------------------------------------------------------- /ffi/long_mode/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yaxpeax_x86_ffi_long_mode" 3 | version = "0.1.0" 4 | authors = ["iximeow "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | yaxpeax-x86 = { path = "../../", default-features = false } 9 | yaxpeax-arch = { version = "0.3.1", default-features = false } 10 | 11 | [lib] 12 | name = "yaxpeax_x86_ffi_long_mode" 13 | path = "src/lib.rs" 14 | crate-type = ["staticlib", "cdylib"] 15 | 16 | [features] 17 | default = ["fmt"] 18 | 19 | fmt = ["yaxpeax-x86/fmt"] 20 | -------------------------------------------------------------------------------- /ffi/long_mode/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[panic_handler] 4 | fn panic(_info: &core::panic::PanicInfo) -> ! { 5 | loop {} 6 | } 7 | 8 | #[path = "../../src/long_mode.rs"] 9 | mod long_mode; 10 | 11 | pub use long_mode::*; 12 | -------------------------------------------------------------------------------- /ffi/multiarch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yaxpeax_x86_ffi_multiarch" 3 | version = "0.0.3" 4 | authors = ["iximeow "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | yaxpeax-x86 = { path = "../../", default-features = false } 9 | yaxpeax-arch = { version = "0.3.1", default-features = false } 10 | 11 | [lib] 12 | name = "yaxpeax_x86_ffi_multiarch" 13 | path = "src/lib.rs" 14 | crate-type = ["staticlib", "cdylib"] 15 | 16 | [features] 17 | default = ["fmt"] 18 | 19 | fmt = ["yaxpeax-x86/fmt"] 20 | -------------------------------------------------------------------------------- /ffi/multiarch/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[panic_handler] 4 | fn panic(_info: &core::panic::PanicInfo) -> ! { 5 | loop {} 6 | } 7 | 8 | #[path = "../../src/long_mode.rs"] 9 | mod long_mode; 10 | 11 | pub use long_mode::*; 12 | 13 | #[path = "../../src/protected_mode.rs"] 14 | mod protected_mode; 15 | 16 | pub use protected_mode::*; 17 | 18 | #[path = "../../src/real_mode.rs"] 19 | mod real_mode; 20 | 21 | pub use real_mode::*; 22 | -------------------------------------------------------------------------------- /ffi/multiarch/src/long_mode.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(lang_items)] 3 | 4 | #[panic_handler] 5 | #[cold] 6 | fn panic(_panic: &core::panic::PanicInfo) -> ! { 7 | loop {} 8 | } 9 | 10 | #[lang = "eh_personality"] extern fn eh_personality() {} 11 | 12 | use yaxpeax_arch::{Arch, Decoder, LengthedInstruction, AddressBase}; 13 | use yaxpeax_x86::long_mode as amd64; 14 | 15 | #[no_mangle] 16 | pub unsafe extern "C" fn yaxpeax_decode_x86_64_optimistic(data: *const u8, length: u64, inst: *mut amd64::Instruction) -> bool { 17 | let inst: &mut amd64::Instruction = core::mem::transmute(inst); 18 | ::Decoder::default().decode_into(inst, core::slice::from_raw_parts(data as *const u8, length as usize).iter().cloned()).is_err() 19 | } 20 | 21 | #[no_mangle] 22 | pub unsafe extern "C" fn yaxpeax_instr_length_x86_64(inst: *mut amd64::Instruction) -> usize { 23 | let inst: &mut amd64::Instruction = core::mem::transmute(inst); 24 | 0.wrapping_offset(inst.len()).to_linear() 25 | } 26 | 27 | #[cfg(fmt)] 28 | mod write_sink; 29 | 30 | #[cfg(fmt)] 31 | mod fmt { 32 | use write_sink::InstructionSink; 33 | 34 | use core::fmt::Write; 35 | 36 | #[no_mangle] 37 | pub unsafe extern "C" fn yaxpeax_x86_64_instr_fmt_intel(inst: *mut amd64::Instruction, text: *mut u8, len: usize) { 38 | let inst: &mut amd64::Instruction = core::mem::transmute(inst); 39 | let res = core::slice::from_raw_parts_mut(text, len); 40 | 41 | write!(InstructionSink { buf: res, offs: 0 }, "{}", inst.display_with(DisplayStyle::Intel)).unwrap(); 42 | } 43 | 44 | #[no_mangle] 45 | pub unsafe extern "C" fn yaxpeax_x86_64_instr_fmt_c(inst: *mut amd64::Instruction, text: *mut u8, len: usize) { 46 | let inst: &mut amd64::Instruction = core::mem::transmute(inst); 47 | let res = core::slice::from_raw_parts_mut(text, len); 48 | 49 | write!(InstructionSink { buf: res, offs: 0 }, "{}", inst.display_with(DisplayStyle::C)).unwrap(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ffi/multiarch/src/protected_mode.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(lang_items)] 3 | 4 | #[panic_handler] 5 | #[cold] 6 | fn panic(_panic: &core::panic::PanicInfo) -> ! { 7 | loop {} 8 | } 9 | 10 | #[lang = "eh_personality"] extern fn eh_personality() {} 11 | 12 | use yaxpeax_arch::{Arch, Decoder, LengthedInstruction, AddressBase}; 13 | use yaxpeax_x86::protected_mode as x86; 14 | 15 | #[no_mangle] 16 | pub unsafe extern "C" fn yaxpeax_decode_x86_32_optimistic(data: *const u8, length: u64, inst: *mut x86::Instruction) -> bool { 17 | let inst: &mut x86::Instruction = core::mem::transmute(inst); 18 | ::Decoder::default().decode_into(inst, core::slice::from_raw_parts(data as *const u8, length as usize).iter().cloned()).is_err() 19 | } 20 | 21 | #[no_mangle] 22 | pub unsafe extern "C" fn yaxpeax_instr_length_x86_64(inst: *mut x86::Instruction) -> usize { 23 | let inst: &mut x86::Instruction = core::mem::transmute(inst); 24 | 0.wrapping_offset(inst.len()).to_linear() 25 | } 26 | 27 | #[cfg(fmt)] 28 | mod write_sink; 29 | 30 | #[cfg(fmt)] 31 | mod fmt { 32 | use write_sink::InstructionSink; 33 | 34 | use core::fmt::Write; 35 | 36 | #[no_mangle] 37 | pub unsafe extern "C" fn yaxpeax_instr_fmt(inst: *mut x86::Instruction, text: *mut u8, len: usize) { 38 | let inst: &mut x86::Instruction = core::mem::transmute(inst); 39 | let res = core::slice::from_raw_parts_mut(text, len); 40 | 41 | write!(InstructionSink { buf: res, offs: 0 }, "{}", inst).unwrap(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ffi/protected_mode/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yaxpeax_x86_ffi_protected_mode" 3 | version = "0.0.2" 4 | authors = ["iximeow "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | yaxpeax-x86 = { path = "../../", default-features = false } 9 | yaxpeax-arch = { version = "0.3.1", default-features = false } 10 | 11 | [lib] 12 | name = "yaxpeax_x86_ffi_protected_mode" 13 | path = "src/lib.rs" 14 | crate-type = ["staticlib", "cdylib"] 15 | 16 | [features] 17 | default = ["fmt"] 18 | 19 | fmt = ["yaxpeax-x86/fmt"] 20 | -------------------------------------------------------------------------------- /ffi/protected_mode/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[panic_handler] 4 | fn panic(_info: &core::panic::PanicInfo) -> ! { 5 | loop {} 6 | } 7 | 8 | #[path = "../../src/protected_mode.rs"] 9 | mod protected_mode; 10 | 11 | pub use protected_mode::*; 12 | -------------------------------------------------------------------------------- /ffi/real_mode/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yaxpeax_x86_ffi_real_mode" 3 | version = "0.0.2" 4 | authors = ["iximeow "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | yaxpeax-x86 = { path = "../../", default-features = false } 9 | yaxpeax-arch = { version = "0.3.1", default-features = false } 10 | 11 | [lib] 12 | name = "yaxpeax_x86_ffi_real_mode" 13 | path = "src/lib.rs" 14 | crate-type = ["staticlib", "cdylib"] 15 | 16 | [features] 17 | default = ["fmt"] 18 | 19 | fmt = ["yaxpeax-x86/fmt"] 20 | -------------------------------------------------------------------------------- /ffi/real_mode/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[panic_handler] 4 | fn panic(_info: &core::panic::PanicInfo) -> ! { 5 | loop {} 6 | } 7 | 8 | #[path = "../../src/real_mode.rs"] 9 | mod real_mode; 10 | 11 | pub use real_mode::*; 12 | -------------------------------------------------------------------------------- /ffi/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly 2 | -------------------------------------------------------------------------------- /ffi/src/long_mode.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_arch::{Arch, Decoder, LengthedInstruction, U8Reader, AddressBase}; 2 | use yaxpeax_x86::long_mode; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn yaxpeax_x86_64_decode(data: *const u8, length: u64, inst: *mut long_mode::Instruction) -> bool { 6 | let inst: &mut long_mode::Instruction = core::mem::transmute(inst); 7 | let mut reader = U8Reader::new(core::slice::from_raw_parts(data as *const u8, length as usize)); 8 | ::Decoder::default().decode_into(inst, &mut reader).is_err() 9 | } 10 | 11 | #[no_mangle] 12 | pub unsafe extern "C" fn yaxpeax_x86_64_instr_length(inst: *mut long_mode::Instruction) -> usize { 13 | let inst: &mut long_mode::Instruction = core::mem::transmute(inst); 14 | 0.wrapping_offset(inst.len()).to_linear() 15 | } 16 | 17 | #[cfg(feature = "fmt")] 18 | mod write_sink; 19 | 20 | #[cfg(feature = "fmt")] 21 | mod fmt { 22 | use super::write_sink::InstructionSink; 23 | 24 | use core::fmt::Write; 25 | 26 | use yaxpeax_x86::long_mode; 27 | 28 | #[no_mangle] 29 | pub unsafe extern "C" fn yaxpeax_x86_64_fmt(inst: *mut long_mode::Instruction, text: *mut u8, len: usize) { 30 | let inst: &mut long_mode::Instruction = core::mem::transmute(inst); 31 | let res = core::slice::from_raw_parts_mut(text, len); 32 | 33 | write!(InstructionSink { buf: res, offs: 0 }, "{}", inst).unwrap(); 34 | } 35 | } 36 | 37 | #[cfg(feature = "fmt")] 38 | pub use fmt::yaxpeax_x86_64_fmt; 39 | -------------------------------------------------------------------------------- /ffi/src/protected_mode.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_arch::{Arch, Decoder, LengthedInstruction, U8Reader, AddressBase}; 2 | use yaxpeax_x86::protected_mode; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn yaxpeax_x86_32_decode(data: *const u8, length: u64, inst: *mut protected_mode::Instruction) -> bool { 6 | let inst: &mut protected_mode::Instruction = core::mem::transmute(inst); 7 | let mut reader = U8Reader::new(core::slice::from_raw_parts(data as *const u8, length as usize)); 8 | ::Decoder::default().decode_into(inst, &mut reader).is_err() 9 | } 10 | 11 | #[no_mangle] 12 | pub unsafe extern "C" fn yaxpeax_x86_32_instr_length(inst: *mut protected_mode::Instruction) -> usize { 13 | let inst: &mut protected_mode::Instruction = core::mem::transmute(inst); 14 | 0.wrapping_offset(inst.len()).to_linear() 15 | } 16 | 17 | #[cfg(feature = "fmt")] 18 | mod write_sink; 19 | 20 | #[cfg(feature = "fmt")] 21 | mod fmt { 22 | use super::write_sink::InstructionSink; 23 | 24 | use core::fmt::Write; 25 | 26 | use yaxpeax_x86::protected_mode; 27 | 28 | #[no_mangle] 29 | pub unsafe extern "C" fn yaxpeax_x86_32_fmt(inst: *mut protected_mode::Instruction, text: *mut u8, len: usize) { 30 | let inst: &mut protected_mode::Instruction = core::mem::transmute(inst); 31 | let res = core::slice::from_raw_parts_mut(text, len); 32 | 33 | write!(InstructionSink { buf: res, offs: 0 }, "{}", inst).unwrap(); 34 | } 35 | } 36 | 37 | #[cfg(feature = "fmt")] 38 | pub use fmt::yaxpeax_x86_32_fmt; 39 | -------------------------------------------------------------------------------- /ffi/src/real_mode.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_arch::{Arch, Decoder, LengthedInstruction, U8Reader, AddressBase}; 2 | use yaxpeax_x86::real_mode; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn yaxpeax_x86_16_decode(data: *const u8, length: u64, inst: *mut real_mode::Instruction) -> bool { 6 | let inst: &mut real_mode::Instruction = core::mem::transmute(inst); 7 | let mut reader = U8Reader::new(core::slice::from_raw_parts(data as *const u8, length as usize)); 8 | ::Decoder::default().decode_into(inst, &mut reader).is_err() 9 | } 10 | 11 | #[no_mangle] 12 | pub unsafe extern "C" fn yaxpeax_x86_16_instr_length(inst: *mut real_mode::Instruction) -> usize { 13 | let inst: &mut real_mode::Instruction = core::mem::transmute(inst); 14 | 0.wrapping_offset(inst.len()).to_linear() 15 | } 16 | 17 | #[cfg(feature = "fmt")] 18 | mod write_sink; 19 | 20 | #[cfg(feature = "fmt")] 21 | mod fmt { 22 | use super::write_sink::InstructionSink; 23 | 24 | use core::fmt::Write; 25 | 26 | use yaxpeax_x86::real_mode; 27 | 28 | #[no_mangle] 29 | pub unsafe extern "C" fn yaxpeax_x86_16_fmt(inst: *mut real_mode::Instruction, text: *mut u8, len: usize) { 30 | let inst: &mut real_mode::Instruction = core::mem::transmute(inst); 31 | let res = core::slice::from_raw_parts_mut(text, len); 32 | 33 | write!(InstructionSink { buf: res, offs: 0 }, "{}", inst).unwrap(); 34 | } 35 | } 36 | 37 | #[cfg(feature = "fmt")] 38 | pub use fmt::yaxpeax_x86_16_fmt; 39 | -------------------------------------------------------------------------------- /ffi/src/write_sink.rs: -------------------------------------------------------------------------------- 1 | pub struct InstructionSink<'buf> { 2 | pub buf: &'buf mut [u8], 3 | pub offs: usize, 4 | } 5 | 6 | impl<'a> core::fmt::Write for InstructionSink<'a> { 7 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 8 | for b in s.bytes() { 9 | if self.offs < self.buf.len() { 10 | self.buf[self.offs] = b; 11 | self.offs += 1; 12 | } else { 13 | break; 14 | } 15 | } 16 | 17 | Ok(()) 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "yaxpeax-x86-fuzz" 4 | version = "0.0.1" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies.yaxpeax-x86] 12 | path = ".." 13 | [dependencies.yaxpeax-arch] 14 | version = "0.3.1" 15 | [dependencies.libfuzzer-sys] 16 | git = "https://github.com/rust-fuzz/libfuzzer-sys.git" 17 | 18 | # Prevent this from interfering with workspaces 19 | [workspace] 20 | members = ["."] 21 | 22 | [[bin]] 23 | name = "decode_does_not_panic" 24 | path = "fuzz_targets/decode_does_not_panic.rs" 25 | 26 | [[bin]] 27 | name = "display_does_not_panic" 28 | path = "fuzz_targets/display_does_not_panic.rs" 29 | test = false 30 | doc = false 31 | 32 | [[bin]] 33 | name = "displaysink_used_correctly" 34 | path = "fuzz_targets/displaysink_used_correctly.rs" 35 | test = false 36 | doc = false 37 | 38 | [[bin]] 39 | name = "instruction_text_buffer_size_ok" 40 | path = "fuzz_targets/instruction_text_buffer_size_ok.rs" 41 | test = false 42 | doc = false 43 | 44 | [[bin]] 45 | name = "display_c_does_not_panic" 46 | path = "fuzz_targets/display_c_does_not_panic.rs" 47 | test = false 48 | doc = false 49 | 50 | [[bin]] 51 | name = "does_not_decode_invalid_registers" 52 | path = "fuzz_targets/does_not_decode_invalid_registers.rs" 53 | test = false 54 | doc = false 55 | 56 | [[bin]] 57 | name = "small_reg_is_always_old_bank_if_possible" 58 | path = "fuzz_targets/small_reg_is_always_old_bank_if_possible.rs" 59 | test = false 60 | doc = false 61 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/decode_does_not_panic.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #[macro_use] extern crate libfuzzer_sys; 3 | extern crate yaxpeax_x86; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); 7 | let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); 8 | let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); 9 | x86_64_decoder.decode_slice(data).expect("is ok"); 10 | x86_32_decoder.decode_slice(data).expect("is ok"); 11 | x86_16_decoder.decode_slice(data).expect("is ok"); 12 | }); 13 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/display_c_does_not_panic.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #[macro_use] extern crate libfuzzer_sys; 3 | extern crate yaxpeax_x86; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); 7 | let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); 8 | let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); 9 | 10 | if let Ok(inst) = x86_64_decoder.decode_slice(data) { 11 | let _ = inst.display_with(yaxpeax_x86::long_mode::DisplayStyle::C).to_string(); 12 | }; 13 | 14 | if let Ok(inst) = x86_32_decoder.decode_slice(data) { 15 | let _ = inst.display_with(yaxpeax_x86::protected_mode::DisplayStyle::C).to_string(); 16 | }; 17 | 18 | if let Ok(inst) = x86_16_decoder.decode_slice(data) { 19 | let _ = inst.display_with(yaxpeax_x86::real_mode::DisplayStyle::C).to_string(); 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/display_does_not_panic.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #[macro_use] extern crate libfuzzer_sys; 3 | extern crate yaxpeax_x86; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); 7 | let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); 8 | let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); 9 | 10 | if let Ok(inst) = x86_64_decoder.decode_slice(data) { 11 | let mut out = String::new(); 12 | inst.write_to(&mut out).expect("format does not panic"); 13 | let mut text_buf = yaxpeax_x86::long_mode::InstructionTextBuffer::new(); 14 | text_buf.format_inst(&inst.display_with(yaxpeax_x86::long_mode::DisplayStyle::Intel)).expect("can format"); 15 | assert_eq!(text_buf.text_str(), out); 16 | }; 17 | 18 | if let Ok(inst) = x86_32_decoder.decode_slice(data) { 19 | let mut out = String::new(); 20 | inst.write_to(&mut out).expect("format does not panic"); 21 | let mut text_buf = yaxpeax_x86::protected_mode::InstructionTextBuffer::new(); 22 | text_buf.format_inst(&inst.display_with(yaxpeax_x86::protected_mode::DisplayStyle::Intel)).expect("can format"); 23 | assert_eq!(text_buf.text_str(), out); 24 | }; 25 | 26 | if let Ok(inst) = x86_16_decoder.decode_slice(data) { 27 | let mut out = String::new(); 28 | inst.write_to(&mut out).expect("format does not panic"); 29 | let mut text_buf = yaxpeax_x86::real_mode::InstructionTextBuffer::new(); 30 | text_buf.format_inst(&inst.display_with(yaxpeax_x86::real_mode::DisplayStyle::Intel)).expect("can format"); 31 | assert_eq!(text_buf.text_str(), out); 32 | }; 33 | }); 34 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/displaysink_used_correctly.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #[macro_use] extern crate libfuzzer_sys; 3 | extern crate yaxpeax_x86; 4 | extern crate yaxpeax_arch; 5 | 6 | fuzz_target!(|data: &[u8]| { 7 | let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); 8 | let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); 9 | let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); 10 | 11 | use yaxpeax_arch::testkit::DisplaySinkValidator; 12 | 13 | if let Ok(inst) = x86_64_decoder.decode_slice(data) { 14 | inst.display_into(&mut DisplaySinkValidator::new()).expect("instruction can be displayed"); 15 | }; 16 | 17 | if let Ok(inst) = x86_32_decoder.decode_slice(data) { 18 | inst.display_into(&mut DisplaySinkValidator::new()).expect("instruction can be displayed"); 19 | }; 20 | 21 | if let Ok(inst) = x86_16_decoder.decode_slice(data) { 22 | inst.display_into(&mut DisplaySinkValidator::new()).expect("instruction can be displayed"); 23 | }; 24 | }); 25 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/does_not_decode_invalid_registers.rs: -------------------------------------------------------------------------------- 1 | //! instruction text should never include the word BUG - this is a symptom of selecting an invalid 2 | //! RegSpec while disassembling. 3 | 4 | #![no_main] 5 | #[macro_use] extern crate libfuzzer_sys; 6 | extern crate yaxpeax_x86; 7 | 8 | fuzz_target!(|data: &[u8]| { 9 | let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); 10 | let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); 11 | let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); 12 | 13 | if let Ok(inst) = x86_64_decoder.decode_slice(data) { 14 | use yaxpeax_x86::long_mode::Operand; 15 | let mut res = String::new(); 16 | inst.write_to(&mut res).expect("format does not panic"); 17 | assert!(!res.contains("BUG")); 18 | for i in 0..inst.operand_count() { 19 | match inst.operand(i) { 20 | Operand::Register(reg) => { 21 | assert!(!reg.class().name().contains("BUG")); 22 | } 23 | Operand::RegisterMaskMerge(r1, r2, _) => { 24 | assert!(!r1.class().name().contains("BUG")); 25 | assert!(!r2.class().name().contains("BUG")); 26 | } 27 | Operand::RegisterMaskMergeSae(r1, r2, _, _) => { 28 | assert!(!r1.class().name().contains("BUG")); 29 | assert!(!r2.class().name().contains("BUG")); 30 | } 31 | Operand::RegisterMaskMergeSaeNoround(r1, r2, _) => { 32 | assert!(!r1.class().name().contains("BUG")); 33 | assert!(!r2.class().name().contains("BUG")); 34 | } 35 | Operand::RegDeref(r1) => { 36 | assert!(!r1.class().name().contains("BUG")); 37 | } 38 | Operand::RegDisp(r1, _) => { 39 | assert!(!r1.class().name().contains("BUG")); 40 | } 41 | Operand::RegScale(r1, _) => { 42 | assert!(!r1.class().name().contains("BUG")); 43 | } 44 | Operand::RegIndexBase(r1, r2) => { 45 | assert!(!r1.class().name().contains("BUG")); 46 | assert!(!r2.class().name().contains("BUG")); 47 | } 48 | Operand::RegIndexBaseDisp(r1, r2, _) => { 49 | assert!(!r1.class().name().contains("BUG")); 50 | assert!(!r2.class().name().contains("BUG")); 51 | } 52 | Operand::RegScaleDisp(r1, _, _) => { 53 | assert!(!r1.class().name().contains("BUG")); 54 | } 55 | Operand::RegIndexBaseScale(r1, r2, _) => { 56 | assert!(!r1.class().name().contains("BUG")); 57 | assert!(!r2.class().name().contains("BUG")); 58 | } 59 | Operand::RegIndexBaseScaleDisp(r1, r2, _, _) => { 60 | assert!(!r1.class().name().contains("BUG")); 61 | assert!(!r2.class().name().contains("BUG")); 62 | } 63 | Operand::RegDerefMasked(r1, r2) => { 64 | assert!(!r1.class().name().contains("BUG")); 65 | assert!(!r2.class().name().contains("BUG")); 66 | } 67 | Operand::RegDispMasked(r1, _, r2) => { 68 | assert!(!r1.class().name().contains("BUG")); 69 | assert!(!r2.class().name().contains("BUG")); 70 | } 71 | Operand::RegScaleMasked(r1, _, r2) => { 72 | assert!(!r1.class().name().contains("BUG")); 73 | assert!(!r2.class().name().contains("BUG")); 74 | } 75 | Operand::RegIndexBaseMasked(r1, r2, r3) => { 76 | assert!(!r1.class().name().contains("BUG")); 77 | assert!(!r2.class().name().contains("BUG")); 78 | assert!(!r3.class().name().contains("BUG")); 79 | } 80 | Operand::RegIndexBaseDispMasked(r1, r2, _, r3) => { 81 | assert!(!r1.class().name().contains("BUG")); 82 | assert!(!r2.class().name().contains("BUG")); 83 | assert!(!r3.class().name().contains("BUG")); 84 | } 85 | Operand::RegScaleDispMasked(r1, _, _, r2) => { 86 | assert!(!r1.class().name().contains("BUG")); 87 | assert!(!r2.class().name().contains("BUG")); 88 | } 89 | Operand::RegIndexBaseScaleMasked(r1, r2, _, r3) => { 90 | assert!(!r1.class().name().contains("BUG")); 91 | assert!(!r2.class().name().contains("BUG")); 92 | assert!(!r3.class().name().contains("BUG")); 93 | } 94 | Operand::RegIndexBaseScaleDispMasked(r1, r2, _, _, r3) => { 95 | assert!(!r1.class().name().contains("BUG")); 96 | assert!(!r2.class().name().contains("BUG")); 97 | assert!(!r3.class().name().contains("BUG")); 98 | } 99 | Operand::Nothing => { 100 | panic!("`Nothing` should not be an operand listed in `inst.operand_count()`"); 101 | } 102 | _ => { 103 | /* operands with no register - immediates or a non-rip-relative displacement */ 104 | } 105 | } 106 | } 107 | }; 108 | 109 | if let Ok(inst) = x86_32_decoder.decode_slice(data) { 110 | use yaxpeax_x86::protected_mode::Operand; 111 | let mut res = String::new(); 112 | inst.write_to(&mut res).expect("format does not panic"); 113 | assert!(!res.contains("BUG")); 114 | for i in 0..inst.operand_count() { 115 | match inst.operand(i) { 116 | Operand::Register(reg) => { 117 | assert!(!reg.class().name().contains("BUG")); 118 | } 119 | Operand::RegisterMaskMerge(r1, r2, _) => { 120 | assert!(!r1.class().name().contains("BUG")); 121 | assert!(!r2.class().name().contains("BUG")); 122 | } 123 | Operand::RegisterMaskMergeSae(r1, r2, _, _) => { 124 | assert!(!r1.class().name().contains("BUG")); 125 | assert!(!r2.class().name().contains("BUG")); 126 | } 127 | Operand::RegisterMaskMergeSaeNoround(r1, r2, _) => { 128 | assert!(!r1.class().name().contains("BUG")); 129 | assert!(!r2.class().name().contains("BUG")); 130 | } 131 | Operand::RegDeref(r1) => { 132 | assert!(!r1.class().name().contains("BUG")); 133 | } 134 | Operand::RegDisp(r1, _) => { 135 | assert!(!r1.class().name().contains("BUG")); 136 | } 137 | Operand::RegScale(r1, _) => { 138 | assert!(!r1.class().name().contains("BUG")); 139 | } 140 | Operand::RegIndexBase(r1, r2) => { 141 | assert!(!r1.class().name().contains("BUG")); 142 | assert!(!r2.class().name().contains("BUG")); 143 | } 144 | Operand::RegIndexBaseDisp(r1, r2, _) => { 145 | assert!(!r1.class().name().contains("BUG")); 146 | assert!(!r2.class().name().contains("BUG")); 147 | } 148 | Operand::RegScaleDisp(r1, _, _) => { 149 | assert!(!r1.class().name().contains("BUG")); 150 | } 151 | Operand::RegIndexBaseScale(r1, r2, _) => { 152 | assert!(!r1.class().name().contains("BUG")); 153 | assert!(!r2.class().name().contains("BUG")); 154 | } 155 | Operand::RegIndexBaseScaleDisp(r1, r2, _, _) => { 156 | assert!(!r1.class().name().contains("BUG")); 157 | assert!(!r2.class().name().contains("BUG")); 158 | } 159 | Operand::RegDerefMasked(r1, r2) => { 160 | assert!(!r1.class().name().contains("BUG")); 161 | assert!(!r2.class().name().contains("BUG")); 162 | } 163 | Operand::RegDispMasked(r1, _, r2) => { 164 | assert!(!r1.class().name().contains("BUG")); 165 | assert!(!r2.class().name().contains("BUG")); 166 | } 167 | Operand::RegScaleMasked(r1, _, r2) => { 168 | assert!(!r1.class().name().contains("BUG")); 169 | assert!(!r2.class().name().contains("BUG")); 170 | } 171 | Operand::RegIndexBaseMasked(r1, r2, r3) => { 172 | assert!(!r1.class().name().contains("BUG")); 173 | assert!(!r2.class().name().contains("BUG")); 174 | assert!(!r3.class().name().contains("BUG")); 175 | } 176 | Operand::RegIndexBaseDispMasked(r1, r2, _, r3) => { 177 | assert!(!r1.class().name().contains("BUG")); 178 | assert!(!r2.class().name().contains("BUG")); 179 | assert!(!r3.class().name().contains("BUG")); 180 | } 181 | Operand::RegScaleDispMasked(r1, _, _, r2) => { 182 | assert!(!r1.class().name().contains("BUG")); 183 | assert!(!r2.class().name().contains("BUG")); 184 | } 185 | Operand::RegIndexBaseScaleMasked(r1, r2, _, r3) => { 186 | assert!(!r1.class().name().contains("BUG")); 187 | assert!(!r2.class().name().contains("BUG")); 188 | assert!(!r3.class().name().contains("BUG")); 189 | } 190 | Operand::RegIndexBaseScaleDispMasked(r1, r2, _, _, r3) => { 191 | assert!(!r1.class().name().contains("BUG")); 192 | assert!(!r2.class().name().contains("BUG")); 193 | assert!(!r3.class().name().contains("BUG")); 194 | } 195 | Operand::Nothing => { 196 | panic!("`Nothing` should not be an operand listed in `inst.operand_count()`"); 197 | } 198 | _ => { 199 | /* operands with no register - immediates or a non-rip-relative displacement */ 200 | } 201 | } 202 | } 203 | }; 204 | 205 | if let Ok(inst) = x86_16_decoder.decode_slice(data) { 206 | use yaxpeax_x86::real_mode::Operand; 207 | let mut res = String::new(); 208 | inst.write_to(&mut res).expect("format does not panic"); 209 | assert!(!res.contains("BUG")); 210 | for i in 0..inst.operand_count() { 211 | match inst.operand(i) { 212 | Operand::Register(reg) => { 213 | assert!(!reg.class().name().contains("BUG")); 214 | } 215 | Operand::RegisterMaskMerge(r1, r2, _) => { 216 | assert!(!r1.class().name().contains("BUG")); 217 | assert!(!r2.class().name().contains("BUG")); 218 | } 219 | Operand::RegisterMaskMergeSae(r1, r2, _, _) => { 220 | assert!(!r1.class().name().contains("BUG")); 221 | assert!(!r2.class().name().contains("BUG")); 222 | } 223 | Operand::RegisterMaskMergeSaeNoround(r1, r2, _) => { 224 | assert!(!r1.class().name().contains("BUG")); 225 | assert!(!r2.class().name().contains("BUG")); 226 | } 227 | Operand::RegDeref(r1) => { 228 | assert!(!r1.class().name().contains("BUG")); 229 | } 230 | Operand::RegDisp(r1, _) => { 231 | assert!(!r1.class().name().contains("BUG")); 232 | } 233 | Operand::RegScale(r1, _) => { 234 | assert!(!r1.class().name().contains("BUG")); 235 | } 236 | Operand::RegIndexBase(r1, r2) => { 237 | assert!(!r1.class().name().contains("BUG")); 238 | assert!(!r2.class().name().contains("BUG")); 239 | } 240 | Operand::RegIndexBaseDisp(r1, r2, _) => { 241 | assert!(!r1.class().name().contains("BUG")); 242 | assert!(!r2.class().name().contains("BUG")); 243 | } 244 | Operand::RegScaleDisp(r1, _, _) => { 245 | assert!(!r1.class().name().contains("BUG")); 246 | } 247 | Operand::RegIndexBaseScale(r1, r2, _) => { 248 | assert!(!r1.class().name().contains("BUG")); 249 | assert!(!r2.class().name().contains("BUG")); 250 | } 251 | Operand::RegIndexBaseScaleDisp(r1, r2, _, _) => { 252 | assert!(!r1.class().name().contains("BUG")); 253 | assert!(!r2.class().name().contains("BUG")); 254 | } 255 | Operand::RegDerefMasked(r1, r2) => { 256 | assert!(!r1.class().name().contains("BUG")); 257 | assert!(!r2.class().name().contains("BUG")); 258 | } 259 | Operand::RegDispMasked(r1, _, r2) => { 260 | assert!(!r1.class().name().contains("BUG")); 261 | assert!(!r2.class().name().contains("BUG")); 262 | } 263 | Operand::RegScaleMasked(r1, _, r2) => { 264 | assert!(!r1.class().name().contains("BUG")); 265 | assert!(!r2.class().name().contains("BUG")); 266 | } 267 | Operand::RegIndexBaseMasked(r1, r2, r3) => { 268 | assert!(!r1.class().name().contains("BUG")); 269 | assert!(!r2.class().name().contains("BUG")); 270 | assert!(!r3.class().name().contains("BUG")); 271 | } 272 | Operand::RegIndexBaseDispMasked(r1, r2, _, r3) => { 273 | assert!(!r1.class().name().contains("BUG")); 274 | assert!(!r2.class().name().contains("BUG")); 275 | assert!(!r3.class().name().contains("BUG")); 276 | } 277 | Operand::RegScaleDispMasked(r1, _, _, r2) => { 278 | assert!(!r1.class().name().contains("BUG")); 279 | assert!(!r2.class().name().contains("BUG")); 280 | } 281 | Operand::RegIndexBaseScaleMasked(r1, r2, _, r3) => { 282 | assert!(!r1.class().name().contains("BUG")); 283 | assert!(!r2.class().name().contains("BUG")); 284 | assert!(!r3.class().name().contains("BUG")); 285 | } 286 | Operand::RegIndexBaseScaleDispMasked(r1, r2, _, _, r3) => { 287 | assert!(!r1.class().name().contains("BUG")); 288 | assert!(!r2.class().name().contains("BUG")); 289 | assert!(!r3.class().name().contains("BUG")); 290 | } 291 | Operand::Nothing => { 292 | panic!("`Nothing` should not be an operand listed in `inst.operand_count()`"); 293 | } 294 | _ => { 295 | /* operands with no register - immediates or a non-rip-relative displacement */ 296 | } 297 | } 298 | } 299 | }; 300 | }); 301 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/instruction_text_buffer_size_ok.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #[macro_use] extern crate libfuzzer_sys; 3 | extern crate yaxpeax_x86; 4 | extern crate yaxpeax_arch; 5 | 6 | use std::fmt::Write; 7 | 8 | fuzz_target!(|data: &[u8]| { 9 | let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); 10 | let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); 11 | let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); 12 | 13 | if let Ok(inst) = x86_64_decoder.decode_slice(data) { 14 | use yaxpeax_x86::long_mode::DisplayStyle; 15 | 16 | let mut s = String::new(); 17 | write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); 18 | // MAX_INSTRUCTION_LEN is not a public crate item yet... 19 | assert!(s.len() < 512); 20 | s.clear(); 21 | write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); 22 | // MAX_INSTRUCTION_LEN is not a public crate item yet... 23 | assert!(s.len() < 512); 24 | }; 25 | 26 | if let Ok(inst) = x86_32_decoder.decode_slice(data) { 27 | use yaxpeax_x86::protected_mode::DisplayStyle; 28 | 29 | let mut s = String::new(); 30 | write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); 31 | // MAX_INSTRUCTION_LEN is not a public crate item yet... 32 | assert!(s.len() < 512); 33 | s.clear(); 34 | write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); 35 | // MAX_INSTRUCTION_LEN is not a public crate item yet... 36 | assert!(s.len() < 512); 37 | }; 38 | 39 | if let Ok(inst) = x86_16_decoder.decode_slice(data) { 40 | use yaxpeax_x86::real_mode::DisplayStyle; 41 | 42 | let mut s = String::new(); 43 | write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); 44 | // MAX_INSTRUCTION_LEN is not a public crate item yet... 45 | assert!(s.len() < 512); 46 | s.clear(); 47 | write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); 48 | // MAX_INSTRUCTION_LEN is not a public crate item yet... 49 | assert!(s.len() < 512); 50 | }; 51 | }); 52 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/small_reg_is_always_old_bank_if_possible.rs: -------------------------------------------------------------------------------- 1 | //! if a register has a single-byte register operand, and it's one of `al`, `bl`, `cl`, or `dl`, it 2 | //! should compare equal to the `RegSpec` produced by `RegSpec::al()` and so on. 3 | //! 4 | //! at one point this was a bug; `RegSpec::al()` would use `RegisterBank::B`, but an instruction 5 | //! with `rex.w` set could get an `al` backed by a `RegSpec` in `RegisterBank::rB`. 6 | 7 | #![no_main] 8 | #[macro_use] extern crate libfuzzer_sys; 9 | extern crate yaxpeax_x86; 10 | 11 | // this test is not meaningful for 32-bit or 16-bit modes, there are no register synonyms in those 12 | // cases. leaving them in for fuzz targets to match other cases, and In Case Of Future Change. 13 | fuzz_target!(|data: &[u8]| { 14 | let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); 15 | // let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); 16 | // let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); 17 | 18 | if let Ok(inst) = x86_64_decoder.decode_slice(data) { 19 | for i in 0..inst.operand_count() { 20 | match inst.operand(i) { 21 | yaxpeax_x86::long_mode::Operand::Register(reg) => { 22 | if reg.num() < 4 && reg.class() == yaxpeax_x86::long_mode::register_class::RB { 23 | assert!(false, "instruction has rex.w register that aliases old byte registers"); 24 | } else { 25 | /* not a potentially-unwanted register */ 26 | } 27 | }, 28 | _ => { /* not a relevant operand kind. immediate or memory of some kind. */ } 29 | } 30 | } 31 | }; 32 | 33 | /* 34 | if let Ok(inst) = x86_32_decoder.decode_slice(data) { 35 | for i in 0..inst.operand_count() { 36 | match inst.operand(i) { 37 | Operand::Register(_reg) => { 38 | /* not a potentially-unwanted register */ 39 | }, 40 | _ => { /* not a relevant operand kind. immediate or memory of some kind. */ } 41 | } 42 | } 43 | }; 44 | 45 | if let Ok(inst) = x86_16_decoder.decode_slice(data) { 46 | for i in 0..inst.operand_count() { 47 | match inst.operand(i) { 48 | Operand::Register(_reg) => { 49 | /* not a potentially-unwanted register */ 50 | }, 51 | _ => { /* not a relevant operand kind. immediate or memory of some kind. */ } 52 | } 53 | } 54 | }; 55 | */ 56 | }); 57 | -------------------------------------------------------------------------------- /goodfile: -------------------------------------------------------------------------------- 1 | Build.dependencies({"git", "make", "rustc", "cargo"}) 2 | 3 | Build.metric( 4 | "nightly version", 5 | string.gsub(Build.check_output({"rustc", "--version"}).stdout, '^%s*(.*)%s*$', '%1') 6 | ) 7 | 8 | Step.start("crate") 9 | Step.push("build") 10 | Build.run({"cargo", "build"}) -- `run` automatically records stdout and stderr to log files named after the command 11 | Step.advance("test") 12 | Build.run({"cargo", "test"}, {name="test stdlib/fmt"}) -- artifacts are stored under `name` if that's present 13 | Build.run({"cargo", "test", "--no-default-features"}, {name="test nostdlib/nofmt"}) 14 | Build.run({"cargo", "test", "--no-default-features", "--features", "fmt"}, {name="test nostdlib/fmt"}) 15 | 16 | Step.start("ffi") 17 | Step.push("build") 18 | Build.run({"cargo", "+nightly", "build", "-Z", "build-std", "--release", "--no-default-features", "--target", Build.environment.vars.native_rust_triple}, {cwd="ffi/"}) 19 | 20 | Step.advance("validate") 21 | sopath = "ffi/target/" .. Build.environment.vars.native_rust_triple .. "/release/libyaxpeax_x86_ffi_long_mode.so" 22 | Build.run({"ls", sopath}) 23 | Build.metric( 24 | "libyaxpeax_x86_ffi_long_mode.so size (bytes)", 25 | Build.environment.size(sopath) 26 | ) 27 | Build.artifact(sopath) 28 | 29 | -- now run some perf numbers... 30 | Step.start("perf") 31 | Build.run({"git", "clone", "https://github.com/iximeow/disas-bench.git", "disas-bench"}) 32 | Build.run({"git", "submodule", "update", "--recursive", "--init"}, {cwd="disas-bench"}) 33 | Build.run({"git", "remote", "add", "dev", "../../.."}, {cwd="disas-bench/libs/yaxpeax"}) 34 | Build.run({"git", "fetch", "-a", "dev"}, {cwd="disas-bench/libs/yaxpeax"}) 35 | Build.run({"git", "checkout", Build.sha}, {cwd="disas-bench/libs/yaxpeax"}) 36 | Step.push("build") 37 | Build.run({"make", "make-bench-yaxpeax"}, {cwd="disas-bench/bench/yaxpeax"}) 38 | 39 | Build.metric( 40 | "bench-yaxpeax-fmt size (bytes)", 41 | Build.environment.size("disas-bench/bench/yaxpeax/bench-yaxpeax-fmt") 42 | ) 43 | 44 | Build.metric( 45 | "bench-yaxpeax-no-fmt size (bytes)", 46 | Build.environment.size("disas-bench/bench/yaxpeax/bench-yaxpeax-no-fmt") 47 | ) 48 | 49 | -- fmt 50 | Step.advance("fmt") 51 | bench_start = Build.now_ms() 52 | 53 | Build.run({"./bench-yaxpeax-fmt", "20", "0x400", "0x2460400", "../../input/xul.dll"}, {cwd="disas-bench/bench/yaxpeax"}) 54 | 55 | bench_end = Build.now_ms() 56 | Build.metric("fmt runtime (ms)", bench_end - bench_start) 57 | 58 | -- no-fmt 59 | Step.advance("no-fmt") 60 | bench_start = Build.now_ms() 61 | 62 | Build.run({"./bench-yaxpeax-no-fmt", "20", "0x400", "0x2460400", "../../input/xul.dll"}, {cwd="disas-bench/bench/yaxpeax"}) 63 | 64 | bench_end = Build.now_ms() 65 | Build.metric("no-fmt runtime (ms)", bench_end - bench_start) 66 | 67 | -- perf 68 | 69 | if Build.environment.has("perf") then 70 | perf_setting = Build.check_output({"cat", "/proc/sys/kernel/perf_event_paranoid"}) 71 | -- TODO: roll this up into some perf tools in the lua env. for now, if perf 72 | -- event paranoid is >2 then we'll probably just fail the build trying and 73 | -- failing to run perf. 74 | perf_out = Build.check_output({"perf", "stat", "-x", ";", "-e", "cycles,instructions", "./bench-yaxpeax-no-fmt", "20", "0x400", "0x2460400", "../../input/xul.dll"}, {cwd="disas-bench/bench/yaxpeax"}) 75 | 76 | measurements = {} 77 | 78 | for count, unit, name in perf_out.stderr:gmatch("([^;]*);([^;]*);([^;]*)[^\n]*\n?") do 79 | measurements[name] = tonumber(count) 80 | end 81 | 82 | insts, good, bad, ms = perf_out.stdout:match("Disassembled (%d*) instructions %((%d*) valid, (%d*) bad%), (%d*) ms") 83 | 84 | measurements["decoded"] = tonumber(insts) 85 | measurements["elapsed_ms"] = tonumber(ms) 86 | 87 | local instructions_name = "instructions:u" 88 | local cycles_name = "cycles:u" 89 | if measurements[instructions_name] == nil then 90 | instructions_name = "instructions" 91 | cycles_name = "cycles" 92 | end 93 | 94 | ipc = measurements[instructions_name] / measurements[cycles_name] 95 | Build.metric("no-fmt IPC", string.format("%.3f", ipc)) 96 | inst_per_decode = measurements[instructions_name] / measurements["decoded"] 97 | Build.metric("no-fmt instructions/decode", string.format("%.1f", inst_per_decode)) 98 | ms_per_decode = measurements["elapsed_ms"] / measurements["decoded"] 99 | Build.metric("no-fmt ns/decode", string.format("%.2f", ms_per_decode * 1000000)) 100 | 101 | 102 | perf_out = Build.check_output({"perf", "stat", "-x", ";", "-e", "cycles,instructions", "./bench-yaxpeax-fmt", "20", "0x400", "0x2460400", "../../input/xul.dll"}, {cwd="disas-bench/bench/yaxpeax"}) 103 | 104 | measurements = {} 105 | 106 | for count, unit, name in perf_out.stderr:gmatch("([^;]*);([^;]*);([^;]*)[^\n]*\n?") do 107 | measurements[name] = tonumber(count) 108 | end 109 | 110 | insts, good, bad, ms = perf_out.stdout:match("Disassembled (%d*) instructions %((%d*) valid, (%d*) bad%), (%d*) ms") 111 | 112 | measurements["decoded"] = tonumber(insts) 113 | measurements["elapsed_ms"] = tonumber(ms) 114 | 115 | ipc = measurements[instructions_name] / measurements[cycles_name] 116 | Build.metric("fmt IPC", string.format("%.3f", ipc)) 117 | inst_per_decode = measurements[instructions_name] / measurements["decoded"] 118 | Build.metric("fmt instructions/decode+display", string.format("%.1f", inst_per_decode)) 119 | ms_per_decode = measurements["elapsed_ms"] / measurements["decoded"] 120 | Build.metric("fmt ns/decode+display", string.format("%.2f", ms_per_decode * 1000000)) 121 | end 122 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # `yaxpeax-x86`, a decoder for x86-family instruction sets 2 | //! 3 | //! `yaxpeax-x86` provides x86 decoders, for 64-, 32-, and 16-bit modes. `yaxpeax-x86` also 4 | //! implements traits defined by `yaxpeax_arch`, making it suitable for interchangeable use with 5 | //! other `yaxpeax`-family instruction decoders. 6 | //! 7 | //! ## usage 8 | //! 9 | //! the fastest way to decode an x86 instruction is through [`amd64::InstDecoder::decode_slice()`]: 10 | //! ``` 11 | //! let decoder = yaxpeax_x86::amd64::InstDecoder::default(); 12 | //! 13 | //! let inst = decoder.decode_slice(&[0x33, 0xc0]).unwrap(); 14 | //! 15 | //! #[cfg(features="fmt")] 16 | //! assert_eq!("xor eax, eax", inst.to_string()); 17 | //! ``` 18 | //! 19 | //! instructions, operands, registers, and generally all decoding structures, are in their mode's 20 | //! respective submodule: 21 | //! * `x86_64`/`amd64` decoding is under [`long_mode`] 22 | //! * `x86_32`/`x86` decoding is under [`protected_mode`] 23 | //! * `x86_16`/`8086` decoding is under [`real_mode`] 24 | //! 25 | //! all modes have equivalent data available in a decoded instruction. for example, all modes have 26 | //! library-friendly `Operand` and `RegSpec` types: 27 | //! 28 | //! ``` 29 | //! use yaxpeax_x86::amd64::{InstDecoder, Operand, RegSpec}; 30 | //! 31 | //! let decoder = yaxpeax_x86::amd64::InstDecoder::default(); 32 | //! 33 | //! let inst = decoder.decode_slice(&[0x33, 0x01]).unwrap(); 34 | //! 35 | //! #[cfg(features="fmt")] 36 | //! assert_eq!("xor eax, dword [rcx]", inst.to_string()); 37 | //! 38 | //! assert_eq!(Operand::Register { reg: RegSpec::eax() }, inst.operand(0)); 39 | //! #[cfg(features="fmt")] 40 | //! assert_eq!("eax", inst.operand(0).to_string()); 41 | //! assert_eq!(Operand::MemDeref { base: RegSpec::rcx() }, inst.operand(1)); 42 | //! 43 | //! // an operand in isolation does not know the size of memory it references, if any 44 | //! #[cfg(features="fmt")] 45 | //! assert_eq!("[rcx]", inst.operand(1).to_string()); 46 | //! 47 | //! // and for memory operands, the size must be read from the instruction itself: 48 | //! let mem_size: yaxpeax_x86::amd64::MemoryAccessSize = inst.mem_size().unwrap(); 49 | //! assert_eq!("dword", mem_size.size_name()); 50 | //! 51 | //! // `MemoryAccessSize::size_name()` is how its `Display` impl works, as well: 52 | //! #[cfg(features="fmt")] 53 | //! assert_eq!("dword", mem_size.to_string()); 54 | //! ``` 55 | //! 56 | //! `yaxpeax-x86` can also be used to decode instructions generically through the `yaxpeax-arch` 57 | //! traits: 58 | //! ``` 59 | //! mod decoder { 60 | //! use yaxpeax_arch::{Arch, AddressDisplay, Decoder, Reader, ReaderBuilder}; 61 | //! 62 | //! // have to play some games so this example works right even without `fmt` enabled! 63 | //! #[cfg(feature="fmt")] 64 | //! trait InstBound: std::fmt::Display {} 65 | //! #[cfg(not(feature="fmt"))] 66 | //! trait InstBound {} 67 | //! 68 | //! #[cfg(feature="fmt")] 69 | //! impl InstBound for T {} 70 | //! #[cfg(not(feature="fmt"))] 71 | //! impl InstBound for T {} 72 | //! 73 | //! pub fn decode_stream< 74 | //! 'data, 75 | //! A: yaxpeax_arch::Arch, 76 | //! U: ReaderBuilder, 77 | //! >(data: U) where 78 | //! A::Instruction: InstBound, 79 | //! { 80 | //! let mut reader = ReaderBuilder::read_from(data); 81 | //! let mut address: A::Address = reader.total_offset(); 82 | //! 83 | //! let decoder = A::Decoder::default(); 84 | //! let mut decode_res = decoder.decode(&mut reader); 85 | //! loop { 86 | //! match decode_res { 87 | //! Ok(ref inst) => { 88 | //! #[cfg(feature="fmt")] 89 | //! println!("{}: {}", address.show(), inst); 90 | //! decode_res = decoder.decode(&mut reader); 91 | //! address = reader.total_offset(); 92 | //! } 93 | //! Err(e) => { 94 | //! println!("{}: decode error: {}", address.show(), e); 95 | //! break; 96 | //! } 97 | //! } 98 | //! } 99 | //! } 100 | //! } 101 | //! 102 | //! use yaxpeax_x86::amd64::{Arch as x86_64}; 103 | //! use yaxpeax_arch::{ReaderBuilder, U8Reader}; 104 | //! let data: &[u8] = &[0x55, 0x33, 0xc0, 0x48, 0x8b, 0x02, 0x5d, 0xc3]; 105 | //! decoder::decode_stream::(data); 106 | //! ``` 107 | //! 108 | //! ## `#![no_std]` 109 | //! 110 | //! `yaxpeax-x86` supports `no_std` usage. to be built `no_std`, `yaxpeax-x86` only needs 111 | //! `default-features = false` in the corresponding `Cargo.toml` dependency. if formatting is 112 | //! needed with `std` disabled, it can be re-enabled by explicitly requesting the `fmt` features 113 | //! like: 114 | //! ```text 115 | //! yaxpeax-x86 = { version = "*", default-features = false, features = ["fmt"] } 116 | //! ``` 117 | //! 118 | //! this is how the `.so` and `.a` packaging in 119 | //! [`ffi/`](https://github.com/iximeow/yaxpeax-x86/tree/no-gods-no-/ffi) is performed. 120 | 121 | #![no_std] 122 | 123 | #[cfg(feature="use-serde")] 124 | #[macro_use] extern crate serde_derive; 125 | #[cfg(feature="use-serde")] 126 | extern crate serde; 127 | 128 | #[cfg(feature="std")] 129 | extern crate alloc; 130 | 131 | #[macro_use] 132 | mod isa_settings; 133 | 134 | pub mod long_mode; 135 | pub use long_mode as amd64; 136 | pub use long_mode::Arch as x86_64; 137 | 138 | pub mod protected_mode; 139 | pub use protected_mode::Arch as x86_32; 140 | 141 | pub mod real_mode; 142 | pub use real_mode::Arch as x86_16; 143 | 144 | // this exists to size `InstructionTextBuffer`'s buffer. it ideally would come from an `Arch` 145 | // impl, or something related to `Arch`, but i'm not yet sure how to wire that up into 146 | // yaxpeax-arch. so instead calculate an appropriate max size for all of 16-bit/32-bit/64-bit 147 | // instruction printing that `InstructionTextBuffer` can be used for. 148 | // 149 | // `InstructionTextBuffer` prints an `InstructionDisplayer`, which means either intel syntax or 150 | // pseudo-C. in the future, at&t probably, as well. 151 | // 152 | // the pseudo-C syntax's max length would be something like: 153 | // ``` 154 | // xacquire xrelease lock { repnz qword if /* signed */ greater_or_equal(rflags) then jmp gs:[xmm31 + 155 | // xmm31 * 8 + 0x12345678]{k7}{z}{rne-sae} } 156 | // ``` 157 | // (which is nonsensical) or for an unknown opcode, 158 | // ``` 159 | // xacquire xrelease lock { op0 = op(op0, op1, op2, op3) } 160 | // ``` 161 | // where `opN` is an operand. the longest operand, same as above, would be something like 162 | // ``` 163 | // gs:[xmm31 + xmm31 * 8 + 0x12345678]{k7}{z}{rne-sae} 164 | // ``` 165 | // for a length like 262 bytes of operand, 55 bytes of prefixes and syntax, and another up-to-20 166 | // bytes of opcode. 167 | // 168 | // the longest contextualize_c might write is around 337 bytes. round up to 512 because it's.. not 169 | // much extra. 170 | // 171 | // the same reasoning for intel syntax yields a smaller instruction: 172 | // ``` 173 | // xacquire xrelease lock op op1, op2, op3, op4 174 | // ``` 175 | // where the longest operands are the same as above. this comes out to closer to 307 bytes. 512 176 | // bytes is still the longest of the two options. 177 | #[allow(dead_code)] // can be an unused constant in some library configurations 178 | const MAX_INSTRUCTION_LEN: usize = 512; 179 | 180 | const MEM_SIZE_STRINGS: [&'static str; 65] = [ 181 | "BUG", 182 | "byte", "word", "BUG", "dword", "ptr", "far", "BUG", "qword", 183 | "BUG", "mword", "BUG", "BUG", "BUG", "BUG", "BUG", "xmmword", 184 | "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", 185 | "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "ymmword", 186 | "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", 187 | "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "m384b", 188 | "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", 189 | "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "ptr", "zmmword", 190 | ]; 191 | 192 | pub struct MemoryAccessSize { 193 | size: u8, 194 | } 195 | impl MemoryAccessSize { 196 | /// return the number of bytes referenced by this memory access. 197 | /// 198 | /// if the number of bytes cannot be confidently known by the instruction in isolation (as is 199 | /// the case for `xsave`/`xrstor`-style "operate on all processor state" instructions), this 200 | /// function will return `None`. 201 | pub fn bytes_size(&self) -> Option { 202 | if self.size == 63 { 203 | None 204 | } else { 205 | Some(self.size) 206 | } 207 | } 208 | 209 | /// a human-friendly label for the number of bytes this memory access references. 210 | /// 211 | /// there are some differences from size names that may be expected elsewhere; `yaxpeax-x86` 212 | /// prefers to use consistent names for a width even if the way those bytes are used varies. 213 | /// 214 | /// the sizes `yaxpeax-x86` knows are as follows: 215 | /// | size (bytes) | name | 216 | /// |--------------|------------| 217 | /// | 1 | `byte` | 218 | /// | 2 | `word` | 219 | /// | 4 | `dword` | 220 | /// | 6 | `far` | 221 | /// | 8 | `qword` | 222 | /// | 10 | `mword` | 223 | /// | 16 | `xmmword` | 224 | /// | 32 | `ymmword` | 225 | /// | 64 | `zmmword` | 226 | /// | variable | `ptr` | 227 | /// 228 | /// "mword" refers to an mmx-sized access - 80 bits, or 10 bytes. `mword` is also used for 229 | /// 64-bit far calls, because they reference a contiguous ten bytes; two bytes of segment 230 | /// selector and eight bytes of address. 231 | /// 232 | /// "variable" accesses access a number of bytes dependent on the physical processor and its 233 | /// operating mode. this is particularly relevant for `xsave`/`xrstor`-style instructions. 234 | pub fn size_name(&self) -> &'static str { 235 | MEM_SIZE_STRINGS[self.size as usize] 236 | } 237 | } 238 | 239 | #[cfg(feature = "fmt")] 240 | impl core::fmt::Display for MemoryAccessSize { 241 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 242 | f.write_str(self.size_name()) 243 | } 244 | } 245 | 246 | #[cfg(feature = "fmt")] 247 | impl core::fmt::Debug for MemoryAccessSize { 248 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 249 | core::fmt::Display::fmt(self, f) 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/long_mode/evex.rs: -------------------------------------------------------------------------------- 1 | // use crate::long_mode::{OperandSpec, DecodeError, RegSpec, RegisterBank, Instruction, Opcode}; 2 | use crate::long_mode::{Arch, DecodeError, RegSpec, RegisterBank, Instruction, Opcode}; 3 | use crate::long_mode::{read_modrm, read_E_vex, read_imm_unsigned}; 4 | use yaxpeax_arch::Reader; 5 | 6 | const DEFAULT_EVEX_REGISTER_SIZE: RegisterBank = RegisterBank::Q; 7 | const DEFAULT_EVEX_REGISTER_WIDTH: u8 = 8; 8 | 9 | fn isa_has_qwords() -> bool { 10 | true 11 | } 12 | 13 | include!("../shared/generated_evex.in"); 14 | include!("../shared/evex.in"); 15 | -------------------------------------------------------------------------------- /src/long_mode/isa_settings.rs: -------------------------------------------------------------------------------- 1 | use super::{BMI1, BMI2, DecodeError, InstDecoder, Instruction, Opcode}; 2 | 3 | crate::isa_settings::gen_arch_isa_settings!(Instruction, Opcode, DecodeError, InstDecoder); 4 | -------------------------------------------------------------------------------- /src/long_mode/uarch.rs: -------------------------------------------------------------------------------- 1 | //! information for AMD and Intel microarchitectures in the modules below is sourced from a 2 | //! combination of Wikipedia (especially for dates), one-off research for particular 3 | //! microarchitectures, and `InstLatx64`'s CPUID dumps via [chip directory](https://github.com/iximeow/chip_directory). 4 | //! 5 | //! these microarchitecture-specific decoders are relatively rarely used, but generally should be 6 | //! accurate. 7 | 8 | pub mod amd { 9 | //! initial information for the mircoarchitecture (families) described here came from a 10 | //! combination of the Wikipedia pages 11 | //! [https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview](https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview) 12 | //! and 13 | //! [https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features](https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features). 14 | //! it has been since "augmented" by the CPUID dumps from InstLatx64, via [chip 15 | //! directory](https://github.com/iximeow/chip_directory/tree/no-gods-no-/x86). scare quotes 16 | //! because in several cases CPUID measurement error adds, rather than removes, ambiguity. 17 | //! additionally, for some CPU features, InstLatx64 has CPUID dumps of early engineering 18 | //! samples where features are not present. later production steppings of those parts do 19 | //! universally have the corresponding feature, which makes it less obvious which features are 20 | //! universally present in a family, standardized in a following architecture, unevenly present 21 | //! due to market segmentation, and so on. 22 | //! 23 | //! microarchitectures as defined here are with respect to flags reported by CPUID. notably, 24 | //! `Zen` does not report `FMA4` support by `CPUID`, but instructions in that extension 25 | //! reportedly function correctly (agner p217). 26 | //! 27 | //! [agner](https://www.agner.org/optimize/microarchitecture.pdf) 28 | //! as retrieved 2020 may 19, 29 | //! `sha256: 87ff152ae18c017dcbfb9f7ee6e88a9f971f6250fd15a70a3dd87c3546323bd5` 30 | 31 | use crate::long_mode::InstDecoder; 32 | 33 | /// `k8` was the first AMD microarchitecture to implement x86_64, launched in 2003. while later 34 | /// `k8`-based processors supported SSE3, these predefined decoders pick the lower end of 35 | /// support - SSE2 and no later. 36 | pub fn k8() -> InstDecoder { 37 | InstDecoder::minimal() 38 | .with_3dnow() 39 | .with_3dnowprefetch() 40 | .with_cmov() 41 | } 42 | 43 | /// `k10` was the successor to `k8`, launched in 2007. `k10` cores extended SSE support through 44 | /// to SSE4.2a, as well as consistent `cmov` support, among other features. 45 | pub fn k10() -> InstDecoder { 46 | k8() 47 | .with_cmpxchg16b() 48 | .with_svm() 49 | .with_abm() 50 | .with_lahfsahf() 51 | .with_sse3() 52 | .with_sse4a() 53 | } 54 | 55 | /// `Bulldozer` was the successor to `K10`, launched in 2011. `Bulldozer` cores include AVX 56 | /// support among other extensions, and are notable for including `AESNI`. `Bulldozer` was also 57 | /// the first microarchitecture to *remove* support for 3DNow instructions. 58 | pub fn bulldozer() -> InstDecoder { 59 | InstDecoder::minimal() 60 | // first, apply all the K8 extensions again, sans 3DNow 61 | // .. should be sse, sse2 62 | // then the K10 63 | .with_cmpxchg16b() 64 | .with_svm() 65 | .with_abm() 66 | .with_lahfsahf() 67 | .with_sse3() 68 | .with_sse4a() 69 | // now the new extensions 70 | .with_ssse3() 71 | .with_sse4() 72 | .with_sse4_2() 73 | .with_bmi1() 74 | .with_aesni() 75 | .with_pclmulqdq() 76 | .with_f16c() 77 | .with_avx() 78 | .with_fma4() 79 | .with_xop() 80 | .with_xsave() 81 | .with_skinit() 82 | } 83 | 84 | /// `Piledriver` was the successor to `Bulldozer`, launched in 2012. 85 | pub fn piledriver() -> InstDecoder { 86 | bulldozer() 87 | .with_tbm() 88 | .with_fma3() 89 | .with_fma4() 90 | } 91 | 92 | /// `Steamroller` was the successor to `Piledriver`, launched in 2014. unlike `Piledriver` 93 | /// cores, these cores do not support `TBM` or `FMA3`. 94 | pub fn steamroller() -> InstDecoder { 95 | bulldozer() 96 | } 97 | 98 | /// `Excavator` was the successor to `Steamroller`, launched in 2015. 99 | pub fn excavator() -> InstDecoder { 100 | steamroller() 101 | .with_movbe() 102 | .with_bmi2() 103 | .with_rdrand() 104 | .with_avx() 105 | .with_xop() 106 | .with_bmi2() 107 | .with_sha() 108 | .with_rdrand() 109 | .with_avx2() 110 | } 111 | 112 | /// `Zen` was the successor to `Excavator`, launched in 2017. `Zen` cores extend SIMD 113 | /// instructions to AVX2 and discarded FMA4, TBM, and XOP extensions. they also gained ADX, 114 | /// SHA, RDSEED, and other extensions. 115 | pub fn zen() -> InstDecoder { 116 | // no nice way to *un*set feature bits, but several extensions were dropped. 117 | // so, start again. 118 | InstDecoder::minimal() 119 | // first, apply all the K8 extensions again, sans 3DNow 120 | // .. should be sse, sse2 121 | // then the K10 122 | .with_cmpxchg16b() 123 | .with_svm() 124 | .with_abm() 125 | .with_lahfsahf() 126 | .with_sse3() 127 | .with_sse4a() 128 | // now, bundle all the K10->Bulldozer features.. 129 | .with_ssse3() 130 | .with_sse4() 131 | .with_sse4_2() 132 | .with_bmi1() 133 | .with_aesni() 134 | .with_pclmulqdq() 135 | .with_f16c() 136 | .with_avx() 137 | .with_xsave() 138 | .with_skinit() 139 | // finally all the Bulldozer (/Piledriver/Steamroller/Excavator)->Zen features 140 | .with_avx2() 141 | .with_movbe() 142 | .with_bmi2() 143 | .with_adx() 144 | .with_sha() 145 | .with_rdrand() 146 | .with_rdseed() 147 | .with_fma3() 148 | 149 | .with_xsavec() 150 | .with_xsaves() 151 | .with_xsaveopt() 152 | .with_clflushopt() 153 | .with_clwb() 154 | .with_fsgsbase() 155 | .with_monitorx() 156 | } 157 | 158 | /// `Zen 2`, launched in 2019, succeeded `Zen`/`Zen+`. there aren't many instruction set 159 | /// extensions here, but `clwb`, `rdpid`, and `wbnoinvd` show up here. 160 | pub fn zen2() -> InstDecoder { 161 | zen() 162 | .with_clwb() 163 | .with_rdpid() 164 | .with_wbnoinvd() 165 | } 166 | 167 | /// `Zen 3`, launched in 2020, succeeded `Zen 2`. like `Zen 2`, there aren't many instruction 168 | /// set extensions here. 169 | pub fn zen3() -> InstDecoder { 170 | zen2() 171 | .with_invpcid() 172 | .with_vaes() 173 | .with_vpclmulqdq() 174 | } 175 | 176 | /// `Zen 4`, launched in 2022, succeeded `Zen 3`. `Zen 4` is notable for being the first AMD 177 | /// processor family supporting AVX-512. 178 | pub fn zen4() -> InstDecoder { 179 | zen3() 180 | .with_avx512_f() 181 | .with_avx512_vl() 182 | .with_avx512_bw() 183 | .with_avx512_cd() 184 | .with_avx512_cd() 185 | .with_avx512_vbmi() 186 | .with_avx512_vbmi2() 187 | .with_avx512_vpopcntdq() 188 | .with_gfni() 189 | } 190 | 191 | /// `Zen 5`, launched in 2024, succeeded `Zen 4`. `Zen 5` adds only a few additional 192 | /// instructions; some AVX-512 features, `enqcmd`, and `movdir64b`. 193 | pub fn zen5() -> InstDecoder { 194 | zen4() 195 | .with_movdir64b() 196 | .with_enqcmd() 197 | } 198 | } 199 | 200 | pub mod intel { 201 | //! sourced by walking wikipedia pages. seriously! this stuff is kinda hard to figure out! 202 | 203 | use crate::long_mode::InstDecoder; 204 | 205 | /// `Netburst` was the first Intel microarchitecture to implement x86_64, beginning with the 206 | /// `Prescott` family launched in 2004. while the wider `Netburst` family launched in 2000 207 | /// with only SSE2, the first `x86_64`-supporting incarnation was `Prescott` which indeed 208 | /// included SSE3. 209 | pub fn netburst() -> InstDecoder { 210 | InstDecoder::minimal() 211 | .with_cmov() 212 | .with_cmpxchg16b() 213 | .with_sse3() 214 | } 215 | 216 | /// `Core` was the successor to `Netburst`, launched in 2006. it included up to SSE4, with 217 | /// processors using this architecture shipped under the names "Merom", "Conroe", and 218 | /// "Woodcrest", for mobile, desktop, and server processors respectively. not to be confused 219 | /// with the later `Nehalem` microarchitecture that introduced the `Core i*` product lines, 220 | /// `Core 2 *` processors used the `Core` architecture. 221 | pub fn core() -> InstDecoder { 222 | netburst() 223 | .with_ssse3() 224 | .with_sse4() 225 | } 226 | 227 | /// `Penryn` was the successor to `Core`, launched in early 2008. it added SSE4.1, along with 228 | /// virtualization extensions. 229 | pub fn penryn() -> InstDecoder { 230 | core() 231 | .with_sse4_1() 232 | } 233 | 234 | /// `Nehalem` was the successor to `Penryn`, launched in late 2008. not to be confused with the 235 | /// earlier `Core` microarchitecture, the `Core i*` products were based on `Nehalem` cores. 236 | /// `Nehalem` added SSE4.2 extensions, along with the `POPCNT` instruction. 237 | pub fn nehalem() -> InstDecoder { 238 | penryn() 239 | .with_sse4_2() 240 | .with_popcnt() 241 | } 242 | 243 | /// `Westmere` was the successor to `Nehalem`, launched in 2010. it added AES-NI and CLMUL 244 | /// extensions. 245 | pub fn westmere() -> InstDecoder { 246 | nehalem() 247 | .with_aesni() 248 | .with_pclmulqdq() 249 | } 250 | 251 | /// `Sandy Bridge` was the successor to `Westmere`, launched in 2011. it added AVX 252 | /// instructions. 253 | pub fn sandybridge() -> InstDecoder { 254 | westmere() 255 | .with_avx() 256 | } 257 | 258 | /// `Ivy Bridge` was the successor to `Sandy Bridge`, launched in 2012. it added F16C 259 | /// extensions for 16-bit floating point conversion, and the RDRAND instruction. 260 | pub fn ivybridge() -> InstDecoder { 261 | sandybridge() 262 | .with_f16c() 263 | .with_rdrand() 264 | } 265 | 266 | /// `Haswell` was the successor to `Ivy Bridge`, launched in 2013. it added several instruction 267 | /// set extensions: AVX2, BMI1, BMI2, ABM, and FMA3. 268 | pub fn haswell() -> InstDecoder { 269 | ivybridge() 270 | .with_bmi1() 271 | .with_bmi2() 272 | .with_abm() 273 | .with_fma3() 274 | .with_avx2() 275 | } 276 | 277 | /// `Haswell-EX` was a variant of `Haswell` launched in 2015 with functional TSX. these cores 278 | /// were shipped as `E7-48xx/E7-88xx v3` models of processors. 279 | pub fn haswell_ex() -> InstDecoder { 280 | haswell() 281 | .with_tsx() 282 | } 283 | 284 | /// `Broadwell` was the successor to `Haswell`, launched in late 2014. it added ADX, RDSEED, 285 | /// and PREFETCHW, as well as broadly rolling out TSX. TSX is enabled on this decoder because 286 | /// some chips of this microarchitecture rolled out with TSX, and lack of TSX seems to be 287 | /// reported as an errata (for example, the `Broadwell-Y` line of parts). 288 | pub fn broadwell() -> InstDecoder { 289 | haswell_ex() 290 | .with_adx() 291 | .with_rdseed() 292 | .with_prefetchw() 293 | } 294 | 295 | /// `Skylake` was the successor to `Broadwell`, launched in mid 2015. it added MPX and SGX 296 | /// extensions, as well as a mixed rollout of AVX512 in different subsets for different product 297 | /// lines. 298 | /// 299 | /// AVX512 is not enabled on this decoder by default because there doesn't seem to be a lowest 300 | /// common denominator: if you want a `Skylake` decoder with AVX512, something like the 301 | /// following: 302 | /// ``` 303 | /// yaxpeax_x86::long_mode::uarch::intel::skylake() 304 | /// .with_avx512_f() 305 | /// .with_avx512_dq(); 306 | /// ``` 307 | /// is likely your best option. 308 | pub fn skylake() -> InstDecoder { 309 | broadwell() 310 | .with_mpx() 311 | .with_sgx() 312 | } 313 | 314 | /// `Kaby Lake` was the successor to `Sky Lake`, launched in 2016. it adds no extensions to 315 | /// x86_64 implementation beyond `skylake`. 316 | pub fn kabylake() -> InstDecoder { 317 | skylake() 318 | } 319 | // ice lake is shipping so that should probably be included... 320 | } 321 | -------------------------------------------------------------------------------- /src/protected_mode/evex.rs: -------------------------------------------------------------------------------- 1 | // use crate::long_mode::{OperandSpec, DecodeError, RegSpec, RegisterBank, Instruction, Opcode}; 2 | use crate::protected_mode::{Arch, DecodeError, RegSpec, RegisterBank, Instruction, Opcode}; 3 | use crate::protected_mode::{read_modrm, read_E_vex, read_imm_unsigned}; 4 | use yaxpeax_arch::Reader; 5 | 6 | const DEFAULT_EVEX_REGISTER_SIZE: RegisterBank = RegisterBank::D; 7 | const DEFAULT_EVEX_REGISTER_WIDTH: u8 = 4; 8 | 9 | fn isa_has_qwords() -> bool { 10 | false 11 | } 12 | 13 | include!("../shared/generated_evex.in"); 14 | include!("../shared/evex.in"); 15 | -------------------------------------------------------------------------------- /src/protected_mode/isa_settings.rs: -------------------------------------------------------------------------------- 1 | use super::{BMI1, BMI2, DecodeError, InstDecoder, Instruction, Opcode}; 2 | 3 | crate::isa_settings::gen_arch_isa_settings!(Instruction, Opcode, DecodeError, InstDecoder); 4 | -------------------------------------------------------------------------------- /src/protected_mode/uarch.rs: -------------------------------------------------------------------------------- 1 | //! information for AMD and Intel microarchitectures in the modules below is sourced from a 2 | //! combination of Wikipedia (especially for dates), one-off research for particular 3 | //! microarchitectures, and `InstLatx64`'s CPUID dumps via [chip directory](https://github.com/iximeow/chip_directory). 4 | //! 5 | //! these microarchitecture-specific decoders are relatively rarely used, but generally should be 6 | //! accurate. 7 | 8 | pub mod amd { 9 | //! initial information for the mircoarchitecture (families) described here came from a 10 | //! combination of the Wikipedia pages 11 | //! [https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview](https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview) 12 | //! and 13 | //! [https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features](https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features). 14 | //! it has been since "augmented" by the CPUID dumps from InstLatx64, via [chip 15 | //! directory](https://github.com/iximeow/chip_directory/tree/no-gods-no-/x86). scare quotes 16 | //! because in several cases CPUID measurement error adds, rather than removes, ambiguity. 17 | //! additionally, for some CPU features, InstLatx64 has CPUID dumps of early engineering 18 | //! samples where features are not present. later production steppings of those parts do 19 | //! universally have the corresponding feature, which makes it less obvious which features are 20 | //! universally present in a family, standardized in a following architecture, unevenly present 21 | //! due to market segmentation, and so on. 22 | //! 23 | //! microarchitectures as defined here are with respect to flags reported by CPUID. notably, 24 | //! `Zen` does not report `FMA4` support by `CPUID`, but instructions in that extension 25 | //! reportedly function correctly (agner p217). 26 | //! 27 | //! [agner](https://www.agner.org/optimize/microarchitecture.pdf) 28 | //! as retrieved 2020 may 19, 29 | //! `sha256: 87ff152ae18c017dcbfb9f7ee6e88a9f971f6250fd15a70a3dd87c3546323bd5` 30 | 31 | use crate::protected_mode::InstDecoder; 32 | 33 | /// `k8` was the first AMD microarchitecture to implement x86_64, launched in 2003. while later 34 | /// `k8`-based processors supported SSE3, these predefined decoders pick the lower end of 35 | /// support - SSE2 and no later. 36 | pub fn k8() -> InstDecoder { 37 | InstDecoder::minimal() 38 | .with_3dnow() 39 | .with_3dnowprefetch() 40 | .with_cmov() 41 | } 42 | 43 | /// `k10` was the successor to `k8`, launched in 2007. `k10` cores extended SSE support through 44 | /// to SSE4.2a, as well as consistent `cmov` support, among other features. 45 | pub fn k10() -> InstDecoder { 46 | k8() 47 | .with_cmpxchg16b() 48 | .with_svm() 49 | .with_abm() 50 | .with_lahfsahf() 51 | .with_sse3() 52 | .with_sse4a() 53 | } 54 | 55 | /// `Bulldozer` was the successor to `K10`, launched in 2011. `Bulldozer` cores include AVX 56 | /// support among other extensions, and are notable for including `AESNI`. `Bulldozer` was also 57 | /// the first microarchitecture to *remove* support for 3DNow instructions. 58 | pub fn bulldozer() -> InstDecoder { 59 | InstDecoder::minimal() 60 | // first, apply all the K8 extensions again, sans 3DNow 61 | // .. should be sse, sse2 62 | // then the K10 63 | .with_cmpxchg16b() 64 | .with_svm() 65 | .with_abm() 66 | .with_lahfsahf() 67 | .with_sse3() 68 | .with_sse4a() 69 | // now the new extensions 70 | .with_ssse3() 71 | .with_sse4() 72 | .with_sse4_2() 73 | .with_bmi1() 74 | .with_aesni() 75 | .with_pclmulqdq() 76 | .with_f16c() 77 | .with_avx() 78 | .with_fma4() 79 | .with_xop() 80 | .with_xsave() 81 | .with_skinit() 82 | } 83 | 84 | /// `Piledriver` was the successor to `Bulldozer`, launched in 2012. 85 | pub fn piledriver() -> InstDecoder { 86 | bulldozer() 87 | .with_tbm() 88 | .with_fma3() 89 | .with_fma4() 90 | } 91 | 92 | /// `Steamroller` was the successor to `Piledriver`, launched in 2014. unlike `Piledriver` 93 | /// cores, these cores do not support `TBM` or `FMA3`. 94 | pub fn steamroller() -> InstDecoder { 95 | bulldozer() 96 | } 97 | 98 | /// `Excavator` was the successor to `Steamroller`, launched in 2015. 99 | pub fn excavator() -> InstDecoder { 100 | steamroller() 101 | .with_movbe() 102 | .with_bmi2() 103 | .with_rdrand() 104 | .with_avx() 105 | .with_xop() 106 | .with_bmi2() 107 | .with_sha() 108 | .with_rdrand() 109 | .with_avx2() 110 | } 111 | 112 | /// `Zen` was the successor to `Excavator`, launched in 2017. `Zen` cores extend SIMD 113 | /// instructions to AVX2 and discarded FMA4, TBM, and XOP extensions. they also gained ADX, 114 | /// SHA, RDSEED, and other extensions. 115 | pub fn zen() -> InstDecoder { 116 | // no nice way to *un*set feature bits, but several extensions were dropped. 117 | // so, start again. 118 | InstDecoder::minimal() 119 | // first, apply all the K8 extensions again, sans 3DNow 120 | // .. should be sse, sse2 121 | // then the K10 122 | .with_cmpxchg16b() 123 | .with_svm() 124 | .with_abm() 125 | .with_lahfsahf() 126 | .with_sse3() 127 | .with_sse4a() 128 | // now, bundle all the K10->Bulldozer features.. 129 | .with_ssse3() 130 | .with_sse4() 131 | .with_sse4_2() 132 | .with_bmi1() 133 | .with_aesni() 134 | .with_pclmulqdq() 135 | .with_f16c() 136 | .with_avx() 137 | .with_xsave() 138 | .with_skinit() 139 | // finally all the Bulldozer (/Piledriver/Steamroller/Excavator)->Zen features 140 | .with_avx2() 141 | .with_movbe() 142 | .with_bmi2() 143 | .with_adx() 144 | .with_sha() 145 | .with_rdrand() 146 | .with_rdseed() 147 | .with_fma3() 148 | 149 | .with_xsavec() 150 | .with_xsaves() 151 | .with_xsaveopt() 152 | .with_clflushopt() 153 | .with_clwb() 154 | .with_fsgsbase() 155 | .with_monitorx() 156 | } 157 | 158 | /// `Zen 2`, launched in 2019, succeeded `Zen`/`Zen+`. there aren't many instruction set 159 | /// extensions here, but `clwb`, `rdpid`, and `wbnoinvd` show up here. 160 | pub fn zen2() -> InstDecoder { 161 | zen() 162 | .with_clwb() 163 | .with_rdpid() 164 | .with_wbnoinvd() 165 | } 166 | 167 | /// `Zen 3`, launched in 2020, succeeded `Zen 2`. like `Zen 2`, there aren't many instruction 168 | /// set extensions here. 169 | pub fn zen3() -> InstDecoder { 170 | zen2() 171 | .with_invpcid() 172 | .with_vaes() 173 | .with_vpclmulqdq() 174 | } 175 | 176 | /// `Zen 4`, launched in 2022, succeeded `Zen 3`. `Zen 4` is notable for being the first AMD 177 | /// processor family supporting AVX-512. 178 | pub fn zen4() -> InstDecoder { 179 | zen3() 180 | .with_avx512_f() 181 | .with_avx512_vl() 182 | .with_avx512_bw() 183 | .with_avx512_cd() 184 | .with_avx512_cd() 185 | .with_avx512_vbmi() 186 | .with_avx512_vbmi2() 187 | .with_avx512_vpopcntdq() 188 | .with_gfni() 189 | } 190 | 191 | /// `Zen 5`, launched in 2024, succeeded `Zen 4`. `Zen 5` adds only a few additional 192 | /// instructions; some AVX-512 features, `enqcmd`, and `movdir64b`. 193 | pub fn zen5() -> InstDecoder { 194 | zen4() 195 | .with_movdir64b() 196 | .with_enqcmd() 197 | } 198 | } 199 | 200 | pub mod intel { 201 | //! sourced by walking wikipedia pages. seriously! this stuff is kinda hard to figure out! 202 | 203 | use crate::protected_mode::InstDecoder; 204 | 205 | /// `Netburst` was the first Intel microarchitecture to implement x86_64, beginning with the 206 | /// `Prescott` family launched in 2004. while the wider `Netburst` family launched in 2000 207 | /// with only SSE2, the first `x86_64`-supporting incarnation was `Prescott` which indeed 208 | /// included SSE3. 209 | pub fn netburst() -> InstDecoder { 210 | InstDecoder::minimal() 211 | .with_cmov() 212 | .with_sse3() 213 | } 214 | 215 | /// `Core` was the successor to `Netburst`, launched in 2006. it included up to SSE4, with 216 | /// processors using this architecture shipped under the names "Merom", "Conroe", and 217 | /// "Woodcrest", for mobile, desktop, and server processors respectively. not to be confused 218 | /// with the later `Nehalem` microarchitecture that introduced the `Core i*` product lines, 219 | /// `Core 2 *` processors used the `Core` architecture. 220 | pub fn core() -> InstDecoder { 221 | netburst() 222 | .with_ssse3() 223 | .with_sse4() 224 | } 225 | 226 | /// `Penryn` was the successor to `Core`, launched in early 2008. it added SSE4.1, along with 227 | /// virtualization extensions. 228 | pub fn penryn() -> InstDecoder { 229 | core() 230 | .with_sse4_1() 231 | } 232 | 233 | /// `Nehalem` was the successor to `Penryn`, launched in late 2008. not to be confused with the 234 | /// earlier `Core` microarchitecture, the `Core i*` products were based on `Nehalem` cores. 235 | /// `Nehalem` added SSE4.2 extensions, along with the `POPCNT` instruction. 236 | pub fn nehalem() -> InstDecoder { 237 | penryn() 238 | .with_sse4_2() 239 | .with_popcnt() 240 | } 241 | 242 | /// `Westmere` was the successor to `Nehalem`, launched in 2010. it added AES-NI and CLMUL 243 | /// extensions. 244 | pub fn westmere() -> InstDecoder { 245 | nehalem() 246 | .with_aesni() 247 | .with_pclmulqdq() 248 | } 249 | 250 | /// `Sandy Bridge` was the successor to `Westmere`, launched in 2011. it added AVX 251 | /// instructions. 252 | pub fn sandybridge() -> InstDecoder { 253 | westmere() 254 | .with_avx() 255 | } 256 | 257 | /// `Ivy Bridge` was the successor to `Sandy Bridge`, launched in 2012. it added F16C 258 | /// extensions for 16-bit floating point conversion, and the RDRAND instruction. 259 | pub fn ivybridge() -> InstDecoder { 260 | sandybridge() 261 | .with_f16c() 262 | .with_rdrand() 263 | } 264 | 265 | /// `Haswell` was the successor to `Ivy Bridge`, launched in 2013. it added several instruction 266 | /// set extensions: AVX2, BMI1, BMI2, ABM, and FMA3. 267 | pub fn haswell() -> InstDecoder { 268 | ivybridge() 269 | .with_bmi1() 270 | .with_bmi2() 271 | .with_abm() 272 | .with_fma3() 273 | .with_avx2() 274 | } 275 | 276 | /// `Haswell-EX` was a variant of `Haswell` launched in 2015 with functional TSX. these cores 277 | /// were shipped as `E7-48xx/E7-88xx v3` models of processors. 278 | pub fn haswell_ex() -> InstDecoder { 279 | haswell() 280 | .with_tsx() 281 | } 282 | 283 | /// `Broadwell` was the successor to `Haswell`, launched in late 2014. it added ADX, RDSEED, 284 | /// and PREFETCHW, as well as broadly rolling out TSX. TSX is enabled on this decoder because 285 | /// some chips of this microarchitecture rolled out with TSX, and lack of TSX seems to be 286 | /// reported as an errata (for example, the `Broadwell-Y` line of parts). 287 | pub fn broadwell() -> InstDecoder { 288 | haswell_ex() 289 | .with_adx() 290 | .with_rdseed() 291 | .with_prefetchw() 292 | } 293 | 294 | /// `Skylake` was the successor to `Broadwell`, launched in mid 2015. it added MPX and SGX 295 | /// extensions, as well as a mixed rollout of AVX512 in different subsets for different product 296 | /// lines. 297 | /// 298 | /// AVX512 is not enabled on this decoder by default because there doesn't seem to be a lowest 299 | /// common denominator: if you want a `Skylake` decoder with AVX512, something like the 300 | /// following: 301 | /// ``` 302 | /// yaxpeax_x86::protected_mode::uarch::intel::skylake() 303 | /// .with_avx512_f() 304 | /// .with_avx512_dq(); 305 | /// ``` 306 | /// is likely your best option. 307 | pub fn skylake() -> InstDecoder { 308 | broadwell() 309 | .with_mpx() 310 | .with_sgx() 311 | } 312 | 313 | /// `Kaby Lake` was the successor to `Sky Lake`, launched in 2016. it adds no extensions to 314 | /// x86_64 implementaiton beyond `skylake`. 315 | pub fn kabylake() -> InstDecoder { 316 | skylake() 317 | } 318 | // ice lake is shipping so that should probably be included... 319 | } 320 | -------------------------------------------------------------------------------- /src/real_mode/evex.rs: -------------------------------------------------------------------------------- 1 | // use crate::long_mode::{OperandSpec, DecodeError, RegSpec, RegisterBank, Instruction, Opcode}; 2 | use crate::real_mode::{Arch, DecodeError, RegSpec, RegisterBank, Instruction, Opcode}; 3 | use crate::real_mode::{read_modrm, read_E_vex, read_imm_unsigned}; 4 | use yaxpeax_arch::Reader; 5 | 6 | const DEFAULT_EVEX_REGISTER_SIZE: RegisterBank = RegisterBank::D; 7 | const DEFAULT_EVEX_REGISTER_WIDTH: u8 = 4; 8 | 9 | fn isa_has_qwords() -> bool { 10 | false 11 | } 12 | 13 | include!("../shared/generated_evex.in"); 14 | include!("../shared/evex.in"); 15 | -------------------------------------------------------------------------------- /src/real_mode/isa_settings.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iximeow/yaxpeax-x86/5d4abf4603c46a7e1c1de5b436d699c09dcf8861/src/real_mode/isa_settings.rs -------------------------------------------------------------------------------- /src/real_mode/uarch.rs: -------------------------------------------------------------------------------- 1 | //! information for AMD and Intel microarchitectures in the modules below is sourced from a 2 | //! combination of Wikipedia (especially for dates), one-off research for particular 3 | //! microarchitectures, and `InstLatx64`'s CPUID dumps via [chip directory](https://github.com/iximeow/chip_directory). 4 | //! 5 | //! these microarchitecture-specific decoders are relatively rarely used, but generally should be 6 | //! accurate. 7 | 8 | pub mod amd { 9 | //! initial information for the mircoarchitecture (families) described here came from a 10 | //! combination of the Wikipedia pages 11 | //! [https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview](https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview) 12 | //! and 13 | //! [https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features](https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features). 14 | //! it has been since "augmented" by the CPUID dumps from InstLatx64, via [chip 15 | //! directory](https://github.com/iximeow/chip_directory/tree/no-gods-no-/x86). scare quotes 16 | //! because in several cases CPUID measurement error adds, rather than removes, ambiguity. 17 | //! additionally, for some CPU features, InstLatx64 has CPUID dumps of early engineering 18 | //! samples where features are not present. later production steppings of those parts do 19 | //! universally have the corresponding feature, which makes it less obvious which features are 20 | //! universally present in a family, standardized in a following architecture, unevenly present 21 | //! due to market segmentation, and so on. 22 | //! 23 | //! microarchitectures as defined here are with respect to flags reported by CPUID. notably, 24 | //! `Zen` does not report `FMA4` support by `CPUID`, but instructions in that extension 25 | //! reportedly function correctly (agner p217). 26 | //! 27 | //! [agner](https://www.agner.org/optimize/microarchitecture.pdf) 28 | //! as retrieved 2020 may 19, 29 | //! `sha256: 87ff152ae18c017dcbfb9f7ee6e88a9f971f6250fd15a70a3dd87c3546323bd5` 30 | 31 | use crate::real_mode::InstDecoder; 32 | 33 | /// `k8` was the first AMD microarchitecture to implement x86_64, launched in 2003. while later 34 | /// `k8`-based processors supported SSE3, these predefined decoders pick the lower end of 35 | /// support - SSE2 and no later. 36 | pub fn k8() -> InstDecoder { 37 | InstDecoder::minimal() 38 | .with_3dnow() 39 | .with_3dnowprefetch() 40 | .with_cmov() 41 | } 42 | 43 | /// `k10` was the successor to `k8`, launched in 2007. `k10` cores extended SSE support through 44 | /// to SSE4.2a, as well as consistent `cmov` support, among other features. 45 | pub fn k10() -> InstDecoder { 46 | k8() 47 | .with_cmpxchg16b() 48 | .with_svm() 49 | .with_abm() 50 | .with_lahfsahf() 51 | .with_sse3() 52 | .with_sse4a() 53 | } 54 | 55 | /// `Bulldozer` was the successor to `K10`, launched in 2011. `Bulldozer` cores include AVX 56 | /// support among other extensions, and are notable for including `AESNI`. `Bulldozer` was also 57 | /// the first microarchitecture to *remove* support for 3DNow instructions. 58 | pub fn bulldozer() -> InstDecoder { 59 | InstDecoder::minimal() 60 | // first, apply all the K8 extensions again, sans 3DNow 61 | // .. should be sse, sse2 62 | // then the K10 63 | .with_cmpxchg16b() 64 | .with_svm() 65 | .with_abm() 66 | .with_lahfsahf() 67 | .with_sse3() 68 | .with_sse4a() 69 | // now the new extensions 70 | .with_ssse3() 71 | .with_sse4() 72 | .with_sse4_2() 73 | .with_bmi1() 74 | .with_aesni() 75 | .with_pclmulqdq() 76 | .with_f16c() 77 | .with_avx() 78 | .with_fma4() 79 | .with_xop() 80 | .with_xsave() 81 | .with_skinit() 82 | } 83 | 84 | /// `Piledriver` was the successor to `Bulldozer`, launched in 2012. 85 | pub fn piledriver() -> InstDecoder { 86 | bulldozer() 87 | .with_tbm() 88 | .with_fma3() 89 | .with_fma4() 90 | } 91 | 92 | /// `Steamroller` was the successor to `Piledriver`, launched in 2014. unlike `Piledriver` 93 | /// cores, these cores do not support `TBM` or `FMA3`. 94 | pub fn steamroller() -> InstDecoder { 95 | bulldozer() 96 | } 97 | 98 | /// `Excavator` was the successor to `Steamroller`, launched in 2015. 99 | pub fn excavator() -> InstDecoder { 100 | steamroller() 101 | .with_movbe() 102 | .with_bmi2() 103 | .with_rdrand() 104 | .with_avx() 105 | .with_xop() 106 | .with_bmi2() 107 | .with_sha() 108 | .with_rdrand() 109 | .with_avx2() 110 | } 111 | 112 | /// `Zen` was the successor to `Excavator`, launched in 2017. `Zen` cores extend SIMD 113 | /// instructions to AVX2 and discarded FMA4, TBM, and XOP extensions. they also gained ADX, 114 | /// SHA, RDSEED, and other extensions. 115 | pub fn zen() -> InstDecoder { 116 | // no nice way to *un*set feature bits, but several extensions were dropped. 117 | // so, start again. 118 | InstDecoder::minimal() 119 | // first, apply all the K8 extensions again, sans 3DNow 120 | // .. should be sse, sse2 121 | // then the K10 122 | .with_cmpxchg16b() 123 | .with_svm() 124 | .with_abm() 125 | .with_lahfsahf() 126 | .with_sse3() 127 | .with_sse4a() 128 | // now, bundle all the K10->Bulldozer features.. 129 | .with_ssse3() 130 | .with_sse4() 131 | .with_sse4_2() 132 | .with_bmi1() 133 | .with_aesni() 134 | .with_pclmulqdq() 135 | .with_f16c() 136 | .with_avx() 137 | .with_xsave() 138 | .with_skinit() 139 | // finally all the Bulldozer (/Piledriver/Steamroller/Excavator)->Zen features 140 | .with_avx2() 141 | .with_movbe() 142 | .with_bmi2() 143 | .with_adx() 144 | .with_sha() 145 | .with_rdrand() 146 | .with_rdseed() 147 | .with_fma3() 148 | 149 | .with_xsavec() 150 | .with_xsaves() 151 | .with_xsaveopt() 152 | .with_clflushopt() 153 | .with_clwb() 154 | .with_fsgsbase() 155 | .with_monitorx() 156 | } 157 | 158 | /// `Zen 2`, launched in 2019, succeeded `Zen`/`Zen+`. there aren't many instruction set 159 | /// extensions here, but `clwb`, `rdpid`, and `wbnoinvd` show up here. 160 | pub fn zen2() -> InstDecoder { 161 | zen() 162 | .with_clwb() 163 | .with_rdpid() 164 | .with_wbnoinvd() 165 | } 166 | 167 | /// `Zen 3`, launched in 2020, succeeded `Zen 2`. like `Zen 2`, there aren't many instruction 168 | /// set extensions here. 169 | pub fn zen3() -> InstDecoder { 170 | zen2() 171 | .with_invpcid() 172 | .with_vaes() 173 | .with_vpclmulqdq() 174 | } 175 | 176 | /// `Zen 4`, launched in 2022, succeeded `Zen 3`. `Zen 4` is notable for being the first AMD 177 | /// processor family supporting AVX-512. 178 | pub fn zen4() -> InstDecoder { 179 | zen3() 180 | .with_avx512_f() 181 | .with_avx512_vl() 182 | .with_avx512_bw() 183 | .with_avx512_cd() 184 | .with_avx512_cd() 185 | .with_avx512_vbmi() 186 | .with_avx512_vbmi2() 187 | .with_avx512_vpopcntdq() 188 | .with_gfni() 189 | } 190 | 191 | /// `Zen 5`, launched in 2024, succeeded `Zen 4`. `Zen 5` adds only a few additional 192 | /// instructions; some AVX-512 features, `enqcmd`, and `movdir64b`. 193 | pub fn zen5() -> InstDecoder { 194 | zen4() 195 | .with_movdir64b() 196 | .with_enqcmd() 197 | } 198 | } 199 | 200 | pub mod intel { 201 | //! sourced by walking wikipedia pages. seriously! this stuff is kinda hard to figure out! 202 | 203 | use crate::real_mode::InstDecoder; 204 | 205 | /// `Netburst` was the first Intel microarchitecture to implement x86_64, beginning with the 206 | /// `Prescott` family launched in 2004. while the wider `Netburst` family launched in 2000 207 | /// with only SSE2, the first `x86_64`-supporting incarnation was `Prescott` which indeed 208 | /// included SSE3. 209 | pub fn netburst() -> InstDecoder { 210 | InstDecoder::minimal() 211 | .with_cmov() 212 | .with_sse3() 213 | } 214 | 215 | /// `Core` was the successor to `Netburst`, launched in 2006. it included up to SSE4, with 216 | /// processors using this architecture shipped under the names "Merom", "Conroe", and 217 | /// "Woodcrest", for mobile, desktop, and server processors respectively. not to be confused 218 | /// with the later `Nehalem` microarchitecture that introduced the `Core i*` product lines, 219 | /// `Core 2 *` processors used the `Core` architecture. 220 | pub fn core() -> InstDecoder { 221 | netburst() 222 | .with_ssse3() 223 | .with_sse4() 224 | } 225 | 226 | /// `Penryn` was the successor to `Core`, launched in early 2008. it added SSE4.1, along with 227 | /// virtualization extensions. 228 | pub fn penryn() -> InstDecoder { 229 | core() 230 | .with_sse4_1() 231 | } 232 | 233 | /// `Nehalem` was the successor to `Penryn`, launched in late 2008. not to be confused with the 234 | /// earlier `Core` microarchitecture, the `Core i*` products were based on `Nehalem` cores. 235 | /// `Nehalem` added SSE4.2 extensions, along with the `POPCNT` instruction. 236 | pub fn nehalem() -> InstDecoder { 237 | penryn() 238 | .with_sse4_2() 239 | .with_popcnt() 240 | } 241 | 242 | /// `Westmere` was the successor to `Nehalem`, launched in 2010. it added AES-NI and CLMUL 243 | /// extensions. 244 | pub fn westmere() -> InstDecoder { 245 | nehalem() 246 | .with_aesni() 247 | .with_pclmulqdq() 248 | } 249 | 250 | /// `Sandy Bridge` was the successor to `Westmere`, launched in 2011. it added AVX 251 | /// instructions. 252 | pub fn sandybridge() -> InstDecoder { 253 | westmere() 254 | .with_avx() 255 | } 256 | 257 | /// `Ivy Bridge` was the successor to `Sandy Bridge`, launched in 2012. it added F16C 258 | /// extensions for 16-bit floating point conversion, and the RDRAND instruction. 259 | pub fn ivybridge() -> InstDecoder { 260 | sandybridge() 261 | .with_f16c() 262 | .with_rdrand() 263 | } 264 | 265 | /// `Haswell` was the successor to `Ivy Bridge`, launched in 2013. it added several instruction 266 | /// set extensions: AVX2, BMI1, BMI2, ABM, and FMA3. 267 | pub fn haswell() -> InstDecoder { 268 | ivybridge() 269 | .with_bmi1() 270 | .with_bmi2() 271 | .with_abm() 272 | .with_fma3() 273 | .with_avx2() 274 | } 275 | 276 | /// `Haswell-EX` was a variant of `Haswell` launched in 2015 with functional TSX. these cores 277 | /// were shipped as `E7-48xx/E7-88xx v3` models of processors. 278 | pub fn haswell_ex() -> InstDecoder { 279 | haswell() 280 | .with_tsx() 281 | } 282 | 283 | /// `Broadwell` was the successor to `Haswell`, launched in late 2014. it added ADX, RDSEED, 284 | /// and PREFETCHW, as well as broadly rolling out TSX. TSX is enabled on this decoder because 285 | /// some chips of this microarchitecture rolled out with TSX, and lack of TSX seems to be 286 | /// reported as an errata (for example, the `Broadwell-Y` line of parts). 287 | pub fn broadwell() -> InstDecoder { 288 | haswell_ex() 289 | .with_adx() 290 | .with_rdseed() 291 | .with_prefetchw() 292 | } 293 | 294 | /// `Skylake` was the successor to `Broadwell`, launched in mid 2015. it added MPX and SGX 295 | /// extensions, as well as a mixed rollout of AVX512 in different subsets for different product 296 | /// lines. 297 | /// 298 | /// AVX512 is not enabled on this decoder by default because there doesn't seem to be a lowest 299 | /// common denominator: if you want a `Skylake` decoder with AVX512, something like the 300 | /// following: 301 | /// ``` 302 | /// yaxpeax_x86::real_mode::uarch::intel::skylake() 303 | /// .with_avx512_f() 304 | /// .with_avx512_dq(); 305 | /// ``` 306 | /// is likely your best option. 307 | pub fn skylake() -> InstDecoder { 308 | broadwell() 309 | .with_mpx() 310 | .with_sgx() 311 | } 312 | 313 | /// `Kaby Lake` was the successor to `Sky Lake`, launched in 2016. it adds no extensions to 314 | /// x86_64 implementaiton beyond `skylake`. 315 | pub fn kabylake() -> InstDecoder { 316 | skylake() 317 | } 318 | // ice lake is shipping so that should probably be included... 319 | } 320 | -------------------------------------------------------------------------------- /test/bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | extern crate yaxpeax_x86; 5 | extern crate yaxpeax_arch; 6 | use yaxpeax_arch::Decoder; 7 | 8 | #[cfg(feature = "capstone_bench")] 9 | use std::ffi::c_void; 10 | 11 | #[cfg(feature = "capstone_bench")] 12 | use std::io::Write; 13 | 14 | use test::Bencher; 15 | 16 | /* 17 | use yaxpeax_x86::{Instruction, decode_one}; 18 | 19 | fn decode(bytes: &[u8]) -> Option { 20 | let mut instr = Instruction::invalid(); 21 | match decode_one(bytes.iter().map(|x| *x), &mut instr) { 22 | Some(()) => Some(instr), 23 | None => None 24 | } 25 | } 26 | */ 27 | 28 | #[bench] 29 | fn bench_1020000_instrs(b: &mut Bencher) { 30 | b.iter(|| { 31 | for _i in 0..30000 { 32 | test::black_box(do_decode_swathe()); 33 | } 34 | }) 35 | } 36 | 37 | #[cfg(feature = "capstone_bench")] 38 | #[bench] 39 | fn bench_102000_intrs_capstone(b: &mut Bencher) { 40 | let handle = get_cs_handle(); 41 | // panic!("Allocating.."); 42 | let mut instr: *mut c_void = unsafe { cs_malloc(handle) }; 43 | // panic!("Allocated..."); 44 | b.iter(|| { 45 | for i in (0..3000) { 46 | test::black_box(do_capstone_decode_swathe(handle, instr)); 47 | } 48 | }) 49 | } 50 | 51 | const DECODE_DATA: [u8; 130] = [ 52 | 0x48, 0xc7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 53 | 0x48, 0x89, 0x44, 0x24, 0x08, 54 | 0x48, 0x89, 0x43, 0x18, 55 | 0x48, 0xc7, 0x43, 0x10, 0x00, 0x00, 0x00, 0x00, 56 | 0x49, 0x89, 0x4e, 0x08, 57 | 0x48, 0x8b, 0x32, 58 | 0x49, 0x89, 0x46, 0x10, 59 | 0x4d, 0x0f, 0x43, 0xec, 0x49, 60 | 0x0f, 0xb6, 0x06, 61 | 0x0f, 0xb7, 0x06, 62 | 0x66, 0x41, 0x50, 63 | 0x66, 0x41, 0x31, 0xc0, 64 | 0x66, 0x41, 0x32, 0xc0, 65 | 0x40, 0x32, 0xc5, 66 | 0x73, 0x31, 67 | 0x72, 0x5a, 68 | 0x0f, 0x86, 0x8b, 0x01, 0x00, 0x00, 69 | 0x74, 0x47, 70 | 0xff, 0x15, 0x7e, 0x72, 0x24, 0x00, 71 | 0xc3, 72 | 0x48, 0x3d, 0x01, 0xf0, 0xff, 0xff, 73 | 0x3d, 0x01, 0xf0, 0xff, 0xff, 74 | 0x48, 0x83, 0xf8, 0xff, 75 | 0x48, 0x39, 0xc6, 76 | 0x48, 0x8d, 0xa4, 0xc7, 0x20, 0x00, 0x00, 0x12, 77 | 0x33, 0xc0, 78 | 0x48, 0x8d, 0x53, 0x08, 79 | 0x31, 0xc9, 80 | 0x48, 0x29, 0xc8, 81 | 0x48, 0x03, 0x0b, 82 | 0x5b, 83 | 0x41, 0x5e, 84 | 0x48, 0x8d, 0x0c, 0x12, 85 | 0xf6, 0xc2, 0x18 86 | ]; 87 | 88 | fn do_decode_swathe() { 89 | // let mut buf = [0u8; 128]; 90 | let mut result = yaxpeax_x86::long_mode::Instruction::invalid(); 91 | let mut reader = yaxpeax_arch::U8Reader::new(&DECODE_DATA[..]); 92 | let decoder = yaxpeax_x86::long_mode::InstDecoder::default(); 93 | loop { 94 | match decoder.decode_into(&mut result, &mut reader) { 95 | Ok(()) => { 96 | #[cfg(feature = "capstone_bench")] 97 | test::black_box(write!(&mut buf[..], "{}", result)); 98 | test::black_box(&result); 99 | }, 100 | Err(_) => { 101 | // println!("done."); 102 | break; 103 | } 104 | } 105 | } 106 | } 107 | 108 | #[cfg(feature = "capstone_bench")] 109 | extern "C" { 110 | pub fn cs_open(arch: u32, mode: u32, handle: *mut usize) -> usize; 111 | } 112 | 113 | #[cfg(feature = "capstone_bench")] 114 | extern "C" { 115 | pub fn cs_malloc(handle: usize) -> *mut c_void; 116 | } 117 | 118 | #[cfg(feature = "capstone_bench")] 119 | extern "C" { 120 | pub fn cs_disasm_iter( 121 | arch: usize, 122 | code: *mut *const u8, 123 | size: *mut usize, 124 | address: *mut u64, 125 | insn: *mut c_void 126 | ) -> bool; 127 | } 128 | 129 | #[cfg(feature = "capstone_bench")] 130 | fn get_cs_handle() -> usize { 131 | let mut handle: usize = 0; 132 | let res = unsafe { cs_open(3, 4, &mut handle as *mut usize) }; 133 | handle 134 | } 135 | 136 | #[cfg(feature = "capstone_bench")] 137 | fn get_instr(handle: usize) -> *mut c_void { 138 | unsafe { cs_malloc(handle) as *mut c_void } 139 | } 140 | 141 | #[cfg(feature = "capstone_bench")] 142 | fn do_capstone_decode_swathe(cs: usize, instr: *mut c_void) { 143 | unsafe { 144 | let mut code = &DECODE_DATA as *const u8; 145 | let mut len = DECODE_DATA.len(); 146 | let mut addr = 0u64; 147 | loop { 148 | let result = cs_disasm_iter( 149 | cs, 150 | &mut code as *mut *const u8, 151 | &mut len as *mut usize, 152 | &mut addr as *mut u64, 153 | instr 154 | ); 155 | //panic!("at least one succeeded"); 156 | if result == false { 157 | return; 158 | } 159 | } 160 | } 161 | } 162 | 163 | //#[bench] 164 | //#[ignore] 165 | // VEX prefixes are not supported at the moment, in any form 166 | //fn test_avx() { 167 | // assert_eq!(&format!("{}", decode( 168 | // &[0xc5, 0xf8, 0x10, 0x00] 169 | // ).unwrap()), "vmovups xmm0, xmmword [rax]"); 170 | //} 171 | -------------------------------------------------------------------------------- /test/lib_test.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_disasm() { 3 | let mut instr = Instruction::invalid(); 4 | arch::x86_64::instr::decode_one(&[0x33, 0xc0], &mut instr); 5 | assert_eq!(1, 1); 6 | } 7 | -------------------------------------------------------------------------------- /test/long_mode/descriptions.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Write; 2 | 3 | use yaxpeax_arch::{AddressBase, LengthedInstruction}; 4 | use yaxpeax_arch::annotation::FieldDescription; 5 | use yaxpeax_x86::long_mode::Opcode; 6 | use yaxpeax_x86::long_mode::RegSpec; 7 | use yaxpeax_x86::long_mode::InstDecoder; 8 | use yaxpeax_x86::long_mode::Instruction; 9 | use yaxpeax_x86::long_mode::InnerDescription; 10 | use yaxpeax_arch::annotation::AnnotatingDecoder; 11 | 12 | fn test_annotations(data: &[u8], expected: &'static str, checks: &[AnnotationCheck]) { 13 | test_annotations_under(&InstDecoder::default(), data, expected, checks); 14 | } 15 | 16 | // pair up field descriptions and the check that matched them. we'll use this for 17 | // reporting errors if checks don't match up. 18 | #[derive(PartialEq, Eq, Copy, Clone)] 19 | enum CheckResult { 20 | Matched, 21 | Failed, 22 | Ignored, 23 | } 24 | 25 | impl CheckResult { 26 | fn consumed_check(&self) -> bool { 27 | *self != CheckResult::Ignored 28 | } 29 | } 30 | 31 | struct MatchResult { 32 | check: AnnotationCheck, 33 | result: CheckResult, 34 | } 35 | 36 | #[derive(Clone)] 37 | enum AnnotationCheck { 38 | // does not match any description; intended to assert that there should be no extra annotations 39 | // after the last check. 40 | NoExtra, 41 | Exact { 42 | // check the reported annotation matches this description 43 | desc: InnerDescription, 44 | start_bit: u32, 45 | end_bit: u32, 46 | }, 47 | Approximate { 48 | check: fn(&InnerDescription) -> bool, 49 | start_bit: u32, 50 | end_bit: u32, 51 | } 52 | } 53 | 54 | impl std::fmt::Display for AnnotationCheck { 55 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 56 | match self { 57 | AnnotationCheck::NoExtra => { 58 | write!(f, "no further field descriptions expected") 59 | } 60 | AnnotationCheck::Exact { desc, start_bit, end_bit } => { 61 | write!(f, "bit {}:{}; {}", start_bit, end_bit, desc) 62 | } 63 | AnnotationCheck::Approximate { 64 | start_bit, end_bit, .. 65 | } => { 66 | write!(f, "bit {}:{}; (fn-based match)", start_bit, end_bit) 67 | } 68 | } 69 | } 70 | } 71 | 72 | impl AnnotationCheck { 73 | fn exact(start: u32, end: u32, desc: InnerDescription) -> AnnotationCheck { 74 | AnnotationCheck::Exact { 75 | desc, 76 | start_bit: start, 77 | end_bit: end, 78 | } 79 | } 80 | 81 | fn approximate(start: u32, end: u32, check: fn(&InnerDescription) -> bool) -> AnnotationCheck { 82 | AnnotationCheck::Approximate { 83 | check, 84 | start_bit: start, 85 | end_bit: end, 86 | } 87 | } 88 | 89 | fn no_extra() -> AnnotationCheck { 90 | AnnotationCheck::NoExtra 91 | } 92 | 93 | fn matches(&self, actual_start: u32, actual_end: u32, actual_desc: InnerDescription) -> CheckResult { 94 | match self { 95 | AnnotationCheck::NoExtra => { 96 | CheckResult::Failed 97 | }, 98 | AnnotationCheck::Exact { start_bit, end_bit, desc } => { 99 | let bits_match = *start_bit == actual_start && *end_bit == actual_end; 100 | let desc_match = desc == &actual_desc; 101 | let fail_anyway = match (desc, &actual_desc) { 102 | // expect that there's only one `Number` field with a given name, so if the 103 | // bits are wrong or the value is wrong, that's a guaranteed fail. 104 | (InnerDescription::Number(expected_name, _), InnerDescription::Number(actual_name, _)) => { 105 | if expected_name == actual_name { 106 | true 107 | } else { 108 | false 109 | } 110 | } 111 | // expect that there's only one opcode field. there won't be a second one that 112 | // we might match on later. 113 | (InnerDescription::Opcode(_), InnerDescription::Opcode(_)) => { 114 | true 115 | } 116 | (_expected, _actual) => false 117 | }; 118 | 119 | if (!bits_match && !desc_match) && !fail_anyway { 120 | return CheckResult::Ignored; 121 | } 122 | 123 | if bits_match && desc_match { 124 | CheckResult::Matched 125 | } else { 126 | CheckResult::Failed 127 | } 128 | }, 129 | AnnotationCheck::Approximate { start_bit, end_bit, check } => { 130 | let bits_match = *start_bit == actual_start && *end_bit == actual_end; 131 | let desc_match = check(&actual_desc); 132 | 133 | if !bits_match && !desc_match { 134 | return CheckResult::Ignored; 135 | } 136 | 137 | if bits_match && desc_match { 138 | CheckResult::Matched 139 | } else { 140 | CheckResult::Failed 141 | } 142 | } 143 | } 144 | } 145 | } 146 | 147 | impl std::fmt::Display for CheckResult { 148 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 149 | let s = match self { 150 | CheckResult::Matched => "\x1b[32mmatched\x1b[0m ", 151 | CheckResult::Failed => "\x1b[31mfailed\x1b[0m ", 152 | CheckResult::Ignored => "\x1b[33mignored\x1b[0m", 153 | }; 154 | f.write_str(s) 155 | } 156 | } 157 | 158 | fn test_annotations_under(decoder: &InstDecoder, data: &[u8], expected: &'static str, checks: &[AnnotationCheck]) { 159 | let mut hex = String::new(); 160 | for b in data { 161 | write!(hex, "{:02x}", b).unwrap(); 162 | } 163 | let mut reader = yaxpeax_arch::U8Reader::new(data); 164 | let mut sink = yaxpeax_arch::annotation::VecSink::new(); 165 | let mut inst = Instruction::default(); 166 | match decoder.decode_with_annotation(&mut inst, &mut reader, &mut sink) { 167 | Ok(()) => { 168 | let text = format!("{}", inst); 169 | assert!( 170 | text == expected, 171 | "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", 172 | hex, 173 | inst, 174 | decoder, 175 | text, 176 | expected 177 | ); 178 | 179 | let mut matches: Vec<((u32, u32, InnerDescription), Option)> = Vec::new(); 180 | let mut extra_checks: Vec = Vec::new(); 181 | 182 | sink.records.sort_by_key(|x| x.2.id()); 183 | let mut rec_iter = sink.records.iter(); 184 | let mut check_iter = checks.iter(); 185 | 186 | let mut check = check_iter.next(); 187 | 188 | let mut failed = false; 189 | 190 | while let Some((bit_start, bit_end, desc)) = rec_iter.next().cloned() { 191 | if let Some(curr_check) = check { 192 | if let AnnotationCheck::NoExtra = curr_check { 193 | failed = true; 194 | } 195 | 196 | let check_result = curr_check.matches(bit_start, bit_end, desc.desc().clone()); 197 | if check_result == CheckResult::Failed { 198 | failed = true; 199 | } 200 | 201 | matches.push(((bit_start, bit_end, desc.desc().clone()), Some(MatchResult { 202 | check: curr_check.clone(), 203 | result: check_result 204 | }))); 205 | 206 | if check_result.consumed_check() { 207 | check = check_iter.next(); 208 | } 209 | } else { 210 | // no more checks, so we'll have passed the test at least. continue scooping up 211 | // field descriptions into `matches` with no checks. 212 | matches.push(((bit_start, bit_end, desc.desc().clone()), None)); 213 | } 214 | } 215 | 216 | while let Some(missed_check) = check { 217 | check = check_iter.next(); 218 | if let AnnotationCheck::NoExtra = missed_check { 219 | // "no extra" will be "missed" in that nothing matches it above. in the success 220 | // case, it's a leftover check, and should be the only one remaining if the 221 | // test is written correctly. so skip it here, and see if we've exhausted the 222 | // list of checks.. 223 | continue; 224 | } 225 | extra_checks.push(missed_check.clone()); 226 | } 227 | 228 | if extra_checks.len() > 0 { 229 | failed = true; 230 | } 231 | 232 | if failed { 233 | eprintln!("[!] annotation check for {}, `{}`, failed:", hex, inst); 234 | for ((bit_start, bit_end, desc), check) in matches { 235 | let mut desc = format!("bit {}:{}; {}", bit_start, bit_end, desc); 236 | while desc.len() < 60 { 237 | desc.push(' '); 238 | } 239 | desc.push(' '); 240 | let comment = match check { 241 | None => { 242 | "\x1b[34mno check\x1b[0m".to_owned() 243 | } 244 | Some(MatchResult { 245 | result, 246 | check 247 | }) => { 248 | if result == CheckResult::Ignored { 249 | result.to_string() 250 | } else { 251 | format!("{}{}", result, check) 252 | } 253 | } 254 | }; 255 | eprintln!(" - {}{}", desc, comment); 256 | } 257 | for check in extra_checks { 258 | eprintln!(" ! \x1b[31mextra check\x1b[0m: {}", check); 259 | } 260 | } 261 | assert!(!failed); 262 | 263 | // while we're at it, test that the instruction is as long, and no longer, than its 264 | // input 265 | assert_eq!((0u64.wrapping_offset(inst.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected); 266 | }, 267 | Err(e) => { 268 | cfg_if::cfg_if! { 269 | if #[cfg(feature="fmt")] { 270 | assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected); 271 | } else { 272 | // avoid the unused `e` warning 273 | let _ = e; 274 | assert!(false, "decode error () for {} under decoder :\n expected: {}\n", hex, expected); 275 | } 276 | } 277 | } 278 | } 279 | } 280 | 281 | #[test] 282 | fn test_modrm_decode() { 283 | test_annotations(&[0xff, 0xc0], "inc eax", &[ 284 | AnnotationCheck::exact(11, 13, InnerDescription::Opcode(Opcode::INC)), 285 | AnnotationCheck::approximate(0, 7, |desc| { desc.to_string().contains("ModRM_0xff_Ev") }), 286 | AnnotationCheck::approximate(14, 15, |desc| { 287 | desc.to_string().contains("mmm") && 288 | desc.to_string().contains("register number") && 289 | desc.to_string().contains("mod bits: 11") 290 | }), 291 | AnnotationCheck::exact(8, 10, InnerDescription::RegisterNumber("mmm", 0, RegSpec::eax())), 292 | AnnotationCheck::no_extra(), 293 | ]); 294 | test_annotations(&[0xc1, 0xe0, 0x03], "shl eax, 0x3", &[ 295 | AnnotationCheck::exact(11, 13, InnerDescription::Opcode(Opcode::SHL)), 296 | AnnotationCheck::exact(16, 23, InnerDescription::Number("imm", 3)), 297 | AnnotationCheck::approximate(0, 7, |desc| { desc.to_string().contains("ModRM_0xc1_Ev_Ib") }), 298 | AnnotationCheck::approximate(14, 15, |desc| { 299 | desc.to_string().contains("mmm") && 300 | desc.to_string().contains("register number") && 301 | desc.to_string().contains("mod bits: 11") 302 | }), 303 | AnnotationCheck::exact(8, 10, InnerDescription::RegisterNumber("mmm", 0, RegSpec::eax())), 304 | AnnotationCheck::no_extra(), 305 | ]); 306 | test_annotations(&[0x33, 0x08], "xor ecx, dword [rax]", &[ 307 | AnnotationCheck::exact(0, 7, InnerDescription::Opcode(Opcode::XOR)), 308 | AnnotationCheck::approximate(0, 7, |desc| { desc.to_string() == "operand code `Gv_Ev`" }), 309 | AnnotationCheck::approximate(7, 7, |desc| { desc.to_string().contains("operands begin") }), 310 | AnnotationCheck::approximate(14, 15, |desc| { 311 | desc.to_string().contains("memory operand is [reg]") && 312 | desc.to_string().contains("mod bits: 00") 313 | }), 314 | AnnotationCheck::exact(8, 10, InnerDescription::RegisterNumber("mmm", 0, RegSpec::rax())), 315 | AnnotationCheck::exact(11, 13, InnerDescription::RegisterNumber("rrr", 1, RegSpec::ecx())), 316 | AnnotationCheck::no_extra(), 317 | ]); 318 | test_annotations(&[0x66, 0x0f, 0x38, 0x00, 0xc1], "pshufb xmm0, xmm1", &[ 319 | AnnotationCheck::exact(0, 7, InnerDescription::Misc("operand size override (to 16 bits)")), 320 | AnnotationCheck::approximate(38, 39, |desc| { 321 | desc.to_string().contains("mmm") && 322 | desc.to_string().contains("register number") && 323 | desc.to_string().contains("mod bits: 11") 324 | }), 325 | AnnotationCheck::exact(32, 34, InnerDescription::RegisterNumber("mmm", 1, RegSpec::ecx())), 326 | AnnotationCheck::exact(35, 37, InnerDescription::RegisterNumber("rrr", 0, RegSpec::eax())), 327 | AnnotationCheck::no_extra(), 328 | ]); 329 | 330 | // modrm + rex.w 331 | test_annotations(&[0x48, 0x33, 0x08], "xor rcx, qword [rax]", &[]); 332 | test_annotations(&[0x48, 0x33, 0x20], "xor rsp, qword [rax]", &[]); 333 | test_annotations(&[0x48, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor rax, qword [rip + 0x12345678]", &[]); 334 | 335 | // specifically sib with base == 0b101 336 | // mod bits 00 337 | test_annotations(&[0x42, 0x33, 0x34, 0x25, 0x20, 0x30, 0x40, 0x50], "xor esi, dword [r12 * 1 + 0x50403020]", &[]); 338 | test_annotations(&[0x43, 0x33, 0x34, 0x25, 0x20, 0x30, 0x40, 0x50], "xor esi, dword [r12 * 1 + 0x50403020]", &[]); 339 | // mod bits 01 340 | test_annotations(&[0x42, 0x33, 0x74, 0x25, 0x20], "xor esi, dword [rbp + r12 * 1 + 0x20]", &[]); 341 | test_annotations(&[0x43, 0x33, 0x74, 0x25, 0x20], "xor esi, dword [r13 + r12 * 1 + 0x20]", &[]); 342 | // mod bits 10 343 | test_annotations(&[0x42, 0x33, 0xb4, 0x25, 0x20, 0x30, 0x40, 0x50], "xor esi, dword [rbp + r12 * 1 + 0x50403020]", &[]); 344 | test_annotations(&[0x43, 0x33, 0xb4, 0x25, 0x20, 0x30, 0x40, 0x50], "xor esi, dword [r13 + r12 * 1 + 0x50403020]", &[]); 345 | } 346 | -------------------------------------------------------------------------------- /test/long_mode/display.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Write; 2 | 3 | use yaxpeax_arch::{AddressBase, Decoder, LengthedInstruction}; 4 | use yaxpeax_x86::long_mode::{DisplayStyle, InstDecoder}; 5 | 6 | fn test_display(data: &[u8], expected: &'static str) { 7 | test_display_under(&InstDecoder::default(), DisplayStyle::Intel, data, expected); 8 | } 9 | 10 | fn test_c_display(data: &[u8], expected: &'static str) { 11 | test_display_under(&InstDecoder::default(), DisplayStyle::C, data, expected); 12 | } 13 | 14 | fn test_display_under(decoder: &InstDecoder, style: DisplayStyle, data: &[u8], expected: &'static str) { 15 | let mut hex = String::new(); 16 | for b in data { 17 | write!(hex, "{:02x}", b).unwrap(); 18 | } 19 | let mut reader = yaxpeax_arch::U8Reader::new(data); 20 | match decoder.decode(&mut reader) { 21 | Ok(instr) => { 22 | let text = format!("{}", instr.display_with(style)); 23 | assert!( 24 | text == expected, 25 | "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", 26 | hex, 27 | instr, 28 | decoder, 29 | text, 30 | expected 31 | ); 32 | // while we're at it, test that the instruction is as long, and no longer, than its 33 | // input 34 | assert_eq!((0u64.wrapping_offset(instr.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected); 35 | }, 36 | Err(e) => { 37 | assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected); 38 | } 39 | } 40 | } 41 | 42 | // decided i do not like at&t syntax much at all. not going to write a formatter for it. some test 43 | // cases will live on in case someone else feels like adding one, or i get mad enough to do it. 44 | #[allow(unreachable_code)] 45 | #[ignore] 46 | #[test] 47 | fn test_instructions_atnt() { 48 | // `ignore` is now used to avoid running (slow!) exhaustive tests in a default `cargo test`. 49 | // running exhaustive tests now runs these tests, which fail. so instead, return early. 50 | return; 51 | // just modrm 52 | test_display(&[0x33, 0x08], "xor (%rax), %ecx"); 53 | test_display(&[0x33, 0x20], "xor (%rax), %esp"); 54 | test_display(&[0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor 0x12345678(%rip), %eax"); 55 | test_display(&[0x33, 0x41, 0x23], "xor 0x23(%rcx), %eax"); 56 | test_display(&[0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor %0x43650123, %eax"); 57 | test_display(&[0x33, 0xc1], "xor %ecx, %eax"); 58 | 59 | // modrm + rex.w 60 | test_display(&[0x48, 0x33, 0x08], "xor (%rax), %rcx"); 61 | test_display(&[0x48, 0x33, 0x20], "xor (%rax), %rsp"); 62 | test_display(&[0x48, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor 0x12345678(%rip), %rax"); 63 | test_display(&[0x48, 0x33, 0x41, 0x23], "xor 0x23(%rcx), %rax"); 64 | test_display(&[0x48, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor 0x43650123(%rcx), %rax"); 65 | test_display(&[0x48, 0x33, 0xc1], "xor %rcx, %rax"); 66 | 67 | // modrm + rex.r 68 | test_display(&[0x44, 0x33, 0x08], "xor (%rax), %r9d"); 69 | test_display(&[0x44, 0x33, 0x20], "xor (%rax), %r12d"); 70 | test_display(&[0x44, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor 0x12345678(%rip), %r8d"); 71 | test_display(&[0x44, 0x33, 0x41, 0x23], "xor 0x23(%rcx), %r8d"); 72 | test_display(&[0x44, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor 0x43650123(%rcx), %r8d"); 73 | test_display(&[0x44, 0x33, 0xc1], "xor %ecx, %r8d"); 74 | 75 | // modrm + rex.rb 76 | test_display(&[0x45, 0x33, 0x08], "xor (%r8), %r9d"); 77 | test_display(&[0x45, 0x33, 0x20], "xor (%r8), %r12d"); 78 | test_display(&[0x45, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor 0x12345678(%rip), %r8d"); 79 | test_display(&[0x45, 0x33, 0x41, 0x23], "xor 0x23(%r9), %r8d"); 80 | test_display(&[0x45, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor 0x43650123(%r9), %r8d"); 81 | test_display(&[0x45, 0x33, 0xc1], "xor %r9d, %r8d"); 82 | 83 | // sib 84 | test_display(&[0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "xor (0x44332211), %eax"); 85 | test_display(&[0x41, 0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "xor (0x44332211), %eax"); 86 | 87 | test_display(&[0x41, 0x33, 0x44, 0x65, 0x11], "xor 0x11(%r13), %eax"); 88 | 89 | test_display(&[0x42, 0x33, 0x34, 0x25, 0x20, 0x30, 0x40, 0x50], "xor 0x50403020(,%r12,1), %esi"); 90 | 91 | test_display(&[0x4f, 0x0f, 0xe7, 0x03], "movntq %mm0, (%r11)"); 92 | test_display(&[0x0f, 0xe7, 0x03], "movntq %mm0, (%rbx)"); 93 | 94 | test_display(&[0x4f, 0x0f, 0x7f, 0x0f], "movq %mm1, (%r15)"); 95 | test_display(&[0x0f, 0xc4, 0xc0, 0x14], "pinsrw $0x14, %eax, %mm0"); 96 | 97 | test_display(&[0x4f, 0x0f, 0xd1, 0x00], "psrlw (%r8), %mm0"); 98 | test_display(&[0x0f, 0xe5, 0x3d, 0xaa, 0xbb, 0xcc, 0x77], "pmulhw 0x77ccbbaa(%rip), %mm7"); 99 | } 100 | 101 | #[test] 102 | fn test_instructions_c() { 103 | // just modrm 104 | test_c_display(&[0x33, 0x08], "ecx ^= [rax]"); 105 | test_c_display(&[0x33, 0x20], "esp ^= [rax]"); 106 | test_c_display(&[0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "eax ^= [rip + 0x12345678]"); 107 | test_c_display(&[0x33, 0x41, 0x23], "eax ^= [rcx + 0x23]"); 108 | test_c_display(&[0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "eax ^= [rcx + 0x43650123]"); 109 | test_c_display(&[0x33, 0xc1], "eax ^= ecx"); 110 | 111 | // modrm + rex.w 112 | test_c_display(&[0x48, 0x33, 0x08], "rcx ^= [rax]"); 113 | test_c_display(&[0x48, 0x33, 0x20], "rsp ^= [rax]"); 114 | test_c_display(&[0x48, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "rax ^= [rip + 0x12345678]"); 115 | test_c_display(&[0x48, 0x33, 0x41, 0x23], "rax ^= [rcx + 0x23]"); 116 | test_c_display(&[0x48, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "rax ^= [rcx + 0x43650123]"); 117 | test_c_display(&[0x48, 0x33, 0xc1], "rax ^= rcx"); 118 | 119 | // modrm + rex.r 120 | test_c_display(&[0x44, 0x33, 0x08], "r9d ^= [rax]"); 121 | test_c_display(&[0x44, 0x33, 0x20], "r12d ^= [rax]"); 122 | test_c_display(&[0x44, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "r8d ^= [rip + 0x12345678]"); 123 | test_c_display(&[0x44, 0x33, 0x41, 0x23], "r8d ^= [rcx + 0x23]"); 124 | test_c_display(&[0x44, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "r8d ^= [rcx + 0x43650123]"); 125 | test_c_display(&[0x44, 0x33, 0xc1], "r8d ^= ecx"); 126 | 127 | // modrm + rex.rb 128 | test_c_display(&[0x45, 0x33, 0x08], "r9d ^= [r8]"); 129 | test_c_display(&[0x45, 0x33, 0x20], "r12d ^= [r8]"); 130 | test_c_display(&[0x45, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "r8d ^= [rip + 0x12345678]"); 131 | test_c_display(&[0x45, 0x33, 0x41, 0x23], "r8d ^= [r9 + 0x23]"); 132 | test_c_display(&[0x45, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "r8d ^= [r9 + 0x43650123]"); 133 | test_c_display(&[0x45, 0x33, 0xc1], "r8d ^= r9d"); 134 | 135 | // sib 136 | test_c_display(&[0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "eax ^= [0x44332211]"); 137 | test_c_display(&[0x41, 0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "eax ^= [0x44332211]"); 138 | 139 | test_c_display(&[0x41, 0x33, 0x44, 0x65, 0x11], "eax ^= [r13 + 0x11]"); 140 | 141 | test_c_display(&[0x42, 0x33, 0x34, 0x25, 0x20, 0x30, 0x40, 0x50], "esi ^= [r12 * 1 + 0x50403020]"); 142 | 143 | test_c_display(&[0x4f, 0x0f, 0xe7, 0x03], "[r11] = movntq(mm0)"); 144 | test_c_display(&[0x0f, 0xe7, 0x03], "[rbx] = movntq(mm0)"); 145 | 146 | test_c_display(&[0x4f, 0x0f, 0x7f, 0x0f], "[r15] = movq(mm1)"); 147 | test_c_display(&[0x0f, 0xc4, 0xc0, 0x14], "mm0 = pinsrw(mm0, eax, 0x14)"); 148 | 149 | test_c_display(&[0x4f, 0x0f, 0xd1, 0x00], "mm0 = psrlw(mm0, [r8])"); 150 | test_c_display(&[0x0f, 0xe5, 0x3d, 0xaa, 0xbb, 0xcc, 0x77], "mm7 = pmulhw(mm7, [rip + 0x77ccbbaa])"); 151 | 152 | test_c_display(&[0xf3, 0x48, 0xa5], "rep qword { es:[rdi++] = ds:[rsi++] }"); 153 | test_c_display(&[0xf3, 0xa5], "rep dword { es:[rdi++] = ds:[rsi++] }"); 154 | test_c_display(&[0xf3, 0x66, 0xa5], "rep word { es:[rdi++] = ds:[rsi++] }"); 155 | test_c_display(&[0xf3, 0xa4], "rep byte { es:[rdi++] = ds:[rsi++] }"); 156 | 157 | test_c_display(&[0xf6, 0xc2, 0x18], "rflags = flags(dl & 0x18)"); 158 | test_c_display(&[0xf6, 0xc2, 0x18], "rflags = flags(dl & 0x18)"); 159 | test_c_display(&[0x84, 0xc0], "rflags = flags(al & al)"); 160 | test_c_display(&[0x85, 0xc0], "rflags = flags(eax & eax)"); 161 | test_c_display(&[0x3a, 0xc0], "rflags = flags(al - al)"); 162 | test_c_display(&[0x3b, 0xc0], "rflags = flags(eax - eax)"); 163 | 164 | test_c_display(&[0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(r11d) (x86 bsf)"); 165 | test_c_display(&[0xf3, 0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(r11d)"); 166 | // test_c_display(&[0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(r11d) (x86 bsf"); // for non-bm1 167 | test_c_display(&[0x41, 0x0f, 0xbd, 0xd3], "edx = msb(r11d)"); 168 | // test_c_display(&[0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(r11d) (x86 bsr"); // for non-bm1 169 | test_c_display(&[0xd2, 0xc0], "al = al rol cl"); 170 | test_c_display(&[0xd2, 0xc8], "al = al ror cl"); 171 | test_c_display(&[0xd2, 0xd0], "al = al rcl cl"); 172 | test_c_display(&[0xd2, 0xd8], "al = al rcr cl"); 173 | test_c_display(&[0xd2, 0xe0], "al = al << cl"); 174 | test_c_display(&[0xd2, 0xe8], "al = al >> cl"); 175 | test_c_display(&[0xd2, 0xf0], "al = al <<< cl"); 176 | test_c_display(&[0xd2, 0xf8], "al = al >>> cl"); 177 | 178 | test_c_display(&[0xc4, 0xc3, 0x7b, 0xf0, 0x01, 0x05], "eax = [r9] ror 0x5 (x86 rorx)"); 179 | test_c_display(&[0xc4, 0xc2, 0xe3, 0xf7, 0x01], "rax = [r9] >> rbx (x86 shrx)"); 180 | test_c_display(&[0xc4, 0xc2, 0xe1, 0xf7, 0x01], "rax = [r9] << rbx (x86 shlx)"); 181 | 182 | test_c_display(&[0xd2, 0xe0], "al = al << cl"); 183 | 184 | test_c_display(&[0x66, 0x0f, 0xac, 0xcf, 0x11], "di = shrd(di, cx, 0x11)"); 185 | test_c_display(&[0x0f, 0xa5, 0xc9], "ecx = shld(ecx, ecx, cl)"); 186 | 187 | test_c_display(&[0x66, 0x0f, 0x38, 0xf6, 0x01], "eax += [rcx] + rflags.cf"); 188 | test_c_display(&[0xf3, 0x4f, 0x0f, 0x38, 0xf6, 0x01], "r8 += [r9] + rflags.of"); 189 | 190 | test_c_display(&[0xfe, 0x00], "byte [rax]++"); 191 | test_c_display(&[0x66, 0xff, 0x08], "word [rax]--"); 192 | test_c_display(&[0xff, 0x00], "dword [rax]++"); 193 | test_c_display(&[0x48, 0xff, 0x00], "qword [rax]++"); 194 | 195 | test_c_display(&[0xff, 0xe0], "jmp rax"); 196 | } 197 | -------------------------------------------------------------------------------- /test/long_mode/opcode.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_x86::long_mode::{ConditionCode, Opcode}; 2 | 3 | #[test] 4 | fn conditional_instructions() { 5 | const JCC: &'static [(Opcode, ConditionCode); 16] = &[ 6 | (Opcode::JO, ConditionCode::O), 7 | (Opcode::JNO, ConditionCode::NO), 8 | (Opcode::JB, ConditionCode::B), 9 | (Opcode::JNB, ConditionCode::AE), 10 | (Opcode::JZ, ConditionCode::Z), 11 | (Opcode::JNZ, ConditionCode::NZ), 12 | (Opcode::JA, ConditionCode::A), 13 | (Opcode::JNA, ConditionCode::BE), 14 | (Opcode::JS, ConditionCode::S), 15 | (Opcode::JNS, ConditionCode::NS), 16 | (Opcode::JP, ConditionCode::P), 17 | (Opcode::JNP, ConditionCode::NP), 18 | (Opcode::JL, ConditionCode::L), 19 | (Opcode::JGE, ConditionCode::GE), 20 | (Opcode::JG, ConditionCode::G), 21 | (Opcode::JLE, ConditionCode::LE), 22 | ]; 23 | for (opc, cond) in JCC.iter() { 24 | assert!(opc.is_jcc()); 25 | assert!(!opc.is_setcc()); 26 | assert!(!opc.is_cmovcc()); 27 | assert_eq!(opc.condition(), Some(*cond)); 28 | } 29 | 30 | const SETCC: &'static [(Opcode, ConditionCode); 16] = &[ 31 | (Opcode::SETO, ConditionCode::O), 32 | (Opcode::SETNO, ConditionCode::NO), 33 | (Opcode::SETB, ConditionCode::B), 34 | (Opcode::SETAE, ConditionCode::AE), 35 | (Opcode::SETZ, ConditionCode::Z), 36 | (Opcode::SETNZ, ConditionCode::NZ), 37 | (Opcode::SETA, ConditionCode::A), 38 | (Opcode::SETBE, ConditionCode::BE), 39 | (Opcode::SETS, ConditionCode::S), 40 | (Opcode::SETNS, ConditionCode::NS), 41 | (Opcode::SETP, ConditionCode::P), 42 | (Opcode::SETNP, ConditionCode::NP), 43 | (Opcode::SETL, ConditionCode::L), 44 | (Opcode::SETGE, ConditionCode::GE), 45 | (Opcode::SETG, ConditionCode::G), 46 | (Opcode::SETLE, ConditionCode::LE), 47 | ]; 48 | for (opc, cond) in SETCC.iter() { 49 | assert!(!opc.is_jcc()); 50 | assert!(opc.is_setcc()); 51 | assert!(!opc.is_cmovcc()); 52 | assert_eq!(opc.condition(), Some(*cond)); 53 | } 54 | 55 | const CMOVCC: &'static [(Opcode, ConditionCode); 16] = &[ 56 | (Opcode::CMOVO, ConditionCode::O), 57 | (Opcode::CMOVNO, ConditionCode::NO), 58 | (Opcode::CMOVB, ConditionCode::B), 59 | (Opcode::CMOVNB, ConditionCode::AE), 60 | (Opcode::CMOVZ, ConditionCode::Z), 61 | (Opcode::CMOVNZ, ConditionCode::NZ), 62 | (Opcode::CMOVA, ConditionCode::A), 63 | (Opcode::CMOVNA, ConditionCode::BE), 64 | (Opcode::CMOVS, ConditionCode::S), 65 | (Opcode::CMOVNS, ConditionCode::NS), 66 | (Opcode::CMOVP, ConditionCode::P), 67 | (Opcode::CMOVNP, ConditionCode::NP), 68 | (Opcode::CMOVL, ConditionCode::L), 69 | (Opcode::CMOVGE, ConditionCode::GE), 70 | (Opcode::CMOVG, ConditionCode::G), 71 | (Opcode::CMOVLE, ConditionCode::LE), 72 | ]; 73 | for (opc, cond) in CMOVCC.iter() { 74 | assert!(!opc.is_jcc()); 75 | assert!(!opc.is_setcc()); 76 | assert!(opc.is_cmovcc()); 77 | assert_eq!(opc.condition(), Some(*cond)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/long_mode/operand.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_x86::long_mode::{InstDecoder, Operand, RegSpec}; 2 | use yaxpeax_x86::MemoryAccessSize; 3 | 4 | #[test] 5 | fn register_widths() { 6 | assert_eq!(Operand::Register { reg: RegSpec::rsp() }.width(), Some(8)); 7 | assert_eq!(Operand::Register { reg: RegSpec::esp() }.width(), Some(4)); 8 | assert_eq!(Operand::Register { reg: RegSpec::sp() }.width(), Some(2)); 9 | assert_eq!(Operand::Register { reg: RegSpec::cl() }.width(), Some(1)); 10 | assert_eq!(Operand::Register { reg: RegSpec::ch() }.width(), Some(1)); 11 | assert_eq!(Operand::Register { reg: RegSpec::gs() }.width(), Some(2)); 12 | } 13 | 14 | #[test] 15 | fn memory_widths() { 16 | // the register operand directly doesn't report a size - it comes from the `Instruction` for 17 | // which this is an operand . 18 | assert_eq!(Operand::MemDeref { base: RegSpec::rsp() }.width(), None); 19 | 20 | fn mem_size_of(data: &[u8]) -> MemoryAccessSize { 21 | let decoder = InstDecoder::default(); 22 | decoder.decode_slice(data).unwrap().mem_size().unwrap() 23 | } 24 | 25 | // and checking the memory size direcly reports correct names 26 | assert_eq!(mem_size_of(&[0x32, 0x00]).size_name(), "byte"); 27 | assert_eq!(mem_size_of(&[0x66, 0x33, 0x00]).size_name(), "word"); 28 | assert_eq!(mem_size_of(&[0x33, 0x00]).size_name(), "dword"); 29 | assert_eq!(mem_size_of(&[0x48, 0x33, 0x00]).size_name(), "qword"); 30 | } 31 | 32 | #[test] 33 | fn test_implied_memory_width() { 34 | fn mem_size_of(data: &[u8]) -> Option { 35 | let decoder = InstDecoder::default(); 36 | decoder.decode_slice(data).unwrap().mem_size().unwrap().bytes_size() 37 | } 38 | 39 | // test push, pop, call, and ret 40 | assert_eq!(mem_size_of(&[0xc3]), Some(8)); 41 | assert_eq!(mem_size_of(&[0xe8, 0x11, 0x22, 0x33, 0x44]), Some(8)); 42 | assert_eq!(mem_size_of(&[0x50]), Some(8)); 43 | assert_eq!(mem_size_of(&[0x58]), Some(8)); 44 | assert_eq!(mem_size_of(&[0x66, 0x50]), Some(8)); 45 | assert_eq!(mem_size_of(&[0x66, 0x58]), Some(8)); 46 | assert_eq!(mem_size_of(&[0xff, 0xf0]), Some(8)); 47 | assert_eq!(mem_size_of(&[0x66, 0xff, 0xf0]), Some(2)); 48 | // operand-size prefixed call and jump still reads 8 bytes (prefix ignored) 49 | assert_eq!(mem_size_of(&[0x66, 0xff, 0x10]), Some(8)); 50 | assert_eq!(mem_size_of(&[0x66, 0xff, 0x20]), Some(8)); 51 | } 52 | -------------------------------------------------------------------------------- /test/long_mode/regspec.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_x86::long_mode::{register_class, RegSpec}; 2 | use std::collections::{BTreeMap, HashMap}; 3 | 4 | #[test] 5 | fn test_ord() { 6 | let _: BTreeMap = BTreeMap::new(); 7 | } 8 | 9 | #[test] 10 | fn test_hash() { 11 | let _: HashMap = HashMap::new(); 12 | } 13 | 14 | #[cfg(feature="fmt")] 15 | #[test] 16 | fn test_labels() { 17 | assert_eq!(RegSpec::rip().name(), "rip"); 18 | assert_eq!(RegSpec::eip().name(), "eip"); 19 | assert_eq!(RegSpec::rflags().name(), "rflags"); 20 | assert_eq!(RegSpec::rbp().name(), "rbp"); 21 | assert_eq!(RegSpec::gs().name(), "gs"); 22 | assert_eq!(RegSpec::al().name(), "al"); 23 | } 24 | 25 | #[cfg(feature="fmt")] 26 | #[test] 27 | fn test_bank_names() { 28 | assert_eq!(RegSpec::al().class().name(), "byte"); 29 | assert_eq!(RegSpec::r8b().class().name(), "rex-byte"); 30 | assert_eq!(RegSpec::ax().class().name(), "word"); 31 | assert_eq!(RegSpec::eax().class().name(), "dword"); 32 | assert_eq!(RegSpec::rax().class().name(), "qword"); 33 | assert_eq!(RegSpec::fs().class().name(), "segment"); 34 | assert_eq!(RegSpec::eflags().class().name(), "eflags"); 35 | assert_eq!(RegSpec::rflags().class().name(), "rflags"); 36 | assert_eq!(RegSpec::eip().class().name(), "eip"); 37 | assert_eq!(RegSpec::rip().class().name(), "rip"); 38 | assert_eq!(RegSpec::st0().class().name(), "x87-stack"); 39 | assert_eq!(RegSpec::mm0().class().name(), "mmx"); 40 | assert_eq!(RegSpec::xmm0().class().name(), "xmm"); 41 | assert_eq!(RegSpec::ymm0().class().name(), "ymm"); 42 | assert_eq!(RegSpec::zmm0().class().name(), "zmm"); 43 | } 44 | 45 | // this should compile. 46 | #[test] 47 | fn match_bank_kind() { 48 | match RegSpec::al().class() { 49 | register_class::X => { 50 | panic!("al is an xmm register? don't think so"); 51 | } 52 | register_class::B => { 53 | println!("al is a byte register"); 54 | } 55 | other => { 56 | panic!("unknown register kind: {:?}", other); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/protected_mode/display.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Write; 2 | 3 | use yaxpeax_arch::{AddressBase, Decoder, LengthedInstruction}; 4 | use yaxpeax_x86::protected_mode::{DisplayStyle, InstDecoder}; 5 | 6 | fn test_display(data: &[u8], expected: &'static str) { 7 | test_display_under(&InstDecoder::default(), DisplayStyle::Intel, data, expected); 8 | } 9 | 10 | fn test_c_display(data: &[u8], expected: &'static str) { 11 | test_display_under(&InstDecoder::default(), DisplayStyle::C, data, expected); 12 | } 13 | 14 | fn test_display_under(decoder: &InstDecoder, style: DisplayStyle, data: &[u8], expected: &'static str) { 15 | let mut hex = String::new(); 16 | for b in data { 17 | write!(hex, "{:02x}", b).unwrap(); 18 | } 19 | let mut reader = yaxpeax_arch::U8Reader::new(data); 20 | match decoder.decode(&mut reader) { 21 | Ok(instr) => { 22 | let text = format!("{}", instr.display_with(style)); 23 | assert!( 24 | text == expected, 25 | "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", 26 | hex, 27 | instr, 28 | decoder, 29 | text, 30 | expected 31 | ); 32 | // while we're at it, test that the instruction is as long, and no longer, than its 33 | // input 34 | assert_eq!((0u32.wrapping_offset(instr.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected); 35 | }, 36 | Err(e) => { 37 | assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected); 38 | } 39 | } 40 | } 41 | 42 | // decided i do not like at&t syntax much at all. not going to write a formatter for it. some test 43 | // cases will live on in case someone else feels like adding one, or i get mad enough to do it. 44 | #[allow(unreachable_code)] 45 | #[ignore] 46 | #[test] 47 | fn test_instructions_atnt() { 48 | // `ignore` is now used to avoid running (slow!) exhaustive tests in a default `cargo test`. 49 | // running exhaustive tests now runs these tests, which fail. so instead, return early. 50 | return; 51 | // just modrm 52 | test_display(&[0x33, 0x08], "xor (%eax), %ecx"); 53 | test_display(&[0x33, 0x20], "xor (%eax), %esp"); 54 | test_display(&[0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor (0x12345678), %eax"); 55 | test_display(&[0x33, 0x41, 0x23], "xor 0x23(%ecx), %eax"); 56 | test_display(&[0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor %0x43650123, %eax"); 57 | test_display(&[0x33, 0xc1], "xor %ecx, %eax"); 58 | 59 | // sib 60 | test_display(&[0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "xor (0x44332211), %eax"); 61 | test_display(&[0x41, 0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "xor (0x44332211), %eax"); 62 | 63 | test_display(&[0x33, 0x44, 0x65, 0x11], "xor 0x11(%r13), %eax"); 64 | 65 | test_display(&[0x33, 0x34, 0x25, 0x20, 0x30, 0x40, 0x50], "xor 0x50403020, %esi"); 66 | 67 | test_display(&[0x0f, 0xe7, 0x03], "movntq %mm0, (%ebx)"); 68 | 69 | test_display(&[0x0f, 0x7f, 0x0f], "movq %mm1, (%edi)"); 70 | test_display(&[0x0f, 0xc4, 0xc0, 0x14], "pinsrw $0x14, %eax, %mm0"); 71 | 72 | test_display(&[0x0f, 0xd1, 0x00], "psrlw (%eax), %mm0"); 73 | test_display(&[0x0f, 0xe5, 0x3d, 0xaa, 0xbb, 0xcc, 0x77], "pmulhw 0x77ccbbaa, %mm7"); 74 | } 75 | 76 | #[test] 77 | fn test_instructions_c() { 78 | // just modrm 79 | test_c_display(&[0x33, 0x08], "ecx ^= [eax]"); 80 | test_c_display(&[0x33, 0x20], "esp ^= [eax]"); 81 | test_c_display(&[0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "eax ^= [0x12345678]"); 82 | test_c_display(&[0x33, 0x41, 0x23], "eax ^= [ecx + 0x23]"); 83 | test_c_display(&[0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "eax ^= [ecx + 0x43650123]"); 84 | test_c_display(&[0x33, 0xc1], "eax ^= ecx"); 85 | 86 | // sib 87 | test_c_display(&[0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "eax ^= [0x44332211]"); 88 | 89 | test_c_display(&[0x33, 0x44, 0x65, 0x11], "eax ^= [ebp + 0x11]"); 90 | 91 | test_c_display(&[0x33, 0x34, 0x25, 0x20, 0x30, 0x40, 0x50], "esi ^= [0x50403020]"); 92 | 93 | test_c_display(&[0x0f, 0xe7, 0x03], "[ebx] = movntq(mm0)"); 94 | 95 | test_c_display(&[0x0f, 0x7f, 0x0f], "[edi] = movq(mm1)"); 96 | test_c_display(&[0x0f, 0xc4, 0xc0, 0x14], "mm0 = pinsrw(mm0, eax, 0x14)"); 97 | 98 | test_c_display(&[0x0f, 0xd1, 0x00], "mm0 = psrlw(mm0, [eax])"); 99 | test_c_display(&[0x0f, 0xe5, 0x3d, 0xaa, 0xbb, 0xcc, 0x77], "mm7 = pmulhw(mm7, [0x77ccbbaa])"); 100 | 101 | test_c_display(&[0xf3, 0xa5], "rep dword { es:[edi++] = ds:[esi++] }"); 102 | test_c_display(&[0xf3, 0x66, 0xa5], "rep word { es:[edi++] = ds:[esi++] }"); 103 | test_c_display(&[0xf3, 0xa4], "rep byte { es:[edi++] = ds:[esi++] }"); 104 | 105 | test_c_display(&[0xf6, 0xc2, 0x18], "eflags = flags(dl & 0x18)"); 106 | test_c_display(&[0xf6, 0xc2, 0x18], "eflags = flags(dl & 0x18)"); 107 | test_c_display(&[0x84, 0xc0], "eflags = flags(al & al)"); 108 | test_c_display(&[0x85, 0xc0], "eflags = flags(eax & eax)"); 109 | test_c_display(&[0x3a, 0xc0], "eflags = flags(al - al)"); 110 | test_c_display(&[0x3b, 0xc0], "eflags = flags(eax - eax)"); 111 | 112 | test_c_display(&[0x0f, 0xbc, 0xd3], "edx = lsb(ebx) (x86 bsf)"); 113 | test_c_display(&[0xf3, 0x0f, 0xbc, 0xd3], "edx = lsb(ebx)"); 114 | // test_c_display(&[0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(ebx) (x86 bsf"); // for non-bm1 115 | test_c_display(&[0x0f, 0xbd, 0xd3], "edx = msb(ebx)"); 116 | // test_c_display(&[0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(ebx) (x86 bsr"); // for non-bm1 117 | test_c_display(&[0xd2, 0xc0], "al = al rol cl"); 118 | test_c_display(&[0xd2, 0xc8], "al = al ror cl"); 119 | test_c_display(&[0xd2, 0xd0], "al = al rcl cl"); 120 | test_c_display(&[0xd2, 0xd8], "al = al rcr cl"); 121 | test_c_display(&[0xd2, 0xe0], "al = al << cl"); 122 | test_c_display(&[0xd2, 0xe8], "al = al >> cl"); 123 | test_c_display(&[0xd2, 0xf0], "al = al <<< cl"); 124 | test_c_display(&[0xd2, 0xf8], "al = al >>> cl"); 125 | 126 | test_c_display(&[0xc4, 0xc3, 0x7b, 0xf0, 0x01, 0x05], "eax = [ecx] ror 0x5 (x86 rorx)"); 127 | test_c_display(&[0xc4, 0xc2, 0xe3, 0xf7, 0x01], "eax = [ecx] >> ebx (x86 shrx)"); 128 | test_c_display(&[0xc4, 0xc2, 0xe1, 0xf7, 0x01], "eax = [ecx] << ebx (x86 shlx)"); 129 | 130 | test_c_display(&[0xd2, 0xe0], "al = al << cl"); 131 | 132 | test_c_display(&[0x66, 0x0f, 0xac, 0xcf, 0x11], "di = shrd(di, cx, 0x11)"); 133 | test_c_display(&[0x0f, 0xa5, 0xc9], "ecx = shld(ecx, ecx, cl)"); 134 | 135 | test_c_display(&[0x66, 0x0f, 0x38, 0xf6, 0x01], "eax += [ecx] + eflags.cf"); 136 | 137 | test_c_display(&[0xfe, 0x00], "byte [eax]++"); 138 | test_c_display(&[0x66, 0xff, 0x08], "word [eax]--"); 139 | test_c_display(&[0xff, 0x00], "dword [eax]++"); 140 | 141 | test_c_display(&[0xff, 0xe0], "jmp eax"); 142 | } 143 | -------------------------------------------------------------------------------- /test/protected_mode/opcode.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_x86::protected_mode::{ConditionCode, Opcode}; 2 | 3 | #[test] 4 | fn conditional_instructions() { 5 | const JCC: &'static [(Opcode, ConditionCode); 16] = &[ 6 | (Opcode::JO, ConditionCode::O), 7 | (Opcode::JNO, ConditionCode::NO), 8 | (Opcode::JB, ConditionCode::B), 9 | (Opcode::JNB, ConditionCode::AE), 10 | (Opcode::JZ, ConditionCode::Z), 11 | (Opcode::JNZ, ConditionCode::NZ), 12 | (Opcode::JA, ConditionCode::A), 13 | (Opcode::JNA, ConditionCode::BE), 14 | (Opcode::JS, ConditionCode::S), 15 | (Opcode::JNS, ConditionCode::NS), 16 | (Opcode::JP, ConditionCode::P), 17 | (Opcode::JNP, ConditionCode::NP), 18 | (Opcode::JL, ConditionCode::L), 19 | (Opcode::JGE, ConditionCode::GE), 20 | (Opcode::JG, ConditionCode::G), 21 | (Opcode::JLE, ConditionCode::LE), 22 | ]; 23 | for (opc, cond) in JCC.iter() { 24 | assert!(opc.is_jcc()); 25 | assert!(!opc.is_setcc()); 26 | assert!(!opc.is_cmovcc()); 27 | assert_eq!(opc.condition(), Some(*cond)); 28 | } 29 | 30 | const SETCC: &'static [(Opcode, ConditionCode); 16] = &[ 31 | (Opcode::SETO, ConditionCode::O), 32 | (Opcode::SETNO, ConditionCode::NO), 33 | (Opcode::SETB, ConditionCode::B), 34 | (Opcode::SETAE, ConditionCode::AE), 35 | (Opcode::SETZ, ConditionCode::Z), 36 | (Opcode::SETNZ, ConditionCode::NZ), 37 | (Opcode::SETA, ConditionCode::A), 38 | (Opcode::SETBE, ConditionCode::BE), 39 | (Opcode::SETS, ConditionCode::S), 40 | (Opcode::SETNS, ConditionCode::NS), 41 | (Opcode::SETP, ConditionCode::P), 42 | (Opcode::SETNP, ConditionCode::NP), 43 | (Opcode::SETL, ConditionCode::L), 44 | (Opcode::SETGE, ConditionCode::GE), 45 | (Opcode::SETG, ConditionCode::G), 46 | (Opcode::SETLE, ConditionCode::LE), 47 | ]; 48 | for (opc, cond) in SETCC.iter() { 49 | assert!(!opc.is_jcc()); 50 | assert!(opc.is_setcc()); 51 | assert!(!opc.is_cmovcc()); 52 | assert_eq!(opc.condition(), Some(*cond)); 53 | } 54 | 55 | const CMOVCC: &'static [(Opcode, ConditionCode); 16] = &[ 56 | (Opcode::CMOVO, ConditionCode::O), 57 | (Opcode::CMOVNO, ConditionCode::NO), 58 | (Opcode::CMOVB, ConditionCode::B), 59 | (Opcode::CMOVNB, ConditionCode::AE), 60 | (Opcode::CMOVZ, ConditionCode::Z), 61 | (Opcode::CMOVNZ, ConditionCode::NZ), 62 | (Opcode::CMOVA, ConditionCode::A), 63 | (Opcode::CMOVNA, ConditionCode::BE), 64 | (Opcode::CMOVS, ConditionCode::S), 65 | (Opcode::CMOVNS, ConditionCode::NS), 66 | (Opcode::CMOVP, ConditionCode::P), 67 | (Opcode::CMOVNP, ConditionCode::NP), 68 | (Opcode::CMOVL, ConditionCode::L), 69 | (Opcode::CMOVGE, ConditionCode::GE), 70 | (Opcode::CMOVG, ConditionCode::G), 71 | (Opcode::CMOVLE, ConditionCode::LE), 72 | ]; 73 | for (opc, cond) in CMOVCC.iter() { 74 | assert!(!opc.is_jcc()); 75 | assert!(!opc.is_setcc()); 76 | assert!(opc.is_cmovcc()); 77 | assert_eq!(opc.condition(), Some(*cond)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/protected_mode/operand.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_x86::protected_mode::{InstDecoder, Operand, RegSpec}; 2 | use yaxpeax_x86::MemoryAccessSize; 3 | 4 | #[test] 5 | fn register_widths() { 6 | assert_eq!(Operand::Register { reg: RegSpec::esp() }.width(), Some(4)); 7 | assert_eq!(Operand::Register { reg: RegSpec::sp() }.width(), Some(2)); 8 | assert_eq!(Operand::Register { reg: RegSpec::cl() }.width(), Some(1)); 9 | assert_eq!(Operand::Register { reg: RegSpec::ch() }.width(), Some(1)); 10 | assert_eq!(Operand::Register { reg: RegSpec::gs() }.width(), Some(2)); 11 | } 12 | 13 | #[test] 14 | fn memory_widths() { 15 | // the register operand directly doesn't report a size - it comes from the `Instruction` for 16 | // which this is an operand. 17 | assert_eq!(Operand::MemDeref { base: RegSpec::esp() }.width(), None); 18 | 19 | fn mem_size_of(data: &[u8]) -> MemoryAccessSize { 20 | let decoder = InstDecoder::default(); 21 | decoder.decode_slice(data).unwrap().mem_size().unwrap() 22 | } 23 | 24 | // and checking the memory size direcly reports correct names 25 | assert_eq!(mem_size_of(&[0x32, 0x00]).size_name(), "byte"); 26 | assert_eq!(mem_size_of(&[0x66, 0x33, 0x00]).size_name(), "word"); 27 | assert_eq!(mem_size_of(&[0x33, 0x00]).size_name(), "dword"); 28 | } 29 | 30 | #[test] 31 | fn test_implied_memory_width() { 32 | fn mem_size_of(data: &[u8]) -> Option { 33 | let decoder = InstDecoder::default(); 34 | decoder.decode_slice(data).unwrap().mem_size().unwrap().bytes_size() 35 | } 36 | 37 | // test push, pop, call, and ret 38 | assert_eq!(mem_size_of(&[0xc3]), Some(4)); 39 | assert_eq!(mem_size_of(&[0xe8, 0x11, 0x22, 0x33, 0x44]), Some(4)); 40 | assert_eq!(mem_size_of(&[0x50]), Some(4)); 41 | assert_eq!(mem_size_of(&[0x58]), Some(4)); 42 | assert_eq!(mem_size_of(&[0x66, 0x50]), Some(4)); 43 | assert_eq!(mem_size_of(&[0x66, 0x58]), Some(4)); 44 | assert_eq!(mem_size_of(&[0xff, 0xf0]), Some(4)); 45 | assert_eq!(mem_size_of(&[0x66, 0xff, 0xf0]), Some(2)); 46 | // unlike 64-bit mode, operand-size prefixed call and jump do have a different size: they read 47 | // two bytes. 48 | assert_eq!(mem_size_of(&[0x66, 0xff, 0x10]), Some(2)); 49 | assert_eq!(mem_size_of(&[0x66, 0xff, 0x20]), Some(2)); 50 | } 51 | -------------------------------------------------------------------------------- /test/protected_mode/regspec.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_x86::protected_mode::{register_class, RegSpec}; 2 | use std::collections::{BTreeMap, HashMap}; 3 | 4 | #[test] 5 | fn test_ord() { 6 | let _: BTreeMap = BTreeMap::new(); 7 | } 8 | 9 | #[test] 10 | fn test_hash() { 11 | let _: HashMap = HashMap::new(); 12 | } 13 | 14 | #[cfg(feature="fmt")] 15 | #[test] 16 | fn test_labels() { 17 | assert_eq!(RegSpec::eip().name(), "eip"); 18 | assert_eq!(RegSpec::ebp().name(), "ebp"); 19 | assert_eq!(RegSpec::gs().name(), "gs"); 20 | assert_eq!(RegSpec::al().name(), "al"); 21 | } 22 | 23 | #[cfg(feature="fmt")] 24 | #[test] 25 | fn test_bank_names() { 26 | assert_eq!(RegSpec::al().class().name(), "byte"); 27 | assert_eq!(RegSpec::ax().class().name(), "word"); 28 | assert_eq!(RegSpec::eax().class().name(), "dword"); 29 | assert_eq!(RegSpec::fs().class().name(), "segment"); 30 | assert_eq!(RegSpec::eflags().class().name(), "eflags"); 31 | assert_eq!(RegSpec::eip().class().name(), "eip"); 32 | assert_eq!(RegSpec::st0().class().name(), "x87-stack"); 33 | assert_eq!(RegSpec::mm0().class().name(), "mmx"); 34 | assert_eq!(RegSpec::xmm0().class().name(), "xmm"); 35 | assert_eq!(RegSpec::ymm0().class().name(), "ymm"); 36 | assert_eq!(RegSpec::zmm0().class().name(), "zmm"); 37 | } 38 | 39 | // this should compile. 40 | #[test] 41 | fn match_bank_kind() { 42 | match RegSpec::al().class() { 43 | register_class::X => { 44 | panic!("al is an xmm register? don't think so"); 45 | } 46 | register_class::B => { 47 | println!("al is a byte register"); 48 | } 49 | other => { 50 | panic!("unknown register kind: {:?}", other); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/real_mode/opcode.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_x86::real_mode::{ConditionCode, Opcode}; 2 | 3 | #[test] 4 | fn conditional_instructions() { 5 | const JCC: &'static [(Opcode, ConditionCode); 16] = &[ 6 | (Opcode::JO, ConditionCode::O), 7 | (Opcode::JNO, ConditionCode::NO), 8 | (Opcode::JB, ConditionCode::B), 9 | (Opcode::JNB, ConditionCode::AE), 10 | (Opcode::JZ, ConditionCode::Z), 11 | (Opcode::JNZ, ConditionCode::NZ), 12 | (Opcode::JA, ConditionCode::A), 13 | (Opcode::JNA, ConditionCode::BE), 14 | (Opcode::JS, ConditionCode::S), 15 | (Opcode::JNS, ConditionCode::NS), 16 | (Opcode::JP, ConditionCode::P), 17 | (Opcode::JNP, ConditionCode::NP), 18 | (Opcode::JL, ConditionCode::L), 19 | (Opcode::JGE, ConditionCode::GE), 20 | (Opcode::JG, ConditionCode::G), 21 | (Opcode::JLE, ConditionCode::LE), 22 | ]; 23 | for (opc, cond) in JCC.iter() { 24 | assert!(opc.is_jcc()); 25 | assert!(!opc.is_setcc()); 26 | assert!(!opc.is_cmovcc()); 27 | assert_eq!(opc.condition(), Some(*cond)); 28 | } 29 | 30 | const SETCC: &'static [(Opcode, ConditionCode); 16] = &[ 31 | (Opcode::SETO, ConditionCode::O), 32 | (Opcode::SETNO, ConditionCode::NO), 33 | (Opcode::SETB, ConditionCode::B), 34 | (Opcode::SETAE, ConditionCode::AE), 35 | (Opcode::SETZ, ConditionCode::Z), 36 | (Opcode::SETNZ, ConditionCode::NZ), 37 | (Opcode::SETA, ConditionCode::A), 38 | (Opcode::SETBE, ConditionCode::BE), 39 | (Opcode::SETS, ConditionCode::S), 40 | (Opcode::SETNS, ConditionCode::NS), 41 | (Opcode::SETP, ConditionCode::P), 42 | (Opcode::SETNP, ConditionCode::NP), 43 | (Opcode::SETL, ConditionCode::L), 44 | (Opcode::SETGE, ConditionCode::GE), 45 | (Opcode::SETG, ConditionCode::G), 46 | (Opcode::SETLE, ConditionCode::LE), 47 | ]; 48 | for (opc, cond) in SETCC.iter() { 49 | assert!(!opc.is_jcc()); 50 | assert!(opc.is_setcc()); 51 | assert!(!opc.is_cmovcc()); 52 | assert_eq!(opc.condition(), Some(*cond)); 53 | } 54 | 55 | const CMOVCC: &'static [(Opcode, ConditionCode); 16] = &[ 56 | (Opcode::CMOVO, ConditionCode::O), 57 | (Opcode::CMOVNO, ConditionCode::NO), 58 | (Opcode::CMOVB, ConditionCode::B), 59 | (Opcode::CMOVNB, ConditionCode::AE), 60 | (Opcode::CMOVZ, ConditionCode::Z), 61 | (Opcode::CMOVNZ, ConditionCode::NZ), 62 | (Opcode::CMOVA, ConditionCode::A), 63 | (Opcode::CMOVNA, ConditionCode::BE), 64 | (Opcode::CMOVS, ConditionCode::S), 65 | (Opcode::CMOVNS, ConditionCode::NS), 66 | (Opcode::CMOVP, ConditionCode::P), 67 | (Opcode::CMOVNP, ConditionCode::NP), 68 | (Opcode::CMOVL, ConditionCode::L), 69 | (Opcode::CMOVGE, ConditionCode::GE), 70 | (Opcode::CMOVG, ConditionCode::G), 71 | (Opcode::CMOVLE, ConditionCode::LE), 72 | ]; 73 | for (opc, cond) in CMOVCC.iter() { 74 | assert!(!opc.is_jcc()); 75 | assert!(!opc.is_setcc()); 76 | assert!(opc.is_cmovcc()); 77 | assert_eq!(opc.condition(), Some(*cond)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/real_mode/operand.rs: -------------------------------------------------------------------------------- 1 | use yaxpeax_x86::real_mode::{InstDecoder}; 2 | 3 | #[test] 4 | fn test_implied_memory_width() { 5 | fn mem_size_of(data: &[u8]) -> Option { 6 | let decoder = InstDecoder::default(); 7 | decoder.decode_slice(data).unwrap().mem_size().unwrap().bytes_size() 8 | } 9 | 10 | // test push, pop, call, and ret 11 | assert_eq!(mem_size_of(&[0xc3]), Some(2)); 12 | assert_eq!(mem_size_of(&[0xe8, 0x11, 0x22, 0x33, 0x44]), Some(2)); 13 | assert_eq!(mem_size_of(&[0x50]), Some(2)); 14 | assert_eq!(mem_size_of(&[0x58]), Some(2)); 15 | assert_eq!(mem_size_of(&[0x66, 0x50]), Some(2)); 16 | assert_eq!(mem_size_of(&[0x66, 0x58]), Some(2)); 17 | assert_eq!(mem_size_of(&[0xff, 0xf0]), Some(2)); 18 | assert_eq!(mem_size_of(&[0x66, 0xff, 0xf0]), Some(4)); 19 | // unlike 64-bit mode, operand-size prefixed call and jump do have a different size: they read 20 | // four bytes. 21 | assert_eq!(mem_size_of(&[0x66, 0xff, 0x10]), Some(4)); 22 | assert_eq!(mem_size_of(&[0x66, 0xff, 0x20]), Some(4)); 23 | } 24 | -------------------------------------------------------------------------------- /test/test.rs: -------------------------------------------------------------------------------- 1 | extern crate yaxpeax_arch; 2 | extern crate yaxpeax_x86; 3 | 4 | mod long_mode; 5 | mod protected_mode; 6 | mod real_mode; 7 | --------------------------------------------------------------------------------