diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java index 2ccf428c5c..3197942c15 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java @@ -130,6 +130,8 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { return false; } + Msg.info(this, "Go version %s".formatted(goBinary.getGoVer())); + goTypes = goBinary.getGoTypes(); markupSession = goBinary.createMarkupSession(monitor); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoApiSnapshot.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoApiSnapshot.java index b976d069f7..62f6c97f57 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoApiSnapshot.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoApiSnapshot.java @@ -31,6 +31,7 @@ import ghidra.app.util.bin.format.golang.GoVer; import ghidra.formats.gfilesystem.FSRL; import ghidra.formats.gfilesystem.FileSystemService; import ghidra.framework.Application; +import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -124,30 +125,44 @@ public class GoApiSnapshot { return null; } - ByteProvider bp = null; - if (goVer.getPatch() == 0) { - // doesn't need diffpatching - bp = fsService.getByteProvider(fsService.getLocalFSRL(jsonFile), false, monitor); + File patchDiffFile = getPatchVerDiffFile(goVer); + if (patchDiffFile == null) { + return fsService.getByteProvider(fsService.getLocalFSRL(jsonFile), false, monitor); + } + + FSRL fsrl = fsService.getFullyQualifiedFSRL(fsService.getLocalFSRL(jsonFile), monitor); + ByteProvider bp = fsService.getDerivedByteProviderPush(fsrl, null, + "go%s.json".formatted(goVer), -1, os -> { + JsonPatch jsonPatch = JsonPatch.read(patchDiffFile); + JsonPatchApplier jpa = new JsonPatchApplier(jsonFile); + monitor.initialize(jsonPatch.getSectionCount(), + "Patching Go API snapshot %s -> %s".formatted(baseVer, goVer)); + jpa.apply(jsonPatch, monitor); + OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8); + new Gson().toJson(jpa.getJson(), osw); + osw.flush(); // don't close outputstream, handled by caller + }, monitor); + return bp; + } + + static File getPatchVerDiffFile(GoVer goVer) { + // returns the closest patch file that is present, or null if patch-ver-num is already 0 + // or no patch diff files are found for the specified major.minor version. + GoVer patchVer = goVer; + File patchDiffFile = null; + while (patchVer.getPatch() > 0 && + (patchDiffFile = getApiSnapshotFile(patchVer, "patchverdiffs/", ".diff")) == null) { + patchVer = patchVer.prevPatch(); + } + + if (patchVer.getPatch() != goVer.getPatch()) { + Msg.warn(GoApiSnapshot.class, + "Falling back from %s to %s for Go API snapshot".formatted(goVer, patchVer)); } else { - File patchDiffFile = getApiSnapshotFile(goVer, "patchverdiffs/", ".diff"); - if (patchDiffFile != null) { - FSRL fsrl = - fsService.getFullyQualifiedFSRL(fsService.getLocalFSRL(jsonFile), monitor); - bp = fsService.getDerivedByteProviderPush(fsrl, null, - "go%s.json".formatted(goVer), -1, os -> { - JsonPatch jsonPatch = JsonPatch.read(patchDiffFile); - JsonPatchApplier jpa = new JsonPatchApplier(jsonFile); - monitor.initialize(jsonPatch.getSectionCount(), - "Patching Go API snapshot %s -> %s".formatted(baseVer, goVer)); - jpa.apply(jsonPatch, monitor); - OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8); - new Gson().toJson(jpa.getJson(), osw); - osw.flush(); // don't close outputstream, handled by caller - }, monitor); - } + Msg.info(GoApiSnapshot.class, "Using Go API snapshot for %s".formatted(goVer)); } - return bp; + return patchDiffFile; } static File getApiSnapshotFile(GoVer goVer, String subdir, String suffix) { @@ -189,7 +204,7 @@ public class GoApiSnapshot { */ private static GoApiSnapshot read(InputStream is, List archNames, GoVer ver) throws IOException { - Gson gson = new GsonBuilder() + Gson gson = new GsonBuilder() // register postfix handler .registerTypeAdapter(GoTypeDef.class, new GoTypeDefDeserializer()) .registerTypeAdapterFactory(new GsonPostFixupAdapter()) .create(); @@ -418,6 +433,7 @@ public class GoApiSnapshot { TypeParams = List.of(); } } + @Override public String toString() { return "GoFuncTypeDef [Params=" + Params + ", Results=" + Results + ", TypeParams=" + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java index a274200b78..97b1c3a873 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/GoRttiMapper.java @@ -155,7 +155,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex * @return new {@link GoRttiMapper}, or null if basic Go information is not found in the * binary * @throws BootstrapInfoException if it is a Go binary and has an unsupported or - * unparseable version number or if there was a missing Go bootstrap .gdt file + * unparseable version number or if Go bootstrap info was missing * @throws IOException if there was an error in the Ghidra Go RTTI reading logic */ public static GoRttiMapper getGoBinary(Program program, TaskMonitor monitor) @@ -326,9 +326,9 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex * @param goVer version of Go * @param apiSnapshot json func signatures and data types * @throws IOException if error linking a structure mapped structure to its matching - * ghidra structure, which is a programming error or a corrupted bootstrap gdt - * @throws BootstrapInfoException if there is no matching bootstrap gdt for this specific - * type of Go binary + * ghidra structure, which is a programming error or invalid bootstrap info + * @throws BootstrapInfoException if there is no bootstrap info for this specific + * type and version of Go binary */ public GoRttiMapper(Program program, GoBuildInfo buildInfo, int ptrSize, GoVer goVer, GoApiSnapshot apiSnapshot) throws IOException, BootstrapInfoException {