diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index 537ff38f3f..0e001258da 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -91,6 +91,7 @@ data/typeinfo/golang/golang_1.17_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.18_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.19_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.20_anybit_any.gdt||GHIDRA||||END| +data/typeinfo/golang/golang_1.21_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/runtimesnapshot.go||GHIDRA||||END| data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END| data/typeinfo/rust/rust-common.gdt||GHIDRA||||END| diff --git a/Ghidra/Features/Base/data/typeinfo/golang/golang_1.21_anybit_any.gdt b/Ghidra/Features/Base/data/typeinfo/golang/golang_1.21_anybit_any.gdt new file mode 100644 index 0000000000..55ae6c9ca6 Binary files /dev/null and b/Ghidra/Features/Base/data/typeinfo/golang/golang_1.21_anybit_any.gdt differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangStringAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangStringAnalyzer.java index c36732635c..31800b3d7c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangStringAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangStringAnalyzer.java @@ -99,7 +99,7 @@ public class GolangStringAnalyzer extends AbstractAnalyzer { goBinary = GoRttiMapper.getSharedGoBinary(program, monitor); if (goBinary == null) { - Msg.error(this, "Golang analyzer error: unable to get GoRttiMapper"); + Msg.error(this, "Golang string analyzer error: unable to get GoRttiMapper"); return false; } markupSession = goBinary.createMarkupSession(monitor); 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 bfa1580741..03aa973861 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 @@ -112,7 +112,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { goBinary = GoRttiMapper.getSharedGoBinary(program, monitor); if (goBinary == null) { - Msg.error(this, "Golang analyzer error: unable to get GoRttiMapper"); + Msg.error(this, "Golang symbol analyzer error: unable to get GoRttiMapper"); return false; } @@ -136,6 +136,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { } if (analyzerOptions.propagateRtti) { + Msg.info(this, "Golang symbol analyzer: scheduling RTTI propagation after reference analysis"); aam.schedule(new PropagateRttiBackgroundCommand(goBinary), AnalysisPriority.REFERENCE_ANALYSIS.after().priority()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/BootstrapInfoException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/BootstrapInfoException.java new file mode 100644 index 0000000000..b68f8fd72f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/BootstrapInfoException.java @@ -0,0 +1,38 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.golang; + +import java.io.IOException; + +public class BootstrapInfoException extends IOException { + + public BootstrapInfoException() { + // empty + } + + public BootstrapInfoException(String message) { + super(message); + } + + public BootstrapInfoException(Throwable cause) { + super(cause); + } + + public BootstrapInfoException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoVer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoVer.java index 06608003fb..b9f6bca38f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoVer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/GoVer.java @@ -28,12 +28,14 @@ public enum GoVer { V1_17(1, 17), V1_18(1, 18), V1_19(1, 19), - V1_20(1, 20); + V1_20(1, 20), + V1_21(1, 21), + V1_22(1, 22); private final int major; private final int minor; - private GoVer(int major, int minor) { + GoVer(int major, int minor) { this.major = major; this.minor = minor; } @@ -89,7 +91,7 @@ public enum GoVer { } } catch (NumberFormatException e) { - return UNKNOWN; + // fall thru, return unknown } return UNKNOWN; } 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 c835584f31..f5e194dc4b 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 @@ -112,54 +112,63 @@ public class GoRttiMapper extends DataTypeMapper { GoRttiMapper supplier_result = getGoBinary(program); if (supplier_result != null) { supplier_result.init(monitor); + return supplier_result; } - return supplier_result; } - catch (IllegalArgumentException | IOException e) { - TransientProgramProperties.getProperty(program, FAILED_FLAG, - TransientProgramProperties.SCOPE.PROGRAM, Boolean.class, () -> true); // also sets it - - if (e instanceof IOException) { - // this is a more serious error, and the stack trace should be written - // to the application log - Msg.error(GoRttiMapper.class, - "Failed to read golang info for: " + program.getName(), e); - - } - AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(program); - if (aam.isAnalyzing()) { - // should cause a modal popup at end of analysis that the go binary wasn't - // supported - MessageLog log = aam.getMessageLog(); - log.appendMsg(e.getMessage()); - } - else { - Msg.warn(GoRttiMapper.class, "Golang program: " + e.getMessage()); - } - - return null; + catch (BootstrapInfoException mbie) { + Msg.warn(GoRttiMapper.class, mbie.getMessage()); + logAnalyzerMsg(program, mbie.getMessage()); } + catch (IOException e) { + // this is a more serious error, and the stack trace should be written + // to the application log + Msg.error(GoRttiMapper.class, "Failed to read golang info", e); + logAnalyzerMsg(program, e.getMessage()); + } + + // this sets the failed flag + TransientProgramProperties.getProperty(program, FAILED_FLAG, + TransientProgramProperties.SCOPE.PROGRAM, Boolean.class, () -> true); + + return null; }); return goBinary; } + private static void logAnalyzerMsg(Program program, String msg) { + AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(program); + if (aam.isAnalyzing()) { + // should cause a modal popup at end of analysis that will show the message + MessageLog log = aam.getMessageLog(); + log.appendMsg(msg); + } + } + /** * Creates a {@link GoRttiMapper} representing the specified program. * * @param program {@link Program} * @return new {@link GoRttiMapper}, or null if basic golang information is not found in the * binary - * @throws IllegalArgumentException if the golang binary is an unsupported version + * @throws BootstrapInfoException if it is a golang binary and has an unsupported or + * unparseable version number or if there was a missing golang bootstrap .gdt file * @throws IOException if there was an error in the Ghidra golang rtti reading logic */ public static GoRttiMapper getGoBinary(Program program) - throws IllegalArgumentException, IOException { + throws BootstrapInfoException, IOException { GoBuildInfo buildInfo = GoBuildInfo.fromProgram(program); - GoVer goVer; - if (buildInfo == null || (goVer = buildInfo.getVerEnum()) == GoVer.UNKNOWN) { + if (buildInfo == null) { + // probably not a golang binary return null; } + + GoVer goVer = buildInfo.getVerEnum(); + if (goVer == GoVer.UNKNOWN) { + throw new BootstrapInfoException( + "Unsupported Golang version, version info: '%s'".formatted(buildInfo.getVersion())); + } + ResourceFile gdtFile = findGolangBootstrapGDT(goVer, buildInfo.getPointerSize(), getGolangOSString(program)); if (gdtFile == null) { @@ -324,11 +333,11 @@ public class GoRttiMapper extends DataTypeMapper { * if not present and types recovered via DWARF should be used instead * @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 IllegalArgumentException if there is no matching bootstrap gdt for this specific + * @throws BootstrapInfoException if there is no matching bootstrap gdt for this specific * type of golang binary */ public GoRttiMapper(Program program, int ptrSize, Endian endian, GoVer goVersion, - ResourceFile archiveGDT) throws IOException, IllegalArgumentException { + ResourceFile archiveGDT) throws IOException, BootstrapInfoException { super(program, archiveGDT); this.goVersion = goVersion; @@ -357,13 +366,15 @@ public class GoRttiMapper extends DataTypeMapper { if (archiveGDT == null) { // a normal'ish situation where there isn't a .gdt for this arch/binary and there // isn't any DWARF. - throw new IllegalArgumentException( - "Missing golang .gdt archive for %s, no fallback DWARF info, unable to extract golang RTTI info." + throw new BootstrapInfoException( + "Missing golang .gdt archive for %s, no fallback DWARF info, unable to extract Golang RTTI info." .formatted(goVersion)); } - // a bad situation where the data type info is corrupted - throw new IOException("Invalid or missing Golang bootstrap GDT file: %s" - .formatted(archiveGDT.getAbsolutePath())); + + // we have a .gdt, but something failed. + throw new IOException("Invalid Golang bootstrap GDT file or struct mapping info: %s" + .formatted(archiveGDT.getAbsolutePath()), + e); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoArrayType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoArrayType.java index d2f1a305fb..1719ffceb9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoArrayType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoArrayType.java @@ -26,7 +26,7 @@ import ghidra.program.model.data.DataType; /** * {@link GoType} structure that defines an array. */ -@StructureMapping(structureName = "runtime.arraytype") +@StructureMapping(structureName = {"runtime.arraytype", "internal/abi.ArrayType"}) public class GoArrayType extends GoType { @FieldMapping diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoBaseType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoBaseType.java index 18b3ab4238..a0d68d17b4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoBaseType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoBaseType.java @@ -35,7 +35,7 @@ import ghidra.app.util.bin.format.golang.structmapping.*; * struct specialized_type { basetype_struct; (various_fields)* } struct uncommon; * */ -@StructureMapping(structureName = "runtime._type") +@StructureMapping(structureName = {"runtime._type", "internal/abi.Type"}) public class GoBaseType { @ContextField @@ -44,17 +44,17 @@ public class GoBaseType { @ContextField private GoRttiMapper programContext; - @FieldMapping(signedness = Signedness.Unsigned) + @FieldMapping(fieldName = {"size", "Size_"}, signedness = Signedness.Unsigned) private long size; - @FieldMapping + @FieldMapping(fieldName = {"ptrdata", "PtrBytes"}) private long ptrdata; @FieldMapping @EOLComment("flags") private int tflag; - @FieldMapping + @FieldMapping(fieldName = {"kind", "Kind_"}) @EOLComment private int kind; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoChanType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoChanType.java index f30fb2173a..5fbd13addb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoChanType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoChanType.java @@ -25,7 +25,7 @@ import ghidra.util.Msg; /** * A {@link GoType} structure that defines a go channel */ -@StructureMapping(structureName = "runtime.chantype") +@StructureMapping(structureName = {"runtime.chantype", "internal/abi.ChanType"}) public class GoChanType extends GoType { @FieldMapping diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoFuncType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoFuncType.java index 87da2072cf..af85ddff3f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoFuncType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoFuncType.java @@ -27,7 +27,7 @@ import ghidra.program.model.data.*; /** * A {@link GoType} structure that defines a function type. */ -@StructureMapping(structureName = "runtime.functype") +@StructureMapping(structureName = {"runtime.functype", "internal/abi.FuncType"}) public class GoFuncType extends GoType { /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoIMethod.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoIMethod.java index 8f6b2f1b77..8b0f7eaa0c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoIMethod.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoIMethod.java @@ -22,7 +22,7 @@ import ghidra.app.util.bin.format.golang.structmapping.*; import ghidra.program.model.address.Address; import ghidra.program.model.data.FunctionDefinition; -@StructureMapping(structureName = "runtime.imethod") +@StructureMapping(structureName = {"runtime.imethod", "internal/abi.Imethod"}) public class GoIMethod implements StructureMarkup { @ContextField @@ -36,7 +36,7 @@ public class GoIMethod implements StructureMarkup { @EOLComment("getName") private long name; - @FieldMapping + @FieldMapping(fieldName = {"ityp", "Typ"}) @MarkupReference("getType") private long ityp; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoInterfaceType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoInterfaceType.java index 26aa5fc2be..8382a02844 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoInterfaceType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoInterfaceType.java @@ -28,14 +28,14 @@ import ghidra.util.exception.CancelledException; /** * A {@link GoType} structure that defines a golang interface. */ -@StructureMapping(structureName = "runtime.interfacetype") +@StructureMapping(structureName = {"runtime.interfacetype", "internal/abi.InterfaceType"}) public class GoInterfaceType extends GoType { @FieldMapping @MarkupReference("getPkgPath") private long pkgpath; // pointer to name - @FieldMapping + @FieldMapping(fieldName = {"mhdr", "Methods"}) private GoSlice mhdr; public GoInterfaceType() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoMapType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoMapType.java index 290f21b257..85be7d9247 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoMapType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoMapType.java @@ -30,7 +30,7 @@ import ghidra.util.Msg; * See {@link GoRttiMapper#getMapGoType()} or the "runtime.hmap" type for the definition of * a instance of a map variable in memory. */ -@StructureMapping(structureName = "runtime.maptype") +@StructureMapping(structureName = {"runtime.maptype", "internal/abi.MapType"}) public class GoMapType extends GoType { @FieldMapping @@ -51,7 +51,7 @@ public class GoMapType extends GoType { @FieldMapping private int keysize; - @FieldMapping + @FieldMapping(fieldName = {"elemsize", "ValueSize"}) private int elemsize; @FieldMapping diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoMethod.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoMethod.java index b67e235e48..7dc3c57518 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoMethod.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoMethod.java @@ -28,7 +28,7 @@ import ghidra.util.NumericUtilities; /** * Structure that defines a method for a GoType, found in the type's {@link GoUncommonType} struct. */ -@StructureMapping(structureName = "runtime.method") +@StructureMapping(structureName = {"runtime.method", "internal/abi.Method"}) public class GoMethod implements StructureMarkup { @ContextField private GoRttiMapper programContext; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoPlainType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoPlainType.java index 2b1de93894..4647a8307a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoPlainType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoPlainType.java @@ -32,7 +32,7 @@ import ghidra.util.Msg; *

* {@link GoType} structure that defines a built-in primitive type. */ -@StructureMapping(structureName = "runtime._type") +@StructureMapping(structureName = {"runtime._type", "internal/abi.Type"}) public class GoPlainType extends GoType implements StructureReader { @Override public void readStructure() throws IOException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoPointerType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoPointerType.java index d6aaa1daf8..6d4824a31b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoPointerType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoPointerType.java @@ -26,7 +26,7 @@ import ghidra.program.model.data.PointerDataType; /** * {@link GoType} structure that defines a pointer. */ -@StructureMapping(structureName = "runtime.ptrtype") +@StructureMapping(structureName = {"runtime.ptrtype", "internal/abi.PtrType"}) public class GoPointerType extends GoType { @FieldMapping @MarkupReference("getElement") diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoSliceType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoSliceType.java index 585dcbb294..741355ccb6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoSliceType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoSliceType.java @@ -29,7 +29,7 @@ import ghidra.program.model.data.*; * See {@link GoRttiMapper#getGenericSliceDT()} or the "runtime.slice" type for the definition of * a instance of a slice variable in memory. */ -@StructureMapping(structureName = "runtime.slicetype") +@StructureMapping(structureName = {"runtime.slicetype", "internal/abi.SliceType"}) public class GoSliceType extends GoType { @FieldMapping diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructField.java index 843a117cca..1474c29559 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructField.java @@ -24,7 +24,7 @@ import ghidra.app.util.bin.format.golang.structmapping.*; /** * Structure used to define a field in a {@link GoStructType struct type}. */ -@StructureMapping(structureName = "runtime.structfield") +@StructureMapping(structureName = {"runtime.structfield", "internal/abi.StructField"}) public class GoStructField { @ContextField diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructType.java index 4a4cab90a3..85f7de8b62 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoStructType.java @@ -29,7 +29,7 @@ import ghidra.util.exception.CancelledException; /** * Golang type information about a specific structure type. */ -@StructureMapping(structureName = "runtime.structtype") +@StructureMapping(structureName = {"runtime.structtype", "internal/abi.StructType"}) public class GoStructType extends GoType { @FieldMapping diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoType.java index ea61eff210..e7836716cc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoType.java @@ -69,7 +69,7 @@ public abstract class GoType implements StructureMarkup { @ContextField protected StructureContext context; - @FieldMapping + @FieldMapping(fieldName = {"typ", "Type"}) @Markup @FieldOutput protected GoBaseType typ; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoTypeDetector.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoTypeDetector.java index 2d9ef56caa..6ebcf3b957 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoTypeDetector.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoTypeDetector.java @@ -21,9 +21,9 @@ import ghidra.app.util.bin.format.golang.structmapping.StructureMapping; /** * Small stub that is only used to fetch the "kind" field so that the real gotype can be detected */ -@StructureMapping(structureName = "runtime._type") +@StructureMapping(structureName = {"runtime._type", "internal/abi.Type"}) public class GoTypeDetector { - @FieldMapping + @FieldMapping(fieldName = {"kind", "Kind_"}) private int kind; public GoKind getKind() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoUncommonType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoUncommonType.java index 35eef0ed35..7b216afb23 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoUncommonType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/rtti/types/GoUncommonType.java @@ -26,7 +26,7 @@ import ghidra.util.Msg; * Structure found immediately after a {@link GoType} structure, if it has the uncommon flag * set. */ -@StructureMapping(structureName = "runtime.uncommontype") +@StructureMapping(structureName = {"runtime.uncommontype", "internal/abi.UncommonType"}) public class GoUncommonType { @ContextField diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java index 8291943656..246e3da45d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/DataTypeMapper.java @@ -169,21 +169,30 @@ public class DataTypeMapper implements AutoCloseable { * @throws IOException if the class's Ghidra structure data type could not be found */ public void registerStructure(Class clazz) throws IOException { - Structure structDT = null; - String structName = StructureMappingInfo.getStructureDataTypeNameForClass(clazz); - if (structName != null && !structName.isBlank()) { - structDT = getType(structName, Structure.class); - } - if (!StructureReader.class.isAssignableFrom(clazz) && structDT == null) { - if (structName == null || structName.isBlank()) { - structName = ""; + StructureMapping sma = clazz.getAnnotation(StructureMapping.class); + List structNames = sma != null ? Arrays.asList(sma.structureName()) : List.of(); + Structure structDT = getType(structNames, Structure.class); + if (structDT == null) { + String dtName = structNames.isEmpty() ? "" : String.join("|", structNames); + if (!StructureReader.class.isAssignableFrom(clazz)) { + throw new IOException("Missing struct definition for class %s, structure name: [%s]" + .formatted(clazz.getSimpleName(), dtName)); + } + if (structNames.size() != 1) { + throw new IOException( + "Bad StructMapping,StructureReader definition for class %s, structure name: [%s]" + .formatted(clazz.getSimpleName(), dtName)); } - throw new IOException( - "Missing struct definition %s - %s".formatted(clazz.getSimpleName(), structName)); } - StructureMappingInfo structMappingInfo = StructureMappingInfo.fromClass(clazz, structDT); - mappingInfo.put(clazz, structMappingInfo); + try { + StructureMappingInfo structMappingInfo = + StructureMappingInfo.fromClass(clazz, structDT); + mappingInfo.put(clazz, structMappingInfo); + } + catch (IllegalArgumentException e) { + throw new IOException(e.getMessage()); + } } /** @@ -282,7 +291,32 @@ public class DataTypeMapper implements AutoCloseable { } /** - * Returns a named {@link DataType}, searching the registered + * Returns a named {@link DataType}, searching the registered + * {@link #addProgramSearchCategoryPath(CategoryPath...) program} + * and {@link #addArchiveSearchCategoryPath(CategoryPath...) archive} category paths. + *

+ * DataTypes that were found in the attached archive gdt manager will be copied into the + * program's data type manager before being returned. + * + * @param DataType or derived type + * @param names list containing the data type name and any alternates + * @param clazz expected DataType class + * @return DataType or null if not found + */ + public T getType(List names, Class clazz) { + for (String dtName : names) { + if (dtName != null && !dtName.isBlank()) { + T result = getType(dtName, clazz); + if (result != null) { + return result; + } + } + } + return null; + } + + /** + * Returns a named {@link DataType}, searching the registered * {@link #addProgramSearchCategoryPath(CategoryPath...) program} * and {@link #addArchiveSearchCategoryPath(CategoryPath...) archive} category paths. *

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/FieldMapping.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/FieldMapping.java index c0545d61b4..29ccc9f54f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/FieldMapping.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/FieldMapping.java @@ -15,8 +15,8 @@ */ package ghidra.app.util.bin.format.golang.structmapping; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -44,10 +44,10 @@ public @interface FieldMapping { * Overrides the field name that is matched in the structure. *

* Can be a single name, or a list of names that will be used to find the structure - * field. + * field. The name is case-insensitive. * - * @return name, or list of names, of the structure field to map, or unset to use the - * java field's name + * @return name, or list of names (case insensitive), of the structure field to map, + * or unset to use the java field's name */ String[] fieldName() default ""; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMapping.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMapping.java index cf0bd953ce..6e8fa375a1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMapping.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMapping.java @@ -21,54 +21,54 @@ import java.lang.annotation.*; * Indicates that the tagged class corresponds to a Ghidra structure. *

* For fixed/static length structures, an existing Ghidra structure data type will be found and - * then bound to the tagged class, and it will control how instances of the tagged class + * then bound to the tagged class, and it will control how instances of the tagged class * are deserialized. Only fields that are interesting / relevant need to be tagged with - * a {@link FieldMapping} annotation, which causes them to be pulled into the java structure. + * a {@link FieldMapping} annotation, which causes them to be pulled into the java class. *

- * For {@link FieldOutput#isVariableLength() variable} length structures, a unique Ghidra + * For {@link FieldOutput#isVariableLength() variable} length structures, a unique Ghidra * structure data type will be created for each combination of field lengths, and the tagged * class must deserialize itself by implementing the {@link StructureReader} interface. (each * field that needs to be mapped into the Ghidra structure must be tagged with a {@link FieldOutput} * annotation) *

- * In either case, various annotations on fields and methods will control how this structure + * In either case, various annotations on fields and methods will control how this structure * will be marked up in the Ghidra program. *

* The tagged class must be {@link DataTypeMapper#registerStructure(Class) registered} with - * the {@link DataTypeMapper program context} to enable the suite of structure mapped classes + * the {@link DataTypeMapper program context} to enable the suite of structure mapped classes * to work together when applied to a Ghidra binary. *

* For variable length structure classes, when the struct mapping system creates a custom-fitted * structure to markup a specific location with its specific data, the new struct data type's name * will be patterned as "structurename_NN_MM_...", where NN and MM and etc are the lengths of the - * variable length fields found in the structure. + * variable length fields found in the structure. *

* Structure mapped classes must have a {@link StructureContext} member variable that is tagged * with the {@link ContextField} annotation, and probably should have a {@link DataTypeMapper} - * member variable (that corresponds to a more specific type of DataTypeMapper) that is also + * member variable (that corresponds to a more specific type of DataTypeMapper) that is also * tagged with the ContextField annotation. - * + * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface StructureMapping { /** - * Specifies the name of a Ghidra structure that the tagged class represents. For fixed - * length structures, the {@link DataTypeMapper} will search for this Ghidra data type - * in it's configured + * Specifies the name, and optionally alternate names, of a Ghidra structure that the tagged + * class represents. For fixed length structures, the {@link DataTypeMapper} will search + * for this Ghidra data type in it's configured * {@link DataTypeMapper#addArchiveSearchCategoryPath(ghidra.program.model.data.CategoryPath...) archive} - * and + * and * {@link DataTypeMapper#addProgramSearchCategoryPath(ghidra.program.model.data.CategoryPath...) program} * search paths. - * - * @return name of a Ghidra structure data type + * + * @return name(s) of a Ghidra structure data type */ - String structureName(); + String[] structureName(); /** - * Optional reference to a 'function' (implemented via a class) that will be called to do + * Optional reference to a 'function' (implemented via a class) that will be called to do * custom markup. - * + * * @return {@link StructureMarkupFunction} class */ @SuppressWarnings("rawtypes") diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMappingInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMappingInfo.java index 8184b4d14b..10927ebdd9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMappingInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/golang/structmapping/StructureMappingInfo.java @@ -32,18 +32,6 @@ import ghidra.util.exception.DuplicateNameException; */ public class StructureMappingInfo { - /** - * Returns the name of the structure data type that will define the binary layout - * of the mapped fields in the target class. - * - * @param targetClass structure mapped class - * @return the structure name - */ - public static String getStructureDataTypeNameForClass(Class targetClass) { - StructureMapping sma = targetClass.getAnnotation(StructureMapping.class); - return sma != null ? sma.structureName() : null; - } - /** * Returns the mapping info for a class, using annotations found in that class. * @@ -70,6 +58,7 @@ public class StructureMappingInfo { private final String structureName; private final Structure structureDataType; // null if variable length fields + private final Map fieldNameLookup; // case insensitive lookup private final List> fields = new ArrayList<>(); private final List> outputFields = new ArrayList<>(); @@ -85,7 +74,8 @@ public class StructureMappingInfo { this.structureDataType = structDataType; this.structureName = structureDataType != null ? structureDataType.getName() - : sma.structureName(); + : sma.structureName()[0]; + this.fieldNameLookup = indexStructFields(structDataType); this.useFieldMappingInfo = !StructureReader.class.isAssignableFrom(targetClass); this.instanceCreator = findInstanceCreator(); @@ -259,18 +249,6 @@ public class StructureMappingInfo { } } - private DataTypeComponent getField(String name) { - if (!useFieldMappingInfo || name == null || name.isBlank()) { - return null; - } - for (DataTypeComponent dtc : structureDataType.getDefinedComponents()) { - if (name.equals(dtc.getFieldName())) { - return dtc; - } - } - return null; - } - private void readFieldInfo(Class clazz) { Class superclass = clazz.getSuperclass(); if (superclass != null) { @@ -316,8 +294,9 @@ public class StructureMappingInfo { if (fma.optional()) { return null; } - throw new IllegalArgumentException("Missing structure field: %s in %s" - .formatted(Arrays.toString(fieldNames), targetClass.getSimpleName())); + throw new IllegalArgumentException( + "Missing structure field: %s.%s for %s.%s".formatted(structureName, + Arrays.toString(fieldNames), targetClass.getSimpleName(), field.getName())); } Signedness signedness = fma != null ? fma.signedness() : Signedness.Unspecified; @@ -346,7 +325,7 @@ public class StructureMappingInfo { private DataTypeComponent getFirstMatchingField(String[] fieldNames) { for (String fieldName : fieldNames) { - DataTypeComponent dtc = getField(fieldName); + DataTypeComponent dtc = fieldNameLookup.get(fieldName.toLowerCase()); if (dtc != null) { return dtc; } @@ -400,6 +379,20 @@ public class StructureMappingInfo { return struct.isZeroLength() ? 0 : struct.getLength(); } + private static Map indexStructFields(Structure struct) { + if (struct == null) { + return Map.of(); + } + Map result = new HashMap<>(); + for (DataTypeComponent dtc : struct.getDefinedComponents()) { + String fieldName = dtc.getFieldName(); + if (fieldName != null) { + result.put(fieldName.toLowerCase(), dtc); + } + } + return result; + } + //--------------------------------------------------------------------------------------------- interface ReadFromStructureFunction { T readStructure(StructureContext context) throws IOException; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java index 827bc672cc..d9dda4b7ae 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeArchiveIDTest.java @@ -39,17 +39,18 @@ public class DataTypeArchiveIDTest extends AbstractGenericTest { private static final String MAC_OS_10_9_GDT_PATH = "typeinfo/mac_10.9/mac_osx.gdt"; //@formatter:off - private static final Map archiveIdMap = Map.of( - WIN_VS12_32_GDT_PATH, "2644092282468053077", - WIN_VS12_64_GDT_PATH, "3193696833254024484", - GENERIC_CLIB_32_GDT_PATH, "2644097909188870631", - GENERIC_CLIB_64_GDT_PATH, "3193699959493190971", - MAC_OS_10_9_GDT_PATH, "2650667045259492112", - "typeinfo/golang/golang_1.17_anybit_any.gdt", "3533627828569507753", - "typeinfo/golang/golang_1.18_anybit_any.gdt", "3528902399865061936", - "typeinfo/golang/golang_1.19_anybit_any.gdt", "3533812166493410774", - "typeinfo/golang/golang_1.20_anybit_any.gdt", "3533817003441909616", - "typeinfo/rust/rust-common.gdt","3557867258392862055"); + private static final Map archiveIdMap = Map.ofEntries( + Map.entry(WIN_VS12_32_GDT_PATH, "2644092282468053077"), + Map.entry(WIN_VS12_64_GDT_PATH, "3193696833254024484"), + Map.entry(GENERIC_CLIB_32_GDT_PATH, "2644097909188870631"), + Map.entry(GENERIC_CLIB_64_GDT_PATH, "3193699959493190971"), + Map.entry(MAC_OS_10_9_GDT_PATH, "2650667045259492112"), + Map.entry("typeinfo/golang/golang_1.17_anybit_any.gdt", "3533627828569507753"), + Map.entry("typeinfo/golang/golang_1.18_anybit_any.gdt", "3528902399865061936"), + Map.entry("typeinfo/golang/golang_1.19_anybit_any.gdt", "3533812166493410774"), + Map.entry("typeinfo/golang/golang_1.20_anybit_any.gdt", "3533817003441909616"), + Map.entry("typeinfo/golang/golang_1.21_anybit_any.gdt", "3574190573109087960"), + Map.entry("typeinfo/rust/rust-common.gdt", "3557867258392862055")); //@formatter:on private Map getCurrentGdts() { diff --git a/Ghidra/Processors/x86/data/languages/x86-64-golang.register.info b/Ghidra/Processors/x86/data/languages/x86-64-golang.register.info index 11e51693e0..b9233453ad 100644 --- a/Ghidra/Processors/x86/data/languages/x86-64-golang.register.info +++ b/Ghidra/Processors/x86/data/languages/x86-64-golang.register.info @@ -1,6 +1,6 @@ - +