mirror of
https://github.com/ModOrganizer2/udis86.git
synced 2026-07-27 14:05:57 -07:00
393 lines
13 KiB
Python
393 lines
13 KiB
Python
# udis86 - scripts/ud_opcode.py
|
|
#
|
|
# Copyright (c) 2009, 2013 Vivek Thampi
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without modification,
|
|
# are permitted provided that the following conditions are met:
|
|
#
|
|
# * Redistributions of source code must retain the above copyright notice,
|
|
# this list of conditions and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
# this list of conditions and the following disclaimer in the documentation
|
|
# and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
class UdInsnDef:
|
|
"""An x86 instruction definition
|
|
"""
|
|
def __init__(self, id, **insnDef):
|
|
self.id = id
|
|
self.mnemonic = insnDef['mnemonic']
|
|
self._prefixes = insnDef['prefixes']
|
|
self._opcodes = insnDef['opcodes']
|
|
self.operands = insnDef['operands']
|
|
self._cpuid = insnDef['cpuid']
|
|
|
|
def __str__(self):
|
|
return self.mnemonic + " " + ', '.join(self.operands) + ' '.join(self._opcodes)
|
|
|
|
|
|
class UdOpcodeTable:
|
|
"""A single table of instruction definitions, indexed by
|
|
a decode field.
|
|
"""
|
|
|
|
class CollisionError(Exception):
|
|
pass
|
|
|
|
class IndexError(Exception):
|
|
"""Invalid Index Error"""
|
|
pass
|
|
|
|
@classmethod
|
|
def vendor2idx(cls, v):
|
|
return (0 if v == 'amd'
|
|
else (1 if v == 'intel'
|
|
else 2))
|
|
|
|
@classmethod
|
|
def vex2idx(cls, v):
|
|
vexOpcExtMap = {
|
|
'none' : 0x0,
|
|
'0f' : 0x1,
|
|
'0f38' : 0x2,
|
|
'0f3a' : 0x3,
|
|
'f2' : 0x4,
|
|
'f2_0f' : 0x5,
|
|
'f2_0f38' : 0x6,
|
|
'f2_0f3a' : 0x7,
|
|
'f3' : 0x8,
|
|
'f3_0f' : 0x9,
|
|
'f3_0f38' : 0xa,
|
|
'f3_0f3a' : 0xb,
|
|
'66' : 0xc,
|
|
'66_0f' : 0xd,
|
|
'66_0f38' : 0xe,
|
|
'66_0f3a' : 0xf
|
|
}
|
|
return vexOpcExtMap[v]
|
|
|
|
|
|
# A mapping of opcode extensions to their representational
|
|
# values used in the opcode map.
|
|
OpcExtMap = {
|
|
'/rm' : lambda v: int(v, 16),
|
|
'/x87' : lambda v: int(v, 16),
|
|
'/3dnow' : lambda v: int(v, 16),
|
|
'/reg' : lambda v: int(v, 16),
|
|
# modrm.mod
|
|
# (!11, 11) => (00b, 01b)
|
|
'/mod' : lambda v: 0 if v == '!11' else 1,
|
|
# Mode extensions:
|
|
# (16, 32, 64) => (00, 01, 02)
|
|
'/o' : lambda v: (int(v) / 32),
|
|
'/a' : lambda v: (int(v) / 32),
|
|
# Disassembly mode
|
|
# (!64, 64) => (00b, 01b)
|
|
'/m' : lambda v: 1 if v == '64' else 0,
|
|
# SSE
|
|
# none => 0
|
|
# f2 => 1
|
|
# f3 => 2
|
|
# 66 => 3
|
|
'/sse' : lambda v: (0 if v == 'none'
|
|
else (((int(v, 16) & 0xf) + 1) / 2)),
|
|
# AVX
|
|
'/vex' : lambda v: UdOpcodeTable.vex2idx(v),
|
|
# Vendor
|
|
'/vendor': lambda v: UdOpcodeTable.vendor2idx(v)
|
|
}
|
|
|
|
|
|
_TableInfo = {
|
|
'opctbl' : { 'label' : 'UD_TAB__OPC_TABLE', 'size' : 256 },
|
|
'/sse' : { 'label' : 'UD_TAB__OPC_SSE', 'size' : 4 },
|
|
'/reg' : { 'label' : 'UD_TAB__OPC_REG', 'size' : 8 },
|
|
'/rm' : { 'label' : 'UD_TAB__OPC_RM', 'size' : 8 },
|
|
'/mod' : { 'label' : 'UD_TAB__OPC_MOD', 'size' : 2 },
|
|
'/m' : { 'label' : 'UD_TAB__OPC_MODE', 'size' : 2 },
|
|
'/x87' : { 'label' : 'UD_TAB__OPC_X87', 'size' : 64 },
|
|
'/a' : { 'label' : 'UD_TAB__OPC_ASIZE', 'size' : 3 },
|
|
'/o' : { 'label' : 'UD_TAB__OPC_OSIZE', 'size' : 3 },
|
|
'/3dnow' : { 'label' : 'UD_TAB__OPC_3DNOW', 'size' : 256 },
|
|
'/vendor' : { 'label' : 'UD_TAB__OPC_VENDOR', 'size' : 3 },
|
|
'/vex' : { 'label' : 'UD_TAB__OPC_VEX', 'size' : 16 },
|
|
}
|
|
|
|
|
|
def __init__(self, id, typ):
|
|
assert typ in self._TableInfo
|
|
self.id = id
|
|
self._typ = typ
|
|
self._entries = {}
|
|
|
|
|
|
def size(self):
|
|
return self._TableInfo[self._typ]['size']
|
|
|
|
|
|
def label(self):
|
|
return self._TableInfo[self._typ]['label']
|
|
|
|
|
|
def name(self):
|
|
return "ud_itab__" + self._id
|
|
|
|
def __str__(self):
|
|
return "table-%s" % self._typ
|
|
|
|
|
|
def add(self, opc, obj):
|
|
typ = UdOpcodeTable.getOpcodeTyp(opc)
|
|
idx = UdOpcodeTable.getOpcodeIdx(opc)
|
|
if self._typ != typ or idx in self._entries:
|
|
raise CollisionError()
|
|
self._entries[idx] = obj
|
|
|
|
|
|
def lookup(self, opc):
|
|
typ = UdOpcodeTable.getOpcodeTyp(opc)
|
|
idx = UdOpcodeTable.getOpcodeIdx(opc)
|
|
if self._typ != typ:
|
|
raise CollisionError()
|
|
return self._entries.get(idx, None)
|
|
|
|
|
|
def entryAt(self, index):
|
|
"""Returns the entry at a given index of the table,
|
|
None if there is none. Raises an exception if the
|
|
index is out of bounds.
|
|
"""
|
|
if index < self.size():
|
|
return self._entries.get(index, None)
|
|
raise self.IndexError("index out of bounds: %s" % index)
|
|
|
|
|
|
@classmethod
|
|
def getOpcodeTyp(cls, opc):
|
|
if opc.startswith('/'):
|
|
return opc.split('=')[0]
|
|
else:
|
|
return 'opctbl'
|
|
|
|
|
|
@classmethod
|
|
def getOpcodeIdx(cls, opc):
|
|
if opc.startswith('/'):
|
|
typ, v = opc.split('=')
|
|
return cls.OpcExtMap[typ](v)
|
|
else:
|
|
# plain opctbl opcode
|
|
return int(opc, 16)
|
|
|
|
|
|
class UdOpcodeTables:
|
|
|
|
"""opcode from the udis86 optable.
|
|
"""
|
|
|
|
def __init__(self):
|
|
# Root Table --
|
|
# The root table is always a 256 entry opctbl, indexed
|
|
# by a plain opcode byte
|
|
self._tableID = 0
|
|
self._insnID = 0
|
|
self._tables = []
|
|
self._insns = []
|
|
self._mnemonics = {}
|
|
self.root = self.newTable('opctbl')
|
|
|
|
|
|
def newTable(self, typ):
|
|
tbl = UdOpcodeTable(self._tableID, typ)
|
|
self._tables.append(tbl)
|
|
self._tableID += 1
|
|
return tbl
|
|
|
|
|
|
def mkTrie(self, opcStr, obj):
|
|
if len(opcStr) == 0:
|
|
return obj
|
|
opc = opcStr[0]
|
|
tbl = self.newTable(UdOpcodeTable.getOpcodeTyp(opc))
|
|
tbl.add(opc, self.mkTrie(opcStr[1:], obj))
|
|
return tbl
|
|
|
|
|
|
def walk(self, tbl, opcodes):
|
|
opc = opcStr[0]
|
|
e = tbl.lookup(opc)
|
|
if e is None:
|
|
return None
|
|
elif isinstance(e, UdOpcodeTable) and len(opcStr[1:]):
|
|
return self.walk(e, opcStr[1:])
|
|
return e
|
|
|
|
|
|
def map(self, tbl, opcStr, obj):
|
|
opc = opcStr[0]
|
|
e = tbl.lookup(opc)
|
|
if e is None:
|
|
tbl.add(opc, self.mkTrie(opcStr[1:], obj))
|
|
else:
|
|
self.map(e, opcStr[1:], obj)
|
|
|
|
|
|
def addInsn(self, **insnDef):
|
|
|
|
# Canonicalize opcode list
|
|
opcexts = insnDef['opcexts']
|
|
opcodes = list(insnDef['opcodes'])
|
|
|
|
# Re-order vex
|
|
if '/vex' in opcexts:
|
|
assert opcodes[0] == 'c4' or opcodes[0] == 'c5'
|
|
opcodes.insert(1, '/vex=' + opcexts['/vex'])
|
|
|
|
# Add extensions. The order is important, and determines how
|
|
# well the opcode table is packed. Also note, /sse must be
|
|
# before /o, because /sse may consume operand size prefix
|
|
# affect the outcome of /o.
|
|
for ext in ('/mod', '/x87', '/reg', '/rm', '/sse', '/o', '/a', '/m',
|
|
'/3dnow', '/vendor'):
|
|
if ext in opcexts:
|
|
opcodes.append(ext + '=' + opcexts[ext])
|
|
|
|
insn = UdInsnDef(self._insnID,
|
|
mnemonic = insnDef['mnemonic'],
|
|
prefixes = insnDef['prefixes'],
|
|
operands = insnDef['operands'],
|
|
opcodes = opcodes,
|
|
cpuid = insnDef['cpuid'])
|
|
self._insns.append(insn)
|
|
self._insnID += 1
|
|
|
|
# add to lookup by mnemonic structure
|
|
if insn.mnemonic not in self._mnemonics:
|
|
self._mnemonics[insn.mnemonic] = [ insn ]
|
|
else:
|
|
self._mnemonics[insn.mnemonic].append(insn)
|
|
|
|
try:
|
|
self.map(self.root, opcodes, insn)
|
|
except UdOpcodeTable.CollisionError as e:
|
|
# TODO
|
|
raise
|
|
except Exception as e:
|
|
print e, insnDef['mnemonic'], opcodes, opcexts
|
|
raise
|
|
|
|
|
|
def addInsnDef(self, insnDef):
|
|
opcodes = []
|
|
opcexts = {}
|
|
|
|
# pack plain opcodes first, and collect opcode
|
|
# extensions
|
|
for opc in insnDef['opcodes']:
|
|
if not opc.startswith('/'):
|
|
opcodes.append(opc)
|
|
else:
|
|
e, v = opc.split('=')
|
|
opcexts[e] = v
|
|
|
|
# artificially add a /sse=none for 2 byte opcodes
|
|
if (opcodes[0] == '0f' and opcodes[1] != '0f' and
|
|
'/sse' not in opcexts):
|
|
opcexts['/sse'] = 'none'
|
|
|
|
# treat vendor as an opcode extension
|
|
if len(insnDef['vendor']):
|
|
opcexts['/vendor'] = insnDef['vendor']
|
|
|
|
if 'avx' in insnDef['cpuid'] and '/sse' in opcexts:
|
|
sseoprs = []
|
|
for opr in insnDef['operands']:
|
|
if opr not in ( 'H', ):
|
|
sseoprs.append(opr)
|
|
|
|
self.addInsn(mnemonic=insnDef['mnemonic'],
|
|
prefixes=insnDef['prefixes'],
|
|
opcodes=opcodes,
|
|
opcexts=opcexts,
|
|
operands=sseoprs,
|
|
cpuid=insnDef['cpuid'])
|
|
|
|
# This is a legacy sse style instruction definition
|
|
# for an AVX instruction
|
|
vexopcs = ['c4']
|
|
vexopcexts = dict([(e, opcexts[e]) for e in opcexts if e != '/sse'])
|
|
vexopcexts['/vex'] = opcexts['/sse'] + '_' + '0f'
|
|
if opcodes[1] == '38' or opcodes[1] == '3a':
|
|
vexopcexts['/vex'] += opcodes[1]
|
|
vexopcs.extend(opcodes[2:])
|
|
else:
|
|
vexopcs.extend(opcodes[1:])
|
|
|
|
self.addInsn(mnemonic='v' + insnDef['mnemonic'],
|
|
prefixes=insnDef['prefixes'],
|
|
opcodes=vexopcs,
|
|
opcexts=vexopcexts,
|
|
operands=insnDef['operands'],
|
|
cpuid=insnDef['cpuid'])
|
|
else:
|
|
self.addInsn(mnemonic=insnDef['mnemonic'],
|
|
prefixes=insnDef['prefixes'],
|
|
opcodes=opcodes,
|
|
opcexts=opcexts,
|
|
operands=insnDef['operands'],
|
|
cpuid=insnDef['cpuid'])
|
|
|
|
def getInsnList(self):
|
|
"""Returns an ordered (by id) list of instructions
|
|
"""
|
|
return self._insns
|
|
|
|
|
|
def getTableList(self):
|
|
"""Returns an ordered (by id) list of tables
|
|
"""
|
|
return self._tables
|
|
|
|
|
|
def getMnemonicsList(self):
|
|
"""Returns a sorted list of mnemonics
|
|
"""
|
|
return sorted(self._mnemonics.keys())
|
|
|
|
|
|
def print_table( self, table, pfxs='' ):
|
|
print("%s |" % pfxs)
|
|
keys = table[ 'entries' ].keys()
|
|
if ( len( keys ) ):
|
|
keys.sort()
|
|
for idx in keys:
|
|
e = table[ 'entries' ][ idx ]
|
|
if e[ 'type' ] == 'insn':
|
|
print("%s |-<%s>" % ( pfxs, idx )),
|
|
print("%s %s" % ( e[ 'mnemonic' ], ' '.join( e[ 'operands'] ) ))
|
|
else:
|
|
print("%s |-<%s> %s" % ( pfxs, idx, e['type'] ))
|
|
self.print_table( e, pfxs + ' |' )
|
|
|
|
def print_tree( self ):
|
|
self.print_table( self.OpcodeTable0, '' )
|
|
|
|
|
|
def printStats(self):
|
|
print("stats: ")
|
|
print(" Num tables = %d" % len(self.getTableList()))
|
|
print(" Num insnDefs = %d" % len(self.getInsnList()))
|