Merge remote-tracking branch 'origin/GP-3288_dev747368_golang_1_17--SQUASHED'

This commit is contained in:
Ryan Kurtz 2023-05-26 11:47:47 -04:00
commit 007cfacd6c
13 changed files with 226 additions and 62 deletions

View file

@ -86,6 +86,7 @@ data/symbols/win64/mfc90u.exports||GHIDRA||||END|
data/symbols/win64/msvcrt.hints||GHIDRA||||END|
data/typeinfo/generic/generic_clib.gdt||GHIDRA||||END|
data/typeinfo/generic/generic_clib_64.gdt||GHIDRA||||END|
data/typeinfo/golang/golang_1.17_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/golang/golang_1.18_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END|
data/typeinfo/win32/msvcrt/clsids.txt||GHIDRA||reviewed||END|

View file

@ -343,8 +343,8 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
* <p>
* The zerobase symbol is used as the location of parameters that are zero-length.
*
* @param prog
* @return
* @param prog {@link Program}
* @return {@link Address} of the runtime.zerobase, or artificial substitute
*/
public static Address getZerobaseAddress(Program prog) {
Symbol zerobaseSym = SymbolUtilities.getUniqueSymbol(prog, "runtime.zerobase");

View file

@ -17,7 +17,6 @@ package ghidra.app.util.bin.format.golang.rtti;
import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.golang.structmapping.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.SymbolUtilities;
@ -31,27 +30,51 @@ public class GoFuncData implements StructureMarkup<GoFuncData> {
@ContextField
private StructureContext<GoFuncData> context;
@FieldMapping
@FieldMapping(optional = true)
@EOLComment("description")
@MarkupReference("funcAddress")
private long entryoff;
private long entryoff; // valid in >=1.18, relative offset of function
@FieldMapping(optional = true)
@EOLComment("description")
@MarkupReference("funcAddress")
private long entry; // valid in <=1.17, location of function
@FieldMapping
@MarkupReference("nameAddress")
private long nameoff;
private Address funcAddress; // set when entryoff or entry are set
public void setEntryoff(long entryoff) {
this.entryoff = entryoff;
GoModuledata moduledata = getModuledata();
this.funcAddress = moduledata != null ? moduledata.getText().add(entryoff) : null;
}
public void setEntry(long entry) {
this.entry = entry;
this.funcAddress = context.getDataTypeMapper().getCodeAddress(entry);
}
public Address getFuncAddress() {
return getModuledata().getText().add(entryoff);
return funcAddress;
}
public Address getNameAddress() {
return getModuledata().getFuncnametab().getArrayAddress().add(nameoff);
GoModuledata moduledata = getModuledata();
return moduledata != null
? moduledata.getFuncnametab().getArrayAddress().add(nameoff)
: null;
}
public String getName() throws IOException {
BinaryReader reader =
programContext.getReader(getModuledata().getFuncnametab().getArrayOffset() + nameoff);
return reader.readNextUtf8String();
GoModuledata moduledata = getModuledata();
return moduledata != null
? programContext.getReader(moduledata.getFuncnametab().getArrayOffset() + nameoff)
.readNextUtf8String()
: null;
}
public String getDescription() throws IOException {

View file

@ -28,21 +28,42 @@ public class GoFunctabEntry {
@ContextField
private StructureContext<GoFunctabEntry> context;
@FieldMapping
@FieldMapping(optional = true)
@MarkupReference("funcAddress")
private long entryoff; // offset relative to module's .text
private long entryoff; // valid in >=1.18, relative offset of function
@FieldMapping(optional = true)
@MarkupReference("funcAddress")
private long entry; // valid in <=1.17, location of function
@FieldMapping
@MarkupReference("funcData")
private long funcoff; // offset into pclntable -> _func
private Address funcAddress;
public void setEntryoff(long entryoff) {
this.entryoff = entryoff;
GoModuledata moduledata = getModuledata();
this.funcAddress = moduledata != null ? moduledata.getText().add(entryoff) : null;
}
public void setEntry(long entry) {
this.entry = entry;
this.funcAddress = programContext.getCodeAddress(entry);
}
public Address getFuncAddress() {
return getModuledata().getText().add(entryoff);
return funcAddress;
}
@Markup
public GoFuncData getFuncData() throws IOException {
return funcoff != 0 ? getModuledata().getFuncDataInstance(funcoff) : null;
GoModuledata moduledata = getModuledata();
return funcoff != 0 && moduledata != null
? moduledata.getFuncDataInstance(funcoff)
: null;
}
private GoModuledata getModuledata() {

View file

@ -24,8 +24,7 @@ import ghidra.app.util.bin.format.golang.rtti.types.GoType;
import ghidra.app.util.bin.format.golang.structmapping.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StringUTF8DataType;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
@ -85,6 +84,9 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
@FieldMapping
private GoSlice itablinks; // []*runtime.itab (array of pointers to runtime.tab)
@FieldMapping
private GoSlice textsectmap; // []runtime.textsect, symbol runtime.textsectionmap also points
public GoModuledata() {
// empty
}
@ -97,7 +99,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
* @return boolean true if match, false if no match
*/
public boolean matchesPclntab(GoPcHeader pclntab) {
return pclntab.getTextStart().equals(getText()) &&
return (!pclntab.hasTextStart() || pclntab.getTextStart().equals(getText())) &&
pclntab.getFuncnameAddress().equals(funcnametab.getArrayAddress());
}
@ -163,7 +165,6 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
itablinks.markupArray("moduledata.itablinks", GoItab.class, true, session);
//cutab.markupArray("moduledata.cutab", dataTypeMapper.getUint32DT(), false);
markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen(), session);
markupStringTable(filetab.getArrayAddress(), filetab.getLen(), session);
@ -176,6 +177,12 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
subSlice.markupArray("moduledata.ftab", GoFunctabEntry.class, false, session);
subSlice.markupArrayElements(GoFunctabEntry.class, session);
}
Structure textsectDT =
programContext.getGhidraDataType("runtime.textsect", Structure.class);
if (textsectDT != null) {
textsectmap.markupArray("runtime.textsectionmap", textsectDT, false, session);
}
}
@Markup

View file

@ -112,7 +112,7 @@ public class GoPcHeader {
Memory memory = programContext.getProgram().getMemory();
Address pclntabAddr =
memory.findBytes(range.getMinAddress(), range.getMaxAddress(), searchBytes, searchMask,
true, TaskMonitor.DUMMY);
true, monitor);
if (pclntabAddr == null) {
return null;
}
@ -154,7 +154,7 @@ public class GoPcHeader {
@FieldMapping
private byte ptrSize;
@FieldMapping
@FieldMapping(optional = true) // present >= 1.18
@MarkupReference
private long textStart; // should be same as offset of ".text"
@ -190,6 +190,10 @@ public class GoPcHeader {
return ver;
}
public boolean hasTextStart() {
return textStart != 0;
}
public Address getTextStart() {
return programContext.getDataAddress(textStart);
}

View file

@ -44,16 +44,17 @@ public class GoSlice {
private long cap;
public GoSlice() {
// emtpy
}
public GoSlice(long array, long len, long cap) {
this.array = array;
this.len = len;
this.cap = cap;
this(array, len, cap, null);
}
public GoSlice(long array, long len, long cap, GoRttiMapper programContext) {
this(array, len, cap);
this.array = array;
this.len = len;
this.cap = cap;
this.programContext = programContext;
}
@ -69,6 +70,10 @@ public class GoSlice {
return new GoSlice(array + (startElement * elementSize), elementCount, elementCount, programContext);
}
public boolean isValid() {
return array != 0 && isValid(1);
}
public boolean isValid(int elementSize) {
try {
Memory memory = programContext.getProgram().getMemory();
@ -191,7 +196,7 @@ public class GoSlice {
*/
public void markupArray(String sliceName, DataType elementType, boolean ptr,
MarkupSession session) throws IOException {
if (len == 0) {
if (len == 0 || !isValid()) {
return;
}
DataTypeManager dtm = programContext.getDTM();

View file

@ -41,14 +41,41 @@ import java.lang.annotation.Target;
@Target(FIELD)
public @interface FieldMapping {
/**
* Overrides the field name that is matched in the structure
* 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.
*
* @return name of the structure field to map, or unset to use the java field's name
* @return name, or list of names, of the structure field to map, or unset to use the
* java field's name
*/
String fieldName() default "";
String[] fieldName() default "";
/**
* Optional function that will deserialize the tagged field
* Marks this field as optional.
* <p>
* When marked optional, if a binding between the tagged java field and a structure field is
* not successfully found, this field definition will be skipped.
*
* @return boolean flag, if true this field is optional, if false or unset, the field is
* required
*/
boolean optional() default false;
/**
* Specifies the name of a setter method that will be used to assign the deserialized value
* to the java field.
* <p>
* If unset, a "void setFieldname(field_type)" method will be searched for.
* <p>
* If no setter method is present, the field's value will be directly assigned.
*
* @return optional name of a setter method
*/
String setter() default "";
/**
* Optional function that will deserialize the tagged field.
*
* @return {@link FieldReadFunction}
*/

View file

@ -77,6 +77,7 @@ public class FieldMappingInfo<T> {
private final List<FieldMarkupFunction<T>> markupFuncs = new ArrayList<>();
private FieldReadFunction<T> readerFunc;
private Method setterMethod;
private FieldMappingInfo(Field field, String dtcFieldName, DataTypeComponent dtc,
Signedness signedness, int length) {
@ -198,13 +199,24 @@ public class FieldMappingInfo<T> {
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public void setReadFuncClass(Class<? extends FieldReadFunction> readFuncClass) {
if (readFuncClass != FieldReadFunction.class) {
this.readerFunc = ReflectionHelper.createInstance(readFuncClass, this);
public void setFieldValueDeserializationInfo(Class<? extends FieldReadFunction> fieldReadValueClass,
Class<?> structTargetClass, String setterNameOverride) {
// setup the logic for assigning the deserialized value to the java field
Class<?> fieldType = field.getType();
// TODO: be more strict about setter name, if specified it must be found or error
Method setterMethod = ReflectionHelper.findSetter(field.getName(), setterNameOverride,
structTargetClass, fieldType);
if (setterMethod != null) {
this.setterMethod = setterMethod;
}
// setup the logic for deserializing the value from the in-memory structure
if (fieldReadValueClass != FieldReadFunction.class) {
this.readerFunc = ReflectionHelper.createInstance(fieldReadValueClass, this);
return;
}
Class<?> fieldType = field.getType();
if (ReflectionHelper.isPrimitiveType(fieldType)) {
this.readerFunc = getReadPrimitiveValueFunc(fieldType);
return;
@ -217,6 +229,16 @@ public class FieldMappingInfo<T> {
}
public void assignField(FieldContext<T> fieldContext, Object value) throws IOException {
T structureInstance = fieldContext.getStructureInstance();
if (setterMethod != null) {
ReflectionHelper.callSetter(structureInstance, setterMethod, value);
}
else {
ReflectionHelper.assignField(field, structureInstance, value);
}
}
private FieldReadFunction<T> getReadPrimitiveValueFunc(Class<?> destClass) {
// Create a lambda that reads a primitive value from a context that is specific to the
// java field's type (destClass)

View file

@ -268,7 +268,7 @@ public class MarkupSession {
if (instance instanceof StructureMarkup<?> sm) {
String structureLabel = sm.getStructureLabel();
if (structureLabel != null && structureLabel.isBlank()) {
if (structureLabel != null && !structureLabel.isBlank()) {
labelAddress(addr, structureLabel);
}
}

View file

@ -51,11 +51,26 @@ public class ReflectionHelper {
Map.entry(Byte.TYPE, "byte"),
Map.entry(Character.class, "wchar"),
Map.entry(Character.TYPE, "wchar"));
private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_CLASSES =
Map.ofEntries(
Map.entry(Long.TYPE, Long.class),
Map.entry(Integer.TYPE, Integer.class),
Map.entry(Short.TYPE, Short.class),
Map.entry(Byte.TYPE, Byte.class),
Map.entry(Character.TYPE, Character.class));
public static boolean isPrimitiveType(Class<?> clazz) {
return NUM_CLASSES.contains(clazz);
}
public static Class<?> getPrimitiveWrapper(Class<?> primitiveType) {
Class<?> wrapperClass = PRIMITIVE_WRAPPER_CLASSES.get(primitiveType);
if (wrapperClass == null) {
throw new IllegalArgumentException();
}
return wrapperClass;
}
/**
* Write a value to a field in a java class.
*
@ -198,6 +213,17 @@ public class ReflectionHelper {
return getter;
}
public static Method findSetter(String fieldName, String setterNameOverride,
Class<?> structClass, Class<?> valueClass) {
Method setter = getMethod(structClass, setterNameOverride, valueClass);
if (setter == null) {
String setSetterName = "set%s%s".formatted(fieldName.substring(0, 1).toUpperCase(),
fieldName.substring(1));
setter = getMethod(structClass, setSetterName, valueClass);
}
return setter;
}
public static <T> Constructor<T> getCtor(Class<T> clazz, Class<?>... paramTypes) {
try {
return clazz.getDeclaredConstructor(paramTypes);
@ -209,18 +235,20 @@ public class ReflectionHelper {
}
static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
try {
// try both public and private methods in the class
return clazz.getDeclaredMethod(methodName, paramTypes);
}
catch (NoSuchMethodException | SecurityException e) {
// fail, next try inherited public methods
}
try {
return clazz.getMethod(methodName, paramTypes);
}
catch (NoSuchMethodException | SecurityException e) {
// fail
if (methodName != null && !methodName.isBlank()) {
try {
// try both public and private methods in the class
return clazz.getDeclaredMethod(methodName, paramTypes);
}
catch (NoSuchMethodException | SecurityException e) {
// fail, next try inherited public methods
}
try {
return clazz.getMethod(methodName, paramTypes);
}
catch (NoSuchMethodException | SecurityException e) {
// fail
}
}
return null;
}
@ -303,6 +331,15 @@ public class ReflectionHelper {
}
}
public static <T> void callSetter(Object obj, Method setterMethod, T value) throws IOException {
try {
setterMethod.invoke(obj, value);
}
catch (IllegalAccessException | InvocationTargetException e) {
throw new IOException(e);
}
}
/**
* Returns a list of methods that have been marked with a specific annotation.
*

View file

@ -163,7 +163,7 @@ public class StructureMappingInfo<T> {
throw new IOException("Missing read info for field: " + fieldInfo.getField());
}
Object value = readFunc.get(fieldReadContext);
assignField(fieldReadContext, value);
fieldInfo.assignField(fieldReadContext, value);
}
context.reader.setPointerIndex(context.getStructureEnd());
}
@ -271,14 +271,6 @@ public class StructureMappingInfo<T> {
return null;
}
private void assignField(FieldContext<T> fieldContext, Object value)
throws IOException {
Field field = fieldContext.fieldInfo().getField();
T structureInstance = fieldContext.getStructureInstance();
ReflectionHelper.assignField(field, structureInstance, value);
}
private void readFieldInfo(Class<?> clazz) {
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
@ -291,6 +283,10 @@ public class StructureMappingInfo<T> {
FieldOutput foa = field.getAnnotation(FieldOutput.class);
if (fma != null || foa != null) {
FieldMappingInfo<T> fmi = readFieldMappingInfo(field, fma);
if (fmi == null) {
// was marked optional field, just skip
continue;
}
field.setAccessible(true);
fields.add(fmi);
@ -314,22 +310,26 @@ public class StructureMappingInfo<T> {
}
private FieldMappingInfo<T> readFieldMappingInfo(Field field, FieldMapping fma) {
String fieldName = fma != null && !fma.fieldName().isBlank()
? fma.fieldName()
: field.getName();
DataTypeComponent dtc = getField(fieldName);
String[] fieldNames = getFieldNamesToSearchFor(field, fma);
DataTypeComponent dtc = getFirstMatchingField(fieldNames);
if (useFieldMappingInfo && dtc == null) {
if (fma.optional()) {
return null;
}
throw new IllegalArgumentException("Missing structure field: %s in %s"
.formatted(fieldName, targetClass.getSimpleName()));
.formatted(Arrays.toString(fieldNames), targetClass.getSimpleName()));
}
Signedness signedness = fma != null ? fma.signedness() : Signedness.Unspecified;
int length = fma != null ? fma.length() : -1;
FieldMappingInfo<T> fmi = useFieldMappingInfo
? FieldMappingInfo.createEarlyBinding(field, dtc, signedness, length)
: FieldMappingInfo.createLateBinding(field, fieldName, signedness, length);
: FieldMappingInfo.createLateBinding(field, fieldNames[0], signedness, length);
fmi.setReadFuncClass(fma != null ? fma.readFunc() : FieldReadFunction.class);
Class<? extends FieldReadFunction> fieldReadFuncClass =
fma != null ? fma.readFunc() : FieldReadFunction.class;
String setterNameOverride = fma != null ? fma.setter() : null;
fmi.setFieldValueDeserializationInfo(fieldReadFuncClass, targetClass, setterNameOverride);
fmi.addMarkupNestedFuncs();
fmi.addCommentMarkupFuncs();
fmi.addMarkupReferenceFunc();
@ -337,6 +337,23 @@ public class StructureMappingInfo<T> {
return fmi;
}
private String[] getFieldNamesToSearchFor(Field field, FieldMapping fma) {
String[] fmaFieldNames = fma != null ? fma.fieldName() : null;
return fmaFieldNames != null && fmaFieldNames.length != 0 && !fmaFieldNames[0].isBlank()
? fmaFieldNames
: new String[] { field.getName() };
}
private DataTypeComponent getFirstMatchingField(String[] fieldNames) {
for (String fieldName : fieldNames) {
DataTypeComponent dtc = getField(fieldName);
if (dtc != null) {
return dtc;
}
}
return null;
}
private FieldOutputInfo<T> readFieldOutputInfo(FieldMappingInfo<T> fmi, FieldOutput foa) {
FieldOutputInfo<T> foi = new FieldOutputInfo<>(fmi, foa.dataTypeName(),
foa.isVariableLength(), foa.ordinal(), foa.offset());