Merge remote-tracking branch

'origin/GP-4183_dev747368_PR-6072_astrelsky_golang_1.21' (Closes #6072)
This commit is contained in:
Ryan Kurtz 2024-01-10 12:00:06 -05:00
commit 676e60f71b
29 changed files with 219 additions and 138 deletions

View file

@ -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.18_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/golang/golang_1.19_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.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/golang/runtimesnapshot.go||GHIDRA||||END|
data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END| data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END|
data/typeinfo/rust/rust-common.gdt||GHIDRA||||END| data/typeinfo/rust/rust-common.gdt||GHIDRA||||END|

View file

@ -99,7 +99,7 @@ public class GolangStringAnalyzer extends AbstractAnalyzer {
goBinary = GoRttiMapper.getSharedGoBinary(program, monitor); goBinary = GoRttiMapper.getSharedGoBinary(program, monitor);
if (goBinary == null) { 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; return false;
} }
markupSession = goBinary.createMarkupSession(monitor); markupSession = goBinary.createMarkupSession(monitor);

View file

@ -112,7 +112,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
goBinary = GoRttiMapper.getSharedGoBinary(program, monitor); goBinary = GoRttiMapper.getSharedGoBinary(program, monitor);
if (goBinary == null) { 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; return false;
} }
@ -136,6 +136,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
} }
if (analyzerOptions.propagateRtti) { if (analyzerOptions.propagateRtti) {
Msg.info(this, "Golang symbol analyzer: scheduling RTTI propagation after reference analysis");
aam.schedule(new PropagateRttiBackgroundCommand(goBinary), aam.schedule(new PropagateRttiBackgroundCommand(goBinary),
AnalysisPriority.REFERENCE_ANALYSIS.after().priority()); AnalysisPriority.REFERENCE_ANALYSIS.after().priority());
} }

View file

@ -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);
}
}

View file

@ -28,12 +28,14 @@ public enum GoVer {
V1_17(1, 17), V1_17(1, 17),
V1_18(1, 18), V1_18(1, 18),
V1_19(1, 19), 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 major;
private final int minor; private final int minor;
private GoVer(int major, int minor) { GoVer(int major, int minor) {
this.major = major; this.major = major;
this.minor = minor; this.minor = minor;
} }
@ -89,7 +91,7 @@ public enum GoVer {
} }
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
return UNKNOWN; // fall thru, return unknown
} }
return UNKNOWN; return UNKNOWN;
} }

View file

@ -112,54 +112,63 @@ public class GoRttiMapper extends DataTypeMapper {
GoRttiMapper supplier_result = getGoBinary(program); GoRttiMapper supplier_result = getGoBinary(program);
if (supplier_result != null) { if (supplier_result != null) {
supplier_result.init(monitor); supplier_result.init(monitor);
return supplier_result;
} }
return supplier_result;
} }
catch (IllegalArgumentException | IOException e) { catch (BootstrapInfoException mbie) {
TransientProgramProperties.getProperty(program, FAILED_FLAG, Msg.warn(GoRttiMapper.class, mbie.getMessage());
TransientProgramProperties.SCOPE.PROGRAM, Boolean.class, () -> true); // also sets it logAnalyzerMsg(program, mbie.getMessage());
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 (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; 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. * Creates a {@link GoRttiMapper} representing the specified program.
* *
* @param program {@link Program} * @param program {@link Program}
* @return new {@link GoRttiMapper}, or null if basic golang information is not found in the * @return new {@link GoRttiMapper}, or null if basic golang information is not found in the
* binary * 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 * @throws IOException if there was an error in the Ghidra golang rtti reading logic
*/ */
public static GoRttiMapper getGoBinary(Program program) public static GoRttiMapper getGoBinary(Program program)
throws IllegalArgumentException, IOException { throws BootstrapInfoException, IOException {
GoBuildInfo buildInfo = GoBuildInfo.fromProgram(program); GoBuildInfo buildInfo = GoBuildInfo.fromProgram(program);
GoVer goVer; if (buildInfo == null) {
if (buildInfo == null || (goVer = buildInfo.getVerEnum()) == GoVer.UNKNOWN) { // probably not a golang binary
return null; return null;
} }
GoVer goVer = buildInfo.getVerEnum();
if (goVer == GoVer.UNKNOWN) {
throw new BootstrapInfoException(
"Unsupported Golang version, version info: '%s'".formatted(buildInfo.getVersion()));
}
ResourceFile gdtFile = ResourceFile gdtFile =
findGolangBootstrapGDT(goVer, buildInfo.getPointerSize(), getGolangOSString(program)); findGolangBootstrapGDT(goVer, buildInfo.getPointerSize(), getGolangOSString(program));
if (gdtFile == null) { if (gdtFile == null) {
@ -324,11 +333,11 @@ public class GoRttiMapper extends DataTypeMapper {
* if not present and types recovered via DWARF should be used instead * if not present and types recovered via DWARF should be used instead
* @throws IOException if error linking a structure mapped structure to its matching * @throws IOException if error linking a structure mapped structure to its matching
* ghidra structure, which is a programming error or a corrupted bootstrap gdt * 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 * type of golang binary
*/ */
public GoRttiMapper(Program program, int ptrSize, Endian endian, GoVer goVersion, public GoRttiMapper(Program program, int ptrSize, Endian endian, GoVer goVersion,
ResourceFile archiveGDT) throws IOException, IllegalArgumentException { ResourceFile archiveGDT) throws IOException, BootstrapInfoException {
super(program, archiveGDT); super(program, archiveGDT);
this.goVersion = goVersion; this.goVersion = goVersion;
@ -357,13 +366,15 @@ public class GoRttiMapper extends DataTypeMapper {
if (archiveGDT == null) { if (archiveGDT == null) {
// a normal'ish situation where there isn't a .gdt for this arch/binary and there // a normal'ish situation where there isn't a .gdt for this arch/binary and there
// isn't any DWARF. // isn't any DWARF.
throw new IllegalArgumentException( throw new BootstrapInfoException(
"Missing golang .gdt archive for %s, no fallback DWARF info, unable to extract golang RTTI info." "Missing golang .gdt archive for %s, no fallback DWARF info, unable to extract Golang RTTI info."
.formatted(goVersion)); .formatted(goVersion));
} }
// a bad situation where the data type info is corrupted
throw new IOException("Invalid or missing Golang bootstrap GDT file: %s" // we have a .gdt, but something failed.
.formatted(archiveGDT.getAbsolutePath())); throw new IOException("Invalid Golang bootstrap GDT file or struct mapping info: %s"
.formatted(archiveGDT.getAbsolutePath()),
e);
} }
} }

View file

@ -26,7 +26,7 @@ import ghidra.program.model.data.DataType;
/** /**
* {@link GoType} structure that defines an array. * {@link GoType} structure that defines an array.
*/ */
@StructureMapping(structureName = "runtime.arraytype") @StructureMapping(structureName = {"runtime.arraytype", "internal/abi.ArrayType"})
public class GoArrayType extends GoType { public class GoArrayType extends GoType {
@FieldMapping @FieldMapping

View file

@ -35,7 +35,7 @@ import ghidra.app.util.bin.format.golang.structmapping.*;
* struct specialized_type { basetype_struct; (various_fields)* } struct uncommon; * struct specialized_type { basetype_struct; (various_fields)* } struct uncommon;
* </pre> * </pre>
*/ */
@StructureMapping(structureName = "runtime._type") @StructureMapping(structureName = {"runtime._type", "internal/abi.Type"})
public class GoBaseType { public class GoBaseType {
@ContextField @ContextField
@ -44,17 +44,17 @@ public class GoBaseType {
@ContextField @ContextField
private GoRttiMapper programContext; private GoRttiMapper programContext;
@FieldMapping(signedness = Signedness.Unsigned) @FieldMapping(fieldName = {"size", "Size_"}, signedness = Signedness.Unsigned)
private long size; private long size;
@FieldMapping @FieldMapping(fieldName = {"ptrdata", "PtrBytes"})
private long ptrdata; private long ptrdata;
@FieldMapping @FieldMapping
@EOLComment("flags") @EOLComment("flags")
private int tflag; private int tflag;
@FieldMapping @FieldMapping(fieldName = {"kind", "Kind_"})
@EOLComment @EOLComment
private int kind; private int kind;

View file

@ -25,7 +25,7 @@ import ghidra.util.Msg;
/** /**
* A {@link GoType} structure that defines a go channel * 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 { public class GoChanType extends GoType {
@FieldMapping @FieldMapping

View file

@ -27,7 +27,7 @@ import ghidra.program.model.data.*;
/** /**
* A {@link GoType} structure that defines a function type. * 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 { public class GoFuncType extends GoType {
/** /**

View file

@ -22,7 +22,7 @@ import ghidra.app.util.bin.format.golang.structmapping.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.FunctionDefinition; import ghidra.program.model.data.FunctionDefinition;
@StructureMapping(structureName = "runtime.imethod") @StructureMapping(structureName = {"runtime.imethod", "internal/abi.Imethod"})
public class GoIMethod implements StructureMarkup<GoIMethod> { public class GoIMethod implements StructureMarkup<GoIMethod> {
@ContextField @ContextField
@ -36,7 +36,7 @@ public class GoIMethod implements StructureMarkup<GoIMethod> {
@EOLComment("getName") @EOLComment("getName")
private long name; private long name;
@FieldMapping @FieldMapping(fieldName = {"ityp", "Typ"})
@MarkupReference("getType") @MarkupReference("getType")
private long ityp; private long ityp;

View file

@ -28,14 +28,14 @@ import ghidra.util.exception.CancelledException;
/** /**
* A {@link GoType} structure that defines a golang interface. * 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 { public class GoInterfaceType extends GoType {
@FieldMapping @FieldMapping
@MarkupReference("getPkgPath") @MarkupReference("getPkgPath")
private long pkgpath; // pointer to name private long pkgpath; // pointer to name
@FieldMapping @FieldMapping(fieldName = {"mhdr", "Methods"})
private GoSlice mhdr; private GoSlice mhdr;
public GoInterfaceType() { public GoInterfaceType() {

View file

@ -30,7 +30,7 @@ import ghidra.util.Msg;
* See {@link GoRttiMapper#getMapGoType()} or the "runtime.hmap" type for the definition of * See {@link GoRttiMapper#getMapGoType()} or the "runtime.hmap" type for the definition of
* a instance of a map variable in memory. * a instance of a map variable in memory.
*/ */
@StructureMapping(structureName = "runtime.maptype") @StructureMapping(structureName = {"runtime.maptype", "internal/abi.MapType"})
public class GoMapType extends GoType { public class GoMapType extends GoType {
@FieldMapping @FieldMapping
@ -51,7 +51,7 @@ public class GoMapType extends GoType {
@FieldMapping @FieldMapping
private int keysize; private int keysize;
@FieldMapping @FieldMapping(fieldName = {"elemsize", "ValueSize"})
private int elemsize; private int elemsize;
@FieldMapping @FieldMapping

View file

@ -28,7 +28,7 @@ import ghidra.util.NumericUtilities;
/** /**
* Structure that defines a method for a GoType, found in the type's {@link GoUncommonType} struct. * 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<GoMethod> { public class GoMethod implements StructureMarkup<GoMethod> {
@ContextField @ContextField
private GoRttiMapper programContext; private GoRttiMapper programContext;

View file

@ -32,7 +32,7 @@ import ghidra.util.Msg;
* <p> * <p>
* {@link GoType} structure that defines a built-in primitive type. * {@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<GoType> { public class GoPlainType extends GoType implements StructureReader<GoType> {
@Override @Override
public void readStructure() throws IOException { public void readStructure() throws IOException {

View file

@ -26,7 +26,7 @@ import ghidra.program.model.data.PointerDataType;
/** /**
* {@link GoType} structure that defines a pointer. * {@link GoType} structure that defines a pointer.
*/ */
@StructureMapping(structureName = "runtime.ptrtype") @StructureMapping(structureName = {"runtime.ptrtype", "internal/abi.PtrType"})
public class GoPointerType extends GoType { public class GoPointerType extends GoType {
@FieldMapping @FieldMapping
@MarkupReference("getElement") @MarkupReference("getElement")

View file

@ -29,7 +29,7 @@ import ghidra.program.model.data.*;
* See {@link GoRttiMapper#getGenericSliceDT()} or the "runtime.slice" type for the definition of * See {@link GoRttiMapper#getGenericSliceDT()} or the "runtime.slice" type for the definition of
* a instance of a slice variable in memory. * a instance of a slice variable in memory.
*/ */
@StructureMapping(structureName = "runtime.slicetype") @StructureMapping(structureName = {"runtime.slicetype", "internal/abi.SliceType"})
public class GoSliceType extends GoType { public class GoSliceType extends GoType {
@FieldMapping @FieldMapping

View file

@ -24,7 +24,7 @@ import ghidra.app.util.bin.format.golang.structmapping.*;
/** /**
* Structure used to define a field in a {@link GoStructType struct type}. * 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 { public class GoStructField {
@ContextField @ContextField

View file

@ -29,7 +29,7 @@ import ghidra.util.exception.CancelledException;
/** /**
* Golang type information about a specific structure type. * Golang type information about a specific structure type.
*/ */
@StructureMapping(structureName = "runtime.structtype") @StructureMapping(structureName = {"runtime.structtype", "internal/abi.StructType"})
public class GoStructType extends GoType { public class GoStructType extends GoType {
@FieldMapping @FieldMapping

View file

@ -69,7 +69,7 @@ public abstract class GoType implements StructureMarkup<GoType> {
@ContextField @ContextField
protected StructureContext<GoType> context; protected StructureContext<GoType> context;
@FieldMapping @FieldMapping(fieldName = {"typ", "Type"})
@Markup @Markup
@FieldOutput @FieldOutput
protected GoBaseType typ; protected GoBaseType typ;

View file

@ -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 * 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 { public class GoTypeDetector {
@FieldMapping @FieldMapping(fieldName = {"kind", "Kind_"})
private int kind; private int kind;
public GoKind getKind() { public GoKind getKind() {

View file

@ -26,7 +26,7 @@ import ghidra.util.Msg;
* Structure found immediately after a {@link GoType} structure, if it has the uncommon flag * Structure found immediately after a {@link GoType} structure, if it has the uncommon flag
* set. * set.
*/ */
@StructureMapping(structureName = "runtime.uncommontype") @StructureMapping(structureName = {"runtime.uncommontype", "internal/abi.UncommonType"})
public class GoUncommonType { public class GoUncommonType {
@ContextField @ContextField

View file

@ -169,21 +169,30 @@ public class DataTypeMapper implements AutoCloseable {
* @throws IOException if the class's Ghidra structure data type could not be found * @throws IOException if the class's Ghidra structure data type could not be found
*/ */
public <T> void registerStructure(Class<T> clazz) throws IOException { public <T> void registerStructure(Class<T> clazz) throws IOException {
Structure structDT = null; StructureMapping sma = clazz.getAnnotation(StructureMapping.class);
String structName = StructureMappingInfo.getStructureDataTypeNameForClass(clazz); List<String> structNames = sma != null ? Arrays.asList(sma.structureName()) : List.of();
if (structName != null && !structName.isBlank()) { Structure structDT = getType(structNames, Structure.class);
structDT = getType(structName, Structure.class); if (structDT == null) {
} String dtName = structNames.isEmpty() ? "<missing>" : String.join("|", structNames);
if (!StructureReader.class.isAssignableFrom(clazz) && structDT == null) { if (!StructureReader.class.isAssignableFrom(clazz)) {
if (structName == null || structName.isBlank()) { throw new IOException("Missing struct definition for class %s, structure name: [%s]"
structName = "<missing>"; .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<T> structMappingInfo = StructureMappingInfo.fromClass(clazz, structDT); try {
mappingInfo.put(clazz, structMappingInfo); StructureMappingInfo<T> 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.
* <p>
* DataTypes that were found in the attached archive gdt manager will be copied into the
* program's data type manager before being returned.
*
* @param <T> 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 extends DataType> T getType(List<String> names, Class<T> 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} * {@link #addProgramSearchCategoryPath(CategoryPath...) program}
* and {@link #addArchiveSearchCategoryPath(CategoryPath...) archive} category paths. * and {@link #addArchiveSearchCategoryPath(CategoryPath...) archive} category paths.
* <p> * <p>

View file

@ -15,8 +15,8 @@
*/ */
package ghidra.app.util.bin.format.golang.structmapping; package ghidra.app.util.bin.format.golang.structmapping;
import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@ -44,10 +44,10 @@ public @interface FieldMapping {
* Overrides the field name that is matched in the structure. * Overrides the field name that is matched in the structure.
* <p> * <p>
* Can be a single name, or a list of names that will be used to find 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 * @return name, or list of names (case insensitive), of the structure field to map,
* java field's name * or unset to use the java field's name
*/ */
String[] fieldName() default ""; String[] fieldName() default "";

View file

@ -21,54 +21,54 @@ import java.lang.annotation.*;
* Indicates that the tagged class corresponds to a Ghidra structure. * Indicates that the tagged class corresponds to a Ghidra structure.
* <p> * <p>
* For fixed/static length structures, an existing Ghidra structure data type will be found and * 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 * 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.
* <p> * <p>
* 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 * 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 * 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} * field that needs to be mapped into the Ghidra structure must be tagged with a {@link FieldOutput}
* annotation) * annotation)
* <p> * <p>
* 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. * will be marked up in the Ghidra program.
* <p> * <p>
* The tagged class must be {@link DataTypeMapper#registerStructure(Class) registered} with * 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. * to work together when applied to a Ghidra binary.
* <p> * <p>
* For variable length structure classes, when the struct mapping system creates a custom-fitted * 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 * 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 * 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.
* <p> * <p>
* Structure mapped classes must have a {@link StructureContext} member variable that is tagged * 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} * 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. * tagged with the ContextField annotation.
* *
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface StructureMapping { public @interface StructureMapping {
/** /**
* Specifies the name of a Ghidra structure that the tagged class represents. For fixed * Specifies the name, and optionally alternate names, of a Ghidra structure that the tagged
* length structures, the {@link DataTypeMapper} will search for this Ghidra data type * class represents. For fixed length structures, the {@link DataTypeMapper} will search
* in it's configured * for this Ghidra data type in it's configured
* {@link DataTypeMapper#addArchiveSearchCategoryPath(ghidra.program.model.data.CategoryPath...) archive} * {@link DataTypeMapper#addArchiveSearchCategoryPath(ghidra.program.model.data.CategoryPath...) archive}
* and * and
* {@link DataTypeMapper#addProgramSearchCategoryPath(ghidra.program.model.data.CategoryPath...) program} * {@link DataTypeMapper#addProgramSearchCategoryPath(ghidra.program.model.data.CategoryPath...) program}
* search paths. * 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. * custom markup.
* *
* @return {@link StructureMarkupFunction} class * @return {@link StructureMarkupFunction} class
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")

View file

@ -32,18 +32,6 @@ import ghidra.util.exception.DuplicateNameException;
*/ */
public class StructureMappingInfo<T> { public class StructureMappingInfo<T> {
/**
* 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. * Returns the mapping info for a class, using annotations found in that class.
* *
@ -70,6 +58,7 @@ public class StructureMappingInfo<T> {
private final String structureName; private final String structureName;
private final Structure structureDataType; // null if variable length fields private final Structure structureDataType; // null if variable length fields
private final Map<String, DataTypeComponent> fieldNameLookup; // case insensitive lookup
private final List<FieldMappingInfo<T>> fields = new ArrayList<>(); private final List<FieldMappingInfo<T>> fields = new ArrayList<>();
private final List<FieldOutputInfo<T>> outputFields = new ArrayList<>(); private final List<FieldOutputInfo<T>> outputFields = new ArrayList<>();
@ -85,7 +74,8 @@ public class StructureMappingInfo<T> {
this.structureDataType = structDataType; this.structureDataType = structDataType;
this.structureName = structureDataType != null this.structureName = structureDataType != null
? structureDataType.getName() ? structureDataType.getName()
: sma.structureName(); : sma.structureName()[0];
this.fieldNameLookup = indexStructFields(structDataType);
this.useFieldMappingInfo = !StructureReader.class.isAssignableFrom(targetClass); this.useFieldMappingInfo = !StructureReader.class.isAssignableFrom(targetClass);
this.instanceCreator = findInstanceCreator(); this.instanceCreator = findInstanceCreator();
@ -259,18 +249,6 @@ public class StructureMappingInfo<T> {
} }
} }
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) { private void readFieldInfo(Class<?> clazz) {
Class<?> superclass = clazz.getSuperclass(); Class<?> superclass = clazz.getSuperclass();
if (superclass != null) { if (superclass != null) {
@ -316,8 +294,9 @@ public class StructureMappingInfo<T> {
if (fma.optional()) { if (fma.optional()) {
return null; return null;
} }
throw new IllegalArgumentException("Missing structure field: %s in %s" throw new IllegalArgumentException(
.formatted(Arrays.toString(fieldNames), targetClass.getSimpleName())); "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; Signedness signedness = fma != null ? fma.signedness() : Signedness.Unspecified;
@ -346,7 +325,7 @@ public class StructureMappingInfo<T> {
private DataTypeComponent getFirstMatchingField(String[] fieldNames) { private DataTypeComponent getFirstMatchingField(String[] fieldNames) {
for (String fieldName : fieldNames) { for (String fieldName : fieldNames) {
DataTypeComponent dtc = getField(fieldName); DataTypeComponent dtc = fieldNameLookup.get(fieldName.toLowerCase());
if (dtc != null) { if (dtc != null) {
return dtc; return dtc;
} }
@ -400,6 +379,20 @@ public class StructureMappingInfo<T> {
return struct.isZeroLength() ? 0 : struct.getLength(); return struct.isZeroLength() ? 0 : struct.getLength();
} }
private static Map<String, DataTypeComponent> indexStructFields(Structure struct) {
if (struct == null) {
return Map.of();
}
Map<String, DataTypeComponent> 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> { interface ReadFromStructureFunction<T> {
T readStructure(StructureContext<T> context) throws IOException; T readStructure(StructureContext<T> context) throws IOException;

View file

@ -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"; private static final String MAC_OS_10_9_GDT_PATH = "typeinfo/mac_10.9/mac_osx.gdt";
//@formatter:off //@formatter:off
private static final Map<String, String> archiveIdMap = Map.of( private static final Map<String, String> archiveIdMap = Map.ofEntries(
WIN_VS12_32_GDT_PATH, "2644092282468053077", Map.entry(WIN_VS12_32_GDT_PATH, "2644092282468053077"),
WIN_VS12_64_GDT_PATH, "3193696833254024484", Map.entry(WIN_VS12_64_GDT_PATH, "3193696833254024484"),
GENERIC_CLIB_32_GDT_PATH, "2644097909188870631", Map.entry(GENERIC_CLIB_32_GDT_PATH, "2644097909188870631"),
GENERIC_CLIB_64_GDT_PATH, "3193699959493190971", Map.entry(GENERIC_CLIB_64_GDT_PATH, "3193699959493190971"),
MAC_OS_10_9_GDT_PATH, "2650667045259492112", Map.entry(MAC_OS_10_9_GDT_PATH, "2650667045259492112"),
"typeinfo/golang/golang_1.17_anybit_any.gdt", "3533627828569507753", Map.entry("typeinfo/golang/golang_1.17_anybit_any.gdt", "3533627828569507753"),
"typeinfo/golang/golang_1.18_anybit_any.gdt", "3528902399865061936", Map.entry("typeinfo/golang/golang_1.18_anybit_any.gdt", "3528902399865061936"),
"typeinfo/golang/golang_1.19_anybit_any.gdt", "3533812166493410774", Map.entry("typeinfo/golang/golang_1.19_anybit_any.gdt", "3533812166493410774"),
"typeinfo/golang/golang_1.20_anybit_any.gdt", "3533817003441909616", Map.entry("typeinfo/golang/golang_1.20_anybit_any.gdt", "3533817003441909616"),
"typeinfo/rust/rust-common.gdt","3557867258392862055"); Map.entry("typeinfo/golang/golang_1.21_anybit_any.gdt", "3574190573109087960"),
Map.entry("typeinfo/rust/rust-common.gdt", "3557867258392862055"));
//@formatter:on //@formatter:on
private Map<ResourceFile, String> getCurrentGdts() { private Map<ResourceFile, String> getCurrentGdts() {

View file

@ -1,6 +1,6 @@
<golang> <golang>
<!-- see https://github.com/golang/go/blob/master/src/internal/abi/abi_amd64.go --> <!-- see https://github.com/golang/go/blob/master/src/internal/abi/abi_amd64.go -->
<register_info versions="V1_17,V1_18,V1_19,V1_20"> <!-- "all", or comma list of: V1_2,V1_16,V1_17,V1_18 --> <register_info versions="V1_17,V1_18,V1_19,V1_20,V1_21"> <!-- "all", or comma list of: V1_2,V1_16,etc -->
<int_registers list="RAX,RBX,RCX,RDI,RSI,R8,R9,R10,R11"/> <int_registers list="RAX,RBX,RCX,RDI,RSI,R8,R9,R10,R11"/>
<float_registers list="XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,XMM8,XMM9,XMM10,XMM11,XMM12,XMM13,XMM14"/> <float_registers list="XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,XMM8,XMM9,XMM10,XMM11,XMM12,XMM13,XMM14"/>
<stack initialoffset="8" maxalign="8"/> <stack initialoffset="8" maxalign="8"/>