diff --git a/ghidra/ghidra_scripts/MSVC7Mangle.java b/ghidra/ghidra_scripts/MSVC7Mangle.java index 7bf4ab5..744142c 100644 --- a/ghidra/ghidra_scripts/MSVC7Mangle.java +++ b/ghidra/ghidra_scripts/MSVC7Mangle.java @@ -11,6 +11,9 @@ // appear in objdiff, replacing spaces with underscores, e.g. "operator_new" // and "`scalar_deleting_destructor'" (notice the ` and '). // +// MSVC also applies minor name mangling to C symbols. This can be enabled for +// a given symbol by placing it in a top-level namespace named extern_"C". +// // This script can be called in headless mode with the address ranges to mangle // as arguments, e.g. 0x1234-0x5678. Any symbols referenced by functions being // mangled will also be mangled in this mode (so that the references are @@ -135,21 +138,56 @@ public class MSVC7Mangle extends GhidraScript{ /* Generate a mangled name for a function */ final String nameRaw = f.getName(true); - // Internal symbols like intrinsics aren't mangled - if (nameRaw.startsWith("_")) return nameRaw; + // main() and extern "C" symbols get C name mangling + // (some other things, do, too, but just use extern "C" instead + // of making me find and list them all...) + return nameRaw == "main" || + nameRaw.startsWith("extern_\"C\"::") ? mangleCFn (f) + : mangleCppFn(f); + } - // Other special cases - switch (nameRaw) { - case "atexit": return "_atexit"; - case "main" : return "_main" ; - default : {} - } + private static String mangleCFn(final Function f) throws Exception { + /* Produce a C function mangled name + (MSVC does indeed do this despite the folk wisdom that only C++ gets + name mangling; it is certainly simpler than the C++ sort, at least) + */ + return switch (f.getCallingConventionName()) { + case "__cdecl" -> "_" + f.getName(false); + case "__stdcall" -> "_" + f.getName(false) + argSize(f); + case "__fastcall" -> "@" + f.getName(false) + argSize(f); + case "__thiscall" -> throw new Exception( + f.getName() + + "(): __thiscall not allowed for C symbols" + ); + default -> throw new Exception( + f.getName() + + "(): Need to specify calling convention" + ); + }; + } + + private static String argSize(final Function f) { + /* Produce the argument size suffix for a C function + The format is "@123" where "123" is however many bytes the arguments + occupy (each argument occupies at least four bytes). + */ + return "@" + Arrays.stream(f.getSignature(true).getArguments()) + .map(ParameterDefinition::getDataType) + .map(t -> Math.max(t.getLength(), 4)) + .reduce(0, Integer::sum) + .toString(); + } + + private String mangleCppFn(final Function f) throws Exception { + /* Produce a C++ function mangled name */ + final String nameRaw = f.getName(true); final ArrayList dict = new ArrayList<>(); final List nameParts = Arrays.asList(nameRaw.split("::")); Collections.reverse(nameParts); - final boolean isMethod = f.getCallingConventionName().equals("__thiscall") && + final boolean isMethod = f.getCallingConventionName() + .equals("__thiscall") && nameParts.size() >= 2; final String name = mangleIdentifier(nameRaw, isMethod, f.getReturnType(), dict); @@ -500,6 +538,11 @@ public class MSVC7Mangle extends GhidraScript{ ); // Other data + if (name.startsWith("extern_\"C\"::")) { + final String[] nameParts = name.split("::"); + return "_" + nameParts[nameParts.length - 1]; + } + final ArrayList dict = new ArrayList<>(); final String ident = mangleIdentifier(name, false, null, dict);