diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/3p/README | 3 | ||||
| -rw-r--r-- | src/autojump.c | 31 | ||||
| -rw-r--r-- | src/build/cmeta.c | 2 | ||||
| -rw-r--r-- | src/build/cmeta.h | 2 | ||||
| -rw-r--r-- | src/build/codegen.c | 2 | ||||
| -rw-r--r-- | src/con_.c | 2 | ||||
| -rw-r--r-- | src/con_.h | 10 | ||||
| -rw-r--r-- | src/dbg.c | 21 | ||||
| -rw-r--r-- | src/dbg.h | 17 | ||||
| -rw-r--r-- | src/demorec.c | 112 | ||||
| -rw-r--r-- | src/hook.c | 47 | ||||
| -rw-r--r-- | src/os-unix.h | 2 | ||||
| -rw-r--r-- | src/udis86.c | 2 | ||||
| -rw-r--r-- | src/x86.c | 87 | ||||
| -rw-r--r-- | src/x86.h | 451 | 
15 files changed, 664 insertions, 127 deletions
diff --git a/src/3p/README b/src/3p/README index e0c40a6..1617ff0 100644 --- a/src/3p/README +++ b/src/3p/README @@ -2,6 +2,9 @@ These are imported 3rd party library sources, wrangled for ease of plonking into  the build (e.g. relative #includes, etc.).  Used in SST itself: +  [none currently, stuff will probably be added later] + +Used in debug builds, but not compiled into releases:    - udis86  Used at build time: diff --git a/src/autojump.c b/src/autojump.c index e91c854..6c30df9 100644 --- a/src/autojump.c +++ b/src/autojump.c @@ -1,5 +1,5 @@  /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -30,29 +30,18 @@ DEF_CVAR(sst_autojump, "Jump upon hitting the ground while holding space", 0,  struct vec3f { float x, y, z; };  struct CMoveData { -	bool firstrun : 1; -	bool gamecodemoved : 1; +	bool firstrun : 1, gamecodemoved : 1;  	ulong playerhandle;  	int impulse; -	struct vec3f viewangles; -	struct vec3f absviewangles; -	int buttons; -	int oldbuttons; -	float mv_forward; -	float mv_side; -	float mv_up; -	float maxspeed; -	float clientmaxspeed; -	struct vec3f vel; -	struct vec3f angles; -	struct vec3f oldangles; +	struct vec3f viewangles, absviewangles; +	int buttons, oldbuttons; +	float mv_forward, mv_side, mv_up; +	float maxspeed, clmaxspeed; +	struct vec3f vel, angles, oldangles;  	float out_stepheight; -	struct vec3f out_wishvel; -	struct vec3f out_jumpvel; -	struct vec3f constraint_center; -	float constraint_radius; -	float constraint_width; -	float constraint_speedfactor; +	struct vec3f out_wishvel, out_jumpvel; +	struct vec3f constraint_centre; +	float constraint_radius, constraint_width, constraint_speedfactor;  	struct vec3f origin;  }; diff --git a/src/build/cmeta.c b/src/build/cmeta.c index 4e1eb4a..3d9281a 100644 --- a/src/build/cmeta.c +++ b/src/build/cmeta.c @@ -1,5 +1,5 @@  /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above diff --git a/src/build/cmeta.h b/src/build/cmeta.h index 18ff62c..20672f4 100644 --- a/src/build/cmeta.h +++ b/src/build/cmeta.h @@ -1,5 +1,5 @@  /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above diff --git a/src/build/codegen.c b/src/build/codegen.c index 38645e5..6d5fc99 100644 --- a/src/build/codegen.c +++ b/src/build/codegen.c @@ -1,5 +1,5 @@  /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -1,4 +1,4 @@ -/* XXX: THIS FILE SHOULD BE CALLED `con.c` BUT WINDOWS IS STUPID */ +/* THIS FILE SHOULD BE CALLED `con.c` BUT WINDOWS IS STUPID */  /*   * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   * @@ -1,4 +1,4 @@ -/* XXX: THIS FILE SHOULD BE CALLED `con.h` BUT WINDOWS IS STUPID */ +/* THIS FILE SHOULD BE CALLED `con.h` BUT WINDOWS IS STUPID */  /*   * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   * @@ -40,7 +40,7 @@ struct con_cmdargs {  	const char *argv[CON_CMD_MAX_ARGC];  }; -/* an ARGB colour, passed to con_colourmsg */ +/* an RGBA colour, passed to con_colourmsg */  struct con_colour {  	union {  		struct { u8 r, g, b, a; }; @@ -236,7 +236,11 @@ extern void *_con_vtab_iconvar[];  		.parent = &_cvar_##name_, /* bizarre, but how the engine does it */ \  		.defaultval = _Generic(value, char *: value, int: #value, \  				float: #value), \ -		.strlen = _Generic(value, char *: sizeof(value), \ +		/* N.B. the NOLINT comment below isn't for you, the reader, it's for the +		   computer, because clangd decided the only way to turn off a bogus +		   warning is to write a bogus comment. Also note, this comment you're +		   reading now isn't very useful either, I'm just angry. */ \ +		.strlen = _Generic(value, char *: sizeof(value), /*NOLINT*/ \  				default: sizeof(#value)), \  		.fval = _Generic(value, char *: 0, int: value, float: value), \  		.ival = _Generic(value, char *: 0, int: value, float: (int)value), \ @@ -1,5 +1,5 @@  /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,12 @@   * PERFORMANCE OF THIS SOFTWARE.   */ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#endif +  #include "con_.h"  #include "intdefs.h"  #include "ppmagic.h" @@ -46,4 +52,17 @@ void dbg_asmdump(char *name, const void *p, int len) {  	}  } +#ifdef _WIN32 +usize dbg_toghidra(void *addr) { +	void *mod; +	if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | +			GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (ushort *)addr, +			(HMODULE *)&mod/*please leave me alone*/)) { +		con_warn("dbg_toghidra: couldn't get base address\n"); +		return 0; +	} +	return (char *)addr - (char *)mod + 0x10000000; +} +#endif +  // vi: sw=4 ts=4 noet tw=80 cc=80 @@ -1,5 +1,5 @@  /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -17,12 +17,23 @@  #ifndef INC_DBG_H  #define INC_DBG_H -/* prints out a basic hexadecimal listing of a byte range */ +/* + * These functions can all be used for development and debugging but aren't + * available to release builds; this header shouldn't even be #included in real + * code that's committed to a repo. + */ + +/* Prints out a basic hexadecimal listing of a byte range. */  void dbg_hexdump(char *name, const void *p, int len); -/* prints out a disassembly of some instructions in memory */ +/* Prints out a disassembly of some instructions in memory. */  void dbg_asmdump(char *name, const void *p, int len); +#ifdef _WIN32 // at least for now +/* Returns a function's Ghidra address, assuming default project offsets. */ +void *dbg_toghidra(void *addr); +#endif +  #endif  // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/demorec.c b/src/demorec.c index bcc7367..a384e55 100644 --- a/src/demorec.c +++ b/src/demorec.c @@ -1,6 +1,6 @@  /*   * Copyright © 2021 Willian Henrique <wsimanbrazil@yahoo.com.br> - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -27,8 +27,9 @@  #include "intdefs.h"  #include "mem.h"  #include "os.h" -#include "udis86.h" +#include "ppmagic.h"  #include "vcall.h" +#include "x86.h"  #define SIGNONSTATE_SPAWN 5 // ready to receive entity packets  #define SIGNONSTATE_FULL 6 // fully connected, first non-delta packet received @@ -95,7 +96,6 @@ static void hook_stop_callback(const struct con_cmdargs *args) {  	orig_stop_callback(args);  } -  // The engine allows usermessages up to 255 bytes, we add 2 bytes of overhead,  // and then there's the leading bits before that too (see create_message)  static char bb_buf[DEMOREC_CUSTOM_MSG_MAX + 4]; @@ -139,75 +139,59 @@ void demorec_writecustom(void *buf, int len) {  	bitbuf_reset(&bb);  } +// XXX: probably want some general foreach-instruction macro once we start doing +// this kind of hackery in multiple different places +#define NEXT_INSN(p) do { \ +	int _len = x86_len(p); \ +	if (_len == -1) { \ +		con_warn("demorec: %s: unknown or invalid instruction\n", __func__); \ +		return false; \ +	} \ +	(p) += _len; \ +} while (0)  // This finds the "demorecorder" global variable (the engine-wide CDemoRecorder  // instance). -static inline void *find_demorecorder(struct con_cmd *cmd_stop) { -	// The "stop" command calls the virtual function demorecorder.IsRecording(), -	// so just look for the load of the "this" pointer -	struct ud udis; -	ud_init(&udis); -	ud_set_mode(&udis, 32); -	ud_set_input_buffer(&udis, (uchar *)con_getcmdcb(cmd_stop), 32); -	while (ud_disassemble(&udis)) { +static inline bool find_demorecorder(struct con_cmd *cmd_stop) {  #ifdef _WIN32 -		if (ud_insn_mnemonic(&udis) == UD_Imov) { -			const struct ud_operand *dest = ud_insn_opr(&udis, 0); -			const struct ud_operand *src = ud_insn_opr(&udis, 1); -			// looking for a mov from an address into ECX, as per thiscall -			if (dest->type == UD_OP_REG && dest->base == UD_R_ECX && -					src->type == UD_OP_MEM) { -				return *(void **)src->lval.udword; -			} +	uchar *stopcb = (uchar *)con_getcmdcb(cmd_stop); +	// The "stop" command calls the virtual function demorecorder.IsRecording(), +	// so just look for the load of the "this" pointer into ECX +	for (uchar *p = stopcb; p - stopcb < 32;) { +		if (p[0] == X86_MOVRMW && p[1] == X86_MODRM(0, 1, 5)) { +			void **indirect = mem_loadptr(p + 2); +			demorecorder = *indirect; +			return true;  		} +		NEXT_INSN(p); +	}  #else  #warning TODO(linux): implement linux equivalent (cdecl!) -		return 0;  #endif -	} -	return 0; +	return false;  }  // This finds "m_bRecording" and "m_nDemoNumber" using the pointer to the  // original "StopRecording" demorecorder function. -static inline bool find_recmembers(void *stop_recording_func) { -	struct ud udis; -	ud_init(&udis); -	ud_set_mode(&udis, 32); -	// TODO(opt): consider the below: is it really needed? does it matter? -	// way overshooting the size of the function in bytes to make sure it -	// accomodates for possible differences in different games. we make sure -	// to stop early if we find a RET so should be fine -	ud_set_input_buffer(&udis, (uchar *)stop_recording_func, 200); -	while (ud_disassemble(&udis)) { +static inline bool find_recmembers(void *stoprecording) {  #ifdef _WIN32 -		enum ud_mnemonic_code code = ud_insn_mnemonic(&udis); -		if (code == UD_Imov) { -			const struct ud_operand *dest = ud_insn_opr(&udis, 0); -			const struct ud_operand *src = ud_insn_opr(&udis, 1); -			// m_nDemoNumber and m_bRecording are both set to 0 -			// looking for movs with immediates equal to 0 -			// the byte immediate refers to m_bRecording -			if (src->type == UD_OP_IMM && src->lval.ubyte == 0) { -				if (src->size == 8) { -					recording = (bool *)mem_offset(demorecorder, -							dest->lval.udword); -				} -				else { -					demonum = (int *)mem_offset(demorecorder, -							dest->lval.udword); -				} -				if (recording && demonum) return true; // blegh -			} +	for (uchar *p = (uchar *)stoprecording; p - (uchar *)stoprecording < 128;) { +		// m_nDemoNumber = 0 -> mov dword ptr [<reg> + off], 0 +		// XXX: might end up wanting constants for the MRM field masks? +		if (p[0] == X86_MOVMIW && (p[1] & 0xC0) == 0x80 && +				mem_load32(p + 6) == 0) { +			demonum = mem_offset(demorecorder, mem_load32(p + 2));  		} -		else if (code == UD_Iret) { -			return false; +		// m_bRecording = false -> mov byte ptr [<reg> + off], 0 +		else if (p[0] == X86_MOVMI8 && (p[1] & 0xC0) == 0x80 && p[6] == 0) { +			recording = mem_offset(demorecorder, mem_load32(p + 2));  		} +		if (recording && demonum) return true; // blegh +		NEXT_INSN(p); +	}  #else // linux is probably different here idk -#warning TODO(linux): implement linux equivalent -		return false; +#warning TODO(linux): implement linux equivalent (???)  #endif -	}  	return false;  } @@ -215,6 +199,7 @@ static inline bool find_recmembers(void *stop_recording_func) {  // network packet, wraps it up in the appropriate demo framing format and writes  // it out to the demo file being recorded.  static bool find_WriteMessages(void) { +	// TODO(compat): probably rewrite this to just scan for a call instruction!  	const uchar *insns = (*(uchar ***)demorecorder)[gamedata_vtidx_RecordPacket];  	// RecordPacket calls WriteMessages pretty much right away:  	// 56           push  esi @@ -228,10 +213,10 @@ static bool find_WriteMessages(void) {  	// So we just double check the byte pattern...  	static const uchar bytes[] =  #ifdef _WIN32 -{0x56, 0x57, 0x8B, 0xF1, 0x8D, 0xBE, 0x8C, 0x06, 0x00, 0x00, 0x57, 0xE8}; +		HEXBYTES(56, 57, 8B, F1, 8D, BE, 8C, 06, 00, 00, 57, E8);  #else  #warning This is possibly different on Linux too, have a look! -{-1, -1, -1, -1, -1, -1}; +		{-1, -1, -1, -1, -1, -1};  #endif  	if (!memcmp(insns, bytes, sizeof(bytes))) {  		ssize off = mem_loadoffset(insns + sizeof(bytes)); @@ -249,21 +234,17 @@ bool demorec_init(void) {  		con_warn("demorec: missing gamedata entries for this engine\n");  		return false;  	} -  	cmd_stop = con_findcmd("stop");  	if (!cmd_stop) { // can *this* even happen? I hope not!  		con_warn("demorec: couldn't find \"stop\" command\n");  		return false;  	} - -	demorecorder = find_demorecorder(cmd_stop); -	if (!demorecorder) { +	if (!find_demorecorder(cmd_stop)) {  		con_warn("demorec: couldn't find demo recorder instance\n");  		return false;  	}  	void **vtable = *(void ***)demorecorder; -  	// XXX: 16 is totally arbitrary here! figure out proper bounds later  	if (!os_mprot(vtable, 16 * sizeof(void *), PAGE_EXECUTE_READWRITE)) {  #ifdef _WIN32 @@ -276,7 +257,7 @@ bool demorec_init(void) {  		return false;  	} -	if (!find_recmembers(vtable[7])) { +	if (!find_recmembers(vtable[7])) { // XXX: stop hardcoding this!?  		con_warn("demorec: couldn't find m_bRecording and m_nDemoNumber\n");  		return false;  	} @@ -301,11 +282,6 @@ bool demorec_custom_init(void) {  		con_warn("demorec: custom: missing gamedata entries for this engine\n");  		return false;  	} -	// TODO(featgen): auto-check this factory -	if (!factory_engine) { -		con_warn("demorec: missing required interfaces\n"); -		return false; -	}  	// More UncraftedkNowledge:  	// > yeah okay so [the usermessage length is] 11 bits if the demo protocol @@ -21,7 +21,7 @@  #include "intdefs.h"  #include "mem.h"  #include "os.h" -#include "udis86.h" +#include "x86.h"  // Warning: half-arsed hacky implementation (because that's all we really need)  // Almost certainly breaks in some weird cases. Oh well! Most of the time, @@ -39,50 +39,47 @@ static void setrwx(void) {  	os_mprot(trampolines, sizeof(trampolines), PAGE_EXECUTE_READWRITE);  } -#define RELJMP 0xE9 // the first byte of a 5-byte jmp -  void *hook_inline(void *func_, void *target) {  	uchar *func = func_;  	// dumb hack: rather than correcting jmp offsets and having to painstakingly  	// track them all, just look for the underlying thing being jmp-ed to and  	// hook _that_. -	while (*func == RELJMP) func += mem_loadoffset(func + 1) + 5; +	while (*func == X86_JMPIW) func += mem_loadoffset(func + 1) + 5;  	if (!os_mprot(func, 5, PAGE_EXECUTE_READWRITE)) return false; -	struct ud udis; -	ud_init(&udis); -	ud_set_mode(&udis, 32); -	// max insn length is 15, we overwrite 5, so max to copy is 4 + 15 = 19 -	ud_set_input_buffer(&udis, func, 19);  	int len = 0; -	do { -		ud_disassemble(&udis); // assume we don't run out of bytes -		// TODO(opt): this check should probably be gone, keeping it to make dev -		// life easier/less crashy. Really, we should just refrain from hooking -		// things that immediately call or branch (als, immediate jump is -		// already handled above). -		if (ud_insn_mnemonic(&udis) == UD_Ijmp || -				ud_insn_mnemonic(&udis) == UD_Icall) { -			con_warn("hook_inline: jmp/call adjustment NYI\n"); -			return 0; +	for (;;) { +		if (func[len] == X86_CALL) { +			con_warn("hook_inline: can't trampoline call instructions\n"); +			return false; +		} +		int ilen = x86_len(func + len); +		if (ilen == -1) { +			con_warn("hook_inline: unknown or invalid instruction\n"); +			return false;  		} -		len += ud_insn_len(&udis); -	} while (len < 5); +		len += ilen; +		if (len >= 5) break; +		if (func[len] == X86_JMPIW) { +			con_warn("hook_inline: can't trampoline jmp instructions\n"); +			return false; +		} +	}  	// for simplicity, just bump alloc the trampoline. no need to free anyway -	if (nexttrampoline - trampolines > sizeof(trampolines) - len - 6) goto nospc; +	if (nexttrampoline - trampolines > sizeof(trampolines) - len - 6) goto nosp;  	uchar *trampoline = (uchar *)InterlockedExchangeAdd(  			(volatile long *)&nexttrampoline, len + 6);  	// avoid TOCTOU  	if (trampoline - trampolines > sizeof(trampolines) - len - 6) { -nospc:	con_warn("hook_inline: out of trampoline space\n"); +nosp:	con_warn("hook_inline: out of trampoline space\n");  		return 0;  	}  	*trampoline++ = len; // stick length in front for quicker unhooking  	memcpy(trampoline, func, len); -	trampoline[len] = RELJMP; +	trampoline[len] = X86_JMPIW;  	uint diff = func - (trampoline + 5); // goto the continuation  	memcpy(trampoline + len + 1, &diff, 4);  	uchar jmp[8]; -	jmp[0] = RELJMP; +	jmp[0] = X86_JMPIW;  	diff = (uchar *)target - (func + 5); // goto the hook target  	memcpy(jmp + 1, &diff, 4);  	// pad with original bytes so we can do an 8-byte atomic write diff --git a/src/os-unix.h b/src/os-unix.h index 8b7bf27..5c3c604 100644 --- a/src/os-unix.h +++ b/src/os-unix.h @@ -45,6 +45,7 @@ typedef char os_char;  #define os_dlsym dlsym +#ifdef __linux__  static inline bool os_dlfile(void *m, char *buf, int sz) {  	// NOTE: this might be linux/glibc-specific (I haven't checked every  	// implementation). this is fine as we don't use it in any build-time code, @@ -54,6 +55,7 @@ static inline bool os_dlfile(void *m, char *buf, int sz) {  	if (ssz > sz) { errno = ENAMETOOLONG; return false; }  	memcpy(buf, lm->l_name, ssz); return true;  } +#endif  // unix mprot flags are much nicer but cannot be defined in terms of the windows  // ones, so we use the windows ones and define them in terms of the unix ones. diff --git a/src/udis86.c b/src/udis86.c index fcb3656..754ff83 100644 --- a/src/udis86.c +++ b/src/udis86.c @@ -3,8 +3,6 @@  #include "3p/udis86/udis86.c"  #include "3p/udis86/decode.c"  #include "3p/udis86/itab.c" -// this stuff is optional but llvm is smart enough to remove it if it's unused, -// so we keep it in here to be able to use it conveniently for debugging etc.  #include "3p/udis86/syn.c"  #include "3p/udis86/syn-intel.c" diff --git a/src/x86.c b/src/x86.c new file mode 100644 index 0000000..e33efbb --- /dev/null +++ b/src/x86.c @@ -0,0 +1,87 @@ +/* + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "intdefs.h" +#include "x86.h" + +static int mrm(uchar b, int addrlen) { +	// I won't lie: I don't *entirely* understand this particular logic. I +	// largely based it on some public domain code I found on the internet +	if (addrlen == 4 || b & 0xC0) { +		int sib = addrlen == 4 && b < 0xC0 && (b & 7) == 4; +		switch (b & 0xC0) { +			// disp8 +			case 0x40: return 2 + sib; +			// disp16/32 +			case 0: if ((b & 7) == 5) case 0x80: return 1 + addrlen + sib; +		} +		// disp8/32 +		if (sib && (b & 7) == 5) return b & 0x40 ? 3 : 6; +	} +	if (addrlen == 2 && b == 0x26) return 3; +	return 1; // NOTE: include the mrm itself in the byte count +} + +int x86_len(const void *insn_) { +#define CASES(name, _) case name: +	const uchar *insn = insn_; +	int pfxlen = 0, addrlen = 4, operandlen = 4; + +p:	switch (*insn) { +		case X86_PFX_ADSZ: addrlen = 2; goto P; // bit dumb sorry +		case X86_PFX_OPSZ: operandlen = 2; +P:		X86_SEG_PREFIXES(CASES) +		case X86_PFX_LOCK: case X86_PFX_REPN: case X86_PFX_REP: +			// instruction can only be 15 bytes. this could go over, oh well, +			// just don't want to loop for 8 million years +			if (++pfxlen == 14) return -1; +			++insn; +			goto p; +	} + +	switch (*insn) { +		X86_OPS_1BYTE_NO(CASES) return pfxlen + 1; +		X86_OPS_1BYTE_I8(CASES) operandlen = 1; +		X86_OPS_1BYTE_IW(CASES) return pfxlen + 1 + operandlen; +		X86_OPS_1BYTE_I16(CASES) return pfxlen + 3; +		X86_OPS_1BYTE_MRM(CASES) return pfxlen + 1 + mrm(insn[1], addrlen); +		X86_OPS_1BYTE_MRM_I8(CASES) operandlen = 1; +		X86_OPS_1BYTE_MRM_IW(CASES) +			return pfxlen + 1 + operandlen + mrm(insn[1], addrlen); +		case X86_ENTER: return pfxlen + 4; +		case X86_CRAZY8: operandlen = 1; +		case X86_CRAZYW: +			if ((insn[1] & 0x38) >= 0x10) operandlen = 0; +			return pfxlen + 2 + operandlen + mrm(insn[1], addrlen); +		case X86_2BYTE: ++insn; goto b2; +	} +	return -1; + +b2:	switch (*insn) { +		// we don't support any 3 byte ops for now, implement if ever needed... +		case X86_3BYTE1: case X86_3BYTE2: case X86_3DNOW: return -1; +		X86_OPS_2BYTE_NO(CASES) return pfxlen + 2; +		X86_OPS_2BYTE_IW(CASES) return pfxlen + 2 + operandlen; +		X86_OPS_2BYTE_MRM(CASES) return pfxlen + 2 + mrm(insn[1], addrlen); +		X86_OPS_2BYTE_MRM_I8(CASES) operandlen = 1; +			return pfxlen + 2 + operandlen + mrm(insn[1], addrlen); +	} + +	return -1; +#undef CASES +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/x86.h b/src/x86.h new file mode 100644 index 0000000..46a34b2 --- /dev/null +++ b/src/x86.h @@ -0,0 +1,451 @@ +/* + * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INC_X86_H +#define INC_X86_H + +/* + * Opcode-based X86 instruction analysis. In other words, *NOT* a disassembler. + * Only cares about the instructions we expect to see in basic 32-bit userspace + * functions; there's no kernel-mode instructions, no MMX/3DNow!/SSE/AVX, no + * REX, EVEX, yadda yadda. + */ + +// XXX: no BOUND (0x62): ambiguous with EVEX prefix - can't be arsed! + +/* Instruction prefixes: segments */ +#define X86_SEG_PREFIXES(X) \ +	X(X86_PFX_ES,   0x26) \ +	X(X86_PFX_CS,   0x2E) \ +	X(X86_PFX_SS,   0x36) \ +	X(X86_PFX_DS,   0x3E) \ +	X(X86_PFX_FS,   0x64) \ +	X(X86_PFX_GS,   0x65) + +/* Instruction prefixes: operations */ +#define X86_OP_PREFIXES(X) \ +	X(X86_PFX_OPSZ, 0x66) \ +	X(X86_PFX_ADSZ, 0x67) \ +	X(X86_PFX_LOCK, 0xF0) \ +	X(X86_PFX_REPN, 0xF2) \ +	X(X86_PFX_REP,  0xF3) + +/* All instruction prefixes */ +#define X86_PREFIXES(X) X86_SEG_PREFIXES(X) X86_OP_PREFIXES(X) + +/* Single-byte opcodes with no operands */ +#define X86_OPS_1BYTE_NO(X) \ +	X(X86_PUSHES,     0x06) \ +	X(X86_POPES,      0x07) \ +	X(X86_PUSHCS,     0x0E) \ +	X(X86_PUSHSS,     0x16) \ +	X(X86_POPSS,      0x17) \ +	X(X86_PUSHDS,     0x1E) \ +	X(X86_POPDS,      0x1F) \ +	X(X86_DAA,        0x27) \ +	X(X86_DAS,        0x2F) \ +	X(X86_AAA,        0x37) \ +	X(X86_AAS,        0x3F) \ +	X(X86_INCEAX,     0x40) \ +	X(X86_INCECX,     0x41) \ +	X(X86_INCEDX,     0x42) \ +	X(X86_INCEBX,     0x43) \ +	X(X86_INCESP,     0x44) \ +	X(X86_INCEBP,     0x45) \ +	X(X86_INCESI,     0x46) \ +	X(X86_INCEDI,     0x47) \ +	X(X86_DECEAX,     0x48) \ +	X(X86_DECECX,     0x49) \ +	X(X86_DECEDX,     0x4A) \ +	X(X86_DECEBX,     0x4B) \ +	X(X86_DECESP,     0x4C) \ +	X(X86_DECEBP,     0x4D) \ +	X(X86_DECESI,     0x4E) \ +	X(X86_DECEDI,     0x4F) \ +	X(X86_PUSHEAX,    0x50) \ +	X(X86_PUSHECX,    0x51) \ +	X(X86_PUSHEDX,    0x52) \ +	X(X86_PUSHEBX,    0x53) \ +	X(X86_PUSHESP,    0x54) \ +	X(X86_PUSHEBP,    0x55) \ +	X(X86_PUSHESI,    0x56) \ +	X(X86_PUSHEDI,    0x57) \ +	X(X86_POPEAX,     0x58) \ +	X(X86_POPECX,     0x59) \ +	X(X86_POPEDX,     0x5A) \ +	X(X86_POPEBX,     0x5B) \ +	X(X86_POPESP,     0x5C) \ +	X(X86_POPEBP,     0x5D) \ +	X(X86_POPESI,     0x5E) \ +	X(X86_POPEDI,     0x5F) \ +	X(X86_PUSHA,      0x60) \ +	X(X86_POPA,       0x61) \ +	X(X86_NOP,        0x90) \ +	X(X86_XCHGECXEAX, 0x91) \ +	X(X86_XCHGEDXEAX, 0x92) \ +	X(X86_XCHGEBXEAX, 0x93) \ +	X(X86_XCHGESPEAX, 0x94) \ +	X(X86_XCHGEBPEAX, 0x95) \ +	X(X86_XCHGESIEAX, 0x96) \ +	X(X86_XCHGEDIEAX, 0x97) \ +	X(X86_CWDE,       0x98) \ +	X(X86_CDQ,        0x99) \ +	X(X86_WAIT,       0x9B) \ +	X(X86_PUSHF,      0x9C) \ +	X(X86_POPF,       0x9D) \ +	X(X86_SAHF,       0x9E) \ +	X(X86_LAHF,       0x9F) \ +	X(X86_MOVS8,      0xA4) \ +	X(X86_MOVSW,      0xA5) \ +	X(X86_CMPS8,      0xA6) \ +	X(X86_CMPSW,      0xA7) \ +	X(X86_STOS8,      0xAA) \ +	X(X86_STOSD,      0xAB) \ +	X(X86_LODS8,      0xAC) \ +	X(X86_LODSD,      0xAD) \ +	X(X86_SCAS8,      0xAE) \ +	X(X86_SCASD,      0xAF) \ +	X(X86_RET,        0xC3) \ +	X(X86_LEAVE,      0xC9) \ +	X(X86_RETF,       0xCB) \ +	X(X86_INT3,       0xCC) \ +	X(X86_INTO,       0xCE) \ +	X(X86_XLAT,       0xD7) \ +	X(X86_JMPI8,      0xEB) \ +	X(X86_CMC,        0xF5) \ +	X(X86_CLC,        0xF8) \ +	X(X86_STC,        0xF9) \ +	X(X86_CLI,        0xFA) \ +	X(X86_STI,        0xFB) \ +	X(X86_CLD,        0xFC) \ +	X(X86_STD,        0xFD) + +/* Single-byte opcodes with a 1-byte immediate operand */ +#define X86_OPS_1BYTE_I8(X) \ +	X(X86_ADDALI,  0x04) \ +	X(X86_ORALI,   0x0C) \ +	X(X86_ADCALI,  0x14) \ +	X(X86_SBBALI,  0x1C) \ +	X(X86_ANDALI,  0x24) \ +	X(X86_SUBALI,  0x2C) \ +	X(X86_XORALI,  0x34) \ +	X(X86_CMPALI,  0x3C) \ +	X(X86_PUSHI8,  0x6A) \ +	X(X86_MOVALII, 0xA0) /* From offset (indirect) */ \ +	X(X86_MOVIIAL, 0xA2) /* To offset (indirect) */ \ +	X(X86_TESTALI, 0xA8) \ +	X(X86_JO,      0x70) \ +	X(X86_JNO,     0x71) \ +	X(X86_JB,      0x72) /* AKA JC */ \ +	X(X86_JNB,     0x73) /* AKA JNC */ \ +	X(X86_JZ,      0x74) /* AKA JE */ \ +	X(X86_JNZ,     0x75) /* AKA JNZ */ \ +	X(X86_JNA,     0x76) /* AKA JBE */ \ +	X(X86_JA,      0x77) /* AKA JNBE */ \ +	X(X86_JS,      0x78) \ +	X(X86_JNS,     0x79) \ +	X(X86_JP,      0x7A) \ +	X(X86_JNP,     0x7B) \ +	X(X86_JL,      0x7C) /* AKA JNGE */ \ +	X(X86_JNL,     0x7D) /* AKA JGE */ \ +	X(X86_JNG,     0x7E) /* AKA JLE */ \ +	X(X86_JG,      0x7F) /* AKA JNLE */ \ +	X(X86_MOVALI,  0xB0) \ +	X(X86_MOVCLI,  0xB1) \ +	X(X86_MOVDLI,  0xB2) \ +	X(X86_MOVBLI,  0xB3) \ +	X(X86_MOVAHI,  0xB4) \ +	X(X86_MOVCHI,  0xB5) \ +	X(X86_MOVDHI,  0xB6) \ +	X(X86_MOVBHI,  0xB7) \ +	X(X86_INT,     0xCD) \ +	X(X86_AMX,     0xD4) /* Note: D4 0A is referred to as AAM */ \ +	X(X86_ADX,     0xD5) /* Note: D4 0A is referred to as AAD */ \ +	X(X86_LOOPNZ,  0xE0) /* AKA LOOPNE */ \ +	X(X86_LOOPZ,   0xE1) /* AKA LOOPE */ \ +	X(X86_LOOP,    0xE2) \ +	X(X86_JCXZ,    0xE3) + +/* Single-byte opcodes with a word-sized immediate operand */ +#define X86_OPS_1BYTE_IW(X) \ +	X(X86_ADDEAXI,  0x05) \ +	X(X86_OREAXI,   0x0D) \ +	X(X86_ADCEAXI,  0x15) \ +	X(X86_SBBEAXI,  0x1D) \ +	X(X86_ANDEAXI,  0x25) \ +	X(X86_SUBEAXI,  0x2D) \ +	X(X86_XOREAXI,  0x35) \ +	X(X86_CMPEAXI,  0x3D) \ +	X(X86_PUSHIW,   0x68) \ +	X(X86_MOVEAXII, 0xA1) /* From offset (indirect) */ \ +	X(X86_MOVIIEAX, 0xA3) /* To offset (indirect) */ \ +	X(X86_TESTEAXI, 0xA9) \ +	X(X86_MOVEAXI,  0xB8) \ +	X(X86_MOVECXI,  0xB9) \ +	X(X86_MOVEDXI,  0xBA) \ +	X(X86_MOVEBXI,  0xBB) \ +	X(X86_MOVESPI,  0xBC) \ +	X(X86_MOVEBPI,  0xBD) \ +	X(X86_MOVESII,  0xBE) \ +	X(X86_MOVEDII,  0xBF) \ +	X(X86_CALL,     0xE8) \ +	X(X86_JMPIW,    0xE9) + +/* Single-byte opcodes with 16-bit immediate operands, regardless of prefixes */ +#define X86_OPS_1BYTE_I16(X) \ +	X(X86_RETI16,  0xC2) \ +	X(X86_RETFI16, 0xCA) + +/* + * Single-byte opcodes with a ModRM. `MR` in a name means the ModRM is the + * destination, `RM` means it's the source. + */ +#define X86_OPS_1BYTE_MRM(X) \ +	X(X86_ADDMR8,    0x00) \ +	X(X86_ADDMRW,    0x01) \ +	X(X86_ADDRM8,    0x02) \ +	X(X86_ADDRMW,    0x03) \ +	X(X86_ORMR8,     0x08) \ +	X(X86_ORMRW,     0x09) \ +	X(X86_ORRM8,     0x0A) \ +	X(X86_ORRMW,     0x0B) \ +	X(X86_ADCMR8,    0x10) \ +	X(X86_ADCMRW,    0x11) \ +	X(X86_ADCRM8,    0x12) \ +	X(X86_ADCRMW,    0x13) \ +	X(X86_SBBMR8,    0x18) \ +	X(X86_SBBMRW,    0x19) \ +	X(X86_SBBRM8,    0x1A) \ +	X(X86_SBBRMW,    0x1B) \ +	X(X86_ANDMR8,    0x20) \ +	X(X86_ANDMRW,    0x21) \ +	X(X86_ANDRM8,    0x22) \ +	X(X86_ANDRMW,    0x23) \ +	X(X86_SUBMR8,    0x28) \ +	X(X86_SUBMRW,    0x29) \ +	X(X86_SUBRM8,    0x2A) \ +	X(X86_SUBRMW,    0x2B) \ +	X(X86_XORMR8,    0x30) \ +	X(X86_XORMRW,    0x31) \ +	X(X86_XORRM8,    0x32) \ +	X(X86_XORRMW,    0x33) \ +	X(X86_CMPMR8,    0x38) \ +	X(X86_CMPMRW,    0x39) \ +	X(X86_CMPRM8,    0x3A) \ +	X(X86_CMPRMW,    0x3B) \ +	X(X86_ARPL,      0x63) \ +	X(X86_TESTMR8,   0x84) \ +	X(X86_TESTMRW,   0x85) \ +	X(X86_XCHGMR8,   0x86) \ +	X(X86_XCHGMRW,   0x87) \ +	X(X86_MOVMR8,    0x88) \ +	X(X86_MOVMRW,    0x89) \ +	X(X86_MOVRM8,    0x8A) \ +	X(X86_MOVRMW,    0x8B) \ +	X(X86_MOVMS,     0x8C) /* Load 4 bytes from segment register */ \ +	X(X86_LEA,       0x8D) \ +	X(X86_MOVSM,     0x8E) /* Store 4 bytes to segment register */ \ +	X(X86_POPM,      0x8F) \ +	X(X86_LES,       0xC4) \ +	X(X86_LDS,       0xC5) \ +	X(X86_SHIFTM18,  0xD0) /* Shift/roll by 1 place */ \ +	X(X86_SHIFTM1W,  0xD1) /* Shift/roll by 1 place */ \ +	X(X86_SHIFTMCL8, 0xD2) /* Shift/roll by CL places */ \ +	X(X86_SHIFTMCLW, 0xD3) /* Shift/roll by CL places */ \ +	X(X86_FLTBLK1,   0xD8) /* Various float ops (1/8) */ \ +	X(X86_FLTBLK2,   0xD9) /* Various float ops (2/8) */ \ +	X(X86_FLTBLK3,   0xDA) /* Various float ops (3/8) */ \ +	X(X86_FLTBLK4,   0xDB) /* Various float ops (4/8) */ \ +	X(X86_FLTBLK5,   0xDC) /* Various float ops (5/8) */ \ +	X(X86_FLTBLK6,   0xDD) /* Various float ops (6/8) */ \ +	X(X86_FLTBLK7,   0xDE) /* Various float ops (7/8) */ \ +	X(X86_FLTBLK8,   0xDF) /* Various float ops (8/8) */ \ +	X(X86_MISCM8,    0xFE) /* Only documented for MRM.reg in {0, 1} */ \ +	X(X86_MISCMW,    0xFF) + +/* Single-byte opcodes with a ModRM and a 1-byte immediate operand */ +#define X86_OPS_1BYTE_MRM_I8(X) \ +	X(X86_IMULMI8,  0x6B) /* 3-operand multiply */ \ +	X(X86_ALUMI8,   0x80) /* ALU op in MRM.reg, from immediate */ \ +	X(X86_ALUMI8X,  0x82) /* ALU op in MRM.reg, from immediate, redundant?? */ \ +	X(X86_ALUMI8S,  0x83) /* ALU op in MRM.reg, from immediate, sign-extend */ \ +	X(X86_SHIFTMI8, 0xC0) /* Shift/roll by imm8 places */ \ +	X(X86_SHIFTMIW, 0xC1) /* Shift/roll by imm8 places */ \ +	X(X86_MOVMI8,   0xC6) /* Note: RM.reg must be 0 */ + +/* Single-byte opcodes with a ModRM and a word-sized immediate operand */ +#define X86_OPS_1BYTE_MRM_IW(X) \ +	X(X86_IMULMIW, 0x69) /* 3-operand multiply */ \ +	X(X86_ALUMIW,  0x81) /* ALU op in MRM.reg, from immediate */ \ +	X(X86_MOVMIW,  0xC7) /* Note: MRM.reg must be 0 */ + +/* All single-byte x86 instructions */ +#define X86_OPS_1BYTE(X) \ +	X86_OPS_1BYTE_NO(X) \ +	X86_OPS_1BYTE_I8(X) \ +	X86_OPS_1BYTE_IW(X) \ +	X86_OPS_1BYTE_I16(X) \ +	X86_OPS_1BYTE_MRM(X) \ +	X86_OPS_1BYTE_MRM_I8(X) \ +	X86_OPS_1BYTE_MRM_IW(X) \ +	X(X86_ENTER,  0xC8) /* Dumb special case insn: imm16 followed by imm8 */ \ +	X(X86_CRAZY8, 0xF6) /* CRAZY reg-encoded block, has imm8 IFF reg < 2 */ \ +	X(X86_CRAZYW, 0xF7) /* CRAZY reg-encoded block, has imm32/16 IFF reg < 2 */ + +/* Second bytes of opcodes with no operands */ +#define X86_OPS_2BYTE_NO(X) \ +	X(X86_2B_RDTSC,    0x31) \ +	X(X86_2B_RDPMD,    0x33) \ +	X(X86_2B_SYSENTER, 0x34) \ +	X(X86_2B_PUSHFS,   0xA0) \ +	X(X86_2B_POPFS,    0xA1) \ +	X(X86_2B_CPUID,    0xA2) \ +	X(X86_2B_PUSHGS,   0xA8) \ +	X(X86_2B_POPGS,    0xA9) \ +	X(X86_2B_RSM,      0xAA) \ +	X(X86_2B_BSWAPEAX,  0xC8) \ +	X(X86_2B_BSWAPECX,  0xC9) \ +	X(X86_2B_BSWAPEDX,  0xCA) \ +	X(X86_2B_BSWAPEBX,  0xCB) \ +	X(X86_2B_BSWAPESP,  0xCC) \ +	X(X86_2B_BSWAPEBP,  0xCD) \ +	X(X86_2B_BSWAPESI,  0xCE) \ +	X(X86_2B_BSWAPEDI,  0xCF) + +/* Second bytes of opcodes with a word-sized immediate operand */ +#define X86_OPS_2BYTE_IW(X) \ +	X(X86_2B_JOII,  0x80) /* From offset (indirect) */ \ +	X(X86_2B_JNOII, 0x81) /* From offset (indirect) */ \ +	X(X86_2B_JBII,  0x82) /* AKA JC; from offset (indirect) */ \ +	X(X86_2B_JNBII, 0x83) /* AKA JNC; from offset (indirect) */ \ +	X(X86_2B_JZII,  0x84) /* AKA JE; from offset (indirect) */ \ +	X(X86_2B_JNZII, 0x85) /* AKA JNZ; from offset (indirect) */ \ +	X(X86_2B_JNAII, 0x86) /* AKA JBE; from offset (indirect) */ \ +	X(X86_2B_JAII,  0x87) /* AKA JNBE; from offset (indirect) */ \ +	X(X86_2B_JSII,  0x88) /* From offset (indirect) */ \ +	X(X86_2B_JNSII, 0x89) /* From offset (indirect) */ \ +	X(X86_2B_JPII,  0x8A) /* From offset (indirect) */ \ +	X(X86_2B_JNPII, 0x8B) /* From offset (indirect) */ \ +	X(X86_2B_JLII,  0x8C) /* AKA JNGE; from offset (indirect) */ \ +	X(X86_2B_JNLII, 0x8D) /* AKA JGE; from offset (indirect) */ \ +	X(X86_2B_JNGII, 0x8E) /* AKA JLE; from offset (indirect) */ \ +	X(X86_2B_JGII,  0x8F) /* AKA JNLE; from offset (indirect) */ + +/* Second bytes of opcodes with a ModRM */ +#define X86_OPS_2BYTE_MRM(X) \ +	X(X86_2B_NOP,      0x0D) /* Variable length NOP (3-9 with prefix) */ \ +	X(X86_2B_HINTS1,   0x18) /* Prefetch and hint-nop block 1/8 */ \ +	X(X86_2B_HINTS2,   0x19) /* Prefetch and hint-nop block 2/8 */ \ +	X(X86_2B_HINTS3,   0x1A) /* Prefetch and hint-nop block 3/8 */ \ +	X(X86_2B_HINTS4,   0x1B) /* Prefetch and hint-nop block 4/8 */ \ +	X(X86_2B_HINTS5,   0x1C) /* Prefetch and hint-nop block 5/8 */ \ +	X(X86_2B_HINTS6,   0x1D) /* Prefetch and hint-nop block 6/8 */ \ +	X(X86_2B_HINTS7,   0x1E) /* Prefetch and hint-nop block 7/8 */ \ +	X(X86_2B_HINTS8,   0x1F) /* Prefetch and hint-nop block 8/8 */ \ +	X(X86_2B_CMOVO,    0x40) \ +	X(X86_2B_CMOVNO,   0x41) \ +	X(X86_2B_CMOVB,    0x42) /* AKA CMOVC */ \ +	X(X86_2B_CMOVNB,   0x43) /* AKA CMOVNC */ \ +	X(X86_2B_CMOVZ,    0x44) /* AKA CMOVE */ \ +	X(X86_2B_CMOVNZ,   0x45) /* AKA CMOVNE */ \ +	X(X86_2B_CMOVNA,   0x46) /* AKA CMOVBE */ \ +	X(X86_2B_CMOVA,    0x47) /* AKA CMOVNBE */ \ +	X(X86_2B_CMOVS,    0x48) \ +	X(X86_2B_CMOVNS,   0x49) \ +	X(X86_2B_CMOVP,    0x4A) \ +	X(X86_2B_CMOVNP,   0x4B) \ +	X(X86_2B_CMOVL,    0x4C) /* AKA CMOVNGE */ \ +	X(X86_2B_CMOVNL,   0x4D) /* AKA CMOVGE */ \ +	X(X86_2B_CMOVNG,   0x4E) /* AKA CMOVLE */ \ +	X(X86_2B_CMOVG,    0x4F) /* AKA CMOVNLE */ \ +	X(X86_2B_SETO,     0x90) \ +	X(X86_2B_SETNO,    0x91) \ +	X(X86_2B_SETB,     0x92) /* AKA SETC */ \ +	X(X86_2B_SETNB,    0x93) /* AKA SETNC */ \ +	X(X86_2B_SETZ,     0x94) /* AKA SETE */ \ +	X(X86_2B_SETNZ,    0x95) /* AKA SETNZ */ \ +	X(X86_2B_SETNA,    0x96) /* AKA SETBE */ \ +	X(X86_2B_SETA,     0x97) /* AKA SETNBE */ \ +	X(X86_2B_SETS,     0x98) \ +	X(X86_2B_SETNS,    0x99) \ +	X(X86_2B_SETP,     0x9A) \ +	X(X86_2B_SETNP,    0x9B) \ +	X(X86_2B_SETL,     0x9C) /* AKA SETNGE */ \ +	X(X86_2B_SETNL,    0x9D) /* AKA SETGE */ \ +	X(X86_2B_SETNG,    0x9E) /* AKA SETLE */ \ +	X(X86_2B_SETG,     0x9F) /* AKA SETNLE */ \ +	X(X86_2B_BTMR,     0xA3) \ +	X(X86_2B_SHLDMRCL, 0xA5) \ +	X(X86_2B_BTS,      0xAB) \ +	X(X86_2B_SHRDMRCL, 0xAD) \ +	X(X86_2B_MISC,     0xAE) /* Float env stuff, memory fences */ \ +	X(X86_2B_IMUL,     0xAF) \ +	X(X86_2B_CMPXCHG8, 0xB0) \ +	X(X86_2B_CMPXCHGW, 0xB1) \ +	X(X86_2B_MOVZX8,   0xB6) \ +	X(X86_2B_MOVZXW,   0xB7) \ +	X(X86_2B_POPCNT,   0xB8) \ +	X(X86_2B_BTCRM,    0xBB) \ +	X(X86_2B_BSF,      0xBC) \ +	X(X86_2B_BSR,      0xBD) \ +	X(X86_2B_MOVSX8,   0xBE) \ +	X(X86_2B_MOVSXW,   0xBF) \ +	X(X86_2B_XADDRM8,  0xC0) \ +	X(X86_2B_XADDRMW,  0xC1) \ +	/* NOTE: this one is actually a block with some VMX stuff too; it's only +	   CMPXCHG64 (CMPXCHG8B if you prefer) if MRM.reg = 1, but naming it this +	   way seemed more useful since it's what you'll see in normal userspace +	   programs, which is what we're interested in. */ \ +	X(X86_2B_CMPXCHG64, 0xC7) + +/* Second bytes of opcodes with a ModRM and a 1-byte immediate operand */ +#define X86_OPS_2BYTE_MRM_I8(X) \ +	X(X86_2B_SHLDMRI, 0xA4) \ +	X(X86_2B_SHRDMRI, 0xAC) \ +	X(X86_2B_BTXMI,   0xBA) /* BT/BTS/BTR/BTC depending on MRM.reg (4-7) */ + +#define X86_OPS_2BYTE(X) \ +	X86_OPS_2BYTE_NO(X) \ +	X86_OPS_2BYTE_IW(X) \ +	X86_OPS_2BYTE_MRM(X) \ +	X86_OPS_2BYTE_MRM_I8(X) + +#define _X86_ENUM(name, value) name = value, +enum { +	X86_PREFIXES(_X86_ENUM) +	X86_OPS_1BYTE(_X86_ENUM) +	X86_2BYTE = 0x0F, /* First byte of a 2- or 3-byte opcode */ +	X86_OPS_2BYTE(_X86_ENUM) +	X86_3BYTE1 = 0x38, /* One of the two second bytes of a three-byte opcode */ +	X86_3BYTE2 = 0x3A, /* The other second byte of a three-byte opcode */ +	X86_3DNOW  = 0x0F /* The second byte of a three-byte 3DNow! opcode */ +}; +#undef _X86_ENUM + +/* + * Returns the length of an instruction, or -1 if it's a "known unknown" or + * invalid instruction. Doesn't handle unknown unknowns: may explode or hang on + * arbitrary untrusted data. Also doesn't handle, among other things, 3DNow!, + * SSE, MMX, AVX, and such. Aims to be small and fast rather than comprehensive. + */ +int x86_len(const void *insn); + +/* Constructs a ModRM byte, assuming the parameters are all in range. */ +#define X86_MODRM(mod, reg, rm) (unsigned char)((mod) << 6 | (reg) << 3 | rm) + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80  | 
