diff --git a/README.md b/README.md index 7cdfd70..4da8f10 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,8 @@ failed |= fe_enc64(&cur, FE_XOR32rr, FE_AX, FE_AX); failed |= fe_enc64(&cur, FE_MOVZXr32m8, FE_CX, FE_MEM(FE_DI, 1, FE_AX, 0)); // test ecx, ecx failed |= fe_enc64(&cur, FE_TEST32rr, FE_CX, FE_CX); -// jz $ -// This will be replaced later FE_JMPL enforces use of longest offset +// jnz $ +// This will be replaced later; FE_JMPL enforces use of longest offset uint8_t* fwd_jmp = cur; failed |= fe_enc64(&cur, FE_JNZ|FE_JMPL, (intptr_t) cur); uint8_t* loop_tgt = cur; @@ -104,8 +104,9 @@ The API consists of one function to handle encode requests, as well as some macr - For offset operands, specify the target address. ## Known issues -- The encoder doesn't support VEX encodings (yet). - The EVEX prefix (AVX-512) is not supported (yet). +- MPX instructions are not supported. +- HLE prefixes `xacquire`/`xrelease` are not supported by the encoder (yet). - Prefixes for indirect jumps and calls are not properly decoded, e.g. `notrack`, `bnd`. - Low test coverage. (Help needed.) - No Python API. diff --git a/fadec-enc.h b/fadec-enc.h index 9abda89..e6dc637 100644 --- a/fadec-enc.h +++ b/fadec-enc.h @@ -22,16 +22,26 @@ typedef enum { typedef int64_t FeOp; +/** Construct a memory operand. Unused parts can be set to 0 and will be + * ignored. FE_IP can be used as base register, in which case the offset is + * interpreted as the offset from the /current/ position -- the size of the + * encoded instruction will be subtracted during encoding. scale must be 1, 2, + * 4, or 8; but is ignored if idx == 0. **/ #define FE_MEM(base,sc,idx,off) (INT64_MIN | ((int64_t) ((base) & 0xfff) << 32) | ((int64_t) ((idx) & 0xfff) << 44) | ((int64_t) ((sc) & 0xf) << 56) | ((off) & 0xffffffff)) +/** Add segment override prefix. This may or may not generate prefixes for the + * ignored prefixes ES/CS/DS/SS in 64-bit mode. **/ #define FE_SEG(seg) ((((seg) & 0x7) + 1) << 16) +/** Do not use. **/ #define FE_SEG_MASK 0x70000 +/** Overrides address size. **/ #define FE_ADDR32 0x80000 /** Used together with a RIP-relative (conditional) jump, this will force the * use of the encoding with the largest distance. Useful for reserving a jump * when the target offset is still unknown; if the jump is re-encoded later on, * FE_JMPL must be specified there, too, so that the encoding lengths match. **/ #define FE_JMPL 0x100000 +/** Do not use. **/ #define FE_MNEM_MASK 0xffff enum { @@ -41,8 +51,22 @@ enum { FE_MNEM_MAX }; +/** Do not use. **/ #define fe_enc64_1(buf, mnem, op0, op1, op2, op3, ...) fe_enc64_impl(buf, mnem, op0, op1, op2, op3) +/** Encode a single instruction for 64-bit mode. + * \param buf Pointer to the buffer for instruction bytes, must have a size of + * 15 bytes. The pointer is advanced by the number of bytes used for + * encoding the specified instruction. + * \param mnem Mnemonic, optionally or-ed with FE_SEG(), FE_ADDR32, or FE_JMPL. + * \param operands... Instruction operands. Immediate operands are passed as + * plain value; register operands using the FeReg enum; memory operands + * using FE_MEM(); and offset operands for RIP-relative jumps/calls are + * specified as _address in buf_, e.g. (intptr_t) jmptgt, the address of + * buf and the size of the encoded instruction are subtracted internally. + * \return Zero for success or a negative value in case of an error. + **/ #define fe_enc64(buf, ...) fe_enc64_1(buf, __VA_ARGS__, 0, 0, 0, 0, 0) +/** Do not use. **/ int fe_enc64_impl(uint8_t** buf, uint64_t mnem, FeOp op0, FeOp op1, FeOp op2, FeOp op3); #ifdef __cplusplus diff --git a/fadec.h b/fadec.h index 9f437d3..ab8fa0d 100644 --- a/fadec.h +++ b/fadec.h @@ -42,6 +42,7 @@ enum { FD_FLAG_64 = 1 << 7, }; +/** Operand types. **/ typedef enum { FD_OT_NONE = 0, FD_OT_REG = 1, @@ -186,8 +187,7 @@ const char* fdi_name(FdInstrType ty); #define FD_HAS_REP(instr) ((instr)->flags & FD_FLAG_REP) /** Indicates whether the instruction was encoded with a REPNZ prefix. **/ #define FD_HAS_REPNZ(instr) ((instr)->flags & FD_FLAG_REPNZ) -/** Indicates whether the instruction was encoded with a LOCK prefix. Note that - * it is not checked whether the LOCK prefix is valid for the instruction. **/ +/** Indicates whether the instruction was encoded with a LOCK prefix. **/ #define FD_HAS_LOCK(instr) ((instr)->flags & FD_FLAG_LOCK) /** Do not use. **/ #define FD_IS64(instr) ((instr)->flags & FD_FLAG_64) @@ -204,7 +204,7 @@ const char* fdi_name(FdInstrType ty); #define FD_OP_SIZE(instr,idx) ((instr)->operands[idx].size) /** Gets the accessed register index of a register operand. Note that /only/ the * index is returned, no further interpretation of the index (which depends on - * the instruction type) is done. The register type can be fetches using + * the instruction type) is done. The register type can be fetched using * FD_OP_REG_TYPE, e.g. for distinguishing high-byte registers. * Only valid if FD_OP_TYPE == FD_OT_REG **/ #define FD_OP_REG(instr,idx) ((FdReg) (instr)->operands[idx].reg) @@ -236,7 +236,7 @@ const char* fdi_name(FdInstrType ty); * Only valid if FD_OP_TYPE == FD_OT_MEM **/ #define FD_OP_DISP(instr,idx) ((int64_t) (instr)->disp) /** Gets the (sign-extended) encoded constant for an immediate operand. - * Only valid if FD_OP_TYPE == FD_OT_IMM **/ + * Only valid if FD_OP_TYPE == FD_OT_IMM or FD_OP_TYPE == FD_OT_OFF **/ #define FD_OP_IMM(instr,idx) ((instr)->imm) #ifdef __cplusplus