package re3lib; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import ghidra.app.script.GhidraScript; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Function; public class FunctionDatabase { public enum Type { Auto, Fix, Stub } public class Dependency implements java.io.Serializable { private static final long serialVersionUID = 1L; public Address address; public String name; public Dependency(Address address, String name) { this.address = address; this.name = name; } private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { out.writeObject(address != null ? address.toString() : null); out.writeObject(name); } private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { String addressString = (String) in.readObject(); if (addressString != null) { address = RecompileConfig.INSTANCE.script.getCurrentProgram().getAddressFactory().getAddress(addressString); } name = (String) in.readObject(); } } public class Entry implements java.io.Serializable { private static final long serialVersionUID = 1L; public Address address; public String name; public File file; public Type type; public List dependencies = new ArrayList<>(); private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { out.writeObject(address != null ? address.toString() : null); out.writeObject(name); out.writeObject(file != null ? file.toString() : null); out.writeObject(type); out.writeObject(dependencies); } private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { String addressString = (String) in.readObject(); if (addressString != null) { address = RecompileConfig.INSTANCE.script.getCurrentProgram().getAddressFactory().getAddress(addressString); } name = (String) in.readObject(); String fileString = (String) in.readObject(); if (fileString != null) { file = new File(fileString); } type = (Type) in.readObject(); dependencies = (List) in.readObject(); } } public List entries = new ArrayList<>(); private File file; private transient GhidraScript script; public FunctionDatabase(GhidraScript script) { this.script = script; file = new File(RecompileConfig.INSTANCE.outputDir, "functions.dat"); } public void load() throws Exception { if (!file.exists()) { return; } try (java.io.ObjectInputStream ois = new java.io.ObjectInputStream(new java.io.FileInputStream(file))) { entries = (List) ois.readObject(); script.println("Loaded " + entries.size() + " function entries from " + file); } catch (java.io.IOException | ClassNotFoundException e) { script.println("Error loading function database: " + e.getMessage()); } } public void save() throws Exception { try (java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(new java.io.FileOutputStream(file))) { oos.writeObject(entries); script.println("Saved " + entries.size() + " function entries to " + file); } catch (java.io.IOException e) { script.println("Error saving function database: " + e.getMessage()); } } public void add(Entry entry) { entries.add(entry); } public void applyDefaultFilters(boolean rebuildAllGlobals) throws Exception { GlobalDumper globalDumper = new GlobalDumper(script); FunctionDumper dumper = new FunctionDumper(script, globalDumper); if (rebuildAllGlobals) { globalDumper.removeGlobalManifest(); } boolean madeAnyChanges = false; // Create a hash map to store symbol names Map symbolNames = new HashMap<>(); Map exportedFunctionNames = new HashMap<>(); for (Entry entry : entries) { Function function = script.getFunctionAt(entry.address); if (function != null) { String dirComponent = entry.file.getParent().toString(); boolean isAuto = entry.type == Type.Auto; boolean isFix = entry.type == Type.Fix; // Get the actual symbol name and store it in the hash map String symbolName = function.getName(); symbolNames.put(entry.address, symbolName); if (isAuto && !exportedFunctionNames.containsKey(entry.name)) { exportedFunctionNames.put(entry.name, entry.file); } else if (isFix) { exportedFunctionNames.replace(entry.name, entry.file); } } } // Print the number of symbol names collected script.println("Collected " + symbolNames.size() + " symbol names"); boolean dryMode = false; HashSet functionsToRegenerate = new HashSet<>(); Iterator iterator = entries.iterator(); while (iterator.hasNext()) { Entry entry = iterator.next(); Function function = script.getFunctionAt(entry.address); boolean pendingDelete = false; boolean pendingRegenerate = false; if (rebuildAllGlobals) { pendingRegenerate = true; } // Remove CRT and other blacklisted functions if (function == null || !dumper.isValidFunction(function)) { // Remove the file if (entry.file != null && entry.file.exists()) { script.println("Removed file: " + entry.file.getAbsolutePath()); pendingDelete = true; } // Remove entry from the list script.println("Removed invalid function entry: " + entry.name + " at " + entry.address); function = null; } // Check if symbol name matches the symbol name parsed from the file if (function != null) { String actualSymbolName = symbolNames.get(entry.address); if (actualSymbolName == null) { throw new Exception( "Symbol name not found for function at " + entry.address + " in file " + entry.file.getAbsolutePath()); } if (actualSymbolName != null && !actualSymbolName.equals(entry.name)) { File fnExportedFile = exportedFunctionNames.get(entry.name); if (fnExportedFile != null && fnExportedFile != entry.file) { // Already exists elsewhere, so remove this file script.println("Removing duplicate function: " + entry.name + " at " + entry.address + " overridden by " + fnExportedFile); pendingDelete = true; } else { // Regeneral this function script.println("Symbol name mismatch for function at " + entry.address + ": " + "File name: " + entry.name + ", Actual symbol: " + actualSymbolName); entry.name = actualSymbolName; // Update the entry name to match the actual symbol pendingRegenerate = true; } } // Check if dependencies are valid for (Dependency dependency : entry.dependencies) { Function depFunction = script.getFunctionAt(dependency.address); if (depFunction == null) { script.println( "Dependency not found: " + dependency.name + " at " + dependency.address + " in " + entry.file); pendingRegenerate = true; } else if (!dumper.isValidFunction(depFunction) || !depFunction.getName().equals(dependency.name)) { script .println("Invalid dependency: " + dependency.name + " at " + dependency.address + " in " + entry.file + " should be " + dependency.name); pendingRegenerate = true; } } entry.name = actualSymbolName; // Update the entry name to match the actual symbol madeAnyChanges = true; } if (pendingDelete) { iterator.remove(); if (!dryMode) { entry.file.delete(); madeAnyChanges = true; } } else if (pendingRegenerate && entry.type != Type.Stub) { if (!dryMode) { functionsToRegenerate.add(function); madeAnyChanges = true; } } } for (Function function : functionsToRegenerate) { script.println("Regenerating function: " + function.getName() + " at " + function.getEntryPoint()); dumper.dump(function); } if (madeAnyChanges) { // Update CMake timestamp RecompileConfig.INSTANCE.touchCMakeTimestamp(); globalDumper.dumpGlobals(); globalDumper.saveGlobalManifest(); TypeDumper typeDumper = new TypeDumper(script); typeDumper.run(); } } }