diff options
Diffstat (limited to 'web/server/h2o/libh2o/deps/picotls/deps/cifra/src/arm/analyse.py')
-rw-r--r-- | web/server/h2o/libh2o/deps/picotls/deps/cifra/src/arm/analyse.py | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/picotls/deps/cifra/src/arm/analyse.py b/web/server/h2o/libh2o/deps/picotls/deps/cifra/src/arm/analyse.py new file mode 100644 index 000000000..d2c456fef --- /dev/null +++ b/web/server/h2o/libh2o/deps/picotls/deps/cifra/src/arm/analyse.py @@ -0,0 +1,207 @@ +import subprocess +import sys +import re + +function_intro_re = re.compile(r'^(?P<addr>[0-9a-fA-F]{8}) <(?P<name>[a-zA-Z0-9\._]+)>:$') +insn_re = re.compile(r'^\s+(?P<addr>[0-9a-fA-F]+):\s+(?P<insn>[0-9a-fA-F ]+)\s+\t(?P<op>.*)$') + +class Instruction: + def __init__(self, addr, insn, op): + self.addr = long(addr, 16) + self.insn = insn + + args = op.split('\t', 1) + + self.op = args[0].strip() + if len(args) == 2: + comment = args[1].strip().split(';', 1) + else: + comment = args + + self.args = comment[0].strip() + + if len(comment) == 2: + self.comment = comment[1].strip() + else: + self.comment = '' + + def __repr__(self): + return '<insn %r>' % (self.__dict__) + + +def literal_branch_target(t): + return ' <' in t + +class Function: + def __init__(self, addr, name): + self.name = name + self.addr = long(addr, 16) + self.insns = [] + self.calls = [] + + def __repr__(self): + return '<%s %d instructions>' % (self.name, len(self.insns)) + + def add_insn(self, insn): + self.insns.append(Instruction(**insn)) + + def contains_addr(self, addr): + if self.insns: + return addr >= self.addr and addr <= self.insns[-1].addr + else: + return addr == self.addr + + def dump(self): + print self.name + ':' + for insn in self.insns: + print ' ', '%04x' % insn.addr + ':', insn.op, insn.args, '\t;', insn.comment + + def get_literal_word(self, addr): + for insn in self.insns: + if insn.addr == addr and insn.op == '.word': + w = int(insn.args, 16) + if w & 0x80000000: + w = -(w ^ 0xffffffff) + 1 + return w + return None + + def analyse(self, prog): + self.stack_guess = None + regs = {} + + for insn in self.insns: + # stack adjustment with literal + if insn.op == 'sub' and insn.args.startswith('sp, ') and self.stack_guess is None: + sz = int(insn.args.split('#', 1)[1]) + self.stack_guess = sz + + # literal pool loads + if insn.op == 'ldr' and ', [pc, #' in insn.args: + reg, offset = insn.args.split(', [pc, #') + offset = int(offset.replace(']', '')) + word = self.get_literal_word(insn.addr + offset + 2) + if word is not None: + regs[reg] = word + + if insn.op == 'add' and insn.args.startswith('sp, r') and self.stack_guess is None: + reg = insn.args.split(', ')[1] + if reg in regs: + self.stack_guess = regs[reg] + + # static branches + if insn.op[0] == 'b' and literal_branch_target(insn.args): + target = long(insn.args.split(' <', 1)[0], 16) + + targetf = prog.function_at_addr(target) + + if targetf and targetf != self: + self.calls.append(targetf) + + if self.stack_guess is None: + self.stack_guess = 0 + + def stack_usage(self, hints, warns, prog, depth = 0): + hinted_calls = [] + if self.stack_guess: + print ' ' * depth, 'stack:', self.name, self.stack_guess, 'bytes' + + our_hints = [h for h in hints if h and h[0] == self.name] + if our_hints: + hints = [h[1:] for h in our_hints] + hinted_calls = [prog.function_by_name(h[0]) for h in hints if h] + else: + if self.name in warns: + print ' WARN: no calls hints for fn-ptr caller', self.name + + if self.calls + hinted_calls: + call_usage = max([f.stack_usage(hints, warns, prog, depth + 1) for f in self.calls + hinted_calls]) + else: + call_usage = 0 + return self.stack_guess + call_usage + +class Program: + def __init__(self): + self.functions = [] + + # sequence of tuples naming a call sequence known to occur + # this allows working out calls through pointers + self.call_hints = [] + + # function names to warn on if we don't have callees + self.call_warns = set() + + def read_elf(self, elf): + current_fn = None + + for x in subprocess.Popen(['arm-none-eabi-objdump', '-d', elf], + stdout = subprocess.PIPE).stdout: + x = x.rstrip('\n') + m = function_intro_re.match(x) + if m: + fn = Function(**m.groupdict()) + current_fn = fn + self.functions.append(fn) + + m = insn_re.match(x) + if m: + assert current_fn + current_fn.add_insn(m.groupdict()) + + def analyse(self): + for f in self.functions: + f.analyse(self) + + def function_by_name(self, name): + fns = [fn for fn in self.functions if fn.name == name] + if len(fns) == 0: + return None + elif len(fns) == 1: + return fns[0] + else: + print 'warn: more than one function named', name + return None + + def function_at_addr(self, addr): + for f in self.functions: + if f.addr == addr: + return f + return None + + def add_call_hint(self, *seq): + self.call_hints.append(seq) + + def add_call_warn(self, fn): + self.call_warns.add(fn) + + def measure_stack(self, name): + fn = self.function_by_name(name) + if fn is None: + return 0 + + return fn.stack_usage(self.call_hints, self.call_warns, self) + +_, exe, fn = sys.argv + +p = Program() +p.read_elf(exe) + +p.analyse() + +# calls which indirect through fn ptrs +p.add_call_warn('cf_blockwise_accumulate') +p.add_call_warn('cf_blockwise_accumulate_final') + +# hints to resolve those +p.add_call_hint('cf_sha224_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'sha256_update_block') +p.add_call_hint('cf_sha256_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'sha256_update_block') +p.add_call_hint('cf_sha384_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'sha512_update_block') +p.add_call_hint('cf_sha512_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'sha512_update_block') +p.add_call_hint('cf_norx32_encrypt', 'input', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'input_block') +p.add_call_hint('cf_norx32_decrypt', 'input', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'input_block') +p.add_call_hint('cf_cbcmac_stream_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'cbcmac_process') +p.add_call_hint('cf_cmac_stream_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'cmac_process_final_pad') +p.add_call_hint('cf_cmac_stream_update', 'cf_blockwise_accumulate_final', 'cmac_process') +p.add_call_hint('cf_cmac_stream_update', 'cf_blockwise_accumulate_final', 'cmac_process_final_nopad') + + +print 'stack', fn, '=', p.measure_stack(fn) |