neoLegacy/tools/ghidra/ExportLibInfo.java
itsRevela ec5ec84407 feat: add 4JLibs comparison tooling
Tools for comparing 4JLibs binary changes between git refs:
- compare-4jlibs.py: Extracts libs, parses symbol tables via dumpbin,
  demangles with undname, generates structured diff reports
- extract_lib.py: Extracts .obj members from COFF .lib archives
- ExportLibInfo.java: Ghidra headless script for non-LTCG object files
- list-lib-symbols.sh / compare-4jlibs.sh: Shell wrappers
2026-04-17 05:55:25 -05:00

140 lines
5.3 KiB
Java

// Export symbols, functions, and external references from a COFF .lib to a JSON report.
// Designed for headless mode: pass output path as first script argument.
//
// Usage with analyzeHeadless:
// analyzeHeadless <projDir> <projName> -import <file.lib> \
// -postScript ExportLibInfo.java <output.json> -deleteProject
//
//@category 4JLibs
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
public class ExportLibInfo extends GhidraScript {
@Override
public void run() throws Exception {
String[] args = getScriptArgs();
if (args.length < 1) {
printerr("Usage: ExportLibInfo.java <output.json>");
return;
}
File outputFile = new File(args[0]);
outputFile.getParentFile().mkdirs();
PrintWriter pw = new PrintWriter(new FileWriter(outputFile, true));
String programName = currentProgram.getName();
Listing listing = currentProgram.getListing();
SymbolTable symbolTable = currentProgram.getSymbolTable();
ExternalManager extMgr = currentProgram.getExternalManager();
// Collect functions
List<String> functions = new ArrayList<>();
FunctionIterator funcIter = listing.getFunctions(true);
while (funcIter.hasNext() && !monitor.isCancelled()) {
Function f = funcIter.next();
String sig = f.getPrototypeString(false, false);
String callingConv = f.getCallingConventionName();
long size = f.getBody().getNumAddresses();
functions.add(String.format(
" {\"name\": %s, \"entry\": %s, \"signature\": %s, \"callingConvention\": %s, \"size\": %d, \"paramCount\": %d}",
jsonStr(f.getName()),
jsonStr(f.getEntryPoint().toString()),
jsonStr(sig),
jsonStr(callingConv),
size,
f.getParameterCount()
));
}
// Collect all symbols (non-function)
List<String> symbols = new ArrayList<>();
SymbolIterator symIter = symbolTable.getAllSymbols(true);
while (symIter.hasNext() && !monitor.isCancelled()) {
Symbol sym = symIter.next();
if (sym.getSymbolType() == SymbolType.FUNCTION) {
continue; // already captured above
}
if (sym.isExternal()) {
continue; // captured below
}
symbols.add(String.format(
" {\"name\": %s, \"type\": %s, \"address\": %s, \"source\": %s}",
jsonStr(sym.getName(true)),
jsonStr(sym.getSymbolType().toString()),
jsonStr(sym.getAddress().toString()),
jsonStr(sym.getSource().toString())
));
}
// Collect external symbols (imports from other libraries)
List<String> externals = new ArrayList<>();
symIter = symbolTable.getExternalSymbols();
while (symIter.hasNext() && !monitor.isCancelled()) {
Symbol sym = symIter.next();
String extLib = "";
ExternalLocation extLoc = extMgr.getExternalLocation(sym);
if (extLoc != null && extLoc.getLibraryName() != null) {
extLib = extLoc.getLibraryName();
}
externals.add(String.format(
" {\"name\": %s, \"type\": %s, \"library\": %s}",
jsonStr(sym.getName(true)),
jsonStr(sym.getSymbolType().toString()),
jsonStr(extLib)
));
}
// Write JSON object for this program/object-file
pw.println("{");
pw.println(" \"program\": " + jsonStr(programName) + ",");
pw.println(" \"language\": " + jsonStr(currentProgram.getLanguageID().toString()) + ",");
pw.println(" \"compiler\": " + jsonStr(currentProgram.getCompilerSpec().getCompilerSpecID().toString()) + ",");
pw.println(" \"functionCount\": " + functions.size() + ",");
pw.println(" \"functions\": [");
pw.println(String.join(",\n", functions));
pw.println(" ],");
pw.println(" \"symbolCount\": " + symbols.size() + ",");
pw.println(" \"symbols\": [");
pw.println(String.join(",\n", symbols));
pw.println(" ],");
pw.println(" \"externalCount\": " + externals.size() + ",");
pw.println(" \"externals\": [");
pw.println(String.join(",\n", externals));
pw.println(" ]");
pw.println("}");
pw.flush();
pw.close();
println("ExportLibInfo: wrote " + functions.size() + " functions, " +
symbols.size() + " symbols, " + externals.size() + " externals for " +
programName + " -> " + outputFile.getAbsolutePath());
}
private String jsonStr(String s) {
if (s == null) return "null";
return "\"" + s.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t") + "\"";
}
}