| # udis86 - scripts/ud_opcode.py |
| # |
| # Copyright (c) 2009 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 UdOpcodeTables: |
| |
| TableInfo = { |
| 'opctbl' : { 'name' : 'UD_TAB__OPC_TABLE', 'size' : 256 }, |
| '/sse' : { 'name' : 'UD_TAB__OPC_SSE', 'size' : 4 }, |
| '/reg' : { 'name' : 'UD_TAB__OPC_REG', 'size' : 8 }, |
| '/rm' : { 'name' : 'UD_TAB__OPC_RM', 'size' : 8 }, |
| '/mod' : { 'name' : 'UD_TAB__OPC_MOD', 'size' : 2 }, |
| '/m' : { 'name' : 'UD_TAB__OPC_MODE', 'size' : 3 }, |
| '/x87' : { 'name' : 'UD_TAB__OPC_X87', 'size' : 64 }, |
| '/a' : { 'name' : 'UD_TAB__OPC_ASIZE', 'size' : 3 }, |
| '/o' : { 'name' : 'UD_TAB__OPC_OSIZE', 'size' : 3 }, |
| '/3dnow' : { 'name' : 'UD_TAB__OPC_3DNOW', 'size' : 256 }, |
| 'vendor' : { 'name' : 'UD_TAB__OPC_VENDOR', 'size' : 3 }, |
| } |
| |
| OpcodeTable0 = { |
| 'type' : 'opctbl', |
| 'entries' : {}, |
| 'meta' : 'table0' |
| } |
| |
| OpcExtIndex = { |
| |
| # ssef2, ssef3, sse66 |
| 'sse': { |
| 'none' : '00', |
| 'f2' : '01', |
| 'f3' : '02', |
| '66' : '03' |
| }, |
| |
| # /mod= |
| 'mod': { |
| '!11' : '00', |
| '11' : '01' |
| }, |
| |
| # /m=, /o=, /a= |
| 'mode': { |
| '16' : '00', |
| '32' : '01', |
| '64' : '02' |
| }, |
| |
| 'vendor' : { |
| 'amd' : '00', |
| 'intel' : '01', |
| 'any' : '02' |
| } |
| } |
| |
| InsnTable = [] |
| MnemonicsTable = [] |
| |
| ThreeDNowTable = {} |
| |
| def sizeOfTable( self, t ): |
| return self.TableInfo[ t ][ 'size' ] |
| |
| def nameOfTable( self, t ): |
| return self.TableInfo[ t ][ 'name' ] |
| |
| # |
| # Updates a table entry: If the entry doesn't exist |
| # it will create the entry, otherwise, it will walk |
| # while validating the path. |
| # |
| def updateTable( self, table, index, type, meta ): |
| if not index in table[ 'entries' ]: |
| table[ 'entries' ][ index ] = { 'type' : type, 'entries' : {}, 'meta' : meta } |
| if table[ 'entries' ][ index ][ 'type' ] != type: |
| raise NameError( "error: violation in opcode mapping (overwrite) %s with %s." % |
| ( table[ 'entries' ][ index ][ 'type' ], type) ) |
| return table[ 'entries' ][ index ] |
| |
| class Insn: |
| """An abstract type representing an instruction in the opcode map. |
| """ |
| |
| # A mapping of opcode extensions to their representational |
| # values used in the opcode map. |
| OpcExtMap = { |
| '/rm' : lambda v: "%02x" % int(v, 16), |
| '/x87' : lambda v: "%02x" % int(v, 16), |
| '/3dnow' : lambda v: "%02x" % int(v, 16), |
| '/reg' : lambda v: "%02x" % int(v, 16), |
| # modrm.mod |
| # (!11, 11) => (00, 01) |
| '/mod' : lambda v: '00' if v == '!11' else '01', |
| # Mode extensions: |
| # (16, 32, 64) => (00, 01, 02) |
| '/o' : lambda v: "%02x" % (int(v) / 32), |
| '/a' : lambda v: "%02x" % (int(v) / 32), |
| '/m' : lambda v: "%02x" % (int(v) / 32), |
| '/sse' : lambda v: UdOpcodeTables.OpcExtIndex['sse'][v] |
| } |
| |
| def __init__(self, prefixes, mnemonic, opcodes, operands, vendor): |
| self.opcodes = opcodes |
| self.prefixes = prefixes |
| self.mnemonic = mnemonic |
| self.operands = operands |
| self.vendor = vendor |
| self.opcext = {} |
| |
| ssePrefix = None |
| if self.opcodes[0] in ('ssef2', 'ssef3', 'sse66'): |
| ssePrefix = self.opcodes[0][3:] |
| self.opcodes.pop(0) |
| |
| # do some preliminary decoding of the instruction type |
| # 1byte, 2byte or 3byte instruction? |
| self.nByteInsn = 1 |
| if self.opcodes[0] == '0f': # 2byte |
| # 2+ byte opcodes are always disambiguated by an |
| # sse prefix, unless it is a 3d now instruction |
| # which is 0f 0f ... |
| if self.opcodes[1] != '0f' and ssePrefix is None: |
| ssePrefix = 'none' |
| if self.opcodes[1] in ('38', '3a'): # 3byte |
| self.nByteInsn = 3 |
| else: |
| self.nByteInsn = 2 |
| |
| # The opcode that indexes into the opcode table. |
| self.opcode = self.opcodes[self.nByteInsn - 1] |
| |
| # Record opcode extensions |
| for opcode in self.opcodes[self.nByteInsn:]: |
| arg, val = opcode.split('=') |
| self.opcext[arg] = self.OpcExtMap[arg](val) |
| |
| # Record sse extension: the reason sse extension is handled |
| # separately is that historically sse was handled as a first |
| # class opcode, not as an extension. Now that sse is handled |
| # as an extension, we do the manual conversion here, as opposed |
| # to modifying the opcode xml file. |
| if ssePrefix is not None: |
| self.opcext['/sse'] = self.OpcExtMap['/sse'](ssePrefix) |
| |
| def parse(self, table, insn): |
| index = insn.opcodes[0]; |
| if insn.nByteInsn > 1: |
| assert index == '0f' |
| table = self.updateTable(table, index, 'opctbl', '0f') |
| index = insn.opcodes[1] |
| |
| if insn.nByteInsn == 3: |
| table = self.updateTable(table, index, 'opctbl', index) |
| index = insn.opcodes[2] |
| |
| # Walk down the tree, create levels as needed, for opcode |
| # 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'): |
| if ext in insn.opcext: |
| table = self.updateTable(table, index, ext, ext) |
| index = insn.opcext[ext] |
| |
| # additional table for disambiguating vendor |
| if len(insn.vendor): |
| table = self.updateTable(table, index, 'vendor', insn.vendor) |
| index = self.OpcExtIndex['vendor'][insn.vendor] |
| |
| # make leaf node entries |
| leaf = self.updateTable(table, index, 'insn', '') |
| |
| leaf['mnemonic'] = insn.mnemonic |
| leaf['prefixes'] = insn.prefixes |
| leaf['operands'] = insn.operands |
| |
| # add instruction to linear table of instruction forms |
| self.InsnTable.append({ 'prefixes' : insn.prefixes, |
| 'mnemonic' : insn.mnemonic, |
| 'operands' : insn.operands }) |
| |
| # add mnemonic to mnemonic table |
| if not insn.mnemonic in self.MnemonicsTable: |
| self.MnemonicsTable.append(insn.mnemonic) |
| |
| |
| # Adds an instruction definition to the opcode tables |
| def addInsnDef( self, prefixes, mnemonic, opcodes, operands, vendor ): |
| insn = self.Insn(prefixes=prefixes, |
| mnemonic=mnemonic, |
| opcodes=opcodes, |
| operands=operands, |
| vendor=vendor) |
| self.parse(self.OpcodeTable0, insn) |
| |
| 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, '' ) |