Browse Source

Merge pull request #1958 from unamer/master

Support dumping bytecode opcodes in dump_bytecode.py
pull/1983/head
Sami Vaarala 6 years ago
committed by GitHub
parent
commit
1412b4bec7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      AUTHORS.rst
  2. 180
      tools/dump_bytecode.py

1
AUTHORS.rst

@ -95,6 +95,7 @@ bugs, provided ideas, etc; roughly in order of appearance):
* Neil Kolban (https://github.com/nkolban)
* Wilhelm Wanecek (https://github.com/wanecek)
* Andrew Janke (https://github.com/apjanke)
* Unamer (https://github.com/unamer)
If you are accidentally missing from this list, send me an e-mail
(``sami.vaarala@iki.fi``) and I'll fix the omission.

180
tools/dump_bytecode.py

@ -3,19 +3,21 @@
# Utility to dump bytecode into a human readable form.
#
import os
import sys
import struct
import optparse
import yaml
from os.path import isfile
def decode_string(buf, off):
strlen, = struct.unpack('>L', buf[off:off+4])
strlen, = struct.unpack('>L', buf[off:off + 4])
off += 4
strdata = buf[off:off+strlen]
strdata = buf[off:off + strlen]
off += strlen
return off, strdata
def sanitize_string(val):
# Don't try to UTF-8 decode, just escape non-printable ASCII.
def f(c):
@ -23,46 +25,173 @@ def sanitize_string(val):
return '\\x%02x' % ord(c)
else:
return c
return "'" + ''.join(map(f, val)) + "'"
def decode_sanitize_string(buf, off):
off, val = decode_string(buf, off)
return off, sanitize_string(val)
def dump_ins(ins, x):
global ops
if not ops:
return ''
pc = x / 4
args = []
op = ops[ins & 0xff]
comments = []
if 'args' in op:
for j in xrange(len(op['args'])):
A = (ins >> 8) & 0xff
B = (ins >> 16) & 0xff
C = (ins >> 24) & 0xff
BC = (ins >> 16) & 0xffff
ABC = (ins >> 8) & 0xffffff
Bconst = ins & 0x1
Cconst = ins & 0x2
if op['args'][j] == 'A_R':
args.append('r' + str(A))
elif op['args'][j] == 'A_RI':
args.append('r' + str(A) + '(indirect)')
elif op['args'][j] == 'A_C':
args.append('c' + str(A))
elif op['args'][j] == 'A_H':
args.append(hex(A))
elif op['args'][j] == 'A_I':
args.append(str(A))
elif op['args'][j] == 'A_B':
args.append('true' if A else 'false')
elif op['args'][j] == 'B_RC':
args.append('c' if Bconst else 'r' + str(B))
elif op['args'][j] == 'B_R':
args.append('r' + str(B))
elif op['args'][j] == 'B_RI':
args.append('r' + str(B) + '(indirect)')
elif op['args'][j] == 'B_C':
args.append('c' + str(B))
elif op['args'][j] == 'B_H':
args.append(hex(B))
elif op['args'][j] == 'B_I':
args.append(str(B))
elif op['args'][j] == 'C_RC':
args.append('c' if Cconst else 'r' + str(C))
elif op['args'][j] == 'C_R':
args.append('r' + str(C))
elif op['args'][j] == 'C_RI':
args.append('r' + str(C) + '(indirect)')
elif op['args'][j] == 'C_C':
args.append('c' + str(C))
elif op['args'][j] == 'C_H':
args.append(hex(C))
elif op['args'][j] == 'C_I':
args.append(str(C))
elif op['args'][j] == 'BC_R':
args.append('r' + str(BC))
elif op['args'][j] == 'BC_C':
args.append('c' + str(BC))
elif op['args'][j] == 'BC_H':
args.append(hex(BC))
elif op['args'][j] == 'BC_I':
args.append(str(BC))
elif op['args'][j] == 'ABC_H':
args.append(hex(ABC))
elif op['args'][j] == 'ABC_I':
args.append(str(ABC))
elif op['args'][j] == 'BC_LDINT':
args.append(hex(BC - (1 << 15)))
elif op['args'][j] == 'BC_LDINTX':
args.append(hex(BC))
elif op['args'][j] == 'ABC_JUMP':
pc_add = ABC - (1 << 23) + 1
pc_dst = pc + pc_add
args.append(str(pc_dst) + ' (' + ('+' if pc_add >= 0 else '') + str(pc_add) + ')')
else:
args.append('?')
if 'flags' in op:
for f in op['flags']:
if ins & f['mask']:
comments.append(f['name'])
if len(args):
res = '{} {}'.format(op['name'], ', '.join(args))
else:
res = op['name']
if len(comments):
res = '{} ; {}'.format(res, ', '.join(comments))
return res
def dump_function(buf, off, ind):
count_inst, count_const, count_funcs = struct.unpack('>LLL', buf[off:off+12])
count_inst, count_const, count_funcs = struct.unpack('>LLL', buf[off:off + 12])
off += 12
print('%sInstructions: %d' % (ind, count_inst))
print('%sConstants: %d' % (ind, count_const))
print('%sInner functions: %d' % (ind, count_funcs))
# Line numbers present, assuming debugger support; otherwise 0.
nregs, nargs, start_line, end_line = struct.unpack('>HHLL', buf[off:off+12])
nregs, nargs, start_line, end_line = struct.unpack('>HHLL', buf[off:off + 12])
off += 12
print('%sNregs: %d' % (ind, nregs))
print('%sNargs: %d' % (ind, nargs))
print('%sStart line number: %d' % (ind, start_line))
print('%sEnd line number: %d' % (ind, end_line))
compfunc_flags, = struct.unpack('>L', buf[off:off+4])
compfunc_flags, = struct.unpack('>L', buf[off:off + 4])
off += 4
print('%sduk_hcompiledfunction flags: 0x%08x' % (ind, compfunc_flags))
for i in xrange(count_inst):
ins, = struct.unpack('>L', buf[off:off+4])
ins, = struct.unpack('>L', buf[off:off + 4])
off += 4
print('%s %06d: %08lx' % (ind, i, ins))
code = dump_ins(ins, i)
print('%s %06d: %08lx %s' % (ind, i, ins, code))
print('%sConstants:' % ind)
for i in xrange(count_const):
const_type, = struct.unpack('B', buf[off:off+1])
const_type, = struct.unpack('B', buf[off:off + 1])
off += 1
if const_type == 0x00:
off, strdata = decode_sanitize_string(buf, off)
print('%s %06d: %s' % (ind, i, strdata))
elif const_type == 0x01:
num, = struct.unpack('>d', buf[off:off+8])
num, = struct.unpack('>d', buf[off:off + 8])
off += 8
print('%s %06d: %f' % (ind, i, num))
else:
@ -72,7 +201,7 @@ def dump_function(buf, off, ind):
print('%sInner function %d:' % (ind, i))
off = dump_function(buf, off, ind + ' ')
val, = struct.unpack('>L', buf[off:off+4])
val, = struct.unpack('>L', buf[off:off + 4])
off += 4
print('%s.length: %d' % (ind, val))
off, val = decode_sanitize_string(buf, off)
@ -87,11 +216,11 @@ def dump_function(buf, off, ind):
if name == '':
break
name = sanitize_string(name)
val, = struct.unpack('>L', buf[off:off+4])
val, = struct.unpack('>L', buf[off:off + 4])
off += 4
print('%s_Varmap[%s] = %d' % (ind, name, val))
num_formals, = struct.unpack('>L', buf[off:off+4])
num_formals, = struct.unpack('>L', buf[off:off + 4])
off += 4
if num_formals != 0xffffffff:
print('%s_Formals: %d formal arguments' % (ind, num_formals))
@ -104,8 +233,9 @@ def dump_function(buf, off, ind):
return off
def dump_bytecode(buf, off, ind):
sig, = struct.unpack('B', buf[off:off+1])
sig, = struct.unpack('B', buf[off:off + 1])
print('%sSignature byte: 0x%02x' % (ind, sig))
off += 1
if sig == 0xff:
@ -117,9 +247,26 @@ def dump_bytecode(buf, off, ind):
return off
def main():
global ops
ops = None
yamlpath = None
if isfile('duk_opcodes.yaml'):
yamlpath = 'duk_opcodes.yaml'
elif isfile('../debugger/duk_opcodes.yaml'):
yamlpath = '../debugger/duk_opcodes.yaml'
else:
print('WARN: duk_opcodes.yaml NOT found! Unable to show opcodes!')
if yamlpath:
y = yaml.load(open(yamlpath))
ops = y['opcodes']
parser = optparse.OptionParser()
parser.add_option('--hex-decode', dest='hex_decode', default=False, action='store_true', help='Input file is ASCII hex encoded, decode before dump')
parser.add_option('--hex-decode', dest='hex_decode', default=False, action='store_true',
help='Input file is ASCII hex encoded, decode before dump')
(opts, args) = parser.parse_args()
with open(args[0], 'rb') as f:
@ -129,5 +276,6 @@ def main():
d = d.decode('hex')
dump_bytecode(d, 0, '')
if __name__ == '__main__':
main()

Loading…
Cancel
Save