diff --git a/java/ghidra/re3lib/FunctionDatabase.java b/java/ghidra/re3lib/FunctionDatabase.java index 1f3e115a..eb6d2089 100644 --- a/java/ghidra/re3lib/FunctionDatabase.java +++ b/java/ghidra/re3lib/FunctionDatabase.java @@ -46,17 +46,76 @@ public class FunctionDatabase implements AutoCloseable { } } + public static enum CallingConvention { + Cdecl(0), + Stdcall(1), + Fastcall(2); + + private final int value; + + CallingConvention(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static CallingConvention fromValue(int value) { + for (CallingConvention conv : CallingConvention.values()) { + if (conv.value == value) { + return conv; + } + } + throw new IllegalArgumentException("Unknown calling convention value: " + value); + } + + public static CallingConvention fromString(String convStr) { + if (convStr == null) { + return Cdecl; // Default + } + + String lower = convStr.toLowerCase(); + if (lower.contains("fastcall")) { + return Fastcall; + } else if (lower.contains("stdcall")) { + return Stdcall; + } else { + return Cdecl; // Default + } + } + + @Override + public String toString() { + switch (this) { + case Fastcall: + return "fastcall"; + case Stdcall: + return "stdcall"; + case Cdecl: + default: + return "cdecl"; + } + } + } + public static class FunctionEntry { public Address address; public String name; public File file; public Type type; + public CallingConvention callingConvention; public FunctionEntry(Address address, String name, File file, Type type) { + this(address, name, file, type, CallingConvention.Cdecl); + } + + public FunctionEntry(Address address, String name, File file, Type type, CallingConvention callingConvention) { this.address = address; this.name = name; this.file = file; this.type = type; + this.callingConvention = callingConvention; } } @@ -84,6 +143,7 @@ public class FunctionDatabase implements AutoCloseable { private PreparedStatement findByAddressFunctions; private PreparedStatement findByAddressImports; private PreparedStatement insertOrReplaceFunctions; + private PreparedStatement insertOrReplaceImports; private PreparedStatement deleteByFilepathFunctions; private PreparedStatement deleteByFilepathImports; private PreparedStatement loadAllFunctions; @@ -151,19 +211,21 @@ public class FunctionDatabase implements AutoCloseable { private void prepareCachedStatements() throws SQLException { // Find by name findByNameFunctions = connection.prepareStatement( - "SELECT filepath, name, address, type FROM Functions WHERE name = ?"); + "SELECT filepath, name, address, type, calling_convention FROM Functions WHERE name = ?"); findByNameImports = connection.prepareStatement( - "SELECT filepath, name, address, type FROM Imports WHERE name = ?"); + "SELECT filepath, name, address, type, calling_convention FROM Imports WHERE name = ?"); // Find by address findByAddressFunctions = connection.prepareStatement( - "SELECT filepath, name, address, type FROM Functions WHERE address = ?"); + "SELECT filepath, name, address, type, calling_convention FROM Functions WHERE address = ?"); findByAddressImports = connection.prepareStatement( - "SELECT filepath, name, address, type FROM Imports WHERE address = ?"); + "SELECT filepath, name, address, type, calling_convention FROM Imports WHERE address = ?"); // Insert or replace insertOrReplaceFunctions = connection.prepareStatement( - "INSERT OR REPLACE INTO Functions (filepath, name, address, type) VALUES (?, ?, ?, ?)"); + "INSERT OR REPLACE INTO Functions (filepath, name, address, type, calling_convention) VALUES (?, ?, ?, ?, ?)"); + insertOrReplaceImports = connection.prepareStatement( + "INSERT OR REPLACE INTO Imports (filepath, name, address, type, calling_convention) VALUES (?, ?, ?, ?, ?)"); // Delete by filepath deleteByFilepathFunctions = connection.prepareStatement( @@ -173,9 +235,9 @@ public class FunctionDatabase implements AutoCloseable { // Load all entries loadAllFunctions = connection.prepareStatement( - "SELECT filepath, name, address, type FROM Functions"); + "SELECT filepath, name, address, type, calling_convention FROM Functions"); loadAllImports = connection.prepareStatement( - "SELECT filepath, name, address, type FROM Imports"); + "SELECT filepath, name, address, type, calling_convention FROM Imports"); // Global statements findByNameGlobals = connection.prepareStatement( @@ -201,6 +263,8 @@ public class FunctionDatabase implements AutoCloseable { findByAddressImports.close(); if (insertOrReplaceFunctions != null) insertOrReplaceFunctions.close(); + if (insertOrReplaceImports != null) + insertOrReplaceImports.close(); if (deleteByFilepathFunctions != null) deleteByFilepathFunctions.close(); if (deleteByFilepathImports != null) @@ -250,13 +314,15 @@ public class FunctionDatabase implements AutoCloseable { String name = rs.getString("name"); String addressStr = rs.getString("address"); int typeValue = rs.getInt("type"); + int callingConventionValue = rs.getInt("calling_convention"); if (addressStr != null && !addressStr.isEmpty()) { Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr); File file = new File(RemanConfig.INSTANCE.outputDir, filepath); Type type = Type.fromValue(typeValue); + CallingConvention callingConvention = CallingConvention.fromValue(callingConventionValue); - return new FunctionEntry(address, name, file, type); + return new FunctionEntry(address, name, file, type, callingConvention); } return null; } @@ -267,7 +333,8 @@ public class FunctionDatabase implements AutoCloseable { filepath TEXT, name TEXT, address TEXT, - type INTEGER, + type INTEGER DEFAULT 0, + calling_convention INTEGER DEFAULT 0, PRIMARY KEY (name, filepath) )"""; @@ -276,7 +343,8 @@ public class FunctionDatabase implements AutoCloseable { filepath TEXT, name TEXT, address TEXT, - type INTEGER, + type INTEGER DEFAULT 0, + calling_convention INTEGER DEFAULT 0, PRIMARY KEY (name, filepath) )"""; @@ -377,15 +445,40 @@ public class FunctionDatabase implements AutoCloseable { insertOrReplaceFunctions.setString(2, entry.name); insertOrReplaceFunctions.setString(3, entry.address.toString()); insertOrReplaceFunctions.setInt(4, entry.type.getValue()); + insertOrReplaceFunctions.setInt(5, entry.callingConvention.getValue()); insertOrReplaceFunctions.executeUpdate(); - script.println("Added/updated entry: " + entry.name + " at " + entry.address + " in " + relativePath); + script.println("Added/updated entry: " + entry.name + " at " + entry.address + " in " + relativePath + + " (calling convention: " + entry.callingConvention + ")"); } catch (SQLException e) { script.println("Error adding entry: " + e.getMessage()); throw new Exception("Failed to add entry", e); } } + // Helper method to add import entry + public void addImportEntry(FunctionEntry entry) throws Exception { + ensureConnection(); + + String relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath() + .relativize(entry.file.toPath()).toString().replace('\\', '/'); + + try { + insertOrReplaceImports.setString(1, relativePath); + insertOrReplaceImports.setString(2, entry.name); + insertOrReplaceImports.setString(3, entry.address.toString()); + insertOrReplaceImports.setInt(4, entry.type.getValue()); + insertOrReplaceImports.setInt(5, entry.callingConvention.getValue()); + insertOrReplaceImports.executeUpdate(); + + script.println("Added/updated import entry: " + entry.name + " at " + entry.address + " in " + relativePath + + " (calling convention: " + entry.callingConvention + ")"); + } catch (SQLException e) { + script.println("Error adding import entry: " + e.getMessage()); + throw new Exception("Failed to add import entry", e); + } + } + // Helper method to remove entry by file path public void removeEntryAt(String filePath) throws Exception { ensureConnection(); diff --git a/java/ghidra/re3lib/FunctionDumper.java b/java/ghidra/re3lib/FunctionDumper.java index 54d03193..ff41c58c 100644 --- a/java/ghidra/re3lib/FunctionDumper.java +++ b/java/ghidra/re3lib/FunctionDumper.java @@ -131,6 +131,11 @@ public class FunctionDumper { File f4 = new File(RemanConfig.INSTANCE.dirDecompStub, fileName); script.println("Generating function stub for " + externalFunction.getName() + " => " + f4.toString()); + // Extract calling convention from Ghidra function + String callingConventionName = externalFunction.getCallingConventionName(); + FunctionDatabase.CallingConvention callingConvention = FunctionDatabase.CallingConvention.fromString(callingConventionName); + script.println("Detected calling convention: " + callingConventionName + " -> " + callingConvention); + try (PrintWriter writer2 = new PrintWriter(f4, "UTF-8")) { writer2.println("// AUTO-GENERATED FILE!!!!"); writer2.println("// This function has yet to be decompiled using 'Dump Current Function' in ghidra"); @@ -141,14 +146,13 @@ public class FunctionDumper { writer2.println("#include "); writer2.println(); writer2.println("// " + externalFunction.getEntryPoint()); - writer2.println("// " + externalFunction.getName()); + writer2.println("// " + externalFunction.getName() + "(" + callingConvention + ")"); // Parse function signature to extract calling convention, return type, and // parameters String signature = externalFunction.getSignature().getPrototypeString(false); signature = signature.replace(externalFunction.getName(), sanitizedExtFunctionName); script.println("Santized Signature: " + signature); - String callingConvention = externalFunction.getCallingConventionName(); String returnType = externalFunction.getReturnType().toString(); // Generate function stub using appropriate forwarding function @@ -156,8 +160,10 @@ public class FunctionDumper { // Determine which stub function to use based on calling convention String stubFunction; - if (callingConvention != null && callingConvention.equals("__stdcall")) { + if (callingConvention == FunctionDatabase.CallingConvention.Stdcall) { stubFunction = "gh_stub_impl_stdcall"; + } else if (callingConvention == FunctionDatabase.CallingConvention.Fastcall) { + stubFunction = "gh_stub_impl_fastcall"; } else { // Default to cdecl for most cases stubFunction = "gh_stub_impl_cdecl"; @@ -189,9 +195,9 @@ public class FunctionDumper { createdFile = true; } - // Add stub function to database + // Add stub function to database with calling convention FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(externalFunction.getEntryPoint(), - externalFunction.getName(), f4, FunctionDatabase.Type.Stub); + externalFunction.getName(), f4, FunctionDatabase.Type.Stub, callingConvention); functionDatabase.addEntryAt(newEntry); } @@ -204,6 +210,11 @@ public class FunctionDumper { List entries = functionDatabase.findEntriesByAddress(entrypoint); FunctionDatabase.Type targetType = FunctionDatabase.Type.Auto; + // Extract calling convention from Ghidra function + String callingConventionName = function.getCallingConventionName(); + FunctionDatabase.CallingConvention callingConvention = FunctionDatabase.CallingConvention.fromString(callingConventionName); + script.println("Detected calling convention: " + callingConventionName + " -> " + callingConvention); + // Handle forceFixType flag if (forceFixType) { targetType = FunctionDatabase.Type.Fix; @@ -271,9 +282,9 @@ public class FunctionDumper { File f0 = targetFilename; script.println("Processing " + function.getName() + " => " + f0.toString()); - // Update database + // Update database with calling convention FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(entrypoint, function.getName(), f0, - targetType); + targetType, callingConvention); functionDatabase.addEntryAt(newEntry); List externalFunctionCalls = new ArrayList<>(); @@ -305,8 +316,8 @@ public class FunctionDumper { // Parse preliminary line tokens for (int i = 0; i < line.getNumTokens(); i++) { ClangToken token = line.getToken(i); - if (token.getText().equals("__cdecl") || token.getText().equals("__thiscall") - || token.getText().equals("__stdcall")) { + // Remove some of the calling conventions, keep __stdcall/fastcall + if (token.getText().equals("__cdecl") || token.getText().equals("__thiscall")) { // Remove function declaration continue; } @@ -410,9 +421,13 @@ public class FunctionDumper { String proto = externalFunction.getSignature().getPrototypeString(false); String name = externalFunction.getName(); proto = proto.replace(name, Utils.sanitizeIdentifier(name)); + + // Add calling convention information to the comment + String callingConv = externalFunction.getCallingConventionName(); + FunctionDatabase.CallingConvention conv = FunctionDatabase.CallingConvention.fromString(callingConv); headers.add("" + proto + "; // " + externalFunction.getEntryPoint() + " // " - + name); + + name + " // " + conv); } for (String header : headers) {