Compare commits

...

2 commits

Author SHA1 Message Date
KeybadeBlox
fd6815ae42 Complete Ghidra symbol import script 2026-02-17 22:30:10 -05:00
KeybadeBlox
92179ea9bd Add C symbol support to name mangling Ghida script 2026-02-17 20:19:14 -05:00
4 changed files with 1394 additions and 1272 deletions

View file

@ -70,14 +70,14 @@ public class EnhancedExport extends GhidraScript{
(f.isInline() ? "inline" : "notinline") + "\t" + (f.isInline() ? "inline" : "notinline") + "\t" +
Optional.ofNullable(f.getCallFixup()) Optional.ofNullable(f.getCallFixup())
.orElse("nofixup") + "\t" + .orElse("nofixup") + "\t" +
f.getName(true) + f.getName(true) + "\t" +
String.join( String.join(
"\t", "\t",
Arrays.stream(f.getSignature(true) Arrays.stream(f.getSignature(true)
.getArguments()) .getArguments())
.map(arg -> .map(arg ->
"\t" + arg.getDataType().getDisplayName() + arg.getDataType().getDisplayName() + "\t" +
"\t" + arg.getName() arg.getName()
).toArray(String[]::new) ).toArray(String[]::new)
) + ) +
(f.hasVarArgs() ? "\t..." : "") + "\n" (f.hasVarArgs() ? "\t..." : "") + "\n"

View file

@ -8,8 +8,12 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.data.ArrayDataType; import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.PointerDataType; import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.Undefined4DataType;
import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Function.FunctionUpdateType;
import ghidra.program.model.listing.Parameter;
import ghidra.program.model.listing.ParameterImpl;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
@ -50,23 +54,26 @@ public class EnhancedImport extends GhidraScript{
final Address addr, final Address addr,
final String[] parts final String[] parts
) throws Exception { ) throws Exception {
final String name = unqualified(parts[3]);
print("Importing data symbol \"" + parts[3] + "\"..."); print("Importing data symbol \"" + parts[3] + "\"...");
// Create symbol // Create symbol
final Namespace ns = importNamespace(parts[3]); final Namespace ns = importNamespace(parts[3]);
final Symbol s = Optional.ofNullable(getSymbolAt(addr)) final Symbol s = Optional.ofNullable(getSymbolAt(addr))
.orElse(createLabel(addr, name, ns, true, SourceType.USER_DEFINED)); .orElse(createLabel(
addr,
unqualified(parts[3]),
ns,
true,
SourceType.USER_DEFINED
));
// Create data // Create data
final Optional<DataType> t_maybe = makeType(parts[2]); if (makeType(parts[2]).orElse(null) instanceof DataType t) {
if (t_maybe.orElse(null) instanceof DataType t) {
clearListing(addr, addr.add(Math.max(t.getLength(), 1) - 1)); clearListing(addr, addr.add(Math.max(t.getLength(), 1) - 1));
currentProgram.getListing().createData(addr, t); currentProgram.getListing().createData(addr, t);
println(" done."); println(" done.");
} else println(", skipping."); } else println(" skipping.");
} }
private static String unqualified(final String qualifiedName) { private static String unqualified(final String qualifiedName) {
@ -98,7 +105,7 @@ public class EnhancedImport extends GhidraScript{
.getService(DataTypeQueryService.class) .getService(DataTypeQueryService.class)
.findDataTypes(baseName, null); .findDataTypes(baseName, null);
if (foundTypes.size() == 0) { if (foundTypes.size() == 0) {
print(" can't find data type \"" + baseName + "\""); print(" can't find data type \"" + baseName + "\",");
return Optional.empty(); return Optional.empty();
} }
@ -153,6 +160,38 @@ public class EnhancedImport extends GhidraScript{
final Address addr, final Address addr,
final String[] parts final String[] parts
) throws Exception { ) throws Exception {
println("TODO: function \"" + parts[6] + "\""); print("Importing function symbol \"" + parts[6] + "\"...");
final Function f = Optional.ofNullable(getFunctionAt(addr))
.orElse(createFunction(addr, parts[6]));
if (makeType(parts[2]).orElse(null) instanceof DataType t)
f.setReturnType(t, SourceType.USER_DEFINED);
f.setInline(parts[4].equals("inline"));
f.setCallFixup(parts[5].equals("nofixup") ? null : parts[5]);
f.setName(unqualified(parts[6]), SourceType.USER_DEFINED);
if (importNamespace(parts[6]) instanceof Namespace ns)
f.setParentNamespace(ns);
final ArrayList<Parameter> args = new ArrayList<>();
for (int i = 7; i < parts.length - 1; i += 2)
args.add(new ParameterImpl(
parts[i+1],
makeType(parts[i]).orElse(Undefined4DataType.dataType),
currentProgram
));
f.updateFunction(
parts[3],
null,
args,
FunctionUpdateType.DYNAMIC_STORAGE_FORMAL_PARAMS,
true,
SourceType.USER_DEFINED
);
f.setVarArgs(parts[parts.length - 1].equals("..."));
println(" done.");
} }
} }

View file

@ -11,6 +11,9 @@
// appear in objdiff, replacing spaces with underscores, e.g. "operator_new" // appear in objdiff, replacing spaces with underscores, e.g. "operator_new"
// and "`scalar_deleting_destructor'" (notice the ` and '). // 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 // 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 // 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 // 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 */ /* Generate a mangled name for a function */
final String nameRaw = f.getName(true); final String nameRaw = f.getName(true);
// Internal symbols like intrinsics aren't mangled // main() and extern "C" symbols get C name mangling
if (nameRaw.startsWith("_")) return nameRaw; // (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 private static String mangleCFn(final Function f) throws Exception {
switch (nameRaw) { /* Produce a C function mangled name
case "atexit": return "_atexit"; (MSVC does indeed do this despite the folk wisdom that only C++ gets
case "main" : return "_main" ; name mangling; it is certainly simpler than the C++ sort, at least)
default : {} */
} 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<String> dict = new ArrayList<>(); final ArrayList<String> dict = new ArrayList<>();
final List<String> nameParts = Arrays.asList(nameRaw.split("::")); final List<String> nameParts = Arrays.asList(nameRaw.split("::"));
Collections.reverse(nameParts); Collections.reverse(nameParts);
final boolean isMethod = f.getCallingConventionName().equals("__thiscall") && final boolean isMethod = f.getCallingConventionName()
.equals("__thiscall") &&
nameParts.size() >= 2; nameParts.size() >= 2;
final String name = mangleIdentifier(nameRaw, isMethod, f.getReturnType(), dict); final String name = mangleIdentifier(nameRaw, isMethod, f.getReturnType(), dict);
@ -500,6 +538,11 @@ public class MSVC7Mangle extends GhidraScript{
); );
// Other data // Other data
if (name.startsWith("extern_\"C\"::")) {
final String[] nameParts = name.split("::");
return "_" + nameParts[nameParts.length - 1];
}
final ArrayList<String> dict = new ArrayList<>(); final ArrayList<String> dict = new ArrayList<>();
final String ident = mangleIdentifier(name, false, null, dict); final String ident = mangleIdentifier(name, false, null, dict);

2544
ghidra/symboltable.tsv Normal file → Executable file

File diff suppressed because it is too large Load diff