mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge remote-tracking branch
'origin/GP-4183_dev747368_PR-6072_astrelsky_golang_1.21' (Closes #6072)
This commit is contained in:
commit
676e60f71b
29 changed files with 219 additions and 138 deletions
|
@ -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|
|
||||
|
|
Binary file not shown.
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -35,7 +35,7 @@ import ghidra.app.util.bin.format.golang.structmapping.*;
|
|||
* struct specialized_type { basetype_struct; (various_fields)* } struct uncommon;
|
||||
* </pre>
|
||||
*/
|
||||
@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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<GoIMethod> {
|
||||
|
||||
@ContextField
|
||||
|
@ -36,7 +36,7 @@ public class GoIMethod implements StructureMarkup<GoIMethod> {
|
|||
@EOLComment("getName")
|
||||
private long name;
|
||||
|
||||
@FieldMapping
|
||||
@FieldMapping(fieldName = {"ityp", "Typ"})
|
||||
@MarkupReference("getType")
|
||||
private long ityp;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<GoMethod> {
|
||||
@ContextField
|
||||
private GoRttiMapper programContext;
|
||||
|
|
|
@ -32,7 +32,7 @@ import ghidra.util.Msg;
|
|||
* <p>
|
||||
* {@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> {
|
||||
@Override
|
||||
public void readStructure() throws IOException {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -69,7 +69,7 @@ public abstract class GoType implements StructureMarkup<GoType> {
|
|||
@ContextField
|
||||
protected StructureContext<GoType> context;
|
||||
|
||||
@FieldMapping
|
||||
@FieldMapping(fieldName = {"typ", "Type"})
|
||||
@Markup
|
||||
@FieldOutput
|
||||
protected GoBaseType typ;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -169,21 +169,30 @@ public class DataTypeMapper implements AutoCloseable {
|
|||
* @throws IOException if the class's Ghidra structure data type could not be found
|
||||
*/
|
||||
public <T> void registerStructure(Class<T> 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 = "<missing>";
|
||||
StructureMapping sma = clazz.getAnnotation(StructureMapping.class);
|
||||
List<String> structNames = sma != null ? Arrays.asList(sma.structureName()) : List.of();
|
||||
Structure structDT = getType(structNames, Structure.class);
|
||||
if (structDT == null) {
|
||||
String dtName = structNames.isEmpty() ? "<missing>" : 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<T> structMappingInfo = StructureMappingInfo.fromClass(clazz, structDT);
|
||||
mappingInfo.put(clazz, structMappingInfo);
|
||||
try {
|
||||
StructureMappingInfo<T> structMappingInfo =
|
||||
StructureMappingInfo.fromClass(clazz, structDT);
|
||||
mappingInfo.put(clazz, structMappingInfo);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -281,6 +290,31 @@ public class DataTypeMapper implements AutoCloseable {
|
|||
return clazz.isInstance(dataType) ? clazz.cast(dataType) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
* 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 "";
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.lang.annotation.*;
|
|||
* 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
|
||||
* 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>
|
||||
* 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
|
||||
|
@ -53,17 +53,17 @@ import java.lang.annotation.*;
|
|||
@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
|
||||
* {@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
|
||||
|
|
|
@ -32,18 +32,6 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -70,6 +58,7 @@ public class StructureMappingInfo<T> {
|
|||
|
||||
private final String structureName;
|
||||
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<FieldOutputInfo<T>> outputFields = new ArrayList<>();
|
||||
|
@ -85,7 +74,8 @@ public class StructureMappingInfo<T> {
|
|||
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<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) {
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
if (superclass != null) {
|
||||
|
@ -316,8 +294,9 @@ public class StructureMappingInfo<T> {
|
|||
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<T> {
|
|||
|
||||
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<T> {
|
|||
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> {
|
||||
T readStructure(StructureContext<T> context) throws IOException;
|
||||
|
|
|
@ -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<String, String> 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<String, String> 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<ResourceFile, String> getCurrentGdts() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<golang>
|
||||
<!-- 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"/>
|
||||
<float_registers list="XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,XMM8,XMM9,XMM10,XMM11,XMM12,XMM13,XMM14"/>
|
||||
<stack initialoffset="8" maxalign="8"/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue