-
Notifications
You must be signed in to change notification settings - Fork 0
/
deco.py
executable file
·214 lines (199 loc) · 8.86 KB
/
deco.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser(description='Decompile a list of functions.')
parser.add_argument('file', help='the input file')
parser.add_argument('-a', '--print-assembly', action='store_true', help='print the assembly insns')
parser.add_argument('-d', '--print-domtree', action='store_true', help='print dominator tree and complete register state')
parser.add_argument('-D', '--debug', action='store_true', help='print tree construction debug')
args = parser.parse_args()
from chuj.data.bindata import BinData
from chuj.dis.isa.falcon import FalconIsa
from chuj.deco.forest import DecoForest
from chuj.deco.machine import MachineSegment, MachineBlock, MachineBaseBlock, MachineReturn
from chuj.deco.ir import IrGoto, IrCond, IrJump, IrCall, IrReturn, IrHalt
from chuj.deco.struct import StructFunc
forest = DecoForest(debug=args.debug)
data = None
base = None
last_func = None
isa = FalconIsa()
with open(args.file) as f:
for l in f:
l, _, _ = l.partition('#')
p = l.split()
if not p:
continue
cmd = p[0]
if data is None:
if cmd != 'file':
raise ValueError('Expecting a file command')
if len(p) not in {2, 3, 5}:
raise ValueError('File command needs 1, 2, or 4 arguments')
if len(p) > 2:
base = int(p[2], 16)
else:
base = 0
with open(p[1], "rb") as df:
if len(p) > 3:
df.seek(int(p[3], 16))
sz = int(p[4], 16)
data = df.read(sz)
if len(data) != sz:
raise ValueError('not enough data in the file')
else:
data = df.read()
data = BinData(8, data)
segment = MachineSegment(isa, data, base, None)
else:
if cmd == 'func':
if len(p) not in (2, 3):
raise ValueError('fun command needs 1 or 2 arguments')
fun_start = int(p[1], 16)
if len(p) >= 3:
fun_name = p[2]
else:
fun_name = None
fun = forest.mark_function(forest.mark_block(MachineBlock, segment, fun_start))
fun.set_name(fun_name)
last_func = fun
elif cmd == 'nossa':
if len(p) != 3:
raise ValueError('nossa command needs 2 arguments')
off = int(p[2], 16)
last_func.root.forbidden_stack_slots.add(off & 0xffffffff)
elif cmd == 'arg':
if len(p) != 3:
raise ValueError('arg command needs 2 arguments')
reg = isa.regs[p[1]]
last_func.root.make_arg(reg, p[2])
else:
raise ValueError('Unknown command "{}"'.format(cmd))
forest.process()
forest.post_process()
def print_finish(indent, finish):
ind = ' ' * indent
if finish is None:
print('{}Halt'.format(ind))
elif isinstance(finish, IrHalt):
print('{}{}({})'.format(ind, finish.special.name, ', '.join(str(x) for x in finish.ins)))
if isinstance(finish.extra, dict):
for loc, val in finish.extra.items():
print('{} [{} = {}]'.format(ind, loc, val))
elif isinstance(finish, IrGoto):
print('{}goto {}'.format(ind, finish.dst.get_name()))
for phi, val in finish.phi_vals.items():
print('{} {} = {}'.format(ind, phi, val))
if isinstance(finish.extra, dict):
for loc, val in finish.extra.items():
print('{} [{} = {}]'.format(ind, loc, val))
elif isinstance(finish, IrCond):
print('{}if ({}) {{'.format(ind, finish.cond))
print_finish(indent + 1, finish.finp)
print('{}}} else {{'.format(ind))
print_finish(indent + 1, finish.finn)
print('{}}}'.format(ind))
elif isinstance(finish, IrJump):
print('{}goto *{}'.format(ind, finish.addr))
if isinstance(finish.extra, dict):
for loc, val in finish.extra.items():
print('{} [{} = {}]'.format(ind, loc, val))
elif isinstance(finish, IrCall):
for arg, val in finish.arg_vals.items():
print('{} {} = {}'.format(ind, arg, val))
if len(finish.returns) == 0:
print('{}noreturn {}()'.format(ind, finish.tree.get_name()))
elif len(finish.returns) == 1:
print('{}{}()'.format(ind, finish.tree.get_name()))
ret, = finish.returns.values()
for res in ret.results:
mask = forest.live_masks.get(res, 0)
print('{}[{:x}] {} = {}'.format(ind, mask, res, res.res))
print_finish(indent, ret.finish)
else:
print('{}{}() ->'.format(ind, finish.tree.get_name()))
for path, ret in finish.returns.items():
print('{}{}:'.format(ind, path))
for res in ret.results:
mask = forest.live_masks.get(res, 0)
print('{} [{:x}] {} = {}'.format(ind, mask, res, res.loc))
print_finish(indent + 1, ret.finish)
elif isinstance(finish, IrReturn):
for loc, val in finish.res_vals.items():
print('{} {} = {}'.format(ind, loc, val))
print('{}return {}()'.format(ind, finish.path))
if isinstance(finish.extra, dict):
for loc, val in finish.extra.items():
print('{} [{} = {}]'.format(ind, loc, val))
else:
print('{}???'.format(ind))
def print_bb(indent, block):
ind = ' ' * indent
if block.loop:
if block.loop.roots == [block]:
print('{}{}:'.format(ind[4:], block.loop))
print('{}nodes: {}'.format(ind, ', '.join(x.get_name() for x in block.loop.nodes)))
print('{}subloops: {}'.format(ind, ', '.join(str(x) for x in block.loop.subloops)))
print('{}front: {}'.format(ind, ', '.join(x.get_name() for x in block.loop.front)))
else:
print('{}[in {}]'.format(ind[4:], block.loop))
if block.front:
print('{}{} [{}] [FRONT {}]:'.format(ind[4:], block.get_name(), block.weight, ', '.join(x.get_name() for x in block.front)))
else:
print('{}{} [{}]:'.format(ind[4:], block.get_name(), block.weight))
if not block.valid:
print('{}INVALID'.format(ind))
print()
else:
if isinstance(block, MachineBlock):
if args.print_assembly:
for insn in block.raw_insns:
print('{}{:08x} {:18} {}'.format(ind, insn.start, ' '.join(format(x, '02x') for x in data[insn.start:insn.end]), (' '*28 + '\n').join(str(x) for x in insn.insns)))
print()
if isinstance(block, MachineBaseBlock):
for loc, val in block.regstate_in.items():
print('{} [{} = {}]'.format(ind, loc, val))
for loc, phi in block.phis.items():
mask = forest.live_masks.get(phi, 0)
print('{}[{:x}] {} = {}'.format(ind, mask, phi, loc))
for op in block.ops:
print('{}{}'.format(ind, op))
for expr in block.exprs:
mask = forest.live_masks.get(expr, 0)
count = expr.block.expr_counts[expr]
print('{}[{}] [{:x}] {}'.format(ind, count, mask, expr.display()))
print_finish(indent, block.finish)
print('{}simple [SCCS: {}]:'.format(ind, block.simple_sccs))
print_finish(indent, block.simple_finish)
print()
for scc in block.child_sccs:
if len(scc.nodes) == 1:
print_bb(indent + 1, scc.nodes[0])
else:
if scc.front:
print('{}unnatural loop [FRONT {}]:'.format(ind, ', '.join('block_{:x}'.format(x.pos) for x in scc.front)))
else:
print('{}unnatural loop:'.format(ind))
for node in scc.nodes:
print_bb(indent + 2, node)
if args.print_domtree:
for tree in forest.trees:
print('Function {}:'.format(tree.get_name()))
for path in tree.root.ret_paths:
print(' Return path {}'.format(path))
if isinstance(path, MachineReturn):
print(' Results {}'.format(', '.join(str(x) for x in path.results)))
print(' Stack offsets {}'.format(path.stack_offset))
for arg in tree.root.args:
mask = forest.live_masks.get(arg, 0)
print(' [{:x}] {} = {}'.format(mask, arg, arg.loc))
print_bb(1, tree.root)
for tree in forest.trees:
s = StructFunc(tree)
print('// Function at {:#x}.'.format(s.tree.root.pos))
if s.tree.root.stack_slots:
print('// Stack slots: {}.'.format(
', '.join(
str(slot) for slot in s.tree.root.stack_slots.values()
)
))
print(s.str(''))