Upgrade function dumper for cc

This commit is contained in:
Guus Waals 2025-06-01 22:46:30 +08:00
parent ac92d40671
commit f6d2ec6efb
2 changed files with 129 additions and 21 deletions

View File

@ -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 static class FunctionEntry {
public Address address; public Address address;
public String name; public String name;
public File file; public File file;
public Type type; public Type type;
public CallingConvention callingConvention;
public FunctionEntry(Address address, String name, File file, Type type) { 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.address = address;
this.name = name; this.name = name;
this.file = file; this.file = file;
this.type = type; this.type = type;
this.callingConvention = callingConvention;
} }
} }
@ -84,6 +143,7 @@ public class FunctionDatabase implements AutoCloseable {
private PreparedStatement findByAddressFunctions; private PreparedStatement findByAddressFunctions;
private PreparedStatement findByAddressImports; private PreparedStatement findByAddressImports;
private PreparedStatement insertOrReplaceFunctions; private PreparedStatement insertOrReplaceFunctions;
private PreparedStatement insertOrReplaceImports;
private PreparedStatement deleteByFilepathFunctions; private PreparedStatement deleteByFilepathFunctions;
private PreparedStatement deleteByFilepathImports; private PreparedStatement deleteByFilepathImports;
private PreparedStatement loadAllFunctions; private PreparedStatement loadAllFunctions;
@ -151,19 +211,21 @@ public class FunctionDatabase implements AutoCloseable {
private void prepareCachedStatements() throws SQLException { private void prepareCachedStatements() throws SQLException {
// Find by name // Find by name
findByNameFunctions = connection.prepareStatement( 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( 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 // Find by address
findByAddressFunctions = connection.prepareStatement( 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( 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 // Insert or replace
insertOrReplaceFunctions = connection.prepareStatement( 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 // Delete by filepath
deleteByFilepathFunctions = connection.prepareStatement( deleteByFilepathFunctions = connection.prepareStatement(
@ -173,9 +235,9 @@ public class FunctionDatabase implements AutoCloseable {
// Load all entries // Load all entries
loadAllFunctions = connection.prepareStatement( loadAllFunctions = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Functions"); "SELECT filepath, name, address, type, calling_convention FROM Functions");
loadAllImports = connection.prepareStatement( loadAllImports = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Imports"); "SELECT filepath, name, address, type, calling_convention FROM Imports");
// Global statements // Global statements
findByNameGlobals = connection.prepareStatement( findByNameGlobals = connection.prepareStatement(
@ -201,6 +263,8 @@ public class FunctionDatabase implements AutoCloseable {
findByAddressImports.close(); findByAddressImports.close();
if (insertOrReplaceFunctions != null) if (insertOrReplaceFunctions != null)
insertOrReplaceFunctions.close(); insertOrReplaceFunctions.close();
if (insertOrReplaceImports != null)
insertOrReplaceImports.close();
if (deleteByFilepathFunctions != null) if (deleteByFilepathFunctions != null)
deleteByFilepathFunctions.close(); deleteByFilepathFunctions.close();
if (deleteByFilepathImports != null) if (deleteByFilepathImports != null)
@ -250,13 +314,15 @@ public class FunctionDatabase implements AutoCloseable {
String name = rs.getString("name"); String name = rs.getString("name");
String addressStr = rs.getString("address"); String addressStr = rs.getString("address");
int typeValue = rs.getInt("type"); int typeValue = rs.getInt("type");
int callingConventionValue = rs.getInt("calling_convention");
if (addressStr != null && !addressStr.isEmpty()) { if (addressStr != null && !addressStr.isEmpty()) {
Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr); Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr);
File file = new File(RemanConfig.INSTANCE.outputDir, filepath); File file = new File(RemanConfig.INSTANCE.outputDir, filepath);
Type type = Type.fromValue(typeValue); 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; return null;
} }
@ -267,7 +333,8 @@ public class FunctionDatabase implements AutoCloseable {
filepath TEXT, filepath TEXT,
name TEXT, name TEXT,
address TEXT, address TEXT,
type INTEGER, type INTEGER DEFAULT 0,
calling_convention INTEGER DEFAULT 0,
PRIMARY KEY (name, filepath) PRIMARY KEY (name, filepath)
)"""; )""";
@ -276,7 +343,8 @@ public class FunctionDatabase implements AutoCloseable {
filepath TEXT, filepath TEXT,
name TEXT, name TEXT,
address TEXT, address TEXT,
type INTEGER, type INTEGER DEFAULT 0,
calling_convention INTEGER DEFAULT 0,
PRIMARY KEY (name, filepath) PRIMARY KEY (name, filepath)
)"""; )""";
@ -377,15 +445,40 @@ public class FunctionDatabase implements AutoCloseable {
insertOrReplaceFunctions.setString(2, entry.name); insertOrReplaceFunctions.setString(2, entry.name);
insertOrReplaceFunctions.setString(3, entry.address.toString()); insertOrReplaceFunctions.setString(3, entry.address.toString());
insertOrReplaceFunctions.setInt(4, entry.type.getValue()); insertOrReplaceFunctions.setInt(4, entry.type.getValue());
insertOrReplaceFunctions.setInt(5, entry.callingConvention.getValue());
insertOrReplaceFunctions.executeUpdate(); 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) { } catch (SQLException e) {
script.println("Error adding entry: " + e.getMessage()); script.println("Error adding entry: " + e.getMessage());
throw new Exception("Failed to add entry", e); 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 // Helper method to remove entry by file path
public void removeEntryAt(String filePath) throws Exception { public void removeEntryAt(String filePath) throws Exception {
ensureConnection(); ensureConnection();

View File

@ -131,6 +131,11 @@ public class FunctionDumper {
File f4 = new File(RemanConfig.INSTANCE.dirDecompStub, fileName); File f4 = new File(RemanConfig.INSTANCE.dirDecompStub, fileName);
script.println("Generating function stub for " + externalFunction.getName() + " => " + f4.toString()); 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")) { try (PrintWriter writer2 = new PrintWriter(f4, "UTF-8")) {
writer2.println("// AUTO-GENERATED FILE!!!!"); writer2.println("// AUTO-GENERATED FILE!!!!");
writer2.println("// This function has yet to be decompiled using 'Dump Current Function' in ghidra"); 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 <gh_global.h>"); writer2.println("#include <gh_global.h>");
writer2.println(); writer2.println();
writer2.println("// " + externalFunction.getEntryPoint()); writer2.println("// " + externalFunction.getEntryPoint());
writer2.println("// " + externalFunction.getName()); writer2.println("// " + externalFunction.getName() + "(" + callingConvention + ")");
// Parse function signature to extract calling convention, return type, and // Parse function signature to extract calling convention, return type, and
// parameters // parameters
String signature = externalFunction.getSignature().getPrototypeString(false); String signature = externalFunction.getSignature().getPrototypeString(false);
signature = signature.replace(externalFunction.getName(), sanitizedExtFunctionName); signature = signature.replace(externalFunction.getName(), sanitizedExtFunctionName);
script.println("Santized Signature: " + signature); script.println("Santized Signature: " + signature);
String callingConvention = externalFunction.getCallingConventionName();
String returnType = externalFunction.getReturnType().toString(); String returnType = externalFunction.getReturnType().toString();
// Generate function stub using appropriate forwarding function // Generate function stub using appropriate forwarding function
@ -156,8 +160,10 @@ public class FunctionDumper {
// Determine which stub function to use based on calling convention // Determine which stub function to use based on calling convention
String stubFunction; String stubFunction;
if (callingConvention != null && callingConvention.equals("__stdcall")) { if (callingConvention == FunctionDatabase.CallingConvention.Stdcall) {
stubFunction = "gh_stub_impl_stdcall"; stubFunction = "gh_stub_impl_stdcall";
} else if (callingConvention == FunctionDatabase.CallingConvention.Fastcall) {
stubFunction = "gh_stub_impl_fastcall";
} else { } else {
// Default to cdecl for most cases // Default to cdecl for most cases
stubFunction = "gh_stub_impl_cdecl"; stubFunction = "gh_stub_impl_cdecl";
@ -189,9 +195,9 @@ public class FunctionDumper {
createdFile = true; createdFile = true;
} }
// Add stub function to database // Add stub function to database with calling convention
FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(externalFunction.getEntryPoint(), FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(externalFunction.getEntryPoint(),
externalFunction.getName(), f4, FunctionDatabase.Type.Stub); externalFunction.getName(), f4, FunctionDatabase.Type.Stub, callingConvention);
functionDatabase.addEntryAt(newEntry); functionDatabase.addEntryAt(newEntry);
} }
@ -204,6 +210,11 @@ public class FunctionDumper {
List<FunctionDatabase.FunctionEntry> entries = functionDatabase.findEntriesByAddress(entrypoint); List<FunctionDatabase.FunctionEntry> entries = functionDatabase.findEntriesByAddress(entrypoint);
FunctionDatabase.Type targetType = FunctionDatabase.Type.Auto; 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 // Handle forceFixType flag
if (forceFixType) { if (forceFixType) {
targetType = FunctionDatabase.Type.Fix; targetType = FunctionDatabase.Type.Fix;
@ -271,9 +282,9 @@ public class FunctionDumper {
File f0 = targetFilename; File f0 = targetFilename;
script.println("Processing " + function.getName() + " => " + f0.toString()); script.println("Processing " + function.getName() + " => " + f0.toString());
// Update database // Update database with calling convention
FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(entrypoint, function.getName(), f0, FunctionDatabase.FunctionEntry newEntry = new FunctionDatabase.FunctionEntry(entrypoint, function.getName(), f0,
targetType); targetType, callingConvention);
functionDatabase.addEntryAt(newEntry); functionDatabase.addEntryAt(newEntry);
List<Function> externalFunctionCalls = new ArrayList<>(); List<Function> externalFunctionCalls = new ArrayList<>();
@ -305,8 +316,8 @@ public class FunctionDumper {
// Parse preliminary line tokens // Parse preliminary line tokens
for (int i = 0; i < line.getNumTokens(); i++) { for (int i = 0; i < line.getNumTokens(); i++) {
ClangToken token = line.getToken(i); ClangToken token = line.getToken(i);
if (token.getText().equals("__cdecl") || token.getText().equals("__thiscall") // Remove some of the calling conventions, keep __stdcall/fastcall
|| token.getText().equals("__stdcall")) { if (token.getText().equals("__cdecl") || token.getText().equals("__thiscall")) {
// Remove function declaration // Remove function declaration
continue; continue;
} }
@ -410,9 +421,13 @@ public class FunctionDumper {
String proto = externalFunction.getSignature().getPrototypeString(false); String proto = externalFunction.getSignature().getPrototypeString(false);
String name = externalFunction.getName(); String name = externalFunction.getName();
proto = proto.replace(name, Utils.sanitizeIdentifier(name)); 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 headers.add("" + proto
+ "; // " + externalFunction.getEntryPoint() + " // " + "; // " + externalFunction.getEntryPoint() + " // "
+ name); + name + " // " + conv);
} }
for (String header : headers) { for (String header : headers) {