From e19a123b94ac6a25cc2b9a4144f6d4a2cd15df68 Mon Sep 17 00:00:00 2001 From: Guus Waals <_@guusw.nl> Date: Thu, 29 May 2025 18:03:37 +0800 Subject: [PATCH] WIP --- java/ghidra/DumpCurrentFunctionN.java | 117 ++++----- java/ghidra/DumpCurrentFunctionRecursive.java | 93 +++---- java/ghidra/Test.java | 4 +- java/ghidra/re3lib/FunctionDatabase.java | 234 ++++++++++++++++-- java/ghidra/re3lib/FunctionDumper.java | 80 +++--- java/ghidra/re3lib/GlobalDumper.java | 147 +++++------ 6 files changed, 439 insertions(+), 236 deletions(-) diff --git a/java/ghidra/DumpCurrentFunctionN.java b/java/ghidra/DumpCurrentFunctionN.java index e094596d..57be89fe 100644 --- a/java/ghidra/DumpCurrentFunctionN.java +++ b/java/ghidra/DumpCurrentFunctionN.java @@ -9,6 +9,7 @@ import java.util.List; import ghidra.app.script.GhidraScript; import ghidra.pcodeCPort.address.Address; import ghidra.program.model.listing.Function; +import re3lib.FunctionDatabase; import re3lib.FunctionDumper; import re3lib.GlobalDumper; import re3lib.PCallTracer; @@ -19,26 +20,26 @@ public class DumpCurrentFunctionN extends GhidraScript { final int NumFunctions = 8; // class Entry { - // Function function; + // Function function; // } // class QueueEntry { - // Function function; - // List callees; + // Function function; + // List callees; // } // HashSet
visited = new HashSet<>(); // QueueEntry enter(Function function) { - // if (visited.contains(function.getEntryPoint())) - // return null; + // if (visited.contains(function.getEntryPoint())) + // return null; - // visited.add(function.getEntryPoint()); + // visited.add(function.getEntryPoint()); - // QueueEntry entry = new QueueEntry(); - // entry.function = function; + // QueueEntry entry = new QueueEntry(); + // entry.function = function; + + // function.getCalledFunctions(monitor); - // function.getCalledFunctions(monitor); - // } @Override @@ -46,62 +47,66 @@ public class DumpCurrentFunctionN extends GhidraScript { RemanConfig.INSTANCE = new RemanConfig(this); RemanConfig.INSTANCE.createDirectories(); - GlobalDumper globalDumper = new GlobalDumper(this); - globalDumper.loadGlobalManifest(); + try (FunctionDatabase functionDatabase = new FunctionDatabase(this)) { - FunctionDumper functionDumper = new FunctionDumper(this, globalDumper); + GlobalDumper globalDumper = new GlobalDumper(this, functionDatabase); + globalDumper.loadGlobalManifest(); - PCallTracer tracer = new PCallTracer(); - tracer.setBlacklist(functionDumper.functionAddrBlackList); - tracer.traceCalls(getFunctionContaining(currentAddress)); + FunctionDumper functionDumper = new FunctionDumper(this, functionDatabase, globalDumper); - List
queue = new ArrayList<>(); + PCallTracer tracer = new PCallTracer(); + tracer.setBlacklist(functionDumper.functionAddrBlackList); + tracer.traceCalls(getFunctionContaining(currentAddress)); - // List functionsToDump = new ArrayList<>(); - // List functionsToDumpNew = new ArrayList<>(); - // for (Function func : tracer.out) { - // if (FunctionDumper.isDumpedFix(func)) - // continue; + List
queue = new ArrayList<>(); - // println("Dump: " + func.getName()); - // functionsToDump.add(func); + // List functionsToDump = new ArrayList<>(); + // List functionsToDumpNew = new ArrayList<>(); + // for (Function func : tracer.out) { + // if (FunctionDumper.isDumpedFix(func)) + // continue; - // if (!FunctionDumper.isDumpedAuto(func)) - // functionsToDumpNew.add(func); - // } + // println("Dump: " + func.getName()); + // functionsToDump.add(func); - // if (!functionsToDump.isEmpty()) { - // String newOpt = "Only new (" + functionsToDumpNew.size() + ")"; - // String okOpt = "Yes (" + functionsToDump.size() + ")"; - // String choice = askChoice("Confirmation", "About to generate " + functionsToDump.size() + " functions (" - // + functionsToDumpNew.size() + " new), continue?", - // new ArrayList() { - // { - // add(okOpt); - // add(newOpt); - // add("No"); - // } - // }, okOpt); - // if (choice == okOpt) { - // } else if (choice == newOpt) { - // functionsToDump = functionsToDumpNew; - // } else { - // return; - // } + // if (!FunctionDumper.isDumpedAuto(func)) + // functionsToDumpNew.add(func); + // } - // for (Function func : functionsToDump) { - // functionDumper.dump(func); - // } + // if (!functionsToDump.isEmpty()) { + // String newOpt = "Only new (" + functionsToDumpNew.size() + ")"; + // String okOpt = "Yes (" + functionsToDump.size() + ")"; + // String choice = askChoice("Confirmation", "About to generate " + + // functionsToDump.size() + " functions (" + // + functionsToDumpNew.size() + " new), continue?", + // new ArrayList() { + // { + // add(okOpt); + // add(newOpt); + // add("No"); + // } + // }, okOpt); + // if (choice == okOpt) { + // } else if (choice == newOpt) { + // functionsToDump = functionsToDumpNew; + // } else { + // return; + // } - // if (functionDumper.createdFile) - // RecompileConfig.INSTANCE.touchCMakeTimestamp(); + // for (Function func : functionsToDump) { + // functionDumper.dump(func); + // } - // globalDumper.dumpGlobals(); - // globalDumper.saveGlobalManifest(); - // } + // if (functionDumper.createdFile) + // RecompileConfig.INSTANCE.touchCMakeTimestamp(); - // // Dump types - // TypeDumper dumper = new TypeDumper(this); - // dumper.run(); + // globalDumper.dumpGlobals(); + // globalDumper.saveGlobalManifest(); + // } + + // // Dump types + // TypeDumper dumper = new TypeDumper(this); + // dumper.run(); + } } } diff --git a/java/ghidra/DumpCurrentFunctionRecursive.java b/java/ghidra/DumpCurrentFunctionRecursive.java index d5e74694..6800a5d5 100644 --- a/java/ghidra/DumpCurrentFunctionRecursive.java +++ b/java/ghidra/DumpCurrentFunctionRecursive.java @@ -7,6 +7,7 @@ import java.util.List; import ghidra.app.script.GhidraScript; import ghidra.program.model.listing.Function; +import re3lib.FunctionDatabase; import re3lib.FunctionDumper; import re3lib.GlobalDumper; import re3lib.PCallTracer; @@ -19,60 +20,62 @@ public class DumpCurrentFunctionRecursive extends GhidraScript { RemanConfig.INSTANCE = new RemanConfig(this); RemanConfig.INSTANCE.createDirectories(); - GlobalDumper globalDumper = new GlobalDumper(this); - globalDumper.loadGlobalManifest(); + try (FunctionDatabase functionDatabase = new FunctionDatabase(this)) { + GlobalDumper globalDumper = new GlobalDumper(this, functionDatabase); + globalDumper.loadGlobalManifest(); - FunctionDumper functionDumper = new FunctionDumper(this, globalDumper); + FunctionDumper functionDumper = new FunctionDumper(this, functionDatabase, globalDumper); - PCallTracer tracer = new PCallTracer(); - tracer.setBlacklist(functionDumper.functionAddrBlackList); - tracer.traceCalls(getFunctionContaining(currentAddress)); + PCallTracer tracer = new PCallTracer(); + tracer.setBlacklist(functionDumper.functionAddrBlackList); + tracer.traceCalls(getFunctionContaining(currentAddress)); - List functionsToDump = new ArrayList<>(); - List functionsToDumpNew = new ArrayList<>(); - for (Function func : tracer.out) { - if (FunctionDumper.isDumpedFix(func)) - continue; + List functionsToDump = new ArrayList<>(); + List functionsToDumpNew = new ArrayList<>(); + for (Function func : tracer.out) { + if (FunctionDumper.isDumpedFix(func)) + continue; - println("Dump: " + func.getName()); - functionsToDump.add(func); + println("Dump: " + func.getName()); + functionsToDump.add(func); - if (!FunctionDumper.isDumpedAuto(func)) - functionsToDumpNew.add(func); - } - - if (!functionsToDump.isEmpty()) { - String newOpt = "Only new (" + functionsToDumpNew.size() + ")"; - String okOpt = "Yes (" + functionsToDump.size() + ")"; - String choice = askChoice("Confirmation", "About to generate " + functionsToDump.size() + " functions (" - + functionsToDumpNew.size() + " new), continue?", - new ArrayList() { - { - add(okOpt); - add(newOpt); - add("No"); - } - }, okOpt); - if (choice == okOpt) { - } else if (choice == newOpt) { - functionsToDump = functionsToDumpNew; - } else { - return; + if (!FunctionDumper.isDumpedAuto(func)) + functionsToDumpNew.add(func); } - for (Function func : functionsToDump) { - functionDumper.dump(func); + if (!functionsToDump.isEmpty()) { + String newOpt = "Only new (" + functionsToDumpNew.size() + ")"; + String okOpt = "Yes (" + functionsToDump.size() + ")"; + String choice = askChoice("Confirmation", "About to generate " + functionsToDump.size() + " functions (" + + functionsToDumpNew.size() + " new), continue?", + new ArrayList() { + { + add(okOpt); + add(newOpt); + add("No"); + } + }, okOpt); + if (choice == okOpt) { + } else if (choice == newOpt) { + functionsToDump = functionsToDumpNew; + } else { + return; + } + + for (Function func : functionsToDump) { + functionDumper.dump(func); + } + + if (functionDumper.createdFile) + RemanConfig.INSTANCE.touchCMakeTimestamp(); + + globalDumper.dumpGlobals(); + globalDumper.saveGlobalManifest(); } - if (functionDumper.createdFile) - RemanConfig.INSTANCE.touchCMakeTimestamp(); - - globalDumper.dumpGlobals(); - globalDumper.saveGlobalManifest(); + // Dump types + TypeDumper dumper = new TypeDumper(this); + dumper.run(); } - - // Dump types - TypeDumper dumper = new TypeDumper(this); - dumper.run(); } } diff --git a/java/ghidra/Test.java b/java/ghidra/Test.java index 54618f7e..5193d3ea 100644 --- a/java/ghidra/Test.java +++ b/java/ghidra/Test.java @@ -15,8 +15,8 @@ public class Test extends GhidraScript { // Example SQLite usage try (FunctionDatabase db = new FunctionDatabase(this)) { - List entries = db.loadAllEntries(); - for (FunctionDatabase.Entry entry : entries) { + List entries = db.loadAllEntries(); + for (FunctionDatabase.FunctionEntry entry : entries) { println("entry.name: " + entry.name + " entry.address: " + entry.address + " entry.type: " + entry.type); } } diff --git a/java/ghidra/re3lib/FunctionDatabase.java b/java/ghidra/re3lib/FunctionDatabase.java index cda81f61..9248f44f 100644 --- a/java/ghidra/re3lib/FunctionDatabase.java +++ b/java/ghidra/re3lib/FunctionDatabase.java @@ -20,7 +20,7 @@ import ghidra.program.model.address.Address; import ghidra.program.model.listing.Function; public class FunctionDatabase implements AutoCloseable { - public enum Type { + public static enum Type { Auto(0), Fix(1), Stub(2), @@ -46,13 +46,13 @@ public class FunctionDatabase implements AutoCloseable { } } - public class Entry { + public static class FunctionEntry { public Address address; public String name; public File file; public Type type; - public Entry(Address address, String name, File file, Type type) { + public FunctionEntry(Address address, String name, File file, Type type) { this.address = address; this.name = name; this.file = file; @@ -60,6 +60,20 @@ public class FunctionDatabase implements AutoCloseable { } } + public static class GlobalEntry { + public Address address; + public String name; + public String dataType; + public File file; + + public GlobalEntry(Address address, String name, String dataType, File file) { + this.address = address; + this.name = name; + this.dataType = dataType; + this.file = file; + } + } + private File dbFile; private transient GhidraScript script; private Connection connection; @@ -75,6 +89,13 @@ public class FunctionDatabase implements AutoCloseable { private PreparedStatement loadAllFunctions; private PreparedStatement loadAllImports; + // Add these prepared statements after the existing ones + private PreparedStatement findByNameGlobals; + private PreparedStatement findByAddressGlobals; + private PreparedStatement insertOrReplaceGlobals; + private PreparedStatement deleteByFilepathGlobals; + private PreparedStatement loadAllGlobals; + public FunctionDatabase(GhidraScript script) { this.script = script; dbFile = RemanConfig.INSTANCE.databasePath; @@ -155,6 +176,18 @@ public class FunctionDatabase implements AutoCloseable { "SELECT filepath, name, address, type FROM Functions"); loadAllImports = connection.prepareStatement( "SELECT filepath, name, address, type FROM Imports"); + + // Global statements + findByNameGlobals = connection.prepareStatement( + "SELECT filepath, name, address, type FROM Globals WHERE name = ?"); + findByAddressGlobals = connection.prepareStatement( + "SELECT filepath, name, address, type FROM Globals WHERE address = ?"); + insertOrReplaceGlobals = connection.prepareStatement( + "INSERT OR REPLACE INTO Globals (filepath, name, address, type) VALUES (?, ?, ?, ?)"); + deleteByFilepathGlobals = connection.prepareStatement( + "DELETE FROM Globals WHERE filepath = ?"); + loadAllGlobals = connection.prepareStatement( + "SELECT filepath, name, address, type FROM Globals"); } private void closePreparedStatements() throws SQLException { @@ -176,17 +209,28 @@ public class FunctionDatabase implements AutoCloseable { loadAllFunctions.close(); if (loadAllImports != null) loadAllImports.close(); + + if (findByNameGlobals != null) + findByNameGlobals.close(); + if (findByAddressGlobals != null) + findByAddressGlobals.close(); + if (insertOrReplaceGlobals != null) + insertOrReplaceGlobals.close(); + if (deleteByFilepathGlobals != null) + deleteByFilepathGlobals.close(); + if (loadAllGlobals != null) + loadAllGlobals.close(); } - public List loadAllEntries() throws Exception { + public List loadAllEntries() throws Exception { ensureConnection(); - List entries = new ArrayList<>(); + List entries = new ArrayList<>(); try { // Load from Functions table try (ResultSet rs = loadAllFunctions.executeQuery()) { while (rs.next()) { - Entry entry = createEntryFromResultSet(rs); + FunctionEntry entry = createEntryFromResultSet(rs); if (entry != null) { entries.add(entry); } @@ -201,7 +245,7 @@ public class FunctionDatabase implements AutoCloseable { } } - private Entry createEntryFromResultSet(ResultSet rs) throws SQLException { + private FunctionEntry createEntryFromResultSet(ResultSet rs) throws SQLException { String filepath = rs.getString("filepath"); String name = rs.getString("name"); String addressStr = rs.getString("address"); @@ -212,7 +256,7 @@ public class FunctionDatabase implements AutoCloseable { File file = new File(RemanConfig.INSTANCE.outputDir, filepath); Type type = Type.fromValue(typeValue); - return new Entry(address, name, file, type); + return new FunctionEntry(address, name, file, type); } return null; } @@ -236,21 +280,31 @@ public class FunctionDatabase implements AutoCloseable { PRIMARY KEY (name, filepath) )"""; + String createGlobals = """ + CREATE TABLE IF NOT EXISTS Globals ( + filepath TEXT, + name TEXT, + address TEXT, + type TEXT, + PRIMARY KEY (name, filepath) + )"""; + connection.prepareStatement(createFunctions).executeUpdate(); connection.prepareStatement(createImports).executeUpdate(); + connection.prepareStatement(createGlobals).executeUpdate(); } // Helper method to find entries by name - public List findEntriesByName(String name) throws Exception { + public List findEntriesByName(String name) throws Exception { ensureConnection(); - List results = new ArrayList<>(); + List results = new ArrayList<>(); try { // Search Functions table findByNameFunctions.setString(1, name); try (ResultSet rs = findByNameFunctions.executeQuery()) { while (rs.next()) { - Entry entry = createEntryFromResultSet(rs); + FunctionEntry entry = createEntryFromResultSet(rs); if (entry != null) { results.add(entry); } @@ -261,7 +315,7 @@ public class FunctionDatabase implements AutoCloseable { findByNameImports.setString(1, name); try (ResultSet rs = findByNameImports.executeQuery()) { while (rs.next()) { - Entry entry = createEntryFromResultSet(rs); + FunctionEntry entry = createEntryFromResultSet(rs); if (entry != null) { results.add(entry); } @@ -276,9 +330,9 @@ public class FunctionDatabase implements AutoCloseable { } // Helper method to find entries by address - public List findEntriesByAddress(Address address) throws Exception { + public List findEntriesByAddress(Address address) throws Exception { ensureConnection(); - List results = new ArrayList<>(); + List results = new ArrayList<>(); String addressStr = address.toString(); try { @@ -286,7 +340,7 @@ public class FunctionDatabase implements AutoCloseable { findByAddressFunctions.setString(1, addressStr); try (ResultSet rs = findByAddressFunctions.executeQuery()) { while (rs.next()) { - Entry entry = createEntryFromResultSet(rs); + FunctionEntry entry = createEntryFromResultSet(rs); if (entry != null) { results.add(entry); } @@ -297,7 +351,7 @@ public class FunctionDatabase implements AutoCloseable { findByAddressImports.setString(1, addressStr); try (ResultSet rs = findByAddressImports.executeQuery()) { while (rs.next()) { - Entry entry = createEntryFromResultSet(rs); + FunctionEntry entry = createEntryFromResultSet(rs); if (entry != null) { results.add(entry); } @@ -312,7 +366,7 @@ public class FunctionDatabase implements AutoCloseable { } // Helper method to add/update entry (insert or replace based on filename) - public void addEntryAt(Entry entry) throws Exception { + public void addEntryAt(FunctionEntry entry) throws Exception { ensureConnection(); String relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath() @@ -353,7 +407,7 @@ public class FunctionDatabase implements AutoCloseable { } } - public void add(Entry entry) throws Exception { + public void add(FunctionEntry entry) throws Exception { // Add entry directly to database addEntryAt(entry); } @@ -369,12 +423,12 @@ public class FunctionDatabase implements AutoCloseable { boolean madeAnyChanges = false; // Load all entries from database - List entries = loadAllEntries(); + List entries = loadAllEntries(); // Create a hash map to store symbol names Map symbolNames = new HashMap<>(); Map exportedFunctionNames = new HashMap<>(); - for (Entry entry : entries) { + for (FunctionEntry entry : entries) { Function function = script.getFunctionAt(entry.address); if (function != null) { boolean isAuto = entry.type == Type.Auto; @@ -398,9 +452,9 @@ public class FunctionDatabase implements AutoCloseable { HashSet functionsToRegenerate = new HashSet<>(); - Iterator iterator = entries.iterator(); + Iterator iterator = entries.iterator(); while (iterator.hasNext()) { - Entry entry = iterator.next(); + FunctionEntry entry = iterator.next(); Function function = script.getFunctionAt(entry.address); boolean pendingDelete = false; @@ -485,6 +539,142 @@ public class FunctionDatabase implements AutoCloseable { } } + // Global-specific methods + public List loadAllGlobals() throws Exception { + ensureConnection(); + List globals = new ArrayList<>(); + + try { + try (ResultSet rs = loadAllGlobals.executeQuery()) { + while (rs.next()) { + GlobalEntry entry = createGlobalEntryFromResultSet(rs); + if (entry != null) { + globals.add(entry); + } + } + } + + script.println("Loaded " + globals.size() + " global entries from database"); + return globals; + } catch (SQLException e) { + script.println("Error loading globals: " + e.getMessage()); + throw new Exception("Failed to load globals", e); + } + } + + private GlobalEntry createGlobalEntryFromResultSet(ResultSet rs) throws SQLException { + String filepath = rs.getString("filepath"); + String name = rs.getString("name"); + String addressStr = rs.getString("address"); + String typeStr = rs.getString("type"); + + if (addressStr != null && !addressStr.isEmpty()) { + Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr); + File file = new File(RemanConfig.INSTANCE.outputDir, filepath); + + return new GlobalEntry(address, name, typeStr, file); + } + return null; + } + + public List findGlobalsByName(String name) throws Exception { + ensureConnection(); + List results = new ArrayList<>(); + + try { + findByNameGlobals.setString(1, name); + try (ResultSet rs = findByNameGlobals.executeQuery()) { + while (rs.next()) { + GlobalEntry entry = createGlobalEntryFromResultSet(rs); + if (entry != null) { + results.add(entry); + } + } + } + + return results; + } catch (SQLException e) { + script.println("Error finding globals by name: " + e.getMessage()); + throw new Exception("Failed to find globals by name", e); + } + } + + public List findGlobalsByAddress(Address address) throws Exception { + ensureConnection(); + List results = new ArrayList<>(); + String addressStr = address.toString(); + + try { + findByAddressGlobals.setString(1, addressStr); + try (ResultSet rs = findByAddressGlobals.executeQuery()) { + while (rs.next()) { + GlobalEntry entry = createGlobalEntryFromResultSet(rs); + if (entry != null) { + results.add(entry); + } + } + } + + return results; + } catch (SQLException e) { + script.println("Error finding globals by address: " + e.getMessage()); + throw new Exception("Failed to find globals by address", e); + } + } + + public void addGlobal(Address address, String name, String dataType) throws Exception { + ensureConnection(); + + String filepath = "globals.h"; // Default filepath for globals + String addressStr = address.toString(); + + try { + insertOrReplaceGlobals.setString(1, filepath); + insertOrReplaceGlobals.setString(2, name); + insertOrReplaceGlobals.setString(3, addressStr); + insertOrReplaceGlobals.setString(4, dataType); + insertOrReplaceGlobals.executeUpdate(); + + script.println("Added/updated global: " + name + " at " + address + " with type " + dataType); + } catch (SQLException e) { + script.println("Error adding global: " + e.getMessage()); + throw new Exception("Failed to add global", e); + } + } + + public void removeGlobalsByFilepath(String filePath) throws Exception { + ensureConnection(); + + String relativePath; + + // Check if filePath is already a relative path or just a filename + File inputFile = new File(filePath); + if (inputFile.isAbsolute()) { + // Convert absolute path to relative + try { + relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath() + .relativize(inputFile.toPath()).toString().replace('\\', '/'); + } catch (IllegalArgumentException e) { + // Fallback if paths can't be relativized (different drives, etc.) + script.println("Warning: Could not relativize path: " + filePath + ", using as-is"); + relativePath = filePath.replace('\\', '/'); + } + } else { + // Already relative or just a filename, use as-is + relativePath = filePath.replace('\\', '/'); + } + + try { + deleteByFilepathGlobals.setString(1, relativePath); + int deletedCount = deleteByFilepathGlobals.executeUpdate(); + + script.println("Removed " + deletedCount + " global entries for file: " + relativePath); + } catch (SQLException e) { + script.println("Error removing global entries: " + e.getMessage()); + throw new Exception("Failed to remove global entries", e); + } + } + @Override public void close() throws Exception { this.disconnect(); diff --git a/java/ghidra/re3lib/FunctionDumper.java b/java/ghidra/re3lib/FunctionDumper.java index 08af9e04..b408b5b4 100644 --- a/java/ghidra/re3lib/FunctionDumper.java +++ b/java/ghidra/re3lib/FunctionDumper.java @@ -5,7 +5,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashSet; -import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; @@ -23,7 +22,6 @@ import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.HighSymbol; import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.Varnode; -import re3lib.GlobalDumper.GlobalRec; public class FunctionDumper { GhidraScript script; @@ -125,30 +123,49 @@ public class FunctionDumper { String sanitizedFunctionName = Utils.sanitizeIdentifier(function.getName()); String fileName = sanitizedFunctionName + ".cxx"; - - // Remove the stub file, since we now use the decompiled file - File stubFile = new File(RemanConfig.INSTANCE.dirDecompStub, fileName); - if (stubFile.exists()) { - script.println("Removing function stub " + stubFile); - stubFile.delete(); - createdFile = true; - } - - File f0 = new File(RemanConfig.INSTANCE.dirDecompFix, fileName); - if (f0.exists()) { - script.println("Func " + function.getName() + " skipped (gh_fix)"); - f0 = new File(RemanConfig.INSTANCE.dirDecompRef, fileName); - } else { - f0 = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName); - if (f0.exists()) { - f0.delete(); - } else { - createdFile = true; + Address entrypoint = function.getEntryPoint(); + List entries = functionDatabase.findEntriesByAddress(entrypoint); + FunctionDatabase.Type targetType = FunctionDatabase.Type.Auto; + for (FunctionDatabase.FunctionEntry entry : entries) { + script.println("Found existing decompiled entry at " + entry.file + " - " + entry.name); + if (targetType != FunctionDatabase.Type.Ref) { + if (entry.type == FunctionDatabase.Type.Fix) { + targetType = FunctionDatabase.Type.Ref; + } + } + if (entry.type == FunctionDatabase.Type.Stub) { + // Remove the stub file, since we now use the decompiled file + File stubFile = entry.file; + if (stubFile.exists()) { + script.println("Removing function stub " + stubFile); + stubFile.delete(); + createdFile = true; + functionDatabase.removeEntryAt(entry.file.toString()); + } } } + File targetFilename = null; + if (targetType == FunctionDatabase.Type.Ref) { + targetFilename = new File(RemanConfig.INSTANCE.dirDecompRef, fileName); + } else { + targetFilename = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName); + } + if (targetFilename.exists()) { + targetFilename.delete(); + script.println("Overwriting existing file " + targetFilename); + } else { + createdFile = true; + } + + File f0 = targetFilename; script.println("Processing " + function.getName() + " => " + f0.toString()); + // Update database + FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(entrypoint, function.getName(), f0, + targetType); + functionDatabase.addEntryAt(newEntry); + List externalFunctionCalls = new ArrayList<>(); DecompileResults decompRes = RemanConfig.INSTANCE.decompCache.getOrInsert(function); @@ -293,15 +310,20 @@ public class FunctionDumper { // Possibly generate stubs for external functions for (Function externalFunction : externalFunctionCalls) { String sanitizedExtFunctionName = Utils.sanitizeIdentifier(externalFunction.getName()); - fileName = sanitizedExtFunctionName + ".cxx"; - File f2 = new File(RemanConfig.INSTANCE.dirDecompFix, fileName); - File f3 = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName); - if (f2.exists() || f3.exists()) { - // script.println("Skipping external function: " + externalFunction.getName() + - // " - " + externalFunction.getEntryPoint()); - continue; - } + List entries1 = functionDatabase + .findEntriesByAddress(externalFunction.getEntryPoint()); + boolean needStub = true; + for (FunctionDatabase.FunctionEntry entry : entries1) { + if (entry.type == FunctionDatabase.Type.Auto || entry.type == FunctionDatabase.Type.Fix) { + needStub = false; + break; + } + } + if (!needStub) + continue; + + fileName = sanitizedExtFunctionName + ".cxx"; File f4 = new File(RemanConfig.INSTANCE.dirDecompStub, fileName); script.println("Generating function stub for " + externalFunction.getName() + " => " + f4.toString()); diff --git a/java/ghidra/re3lib/GlobalDumper.java b/java/ghidra/re3lib/GlobalDumper.java index 422ff04f..8b2378d8 100644 --- a/java/ghidra/re3lib/GlobalDumper.java +++ b/java/ghidra/re3lib/GlobalDumper.java @@ -47,91 +47,58 @@ public class GlobalDumper { }; GhidraScript script; - File manifestFile; FunctionDatabase functionDatabase; HashMap globalAddrs = new HashMap<>(); public GlobalDumper(GhidraScript script, FunctionDatabase functionDatabase) { this.script = script; this.functionDatabase = functionDatabase; - manifestFile = new File(RemanConfig.INSTANCE.outputDir, "globals.txt"); } public void removeGlobalManifest() { - if (manifestFile.exists()) { - manifestFile.delete(); + // Remove globals from database instead of file + try { + functionDatabase.removeGlobalsByFilepath("globals.h"); + globalAddrs.clear(); + } catch (Exception e) { + script.println("Error removing global manifest: " + e.getMessage()); } } public boolean loadGlobalManifest() throws Exception { - // Globals are stored in the format of - //
|| || - - if (!manifestFile.exists()) { - script.println("Global manifest file not found: " + manifestFile); - return false; - } - - // Get the dataTypeManagerService + globalAddrs.clear(); + + // Load globals from database + List dbGlobals = functionDatabase.loadAllGlobals(); + + // Get the dataTypeManagerService for parsing types DataTypeManagerService dataTypeManagerService = (DataTypeManagerService) script.getState().getTool() .getService(DataTypeManagerService.class); DataTypeManager dtm = script.getCurrentProgram().getDataTypeManager(); DataTypeParser dtp = new DataTypeParser(dataTypeManagerService, AllowedDataTypes.ALL); - try (BufferedReader reader = new BufferedReader(new FileReader(manifestFile))) { - String line; - while ((line = reader.readLine()) != null) { - String[] parts = line.split("\\|\\|"); - if (parts.length == 4) { - Address address = script.parseAddress(parts[0].trim()); - String name = parts[1].trim(); - String categoryPath = parts[2].trim(); - String dataTypePath = parts[3].trim(); - DataTypePath typePath = new DataTypePath(categoryPath, dataTypePath); - DataType type = null; - type = dtm.getDataType(typePath); - if (type == null) { - // script.println("Parsing type: " + dataTypePath); - type = dtp.parse(dataTypePath); - } - if (type == null) { - script.println("WARNING: Failed to find type: " + dataTypePath + " for global: " + name + " at " + address); - continue; - } - globalAddrs.put(address, new GlobalRec(address, name, type)); - } else { - script.println("Invalid global manifest line: " + line); - } - } - } - - script.println("Loaded " + globalAddrs.size() + " globals from " + manifestFile); - return true; - } - - public void addGlobal(Address addr, HighSymbol sym) throws Exception { - if (addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockStart) < 0 - || addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockEnd) > 0) { - throw new Exception("Global address out of range: " + addr); - } - - DataType dt = sym.getDataType(); - // if(symb.get - if (sym.getDataType().getName() == "undefined") { - // script.println("UNDEFINED: " + addr + " - " + dt.getDisplayName() + " - " + - // dt.getClass().getName()); - Data data = script.getDataAt(addr); + + for (FunctionDatabase.GlobalEntry entry : dbGlobals) { + // Note: The database stores type as string, need to reconstruct DataType + // For now, we'll parse it back from the type string stored in database + // This is a limitation of moving from the manifest format + DataType type = null; + + // Try to get from existing data at address + Data data = script.getDataAt(entry.address); if (data != null) { - dt = data.getDataType(); - // script.println("DATA: " + addr + " - " + dt.getDisplayName()); + type = data.getDataType(); } + + if (type == null) { + script.println("WARNING: Could not reconstruct type for global: " + entry.name + " at " + entry.address); + continue; + } + + globalAddrs.put(entry.address, new GlobalRec(entry.address, entry.name, type)); } - if (dt == null) { - script.println("WARNING: Missing type for global: " + sym.getName() + " at " + addr); - return; - } - // script.println("Global: " + addr + " - " + sym.getName() + " - " + - // dt.getDisplayName()); - globalAddrs.put(addr, new GlobalRec(addr, sym.getName(), dt)); + + script.println("Loaded " + globalAddrs.size() + " globals from database"); + return !globalAddrs.isEmpty(); } String escapeCString(String str) { @@ -245,28 +212,43 @@ public class GlobalDumper { } public void saveGlobalManifest() throws Exception { - File backupFile = new File(manifestFile.getParentFile(), manifestFile.getName() + ".bak"); - if (backupFile.exists()) { - if (!backupFile.delete()) { - throw new Exception("Failed to delete backup file: " + backupFile + ", globals will not be saved!"); - } - } + // Save globals to database instead of file + script.println("Saving globals to database"); - if (manifestFile.exists()) { - if (!manifestFile.renameTo(backupFile)) - throw new Exception("Failed to rename manifest file: " + manifestFile + ", globals will not be saved!"); + // Clear existing globals for the default filepath + functionDatabase.removeGlobalsByFilepath("globals.h"); + + // Add all current globals to database + for (GlobalRec global : globalAddrs.values()) { + String dataTypeName = global.type.getDisplayName(); + functionDatabase.addGlobal(global.address, global.name, dataTypeName); + } + } + + public void addGlobal(Address addr, HighSymbol sym) throws Exception { + if (addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockStart) < 0 + || addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockEnd) > 0) { + throw new Exception("Global address out of range: " + addr); } - try (PrintWriter writer = new PrintWriter(manifestFile)) { - script.println("Saving global manifest to " + manifestFile); - GlobalRec[] globals = globalAddrs.values().toArray(new GlobalRec[0]); - Arrays.sort(globals, (a, b) -> a.address.compareTo(b.address)); - for (GlobalRec global : globals) { - DataTypePath path = global.type.getDataTypePath(); - writer.println(global.address.toString() + " || " + global.name + " || " + path.getCategoryPath() + " || " - + path.getDataTypeName()); + DataType dt = sym.getDataType(); + // if(symb.get + if (sym.getDataType().getName() == "undefined") { + // script.println("UNDEFINED: " + addr + " - " + dt.getDisplayName() + " - " + + // dt.getClass().getName()); + Data data = script.getDataAt(addr); + if (data != null) { + dt = data.getDataType(); + // script.println("DATA: " + addr + " - " + dt.getDisplayName()); } } + if (dt == null) { + script.println("WARNING: Missing type for global: " + sym.getName() + " at " + addr); + return; + } + // script.println("Global: " + addr + " - " + sym.getName() + " - " + + // dt.getDisplayName()); + globalAddrs.put(addr, new GlobalRec(addr, sym.getName(), dt)); } public void sanitizeGlobalSymbols() { @@ -288,3 +270,4 @@ public class GlobalDumper { } } } +