dynarmic/externals/zydis/src/Encoder.c

4743 lines
152 KiB
C
Raw Normal View History

Squashed 'externals/zydis/' changes from 25193db0..1ba75aee 1ba75aee Disassembler: fix argument type `usize` -> `u64` de4cbcdb MSVC project updates (#400) e32757cd CMake: Enable `CMAKE_MSVC_RUNTIME_LIBRARY` (MSVC) 325e5e00 CI: fix Doxyfile being used 3ad3d7fc CI: inject Doxygen CSS theme a1dd0652 Doc: remove CSS theme from Zydis repository cc4f22cb Makefile: fix doc target name 83d2afa9 Update zycore to v1.4.0 53dd0880 Match parameter name for documentation 64028db9 Allow doxygen generation to be disabled 4bb3c83f Fixed ISA sets (Fixes #389) 2a9f9c66 Decoder: change error code for `NULL` `buffer` arguments 807af687 Examples: adjust to `ZydisDecoderDecodeFull` changes 71fe85a7 Tools: adjust to `ZydisDecoderDecodeFull` changes 59135031 Decoder: simplify `ZydisDecoderDecodeFull` API e5e52676 Makefile: fix `test` recipe bcb0a361 CMake: rename target `doc` -> `ZydisDoc` 3208f041 Add support for ppc(64) and riscv64 (#383) 0002161e Deps: update zycore to v1.3.0 e0fc2a92 CMake: simplify example build rules 2221bce3 README: switch decoder example to simplified version 392aa410 Examples: add `@file` comment to `Disassemble` example cff56e42 Examples: add `DisassembleSimple` 246d471b Disassemble: add function for decoding and formatting in one step 109d08ba build(make): use new doc target ebf95079 build: add doc target df277052 Fixed kernel build (WDK detection) daf7c62d Decoder: unnest all structs in `ZydisDecodedInstruction` 89cba0c2 Formatter: provide more meaningful documentation for types f8344423 Doc: remove misspelled `@brief` 6cbdb04a Doc: update `Doxyfile` config to latest version 67e15cf0 Doc: fix group references f6dfdbd2 Added `ZydisEncoderNopFill` 404e50b5 Doc: group important macros and refer to them c6141110 CI: clone doxygen theme prior to building doc 9d6c82a3 CI: add automated documentation builds f8866275 Formatter: add missing newline at end of file 2a06d5e5 Decoder: add missing `ZYAN_FALLTHROUGH` b83a3923 CI: enable development mode for all CI builds eca25034 CI: switch to an include-only way of defining the build matrix dd271e90 README: update CI badge URL 90d54ee8 Fix sign compare warnings on clang-cl e66c7010 CI: shorten name prefix 91f9d4ea CI: improved build name generation 14800b6f CI: add ClangCL build on Windows 55b23387 CI: consistently use default-init for empty vars 6c39010e CI: generalize `dev_mode` variable to `cmake_flags` 3160753d Formatter: make `operand_count` check less strict 9b9b72c9 README: include examples via permalinks adb1a85b Examples: add README encoder example to build 0897c271 Doc: switch Doxygen theme 9cb54996 Examples: add README examples to build a762cd6a README: fix example code 503e064c Fixed compiler warnings 091016bd Update workflow f29de7d8 Added tests for encoding in absolute address mode 355ce7e3 Added function to calculate and encode relative operands 73a44921 Updating ZyCore 9413c809 Doc: clarify that `user_data` can be `NULL` f0f44096 Simplify porting guide `Formatter` section 9ba7e55a Combine `ZydisFormatterTokenizeOperand` with `Ex` variant 35ea249d Combine `ZydisFormatterTokenizeInstruction` with `Ex` variant 01dddb93 Combine `ZydisFormatterFormatOperand` and `Ex` variant abcacbcb Combine `ZydisFormatterFormatInstruction` and `Ex` variant 871c40c5 Clean up `.gitignore` file a941e961 Doc: update porting guide e973b3ec MSVC: add `Segment.{h,c}` to projects dbaec910 Build: make segment API an optional feature cd508f3b Utils: split segment API into separate files 2677d5a2 Add Makefile for quick access to various operations 83eddf40 Decoder: fix name confusion (context vs state) e46660c7 Decoder: make placement of parentheses consistent cc9ff174 Fix build without `ZYDIS_FEATURE_ENCODER` 9b882913 Revert default operand-size to 32-bit for `MOVMSKPS/D` 73d7dbb3 Improved signedness data accuracy and consistency (Fixes #327) f46e3e9d Update zasm URL 2ed04d0c Decoder: add `raw.rex` to union dd6235c5 Duplicate encoding to aid Rust bindings c8b5b75d Always initialize `instruction->cpu_flags/fpu_flags` 9392f010 Extend porting guide (#338) 14453b30 Encoder: consistent constant naming (#339) 95329939 Avoid passing variable size to memcpy to prevent external call (#337) f0f0664a Encoder: improve `ZydisArePrefixesCompatible` perf (#334) 7f83c416 Improve LTO support (#333) 70085c71 Encoder: make all internal functions `static` 1581029c Change `st0` operand visibility as specified by SDM 81fae568 Update regression tests for `st0` visibility changes 6d83a277 Declare structs as anonymous within anonymous unions (#329) f146fd18 Apply consistent encodings to existing files cdbf379a Enforce consistent encoding and line endings 0cf3c960 Disable C11 feature warnings in Windows kernel example 6afe1d80 Update Zycore submodule 580cbfe6 Add unions in decoded instruction and operands e14a0789 Fix `vcpkg` install instructions 3f5a3ad8 Cleanup zydis include c0e80e79 Fixed error codes inside `ZydisDecoderDecodeFull` 770c3203 Fix incorrect register definition 0a45c474 Change default operand size for `MOVMSKPS/D` to 64-bit 91742f41 Fixed hidden operand scaling regression (Fixes #309) b44ec347 Remove LuaJIT bindings link 4f232ebf Fixed edge case for hidden operand scaling (Fixes #296) 562a7c1e Fixed newlines on Windows (amalgamation) 79c759ed Fixed signedness compilation warning (MSVC x86) 3092818a Fixed defines in MSVC projects 124571c5 Improve CMake file 69636284 Add amalgamated CI build d866d2e4 Rename STR_REGISTER -> STR_REGISTERS bd392018 Add script for creating amalgamated distributions 311fb0b4 Prevent duplicate CI builds 9770c41d Get rid of ZydisExportConfig.h cbc23017 Update zycore submodule e34e6b97 Remove register lookup special cases a5fb0d09 Fix `MASM` style disassembly (#298) 9fc98de2 Point to zasm for an assembler front-end 4756724a Ignore `.b` for `modrm.rm` encoded mask registers 198fee5d Improved prefix handling a8e43f84 Fixed `MVEX.EH` special cases ecb7e060 Reworked encoding of branching instructions (Fixes #268, Fixes #266) 38aba0fc Improve `ZydisInfo` 0d650daf Fixed decoder example 49005dd8 Add lookup tables for `ZydisRegister` and `ZydisRegisterClass` (#283) c804b8f6 Refactored operand count checks 979f0bea Update porting guide 875c8a75 Decouple operand decoding 5df1b68e Minor refactoring 66e81ebe Add WebAssembly support (#277) 33c5bccf Refactor operand constraints handling b5a92f4e Fixed constraint enforcement for AMX instructions db2c1633 Add regression tests for recent bugs (#275) 17896d72 Add a minimal mode build to CI (#274) 84dfb487 Fix register constraints of certain `AMX` instructions 4581fe30 Fixed build in minimal mode (#273) 70d6bda3 Make test scripts executable 93b503da Add encoder tests to CI (#272) 4f38dd50 Auto-generated `zydis_encoder_types.py` 69884f0e Fix constraints of certain `AMX` instructions 92dffbf2 Correctly set `is_gather` for MVEX instructions 23b499a7 Enforce MVEX constraints 7b981a6f Enforce constraints for gather instructions (Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=41044) 4f4dade2 Move fuzzing corpus out of the repository d835ac30 docs: add man pages (#267) f2429819 Update version-bump-checklist.txt 7d6ee06e Remove legacy code and general cleanup (#262) 162ef4ef Fix various warnings ocurring in pedantic mode 4189d830 Move static asserts to global scope 9fab1a82 `ZydisEncodablePrefix`/`ZydisEncoderInstructionAttribute` -> `ZYDIS_ATTRIB_*` 9f569b7c Size-optimal outputs, bug fixes, FuzzEncoder improvements, misc tooling improvements 1e8e1702 Added regression test suite for encoder 4634cc22 Update `examples/README.md` 043b2cc3 Added separate projects for fuzz targets 0faa2346 Add porting guide 248bc1fc Move instruction attributes into `SharedTypes.h` 4e0cb756 Fix attribute definitions ecb1286f Add example for rewriting instructions 606e361c Fix unaligned pointer write 3707e70f Rebase attribute flags and add `ZYDIS_ATTRIB_HAS_EVEX_B` 8a452a32 Prefer stdlib functions in example 46cd3e02 Documentation improvements fbcfc5d9 Improve encoder example and cleanup README edcac7b3 Extend example to execute the generated code 2a8eb8e2 Add simple encoding example d3b5e241 Fixed compiler warning 76fdffac Added operand masks for enhanced performance cd6a49e9 Implemented instruction encoder (Closes #129) 29bb0163 Don't pack ZydisShortString on Darwin (#259) 55dd08c2 Fix struct initialization in formatter 554efeb7 Update example code in README.md 869dfb42 Remove ZydisPE 6ae9f61b Create SECURITY.md 077b1851 Fix merge conflict 99a22d85 Fix general address-width handling ab535b1a build(cmake): add option to use system installed Zycore 746faa45 Bump version to v3.2 c3720691 Merged some definitions (TBM instructions) 12b4c46f chore: update zycore submodule 6423f083 build(cmake): add version and soversion to the library 2666f80c Update zycore submodule 4a3f25be build(cmake): fix PUBLIC include dir of installed lib c5f5bcf3 Set `ZYDIS_ATTRIB_ACCEPTS_SEGMENT` for non legacy instructions 795f555b Fix `ECX` scaling for `pcmpestri`/`vpcmpestri`/`pcmpistri`/`vpcmpistri` 3737fcbd Fixed segment override handling (Fixes #231) db796058 Fixed FP16 HV broadcasts (Fixes #243) e5764446 Fixed no_source_dest_match check (Fixes #241) 683bf85c Change branch-type from `short` -> `near` for `jkzd`/`jknzd` c462f63a Scale base register of implicit `SP`/`BP` memory operands by stack- instead of address-size e4931747 Do not sign ZydisWinKernel sample 5c193ca3 Ignore segment override for `BNDC{L|N|U}` f30efaf0 Add mission exception class strings to `ZydisInfo` 82a42657 Rebase tests e38110a4 Add missing `notrack` strings to `ZydisInfo` c95307b3 Do not drop `const` modifier 8ac69395 Fixed {sae}/{rc} formatting (Fixes #232) 80d8d523 Fixed issues with enum signedness 5503f8b0 Fuzzing: validate enum ranges 49a087f9 Fixed MVEX rounding mode decoding (Fixes #226) (#227) 60bc090a Rebase tests 4c7ec7fb Replace wrong eviction hint formatter-string 45ad14c6 Add missing register string 2613f262 Fixed Load-op SwizzUpConv(F32) (Fixes #224) 412a3c8e Add support for `no_compat_mode` flag 255513a6 Fix `T1F` handling for 16-bit elements e57da244 Fix element-size for new `QUARTER` tuple-type d36c77f2 Improve malformed `EVEX` conditions 5ec77520 Update MSVC project files 80a99de0 Fix `EVEX` map for certain `AVX-512 FP16` instructions ac3d45b4 Add `MSBuild` build to CI 1588ad23 Update and clean up CI config b622e814 Update zycore submodule 7fc3a946 Fix decoding/formatting of certain `AVX-512 FP16` instructions e6a72bbf Update copyright year 841bd7ca Update zycore submodule 61731ae2 Fix formatting of offset for `PTR` operands (should be unsigned) ad7ddc2c Add `AVX-512 FP16` isa-set (#214) 861187e2 Use lowercase `windows.h` in examples e5758caa Add multiple new ISA extensions 984e0c1d `XOP`/`VEX`/`EVEX` is invalid in 16-bit real mode 58662bf5 Fixed CET/VMX decoding in real mode (Fixes #211) 4c846ec9 Add formatter option to control printing of scale `*1` e994083e Print asterisk in front of absolute `jmp`/`call` address (AT&T) (#207) 2ab6a5cc Improve multiple instruction definitions 179441f3 Add support for `CET` `no-track` prefix (#204) e3fc6790 Ignore segment override for certain operands (#203) 68ad2460 Document xbegin special case 805a88f8 Improved handling of 16-bit relative operands (Fixes #198) cbf808a7 Removed impossible jcxz/jrcxz encodings (Fixes #196) 8080ae55 Fix cmake config files (#191) fe8f1bfd Enhanced formatting for MIB operands efd9c446 Fixes #192 (#193) 6a17c485 Update `Zycore` submodule git-subtree-dir: externals/zydis git-subtree-split: 1ba75aeefae37094c7be8eba07ff81d4fe0f1f20
2022-11-20 20:52:09 +00:00
/***************************************************************************************************
Zyan Disassembler Library (Zydis)
Original Author : Mappa
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
***************************************************************************************************/
// ReSharper disable CppClangTidyClangDiagnosticSwitchEnum
// ReSharper disable CppClangTidyClangDiagnosticCoveredSwitchDefault
// ReSharper disable CppClangTidyClangDiagnosticImplicitFallthrough
#include <Zycore/LibC.h>
#include <Zydis/Encoder.h>
#include <Zydis/Utils.h>
#include <Zydis/Internal/EncoderData.h>
#include <Zydis/Internal/SharedData.h>
/* ============================================================================================== */
/* Macros */
/* ============================================================================================== */
/* ---------------------------------------------------------------------------------------------- */
/* Constants */
/* ---------------------------------------------------------------------------------------------- */
#define ZYDIS_OPSIZE_MAP_BYTEOP 1
#define ZYDIS_OPSIZE_MAP_DEFAULT64 4
#define ZYDIS_OPSIZE_MAP_FORCE64 5
#define ZYDIS_ADSIZE_MAP_IGNORED 1
#define ZYDIS_LEGACY_SEGMENTS (ZYDIS_ATTRIB_HAS_SEGMENT_CS | \
ZYDIS_ATTRIB_HAS_SEGMENT_SS | \
ZYDIS_ATTRIB_HAS_SEGMENT_DS | \
ZYDIS_ATTRIB_HAS_SEGMENT_ES)
#define ZYDIS_ENCODABLE_PREFIXES_NO_SEGMENTS (ZYDIS_ENCODABLE_PREFIXES ^ \
ZYDIS_ATTRIB_HAS_SEGMENT)
/* ---------------------------------------------------------------------------------------------- */
/* ============================================================================================== */
/* Internal enums and types */
/* ============================================================================================== */
/**
* Usage of `REX.W` prefix makes it impossible to use some byte-sized registers. Values of this
* enum are used to track and facilitate enforcement of these restrictions.
*/
typedef enum ZydisEncoderRexType_
{
ZYDIS_REX_TYPE_UNKNOWN,
ZYDIS_REX_TYPE_REQUIRED,
ZYDIS_REX_TYPE_FORBIDDEN,
/**
* Maximum value of this enum.
*/
ZYDIS_REX_TYPE_MAX_VALUE = ZYDIS_REX_TYPE_FORBIDDEN,
/**
* The minimum number of bits required to represent all values of this enum.
*/
ZYDIS_REX_TYPE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_REX_TYPE_MAX_VALUE)
} ZydisEncoderRexType;
/**
* Primary structure used during instruction matching phase. Once filled it contains information
* about matched instruction definition and some values deduced from encoder request. It gets
* converted to `ZydisEncoderInstruction` during instruction building phase.
*/
typedef struct ZydisEncoderInstructionMatch_
{
/**
* A pointer to the `ZydisEncoderRequest` instance.
*/
const ZydisEncoderRequest *request;
/**
* A pointer to the `ZydisEncodableInstruction` instance.
*/
const ZydisEncodableInstruction *definition;
/**
* A pointer to the `ZydisInstructionDefinition` instance.
*/
const ZydisInstructionDefinition *base_definition;
/**
* A pointer to the `ZydisOperandDefinition` array.
*/
const ZydisOperandDefinition *operands;
/**
* Encodable attributes for this instruction.
*/
ZydisInstructionAttributes attributes;
/**
* Effective operand size attribute.
*/
ZyanU8 eosz;
/**
* Effective address size attribute.
*/
ZyanU8 easz;
/**
* Effective displacement size.
*/
ZyanU8 disp_size;
/**
* Effective immediate size.
*/
ZyanU8 imm_size;
/**
* Exponent of compressed displacement scale factor (2^cd8_scale)
*/
ZyanU8 cd8_scale;
/**
* `REX` prefix constraints.
*/
ZydisEncoderRexType rex_type;
/**
* True for special cases where operand size attribute must be lower than 64 bits.
*/
ZyanBool eosz64_forbidden;
/**
* True when instruction definition has relative operand (used for branching instructions).
*/
ZyanBool has_rel_operand;
} ZydisEncoderInstructionMatch;
/**
* Encapsulates information about writable buffer.
*/
typedef struct ZydisEncoderBuffer_
{
/**
* A pointer to actual data buffer.
*/
ZyanU8 *buffer;
/**
* Size of this buffer.
*/
ZyanUSize size;
/**
* Current write offset.
*/
ZyanUSize offset;
} ZydisEncoderBuffer;
/**
* Low-level instruction representation. Once filled this structure contains all information
* required for final instruction emission phase.
*/
typedef struct ZydisEncoderInstruction_
{
/**
* Encodable attributes for this instruction.
*/
ZydisInstructionAttributes attributes;
/**
* The instruction encoding.
*/
ZydisInstructionEncoding encoding;
/**
* The opcode map.
*/
ZydisOpcodeMap opcode_map;
/**
* The opcode.
*/
ZyanU8 opcode;
/**
* The `vvvv` field (`VEX`, `EVEX`, `MVEX`, `XOP`).
*/
ZyanU8 vvvv;
/**
* The `sss` field (`MVEX`).
*/
ZyanU8 sss;
/**
* The mask register ID.
*/
ZyanU8 mask;
/**
* The vector length.
*/
ZyanU8 vector_length;
/**
* The `mod` component of Mod/RM byte.
*/
ZyanU8 mod;
/**
* The `reg` component of Mod/RM byte.
*/
ZyanU8 reg;
/**
* The `rm` component of Mod/RM byte.
*/
ZyanU8 rm;
/**
* The scale component of SIB byte.
*/
ZyanU8 scale;
/**
* The index component of SIB byte.
*/
ZyanU8 index;
/**
* The base component of SIB byte.
*/
ZyanU8 base;
/**
* The `REX.W` bit.
*/
ZyanBool rex_w;
/**
* True if using zeroing mask (`EVEX`).
*/
ZyanBool zeroing;
/**
* True if using eviction hint (`MVEX`).
*/
ZyanBool eviction_hint;
/**
* Size of displacement value.
*/
ZyanU8 disp_size;
/**
* Size of immediate value.
*/
ZyanU8 imm_size;
/**
* The displacement value.
*/
ZyanU64 disp;
/**
* The immediate value.
*/
ZyanU64 imm;
} ZydisEncoderInstruction;
/* ============================================================================================== */
/* Internal functions */
/* ============================================================================================== */
/**
* Converts `ZydisInstructionEncoding` to `ZydisEncodableEncoding`.
*
* @param encoding `ZydisInstructionEncoding` value to convert.
*
* @return Equivalent `ZydisEncodableEncoding` value.
*/
static ZydisEncodableEncoding ZydisGetEncodableEncoding(ZydisInstructionEncoding encoding)
{
static const ZydisEncodableEncoding encoding_lookup[6] =
{
ZYDIS_ENCODABLE_ENCODING_LEGACY,
ZYDIS_ENCODABLE_ENCODING_3DNOW,
ZYDIS_ENCODABLE_ENCODING_XOP,
ZYDIS_ENCODABLE_ENCODING_VEX,
ZYDIS_ENCODABLE_ENCODING_EVEX,
ZYDIS_ENCODABLE_ENCODING_MVEX,
};
ZYAN_ASSERT((ZyanUSize)encoding <= ZYDIS_INSTRUCTION_ENCODING_MAX_VALUE);
return encoding_lookup[encoding];
}
/**
* Converts `ZydisMachineMode` to default stack width value expressed in bits.
*
* @param machine_mode `ZydisMachineMode` value to convert.
*
* @return Stack width for requested machine mode.
*/
static ZyanU8 ZydisGetMachineModeWidth(ZydisMachineMode machine_mode)
{
switch (machine_mode)
{
case ZYDIS_MACHINE_MODE_REAL_16:
case ZYDIS_MACHINE_MODE_LEGACY_16:
case ZYDIS_MACHINE_MODE_LONG_COMPAT_16:
return 16;
case ZYDIS_MACHINE_MODE_LEGACY_32:
case ZYDIS_MACHINE_MODE_LONG_COMPAT_32:
return 32;
case ZYDIS_MACHINE_MODE_LONG_64:
return 64;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Converts `ZydisAddressSizeHint` to address size expressed in bits.
*
* @param hint Address size hint.
*
* @return Address size in bits.
*/
static ZyanU8 ZydisGetAszFromHint(ZydisAddressSizeHint hint)
{
ZYAN_ASSERT((ZyanUSize)hint <= ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE);
static const ZyanU8 lookup[ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE + 1] = { 0, 16, 32, 64 };
return lookup[hint];
}
/**
* Converts `ZydisOperandSizeHint` to operand size expressed in bits.
*
* @param hint Operand size hint.
*
* @return Operand size in bits.
*/
static ZyanU8 ZydisGetOszFromHint(ZydisOperandSizeHint hint)
{
ZYAN_ASSERT((ZyanUSize)hint <= ZYDIS_OPERAND_SIZE_HINT_MAX_VALUE);
static const ZyanU8 lookup[ZYDIS_OPERAND_SIZE_HINT_MAX_VALUE + 1] = { 0, 8, 16, 32, 64 };
return lookup[hint];
}
/**
* Calculates effective operand size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param size_table Array of possible size values for different operand sizes.
* @param desired_size Operand size requested by caller.
* @param exact_match_mode True if desired_size must be matched exactly, false when
* "not lower than" matching is desired.
*
* @return Effective operand size in bits.
*/
static ZyanU8 ZydisGetOperandSizeFromElementSize(ZydisEncoderInstructionMatch *match,
const ZyanU16 *size_table, ZyanU16 desired_size, ZyanBool exact_match_mode)
{
if ((match->base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_DEFAULT64) &&
(match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64))
{
if ((exact_match_mode && (size_table[2] == desired_size)) ||
(!exact_match_mode && (size_table[2] >= desired_size)))
{
return 64;
}
else if (size_table[0] == desired_size)
{
return 16;
}
}
else if ((match->base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_FORCE64) &&
(match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64))
{
if (size_table[2] == desired_size)
{
return 64;
}
}
else
{
static const ZyanI8 eosz_priority_lookup[4][3] =
{
{ 0, 1, -1 },
{ 1, 0, -1 },
{ 1, 2, 0 },
};
const ZyanU8 eosz_index = ZydisGetMachineModeWidth(match->request->machine_mode) >> 5;
for (int i = 0; i < 3; ++i)
{
const ZyanI8 eosz_candidate = eosz_priority_lookup[eosz_index][i];
if ((eosz_candidate == -1) ||
!(match->definition->operand_sizes & (1 << eosz_candidate)))
{
continue;
}
if ((exact_match_mode && (size_table[eosz_candidate] == desired_size)) ||
(!exact_match_mode && (size_table[eosz_candidate] >= desired_size)))
{
return 16 << eosz_candidate;
}
}
}
return 0;
}
/**
* Calculates effective immediate size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param size_table Array of possible size values for different operand sizes.
* @param min_imm_size Minimum immediate size.
*
* @return Effective operand size in bits.
*/
static ZyanU8 ZydisGetScaledImmSize(ZydisEncoderInstructionMatch *match, const ZyanU16 *size_table,
ZyanU8 min_imm_size)
{
if (match->eosz == 0)
{
match->eosz = ZydisGetOperandSizeFromElementSize(match, size_table, min_imm_size,
ZYAN_FALSE);
return match->eosz != 0 ? (ZyanU8)size_table[match->eosz >> 5] : 0;
}
const ZyanU8 index = match->eosz >> 5;
return size_table[index] >= min_imm_size ? (ZyanU8)size_table[index] : 0;
}
/**
* Calculates size of smallest integral type able to represent provided signed value.
*
* @param imm Immediate to be represented.
*
* @return Size of smallest integral type able to represent provided signed value.
*/
static ZyanU8 ZydisGetSignedImmSize(ZyanI64 imm)
{
if (imm >= ZYAN_INT8_MIN && imm <= ZYAN_INT8_MAX)
{
return 8;
}
if (imm >= ZYAN_INT16_MIN && imm <= ZYAN_INT16_MAX)
{
return 16;
}
if (imm >= ZYAN_INT32_MIN && imm <= ZYAN_INT32_MAX)
{
return 32;
}
return 64;
}
/**
* Calculates size of smallest integral type able to represent provided unsigned value.
*
* @param imm Immediate to be represented.
*
* @return Size of smallest integral type able to represent provided unsigned value.
*/
static ZyanU8 ZydisGetUnsignedImmSize(ZyanU64 imm)
{
if (imm <= ZYAN_UINT8_MAX)
{
return 8;
}
if (imm <= ZYAN_UINT16_MAX)
{
return 16;
}
if (imm <= ZYAN_UINT32_MAX)
{
return 32;
}
return 64;
}
/**
* Checks if operand encoding encodes a signed immediate value.
*
* @param encoding Operand encoding for immediate value.
*
* @return True for encodings that represent signed values, false otherwise.
*/
static ZyanBool ZydisIsImmSigned(ZydisOperandEncoding encoding)
{
switch (encoding)
{
case ZYDIS_OPERAND_ENCODING_SIMM8:
case ZYDIS_OPERAND_ENCODING_SIMM16:
case ZYDIS_OPERAND_ENCODING_SIMM32:
case ZYDIS_OPERAND_ENCODING_SIMM64:
case ZYDIS_OPERAND_ENCODING_SIMM16_32_64:
case ZYDIS_OPERAND_ENCODING_SIMM32_32_64:
case ZYDIS_OPERAND_ENCODING_SIMM16_32_32:
case ZYDIS_OPERAND_ENCODING_JIMM8:
case ZYDIS_OPERAND_ENCODING_JIMM16:
case ZYDIS_OPERAND_ENCODING_JIMM32:
case ZYDIS_OPERAND_ENCODING_JIMM64:
case ZYDIS_OPERAND_ENCODING_JIMM16_32_64:
case ZYDIS_OPERAND_ENCODING_JIMM32_32_64:
case ZYDIS_OPERAND_ENCODING_JIMM16_32_32:
return ZYAN_TRUE;
case ZYDIS_OPERAND_ENCODING_DISP8:
case ZYDIS_OPERAND_ENCODING_DISP16:
case ZYDIS_OPERAND_ENCODING_DISP32:
case ZYDIS_OPERAND_ENCODING_DISP64:
case ZYDIS_OPERAND_ENCODING_DISP16_32_64:
case ZYDIS_OPERAND_ENCODING_DISP32_32_64:
case ZYDIS_OPERAND_ENCODING_DISP16_32_32:
case ZYDIS_OPERAND_ENCODING_UIMM8:
case ZYDIS_OPERAND_ENCODING_UIMM16:
case ZYDIS_OPERAND_ENCODING_UIMM32:
case ZYDIS_OPERAND_ENCODING_UIMM64:
case ZYDIS_OPERAND_ENCODING_UIMM16_32_64:
case ZYDIS_OPERAND_ENCODING_UIMM32_32_64:
case ZYDIS_OPERAND_ENCODING_UIMM16_32_32:
case ZYDIS_OPERAND_ENCODING_IS4:
return ZYAN_FALSE;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Calculates effective immediate size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param imm Immediate value to encode.
* @param def_op Operand definition for immediate operand.
*
* @return Effective operand size in bits (0 if function failed).
*/
static ZyanU8 ZydisGetEffectiveImmSize(ZydisEncoderInstructionMatch *match, ZyanI64 imm,
const ZydisOperandDefinition *def_op)
{
ZyanU8 eisz = 0;
ZyanU8 min_size = ZydisIsImmSigned((ZydisOperandEncoding)def_op->op.encoding)
? ZydisGetSignedImmSize(imm)
: ZydisGetUnsignedImmSize((ZyanU64)imm);
switch (def_op->op.encoding)
{
case ZYDIS_OPERAND_ENCODING_UIMM8:
case ZYDIS_OPERAND_ENCODING_SIMM8:
eisz = 8;
break;
case ZYDIS_OPERAND_ENCODING_IS4:
ZYAN_ASSERT(def_op->element_type == ZYDIS_IELEMENT_TYPE_UINT8);
eisz = ((ZyanU64)imm <= 15) ? 8 : 0;
break;
case ZYDIS_OPERAND_ENCODING_UIMM16:
case ZYDIS_OPERAND_ENCODING_SIMM16:
eisz = 16;
break;
case ZYDIS_OPERAND_ENCODING_UIMM32:
case ZYDIS_OPERAND_ENCODING_SIMM32:
eisz = 32;
break;
case ZYDIS_OPERAND_ENCODING_UIMM64:
case ZYDIS_OPERAND_ENCODING_SIMM64:
eisz = 64;
break;
case ZYDIS_OPERAND_ENCODING_UIMM16_32_64:
case ZYDIS_OPERAND_ENCODING_SIMM16_32_64:
{
static const ZyanU16 simm16_32_64_sizes[3] = { 16, 32, 64 };
return ZydisGetScaledImmSize(match, simm16_32_64_sizes, min_size);
}
case ZYDIS_OPERAND_ENCODING_UIMM32_32_64:
case ZYDIS_OPERAND_ENCODING_SIMM32_32_64:
{
static const ZyanU16 simm32_32_64_sizes[3] = { 32, 32, 64 };
return ZydisGetScaledImmSize(match, simm32_32_64_sizes, min_size);
}
case ZYDIS_OPERAND_ENCODING_UIMM16_32_32:
case ZYDIS_OPERAND_ENCODING_SIMM16_32_32:
{
static const ZyanU16 simm16_32_32_sizes[3] = { 16, 32, 32 };
return ZydisGetScaledImmSize(match, simm16_32_32_sizes, min_size);
}
case ZYDIS_OPERAND_ENCODING_DISP16_32_64:
ZYAN_ASSERT(match->easz == 0);
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
if (min_size < 32)
{
min_size = 32;
}
if (min_size == 32 || min_size == 64)
{
match->easz = eisz = min_size;
}
}
else
{
if (min_size < 16)
{
min_size = 16;
}
if (min_size == 16 || min_size == 32)
{
match->easz = eisz = min_size;
}
}
break;
case ZYDIS_OPERAND_ENCODING_JIMM8:
case ZYDIS_OPERAND_ENCODING_JIMM16:
case ZYDIS_OPERAND_ENCODING_JIMM32:
case ZYDIS_OPERAND_ENCODING_JIMM64:
{
ZyanU8 jimm_index = def_op->op.encoding - ZYDIS_OPERAND_ENCODING_JIMM8;
if ((match->request->branch_width != ZYDIS_BRANCH_WIDTH_NONE) &&
(match->request->branch_width != (ZydisBranchWidth)(ZYDIS_BRANCH_WIDTH_8 + jimm_index)))
{
return 0;
}
eisz = 8 << jimm_index;
break;
}
case ZYDIS_OPERAND_ENCODING_JIMM16_32_32:
switch (match->request->branch_width)
{
case ZYDIS_BRANCH_WIDTH_NONE:
{
static const ZyanU16 jimm16_32_32_sizes[3] = { 16, 32, 32 };
return ZydisGetScaledImmSize(match, jimm16_32_32_sizes, min_size);
}
case ZYDIS_BRANCH_WIDTH_16:
eisz = 16;
break;
case ZYDIS_BRANCH_WIDTH_32:
eisz = 32;
break;
case ZYDIS_BRANCH_WIDTH_8:
case ZYDIS_BRANCH_WIDTH_64:
return 0;
default:
ZYAN_UNREACHABLE;
}
break;
default:
ZYAN_UNREACHABLE;
}
return eisz >= min_size ? eisz : 0;
}
/**
* Checks if register width is compatible with effective operand size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg_width Register width in bits.
*
* @return True if width is compatible, false otherwise.
*/
static ZyanBool ZydisCheckOsz(ZydisEncoderInstructionMatch *match, ZydisRegisterWidth reg_width)
{
ZYAN_ASSERT(reg_width <= ZYAN_UINT8_MAX);
if (match->eosz == 0)
{
if (reg_width == 8)
{
return ZYAN_FALSE;
}
match->eosz = (ZyanU8)reg_width;
return ZYAN_TRUE;
}
return match->eosz == (ZyanU8)reg_width ? ZYAN_TRUE : ZYAN_FALSE;
}
/**
* Checks if register width is compatible with effective address size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg_width Register width in bits.
*
* @return True if width is compatible, false otherwise.
*/
static ZyanBool ZydisCheckAsz(ZydisEncoderInstructionMatch *match, ZydisRegisterWidth reg_width)
{
ZYAN_ASSERT(reg_width <= ZYAN_UINT8_MAX);
if (match->easz == 0)
{
if ((match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(reg_width == 16))
{
return ZYAN_FALSE;
}
match->easz = (ZyanU8)reg_width;
return ZYAN_TRUE;
}
return match->easz == (ZyanU8)reg_width ? ZYAN_TRUE : ZYAN_FALSE;
}
/**
* Checks if specified register is valid for provided register class, encoding and machine mode.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg `ZydisRegister` value.
* @param reg_class Register class.
*
* @return True if register value is allowed, false otherwise.
*/
static ZyanBool ZydisIsRegisterAllowed(ZydisEncoderInstructionMatch *match, ZydisRegister reg,
ZydisRegisterClass reg_class)
{
const ZyanI8 reg_id = ZydisRegisterGetId(reg);
ZYAN_ASSERT(reg_id >= 0 && reg_id <= 31);
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
if ((match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_EVEX) &&
(match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_MVEX) &&
(reg_class != ZYDIS_REGCLASS_GPR8) &&
(reg_id >= 16))
{
return ZYAN_FALSE;
}
}
else
{
if (reg_class == ZYDIS_REGCLASS_GPR64)
{
return ZYAN_FALSE;
}
if (reg_id >= 8)
{
return ZYAN_FALSE;
}
}
return ZYAN_TRUE;
}
/**
* Checks if specified scale value is valid for use with SIB addressing.
*
* @param scale Scale value.
*
* @return True if value is valid, false otherwise.
*/
static ZyanBool ZydisIsScaleValid(ZyanU8 scale)
{
switch (scale)
{
case 0:
case 1:
case 2:
case 4:
case 8:
return ZYAN_TRUE;
default:
return ZYAN_FALSE;
}
}
/**
* Enforces register usage constraints associated with usage of `REX` prefix.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg `ZydisRegister` value.
* @param addressing_mode True if checked address is used for address calculations. This
* implies more permissive checks.
*
* @return True if register usage is allowed, false otherwise.
*/
static ZyanBool ZydisValidateRexType(ZydisEncoderInstructionMatch *match, ZydisRegister reg,
ZyanBool addressing_mode)
{
switch (reg)
{
case ZYDIS_REGISTER_AL:
case ZYDIS_REGISTER_CL:
case ZYDIS_REGISTER_DL:
case ZYDIS_REGISTER_BL:
return ZYAN_TRUE;
case ZYDIS_REGISTER_AH:
case ZYDIS_REGISTER_CH:
case ZYDIS_REGISTER_DH:
case ZYDIS_REGISTER_BH:
if (match->rex_type == ZYDIS_REX_TYPE_UNKNOWN)
{
match->rex_type = ZYDIS_REX_TYPE_FORBIDDEN;
}
else if (match->rex_type == ZYDIS_REX_TYPE_REQUIRED)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_REGISTER_SPL:
case ZYDIS_REGISTER_BPL:
case ZYDIS_REGISTER_SIL:
case ZYDIS_REGISTER_DIL:
case ZYDIS_REGISTER_R8B:
case ZYDIS_REGISTER_R9B:
case ZYDIS_REGISTER_R10B:
case ZYDIS_REGISTER_R11B:
case ZYDIS_REGISTER_R12B:
case ZYDIS_REGISTER_R13B:
case ZYDIS_REGISTER_R14B:
case ZYDIS_REGISTER_R15B:
if (match->rex_type == ZYDIS_REX_TYPE_UNKNOWN)
{
match->rex_type = ZYDIS_REX_TYPE_REQUIRED;
}
else if (match->rex_type == ZYDIS_REX_TYPE_FORBIDDEN)
{
return ZYAN_FALSE;
}
break;
default:
if ((ZydisRegisterGetId(reg) > 7) ||
(!addressing_mode && (ZydisRegisterGetClass(reg) == ZYDIS_REGCLASS_GPR64)))
{
if (match->rex_type == ZYDIS_REX_TYPE_UNKNOWN)
{
match->rex_type = ZYDIS_REX_TYPE_REQUIRED;
}
else if (match->rex_type == ZYDIS_REX_TYPE_FORBIDDEN)
{
return ZYAN_FALSE;
}
}
break;
}
return ZYAN_TRUE;
}
/**
* Checks if specified register is valid for use with SIB addressing.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg_class Register class.
* @param reg `ZydisRegister` value.
*
* @return True if register value is allowed, false otherwise.
*/
static ZyanBool ZydisIsValidAddressingClass(ZydisEncoderInstructionMatch *match,
ZydisRegisterClass reg_class, ZydisRegister reg)
{
ZyanBool result;
const ZyanBool is_64 = (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64);
switch (reg_class)
{
case ZYDIS_REGCLASS_INVALID:
return ZYAN_TRUE;
case ZYDIS_REGCLASS_GPR16:
result = !is_64;
break;
case ZYDIS_REGCLASS_GPR32:
result = is_64 || ZydisRegisterGetId(reg) < 8;
break;
case ZYDIS_REGCLASS_GPR64:
result = is_64;
break;
default:
return ZYAN_FALSE;
}
return result && ZydisValidateRexType(match, reg, ZYAN_TRUE);
}
/**
* Helper function that determines correct `ModR/M.RM` value for 16-bit addressing mode.
*
* @param base `ZydisRegister` used as `SIB.base`.
* @param index `ZydisRegister` used as `SIB.index`.
*
* @return `ModR/M.RM` value (-1 if function failed).
*/
static ZyanI8 ZydisGetRm16(ZydisRegister base, ZydisRegister index)
{
static const ZydisRegister modrm16_lookup[8][2] =
{
{ ZYDIS_REGISTER_BX, ZYDIS_REGISTER_SI },
{ ZYDIS_REGISTER_BX, ZYDIS_REGISTER_DI },
{ ZYDIS_REGISTER_BP, ZYDIS_REGISTER_SI },
{ ZYDIS_REGISTER_BP, ZYDIS_REGISTER_DI },
{ ZYDIS_REGISTER_SI, ZYDIS_REGISTER_NONE },
{ ZYDIS_REGISTER_DI, ZYDIS_REGISTER_NONE },
{ ZYDIS_REGISTER_BP, ZYDIS_REGISTER_NONE },
{ ZYDIS_REGISTER_BX, ZYDIS_REGISTER_NONE },
};
for (ZyanI8 i = 0; i < (ZyanI8)ZYAN_ARRAY_LENGTH(modrm16_lookup); ++i)
{
if ((modrm16_lookup[i][0] == base) &&
(modrm16_lookup[i][1] == index))
{
return i;
}
}
return -1;
}
/**
* Encodes `MVEX.sss` field for specified broadcast mode.
*
* @param broadcast Broadcast mode.
*
* @return Corresponding `MVEX.sss` value.
*/
static ZyanU8 ZydisEncodeMvexBroadcastMode(ZydisBroadcastMode broadcast)
{
switch (broadcast)
{
case ZYDIS_BROADCAST_MODE_INVALID:
return 0;
case ZYDIS_BROADCAST_MODE_1_TO_16:
case ZYDIS_BROADCAST_MODE_1_TO_8:
return 1;
case ZYDIS_BROADCAST_MODE_4_TO_16:
case ZYDIS_BROADCAST_MODE_4_TO_8:
return 2;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Encodes `MVEX.sss` field for specified conversion mode.
*
* @param conversion Conversion mode.
*
* @return Corresponding `MVEX.sss` value.
*/
static ZyanU8 ZydisEncodeMvexConversionMode(ZydisConversionMode conversion)
{
switch (conversion)
{
case ZYDIS_CONVERSION_MODE_INVALID:
return 0;
case ZYDIS_CONVERSION_MODE_FLOAT16:
return 3;
case ZYDIS_CONVERSION_MODE_UINT8:
return 4;
case ZYDIS_CONVERSION_MODE_SINT8:
return 5;
case ZYDIS_CONVERSION_MODE_UINT16:
return 6;
case ZYDIS_CONVERSION_MODE_SINT16:
return 7;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Determines scale factor for compressed 8-bit displacement (`EVEX` instructions only).
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return log2(scale factor)
*/
static ZyanU8 ZydisGetCompDispScaleEvex(const ZydisEncoderInstructionMatch *match)
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
ZYAN_ASSERT(match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX);
ZYAN_ASSERT(evex_def->tuple_type);
ZYAN_ASSERT(evex_def->element_size);
const ZyanU8 vector_length = match->definition->vector_length - ZYDIS_VECTOR_LENGTH_128;
static const ZyanU8 size_indexes[ZYDIS_IELEMENT_SIZE_MAX_VALUE + 1] =
{
0, 0, 0, 1, 2, 4
};
ZYAN_ASSERT(evex_def->element_size < ZYAN_ARRAY_LENGTH(size_indexes));
const ZyanU8 size_index = size_indexes[evex_def->element_size];
switch (evex_def->tuple_type)
{
case ZYDIS_TUPLETYPE_FV:
{
static const ZyanU8 scales[2][3][3] =
{
/*B0*/ { /*16*/ { 4, 5, 6 }, /*32*/ { 4, 5, 6 }, /*64*/ { 4, 5, 6 } },
/*B1*/ { /*16*/ { 1, 1, 1 }, /*32*/ { 2, 2, 2 }, /*64*/ { 3, 3, 3 } }
};
const ZyanU8 broadcast = match->request->evex.broadcast ? 1 : 0;
ZYAN_ASSERT(size_index < 3);
return scales[broadcast][size_index][vector_length];
}
case ZYDIS_TUPLETYPE_HV:
{
static const ZyanU8 scales[2][2][3] =
{
/*B0*/ { /*16*/ { 3, 4, 5 }, /*32*/ { 3, 4, 5 } },
/*B1*/ { /*16*/ { 1, 1, 1 }, /*32*/ { 2, 2, 2 } }
};
const ZyanU8 broadcast = match->request->evex.broadcast ? 1 : 0;
ZYAN_ASSERT(size_index < 3);
return scales[broadcast][size_index][vector_length];
}
case ZYDIS_TUPLETYPE_FVM:
{
static const ZyanU8 scales[3] =
{
4, 5, 6
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_GSCAT:
case ZYDIS_TUPLETYPE_T1S:
{
static const ZyanU8 scales[6] =
{
/* */ 0,
/* 8*/ 0,
/* 16*/ 1,
/* 32*/ 2,
/* 64*/ 3,
/*128*/ 4
};
ZYAN_ASSERT(evex_def->element_size < ZYAN_ARRAY_LENGTH(scales));
return scales[evex_def->element_size];
}
case ZYDIS_TUPLETYPE_T1F:
{
static const ZyanU8 scales[3] =
{
/* 16*/ 1,
/* 32*/ 2,
/* 64*/ 3
};
ZYAN_ASSERT(size_index < 3);
return scales[size_index];
}
case ZYDIS_TUPLETYPE_T1_4X:
return 4;
case ZYDIS_TUPLETYPE_T2:
return match->definition->rex_w ? 4 : 3;
case ZYDIS_TUPLETYPE_T4:
return match->definition->rex_w ? 5 : 4;
case ZYDIS_TUPLETYPE_T8:
return 5;
case ZYDIS_TUPLETYPE_HVM:
{
static const ZyanU8 scales[3] =
{
3, 4, 5
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_QVM:
{
static const ZyanU8 scales[3] =
{
2, 3, 4
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_OVM:
{
static const ZyanU8 scales[3] =
{
1, 2, 3
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_M128:
return 4;
case ZYDIS_TUPLETYPE_DUP:
{
static const ZyanU8 scales[3] =
{
3, 5, 6
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_QUARTER:
{
static const ZyanU8 scales[2][3] =
{
/*B0*/ { 2, 3, 4 },
/*B1*/ { 1, 1, 1 }
};
const ZyanU8 broadcast = match->request->evex.broadcast ? 1 : 0;
return scales[broadcast][vector_length];
}
default:
ZYAN_UNREACHABLE;
}
}
/**
* Determines scale factor for compressed 8-bit displacement (`MVEX` instructions only).
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return log2(scale factor)
*/
static ZyanU8 ZydisGetCompDispScaleMvex(const ZydisEncoderInstructionMatch *match)
{
const ZydisInstructionDefinitionMVEX *mvex_def =
(const ZydisInstructionDefinitionMVEX *)match->base_definition;
ZyanU8 index = mvex_def->has_element_granularity;
ZYAN_ASSERT(!index || !mvex_def->broadcast);
if (!index && mvex_def->broadcast)
{
switch (mvex_def->broadcast)
{
case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_8:
case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_16:
index = 1;
break;
case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_8:
case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_16:
index = 2;
break;
default:
ZYAN_UNREACHABLE;
}
}
const ZyanU8 sss = ZydisEncodeMvexBroadcastMode(match->request->mvex.broadcast) |
ZydisEncodeMvexConversionMode(match->request->mvex.conversion);
switch (mvex_def->functionality)
{
case ZYDIS_MVEX_FUNC_IGNORED:
case ZYDIS_MVEX_FUNC_INVALID:
case ZYDIS_MVEX_FUNC_RC:
case ZYDIS_MVEX_FUNC_SAE:
case ZYDIS_MVEX_FUNC_SWIZZLE_32:
case ZYDIS_MVEX_FUNC_SWIZZLE_64:
return 0;
case ZYDIS_MVEX_FUNC_F_32:
case ZYDIS_MVEX_FUNC_I_32:
case ZYDIS_MVEX_FUNC_F_64:
case ZYDIS_MVEX_FUNC_I_64:
return 6;
case ZYDIS_MVEX_FUNC_SF_32:
case ZYDIS_MVEX_FUNC_SF_32_BCST:
case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16:
case ZYDIS_MVEX_FUNC_UF_32:
{
static const ZyanU8 lookup[3][8] =
{
{ 6, 2, 4, 5, 4, 4, 5, 5 },
{ 2, 0, 0, 1, 0, 0, 1, 1 },
{ 4, 0, 0, 3, 2, 2, 3, 3 }
};
ZYAN_ASSERT(sss < ZYAN_ARRAY_LENGTH(lookup[index]));
return lookup[index][sss];
}
case ZYDIS_MVEX_FUNC_SI_32:
case ZYDIS_MVEX_FUNC_UI_32:
case ZYDIS_MVEX_FUNC_SI_32_BCST:
case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16:
{
static const ZyanU8 lookup[3][8] =
{
{ 6, 2, 4, 0, 4, 4, 5, 5 },
{ 2, 0, 0, 0, 0, 0, 1, 1 },
{ 4, 0, 0, 0, 2, 2, 3, 3 }
};
ZYAN_ASSERT(sss < ZYAN_ARRAY_LENGTH(lookup[index]));
return lookup[index][sss];
}
case ZYDIS_MVEX_FUNC_SF_64:
case ZYDIS_MVEX_FUNC_UF_64:
case ZYDIS_MVEX_FUNC_SI_64:
case ZYDIS_MVEX_FUNC_UI_64:
{
static const ZyanU8 lookup[3][3] =
{
{ 6, 3, 5 },
{ 3, 0, 0 },
{ 5, 0, 0 }
};
ZYAN_ASSERT(sss < ZYAN_ARRAY_LENGTH(lookup[index]));
return lookup[index][sss];
}
case ZYDIS_MVEX_FUNC_DF_32:
case ZYDIS_MVEX_FUNC_DI_32:
{
static const ZyanU8 lookup[2][8] =
{
{ 6, 0, 0, 5, 4, 4, 5, 5 },
{ 2, 0, 0, 1, 0, 0, 1, 1 }
};
ZYAN_ASSERT(index < 2);
ZYAN_ASSERT(sss < ZYAN_ARRAY_LENGTH(lookup[index]));
return lookup[index][sss];
}
case ZYDIS_MVEX_FUNC_DF_64:
case ZYDIS_MVEX_FUNC_DI_64:
ZYAN_ASSERT(index < 2);
return index == 0 ? 6 : 3;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Determines scale factor for compressed 8-bit displacement.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return log2(scale factor)
*/
static ZyanU8 ZydisGetCompDispScale(const ZydisEncoderInstructionMatch *match)
{
switch (match->definition->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
case ZYDIS_INSTRUCTION_ENCODING_XOP:
case ZYDIS_INSTRUCTION_ENCODING_VEX:
return 0;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
return ZydisGetCompDispScaleEvex(match);
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
return ZydisGetCompDispScaleMvex(match);
default:
ZYAN_UNREACHABLE;
}
}
/**
* Checks if requested operand matches register operand from instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param user_op Operand definition from `ZydisEncoderRequest` structure.
* @param def_op Decoder's operand definition from current instruction definition.
*
* @return True if operands match, false otherwise.
*/
static ZyanBool ZydisIsRegisterOperandCompatible(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op, const ZydisOperandDefinition *def_op)
{
const ZydisRegisterClass reg_class = ZydisRegisterGetClass(user_op->reg.value);
const ZydisRegisterWidth reg_width = ZydisRegisterClassGetWidth(match->request->machine_mode,
reg_class);
if (reg_width == 0)
{
return ZYAN_FALSE;
}
ZyanBool is4_expected_value = ZYAN_FALSE;
switch (def_op->type)
{
case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_REG:
switch (def_op->op.reg.type)
{
case ZYDIS_IMPLREG_TYPE_STATIC:
if (def_op->op.reg.reg.reg != user_op->reg.value)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_IMPLREG_TYPE_GPR_OSZ:
if ((reg_class != ZYDIS_REGCLASS_GPR8) &&
(reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (def_op->op.reg.reg.id != ZydisRegisterGetId(user_op->reg.value))
{
return ZYAN_FALSE;
}
if (!ZydisCheckOsz(match, reg_width))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_IMPLREG_TYPE_GPR_ASZ:
if ((reg_class != ZYDIS_REGCLASS_GPR8) &&
(reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (def_op->op.reg.reg.id != ZydisRegisterGetId(user_op->reg.value))
{
return ZYAN_FALSE;
}
if (!ZydisCheckAsz(match, reg_width))
{
return ZYAN_FALSE;
}
break;
default:
ZYAN_UNREACHABLE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR8:
if (reg_class != ZYDIS_REGCLASS_GPR8)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
if (!ZydisValidateRexType(match, user_op->reg.value, ZYAN_FALSE))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR16:
if (reg_class != ZYDIS_REGCLASS_GPR16)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR32:
if (reg_class != ZYDIS_REGCLASS_GPR32)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR64:
if (reg_class != ZYDIS_REGCLASS_GPR64)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR16_32_64:
if ((reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (!ZydisCheckOsz(match, reg_width))
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
if (!ZydisValidateRexType(match, user_op->reg.value, ZYAN_FALSE))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR32_32_64:
if ((reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (match->eosz == 0)
{
if (reg_class == ZYDIS_REGCLASS_GPR64)
{
match->eosz = 64;
}
else
{
match->eosz64_forbidden = ZYAN_TRUE;
}
}
else if (match->eosz != (ZyanU8)reg_width)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
if (!ZydisValidateRexType(match, user_op->reg.value, ZYAN_FALSE))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR16_32_32:
if ((reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32))
{
return ZYAN_FALSE;
}
if (!ZydisCheckOsz(match, reg_width))
{
if (match->eosz != 64 || reg_class != ZYDIS_REGCLASS_GPR32)
{
return ZYAN_FALSE;
}
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR_ASZ:
if ((reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (!ZydisCheckAsz(match, reg_width))
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_FPR:
if (reg_class != ZYDIS_REGCLASS_X87)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_MMX:
if (reg_class != ZYDIS_REGCLASS_MMX)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_XMM:
if (reg_class != ZYDIS_REGCLASS_XMM)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
is4_expected_value = def_op->op.encoding == ZYDIS_OPERAND_ENCODING_IS4;
break;
case ZYDIS_SEMANTIC_OPTYPE_YMM:
if (reg_class != ZYDIS_REGCLASS_YMM)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
is4_expected_value = def_op->op.encoding == ZYDIS_OPERAND_ENCODING_IS4;
break;
case ZYDIS_SEMANTIC_OPTYPE_ZMM:
if (reg_class != ZYDIS_REGCLASS_ZMM)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_TMM:
if (reg_class != ZYDIS_REGCLASS_TMM)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_BND:
if (reg_class != ZYDIS_REGCLASS_BOUND)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_SREG:
if (reg_class != ZYDIS_REGCLASS_SEGMENT)
{
return ZYAN_FALSE;
}
if ((def_op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE) &&
(user_op->reg.value == ZYDIS_REGISTER_CS))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_CR:
{
if (reg_class != ZYDIS_REGCLASS_CONTROL)
{
return ZYAN_FALSE;
}
static const ZyanU8 cr_lookup[16] =
{
1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
};
const ZyanI8 reg_id = ZydisRegisterGetId(user_op->reg.value);
if ((match->request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) &&
(reg_id == 8))
{
return ZYAN_FALSE;
}
if (!cr_lookup[reg_id])
{
return ZYAN_FALSE;
}
break;
}
case ZYDIS_SEMANTIC_OPTYPE_DR:
if (reg_class != ZYDIS_REGCLASS_DEBUG)
{
return ZYAN_FALSE;
}
if (user_op->reg.value >= ZYDIS_REGISTER_DR8)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_MASK:
if (reg_class != ZYDIS_REGCLASS_MASK)
{
return ZYAN_FALSE;
}
// MVEX does not require similar policy check
if ((match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) &&
(def_op->op.encoding == ZYDIS_OPERAND_ENCODING_MASK))
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
ZYAN_ASSERT((evex_def->mask_policy != ZYDIS_MASK_POLICY_INVALID) &&
(evex_def->mask_policy != ZYDIS_MASK_POLICY_FORBIDDEN));
if ((evex_def->mask_policy == ZYDIS_MASK_POLICY_REQUIRED) &&
(user_op->reg.value == ZYDIS_REGISTER_K0))
{
return ZYAN_FALSE;
}
if ((evex_def->mask_policy == ZYDIS_MASK_POLICY_ALLOWED) &&
(match->request->evex.zeroing_mask) &&
(user_op->reg.value == ZYDIS_REGISTER_K0))
{
return ZYAN_FALSE;
}
}
break;
default:
ZYAN_UNREACHABLE;
}
if (user_op->reg.is4 != is4_expected_value)
{
return ZYAN_FALSE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested operand matches memory operand from instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param user_op Operand definition from `ZydisEncoderRequest` structure.
* @param def_op Decoder's operand definition from current instruction definition.
*
* @return True if operands match, false otherwise.
*/
static ZyanBool ZydisIsMemoryOperandCompatible(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op, const ZydisOperandDefinition *def_op)
{
switch (def_op->type)
{
case ZYDIS_SEMANTIC_OPTYPE_MEM:
case ZYDIS_SEMANTIC_OPTYPE_AGEN:
case ZYDIS_SEMANTIC_OPTYPE_MIB:
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBX:
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBY:
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBZ:
{
if ((def_op->type == ZYDIS_SEMANTIC_OPTYPE_MIB) &&
(user_op->mem.scale != 0))
{
return ZYAN_FALSE;
}
ZyanI64 displacement = user_op->mem.displacement;
ZyanU8 disp_size = 0;
if (displacement)
{
disp_size = ZydisGetSignedImmSize(displacement);
if (disp_size > 32)
{
return ZYAN_FALSE;
}
if (ZydisGetMachineModeWidth(match->request->machine_mode) == 16)
{
if ((ZyanI16)displacement == 0)
{
disp_size = 0;
}
else
{
disp_size = ZydisGetSignedImmSize((ZyanI16)displacement);
}
}
match->cd8_scale = ZydisGetCompDispScale(match);
if (match->cd8_scale)
{
const ZyanI64 mask = (1 << match->cd8_scale) - 1;
if (!(displacement & mask))
{
disp_size = ZydisGetSignedImmSize(displacement >> match->cd8_scale);
}
else if (disp_size == 8)
{
disp_size = 16;
}
}
}
if (def_op->type != ZYDIS_SEMANTIC_OPTYPE_AGEN)
{
if (match->eosz != 0)
{
const ZyanU8 eosz_index = match->eosz >> 5;
if (def_op->size[eosz_index] != user_op->mem.size)
{
return ZYAN_FALSE;
}
}
else if ((match->definition->vector_length != ZYDIS_VECTOR_LENGTH_INVALID) ||
(match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
{
ZyanU8 eosz_index = ZydisGetMachineModeWidth(match->request->machine_mode) >> 5;
if (match->eosz64_forbidden && (eosz_index == 2))
{
eosz_index = 1;
}
ZyanU16 allowed_mem_size = def_op->size[eosz_index];
if ((!allowed_mem_size) &&
(match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_VEX))
{
ZYAN_ASSERT((match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
(match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX));
switch (match->definition->vector_length)
{
case ZYDIS_VECTOR_LENGTH_128:
allowed_mem_size = 16;
break;
case ZYDIS_VECTOR_LENGTH_256:
allowed_mem_size = 32;
break;
case ZYDIS_VECTOR_LENGTH_INVALID:
ZYAN_ASSERT(match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX);
ZYAN_FALLTHROUGH;
case ZYDIS_VECTOR_LENGTH_512:
allowed_mem_size = 64;
break;
default:
ZYAN_UNREACHABLE;
}
if (match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX)
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
static const ZyanU8 element_sizes[ZYDIS_IELEMENT_SIZE_MAX_VALUE + 1] =
{
0, 1, 2, 4, 8, 16
};
ZYAN_ASSERT(evex_def->element_size < ZYAN_ARRAY_LENGTH(element_sizes));
const ZyanU8 element_size = element_sizes[evex_def->element_size];
if (match->request->evex.broadcast || evex_def->broadcast)
{
allowed_mem_size = element_size;
}
else
{
switch (evex_def->tuple_type)
{
case ZYDIS_TUPLETYPE_FV:
break;
case ZYDIS_TUPLETYPE_HV:
allowed_mem_size /= 2;
break;
case ZYDIS_TUPLETYPE_QUARTER:
allowed_mem_size /= 4;
break;
default:
ZYAN_UNREACHABLE;
}
}
}
else
{
const ZydisInstructionDefinitionMVEX *mvex_def =
(const ZydisInstructionDefinitionMVEX *)match->base_definition;
ZyanU16 element_size;
switch (match->request->mvex.conversion)
{
case ZYDIS_CONVERSION_MODE_INVALID:
switch (mvex_def->functionality)
{
case ZYDIS_MVEX_FUNC_SF_32:
case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16:
case ZYDIS_MVEX_FUNC_UF_32:
case ZYDIS_MVEX_FUNC_DF_32:
case ZYDIS_MVEX_FUNC_SI_32:
case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16:
case ZYDIS_MVEX_FUNC_UI_32:
case ZYDIS_MVEX_FUNC_DI_32:
allowed_mem_size = 64;
element_size = 4;
break;
case ZYDIS_MVEX_FUNC_SF_64:
case ZYDIS_MVEX_FUNC_UF_64:
case ZYDIS_MVEX_FUNC_DF_64:
case ZYDIS_MVEX_FUNC_SI_64:
case ZYDIS_MVEX_FUNC_UI_64:
case ZYDIS_MVEX_FUNC_DI_64:
allowed_mem_size = 64;
element_size = 8;
break;
case ZYDIS_MVEX_FUNC_SF_32_BCST:
case ZYDIS_MVEX_FUNC_SI_32_BCST:
allowed_mem_size = 32;
element_size = 4;
break;
default:
ZYAN_UNREACHABLE;
}
break;
case ZYDIS_CONVERSION_MODE_FLOAT16:
case ZYDIS_CONVERSION_MODE_SINT16:
case ZYDIS_CONVERSION_MODE_UINT16:
allowed_mem_size = 32;
element_size = 2;
break;
case ZYDIS_CONVERSION_MODE_SINT8:
case ZYDIS_CONVERSION_MODE_UINT8:
allowed_mem_size = 16;
element_size = 1;
break;
default:
ZYAN_UNREACHABLE;
}
ZYAN_ASSERT(!mvex_def->broadcast || !match->request->mvex.broadcast);
switch (mvex_def->broadcast)
{
case ZYDIS_MVEX_STATIC_BROADCAST_NONE:
break;
case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_8:
case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_16:
allowed_mem_size = element_size;
break;
case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_8:
case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_16:
allowed_mem_size = element_size * 4;
break;
default:
ZYAN_UNREACHABLE;
}
switch (match->request->mvex.broadcast)
{
case ZYDIS_BROADCAST_MODE_INVALID:
break;
case ZYDIS_BROADCAST_MODE_1_TO_8:
case ZYDIS_BROADCAST_MODE_1_TO_16:
allowed_mem_size = element_size;
break;
case ZYDIS_BROADCAST_MODE_4_TO_8:
case ZYDIS_BROADCAST_MODE_4_TO_16:
allowed_mem_size = element_size * 4;
break;
default:
ZYAN_UNREACHABLE;
}
}
}
if (user_op->mem.size != allowed_mem_size)
{
return ZYAN_FALSE;
}
}
else if (match->definition->rex_w)
{
match->eosz = 64;
}
else if (match->definition->vector_length == ZYDIS_VECTOR_LENGTH_INVALID)
{
match->eosz = ZydisGetOperandSizeFromElementSize(match, def_op->size,
user_op->mem.size, ZYAN_TRUE);
if (match->eosz == 0)
{
return ZYAN_FALSE;
}
}
else
{
ZYAN_UNREACHABLE;
}
}
else
{
if (match->easz != 0)
{
if (match->easz != user_op->mem.size)
{
return ZYAN_FALSE;
}
}
else
{
switch (user_op->mem.size)
{
case 2:
case 4:
case 8:
match->easz = (ZyanU8)user_op->mem.size << 3;
break;
default:
return ZYAN_FALSE;
}
}
}
ZydisRegisterClass vsib_index_class = ZYDIS_REGCLASS_INVALID;
ZyanBool is_vsib = ZYAN_TRUE;
switch (def_op->type)
{
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBX:
vsib_index_class = ZYDIS_REGCLASS_XMM;
break;
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBY:
vsib_index_class = ZYDIS_REGCLASS_YMM;
break;
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBZ:
vsib_index_class = ZYDIS_REGCLASS_ZMM;
break;
default:
is_vsib = ZYAN_FALSE;
break;
}
const ZyanBool is_rip_relative = (user_op->mem.base == ZYDIS_REGISTER_RIP) ||
(user_op->mem.base == ZYDIS_REGISTER_EIP);
if (is_rip_relative)
{
const ZyanBool no_rip_rel = ZYDIS_OPDEF_GET_MEM_HIGH_BIT(match->base_definition->op_rm);
if (no_rip_rel || ((match->definition->modrm & 7) == 4))
{
return ZYAN_FALSE;
}
}
const ZydisRegisterClass reg_base_class = ZydisRegisterGetClass(user_op->mem.base);
if ((reg_base_class == ZYDIS_REGCLASS_INVALID) &&
(user_op->mem.base != ZYDIS_REGISTER_NONE))
{
return ZYAN_FALSE;
}
const ZydisRegisterClass reg_index_class = ZydisRegisterGetClass(user_op->mem.index);
if ((reg_index_class == ZYDIS_REGCLASS_INVALID) &&
(user_op->mem.index != ZYDIS_REGISTER_NONE))
{
return ZYAN_FALSE;
}
if (is_vsib)
{
const ZyanU8 mode_width = ZydisGetMachineModeWidth(match->request->machine_mode);
const ZyanI8 reg_index_id = ZydisRegisterGetId(user_op->mem.index);
if (((match->request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) ||
(reg_base_class != ZYDIS_REGCLASS_GPR64)) &&
(reg_base_class != ZYDIS_REGCLASS_GPR32) &&
(reg_base_class != ZYDIS_REGCLASS_INVALID))
{
return ZYAN_FALSE;
}
if ((reg_base_class == ZYDIS_REGCLASS_GPR32) &&
(mode_width != 64) &&
(ZydisRegisterGetId(user_op->mem.base) > 7))
{
return ZYAN_FALSE;
}
ZyanU8 max_reg_id = 7;
if (mode_width == 64)
{
max_reg_id = match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_VEX ?
31 : 15;
}
if ((reg_index_class != vsib_index_class) ||
(reg_index_id > max_reg_id))
{
return ZYAN_FALSE;
}
}
else
{
if (!ZydisIsValidAddressingClass(match, reg_base_class, user_op->mem.base))
{
if (!is_rip_relative || match->request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
{
return ZYAN_FALSE;
}
}
if (!ZydisIsValidAddressingClass(match, reg_index_class, user_op->mem.index))
{
return ZYAN_FALSE;
}
if (reg_base_class != ZYDIS_REGCLASS_INVALID &&
reg_index_class != ZYDIS_REGCLASS_INVALID &&
reg_base_class != reg_index_class)
{
return ZYAN_FALSE;
}
if (user_op->mem.index == ZYDIS_REGISTER_ESP ||
user_op->mem.index == ZYDIS_REGISTER_RSP)
{
return ZYAN_FALSE;
}
}
if (reg_index_class != ZYDIS_REGCLASS_INVALID &&
user_op->mem.scale == 0 &&
def_op->type != ZYDIS_SEMANTIC_OPTYPE_MIB)
{
return ZYAN_FALSE;
}
if (reg_index_class == ZYDIS_REGCLASS_INVALID &&
user_op->mem.scale != 0)
{
return ZYAN_FALSE;
}
ZyanU8 candidate_easz = 0;
ZyanBool disp_only = ZYAN_FALSE;
if (reg_base_class != ZYDIS_REGCLASS_INVALID)
{
if (is_rip_relative)
{
candidate_easz = user_op->mem.base == ZYDIS_REGISTER_RIP ? 64 : 32;
}
else
{
candidate_easz = (ZyanU8)ZydisRegisterClassGetWidth(match->request->machine_mode,
reg_base_class);
}
}
else if (reg_index_class != ZYDIS_REGCLASS_INVALID)
{
if (is_vsib)
{
candidate_easz = ZydisGetMachineModeWidth(match->request->machine_mode);
}
else
{
candidate_easz = (ZyanU8)ZydisRegisterClassGetWidth(match->request->machine_mode,
reg_index_class);
}
}
else
{
ZyanU8 min_disp_size = match->easz ? match->easz : 16;
if (((min_disp_size == 16) && !(match->definition->address_sizes & ZYDIS_WIDTH_16)) ||
(min_disp_size == 64))
{
min_disp_size = 32;
}
if (ZydisGetUnsignedImmSize(displacement) == 16)
{
disp_size = 16;
}
if (disp_size < min_disp_size)
{
disp_size = min_disp_size;
}
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
candidate_easz = match->easz == 32 ? 32 : 64;
}
else
{
candidate_easz = disp_size;
}
disp_only = ZYAN_TRUE;
}
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
if (is_rip_relative && reg_index_class != ZYDIS_REGCLASS_INVALID)
{
return ZYAN_FALSE;
}
}
else
{
if (candidate_easz == 16 && !disp_only)
{
if (disp_size > 16)
{
return ZYAN_FALSE;
}
const ZyanI8 rm16 = ZydisGetRm16(user_op->mem.base, user_op->mem.index);
if (rm16 == -1)
{
return ZYAN_FALSE;
}
const ZyanU8 allowed_scale = rm16 < 4 ? 1 : 0;
if (user_op->mem.scale != allowed_scale)
{
return ZYAN_FALSE;
}
}
}
if (match->easz != 0)
{
if (match->easz != candidate_easz)
{
return ZYAN_FALSE;
}
}
else
{
match->easz = candidate_easz;
}
if ((match->base_definition->address_size_map == ZYDIS_ADSIZE_MAP_IGNORED) &&
(match->easz != ZydisGetMachineModeWidth(match->request->machine_mode)))
{
return ZYAN_FALSE;
}
match->disp_size = disp_size;
break;
}
case ZYDIS_SEMANTIC_OPTYPE_MOFFS:
if (user_op->mem.base != ZYDIS_REGISTER_NONE ||
user_op->mem.index != ZYDIS_REGISTER_NONE ||
user_op->mem.scale != 0)
{
return ZYAN_FALSE;
}
if (match->eosz != 0)
{
const ZyanU8 eosz_index = match->eosz >> 5;
if (def_op->size[eosz_index] != user_op->mem.size)
{
return ZYAN_FALSE;
}
}
else
{
match->eosz = ZydisGetOperandSizeFromElementSize(match, def_op->size,
user_op->mem.size, ZYAN_TRUE);
if (match->eosz == 0)
{
return ZYAN_FALSE;
}
}
match->disp_size = ZydisGetEffectiveImmSize(match, user_op->mem.displacement, def_op);
if (match->disp_size == 0)
{
return ZYAN_FALSE;
}
// This is not a standard rejection. It's a special case for `mov` instructions (only ones
// to use `moffs` operands). Size of `moffs` is tied to address size attribute, so its
// signedness doesn't matter. However if displacement can be represented as a signed
// integer of smaller size we reject `moffs` variant because it's guaranteed that better
// alternative exists (in terms of size).
ZyanU8 alternative_size = ZydisGetSignedImmSize(user_op->mem.displacement);
const ZyanU8 min_disp_size =
(match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) ? 32 : 16;
if (alternative_size < min_disp_size)
{
alternative_size = min_disp_size;
}
if (alternative_size < match->disp_size)
{
return ZYAN_FALSE;
}
break;
default:
ZYAN_UNREACHABLE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested operand matches pointer operand from instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param user_op Operand definition from `ZydisEncoderRequest` structure.
*
* @return True if operands match, false otherwise.
*/
static ZyanBool ZydisIsPointerOperandCompatible(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op)
{
ZYAN_ASSERT(match->eosz == 0);
ZYAN_ASSERT(match->request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64);
ZYAN_ASSERT((match->request->branch_type == ZYDIS_BRANCH_TYPE_NONE) ||
(match->request->branch_type == ZYDIS_BRANCH_TYPE_FAR));
const ZyanU8 min_disp_size = ZydisGetUnsignedImmSize(user_op->ptr.offset);
const ZyanU8 desired_disp_size = (match->request->branch_width == ZYDIS_BRANCH_WIDTH_NONE)
? ZydisGetMachineModeWidth(match->request->machine_mode)
: (4 << match->request->branch_width);
if (min_disp_size > desired_disp_size)
{
return ZYAN_FALSE;
}
match->eosz = match->disp_size = desired_disp_size;
match->imm_size = 16;
return ZYAN_TRUE;
}
/**
* Checks if requested operand matches immediate operand from instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param user_op Operand definition from `ZydisEncoderRequest` structure.
* @param def_op Decoder's operand definition from current instruction definition.
*
* @return True if operands match, false otherwise.
*/
static ZyanBool ZydisIsImmediateOperandCompabile(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op, const ZydisOperandDefinition *def_op)
{
switch (def_op->type)
{
case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1:
if (user_op->imm.u != 1)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_IMM:
case ZYDIS_SEMANTIC_OPTYPE_REL:
{
const ZyanU8 imm_size = ZydisGetEffectiveImmSize(match, user_op->imm.s, def_op);
if (def_op->op.encoding != ZYDIS_OPERAND_ENCODING_IS4)
{
if (imm_size == 0)
{
return ZYAN_FALSE;
}
if (match->imm_size)
{
ZYAN_ASSERT(match->disp_size == 0);
match->disp_size = match->imm_size;
}
}
else
{
ZYAN_ASSERT(match->imm_size == 0);
if (imm_size != 8)
{
return ZYAN_FALSE;
}
}
match->imm_size = imm_size;
match->has_rel_operand = (def_op->type == ZYDIS_SEMANTIC_OPTYPE_REL);
break;
}
default:
ZYAN_UNREACHABLE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested boardcast mode is compatible with instruction definition.
*
* @param evex_def Definition for `EVEX`-encoded instruction.
* @param vector_length Vector length.
* @param broadcast Requested broadcast mode.
*
* @return True if broadcast mode is compatible, false otherwise.
*/
static ZyanBool ZydisIsBroadcastModeCompatible(const ZydisInstructionDefinitionEVEX *evex_def,
ZydisVectorLength vector_length, ZydisBroadcastMode broadcast)
{
if (broadcast == ZYDIS_BROADCAST_MODE_INVALID)
{
return ZYAN_TRUE;
}
ZyanU8 vector_size = 0;
ZYAN_ASSERT(vector_length != ZYDIS_VECTOR_LENGTH_INVALID);
switch (vector_length)
{
case ZYDIS_VECTOR_LENGTH_128:
vector_size = 16;
break;
case ZYDIS_VECTOR_LENGTH_256:
vector_size = 32;
break;
case ZYDIS_VECTOR_LENGTH_512:
vector_size = 64;
break;
default:
ZYAN_UNREACHABLE;
}
switch (evex_def->tuple_type)
{
case ZYDIS_TUPLETYPE_FV:
break;
case ZYDIS_TUPLETYPE_HV:
vector_size /= 2;
break;
case ZYDIS_TUPLETYPE_QUARTER:
vector_size /= 4;
break;
default:
ZYAN_UNREACHABLE;
}
ZyanU8 element_size;
switch (evex_def->element_size)
{
case ZYDIS_IELEMENT_SIZE_16:
element_size = 2;
break;
case ZYDIS_IELEMENT_SIZE_32:
element_size = 4;
break;
case ZYDIS_IELEMENT_SIZE_64:
element_size = 8;
break;
default:
ZYAN_UNREACHABLE;
}
ZydisBroadcastMode allowed_mode;
const ZyanU8 element_count = vector_size / element_size;
switch (element_count)
{
case 2:
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_2;
break;
case 4:
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_4;
break;
case 8:
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_8;
break;
case 16:
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_16;
break;
case 32:
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_32;
break;
default:
ZYAN_UNREACHABLE;
}
if (broadcast != allowed_mode)
{
return ZYAN_FALSE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested `EVEX`-specific features are compatible with instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param request A pointer to `ZydisEncoderRequest` struct.
*
* @return True if features are compatible, false otherwise.
*/
static ZyanBool ZydisAreEvexFeaturesCompatible(const ZydisEncoderInstructionMatch *match,
const ZydisEncoderRequest *request)
{
if (match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_EVEX)
{
return ZYAN_TRUE;
}
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
if ((!evex_def->accepts_zero_mask) &&
(evex_def->mask_override != ZYDIS_MASK_OVERRIDE_ZEROING) &&
(request->evex.zeroing_mask))
{
return ZYAN_FALSE;
}
switch (evex_def->functionality)
{
case ZYDIS_EVEX_FUNC_INVALID:
if ((request->evex.sae) ||
(request->evex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->evex.rounding != ZYDIS_ROUNDING_MODE_INVALID))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_EVEX_FUNC_BC:
if ((request->evex.sae) ||
(request->evex.rounding != ZYDIS_ROUNDING_MODE_INVALID))
{
return ZYAN_FALSE;
}
if (!ZydisIsBroadcastModeCompatible(evex_def, match->definition->vector_length,
request->evex.broadcast))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_EVEX_FUNC_RC:
if (request->evex.broadcast != ZYDIS_BROADCAST_MODE_INVALID)
{
return ZYAN_FALSE;
}
if (request->evex.rounding == ZYDIS_ROUNDING_MODE_INVALID)
{
if (request->evex.sae)
{
return ZYAN_FALSE;
}
}
else
{
if (!request->evex.sae)
{
return ZYAN_FALSE;
}
}
break;
case ZYDIS_EVEX_FUNC_SAE:
if ((request->evex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->evex.rounding != ZYDIS_ROUNDING_MODE_INVALID))
{
return ZYAN_FALSE;
}
break;
default:
ZYAN_UNREACHABLE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested `MVEX`-specific features are compatible with instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param request A pointer to `ZydisEncoderRequest` struct.
*
* @return True if features are compatible, false otherwise.
*/
static ZyanBool ZydisAreMvexFeaturesCompatible(const ZydisEncoderInstructionMatch *match,
const ZydisEncoderRequest *request)
{
if (match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_MVEX)
{
return ZYAN_TRUE;
}
if (((match->definition->modrm >> 6) == 3) &&
(request->mvex.eviction_hint))
{
return ZYAN_FALSE;
}
const ZydisInstructionDefinitionMVEX *mvex_def =
(const ZydisInstructionDefinitionMVEX *)match->base_definition;
switch (mvex_def->functionality)
{
case ZYDIS_MVEX_FUNC_IGNORED:
case ZYDIS_MVEX_FUNC_INVALID:
case ZYDIS_MVEX_FUNC_F_32:
case ZYDIS_MVEX_FUNC_I_32:
case ZYDIS_MVEX_FUNC_F_64:
case ZYDIS_MVEX_FUNC_I_64:
case ZYDIS_MVEX_FUNC_UF_64:
case ZYDIS_MVEX_FUNC_UI_64:
case ZYDIS_MVEX_FUNC_DF_64:
case ZYDIS_MVEX_FUNC_DI_64:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_RC:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.eviction_hint))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_SAE:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.eviction_hint))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_SWIZZLE_32:
case ZYDIS_MVEX_FUNC_SWIZZLE_64:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_SF_32:
if ((request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_1_TO_16) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_16))
{
return ZYAN_FALSE;
}
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_FLOAT16) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT16) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT16))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_SI_32:
if ((request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_1_TO_16) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_16))
{
return ZYAN_FALSE;
}
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT16) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT16))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_SF_32_BCST:
case ZYDIS_MVEX_FUNC_SI_32_BCST:
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_1_TO_16) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_16))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16:
case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16:
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_16))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_SF_64:
case ZYDIS_MVEX_FUNC_SI_64:
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_1_TO_8) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_8))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_UF_32:
case ZYDIS_MVEX_FUNC_DF_32:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_MVEX_FUNC_UI_32:
case ZYDIS_MVEX_FUNC_DI_32:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT16) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT16))
{
return ZYAN_FALSE;
}
break;
default:
ZYAN_UNREACHABLE;
}
return ZYAN_TRUE;
}
/**
* Checks if operands specified in encoder request satisfy additional constraints mandated by
* matched instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return True if operands passed the checks, false otherwise.
*/
static ZyanBool ZydisCheckConstraints(const ZydisEncoderInstructionMatch *match)
{
const ZydisEncoderOperand *operands = match->request->operands;
ZyanBool is_gather = ZYAN_FALSE;
switch (match->definition->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_VEX:
{
const ZydisInstructionDefinitionVEX *vex_def =
(const ZydisInstructionDefinitionVEX *)match->base_definition;
if (vex_def->is_gather)
{
ZYAN_ASSERT(match->request->operand_count == 3);
ZYAN_ASSERT(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY);
ZYAN_ASSERT(operands[2].type == ZYDIS_OPERAND_TYPE_REGISTER);
const ZyanI8 dest = ZydisRegisterGetId(operands[0].reg.value);
const ZyanI8 index = ZydisRegisterGetId(operands[1].mem.index);
const ZyanI8 mask = ZydisRegisterGetId(operands[2].reg.value);
// If any pair of the index, mask, or destination registers are the same, the
// instruction results a UD fault.
if ((dest == index) || (dest == mask) || (index == mask))
{
return ZYAN_FALSE;
}
}
if (vex_def->no_source_source_match)
{
ZYAN_ASSERT(match->request->operand_count == 3);
ZYAN_ASSERT(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[2].type == ZYDIS_OPERAND_TYPE_REGISTER);
const ZydisRegister dest = operands[0].reg.value;
const ZydisRegister source1 = operands[1].reg.value;
const ZydisRegister source2 = operands[2].reg.value;
// AMX-E4: #UD if srcdest == src1 OR src1 == src2 OR srcdest == src2.
if ((dest == source1) || (source1 == source2) || (dest == source2))
{
return ZYAN_FALSE;
}
}
return ZYAN_TRUE;
}
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
is_gather = evex_def->is_gather;
if (evex_def->no_source_dest_match)
{
ZYAN_ASSERT(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[2].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT((operands[3].type == ZYDIS_OPERAND_TYPE_REGISTER) ||
(operands[3].type == ZYDIS_OPERAND_TYPE_MEMORY));
const ZydisRegister dest = operands[0].reg.value;
const ZydisRegister source1 = operands[2].reg.value;
const ZydisRegister source2 = (operands[3].type == ZYDIS_OPERAND_TYPE_REGISTER)
? operands[3].reg.value
: ZYDIS_REGISTER_NONE;
if ((dest == source1) || (dest == source2))
{
return ZYAN_FALSE;
}
}
break;
}
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
{
const ZydisInstructionDefinitionMVEX *mvex_def =
(const ZydisInstructionDefinitionMVEX *)match->base_definition;
is_gather = mvex_def->is_gather;
break;
}
default:
return ZYAN_TRUE;
}
if ((is_gather) && (operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER))
{
ZYAN_ASSERT(match->request->operand_count == 3);
ZYAN_ASSERT(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[2].type == ZYDIS_OPERAND_TYPE_MEMORY);
const ZyanI8 dest = ZydisRegisterGetId(operands[0].reg.value);
const ZyanI8 index = ZydisRegisterGetId(operands[2].mem.index);
// EVEX: The instruction will #UD fault if the destination vector zmm1 is the same as
// index vector VINDEX.
// MVEX: The KNC GATHER instructions forbid using the same vector register for destination
// and for the index. (https://github.com/intelxed/xed/issues/281#issuecomment-970074554)
if (dest == index)
{
return ZYAN_FALSE;
}
}
return ZYAN_TRUE;
}
/**
* Checks if operands and encoding-specific features from `ZydisEncoderRequest` match
* encoder's instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param request A pointer to `ZydisEncoderRequest` struct.
*
* @return True if definition is compatible, false otherwise.
*/
static ZyanBool ZydisIsDefinitionCompatible(ZydisEncoderInstructionMatch *match,
const ZydisEncoderRequest *request)
{
ZYAN_ASSERT(request->operand_count == match->base_definition->operand_count_visible);
match->operands = ZydisGetOperandDefinitions(match->base_definition);
if (!ZydisAreEvexFeaturesCompatible(match, request))
{
return ZYAN_FALSE;
}
if (!ZydisAreMvexFeaturesCompatible(match, request))
{
return ZYAN_FALSE;
}
for (ZyanU8 i = 0; i < request->operand_count; ++i)
{
const ZydisEncoderOperand *user_op = &request->operands[i];
const ZydisOperandDefinition *def_op = &match->operands[i];
ZYAN_ASSERT(def_op->visibility != ZYDIS_OPERAND_VISIBILITY_HIDDEN);
ZyanBool is_compatible = ZYAN_FALSE;
switch (user_op->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
is_compatible = ZydisIsRegisterOperandCompatible(match, user_op, def_op);
break;
case ZYDIS_OPERAND_TYPE_MEMORY:
is_compatible = ZydisIsMemoryOperandCompatible(match, user_op, def_op);
break;
case ZYDIS_OPERAND_TYPE_POINTER:
is_compatible = ZydisIsPointerOperandCompatible(match, user_op);
break;
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
is_compatible = ZydisIsImmediateOperandCompabile(match, user_op, def_op);
break;
default:
ZYAN_UNREACHABLE;
}
if (!is_compatible)
{
return ZYAN_FALSE;
}
}
ZyanU8 eosz = 0;
if (match->base_definition->branch_type != ZYDIS_BRANCH_TYPE_NONE)
{
switch (request->branch_width)
{
case ZYDIS_BRANCH_WIDTH_NONE:
break;
case ZYDIS_BRANCH_WIDTH_8:
if ((!match->has_rel_operand) ||
(match->base_definition->branch_type != ZYDIS_BRANCH_TYPE_SHORT))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_BRANCH_WIDTH_16:
eosz = 16;
break;
case ZYDIS_BRANCH_WIDTH_32:
eosz = ((match->has_rel_operand) &&
(match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(match->base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_FORCE64))
? 64
: 32;
break;
case ZYDIS_BRANCH_WIDTH_64:
if (match->has_rel_operand)
{
return ZYAN_FALSE;
}
eosz = 64;
break;
default:
ZYAN_UNREACHABLE;
}
}
if (eosz)
{
if (match->eosz != 0)
{
if (match->eosz != eosz)
{
return ZYAN_FALSE;
}
}
else
{
match->eosz = eosz;
}
}
if (!ZydisCheckConstraints(match))
{
return ZYAN_FALSE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested set of prefixes is compatible with instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return A zyan status code.
*/
static ZyanBool ZydisArePrefixesCompatible(const ZydisEncoderInstructionMatch *match)
{
// Early-exit optimization for when no prefixes are requested at all.
if (!(match->attributes & ZYDIS_ENCODABLE_PREFIXES))
{
return ZYAN_TRUE;
}
if ((!match->base_definition->accepts_segment) &&
(match->attributes & ZYDIS_ATTRIB_HAS_SEGMENT))
{
return ZYAN_FALSE;
}
if (match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_LEGACY)
{
return !(match->attributes & ZYDIS_ENCODABLE_PREFIXES_NO_SEGMENTS);
}
const ZydisInstructionDefinitionLEGACY *legacy_def =
(const ZydisInstructionDefinitionLEGACY *)match->base_definition;
if ((!legacy_def->accepts_LOCK) &&
(match->attributes & ZYDIS_ATTRIB_HAS_LOCK))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_REP) &&
(match->attributes & ZYDIS_ATTRIB_HAS_REP))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_REPEREPZ) &&
(match->attributes & ZYDIS_ATTRIB_HAS_REPE))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_REPNEREPNZ) &&
(match->attributes & ZYDIS_ATTRIB_HAS_REPNE))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_BOUND) &&
(match->attributes & ZYDIS_ATTRIB_HAS_BND))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_XACQUIRE) &&
(match->attributes & ZYDIS_ATTRIB_HAS_XACQUIRE))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_XRELEASE) &&
(match->attributes & ZYDIS_ATTRIB_HAS_XRELEASE))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_branch_hints) &&
(match->attributes & (ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN |
ZYDIS_ATTRIB_HAS_BRANCH_TAKEN)))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_NOTRACK) &&
(match->attributes & ZYDIS_ATTRIB_HAS_NOTRACK))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_hle_without_lock) &&
(match->attributes & (ZYDIS_ATTRIB_HAS_XACQUIRE |
ZYDIS_ATTRIB_HAS_XRELEASE)) &&
!(match->attributes & ZYDIS_ATTRIB_HAS_LOCK))
{
return ZYAN_FALSE;
}
return ZYAN_TRUE;
}
/**
* Returns operand mask containing information about operand count and types in a compressed form.
*
* @param request A pointer to `ZydisEncoderRequest` struct.
*
* @return Operand mask.
*/
static ZyanU16 ZydisGetOperandMask(const ZydisEncoderRequest *request)
{
ZyanU16 operand_mask = request->operand_count;
ZyanU8 bit_offset = ZYAN_BITS_TO_REPRESENT(ZYDIS_ENCODER_MAX_OPERANDS);
for (ZyanU8 i = 0; i < request->operand_count; ++i)
{
operand_mask |= (request->operands[i].type - ZYDIS_OPERAND_TYPE_REGISTER) << bit_offset;
bit_offset += ZYAN_BITS_TO_REPRESENT(
ZYDIS_OPERAND_TYPE_MAX_VALUE - ZYDIS_OPERAND_TYPE_REGISTER);
}
return operand_mask;
}
/**
* Handles optimization opportunities indicated by `swappable` field in instruction definition
* structure. See `ZydisEncodableInstruction` for more information.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return True if definition has been swapped, false otherwise.
*/
static ZyanBool ZydisHandleSwappableDefinition(ZydisEncoderInstructionMatch *match)
{
if (!match->definition->swappable)
{
return ZYAN_FALSE;
}
// Special case for ISA-wide unique conflict between two `mov` variants
// mov gpr16_32_64(encoding=opcode), imm(encoding=simm16_32_64,scale_factor=osz)
// mov gpr16_32_64(encoding=modrm_rm), imm(encoding=simm16_32_32,scale_factor=osz)
if (match->request->mnemonic == ZYDIS_MNEMONIC_MOV)
{
const ZyanU8 imm_size = ZydisGetSignedImmSize(match->request->operands[1].imm.s);
if ((match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(match->eosz == 64) &&
(imm_size < 64))
{
return ZYAN_TRUE;
}
}
ZYAN_ASSERT((match->request->operand_count == 2) || (match->request->operand_count == 3));
const ZyanU8 src_index = (match->request->operand_count == 3) ? 2 : 1;
const ZyanI8 dest_id = ZydisRegisterGetId(match->request->operands[0].reg.value);
const ZyanI8 src_id = ZydisRegisterGetId(match->request->operands[src_index].reg.value);
if ((dest_id <= 7) && (src_id > 7))
{
++match->definition;
ZydisGetInstructionDefinition(match->definition->encoding,
match->definition->instruction_reference, &match->base_definition);
match->operands = ZydisGetOperandDefinitions(match->base_definition);
return ZYAN_TRUE;
}
return ZYAN_FALSE;
}
/**
* This function attempts to find a matching instruction definition for provided encoder request.
*
* @param request A pointer to `ZydisEncoderRequest` struct.
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisFindMatchingDefinition(const ZydisEncoderRequest *request,
ZydisEncoderInstructionMatch *match)
{
ZYAN_MEMSET(match, 0, sizeof(ZydisEncoderInstructionMatch));
match->request = request;
match->attributes = request->prefixes;
const ZydisEncodableInstruction *definition = ZYAN_NULL;
const ZyanU8 definition_count = ZydisGetEncodableInstructions(request->mnemonic, &definition);
ZYAN_ASSERT(definition && definition_count);
const ZydisWidthFlag mode_width = ZydisGetMachineModeWidth(request->machine_mode) >> 4;
const ZyanBool is_compat =
(request->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_16) ||
(request->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32);
const ZyanU8 default_asz = ZydisGetAszFromHint(request->address_size_hint);
const ZyanU8 default_osz = ZydisGetOszFromHint(request->operand_size_hint);
const ZyanU16 operand_mask = ZydisGetOperandMask(request);
for (ZyanU8 i = 0; i < definition_count; ++i, ++definition)
{
if (definition->operand_mask != operand_mask)
{
continue;
}
const ZydisInstructionDefinition *base_definition = ZYAN_NULL;
ZydisGetInstructionDefinition(definition->encoding, definition->instruction_reference,
&base_definition);
if (!(definition->modes & mode_width))
{
continue;
}
if ((request->allowed_encodings != ZYDIS_ENCODABLE_ENCODING_DEFAULT) &&
!(ZydisGetEncodableEncoding(definition->encoding) & request->allowed_encodings))
{
continue;
}
if (request->machine_mode == ZYDIS_MACHINE_MODE_REAL_16)
{
if (base_definition->requires_protected_mode)
{
continue;
}
switch (definition->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_XOP:
case ZYDIS_INSTRUCTION_ENCODING_VEX:
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
continue;
default:
break;
}
}
else if ((request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) &&
(definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
{
continue;
}
if (is_compat && base_definition->no_compat_mode)
{
continue;
}
if ((request->branch_type != ZYDIS_BRANCH_TYPE_NONE) &&
(request->branch_type != base_definition->branch_type))
{
continue;
}
if ((base_definition->branch_type == ZYDIS_BRANCH_TYPE_NONE) &&
(request->branch_width != ZYDIS_BRANCH_WIDTH_NONE))
{
continue;
}
match->definition = definition;
match->base_definition = base_definition;
match->operands = ZYAN_NULL;
match->easz = definition->accepts_hint == ZYDIS_SIZE_HINT_ASZ ? default_asz : 0;
match->eosz = definition->accepts_hint == ZYDIS_SIZE_HINT_OSZ ? default_osz : 0;
match->disp_size = match->imm_size = match->cd8_scale = 0;
match->rex_type = ZYDIS_REX_TYPE_UNKNOWN;
match->eosz64_forbidden = ZYAN_FALSE;
match->has_rel_operand = ZYAN_FALSE;
if ((base_definition->operand_size_map != ZYDIS_OPSIZE_MAP_BYTEOP) &&
(match->eosz == 8))
{
continue;
}
if (!ZydisArePrefixesCompatible(match))
{
continue;
}
if (!ZydisIsDefinitionCompatible(match, request))
{
continue;
}
if (ZydisHandleSwappableDefinition(match))
{
if (definition == match->definition)
{
continue;
}
++i;
definition = match->definition;
base_definition = match->base_definition;
}
if (match->easz == 0)
{
if (definition->address_sizes & mode_width)
{
match->easz = (ZyanU8)(mode_width << 4);
}
else if (mode_width == ZYDIS_WIDTH_16)
{
match->easz = 32;
}
else if (mode_width == ZYDIS_WIDTH_32)
{
match->easz = 16;
}
else
{
match->easz = 32;
}
ZYAN_ASSERT(definition->address_sizes & (match->easz >> 4));
}
else if (!(definition->address_sizes & (match->easz >> 4)))
{
continue;
}
if (mode_width == ZYDIS_WIDTH_64)
{
if (base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_DEFAULT64)
{
if (match->eosz == 0)
{
ZYAN_ASSERT(definition->operand_sizes & (ZYDIS_WIDTH_16 | ZYDIS_WIDTH_64));
if (definition->operand_sizes & ZYDIS_WIDTH_64)
{
match->eosz = 64;
}
else
{
match->eosz = 16;
}
}
else if (match->eosz == 32)
{
continue;
}
}
else if (base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_FORCE64)
{
if (match->eosz == 0)
{
match->eosz = 64;
}
else if (match->eosz != 64)
{
continue;
}
}
}
if (match->eosz == 0)
{
const ZydisWidthFlag default_width = (mode_width == ZYDIS_WIDTH_64)
? ZYDIS_WIDTH_32
: mode_width;
if (definition->operand_sizes & default_width)
{
match->eosz = (ZyanU8)(default_width << 4);
}
else if (definition->operand_sizes & ZYDIS_WIDTH_16)
{
match->eosz = 16;
}
else if (definition->operand_sizes & ZYDIS_WIDTH_32)
{
match->eosz = 32;
}
else
{
match->eosz = 64;
}
}
else if (match->eosz64_forbidden && match->eosz == 64)
{
continue;
}
else if (!(definition->operand_sizes & (match->eosz >> 4)))
{
continue;
}
return ZYAN_STATUS_SUCCESS;
}
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
}
/**
* Emits unsigned integer value.
*
* @param data Value to emit.
* @param size Value size in bytes.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitUInt(ZyanU64 data, ZyanU8 size, ZydisEncoderBuffer *buffer)
{
ZYAN_ASSERT(size == 1 || size == 2 || size == 4 || size == 8);
const ZyanUSize new_offset = buffer->offset + size;
if (new_offset > buffer->size)
{
return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
}
// TODO: fix for big-endian systems
// The size variable is not passed on purpose to allow the compiler
// to generate better code with a known size at compile time.
if (size == 1)
{
ZYAN_MEMCPY(buffer->buffer + buffer->offset, &data, 1);
}
else if (size == 2)
{
ZYAN_MEMCPY(buffer->buffer + buffer->offset, &data, 2);
}
else if (size == 4)
{
ZYAN_MEMCPY(buffer->buffer + buffer->offset, &data, 4);
}
else if (size == 8)
{
ZYAN_MEMCPY(buffer->buffer + buffer->offset, &data, 8);
}
else
{
ZYAN_UNREACHABLE;
}
buffer->offset = new_offset;
return ZYAN_STATUS_SUCCESS;
}
/**
* Emits a single byte.
*
* @param byte Value to emit.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitByte(ZyanU8 byte, ZydisEncoderBuffer *buffer)
{
return ZydisEmitUInt(byte, 1, buffer);
}
/**
* Emits legact prefixes.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitLegacyPrefixes(const ZydisEncoderInstruction *instruction,
ZydisEncoderBuffer *buffer)
{
ZyanBool compressed_prefixes = ZYAN_FALSE;
switch (instruction->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_XOP:
case ZYDIS_INSTRUCTION_ENCODING_VEX:
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
compressed_prefixes = ZYAN_TRUE;
break;
default:
break;
}
// Group 1
if (instruction->attributes & ZYDIS_ATTRIB_HAS_LOCK)
{
ZYAN_CHECK(ZydisEmitByte(0xF0, buffer));
}
if (!compressed_prefixes)
{
if (instruction->attributes & (ZYDIS_ATTRIB_HAS_REPNE |
ZYDIS_ATTRIB_HAS_BND |
ZYDIS_ATTRIB_HAS_XACQUIRE))
{
ZYAN_CHECK(ZydisEmitByte(0xF2, buffer));
}
if (instruction->attributes & (ZYDIS_ATTRIB_HAS_REP |
ZYDIS_ATTRIB_HAS_REPE |
ZYDIS_ATTRIB_HAS_XRELEASE))
{
ZYAN_CHECK(ZydisEmitByte(0xF3, buffer));
}
}
// Group 2
if (instruction->attributes & (ZYDIS_ATTRIB_HAS_SEGMENT_CS |
ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN))
{
ZYAN_CHECK(ZydisEmitByte(0x2E, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_SS)
{
ZYAN_CHECK(ZydisEmitByte(0x36, buffer));
}
if (instruction->attributes & (ZYDIS_ATTRIB_HAS_SEGMENT_DS |
ZYDIS_ATTRIB_HAS_BRANCH_TAKEN))
{
ZYAN_CHECK(ZydisEmitByte(0x3E, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_ES)
{
ZYAN_CHECK(ZydisEmitByte(0x26, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_FS)
{
ZYAN_CHECK(ZydisEmitByte(0x64, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_GS)
{
ZYAN_CHECK(ZydisEmitByte(0x65, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_NOTRACK)
{
ZYAN_CHECK(ZydisEmitByte(0x3E, buffer));
}
// Group 3
if (!compressed_prefixes)
{
if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
{
ZYAN_CHECK(ZydisEmitByte(0x66, buffer));
}
}
// Group 4
if (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE)
{
ZYAN_CHECK(ZydisEmitByte(0x67, buffer));
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes low nibble of `REX` prefix.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param high_r A pointer to `ZyanBool` variable that will be set to true when the
* highest `ModR/M.reg` bit cannot be encoded using `REX` prefix.
*
* @return A zyan status code.
*/
static ZyanU8 ZydisEncodeRexLowNibble(const ZydisEncoderInstruction *instruction, ZyanBool *high_r)
{
if (high_r)
{
*high_r = ZYAN_FALSE;
}
ZyanU8 rex = 0;
if ((instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM) &&
(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB))
{
if (instruction->base & 0x08)
{
rex |= 1;
}
if (instruction->index & 0x08)
{
rex |= 2;
}
if (instruction->reg & 0x08)
{
rex |= 4;
}
if (high_r && (instruction->reg & 0x10))
{
*high_r = ZYAN_TRUE;
}
}
else if (instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM)
{
if (instruction->rm & 0x08)
{
rex |= 1;
}
if (instruction->rm & 0x10)
{
rex |= 2;
}
if (instruction->reg & 0x08)
{
rex |= 4;
}
if (high_r && (instruction->reg & 0x10))
{
*high_r = ZYAN_TRUE;
}
}
else
{
if (instruction->rm & 0x08)
{
rex |= 1;
}
}
if (instruction->rex_w)
{
rex |= 8;
}
return rex;
}
/**
* Emits `REX` prefix.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitRex(const ZydisEncoderInstruction *instruction,
ZydisEncoderBuffer *buffer)
{
const ZyanU8 rex = ZydisEncodeRexLowNibble(instruction, ZYAN_NULL);
if (rex || (instruction->attributes & ZYDIS_ATTRIB_HAS_REX))
{
ZYAN_CHECK(ZydisEmitByte(0x40 | rex, buffer));
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes common parts of `VEX` prefix.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param mmmmm A pointer to `ZyanU8` variable that will receive `VEX.mmmmm`
* @param pp A pointer to `ZyanU8` variable that will receive `VEX.pp`
* @param vvvv A pointer to `ZyanU8` variable that will receive `VEX.vvvv`
* @param rex A pointer to `ZyanU8` variable that will receive 'REX`
* @param high_r A pointer to `ZyanBool` variable that will be set to true when the
* highest `ModR/M.reg` bit cannot be encoded using `REX` prefix.
*/
static void ZydisEncodeVexCommons(ZydisEncoderInstruction *instruction, ZyanU8 *mmmmm, ZyanU8 *pp,
ZyanU8 *vvvv, ZyanU8 *rex, ZyanBool *high_r)
{
switch (instruction->opcode_map)
{
case ZYDIS_OPCODE_MAP_DEFAULT:
case ZYDIS_OPCODE_MAP_0F:
case ZYDIS_OPCODE_MAP_0F38:
case ZYDIS_OPCODE_MAP_0F3A:
case ZYDIS_OPCODE_MAP_MAP5:
case ZYDIS_OPCODE_MAP_MAP6:
*mmmmm = (ZyanU8)instruction->opcode_map;
break;
case ZYDIS_OPCODE_MAP_XOP8:
case ZYDIS_OPCODE_MAP_XOP9:
case ZYDIS_OPCODE_MAP_XOPA:
*mmmmm = 8 + ((ZyanU8)instruction->opcode_map - ZYDIS_OPCODE_MAP_XOP8);
break;
default:
ZYAN_UNREACHABLE;
}
instruction->opcode_map = ZYDIS_OPCODE_MAP_DEFAULT;
*pp = 0;
if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
{
*pp = 1;
}
else if (instruction->attributes & ZYDIS_ATTRIB_HAS_REP)
{
*pp = 2;
}
else if (instruction->attributes & ZYDIS_ATTRIB_HAS_REPNE)
{
*pp = 3;
}
*vvvv = ~instruction->vvvv;
*rex = ZydisEncodeRexLowNibble(instruction, high_r);
}
/**
* Emits `XOP` prefix.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitXop(ZydisEncoderInstruction *instruction, ZydisEncoderBuffer *buffer)
{
ZyanU8 mmmmm, pp, vvvv, rex;
ZydisEncodeVexCommons(instruction, &mmmmm, &pp, &vvvv, &rex, ZYAN_NULL);
ZYAN_ASSERT(instruction->vector_length <= 1);
const ZyanU8 b1 = (((~rex) & 0x07) << 5) | mmmmm;
const ZyanU8 b2 = ((rex & 0x08) << 4) | ((vvvv & 0xF) << 3) | (instruction->vector_length << 2) | pp;
ZYAN_CHECK(ZydisEmitByte(0x8F, buffer));
ZYAN_CHECK(ZydisEmitByte(b1, buffer));
ZYAN_CHECK(ZydisEmitByte(b2, buffer));
return ZYAN_STATUS_SUCCESS;
}
/**
* Emits `VEX` prefix.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitVex(ZydisEncoderInstruction *instruction, ZydisEncoderBuffer *buffer)
{
ZyanU8 mmmmm, pp, vvvv, rex;
ZydisEncodeVexCommons(instruction, &mmmmm, &pp, &vvvv, &rex, ZYAN_NULL);
ZYAN_ASSERT(instruction->vector_length <= 1);
if (mmmmm != 1 || (rex & 0x0B))
{
const ZyanU8 b1 = (((~rex) & 0x07) << 5) | mmmmm;
const ZyanU8 b2 = ((rex & 0x08) << 4) |
((vvvv & 0xF) << 3) |
(instruction->vector_length << 2) |
pp;
ZYAN_CHECK(ZydisEmitByte(0xC4, buffer));
ZYAN_CHECK(ZydisEmitByte(b1, buffer));
ZYAN_CHECK(ZydisEmitByte(b2, buffer));
}
else
{
const ZyanU8 b1 = (((~rex) & 0x04) << 5) |
((vvvv & 0xF) << 3) |
(instruction->vector_length << 2) |
pp;
ZYAN_CHECK(ZydisEmitByte(0xC5, buffer));
ZYAN_CHECK(ZydisEmitByte(b1, buffer));
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes common parts of `EVEX` prefix.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param p0 A pointer to `ZyanU8` variable that will receive 2nd byte of `EVEX` prefix.
* @param p1 A pointer to `ZyanU8` variable that will receive 3rd byte of `EVEX` prefix.
* @param vvvvv A pointer to `ZyanU8` variable that will receive `EVEX.vvvvv`.
*/
static void ZydisEncodeEvexCommons(ZydisEncoderInstruction *instruction, ZyanU8 *p0, ZyanU8 *p1,
ZyanU8 *vvvvv)
{
ZyanBool high_r;
ZyanU8 mmmmm, pp, rex;
ZydisEncodeVexCommons(instruction, &mmmmm, &pp, vvvvv, &rex, &high_r);
*p0 = (((~rex) & 0x07) << 5) | mmmmm;
if (!high_r)
{
*p0 |= 0x10;
}
*p1 = ((rex & 0x08) << 4) | ((*vvvvv & 0x0F) << 3) | 0x04 | pp;
}
/**
* Emits `EVEX` prefix.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitEvex(ZydisEncoderInstruction *instruction, ZydisEncoderBuffer *buffer)
{
ZyanU8 p0, p1, vvvvv;
ZydisEncodeEvexCommons(instruction, &p0, &p1, &vvvvv);
ZyanU8 p2 = (instruction->vector_length << 5) | ((vvvvv & 0x10) >> 1) | instruction->mask;
if (instruction->zeroing)
{
p2 |= 0x80;
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX_B)
{
p2 |= 0x10;
}
if (instruction->index & 0x10)
{
p2 &= 0xF7;
}
ZYAN_CHECK(ZydisEmitByte(0x62, buffer));
ZYAN_CHECK(ZydisEmitByte(p0, buffer));
ZYAN_CHECK(ZydisEmitByte(p1, buffer));
ZYAN_CHECK(ZydisEmitByte(p2, buffer));
return ZYAN_STATUS_SUCCESS;
}
/**
* Emits `MVEX` prefix.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitMvex(ZydisEncoderInstruction *instruction, ZydisEncoderBuffer *buffer)
{
ZyanU8 p0, p1, vvvvv;
ZydisEncodeEvexCommons(instruction, &p0, &p1, &vvvvv);
ZyanU8 p2 = (instruction->sss << 4) | ((vvvvv & 0x10) >> 1) | instruction->mask;
if (instruction->eviction_hint)
{
p2 |= 0x80;
}
if (instruction->index & 0x10)
{
p2 &= 0xF7;
}
ZYAN_CHECK(ZydisEmitByte(0x62, buffer));
ZYAN_CHECK(ZydisEmitByte(p0, buffer));
ZYAN_CHECK(ZydisEmitByte(p1 & 0xFB, buffer));
ZYAN_CHECK(ZydisEmitByte(p2, buffer));
return ZYAN_STATUS_SUCCESS;
}
/**
* Emits instruction as stream of bytes.
*
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
* @param buffer A pointer to `ZydisEncoderBuffer` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEmitInstruction(ZydisEncoderInstruction *instruction,
ZydisEncoderBuffer *buffer)
{
ZYAN_CHECK(ZydisEmitLegacyPrefixes(instruction, buffer));
switch (instruction->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
ZYAN_CHECK(ZydisEmitRex(instruction, buffer));
break;
case ZYDIS_INSTRUCTION_ENCODING_XOP:
ZYAN_CHECK(ZydisEmitXop(instruction, buffer));
break;
case ZYDIS_INSTRUCTION_ENCODING_VEX:
ZYAN_CHECK(ZydisEmitVex(instruction, buffer));
break;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
ZYAN_CHECK(ZydisEmitEvex(instruction, buffer));
break;
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
ZYAN_CHECK(ZydisEmitMvex(instruction, buffer));
break;
default:
ZYAN_UNREACHABLE;
}
switch (instruction->opcode_map)
{
case ZYDIS_OPCODE_MAP_DEFAULT:
break;
case ZYDIS_OPCODE_MAP_0F:
ZYAN_CHECK(ZydisEmitByte(0x0F, buffer));
break;
case ZYDIS_OPCODE_MAP_0F38:
ZYAN_CHECK(ZydisEmitByte(0x0F, buffer));
ZYAN_CHECK(ZydisEmitByte(0x38, buffer));
break;
case ZYDIS_OPCODE_MAP_0F3A:
ZYAN_CHECK(ZydisEmitByte(0x0F, buffer));
ZYAN_CHECK(ZydisEmitByte(0x3A, buffer));
break;
case ZYDIS_OPCODE_MAP_0F0F:
ZYAN_CHECK(ZydisEmitByte(0x0F, buffer));
ZYAN_CHECK(ZydisEmitByte(0x0F, buffer));
break;
default:
ZYAN_UNREACHABLE;
}
if (instruction->encoding != ZYDIS_INSTRUCTION_ENCODING_3DNOW)
{
ZYAN_CHECK(ZydisEmitByte(instruction->opcode, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM)
{
const ZyanU8 modrm = (instruction->mod << 6) |
((instruction->reg & 7) << 3) |
(instruction->rm & 7);
ZYAN_CHECK(ZydisEmitByte(modrm, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SIB)
{
const ZyanU8 sib = (instruction->scale << 6) |
((instruction->index & 7) << 3) |
(instruction->base & 7);
ZYAN_CHECK(ZydisEmitByte(sib, buffer));
}
if (instruction->disp_size)
{
ZYAN_CHECK(ZydisEmitUInt(instruction->disp, instruction->disp_size / 8, buffer));
}
if (instruction->imm_size)
{
ZYAN_CHECK(ZydisEmitUInt(instruction->imm, instruction->imm_size / 8, buffer));
}
if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW)
{
ZYAN_CHECK(ZydisEmitByte(instruction->opcode, buffer));
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes register operand as fields inside `ZydisEncoderInstruction` structure.
*
* @param user_op Validated operand definition from `ZydisEncoderRequest` structure.
* @param def_op Decoder's operand definition from instruction definition.
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
*/
void ZydisBuildRegisterOperand(const ZydisEncoderOperand *user_op,
const ZydisOperandDefinition *def_op, ZydisEncoderInstruction *instruction)
{
if (def_op->type == ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_REG)
{
return;
}
ZyanU8 reg_id = 0;
if (ZydisRegisterGetClass(user_op->reg.value) != ZYDIS_REGCLASS_GPR8)
{
reg_id = (ZyanU8)ZydisRegisterGetId(user_op->reg.value);
}
else
{
static const ZyanU8 reg8_lookup[] = {
0, 1, 2, 3, // AL, CL, DL, BL
4, 5, 6, 7, // AH, CH, DH, BH
4, 5, 6, 7, // SPL, BPL, SIL, DIL
8, 9, 10, 11, 12, 13, 14, 15, // R8B-R15B
};
ZYAN_ASSERT(
((ZyanUSize)user_op->reg.value - ZYDIS_REGISTER_AL) < ZYAN_ARRAY_LENGTH(reg8_lookup));
reg_id = reg8_lookup[user_op->reg.value - ZYDIS_REGISTER_AL];
if (user_op->reg.value >= ZYDIS_REGISTER_SPL && user_op->reg.value <= ZYDIS_REGISTER_DIL)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_REX;
}
}
switch (def_op->op.encoding)
{
case ZYDIS_OPERAND_ENCODING_MODRM_REG:
instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
instruction->reg = reg_id;
break;
case ZYDIS_OPERAND_ENCODING_MODRM_RM:
instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
instruction->rm = reg_id;
break;
case ZYDIS_OPERAND_ENCODING_OPCODE:
instruction->opcode += reg_id & 7;
instruction->rm = reg_id;
break;
case ZYDIS_OPERAND_ENCODING_NDSNDD:
instruction->vvvv = reg_id;
break;
case ZYDIS_OPERAND_ENCODING_IS4:
instruction->imm_size = 8;
instruction->imm = reg_id << 4;
break;
case ZYDIS_OPERAND_ENCODING_MASK:
instruction->mask = reg_id;
break;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Encodes memory operand as fields inside `ZydisEncoderInstruction` structure.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param user_op Decoder's operand definition from instruction definition.
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
*/
static void ZydisBuildMemoryOperand(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op, ZydisEncoderInstruction *instruction)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
instruction->disp = (ZyanU64)user_op->mem.displacement;
if (match->easz == 16)
{
const ZyanI8 rm = ZydisGetRm16(user_op->mem.base, user_op->mem.index);
if (rm != -1)
{
instruction->rm = (ZyanU8)rm;
instruction->disp_size = match->disp_size;
switch (instruction->disp_size)
{
case 0:
if (rm == 6)
{
instruction->disp_size = 8;
instruction->mod = 1;
}
break;
case 8:
instruction->mod = 1;
break;
case 16:
instruction->mod = 2;
break;
default:
ZYAN_UNREACHABLE;
}
}
else
{
instruction->rm = 6;
instruction->disp_size = 16;
}
return;
}
if (user_op->mem.index == ZYDIS_REGISTER_NONE)
{
if (user_op->mem.base == ZYDIS_REGISTER_NONE)
{
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
instruction->rm = 4;
instruction->attributes |= ZYDIS_ATTRIB_HAS_SIB;
instruction->base = 5;
instruction->index = 4;
}
else
{
instruction->rm = 5;
}
instruction->disp_size = 32;
return;
}
else if ((user_op->mem.base == ZYDIS_REGISTER_RIP) ||
(user_op->mem.base == ZYDIS_REGISTER_EIP))
{
instruction->rm = 5;
instruction->disp_size = 32;
return;
}
}
const ZyanU8 reg_base_id = (ZyanU8)ZydisRegisterGetId(user_op->mem.base);
const ZyanU8 reg_index_id = (ZyanU8)ZydisRegisterGetId(user_op->mem.index);
instruction->disp_size = match->disp_size;
switch (instruction->disp_size)
{
case 0:
if (reg_base_id == 5 || reg_base_id == 13)
{
instruction->disp_size = 8;
instruction->disp = 0;
instruction->mod = 1;
}
break;
case 8:
instruction->mod = 1;
break;
case 16:
instruction->disp_size = 32;
ZYAN_FALLTHROUGH;
case 32:
instruction->mod = 2;
break;
default:
ZYAN_UNREACHABLE;
}
if ((user_op->mem.index == ZYDIS_REGISTER_NONE) &&
(reg_base_id != 4) &&
(reg_base_id != 12) &&
((match->definition->modrm & 7) != 4))
{
instruction->rm = reg_base_id;
return;
}
instruction->rm = 4;
instruction->attributes |= ZYDIS_ATTRIB_HAS_SIB;
if (reg_base_id != 0xFF)
{
instruction->base = reg_base_id;
}
else
{
instruction->base = 5;
instruction->mod = 0;
instruction->disp_size = 32;
}
if (reg_index_id != 0xFF)
{
instruction->index = reg_index_id;
}
else
{
instruction->index = 4;
}
switch (user_op->mem.scale)
{
case 0:
case 1:
break;
case 2:
instruction->scale = 1;
break;
case 4:
instruction->scale = 2;
break;
case 8:
instruction->scale = 3;
break;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Encodes instruction as emittable `ZydisEncoderInstruction` struct.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param instruction A pointer to `ZydisEncoderInstruction` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisBuildInstruction(ZydisEncoderInstructionMatch *match,
ZydisEncoderInstruction *instruction)
{
ZYAN_MEMSET(instruction, 0, sizeof(ZydisEncoderInstruction));
instruction->attributes = match->attributes;
instruction->encoding = match->definition->encoding;
instruction->opcode_map = match->definition->opcode_map;
instruction->opcode = match->definition->opcode;
instruction->rex_w = match->definition->rex_w;
instruction->mod = (match->definition->modrm >> 6) & 3;
instruction->reg = (match->definition->modrm >> 3) & 7;
instruction->rm = match->definition->modrm & 7;
if (match->definition->modrm)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
}
switch (match->definition->vector_length)
{
case ZYDIS_VECTOR_LENGTH_INVALID:
case ZYDIS_VECTOR_LENGTH_128:
instruction->vector_length = 0;
break;
case ZYDIS_VECTOR_LENGTH_256:
instruction->vector_length = 1;
break;
case ZYDIS_VECTOR_LENGTH_512:
instruction->vector_length = 2;
break;
default:
ZYAN_UNREACHABLE;
}
if (match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX)
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
if (evex_def->mask_override != ZYDIS_MASK_OVERRIDE_ZEROING)
{
instruction->zeroing = match->request->evex.zeroing_mask;
}
if ((match->request->evex.sae) ||
(match->request->evex.broadcast != ZYDIS_BROADCAST_MODE_INVALID))
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_EVEX_B;
}
if (match->request->evex.rounding != ZYDIS_ROUNDING_MODE_INVALID)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_EVEX_B;
switch (match->request->evex.rounding)
{
case ZYDIS_ROUNDING_MODE_RN:
instruction->vector_length = 0;
break;
case ZYDIS_ROUNDING_MODE_RD:
instruction->vector_length = 1;
break;
case ZYDIS_ROUNDING_MODE_RU:
instruction->vector_length = 2;
break;
case ZYDIS_ROUNDING_MODE_RZ:
instruction->vector_length = 3;
break;
default:
ZYAN_UNREACHABLE;
}
}
}
else if (match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX)
{
instruction->sss |= ZydisEncodeMvexBroadcastMode(match->request->mvex.broadcast);
instruction->sss |= ZydisEncodeMvexConversionMode(match->request->mvex.conversion);
switch (match->request->mvex.rounding)
{
case ZYDIS_ROUNDING_MODE_INVALID:
break;
case ZYDIS_ROUNDING_MODE_RN:
case ZYDIS_ROUNDING_MODE_RD:
case ZYDIS_ROUNDING_MODE_RU:
case ZYDIS_ROUNDING_MODE_RZ:
instruction->sss |= match->request->mvex.rounding - ZYDIS_ROUNDING_MODE_RN;
break;
default:
ZYAN_UNREACHABLE;
}
switch (match->request->mvex.swizzle)
{
case ZYDIS_SWIZZLE_MODE_INVALID:
break;
case ZYDIS_SWIZZLE_MODE_DCBA:
case ZYDIS_SWIZZLE_MODE_CDAB:
case ZYDIS_SWIZZLE_MODE_BADC:
case ZYDIS_SWIZZLE_MODE_DACB:
case ZYDIS_SWIZZLE_MODE_AAAA:
case ZYDIS_SWIZZLE_MODE_BBBB:
case ZYDIS_SWIZZLE_MODE_CCCC:
case ZYDIS_SWIZZLE_MODE_DDDD:
instruction->sss |= match->request->mvex.swizzle - ZYDIS_SWIZZLE_MODE_DCBA;
break;
default:
ZYAN_UNREACHABLE;
}
if ((match->request->mvex.sae) ||
(match->request->mvex.eviction_hint) ||
(match->request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID))
{
instruction->eviction_hint = ZYAN_TRUE;
}
if (match->request->mvex.sae)
{
instruction->sss |= 4;
}
// Following instructions violate general `MVEX.EH` handling rules. In all other cases this
// bit is used either as eviction hint (memory operands present) or to encode MVEX-specific
// functionality (register forms). Instructions listed below use `MVEX.EH` to identify
// different instructions with memory operands and don't treat it as eviction hint.
switch (match->request->mnemonic)
{
case ZYDIS_MNEMONIC_VMOVNRAPD:
case ZYDIS_MNEMONIC_VMOVNRAPS:
instruction->eviction_hint = ZYAN_FALSE;
break;
case ZYDIS_MNEMONIC_VMOVNRNGOAPD:
case ZYDIS_MNEMONIC_VMOVNRNGOAPS:
instruction->eviction_hint = ZYAN_TRUE;
break;
default:
break;
}
}
switch (match->definition->mandatory_prefix)
{
case ZYDIS_MANDATORY_PREFIX_NONE:
break;
case ZYDIS_MANDATORY_PREFIX_66:
instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
break;
case ZYDIS_MANDATORY_PREFIX_F2:
instruction->attributes |= ZYDIS_ATTRIB_HAS_REPNE;
break;
case ZYDIS_MANDATORY_PREFIX_F3:
instruction->attributes |= ZYDIS_ATTRIB_HAS_REP;
break;
default:
ZYAN_UNREACHABLE;
}
const ZyanU8 mode_width = ZydisGetMachineModeWidth(match->request->machine_mode);
if (match->easz != mode_width)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
}
if ((match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(match->base_definition->operand_size_map != ZYDIS_OPSIZE_MAP_FORCE64))
{
switch (match->eosz)
{
case 16:
instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
break;
case 32:
break;
case 64:
instruction->rex_w =
match->base_definition->operand_size_map != ZYDIS_OPSIZE_MAP_DEFAULT64;
break;
default:
ZYAN_UNREACHABLE;
}
}
else
{
if (match->eosz != mode_width)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
}
}
for (ZyanU8 i = 0; i < match->request->operand_count; ++i)
{
const ZydisEncoderOperand *user_op = &match->request->operands[i];
const ZydisOperandDefinition *def_op = &match->operands[i];
switch (user_op->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
ZydisBuildRegisterOperand(user_op, def_op, instruction);
break;
case ZYDIS_OPERAND_TYPE_MEMORY:
if (def_op->type != ZYDIS_SEMANTIC_OPTYPE_MOFFS)
{
ZydisBuildMemoryOperand(match, user_op, instruction);
if ((match->cd8_scale) &&
(instruction->disp_size == 8))
{
instruction->disp >>= match->cd8_scale;
}
}
else
{
instruction->disp_size = match->disp_size;
instruction->disp = (ZyanU64)user_op->mem.displacement;
}
break;
case ZYDIS_OPERAND_TYPE_POINTER:
instruction->disp_size = match->disp_size;
instruction->disp = user_op->ptr.offset;
instruction->imm_size = match->imm_size;
instruction->imm = user_op->ptr.segment;
break;
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
if (def_op->type == ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1)
{
break;
}
if (def_op->op.encoding != ZYDIS_OPERAND_ENCODING_IS4)
{
if (instruction->imm_size)
{
ZYAN_ASSERT(instruction->disp_size == 0);
instruction->disp_size = match->disp_size;
instruction->disp = instruction->imm;
}
instruction->imm_size = match->imm_size;
instruction->imm = user_op->imm.u;
}
else
{
ZYAN_ASSERT(instruction->imm_size == 8);
instruction->imm |= user_op->imm.u;
}
break;
default:
ZYAN_UNREACHABLE;
}
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Performs a set of sanity checks that must be satisfied for every valid encoder request.
*
* @param request A pointer to `ZydisEncoderRequest` struct.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEncoderCheckRequestSanity(const ZydisEncoderRequest *request)
{
if (((ZyanUSize)request->machine_mode > ZYDIS_MACHINE_MODE_MAX_VALUE) ||
((ZyanUSize)request->allowed_encodings > ZYDIS_ENCODABLE_ENCODING_MAX_VALUE) ||
((ZyanUSize)request->mnemonic > ZYDIS_MNEMONIC_MAX_VALUE) ||
((ZyanUSize)request->branch_type > ZYDIS_BRANCH_TYPE_MAX_VALUE) ||
((ZyanUSize)request->branch_width > ZYDIS_BRANCH_WIDTH_MAX_VALUE) ||
((ZyanUSize)request->address_size_hint > ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE) ||
((ZyanUSize)request->operand_size_hint > ZYDIS_OPERAND_SIZE_HINT_MAX_VALUE) ||
((ZyanUSize)request->evex.broadcast > ZYDIS_BROADCAST_MODE_MAX_VALUE) ||
((ZyanUSize)request->evex.rounding > ZYDIS_ROUNDING_MODE_MAX_VALUE) ||
((ZyanUSize)request->mvex.broadcast > ZYDIS_BROADCAST_MODE_MAX_VALUE) ||
((ZyanUSize)request->mvex.conversion > ZYDIS_CONVERSION_MODE_MAX_VALUE) ||
((ZyanUSize)request->mvex.rounding > ZYDIS_ROUNDING_MODE_MAX_VALUE) ||
((ZyanUSize)request->mvex.swizzle > ZYDIS_SWIZZLE_MODE_MAX_VALUE) ||
(request->operand_count > ZYDIS_ENCODER_MAX_OPERANDS) ||
(request->mnemonic == ZYDIS_MNEMONIC_INVALID) ||
(request->prefixes & ~ZYDIS_ENCODABLE_PREFIXES))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT)
{
if ((request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(request->prefixes & ZYDIS_LEGACY_SEGMENTS))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZyanU8 seg_override_count = 0;
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_CS)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_SS)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_DS)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_ES)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_FS)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_GS)
{
++seg_override_count;
}
if (seg_override_count != 1)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
ZyanU8 rep_family_count = 0;
if (request->prefixes & ZYDIS_ATTRIB_HAS_REP)
{
++rep_family_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_REPE)
{
++rep_family_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_REPNE)
{
++rep_family_count;
}
if (rep_family_count > 1)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if ((request->prefixes & ZYDIS_ATTRIB_HAS_XACQUIRE) &&
(request->prefixes & ZYDIS_ATTRIB_HAS_XRELEASE))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if ((request->prefixes & ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN) &&
(request->prefixes & ZYDIS_ATTRIB_HAS_BRANCH_TAKEN))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if ((request->prefixes & ZYDIS_ATTRIB_HAS_NOTRACK) &&
(request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
static const ZyanBool branch_lookup
[ZYDIS_BRANCH_WIDTH_MAX_VALUE + 1][ZYDIS_BRANCH_TYPE_MAX_VALUE + 1] =
{
/* NONE */ { ZYAN_TRUE, ZYAN_TRUE, ZYAN_TRUE, ZYAN_TRUE },
/* 8 */ { ZYAN_TRUE, ZYAN_TRUE, ZYAN_FALSE, ZYAN_FALSE },
/* 16 */ { ZYAN_TRUE, ZYAN_FALSE, ZYAN_TRUE, ZYAN_TRUE },
/* 32 */ { ZYAN_TRUE, ZYAN_FALSE, ZYAN_TRUE, ZYAN_TRUE },
/* 64 */ { ZYAN_TRUE, ZYAN_FALSE, ZYAN_TRUE, ZYAN_TRUE },
};
if (!branch_lookup[request->branch_width][request->branch_type])
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if (request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
if (request->address_size_hint == ZYDIS_ADDRESS_SIZE_HINT_16)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
else
{
if ((request->branch_width == ZYDIS_BRANCH_WIDTH_64) ||
(request->address_size_hint == ZYDIS_ADDRESS_SIZE_HINT_64) ||
(request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_64))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
for (ZyanU8 i = 0; i < request->operand_count; ++i)
{
const ZydisEncoderOperand *op = &request->operands[i];
if ((op->type == ZYDIS_OPERAND_TYPE_UNUSED) ||
((ZyanUSize)op->type > ZYDIS_OPERAND_TYPE_MAX_VALUE))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (op->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
if (op->reg.value > ZYDIS_REGISTER_MAX_VALUE)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
break;
case ZYDIS_OPERAND_TYPE_MEMORY:
if (((ZyanUSize)op->mem.base > ZYDIS_REGISTER_MAX_VALUE) ||
((ZyanUSize)op->mem.index > ZYDIS_REGISTER_MAX_VALUE) ||
!ZydisIsScaleValid(op->mem.scale))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
break;
case ZYDIS_OPERAND_TYPE_POINTER:
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes instruction with semantics specified in encoder request structure.
*
* @param request A pointer to the `ZydisEncoderRequest` struct. Must be validated before
* calling this function.
* @param buffer A pointer to the output buffer receiving encoded instruction.
* @param length A pointer to the variable containing length of the output buffer. Upon
* successful return this variable receives length of the encoded instruction.
* @param instruction Internal state of the encoder.
*
* @return A zyan status code.
*/
static ZyanStatus ZydisEncoderEncodeInstructionInternal(const ZydisEncoderRequest *request,
void *buffer, ZyanUSize *length, ZydisEncoderInstruction *instruction)
{
ZydisEncoderInstructionMatch match;
ZYAN_CHECK(ZydisFindMatchingDefinition(request, &match));
ZydisEncoderBuffer output;
output.buffer = (ZyanU8 *)buffer;
output.size = *length;
output.offset = 0;
ZYAN_CHECK(ZydisBuildInstruction(&match, instruction));
ZYAN_CHECK(ZydisEmitInstruction(instruction, &output));
*length = output.offset;
return ZYAN_STATUS_SUCCESS;
}
/* ============================================================================================== */
/* Exported functions */
/* ============================================================================================== */
ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstruction(const ZydisEncoderRequest *request,
void *buffer, ZyanUSize *length)
{
if (!request || !buffer || !length)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZYAN_CHECK(ZydisEncoderCheckRequestSanity(request));
ZydisEncoderInstruction instruction;
return ZydisEncoderEncodeInstructionInternal(request, buffer, length, &instruction);
}
ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstructionAbsolute(ZydisEncoderRequest *request,
void *buffer, ZyanUSize *length, ZyanU64 runtime_address)
{
if (!request || !buffer || !length)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZYAN_CHECK(ZydisEncoderCheckRequestSanity(request));
const ZydisEncoderRelInfo *rel_info = ZydisGetRelInfo(request->mnemonic);
ZydisEncoderOperand *op_rip_rel = ZYAN_NULL;
ZyanBool adjusted_rel = ZYAN_FALSE;
ZyanU64 absolute_address = 0;
ZyanU8 mode_index = ZydisGetMachineModeWidth(request->machine_mode) >> 5;
for (ZyanU8 i = 0; i < request->operand_count; ++i)
{
ZydisEncoderOperand *op = &request->operands[i];
if ((op->type == ZYDIS_OPERAND_TYPE_IMMEDIATE) && rel_info)
{
if (adjusted_rel)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (rel_info->accepts_scaling_hints)
{
case ZYDIS_SIZE_HINT_NONE:
case ZYDIS_SIZE_HINT_OSZ:
{
static const ZyanI8 asz_priority[3][3] =
{
{ 0, 1, 2 },
{ 0, 2, 1 },
{ 0, 2, -1 },
};
static const ZyanI8 osz_priority[3][3] =
{
{ 0, 1, 2 },
{ 0, 2, 1 },
{ 0, 2, 1 },
};
ZyanI8 forced_priority_row[3] = { -1, -1, -1 };
ZyanI8 *priority_row = ZYAN_NULL;
ZyanU8 extra_length = 0;
ZyanU8 start_offset = 0;
if (rel_info->accepts_scaling_hints == ZYDIS_SIZE_HINT_NONE)
{
if ((request->branch_type == ZYDIS_BRANCH_TYPE_FAR) ||
(request->branch_width == ZYDIS_BRANCH_WIDTH_64))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if ((rel_info->accepts_branch_hints) &&
(request->prefixes & (ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN |
ZYDIS_ATTRIB_HAS_BRANCH_TAKEN)))
{
extra_length = 1;
}
if (request->branch_width == ZYDIS_BRANCH_WIDTH_NONE)
{
if (request->branch_type == ZYDIS_BRANCH_TYPE_NEAR)
{
start_offset = 1;
}
priority_row = (ZyanI8 *)&asz_priority[mode_index];
}
else
{
forced_priority_row[0] = (ZyanI8)(request->branch_width - 1);
priority_row = (ZyanI8 *)&forced_priority_row;
}
}
else
{
if (request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_NONE)
{
priority_row = (ZyanI8 *)&osz_priority[mode_index];
}
else
{
if (request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_64)
{
extra_length = 1;
forced_priority_row[0] = 2;
}
else
{
forced_priority_row[0] = (ZyanI8)(request->operand_size_hint - 1);
}
priority_row = (ZyanI8 *)&forced_priority_row;
}
}
ZYAN_ASSERT(ZYAN_ARRAY_LENGTH(asz_priority[0]) ==
ZYAN_ARRAY_LENGTH(osz_priority[0]));
for (ZyanU8 j = start_offset; j < ZYAN_ARRAY_LENGTH(asz_priority[0]); ++j)
{
ZyanI8 size_index = priority_row[j];
if (size_index < 0)
{
break;
}
ZyanU8 base_size = rel_info->size[mode_index][size_index];
if (base_size == 0)
{
continue;
}
ZyanU8 predicted_size = base_size + extra_length;
if (runtime_address > ZYAN_UINT64_MAX - predicted_size + 1)
{
continue;
}
ZyanI64 rel = (ZyanI64)(op->imm.u - (runtime_address + predicted_size));
ZyanU8 rel_size = ZydisGetSignedImmSize(rel);
if (rel_size > (8 << size_index))
{
continue;
}
op->imm.s = rel;
adjusted_rel = ZYAN_TRUE;
break;
}
break;
}
case ZYDIS_SIZE_HINT_ASZ:
{
static const ZyanI8 asz_prefix_lookup[3][ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE + 1] =
{
{ 0, 0, 1, -1 },
{ 0, 1, 0, -1 },
{ 0, -1, 1, 0 },
};
ZyanI8 extra_length = asz_prefix_lookup[mode_index][request->address_size_hint];
if (extra_length < 0)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZyanU8 asz_index = (request->address_size_hint == ZYDIS_ADDRESS_SIZE_HINT_NONE)
? mode_index
: ZydisGetAszFromHint(request->address_size_hint) >> 5;
ZYAN_ASSERT((rel_info->size[asz_index][0] != 0) &&
(rel_info->size[asz_index][1] == 0) &&
(rel_info->size[asz_index][2] == 0) &&
!rel_info->accepts_branch_hints);
ZyanU8 predicted_size = rel_info->size[asz_index][0] + extra_length;
if (runtime_address > ZYAN_UINT64_MAX - predicted_size + 1)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZyanI64 rel = (ZyanI64)(op->imm.u - (runtime_address + predicted_size));
ZyanU8 rel_size = ZydisGetSignedImmSize(rel);
if (rel_size > 8)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
op->imm.s = rel;
adjusted_rel = ZYAN_TRUE;
break;
}
default:
ZYAN_UNREACHABLE;
}
if (!adjusted_rel)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
else if ((op->type == ZYDIS_OPERAND_TYPE_MEMORY) &&
((op->mem.base == ZYDIS_REGISTER_EIP) ||
(op->mem.base == ZYDIS_REGISTER_RIP)))
{
if (op_rip_rel)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
absolute_address = op->mem.displacement;
op->mem.displacement = 0;
op_rip_rel = op;
}
}
ZydisEncoderInstruction instruction;
ZYAN_CHECK(ZydisEncoderEncodeInstructionInternal(request, buffer, length, &instruction));
if (op_rip_rel)
{
ZyanUSize instruction_size = *length;
if (runtime_address > ZYAN_UINT64_MAX - instruction_size + 1)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZyanI64 rip_rel = (ZyanI64)(absolute_address - (runtime_address + instruction_size));
if (ZydisGetSignedImmSize(rip_rel) > 32)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZYAN_ASSERT(instruction.disp_size != 0);
ZyanU8 disp_offset = (instruction.disp_size >> 3) + (instruction.imm_size >> 3);
ZYAN_ASSERT(instruction_size > disp_offset);
ZYAN_MEMCPY((ZyanU8 *)buffer + instruction_size - disp_offset, &rip_rel, sizeof(ZyanI32));
op_rip_rel->mem.displacement = rip_rel;
}
return ZYAN_STATUS_SUCCESS;
}
ZYDIS_EXPORT ZyanStatus ZydisEncoderDecodedInstructionToEncoderRequest(
const ZydisDecodedInstruction *instruction, const ZydisDecodedOperand* operands,
ZyanU8 operand_count, ZydisEncoderRequest *request)
{
if (!instruction || !request || (operand_count && !operands))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZYAN_MEMSET(request, 0, sizeof(ZydisEncoderRequest));
request->machine_mode = instruction->machine_mode;
request->mnemonic = instruction->mnemonic;
request->prefixes = instruction->attributes & ZYDIS_ENCODABLE_PREFIXES;
request->branch_type = instruction->meta.branch_type;
if (!(instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_SEGMENT))
{
request->prefixes &= ~ZYDIS_ATTRIB_HAS_SEGMENT;
}
switch (instruction->address_width)
{
case 16:
request->address_size_hint = ZYDIS_ADDRESS_SIZE_HINT_16;
break;
case 32:
request->address_size_hint = ZYDIS_ADDRESS_SIZE_HINT_32;
break;
case 64:
request->address_size_hint = ZYDIS_ADDRESS_SIZE_HINT_64;
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (instruction->operand_width)
{
case 8:
request->operand_size_hint = ZYDIS_OPERAND_SIZE_HINT_8;
break;
case 16:
request->operand_size_hint = ZYDIS_OPERAND_SIZE_HINT_16;
break;
case 32:
request->operand_size_hint = ZYDIS_OPERAND_SIZE_HINT_32;
break;
case 64:
request->operand_size_hint = ZYDIS_OPERAND_SIZE_HINT_64;
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (request->branch_type)
{
case ZYDIS_BRANCH_TYPE_NONE:
request->branch_width = ZYDIS_BRANCH_WIDTH_NONE;
break;
case ZYDIS_BRANCH_TYPE_SHORT:
request->branch_width = ZYDIS_BRANCH_WIDTH_8;
break;
case ZYDIS_BRANCH_TYPE_NEAR:
case ZYDIS_BRANCH_TYPE_FAR:
switch (instruction->operand_width)
{
case 16:
request->branch_width = ZYDIS_BRANCH_WIDTH_16;
break;
case 32:
request->branch_width = ZYDIS_BRANCH_WIDTH_32;
break;
case 64:
request->branch_width = ZYDIS_BRANCH_WIDTH_64;
break;
default:
ZYAN_UNREACHABLE;
}
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (instruction->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
case ZYDIS_INSTRUCTION_ENCODING_XOP:
case ZYDIS_INSTRUCTION_ENCODING_VEX:
break;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
request->evex.broadcast = !instruction->avx.broadcast.is_static ?
instruction->avx.broadcast.mode : ZYDIS_BROADCAST_MODE_INVALID;
request->evex.rounding = instruction->avx.rounding.mode;
request->evex.sae = instruction->avx.has_sae;
request->evex.zeroing_mask = (instruction->avx.mask.mode == ZYDIS_MASK_MODE_ZEROING ||
instruction->avx.mask.mode == ZYDIS_MASK_MODE_CONTROL_ZEROING) &&
(instruction->raw.evex.z) ? ZYAN_TRUE : ZYAN_FALSE;
break;
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
request->mvex.broadcast = !instruction->avx.broadcast.is_static ?
instruction->avx.broadcast.mode : ZYDIS_BROADCAST_MODE_INVALID;
request->mvex.conversion = instruction->avx.conversion.mode;
request->mvex.rounding = instruction->avx.rounding.mode;
request->mvex.swizzle = instruction->avx.swizzle.mode;
request->mvex.sae = instruction->avx.has_sae;
request->mvex.eviction_hint = instruction->avx.has_eviction_hint;
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
request->allowed_encodings = 1 << instruction->encoding;
if ((operand_count > ZYDIS_ENCODER_MAX_OPERANDS) ||
(operand_count > instruction->operand_count_visible))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
request->operand_count = operand_count;
for (ZyanU8 i = 0; i < operand_count; ++i)
{
const ZydisDecodedOperand *dec_op = &operands[i];
ZydisEncoderOperand *enc_op = &request->operands[i];
enc_op->type = dec_op->type;
switch (dec_op->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
enc_op->reg.value = dec_op->reg.value;
enc_op->reg.is4 = dec_op->encoding == ZYDIS_OPERAND_ENCODING_IS4;
break;
case ZYDIS_OPERAND_TYPE_MEMORY:
enc_op->mem.base = dec_op->mem.base;
enc_op->mem.index = dec_op->mem.index;
enc_op->mem.scale = dec_op->mem.type != ZYDIS_MEMOP_TYPE_MIB ? dec_op->mem.scale : 0;
if (dec_op->encoding == ZYDIS_OPERAND_ENCODING_DISP16_32_64)
{
ZydisCalcAbsoluteAddress(instruction, dec_op, 0,
(ZyanU64 *)&enc_op->mem.displacement);
}
else
{
enc_op->mem.displacement = dec_op->mem.disp.has_displacement ?
dec_op->mem.disp.value : 0;
}
enc_op->mem.size = dec_op->size / 8;
break;
case ZYDIS_OPERAND_TYPE_POINTER:
enc_op->ptr.segment = dec_op->ptr.segment;
enc_op->ptr.offset = dec_op->ptr.offset;
break;
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
enc_op->imm.u = dec_op->imm.value.u;
// `XBEGIN` is an ISA-wide unique instruction because it's not a branching instruction
// but it has a relative operand which behaves differently from all other relatives
// (no truncating behavior in 16-bit mode). Encoder treats it as non-branching
// instruction that scales with hidden operand size.
if ((dec_op->imm.is_relative) &&
(instruction->mnemonic != ZYDIS_MNEMONIC_XBEGIN))
{
switch (instruction->raw.imm->size)
{
case 8:
request->branch_width = ZYDIS_BRANCH_WIDTH_8;
break;
case 16:
request->branch_width = ZYDIS_BRANCH_WIDTH_16;
break;
case 32:
request->branch_width = ZYDIS_BRANCH_WIDTH_32;
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
return ZYAN_STATUS_SUCCESS;
}
ZYDIS_EXPORT ZyanStatus ZydisEncoderNopFill(void *buffer, ZyanUSize length)
{
if (!buffer)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
// Intel SDM Vol. 2B "Recommended Multi-Byte Sequence of NOP Instruction"
static const ZyanU8 nops[9][9] =
{
{ 0x90 },
{ 0x66, 0x90 },
{ 0x0F, 0x1F, 0x00 },
{ 0x0F, 0x1F, 0x40, 0x00 },
{ 0x0F, 0x1F, 0x44, 0x00, 0x00 },
{ 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 },
{ 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 },
{ 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 },
};
ZyanU8 *output = (ZyanU8 *)buffer;
while (length)
{
ZyanUSize nop_size = (length > 9) ? 9 : length;
ZYAN_MEMCPY(output, nops[nop_size - 1], nop_size);
output += nop_size;
length -= nop_size;
}
return ZYAN_STATUS_SUCCESS;
}
/* ============================================================================================== */