|
|
@ -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() |
|
|
|