Started to rework the script in Python.
After a painful struggle with pexpect (TLDR: broken output, 1998 problems), I've managed to get some clean output from QEMU with subprocess.
Does this work on Mac, or it's back to square one?
#!/usr/bin/env python2
from __future__ import print_function
import os, sys
import subprocess
import time
import re
string_stubs = {
"DebugMsg" : [ "startupEntry", "startupEventDispatch", "DisablePowerSave" ],
"task_create" : [ "TaskMain", 'Task"', "systemtask", "CmdShell", "EvShel", "HotPlug", "PowerMgr", "PowerMan" ],
"register_interrupt" : [ "ICAPCHx", "OC4_14", "SIO3_ISR" ],
"CreateStateObject" : [ "DMState", "EMState", "PropState", "SRMState" ],
"create_semaphore" : [ "PropSem", "mallocLock", "stdioLock", "dm_lock" ],
"create_msg_queue" : [ "MainMessQueue", "QueueForDeviceIn", "SystemTaskMSGQueue" ],
}
string_stubs_followed_by = {
"register_func" : ([ "flashwrite", "gpiowrite" ], ["NameService"])
}
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
cam = sys.argv[1]
fw = sys.argv[2] if 2 in sys.argv else ""
eprint("Test run...")
cmd = ('./run_canon_fw.sh %s,firmware="%s;boot=0" -d calls,tail '
'-display none -monitor stdio -serial file:uart.log' % (cam, fw))
eprint(cmd)
qemu = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# tried pexpect, but ran into lots of problems
# including broken terminal, missing newlines, broken pipe messages
# todo: find a minimal example and file a bug report?
qemu.output = ""
def qexpect(strings, timeout):
t0 = time.time()
while time.time() - t0 < timeout:
output = qemu.stderr.readline()
qemu.output += output
if output == '' and qemu.poll() is not None:
eprint("QEMU not running!")
break
if any([str in output for str in strings]):
eprint(output)
break
rc = qemu.poll()
return any([str in output for str in strings])
if qexpect(string_stubs["task_create"], 60):
eprint("Task found")
else:
eprint("Task not found")
# let it run for 5 seconds
qexpect([], 5)
try: print("quit", file=qemu.stdin)
except: pass
q_stdout, q_stderr = qemu.communicate()
qemu.wait()
qemu.output += q_stderr
qemu.lines = qemu.output.split("\n")
with open("find_stubs.log", "w") as log:
print(qemu.output, file=log)
# extract the called function from a line that looks like this:
# call 0x1234(...)
# call 0x1234 DebugMsg(...)
# -> 0x5678 # optional (direct jump)
# -> 0xFFABCD # also optional
def extract_call(lines):
assert len(lines) == 3
if " -> " in lines[1]:
jump_line = lines[2] if " -> " in lines[2] else lines[1]
m = re.search('(?<=-> )(.*?)(?= +at )', jump_line)
if m:
return int(m.groups()[0], 16)
else:
m = re.search('(?<=call )(.*?)(?=\()', lines[0])
if m:
return int(m.groups()[0].split(" ")[0], 16)
# strings : list of strings to be found
# first string has the highest priority
# next_strings : one of these should be on the next line (optional)
def find_stub_from_strings(strings, next_strings):
lines = qemu.lines
for s in strings:
for i,l in enumerate(lines[:-3]):
if s in l and "call " in l:
if next_strings is None or any([ns in lines[i+1] for ns in next_strings]):
return extract_call(lines[i:i+3])
eprint("")
stubs_found = {}
for name, strings in string_stubs.iteritems():
stub = find_stub_from_strings(strings, None)
if stub:
stubs_found[name] = stub
eprint("%8X %s" % (stub, name))
else:
eprint(" ???", name)
for name, (strings, next_strings) in string_stubs_followed_by.iteritems():
stub = find_stub_from_strings(strings, next_strings)
if stub:
stubs_found[name] = stub
eprint("%8X %s" % (stub, name))
else:
eprint(" ???", name)
Good luck making this work in Python 3...