From 80df5ff47c0149e002b16f3165100bd7792f99bb Mon Sep 17 00:00:00 2001 From: Alexis Engelke Date: Sun, 10 Jan 2021 16:50:27 +0100 Subject: [PATCH] instrs: Add reserved NOP/PREFETCH as weak opcodes --- instrs.txt | 26 ++++++++++++------------ parseinstrs.py | 48 ++++++++++++++++++++++++++++++++++----------- tests/test_decode.c | 2 ++ 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/instrs.txt b/instrs.txt index 3638300..e27b6bb 100644 --- a/instrs.txt +++ b/instrs.txt @@ -347,11 +347,8 @@ F2.0f09 NP - - - - WBINVD 0f0d/0m M MEM8 - - - PREFETCH 0f0d/1m M MEM8 - - - PREFETCHW 0f0d/2m M MEM8 - - - PREFETCHWT1 -0f0d/3m M MEM8 - - - RESERVED_PREFETCH -0f0d/4m M MEM8 - - - RESERVED_PREFETCH -0f0d/5m M MEM8 - - - RESERVED_PREFETCH -0f0d/6m M MEM8 - - - RESERVED_PREFETCH -0f0d/7m M MEM8 - - - RESERVED_PREFETCH +# All other slots are reserved, AMD maps them to /0 +*0f0d/m M MEM8 - - - RESERVED_PREFETCH ONLYAMD 0f0e NP - - - - FEMMS ONLYAMD # TODO: actually decode 3DNow! instructions. Given that 3DNow! no longer exists, # this is unlikely to happen, though. @@ -360,15 +357,16 @@ F2.0f09 NP - - - - WBINVD 0f18/1m M MEM8 - - - PREFETCHT0 0f18/2m M MEM8 - - - PREFETCHT1 0f18/3m M MEM8 - - - PREFETCHT2 -0f18/0r M GP - - - RESERVED_NOP -0f18/1r M GP - - - RESERVED_NOP -0f18/2r M GP - - - RESERVED_NOP -0f18/3r M GP - - - RESERVED_NOP -0f18/4 M GP - - - RESERVED_NOP -0f18/5 M GP - - - RESERVED_NOP -0f18/6 M GP - - - RESERVED_NOP -0f18/7 M GP - - - RESERVED_NOP -0f1f M GP - - - NOP +# Reserved NOPs are weak, they can be overridden by other instructions. +*0f18 MR GP GP - - RESERVED_NOP +*0f19 MR GP GP - - RESERVED_NOP +*0f1a MR GP GP - - RESERVED_NOP +*0f1b MR GP GP - - RESERVED_NOP +*0f1c MR GP GP - - RESERVED_NOP +*0f1d MR GP GP - - RESERVED_NOP +*0f1e MR GP GP - - RESERVED_NOP +*0f1f MR GP GP - - RESERVED_NOP +0f1f/0 M GP - - - NOP 0f20 MR GP CR - - MOV_CR DEF64 IGN66 0f21 MR GP DR - - MOV_DR DEF64 IGN66 0f22 RM CR GP - - MOV_CR DEF64 IGN66 diff --git a/parseinstrs.py b/parseinstrs.py index 63f8878..b9a59da 100644 --- a/parseinstrs.py +++ b/parseinstrs.py @@ -345,7 +345,7 @@ class Table: new_items = old.items[:idx] + (entry_name,) + old.items[idx+1:] self.data[name] = TrieEntry(old.kind, new_items, None) - def add_opcode(self, opcode, instr_encoding, root_idx=0): + def _walk_opcode(self, opcode, root_idx): name = "t{},{}".format(root_idx, format_opcode(opcode)) tn = "root%d"%root_idx @@ -361,12 +361,33 @@ class Table: if self.data[tn].kind != kind: raise Exception("{}, have {}, want {}".format( name, self.data[tn].kind, kind)) + return tn + def _add_encoding(self, instr_encoding): desc_idx = self.descs_map.get(instr_encoding) if desc_idx is None: desc_idx = self.descs_map[instr_encoding] = len(self.descs) self.descs.append(instr_encoding) - self._update_table(tn, opcode[-1][1], name, TrieEntry.instr(desc_idx)) + return TrieEntry.instr(desc_idx) + + def add_opcode(self, opcode, instr_encoding, root_idx=0): + name = "t{},{}".format(root_idx, format_opcode(opcode)) + tn = self._walk_opcode(opcode, root_idx) + desc_entry = self._add_encoding(instr_encoding) + self._update_table(tn, opcode[-1][1], name, desc_entry) + + def fill_free(self, opcode, instr_encoding, root_idx=0): + desc_entry = self._add_encoding(instr_encoding) + tn = self._walk_opcode(opcode, root_idx) + queue = [(tn, opcode[-1][1])] + while queue: + tn, idx = queue.pop() + entry = self.data[tn].items[idx] + if not entry: + self._update_table(tn, idx, f"tn,*{idx:x}", desc_entry) + else: + for i in range(len(self.data[entry].items)): + queue.append((entry, i)) def deduplicate(self): parents = defaultdict(set) @@ -468,10 +489,8 @@ template = """// Auto-generated file -- do not modify! def encode_table(entries): mnemonics = defaultdict(list) mnemonics["FE_NOP"].append(("NP", 0, 0, "0x90")) - for opcode, desc in entries: - if desc.mnemonic[:9] == "RESERVED_": - continue - if "ONLY32" in desc.flags: + for weak, opcode, desc in entries: + if weak or "ONLY32" in desc.flags: continue opsizes = {8} if "SIZE_8" in desc.flags else {16, 32, 64} @@ -629,30 +648,37 @@ if __name__ == "__main__": entries = [] for line in args.table.read().splitlines(): if not line or line[0] == "#": continue + line, weak = (line, False) if line[0] != "*" else (line[1:], True) opcode_string, desc_string = tuple(line.split(maxsplit=1)) opcode, desc = Opcode.parse(opcode_string), InstrDesc.parse(desc_string) if "UNDOC" not in desc.flags or args.with_undoc: - entries.append((opcode, desc)) + entries.append((weak, opcode, desc)) - mnemonics = sorted({desc.mnemonic for _, desc in entries}) + mnemonics = sorted({desc.mnemonic for _, _, desc in entries}) decode_mnems_lines = [f"FD_MNEMONIC({m},{i})\n" for i, m in enumerate(mnemonics)] args.decode_mnems.write("".join(decode_mnems_lines)) modes = [32, 64] table = Table(root_count=len(args.modes)) - for opcode, desc in entries: + weak_opcodes = [] + for weak, opcode, desc in entries: for i, mode in enumerate(args.modes): if "ONLY%d"%(96-mode) not in desc.flags: ign66 = opcode.prefix in ("NP", "66", "F2", "F3") for opcode_path in opcode.for_trie(): - table.add_opcode(opcode_path, desc.encode(ign66), i) + if weak: + weak_opcodes.append((opcode_path, desc.encode(ign66), i)) + else: + table.add_opcode(opcode_path, desc.encode(ign66), i) + for k in weak_opcodes: + table.fill_free(*k) table.deduplicate() table_data, annotations, root_offsets, descs = table.compile() mnemonics_intel = [m.replace("SSE_", "").replace("MMX_", "") - .replace("MOVABS", "MOV") + .replace("MOVABS", "MOV").replace("RESERVED_", "") .replace("JMPF", "JMP FAR").replace("CALLF", "CALL FAR") .replace("_S2G", "").replace("_G2S", "") .replace("_CR", "").replace("_DR", "") diff --git a/tests/test_decode.c b/tests/test_decode.c index 5db2dae..ef95550 100644 --- a/tests/test_decode.c +++ b/tests/test_decode.c @@ -111,6 +111,8 @@ main(int argc, char** argv) TEST64("\x44\x8c\xf8", "UD"); // no segment register 7 TEST("\x8e\xc0", "mov es, ax"); TEST("\x8e\xc8", "UD"); // No mov cs, eax + TEST("\x0f\x1e\xc0", "nop eax, eax"); // reserved nop + TEST("\x0f\x1e\x04\x25\x01\x00\x00\x00", "nop dword ptr [0x1], eax"); // reserved nop TEST("\xd8\xc1", "fadd st(0), st(1)"); TEST("\xdc\xc1", "fadd st(1), st(0)"); TEST64("\x41\xd8\xc1", "fadd st(0), st(1)"); // REX.B ignored