def build(self): print_debug("Starting build") self._prepare_dirs() for config in self.configs: if only_build != None and config["short_name"] not in only_build: continue self._set_config(config) self._configTempDir = tempfile.mkdtemp() print_debug("Temp files for this configuration are in: " + self._configTempDir) if "dynamic_link" in self._config and self._config["dynamic_link"]: self.dynamic_link_base = DyLinkCreator() else: self.dynamic_link_base = None self._builtCodeAddr = 0x80001800 if "code_address" in self.project.data: self._builtCodeAddr = self.project.data["code_address"] # hook setup self._hook_contexts = {} for name, hookType in hooks.HookTypes.iteritems(): if hookType.has_context: self._hook_contexts[hookType] = hookType.context_type() self._compile_modules() # figure out how many copies we need to build # this is a mess! if "multi_build" in self._config: self._multi_build = self._config["multi_build"] else: self._multi_build = {self._config["short_name"]: self._config["linker_script"]} for s_name, s_script in self._multi_build.iteritems(): self.current_build_name = s_name self._patches = [] self._rel_patches = [] self._hooks = [] self._create_hooks() self._link(s_name, s_script) self._read_symbol_map() if self.dynamic_link_base: self.dynamic_link = DyLinkCreator(self.dynamic_link_base) self.dynamic_link.set_elf(open(self._currentOutFile, "rb")) for hook in self._hooks: hook.create_patches() self._create_patch(s_name) if delete_temp: shutil.rmtree(self._configTempDir)
def build(self): print_debug('Starting build') self._prepare_dirs() for config in self.configs: if only_build != None and config['short_name'] not in only_build: continue self._set_config(config) self._configTempDir = tempfile.mkdtemp() print_debug('Temp files for this configuration are in: ' + self._configTempDir) if 'dynamic_link' in self._config and self._config['dynamic_link']: self.dynamic_link_base = DyLinkCreator() else: self.dynamic_link_base = None self._builtCodeAddr = 0x80001800 if 'code_address' in self.project.data: self._builtCodeAddr = self.project.data['code_address'] # hook setup self._hook_contexts = {} for name, hookType in hooks.HookTypes.iteritems(): if hookType.has_context: self._hook_contexts[hookType] = hookType.context_type() self._compile_modules() # figure out how many copies we need to build # this is a mess! if 'multi_build' in self._config: self._multi_build = self._config['multi_build'] else: self._multi_build = { self._config['short_name']: self._config['linker_script'] } for s_name, s_script in self._multi_build.iteritems(): self.current_build_name = s_name self._patches = [] self._rel_patches = [] self._hooks = [] self._create_hooks() self._link(s_name, s_script) self._read_symbol_map() if self.dynamic_link_base: self.dynamic_link = DyLinkCreator(self.dynamic_link_base) self.dynamic_link.set_elf(open(self._currentOutFile, 'rb')) for hook in self._hooks: hook.create_patches() self._create_patch(s_name) if delete_temp: shutil.rmtree(self._configTempDir)
class KamekBuilder(object): def __init__(self, project, configs): self.project = project self.configs = configs def build(self): print_debug("Starting build") self._prepare_dirs() for config in self.configs: if only_build != None and config["short_name"] not in only_build: continue self._set_config(config) self._configTempDir = tempfile.mkdtemp() print_debug("Temp files for this configuration are in: " + self._configTempDir) if "dynamic_link" in self._config and self._config["dynamic_link"]: self.dynamic_link_base = DyLinkCreator() else: self.dynamic_link_base = None self._builtCodeAddr = 0x80001800 if "code_address" in self.project.data: self._builtCodeAddr = self.project.data["code_address"] # hook setup self._hook_contexts = {} for name, hookType in hooks.HookTypes.iteritems(): if hookType.has_context: self._hook_contexts[hookType] = hookType.context_type() self._compile_modules() # figure out how many copies we need to build # this is a mess! if "multi_build" in self._config: self._multi_build = self._config["multi_build"] else: self._multi_build = {self._config["short_name"]: self._config["linker_script"]} for s_name, s_script in self._multi_build.iteritems(): self.current_build_name = s_name self._patches = [] self._rel_patches = [] self._hooks = [] self._create_hooks() self._link(s_name, s_script) self._read_symbol_map() if self.dynamic_link_base: self.dynamic_link = DyLinkCreator(self.dynamic_link_base) self.dynamic_link.set_elf(open(self._currentOutFile, "rb")) for hook in self._hooks: hook.create_patches() self._create_patch(s_name) if delete_temp: shutil.rmtree(self._configTempDir) def _prepare_dirs(self): self._outDir = self.project.makeRelativePath(self.project.data["output_dir"]) print_debug("Project will be built in: " + self._outDir) if not os.path.isdir(self._outDir): os.makedirs(self._outDir) print_debug("Created that directory") def _set_config(self, config): self._config = config print_debug("---") print_debug("Building for configuration: " + config["friendly_name"]) self.config_short_name = config["short_name"] if "rel_area_start" in config: self._rel_area = (config["rel_area_start"], config["rel_area_end"]) else: self._rel_area = (-50, -50) def _create_hooks(self): print_debug("---") print_debug("Creating hooks") for m in self.project.modules: if "hooks" in m.data: for hookData in m.data["hooks"]: assert "name" in hookData and "type" in hookData # print_debug('Hook: %s : %s' % (m.moduleName, hookData['name'])) if hookData["type"] in hooks.HookTypes: hookType = hooks.HookTypes[hookData["type"]] hook = hookType(self, m, hookData) self._hooks.append(hook) else: raise ValueError, "Unknown hook type: %s" % hookData["type"] def _compile_modules(self): print_debug("---") print_debug("Compiling modules") # gcc setup cc_command = [ "%s%s-gcc" % (gcc_path, gcc_type), "-nodefaultlibs", "-I.", "-fno-builtin", "-Os", "-fno-exceptions", "-fno-rtti", "-mno-sdata", "-c", ] as_command = cc_command if "defines" in self._config: for d in self._config["defines"]: cc_command.append("-D%s" % d) for i in self._config["include_dirs"]: cc_command.append("-I%s" % i) if use_clang: # This is just for cc cc_command = [ "%sclang" % llvm_path, "-cc1", "-fno-rtti", "-fno-threadsafe-statics", "-fshort-wchar", "-nobuiltininc", "-nostdsysteminc", "-nostdinc++", "-Os", "-Wall", "-std=c++11", "-I.", "-emit-obj", ] if "defines" in self._config: for d in self._config["defines"]: cc_command.append("-D%s" % d) for i in self._config["include_dirs"]: cc_command.append("-I%s" % i) self._moduleFiles = [] for m in self.project.modules: for normal_sourcefile in m.data["source_files"]: print_debug("Compiling %s : %s" % (m.moduleName, normal_sourcefile)) objfile = os.path.join(self._configTempDir, "%d.o" % generate_unique_id()) sourcefile = os.path.join(m.moduleDir, normal_sourcefile) if sourcefile.endswith(".o"): new_command = ["cp", sourcefile, objfile] else: # todo: better extension detection if sourcefile.endswith(".s") or sourcefile.endswith(".S"): command = as_command else: command = cc_command new_command = command + ["-o", objfile, sourcefile] if sourcefile.endswith(".c"): new_command.remove("-std=c++11") if "cc_args" in m.data: new_command += m.data["cc_args"] if show_cmd: print_debug(new_command) errorVal = subprocess.call(new_command) if errorVal != 0: print "BUILD FAILED!" print "compiler returned %d - an error occurred while compiling %s" % (errorVal, sourcefile) sys.exit(1) self._moduleFiles.append(objfile) print_debug("Compilation complete") def _link(self, short_name, script_file): print_debug("---") print_debug("Linking %s (%s)..." % (short_name, script_file)) nice_name = "%s_%s" % (self._config["short_name"], short_name) print_debug("---") self._currentMapFile = "%s/%s_linkmap.map" % (self._outDir, nice_name) outname = "object.plf" if self.dynamic_link_base else "object.bin" self._currentOutFile = "%s/%s_%s" % (self._outDir, nice_name, outname) ld_command = ["%s%s-ld" % (gcc_path, gcc_type), "-L."] ld_command.append("-o") ld_command.append(self._currentOutFile) if self.dynamic_link_base: ld_command.append("-r") ld_command.append("--oformat=elf32-powerpc") else: ld_command.append("--oformat=binary") ld_command.append("-Ttext") ld_command.append("0x%08X" % self._builtCodeAddr) ld_command.append("-T") ld_command.append(script_file) ld_command.append("-Map") ld_command.append(self._currentMapFile) ld_command.append("--no-demangle") # for debugging # ld_command.append('--verbose') ld_command += self._moduleFiles if show_cmd: print_debug(ld_command) errorVal = subprocess.call(ld_command) if errorVal != 0: print "BUILD FAILED!" print "ld returned %d" % errorVal sys.exit(1) print_debug("Successfully linked %s" % short_name) def _read_symbol_map(self): print_debug("---") print_debug("Reading symbol map") self._symbols = [] file = open(self._currentMapFile, "r") for line in file: if "__text_start" in line: self._textSegStart = int(line.split()[0], 0) break # now read the individual symbols # this is probably a bad method to parse it, but whatever for line in file: if "__text_end" in line: self._textSegEnd = int(line.split()[0], 0) break if not line.startswith(" "): continue sym = line.split() sym[0] = int(sym[0], 0) self._symbols.append(sym) # we've found __text_end, so now we should be at the output section currentEndAddress = self._textSegEnd for line in file: if line[0] == ".": # probably a segment data = line.split() if len(data) < 3: continue segAddr = int(data[1], 0) segSize = int(data[2], 0) if segAddr + segSize > currentEndAddress: currentEndAddress = segAddr + segSize self._codeStart = self._textSegStart self._codeEnd = currentEndAddress file.close() print_debug("Read, %d symbol(s) parsed" % len(self._symbols)) # next up, run it through c++filt print_debug("Running c++filt") p = subprocess.Popen(gcc_type + "-c++filt", stdin=subprocess.PIPE, stdout=subprocess.PIPE) symbolNameList = [sym[1] for sym in self._symbols] filtResult = p.communicate("\n".join(symbolNameList)) filteredSymbols = filtResult[0].split("\n") for sym, filt in zip(self._symbols, filteredSymbols): sym.append(filt) print_debug("Done. All symbols complete.") print_debug("Generated code is at 0x%08X .. 0x%08X" % (self._codeStart, self._codeEnd - 4)) def find_func_by_symbol(self, find_symbol): for sym in self._symbols: # if show_cmd: # out = "0x%08x - %s - %s" % (sym[0], sym[1], sym[2]) # print_debug(out) if sym[2] == find_symbol: return sym[0] raise ValueError, "Cannot find function: %s" % find_symbol def add_patch(self, offset, data): if offset >= self._rel_area[0] and offset <= self._rel_area[1] and use_rels: self._rel_patches.append((offset, data)) else: self._patches.append((offset, data)) def _create_patch(self, short_name): print_debug("---") print_debug("Creating patch") nice_name = "%s_%s" % (self._config["short_name"], short_name) # convert the .rel patches to KamekPatcher format if len(self._rel_patches) > 0: kamekpatch = generate_kamek_patches(self._rel_patches) # self._patches.append((0x817F4800, kamekpatch)) self._patches.append((0x80002F60, kamekpatch)) if self.dynamic_link: # put together the dynamic link files dlcode = open("%s/%s_dlcode.bin" % (self._outDir, nice_name), "wb") dlcode.write(self.dynamic_link.code) dlcode.close() dlrelocs = open("%s/%s_dlrelocs.bin" % (self._outDir, nice_name), "wb") dlrelocs.write(self.dynamic_link.build_reloc_data()) dlrelocs.close() else: # add the outfile as a patch if not using dynamic linking file = open(self._currentOutFile, "rb") patch = (self._codeStart, file.read()) file.close() self._patches.append(patch) # generate a Riivolution patch riiv = open("%s/%s_riiv.xml" % (self._outDir, nice_name), "w") for patch in self._patches: riiv.write(generate_riiv_mempatch(*patch) + "\n") riiv.close() # generate an Ocarina patch ocarina = open("%s/%s_ocarina.txt" % (self._outDir, nice_name), "w") for patch in self._patches: ocarina.write(generate_ocarina_patch(*patch) + "\n") ocarina.close() # generate a KamekPatcher patch kpatch = open("%s/%s_loader.bin" % (self._outDir, nice_name), "wb") kpatch.write(generate_kamek_patches(self._patches)) kpatch.close() print_debug("Patches generated")
class KamekBuilder(object): def __init__(self, project, configs): self.project = project self.configs = configs def build(self): print_debug('Starting build') self._prepare_dirs() for config in self.configs: if only_build != None and config['short_name'] not in only_build: continue self._set_config(config) self._configTempDir = tempfile.mkdtemp() print_debug('Temp files for this configuration are in: ' + self._configTempDir) if 'dynamic_link' in self._config and self._config['dynamic_link']: self.dynamic_link_base = DyLinkCreator() else: self.dynamic_link_base = None self._builtCodeAddr = 0x80001800 if 'code_address' in self.project.data: self._builtCodeAddr = self.project.data['code_address'] # hook setup self._hook_contexts = {} for name, hookType in hooks.HookTypes.iteritems(): if hookType.has_context: self._hook_contexts[hookType] = hookType.context_type() self._compile_modules() # figure out how many copies we need to build # this is a mess! if 'multi_build' in self._config: self._multi_build = self._config['multi_build'] else: self._multi_build = { self._config['short_name']: self._config['linker_script'] } for s_name, s_script in self._multi_build.iteritems(): self.current_build_name = s_name self._patches = [] self._rel_patches = [] self._hooks = [] self._create_hooks() self._link(s_name, s_script) self._read_symbol_map() if self.dynamic_link_base: self.dynamic_link = DyLinkCreator(self.dynamic_link_base) self.dynamic_link.set_elf(open(self._currentOutFile, 'rb')) for hook in self._hooks: hook.create_patches() self._create_patch(s_name) if delete_temp: shutil.rmtree(self._configTempDir) def _prepare_dirs(self): self._outDir = self.project.makeRelativePath( self.project.data['output_dir']) print_debug('Project will be built in: ' + self._outDir) if not os.path.isdir(self._outDir): os.makedirs(self._outDir) print_debug('Created that directory') def _set_config(self, config): self._config = config print_debug('---') print_debug('Building for configuration: ' + config['friendly_name']) self.config_short_name = config['short_name'] if 'rel_area_start' in config: self._rel_area = (config['rel_area_start'], config['rel_area_end']) else: self._rel_area = (-50, -50) def _create_hooks(self): print_debug('---') print_debug('Creating hooks') for m in self.project.modules: if 'hooks' in m.data: for hookData in m.data['hooks']: assert 'name' in hookData and 'type' in hookData #print_debug('Hook: %s : %s' % (m.moduleName, hookData['name'])) if hookData['type'] in hooks.HookTypes: hookType = hooks.HookTypes[hookData['type']] hook = hookType(self, m, hookData) self._hooks.append(hook) else: raise ValueError, 'Unknown hook type: %s' % hookData[ 'type'] def _compile_modules(self): print_debug('---') print_debug('Compiling modules') # gcc setup cc_command = [ '%s%s-gcc.exe' % (gcc_path, gcc_type), '-nodefaultlibs', '-I.', '-fno-builtin', '-Os', '-fno-exceptions', '-fno-rtti', '-mno-sdata', '-c' ] as_command = cc_command if 'defines' in self._config: for d in self._config['defines']: cc_command.append('-D%s' % d) for i in self._config['include_dirs']: cc_command.append('-I%s' % i) if use_clang: # This is just for cc cc_command = [ '%sclang.exe' % llvm_path, '-cc1', '-fno-rtti', '-fno-threadsafe-statics', '-fshort-wchar', '-nobuiltininc', '-nostdsysteminc', '-nostdinc++', '-Os', '-Wall', '-std=c++11', '-I.', '-emit-obj' ] if 'defines' in self._config: for d in self._config['defines']: cc_command.append('-D%s' % d) for i in self._config['include_dirs']: cc_command.append('-I%s' % i) self._moduleFiles = [] for m in self.project.modules: for normal_sourcefile in m.data['source_files']: print_debug('Compiling %s : %s' % (m.moduleName, normal_sourcefile)) objfile = os.path.join(self._configTempDir, '%d.o' % generate_unique_id()) sourcefile = os.path.join(m.moduleDir, normal_sourcefile) print_debug('sourcefile: %s' % (sourcefile)) if sourcefile.endswith('.o'): new_command = ['cp', sourcefile, objfile] else: # todo: better extension detection if sourcefile.endswith('.s') or sourcefile.endswith('.S'): command = as_command else: command = cc_command new_command = command + ['-o', objfile, sourcefile] if sourcefile.endswith('.c'): new_command.remove('-std=c++11') if 'cc_args' in m.data: new_command += m.data['cc_args'] if show_cmd: print_debug(new_command) errorVal = subprocess.call(new_command) if errorVal != 0: print 'BUILD FAILED!' print 'compiler returned %d - an error occurred while compiling %s' % ( errorVal, sourcefile) sys.exit(1) self._moduleFiles.append(objfile) print_debug('Compilation complete') def _link(self, short_name, script_file): print_debug('---') print_debug('Linking %s (%s)...' % (short_name, script_file)) nice_name = '%s_%s' % (self._config['short_name'], short_name) print_debug('---') self._currentMapFile = '%s\\%s_linkmap.map' % (self._outDir, nice_name) outname = 'object.plf' if self.dynamic_link_base else 'object.bin' self._currentOutFile = '%s\\%s_%s' % (self._outDir, nice_name, outname) ld_command = ['%s%s-ld.exe' % (gcc_path, gcc_type), '-L.'] ld_command.append('-o') ld_command.append(self._currentOutFile) if self.dynamic_link_base: ld_command.append('-r') ld_command.append('--oformat=elf32-powerpc') else: ld_command.append('--oformat=binary') ld_command.append('-Ttext') ld_command.append('0x%08X' % self._builtCodeAddr) ld_command.append('-T') ld_command.append(script_file) ld_command.append('-Map') ld_command.append(self._currentMapFile) ld_command.append('--no-demangle') # for debugging #ld_command.append('--verbose') ld_command += self._moduleFiles if show_cmd: print_debug(ld_command) errorVal = subprocess.call(ld_command) if errorVal != 0: print 'BUILD FAILED!' print 'ld returned %d' % errorVal sys.exit(1) print_debug('Successfully linked %s' % short_name) def _read_symbol_map(self): print_debug('---') print_debug('Reading symbol map') self._symbols = [] file = open(self._currentMapFile, 'r') for line in file: if '__text_start' in line: self._textSegStart = int(line.split()[0], 0) break # now read the individual symbols # this is probably a bad method to parse it, but whatever for line in file: if '__text_end' in line: self._textSegEnd = int(line.split()[0], 0) break if not line.startswith(' '): continue sym = line.split() sym[0] = int(sym[0], 0) self._symbols.append(sym) # we've found __text_end, so now we should be at the output section currentEndAddress = self._textSegEnd for line in file: if line[0] == '.': # probably a segment data = line.split() if len(data) < 3: continue segAddr = int(data[1], 0) segSize = int(data[2], 0) if segAddr + segSize > currentEndAddress: currentEndAddress = segAddr + segSize self._codeStart = self._textSegStart self._codeEnd = currentEndAddress file.close() print_debug('Read, %d symbol(s) parsed' % len(self._symbols)) # next up, run it through c++filt print_debug('Running c++filt') p = subprocess.Popen(gcc_type + '-c++filt.exe', stdin=subprocess.PIPE, stdout=subprocess.PIPE) symbolNameList = [sym[1] for sym in self._symbols] filtResult = p.communicate('\n'.join(symbolNameList)) filteredSymbols = filtResult[0].split('\n') for sym, filt in zip(self._symbols, filteredSymbols): sym.append(filt.strip()) print_debug('Done. All symbols complete.') print_debug('Generated code is at 0x%08X .. 0x%08X' % (self._codeStart, self._codeEnd - 4)) def find_func_by_symbol(self, find_symbol): for sym in self._symbols: #if show_cmd: #out = "0x%08x - %s - %s" % (sym[0], sym[1], sym[2]) #print_debug(out) #print_debug(find_symbol) if sym[2] == find_symbol: return sym[0] raise ValueError, 'Cannot find function: %s' % find_symbol def add_patch(self, offset, data): if offset >= self._rel_area[0] and offset <= self._rel_area[ 1] and use_rels: self._rel_patches.append((offset, data)) else: self._patches.append((offset, data)) def _create_patch(self, short_name): print_debug('---') print_debug('Creating patch') nice_name = '%s_%s' % (self._config['short_name'], short_name) # convert the .rel patches to KamekPatcher format if len(self._rel_patches) > 0: kamekpatch = generate_kamek_patches(self._rel_patches) #self._patches.append((0x817F4800, kamekpatch)) self._patches.append((0x80002F60, kamekpatch)) if self.dynamic_link: # put together the dynamic link files dlcode = open('%s\\%s_dlcode.bin' % (self._outDir, nice_name), 'wb') dlcode.write(self.dynamic_link.code) dlcode.close() dlrelocs = open('%s\\%s_dlrelocs.bin' % (self._outDir, nice_name), 'wb') dlrelocs.write(self.dynamic_link.build_reloc_data()) dlrelocs.close() else: # add the outfile as a patch if not using dynamic linking file = open(self._currentOutFile, 'rb') patch = (self._codeStart, file.read()) file.close() self._patches.append(patch) # generate a Riivolution patch riiv = open('%s\\%s_riiv.xml' % (self._outDir, nice_name), 'w') for patch in self._patches: riiv.write(generate_riiv_mempatch(*patch) + '\n') riiv.close() # generate an Ocarina patch ocarina = open('%s\\%s_ocarina.txt' % (self._outDir, nice_name), 'w') for patch in self._patches: ocarina.write(generate_ocarina_patch(*patch) + '\n') ocarina.close() # generate a KamekPatcher patch kpatch = open('%s\\%s_loader.bin' % (self._outDir, nice_name), 'wb') kpatch.write(generate_kamek_patches(self._patches)) kpatch.close() print_debug('Patches generated')