def test_g7x(): infomsg(0, 'running g7x 100d tests\n') do_before_push_list(before_push_g7x_100d_data) do_after_pop_list(after_pop_g7x_100d_data) do_likely_func_start_list(likely_func_start_g7x_100d_data) do_likely_tail_call_list(likely_tail_call_g7x_100d_data) infomsg(0, 'done\n')
def test_a540(): infomsg(0, 'running a540 100b tests\n') do_before_push_list(before_push_a540_100b_data) do_after_pop_list(after_pop_a540_100b_data) do_likely_func_start_list(likely_func_start_a540_100b_data) do_likely_tail_call_list(likely_tail_call_a540_100b_data) infomsg(0, 'done\n')
def merge_blocks(self): '''merge adjacent blocks to reduce CHDK core data''' blocks = [] pb = None for b in sorted(self.blocks, key=lambda b: b['offset']): if pb: infomsg( 2, '%s %s %x\n' % (pb['name'], b['name'], b['offset'] - (pb['offset'] + pb['size']))) pb_end = pb['offset'] + pb['size'] # should be handled correctly, but may indicate incorrect block detection if pb_end > b['offset']: warn('overlapping blocks %s %s\n' % (pb['name'], b['name'])) # allow a modest fudge, assuming Canon wouldn't put a varying chunk in a small space if b['offset'] <= pb['offset'] + pb['size'] + 32: nb = pb nb['name'] += ', ' + b['name'] nb['size'] += b['size'] + b['offset'] - (pb['offset'] + pb['size']) pb = nb continue pb = b blocks.append(b) self.blocks = blocks
def create_bytemap(self, mbdef): addr = toAddr(mbdef['start']) src_addr = toAddr(mbdef['src']) mb_src = getMemoryBlock(src_addr) if not mb_src: warn("mblock %s bytemap no source block found" % (mbdef['name'])) return infomsg( 0, "Create bytemap mblock %s 0x%s 0x%x (%d) from %s %s\n" % (mbdef['name'], addr, mbdef['size'], mbdef['size'], mb_src.getName(), src_addr)) if g_options['pretend']: return mem = getCurrentProgram().getMemory() # API changed in 9.2, added "overlay" bool if ghidra.framework.ApplicationVersion(getGhidraVersion( )) >= ghidra.framework.ApplicationVersion('9.2'): mem.createByteMappedBlock(mbdef['name'], addr, src_addr, mbdef['size'], False) else: mem.createByteMappedBlock(mbdef['name'], addr, src_addr, mbdef['size']) mb_new = getMemoryBlock(addr) self.set_attrs(mbdef, mb_new)
def init_levent_table(): addr = toAddr('levent_table') if addr is None: raise ValueError('levent_table not found, check stubs or run ImportCHDKStubs.py') cnt = 0 while True: try: pname = get_valid_pointer_at(addr) if pname is None or getInt(addr.add(4)) == -1: infomsg(0,'break\n') break clearListing(addr, addr.add(11)) b = getByte(pname) # if it looks like a string if is_print_char(b): # if preceding is null, and address isn't already inside data, make string if getByte(pname.add(-1)) == 0 and getDataContaining(pname) == None: createAsciiString(pname) createData(addr, Pointer32DataType()) createDwords(addr.add(4),2) addr = addr.add(12) cnt += 1 except(ghidra.program.model.mem.MemoryAccessException): warn("Memory access exception at %s"%(addr)) break infomsg(0,'levent_table %s %d entries\n'%(toAddr('levent_table'),cnt))
def extract_thumb2_blobs_main(): argparser = argparse.ArgumentParser(description='''\ Extract firmware blobs for cores other than the main CPU from Digic 6 and later firmware, using CHDK stubs_entry.S info ''', formatter_class=argparse.RawDescriptionHelpFormatter) argparser.add_argument('platform', help='CHDK platform name, like g7x') argparser.add_argument('sub', help='Firmware sub name, like 100d') argparser.add_argument("-s", "--source", help="CHDK source root, default current tree", action='store', default=os.path.abspath(os.path.join(self_dir,'..'))) argparser.add_argument("-d", "--dumps", help="CHDK dumps root, default PRIMARY_ROOT or source platform dir",action='store') argparser.add_argument("-o", "--out", help="Output directory, default dump directory",action='store') argparser.add_argument('-v','--verbose',help="Increase verbosity", action='count',default=0) argparser.add_argument("--dumpfile", help="specify dump file directly instead of inferring from platform/sub",action='store') args = argparser.parse_args() chdklib.logutil.verbosity = args.verbose stubs_data = StubsData(warnfunc=warn) # only warnings, no info plat = args.platform platsub = args.sub chdk_root = args.source if args.dumps: dump_root = args.dumps else: dump_root = os.environ.get('PRIMARY_ROOT',os.path.join(chdk_root,'platform')) stubsub = platsub stubplat = plat plat_dir = os.path.join(chdk_root,'platform',stubplat,'sub',stubsub) if args.dumpfile: dump_name = args.dumpfile else: dump_name = os.path.join(dump_root,plat,'sub',platsub,'PRIMARY.BIN') if args.out: out_dir = args.out else: out_dir = os.path.dirname(dump_name) if out_dir == '': out_dir = './' stubs_entry_name = os.path.join(plat_dir,'stubs_entry.S') infomsg(1,'Loading stubs %s %s %s\n'%(stubplat,stubsub,stubs_entry_name)) if not os.path.isfile(stubs_entry_name): sys.stderr.write("ERROR: missing %s\n"%(stubs_entry_name)) sys.exit(1) stubs_data.load_stubs_s(stubs_entry_name,process_comments=True) stubs_data.guess_platform_vals() process_dump(dump_name,out_dir,stubs_data)
def create_uninit(self, mbdef): infomsg( 0, "Create uninit mblock %s 0x%08x 0x%x (%d)\n" % (mbdef['name'], mbdef['start'], mbdef['size'], mbdef['size'])) if g_options['pretend']: return mb_new = createMemoryBlock(mbdef['name'], toAddr(mbdef['start']), None, mbdef['size'], False) self.set_attrs(mbdef, mb_new)
def labels_to_funcs_main(): t0 = datetime.datetime.now() is_thumb2 = str(currentProgram.getLanguageID())[-2:] == 'v7' symbol_count = 0 found_count = 0 create_count = 0 # LAB_ labels are "dynamic" labels, no obvious way to iterate over them # without iterating over everything st = currentProgram.getSymbolTable() mem = currentProgram.getMemory() # getNumSymbols doesn't return dynamic, so not really accurate prog_max = long(st.getNumSymbols() * 2.5) monitor.initialize(prog_max) for s in st.getAllSymbols(True): symbol_count += 1 monitor.incrementProgress(1) monitor.checkCanceled() # already a function, skip if s.getSymbolType() == SymbolType.FUNCTION: continue # ignore non-global symbols like switch cases if s.getParentNamespace().getID() != GLOBAL_NAMESPACE_ID: continue addr = s.getAddress() # skip bad addresses and non-exec blocks mb = mem.getBlock(addr) if not mb or not mb.isExecute(): continue found_count += 1 if is_thumb2 and (addr.offset & 1): monitor.setMessage(s.getName()) if process_thumb_label(s): create_count += 1 else: # already defined as code if getInstructionAt(addr): monitor.setMessage(s.getName()) if process_insn_label(s): create_count += 1 # creating functions appears to reset monitor state if monitor.getMaximum() != prog_max: monitor.setMaximum(prog_max) monitor.setProgress(symbol_count) infomsg( 0, "symbols %d checked %d created %d in %0.1f sec\n" % (symbol_count, found_count, create_count, (datetime.datetime.now() - t0).total_seconds()))
def describe(self, regvals): for rname in sorted(list(regvals)): reg = regvals[rname] if reg is None: rval = "Unknown" elif reg.isRegisterRelativeValue(): rval = str( reg.getRelativeRegister()) + ' %#x' % (reg.getValue()) else: rval = '%#x' % (reg.getValue()) infomsg(-1, '%s %s\n' % (rname, rval))
def list_levent_calls(): cd = LeventCallDescriber() instr = str(askString("Event","Names, numbers or *")).upper() event_ids = [] do_all = False for s in re.split('[ ,]+',instr): if s == '*': do_all = True elif re.match('0x[0-9a-f]+$',s,re.I): event_ids.append(int(s,16)) elif re.match('[0-9]+$',s): event_ids.append(int(s,10)) else: event_id = cd.ld.by_name.get(s) if event_id is None: # iterate over list for case insensitive compare for evdef in cd.ld.list: if evdef['name'].lower() == s.lower(): event_id = evdef['id'] break if event_id is None: warn('ignoring unknown event id %s'%(s)) else: event_ids.append(event_id) if len(event_ids) == 0 and not do_all: warn('no valid IDs specified, exiting') return infomsg(0,"Searching for") msg = [] if do_all: msg.append('all known') for event_id in event_ids: if event_id in cd.ld.by_id and cd.ld.by_id[event_id]['name'] !='': msg.append("%s (%d)"%(cd.ld.by_id[event_id]['name'],event_id)) else: msg.append("%d"%(event_id)) infomsg(0," %s\n"%(', '.join(msg))) for desc in cd.describe_all_calls(): event_id = desc.args[0].val if (do_all and cd.ld.by_id.get(event_id)) or event_id in event_ids: infomsg(0,'%s %s(%s'%(desc.addr,desc.fname,desc.args[0].desc)) if len(desc.args) == 2: infomsg(0,', %s'%(desc.args[1].desc)) infomsg(0,')\n')
def process_thumb_insn_label(addr): # already part of a function, done # TODO could check for incorrect inclusion from tail call if getFunctionContaining(addr): return False # normal function start, already disassembled # without push seems OK, switches excluded in main loop if is_likely_func_start(addr, require_push=False, disassemble=False): if g_options['verbose']: if not is_push_lr(getInstructionAt(addr)): infomsg(0, '%s thumb data notpush\n' % (addr)) else: infomsg(0, '%s thumb data func\n' % (addr)) return do_make_func(addr) return False
def clean_empty_funcs_main(): found_count = 0 clean_count = 0 fm = currentProgram.getFunctionManager() monitor.initialize(fm.getFunctionCount()) for f in fm.getFunctions(True): b = f.getBody() if b.getMinAddress() == b.getMaxAddress(): found_count += 1 if clean_empty_func(f): clean_count += 1 monitor.incrementProgress(1) monitor.checkCanceled() infomsg(0, "found %d cleaned %d\n" % (found_count, clean_count))
def process_call(desc, mzrm_createmsg_addr): # should always have a function, since call describer won't find if not fn = currentProgram.getListing().getFunctionContaining(desc.addr) mzrm_name = desc.args[2].mzrm_name if mzrm_name == '': mzrm_name = 'mzrm_unk_%d' % (desc.args[2].val) # if the function already has a non-default name, leave it sym_source = fn.getSymbol().getSource() if not (sym_source == SourceType.ANALYSIS or sym_source == SourceType.DEFAULT): # TODO could add repeatable comment infomsg( 0, "%s %s no-default source %s\n" % (fn.getEntryPoint(), fn.getName(), sym_source)) return False n = 0 for ref in get_refs_from_addrset(fn.getBody()): if ref.getToAddress() == mzrm_createmsg_addr: n += 1 # only attempt to name if function has exactly one ref to mzrm_createmsg if n != 1: infomsg( 0, '%s %s skip, has %d mzrm_createmsg calls\n' % (fn.getEntryPoint(), mzrm_name, n)) return False # check for existing uses of this name mzrm_name_base = mzrm_name name_count = 0 while True: n_syms = 0 # could be multiple for sym in getSymbols(mzrm_name, None): # if already used with our address, done! if sym.getAddress() == fn.getEntryPoint(): infomsg(0, '%s already %s\n' % (fn.getEntryPoint(), mzrm_name)) return True n_syms += 1 # if none, found a free name if n_syms == 0: break name_count += 1 mzrm_name = '%s_%d' % (mzrm_name_base, name_count) infomsg(0, '%s %s\n' % (fn.getEntryPoint(), mzrm_name)) if not g_options['pretend']: fn.setName(mzrm_name, SourceType.ANALYSIS) return True
def create_split(self, mbdef): addr = toAddr(mbdef['start']) mb_src = getMemoryBlock(addr) if not mb_src: warn("mblock %s split no source block found" % (mbdef['name'])) return infomsg( 0, "Create split mblock %s 0x%s from %s\n" % (mbdef['name'], addr, mb_src.getName())) if g_options['pretend']: return mem = getCurrentProgram().getMemory() mem.split(mb_src, addr) mb_new = getMemoryBlock(addr) mb_new.setName(mbdef['name']) self.set_attrs(mbdef, mb_new)
def clean_code_from_opref(b): addr = b.getAddress() # opref on thumb code has thumb bit set tmode = get_tmode_reg_at(addr) if tmode and addr.getUnsignedOffset() & 1: addr = addr.add(-1) # check if function has already been created starting at this address sym = getSymbolAt(addr) if sym and sym.getSymbolType() == SymbolType.FUNCTION: infomsg(0,'already a function %s\n'%(addr)); return True if is_likely_func_start(addr): return do_make_func(addr) return False
def process_dump(dump_name,out_dir,stubs_data): if not os.path.isfile(dump_name): warn("missing %s"%(dump_name)) return dumper = BlobDumper(dump_name) if dumper.filesize == 0: warn("zero size %s"%(dump_name)) return dumper.process_stubs(stubs_data) with open(dump_name,'rb') as fh: for b in dumper.blocks: oname = os.path.join(out_dir,b['name']+'.bin') fh.seek(b['offset']) data = fh.read(b['size']) infomsg(0,'Write %s 0x%08x %s\n'%(oname,b['start_adr'],b['size'])) with open(oname,'wb') as ofh: ofh.write(data)
def write_options(secname, cfgfile, options_spec, options): # lets caller bypass if options['skip_create_config']: infomsg(0, "Skip writing config file %s\n" % (cfgfile)) return infomsg(0, "Writing config file %s\n" % (cfgfile)) try: fh = open(cfgfile, "w") fh.write('[' + secname + ']\n') for ospec in options_spec: k = ospec['name'] fh.write('# {} {} default={}\n'.format(k, ospec['type'], ospec['default'])) fh.write(ospec['desc']) # this is g_options, to allow merging. Should always have been initialized from defaults, at least fh.write('{}={}\n\n'.format(k, options[k])) fh.close() except IOError: warn('saving default cfg failed')
def thumb_bookmark_cleanup_main(): bmm = getCurrentProgram().getBookmarkManager() # cleanup triggers more disassembly, which can create more of the same kind of errors # try a few times or until nothing cleanable was found for clean_pass in range(1, 11): check_count = 0 clean_count = 0 for b in bmm.getBookmarksIterator("Error"): check_count += 1 if check_bookmark(b): clean_count += 1 if g_options['remove_resolved'] and not g_options['pretend']: bmm.removeBookmark(b) infomsg( 0, "pass %d checked %d cleaned %d\n" % (clean_pass, check_count, clean_count)) if clean_count == 0: break # wait for analysis to complete before next pass analyzeChanges(getCurrentProgram())
def func_bookmark_clean_main(): bmm = getCurrentProgram().getBookmarkManager() check_count = 0 clean_count = 0 for b in bmm.getBookmarksIterator("Analysis"): if (b.getCategory() == 'Function Start Search delayed Overlap' and b.getComment() == 'Function exists at probable good function start'): check_count += 1 status = clean_func_exists(b) elif (b.getCategory() == 'Found Code' and b.getComment() == 'Found code from operand reference'): check_count += 1 status = clean_code_from_opref(b) else: status = False if status: clean_count += 1 if g_options['remove_resolved'] and not g_options['pretend']: bmm.removeBookmark(b) infomsg(0,"checked %d cleaned %d\n"%(check_count, clean_count))
def clean_func_exists(b): jump_ref = False; addr = b.getAddress() # check if function has already been created starting at this address sym = getSymbolAt(addr) if sym and sym.getSymbolType() == SymbolType.FUNCTION: infomsg(0,'already a function %s\n'%(addr)); return True # check references to address: for ref in getReferencesTo(addr): # if the reference is a call, assume this is a valid function start if ref.getReferenceType().isCall(): return do_make_func(addr) if ref.getReferenceType().isJump(): jump_ref = ref # if there's a jump, check if it looks like the start of a function (push et al) if jump_ref and is_likely_func_start(addr): return do_make_func(addr) return False
def name_mzrm_functions(): sym = getSymbol('mzrm_createmsg', None) if not sym: warnf('mzrm_createmsg not present') return mzrm_createmsg_addr = sym.getAddress() filename = str(askFile("mzrm id list", "select")) mzrmlist = MzrmMsgList(filename) cd = MzrmCallDescriber(mzrmlist) found_count = 0 named_count = 0 for desc in cd.describe_all_calls(): if process_call(desc, mzrm_createmsg_addr): named_count += 1 found_count += 1 infomsg(0, "found %d named %d\n" % (found_count, named_count))
def list_mzrm_create_calls(): filename = str(askFile("mzrm id list", "select")) mzrmlist = MzrmMsgList(filename) cd = MzrmCallDescriber(mzrmlist) instr = str(askString("MZRM Messages", "Names, numbers or *")).upper() mzrm_ids = [] do_all = False for s in re.split('[ ,]+', instr): if s == '*': do_all = True elif re.match('0x[0-9a-f]+$', s, re.I): mzrm_ids.append(int(s, 16)) elif re.match('[0-9]+$', s): mzrm_ids.append(int(s, 10)) else: mzrm_msg = mzrmlist.by_name.get(s) if mzrm_msg is None: # iterate over list for case insensitive compare for mzrm_id in mzrmlist.by_id: if mzrmlist.by_id[mzrm_id].name.lower() == s.lower(): mzrm_msg = mzrmlist.by_id[mzrm_id] break if mzrm_msg is None: warn('ignoring unknown mesasge id %s' % (s)) else: mzrm_ids.append(mzrm_msg.mid) if len(mzrm_ids) == 0 and not do_all: warn('no valid IDs specified, exiting') return infomsg(0, "Searching for") msg = [] if do_all: msg.append('all known') for mzrm_id in mzrm_ids: if mzrm_id in mzrmlist.by_id and not mzrmlist.by_id[mzrm_id].unk: msg.append("%s (%d)" % (mzrmlist.by_id[mzrm_id].name, mzrm_id)) else: msg.append("%d" % (mzrm_id)) infomsg(0, " %s\n" % (', '.join(msg))) for desc in cd.describe_all_calls(): mzrm_id = desc.args[2].val if (do_all and mzrmlist.by_id.get(mzrm_id)) or mzrm_id in mzrm_ids: infomsg( 0, '%s %s(%s,%s,%s,%s)\n' % (desc.addr, desc.fname, desc.args[0].desc, desc.args[1].desc, desc.args[2].desc, desc.args[3].desc))
def process_thumb_notdis_label(addr, t_addr): # no instruction, look for possible code for ref in getReferencesTo(t_addr): idesc = get_insn_desc(ref.getFromAddress()) if idesc is None: continue # tbb instructions create weird refs # also don't want anything ref'd by lrdb # mvn almost always constants that look like addresses # mov could be suspect, but also valid since ghidra tracks load + mov mne = idesc.get_mne() if mne == 'tbb' or mne == 'tbh' or mne == 'ldrb' or mne == 'mvn': # infomsg(0,"%s skip ref %s\n"%(addr,mne)) return False if not is_likely_func_start( addr, require_push=False, disassemble=True, tmode=True): return False # infomsg(0,"%s dis thumb\n"%(addr)) bad_data = getDataAt(t_addr) if bad_data: if g_options['verbose']: infomsg(0, '%s remove data %s\n' % (t_addr, bad_data)) if not g_options['pretend']: removeData(bad_data) else: # check for data containing bad_data = getDataContaining(addr) # if it's also unaligned, assume bad # could check for other signs of validity, but already have valid func start if bad_data and (bad_data.getAddress().getOffset() & 1 == 1): if g_options['verbose']: infomsg(0, '%s remove containing data %s\n' % (t_addr, bad_data)) if not g_options['pretend']: removeData(bad_data) # if existing function without disassembled code, remove and try to recreate old_fn = getFunctionAt(addr) if old_fn: # infomsg(0,'%s remove func\n'%(addr)) if not g_options['pretend']: removeFunction(old_fn) if not g_options['pretend']: # set thumb since this case is only used when address has thumb bit set set_tmode_reg_at(addr, 1) if not disassemble(addr): warn("%s dis fail" % (addr)) return False if g_options['verbose']: infomsg(0, '%s thumb data dis func\n' % (addr)) return do_make_func(addr)
def check_thumb_data_conflict(b): m = re.match( 'Failed to disassemble at ([0-9a-fA-F]{8}) due to conflicting data at ([0-9a-fA-F]{8})', b.getComment()) # Not this kind of bookmark if not m: return False # check if data conflict is at +1. i_addr should be the same as bookmark, but number i_addr = int(m.group(1), 16) d_addr = int(m.group(2), 16) if d_addr != i_addr + 1: return False b_addr = b.getAddress() # thumb instruction already there, assume resolved if getInstructionAt(b_addr): if get_tmode_reg_at(b_addr): infomsg(0, "%s looks resolved!\n" % (b_addr)) return True else: # ARM instruction, likely a different problem return False bad_data = getDataAt(toAddr(d_addr)) if bad_data: infomsg(0, 'remove data %s\n' % (bad_data)) if not g_options['pretend']: removeData(bad_data) infomsg(0, "Dis thumb %s\n" % (b_addr)) if not g_options['pretend']: # assume thumb if data was at +1 set_tmode_reg_at(b_addr, 1) if disassemble(b_addr): return True else: infomsg(0, "Dis failed %s\n" % (b_addr)) return False
def list_prop_calls(): filename = str(askFile("platform_camera.h or propsetN.h", "select")) instr = str(askString("Propcases", "Names, numbers or *")).upper() pd = PropsetData(filename) prop_ids = [] do_all = False for s in re.split('[ ,]+', instr): if s == '*': do_all = True elif re.match('0x[0-9a-f]+$', s, re.I): prop_ids.append(int(s, 16)) elif re.match('[0-9]+$', s): prop_ids.append(int(s, 10)) else: if re.match('^PROPCASE_', s): prop_name = s else: prop_name = 'PROPCASE_' + s prop_id = pd.by_name.get(prop_name) if prop_id is None: warn('ignoring unknown propcase id %s' % (prop_name)) else: prop_ids.append(prop_id) if len(prop_ids) == 0 and not do_all: warn('no valid IDs specified, exiting') return infomsg(0, "Searching for") msg = [] if do_all: msg.append('all known') for prop_id in prop_ids: if prop_id in pd.by_id: msg.append("%s (%d)" % (pd.by_id[prop_id], prop_id)) else: msg.append("%d" % (prop_id)) infomsg(0, " %s from propset %d\n" % (', '.join(msg), pd.propset)) cd = PropCallDescriber(filename) for desc in cd.describe_all_calls(): prop_id = desc.args[0].val if (do_all and pd.by_id.get(prop_id)) or prop_id in prop_ids: infomsg( 0, '%s %s(%s,%s,%s)\n' % (desc.addr, desc.fname, desc.args[0].desc, desc.args[1].desc, desc.args[2].desc))
def load_options(secname, cfgfile, options_spec, options): # initialize with default values for ospec in options_spec: options[ospec['name']] = ospec['default'] if not os.path.isfile(cfgfile): infomsg(0, 'Config not found %s\n' % (cfgfile)) return True missing = False # lets caller bypass if options['skip_load_config']: infomsg(0, 'Skip load config %s\n' % (cfgfile)) else: config = ConfigParser.ConfigParser() infomsg(0, 'Load config %s\n' % (cfgfile)) config.read(cfgfile) if config.has_section(secname): for ospec in options_spec: k = ospec['name'] if not config.has_option(secname, k): missing = True continue vt = ospec['type'] if vt == 'bool': options[k] = config.getboolean(secname, k) elif vt == 'int': options[k] = config.getint(secname, k) elif vt == 'enum': cv = config.get(secname, k) if cv in ospec['vals']: options[k] = cv else: warn("unexpected cfg option %s %s" % (k, cv)) else: warn("unexpected option desc %s" % (k)) else: missing = True return missing
def crc32_all(self): infomsg( 1, '%-10s %-10s %-7s %-10s %s\n' % ('start_adr', 'offset', 'size', 'crc32', 'name')) with open(self.filepath, 'rb') as fh: for b in self.blocks: if b['size']: fh.seek(b['offset']) data = fh.read(b['size']) b['crc32'] = zlib.crc32(data) else: b['crc32'] = 0 infomsg( 1, '0x%08x 0x%08x %7d 0x%08x %s\n' % (b['start_adr'], b['offset'], b['size'], b['crc32'], b['name'])) infomsg( 1, "%d blocks %d bytes %d%%\n" % (len(self.blocks), self.total_size, 100 * self.total_size / self.filesize))
def clean_empty_func(f): f_entry = f.getEntryPoint() infomsg(0, 'empty func %s\n' % (f_entry)) if not g_options['pretend']: removeFunction(f) # if the function is inside data or non-code memory block # don't try to recreate if getDataContaining(f_entry): infomsg(0, 'entry inside data %s\n' % (f_entry)) return True # if no instruction, don't re-create if not getInstructionAt(f_entry): infomsg(0, 'entry not code %s\n' % (f_entry)) return True mb = getMemoryBlock(f_entry) if not mb: infomsg(0, 'entry not in memblock!? %s\n' % (f_entry)) return True if not mb.isExecute(): infomsg(0, 'entry in noexec block %s %s\n' % (f_entry, mb.getName())) return True if not mb.isInitialized() and not mb.isMapped(): infomsg(0, 'entry in uninit block %s %s\n' % (f_entry, mb.getName())) return True # infomsg(0,'create %s'%(f_entry)) if g_options['pretend']: return True f_new = createFunction(f_entry, None) if f_new: b = f_new.getBody() if b.getMinAddress() == b.getMaxAddress(): infomsg(0, 'still zero size %s\n' % (f_entry)) removeFunction(f) return False return True else: infomsg(0, 'create failed %s\n' % (f_entry)) return False
def init_chdk_mem_map_main(): init_options() stubs_data = StubsData(warnfunc=warn, infofunc=infomsg) # whatever askDirectory returns isn't actually string sub_dir = str(askDirectory("CHDK platform sub", "select")) filepath = os.path.join(sub_dir, 'stubs_entry.S') if not os.path.isfile(filepath): infomsg(0, 'No stubs_entry.S, trying stubs_entry.S.err\n') filepath = os.path.join(sub_dir, 'stubs_entry.S.err') if not os.path.isfile(filepath): warn('No stubs_entry files found') return stubs_data.load_stubs_s(filepath, process_comments=True) stubs_data.guess_platform_vals() smisc = stubs_data.stubs_entry_misc if not 'ar_rom' in smisc: warn('No ROM region identified, giving up') return if not 'main_fw_code_end' in smisc: warn('Main firmware code end not identified, giving up') return if len(getMemoryBlocks()) > 1: warn('Program already has multiple memory blocks, giving up') mb_rom = getMemoryBlocks()[0] if mb_rom.getStart() != toAddr(smisc['ar_rom']['start_adr']): warn('Memory block start does not match ROM, giving up') return if not mb_rom.isInitialized(): warn('ROM block not initialized, giving up') return if mb_rom.getEnd() <= toAddr(smisc['main_fw_code_end']): warn( 'Already initialized? ROM block end <= main fw code end, giving up' ) return mbops.add_split('ROMDATA', smisc['main_fw_code_end'] + 1, w=False, x=False) make_romstarter_mblock(smisc) # if main firmware doesn't start at rom start, split if 'main_fw_start' in smisc and smisc['main_fw_start'] > smisc['ar_rom'][ 'start_adr']: mbops.add_split('ROMCODE', smisc['main_fw_start'], w=False, x=True) # ignored if not present ar_ramcode = smisc.get('ar_ramcode') ar_btcmcode = smisc.get('ar_btcmcode') ar_ramdata = smisc.get('ar_ramdata') ar_evec = smisc.get('ar_evec') ar_atcm = smisc.get('ar_atcm') mbops.add_stubs_ar(ar_ramdata, 'RAMDATA') mbops.add_stubs_ar(ar_ramcode, 'RAMCODE', x=True) mbops.add_stubs_ar(ar_btcmcode, 'BTCMCODE', x=True) mbops.add_stubs_ar(smisc.get('ar_itcm'), 'ITCM') mbops.add_stubs_ar(smisc.get('ar_uncached'), 'UNCACHED') mbops.add_stubs_ar(smisc.get('ar_dtcm'), 'DTCM') mbops.add_stubs_ar(smisc.get('ar_mmio'), 'MMIO', v=True) mbops.add_stubs_ar(smisc.get('ar_mmio_0xd'), 'MMIO', v=True) mbops.add_stubs_ar(smisc.get('ar_mmio_0xc1'), 'MMIO', v=True) mbops.add_stubs_ar(smisc.get('ar_mmio_0xc8'), 'MMIO', v=True) if smisc['digic'] >= 60: # RAM between ATCM and copied data mbops.add_uninit('RAM', 0x4000, 0x4000) # detected exception vector at address 0, add as initialized if ar_evec and ar_evec['start_adr'] == 0: mbops.add_stubs_ar(smisc.get('ar_evec'), 'EVEC') if ar_atcm and ar_atcm['start_adr'] == 0: mbops.add_uninit('ATCM', ar_evec['last_adr'] + 1, last_adr=ar_atcm['last_adr']) else: mbops.add_stubs_ar(smisc.get('ar_atcm'), 'ATCM') else: mbops.add_stubs_ar(smisc.get('ar_atcm'), 'ATCM') else: # RAM between ITCM and copied data mbops.add_uninit('RAM', 0x1000, 0x900) if ar_ramcode: # RAM between ram data and code (assumes code after data!) mbops.add_uninit('RAM', ar_ramdata['last_adr'] + 1, last_adr=ar_ramcode['start_adr'] - 1) # RAM from end of code to end of RAM mbops.add_uninit('RAM', ar_ramcode['last_adr'] + 1, last_adr=smisc['max_ram_addr']) else: # RAM from end of ram data to end of RAM mbops.add_uninit('RAM', ar_ramdata['last_adr'] + 1, last_adr=smisc['max_ram_addr']) if smisc['digic'] == 60: if ar_btcmcode and ar_btcmcode['start_adr'] == 0xbfe10800: # 0x800 before code mbops.add_uninit('BTCM', 0xbfe10000, 0x800) # from code end to 64k mbops.add_uninit('BTCM', ar_btcmcode['last_adr'] + 1, last_adr=0xbfe20000 - 1) elif smisc['digic'] == 70: # per https://chdk.setepontos.com/index.php?topic=11316.msg142197#msg142197 if ar_btcmcode and ar_btcmcode['start_adr'] == 0xdffc4900: # 0x4900 before code. TODO this is actually initialized, could create mapped mbops.add_uninit('BTCM', 0xdffc0000, 0x4900) # from code end to 256k mbops.add_uninit('BTCM', ar_btcmcode['last_adr'] + 1, last_adr=0xdfffffff) if g_options['include_zico']: for ardef in smisc['zico_blobs']: mbops.add_stubs_ar(ardef, 'ZICO') # set whole ROM read-only before splitting if not g_options['pretend']: mb_rom.setWrite(False) mbops.apply_ops()
def make_romstarter_mblock(smisc): """check for romstarter and create execuable block if possible""" if smisc['digic'] < 60: rs_start = 0xffff0000 rs_start_addr = toAddr(rs_start) # TODO size could be found by checking for run of 0xff rs_size = 0x4000 rs_after = rs_start + rs_size rs_after_addr = rs_start_addr.add(rs_size) elif smisc['digic'] == 60: if not 'main_fw_start' in smisc: infomsg(0, 'make_romstarter_mblock: main fw start not identified\n') return rs_start = smisc['ar_rom']['start_adr'] rs_start_addr = toAddr(rs_start) rs_size = 0x5000 rs_after = rs_start + rs_size rs_after_addr = rs_start_addr.add(rs_size) main_fw_addr = toAddr(smisc['main_fw_start']) if smisc['main_fw_start'] < rs_after: infomsg( 0, 'make_romstarter_mblock: main fw start not too close to romstarter\n' ) return # TODO d7 unclear else: return # get memory block containing start addr mb_src = getMemoryBlock(rs_start_addr) if not mb_src: infomsg(0, 'make_romstarter_mblock: no address containing %s\n', rs_start_addr) return if mb_src != getMemoryBlock(rs_after_addr): infomsg( 0, 'make_romstarter_mblock: multiple memory blocks in region or too small\n' ) return if smisc['digic'] == 60: if mb_src != getMemoryBlock(main_fw_addr): infomsg( 0, 'make_romstarter_mblock: multiple memory blocks in region or too small\n' ) # check if there's enough space if smisc['ar_rom']['last_adr'] < rs_after: infomsg(0, 'make_romstarter_mblock: too small\n') return if smisc['digic'] < 60: pi = get_pinsn_at(rs_start_addr) if not pi: infomsg(0, 'make_romstarter_mblock: no instruction found\n') return if pi.getMnemonicString() != 'b' and pi.getMnemonicString() != 'ldr': infomsg( 0, 'make_romstarter_mblock: unexpected instruction %s\n' % (pi)) return mbops.add_split('ROMSTARTER', rs_start, w=False, x=True) mbops.add_split('ROMDATA', rs_after, w=False, x=False) elif smisc['digic'] == 60: # digic 6 romstarter code doesn't start exactly at rom start, can't do instruction check # TODO romstarter will end up with the original ROM name, should have a rename op mbops.add_split('ROMDATA', rs_after, w=False, x=False)