mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 21:51:47 +00:00
Merge remote-tracking branch 'origin/GP-3440_dev747368_golang_cleanup--SQUASHED'
This commit is contained in:
commit
82f7a0a53d
@ -28,6 +28,7 @@ import ghidra.app.util.bin.format.elf.info.ElfInfoItem.ItemWithAddress;
|
||||
import ghidra.app.util.bin.format.golang.*;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoModuledata;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.MarkupSession;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.Address;
|
||||
@ -80,20 +81,22 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
programContext.discoverGoTypes(monitor);
|
||||
|
||||
GoModuledata firstModule = programContext.getFirstModule();
|
||||
programContext.labelStructure(firstModule, "firstmoduledata");
|
||||
|
||||
|
||||
UnknownProgressWrappingTaskMonitor upwtm =
|
||||
new UnknownProgressWrappingTaskMonitor(monitor, 100);
|
||||
programContext.setMarkupTaskMonitor(upwtm);
|
||||
upwtm.initialize(0);
|
||||
upwtm.setMessage("Marking up Golang RTTI structures");
|
||||
programContext.markup(firstModule, false);
|
||||
programContext.setMarkupTaskMonitor(null);
|
||||
|
||||
MarkupSession markupSession = programContext.createMarkupSession(upwtm);
|
||||
|
||||
markupSession.labelStructure(firstModule, "firstmoduledata");
|
||||
markupSession.markup(firstModule, false);
|
||||
|
||||
markupMiscInfoStructs(program);
|
||||
markupWellknownSymbols(programContext);
|
||||
markupWellknownSymbols(programContext, markupSession);
|
||||
fixupNoReturnFuncs(program);
|
||||
setupProgramContext(programContext);
|
||||
setupProgramContext(programContext, markupSession);
|
||||
programContext.recoverDataTypes(monitor);
|
||||
|
||||
if (analyzerOptions.createBootstrapDatatypeArchive) {
|
||||
@ -121,19 +124,20 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
analyzerOptions.createBootstrapDatatypeArchive);
|
||||
}
|
||||
|
||||
private void markupWellknownSymbols(GoRttiMapper programContext) throws IOException {
|
||||
private void markupWellknownSymbols(GoRttiMapper programContext, MarkupSession session)
|
||||
throws IOException {
|
||||
Program program = programContext.getProgram();
|
||||
|
||||
Symbol g0 = SymbolUtilities.getUniqueSymbol(program, "runtime.g0");
|
||||
Structure gStruct = programContext.getGhidraDataType("runtime.g", Structure.class);
|
||||
if (g0 != null && gStruct != null) {
|
||||
programContext.markupAddressIfUndefined(g0.getAddress(), gStruct);
|
||||
session.markupAddressIfUndefined(g0.getAddress(), gStruct);
|
||||
}
|
||||
|
||||
Symbol m0 = SymbolUtilities.getUniqueSymbol(program, "runtime.m0");
|
||||
Structure mStruct = programContext.getGhidraDataType("runtime.m", Structure.class);
|
||||
if (m0 != null && mStruct != null) {
|
||||
programContext.markupAddressIfUndefined(m0.getAddress(), mStruct);
|
||||
session.markupAddressIfUndefined(m0.getAddress(), mStruct);
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,7 +229,8 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
return newMB.getStart();
|
||||
}
|
||||
|
||||
private void setupProgramContext(GoRttiMapper programContext) throws IOException {
|
||||
private void setupProgramContext(GoRttiMapper programContext, MarkupSession session)
|
||||
throws IOException {
|
||||
Program program = programContext.getProgram();
|
||||
GoRegisterInfo goRegInfo = GoRegisterInfoManager.getInstance()
|
||||
.getRegisterInfoForLang(program.getLanguage(),
|
||||
@ -269,14 +274,14 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
: null;
|
||||
|
||||
if (zerobase == null) {
|
||||
programContext.labelAddress(contextMemoryAddr.add(zerobaseSymbol),
|
||||
session.labelAddress(contextMemoryAddr.add(zerobaseSymbol),
|
||||
ARTIFICIAL_RUNTIME_ZEROBASE_SYMBOLNAME);
|
||||
}
|
||||
|
||||
if (gStruct != null) {
|
||||
Address gAddr = contextMemoryAddr.add(gStructOffset);
|
||||
programContext.markupAddressIfUndefined(gAddr, gStruct);
|
||||
programContext.labelAddress(gAddr, "CURRENT_G");
|
||||
session.markupAddressIfUndefined(gAddr, gStruct);
|
||||
session.labelAddress(gAddr, "CURRENT_G");
|
||||
|
||||
Register currentGoroutineReg = goRegInfo.getCurrentGoroutineRegister();
|
||||
if (currentGoroutineReg != null && txtMemblock != null) {
|
||||
@ -295,7 +300,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
|
||||
}
|
||||
if (mStruct != null) {
|
||||
Address mAddr = contextMemoryAddr.add(mStructOffset);
|
||||
programContext.markupAddressIfUndefined(mAddr, mStruct);
|
||||
session.markupAddressIfUndefined(mAddr, mStruct);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,10 @@ package ghidra.app.util.bin.format.golang;
|
||||
import static ghidra.app.util.bin.StructConverter.ASCII;
|
||||
import static ghidra.app.util.bin.StructConverter.BYTE;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.MemoryByteProvider;
|
||||
@ -63,13 +62,19 @@ public class GoBuildInfo implements ElfInfoItem {
|
||||
* Reads a GoBuildInfo ".go.buildinfo" section from the specified Program, if present.
|
||||
*
|
||||
* @param program {@link Program} that contains the ".go.buildinfo" section
|
||||
* @return new {@link GoBuildInfo} section, if present, null if missing or error
|
||||
* @return new {@link GoBuildInfo} instance, if present, null if missing or error
|
||||
*/
|
||||
public static GoBuildInfo fromProgram(Program program) {
|
||||
ItemWithAddress<GoBuildInfo> wrappedItem = findBuildInfo(program);
|
||||
return wrappedItem != null ? wrappedItem.item() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the GoBuildInfo structure in the most common and easy locations.
|
||||
*
|
||||
* @param program {@link Program} to search
|
||||
* @return new {@link GoBuildInfo} instance, if present, null if missing or error
|
||||
*/
|
||||
public static ItemWithAddress<GoBuildInfo> findBuildInfo(Program program) {
|
||||
// try as if binary is ELF
|
||||
ItemWithAddress<GoBuildInfo> wrappedItem =
|
||||
@ -135,7 +140,7 @@ public class GoBuildInfo implements ElfInfoItem {
|
||||
private final List<GoModuleInfo> dependencies;
|
||||
private final List<GoBuildSettings> buildSettings; // compile/linker flags used during build process
|
||||
|
||||
public GoBuildInfo(int pointerSize, Endian endian, String version, String path,
|
||||
private GoBuildInfo(int pointerSize, Endian endian, String version, String path,
|
||||
GoModuleInfo moduleInfo, List<GoModuleInfo> dependencies,
|
||||
List<GoBuildSettings> buildSettings) {
|
||||
this.pointerSize = pointerSize;
|
||||
|
@ -19,8 +19,19 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Key=value element of Golang Build settings
|
||||
*
|
||||
* @param key string name of property
|
||||
* @param value string value of property
|
||||
*/
|
||||
public record GoBuildSettings(String key, String value) {
|
||||
|
||||
/**
|
||||
* Parses a "key=value" string and returns the parts as a {@link GoBuildSettings}.
|
||||
*
|
||||
* @param s "key=value" string
|
||||
* @return new {@link GoBuildSettings} instance
|
||||
* @throws IOException if error splitting the string into key and value
|
||||
*/
|
||||
public static GoBuildSettings fromString(String s) throws IOException {
|
||||
String[] parts = s.split("=", 2);
|
||||
if (parts.length != 2) {
|
||||
|
@ -17,6 +17,12 @@ package ghidra.app.util.bin.format.golang;
|
||||
|
||||
import ghidra.program.model.data.CategoryPath;
|
||||
|
||||
/**
|
||||
* Misc constant values for golang
|
||||
*/
|
||||
public class GoConstants {
|
||||
/**
|
||||
* Category path to place golang types in
|
||||
*/
|
||||
public static final CategoryPath GOLANG_CATEGORYPATH = new CategoryPath("/golang");
|
||||
}
|
||||
|
@ -40,9 +40,9 @@ public class GoFunctionFixup {
|
||||
* Assigns custom storage for a function's parameters, using the function's current
|
||||
* parameter list (formal info only) as starting information.
|
||||
*
|
||||
* @param func
|
||||
* @throws DuplicateNameException
|
||||
* @throws InvalidInputException
|
||||
* @param func Ghidra {@link Function} to fix
|
||||
* @throws DuplicateNameException if invalid parameter names
|
||||
* @throws InvalidInputException if invalid data types or storage
|
||||
*/
|
||||
public static void fixupFunction(Function func)
|
||||
throws DuplicateNameException, InvalidInputException {
|
||||
@ -51,6 +51,15 @@ public class GoFunctionFixup {
|
||||
fixupFunction(func, goVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns custom storage for a function's parameters, using the function's current
|
||||
* parameter list (formal info only) as starting information.
|
||||
*
|
||||
* @param func Ghidra {@link Function} to fix
|
||||
* @param goVersion {@link GoVer} enum
|
||||
* @throws DuplicateNameException if invalid parameter names
|
||||
* @throws InvalidInputException if invalid data types or storage
|
||||
*/
|
||||
public static void fixupFunction(Function func, GoVer goVersion)
|
||||
throws DuplicateNameException, InvalidInputException {
|
||||
Program program = func.getProgram();
|
||||
@ -150,8 +159,8 @@ public class GoFunctionFixup {
|
||||
* Returns a Ghidra data type that represents a zero-length array, to be used as a replacement
|
||||
* for a zero-length array parameter.
|
||||
*
|
||||
* @param dt
|
||||
* @return
|
||||
* @param dt data type that will donate its name to the created empty array type
|
||||
* @return {@link DataType} that represents a specific zero-length array type
|
||||
*/
|
||||
public static DataType makeEmptyArrayDataType(DataType dt) {
|
||||
StructureDataType struct = new StructureDataType(dt.getCategoryPath(),
|
||||
@ -303,7 +312,7 @@ public class GoFunctionFixup {
|
||||
* <p>
|
||||
* Only valid for storage scheme that has all register storages listed first / contiguous.
|
||||
*
|
||||
* @param varnodes
|
||||
* @param varnodes list of {@link Varnode varnodes} that will be modified in-place
|
||||
*/
|
||||
public static void reverseNonStackStorageLocations(List<Varnode> varnodes) {
|
||||
int regStorageCount;
|
||||
|
@ -15,12 +15,16 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents information about a single golang module dependency.
|
||||
*
|
||||
* @param path module path
|
||||
* @param version module version
|
||||
* @param sum checksum
|
||||
* @param replace replacement module info (may be null)
|
||||
*/
|
||||
public record GoModuleInfo(String path, String version, String sum, GoModuleInfo replace) {
|
||||
|
||||
@ -28,7 +32,8 @@ public record GoModuleInfo(String path, String version, String sum, GoModuleInfo
|
||||
* Parses a GoModuleInfo from a formatted string "path[tab]version[tab]checksum".
|
||||
*
|
||||
* @param s string to parse
|
||||
* @param replace GoModuleInfo that is the replacement for this module
|
||||
* @param replace GoModuleInfo that is the replacement for this module, or null if no
|
||||
* replacement specified
|
||||
* @return new GoModuleInfo instance, never null
|
||||
* @throws IOException if error parsing string
|
||||
*/
|
||||
|
@ -15,10 +15,9 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import org.jdom.*;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
@ -66,7 +65,7 @@ public class GoRegisterInfoManager {
|
||||
* returned that forces all parameters to be stack allocated.
|
||||
*
|
||||
* @param lang {@link Language}
|
||||
* @param goVersion
|
||||
* @param goVersion {@link GoVer} enum
|
||||
* @return {@link GoRegisterInfo}, never null
|
||||
*/
|
||||
public synchronized GoRegisterInfo getRegisterInfoForLang(Language lang, GoVer goVersion) {
|
||||
|
@ -53,6 +53,13 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup {
|
||||
public static final CategoryPath GOLANG_API_EXPORT =
|
||||
new CategoryPath(CategoryPath.ROOT, "GolangAPIExport");
|
||||
|
||||
/**
|
||||
* Returns true if the specified {@link DWARFFunction} wrapper refers to a function in a golang
|
||||
* compile unit.
|
||||
*
|
||||
* @param dfunc {@link DWARFFunction}
|
||||
* @return boolean true or false
|
||||
*/
|
||||
public static boolean isGolangFunction(DWARFFunction dfunc) {
|
||||
DIEAggregate diea = dfunc.diea;
|
||||
int cuLang = diea.getCompilationUnit().getCompileUnit().getLanguage();
|
||||
|
@ -19,7 +19,6 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.elf.info.ElfInfoItem;
|
||||
import ghidra.app.util.bin.format.elf.info.ElfNote;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
@ -32,7 +31,7 @@ public class NoteGoBuildId extends ElfNote {
|
||||
|
||||
/**
|
||||
* Reads a NoteGoBuildId from the specified BinaryReader, matching the signature of
|
||||
* {@link ElfInfoItem.ReaderFunc}.
|
||||
* ElfInfoItem.ReaderFunc.
|
||||
*
|
||||
* @param br BinaryReader
|
||||
* @param unusedProgram context (unused but needed to match signature)
|
||||
|
@ -19,16 +19,9 @@ import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.SymbolUtilities;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
@StructureMapping(structureName = "runtime._func")
|
||||
public class GoFuncData implements StructureMarkup<GoFuncData> {
|
||||
@ -84,33 +77,10 @@ public class GoFuncData implements StructureMarkup<GoFuncData> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void additionalMarkup() throws IOException {
|
||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||
Address addr = getFuncAddress();
|
||||
String name = SymbolUtilities.replaceInvalidChars(getName(), true);
|
||||
Program program = programContext.getProgram();
|
||||
|
||||
Function function = program.getListing().getFunctionAt(addr);
|
||||
if (function == null) {
|
||||
try {
|
||||
if (!program.getMemory()
|
||||
.getLoadedAndInitializedAddressSet()
|
||||
.contains(addr)) {
|
||||
Msg.warn(this,
|
||||
"Unable to create function not contained within loaded memory: %s@%s"
|
||||
.formatted(name, addr));
|
||||
return;
|
||||
}
|
||||
function = program.getFunctionManager()
|
||||
.createFunction(name, addr, new AddressSet(addr), SourceType.IMPORTED);
|
||||
}
|
||||
catch (OverlappingFunctionException | InvalidInputException e) {
|
||||
Msg.error(this, e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: this does nothing. re-evalulate this logic
|
||||
programContext.labelAddress(addr, name);
|
||||
}
|
||||
session.createFunctionIfMissing(name, addr);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,12 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.golang.rtti.types.GoInterfaceType;
|
||||
import ghidra.app.util.bin.format.golang.rtti.types.GoType;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
@ -80,7 +79,7 @@ public class GoItab implements StructureMarkup<GoItab> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void additionalMarkup() throws IOException {
|
||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||
GoSlice funSlice = getFunSlice();
|
||||
List<Address> funcAddrs = Arrays.stream(funSlice.readUIntList(programContext.getPtrSize()))
|
||||
.mapToObj(offset -> programContext.getCodeAddress(offset))
|
||||
@ -88,12 +87,12 @@ public class GoItab implements StructureMarkup<GoItab> {
|
||||
// this adds references from the elements of the artificial slice. However, the reference
|
||||
// from element[0] of the real "fun" array won't show anything in the UI even though
|
||||
// there is a outbound reference there.
|
||||
funSlice.markupElementReferences(programContext.getPtrSize(), funcAddrs);
|
||||
funSlice.markupElementReferences(programContext.getPtrSize(), funcAddrs, session);
|
||||
|
||||
GoSlice extraFunSlice =
|
||||
funSlice.getSubSlice(1, funSlice.getLen() - 1, programContext.getPtrSize());
|
||||
extraFunSlice.markupArray(getStructureName() + "_extra_itab_functions", (DataType) null,
|
||||
true);
|
||||
true, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,11 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.golang.rtti.types.GoType;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
@ -87,8 +86,16 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
private GoSlice itablinks; // []*runtime.itab (array of pointers to runtime.tab)
|
||||
|
||||
public GoModuledata() {
|
||||
// empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the data in this structure to fields in a GoPcHeader and returns true if they
|
||||
* match.
|
||||
*
|
||||
* @param pclntab GoPcHeader instance
|
||||
* @return boolean true if match, false if no match
|
||||
*/
|
||||
public boolean matchesPclntab(GoPcHeader pclntab) {
|
||||
return pclntab.getTextStart().equals(getText()) &&
|
||||
pclntab.getFuncnameAddress().equals(funcnametab.getArrayAddress());
|
||||
@ -150,15 +157,15 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void additionalMarkup() throws IOException {
|
||||
typeLinks.markupArray("moduledata.typeLinks", programContext.getInt32DT(), false);
|
||||
typeLinks.markupElementReferences(4, getTypeList());
|
||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||
typeLinks.markupArray("moduledata.typeLinks", programContext.getInt32DT(), false, session);
|
||||
typeLinks.markupElementReferences(4, getTypeList(), session);
|
||||
|
||||
itablinks.markupArray("moduledata.itablinks", GoItab.class, true);
|
||||
itablinks.markupArray("moduledata.itablinks", GoItab.class, true, session);
|
||||
|
||||
//cutab.markupArray("moduledata.cutab", dataTypeMapper.getUint32DT(), false);
|
||||
markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen());
|
||||
markupStringTable(filetab.getArrayAddress(), filetab.getLen());
|
||||
markupStringTable(funcnametab.getArrayAddress(), funcnametab.getLen(), session);
|
||||
markupStringTable(filetab.getArrayAddress(), filetab.getLen(), session);
|
||||
|
||||
if (ftab.getLen() > 0) {
|
||||
// chop off the last entry as it is not a full entry (it just points to the address
|
||||
@ -166,8 +173,8 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
int entryLen =
|
||||
programContext.getStructureMappingInfo(GoFunctabEntry.class).getStructureLength();
|
||||
GoSlice subSlice = ftab.getSubSlice(0, ftab.getLen() - 1, entryLen);
|
||||
subSlice.markupArray("moduledata.ftab", GoFunctabEntry.class, false);
|
||||
subSlice.markupArrayElements(GoFunctabEntry.class);
|
||||
subSlice.markupArray("moduledata.ftab", GoFunctabEntry.class, false, session);
|
||||
subSlice.markupArrayElements(GoFunctabEntry.class, session);
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,7 +191,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void markupStringTable(Address addr, long stringTableLength) {
|
||||
private void markupStringTable(Address addr, long stringTableLength, MarkupSession session) {
|
||||
DataType stringDT = StringUTF8DataType.dataType;
|
||||
long startOfString = addr.getOffset();
|
||||
long endOfStringTable = startOfString + stringTableLength;
|
||||
@ -195,7 +202,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
long len = reader.getPointerIndex() - startOfString;
|
||||
if (len > 0 && len < Integer.MAX_VALUE) {
|
||||
Address stringAddr = addr.getNewAddress(startOfString);
|
||||
programContext.markupAddress(stringAddr, stringDT, (int) len);
|
||||
session.markupAddress(stringAddr, stringDT, (int) len);
|
||||
}
|
||||
startOfString = reader.getPointerIndex();
|
||||
}
|
||||
@ -235,7 +242,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
*
|
||||
* @param context already initialized {@link GoRttiMapper}
|
||||
* @return new GoModuledata instance, or null if not found
|
||||
* @throws IOException
|
||||
* @throws IOException if error reading found structure
|
||||
*/
|
||||
/* package */ static GoModuledata getFirstModuledata(GoRttiMapper context)
|
||||
throws IOException {
|
||||
@ -249,7 +256,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches memory for a likely GoModuledata
|
||||
* Searches memory for a likely GoModuledata structure.
|
||||
*
|
||||
* @param context already initialized {@link GoRttiMapper}
|
||||
* @param pclntabAddress address of an already found {@link GoPcHeader}
|
||||
@ -257,7 +264,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
* @param range memory range to search. Will be different for different types of binaries
|
||||
* @param monitor {@link TaskMonitor}
|
||||
* @return new GoModuledata instance, or null if not found
|
||||
* @throws IOException
|
||||
* @throws IOException if error reading found structure
|
||||
*/
|
||||
/* package */ static GoModuledata findFirstModule(GoRttiMapper context,
|
||||
Address pclntabAddress, GoPcHeader pclntab, AddressRange range, TaskMonitor monitor)
|
||||
@ -269,6 +276,8 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
Program program = context.getProgram();
|
||||
Memory memory = program.getMemory();
|
||||
|
||||
// Search memory for a pointer to the pclntab struct. The result should be the first
|
||||
// field of the GoModuledata structure.
|
||||
int ptrSize = context.getPtrSize();
|
||||
byte[] searchBytes = new byte[ptrSize];
|
||||
context.getDataConverter().putValue(pclntabAddress.getOffset(), ptrSize, searchBytes, 0);
|
||||
@ -279,6 +288,9 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
|
||||
}
|
||||
|
||||
GoModuledata moduleData = context.readStructure(GoModuledata.class, moduleAddr);
|
||||
|
||||
// Verify that we read a good GoModuledata struct by comparing some of its values to
|
||||
// the pclntab structure.
|
||||
return moduleData.matchesPclntab(pclntab) ? moduleData : null;
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ public class GoPcHeader {
|
||||
* @param range memory range to search (typically .rdata or .noptrdata sections)
|
||||
* @param monitor {@link TaskMonitor} that will let the user cancel
|
||||
* @return {@link Address} of the found pclntab structure, or null if not found
|
||||
* @throws IOException
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public static Address findPclntabAddress(GoRttiMapper programContext, AddressRange range,
|
||||
TaskMonitor monitor) throws IOException {
|
||||
@ -126,7 +126,7 @@ public class GoPcHeader {
|
||||
*
|
||||
* @param provider {@link ByteProvider}
|
||||
* @return boolean true if the byte provider has the magic signature of a pclntab
|
||||
* @throws IOException
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public static boolean isPclntab(ByteProvider provider) throws IOException {
|
||||
byte[] header = provider.readBytes(0, 8);
|
||||
|
@ -77,9 +77,9 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
* is not a supported golang binary.
|
||||
*
|
||||
* @param program {@link Program}
|
||||
* @param log
|
||||
* @param log {@link MessageLog}
|
||||
* @return new {@link GoRttiMapper}, or null if not a golang binary
|
||||
* @throws IOException
|
||||
* @throws IOException if bootstrap gdt is corrupted or some other struct mapping logic error
|
||||
*/
|
||||
public static GoRttiMapper getMapperFor(Program program, MessageLog log) throws IOException {
|
||||
GoBuildInfo buildInfo = GoBuildInfo.fromProgram(program);
|
||||
@ -104,6 +104,15 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the golang bootstrap gdt data type archive, using the specified
|
||||
* version, pointer size and OS name.
|
||||
*
|
||||
* @param goVer {@link GoVer}
|
||||
* @param pointerSizeInBytes pointer size for this binary, or -1 to use wildcard "any"
|
||||
* @param osName name of the operating system, or "any"
|
||||
* @return String, "golang_1.18_64bit_any.gdt"
|
||||
*/
|
||||
public static String getGDTFilename(GoVer goVer, int pointerSizeInBytes, String osName) {
|
||||
String bitSize = pointerSizeInBytes > 0
|
||||
? Integer.toString(pointerSizeInBytes * 8)
|
||||
@ -114,6 +123,12 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return gdtFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a golang OS string based on the Ghidra program.
|
||||
*
|
||||
* @param program {@link Program}
|
||||
* @return String golang OS string such as "linux", "win"
|
||||
*/
|
||||
public static String getGolangOSString(Program program) {
|
||||
String loaderName = program.getExecutableFormat();
|
||||
if (ElfLoader.ELF_NAME.equals(loaderName)) {
|
||||
@ -137,7 +152,7 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
* @param goVer version of Go
|
||||
* @param ptrSize size of pointers
|
||||
* @param osName name of OS
|
||||
* @return
|
||||
* @return ResourceFile of matching bootstrap gdt, or null if nothing matches
|
||||
*/
|
||||
public static ResourceFile findGolangBootstrapGDT(GoVer goVer, int ptrSize, String osName) {
|
||||
ResourceFile result = null;
|
||||
@ -185,8 +200,22 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
private GoType mapGoType;
|
||||
private GoType chanGoType;
|
||||
|
||||
/**
|
||||
* Creates a GoRttiMapper using the specified bootstrap information.
|
||||
*
|
||||
* @param program {@link Program} containing the go binary
|
||||
* @param ptrSize size of pointers
|
||||
* @param endian {@link Endian}
|
||||
* @param goVersion version of go
|
||||
* @param archiveGDT path to the matching golang bootstrap gdt data type file, or null
|
||||
* 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
|
||||
* type of golang binary
|
||||
*/
|
||||
public GoRttiMapper(Program program, int ptrSize, Endian endian, GoVer goVersion,
|
||||
ResourceFile archiveGDT) throws IOException {
|
||||
ResourceFile archiveGDT) throws IOException, IllegalArgumentException {
|
||||
super(program, archiveGDT);
|
||||
|
||||
this.goVersion = goVersion;
|
||||
@ -222,18 +251,41 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the golang version
|
||||
* @return {@link GoVer}
|
||||
*/
|
||||
public GoVer getGolangVersion() {
|
||||
return goVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first module data instance
|
||||
*
|
||||
* @return {@link GoModuledata}
|
||||
*/
|
||||
public GoModuledata getFirstModule() {
|
||||
return modules.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a module data instance to the context
|
||||
*
|
||||
* @param module {@link GoModuledata} to add
|
||||
*/
|
||||
public void addModule(GoModuledata module) {
|
||||
modules.add(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the {@link GoModuledata} that contains the specified offset.
|
||||
* <p>
|
||||
* Useful for finding the {@link GoModuledata} to resolve a relative offset of the text,
|
||||
* types or other area.
|
||||
*
|
||||
* @param offset absolute offset of a structure that a {@link GoModuledata} contains
|
||||
* @return {@link GoModuledata} instance that contains the structure, or null if not found
|
||||
*/
|
||||
public GoModuledata findContainingModule(long offset) {
|
||||
for (GoModuledata module : modules) {
|
||||
if (module.getTypesOffset() <= offset && offset < module.getTypesEndOffset()) {
|
||||
@ -243,6 +295,13 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the {@link GoModuledata} that contains the specified func data offset.
|
||||
*
|
||||
* @param offset absolute offset of a func data structure
|
||||
* @return {@link GoModuledata} instance that contains the specified func data, or null if not
|
||||
* found
|
||||
*/
|
||||
public GoModuledata findContainingModuleByFuncData(long offset) {
|
||||
for (GoModuledata module : modules) {
|
||||
if (module.containsFuncDataInstance(offset)) {
|
||||
@ -257,26 +316,56 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return VARLEN_STRUCTS_CP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data type that represents a golang uintptr
|
||||
*
|
||||
* @return golang uinptr data type
|
||||
*/
|
||||
public DataType getUintptrDT() {
|
||||
return uintptrDT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data type that represents a golang int32
|
||||
*
|
||||
* @return golang int32 data type
|
||||
*/
|
||||
public DataType getInt32DT() {
|
||||
return int32DT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data type that represents a golang uint32
|
||||
*
|
||||
* @return golang uint32 data type
|
||||
*/
|
||||
public DataType getUint32DT() {
|
||||
return uint32DT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data type that represents a generic golang slice.
|
||||
*
|
||||
* @return golang generic slice data type
|
||||
*/
|
||||
public Structure getGenericSliceDT() {
|
||||
return getStructureDataType(GoSlice.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ghidra data type that represents a golang built-in map type.
|
||||
*
|
||||
* @return golang map data type
|
||||
*/
|
||||
public GoType getMapGoType() {
|
||||
return mapGoType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ghidra data type that represents the built-in golang channel type.
|
||||
*
|
||||
* @return golang channel type
|
||||
*/
|
||||
public GoType getChanGoType() {
|
||||
return chanGoType;
|
||||
}
|
||||
@ -286,31 +375,22 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return reader.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of pointers in this binary.
|
||||
*
|
||||
* @return pointer size (ex. 4, or 8)
|
||||
*/
|
||||
public int getPtrSize() {
|
||||
return ptrSize;
|
||||
}
|
||||
|
||||
public GoName resolveNameOff(long ptrInModule, long off) throws IOException {
|
||||
if (off == 0) {
|
||||
return null;
|
||||
}
|
||||
GoModuledata module = findContainingModule(ptrInModule);
|
||||
long nameStart = module.getTypesOffset() + off;
|
||||
return getGoName(nameStart);
|
||||
}
|
||||
|
||||
public GoName getGoName(long offset) throws IOException {
|
||||
return offset != 0 ? readStructure(GoName.class, offset) : null;
|
||||
}
|
||||
|
||||
public GoType resolveTypeOff(long ptrInModule, long off) throws IOException {
|
||||
if (off == 0 || off == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG || off == -1) {
|
||||
return null;
|
||||
}
|
||||
GoModuledata module = findContainingModule(ptrInModule);
|
||||
return getGoType(module.getTypesOffset() + off);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specialized {@link GoType} for the type that is located at the specified location.
|
||||
*
|
||||
* @param offset absolute position of a go type
|
||||
* @return specialized {@link GoType} (example, GoStructType, GoArrayType, etc)
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public GoType getGoType(long offset) throws IOException {
|
||||
if (offset == 0) {
|
||||
return null;
|
||||
@ -324,14 +404,36 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return goType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specialized {@link GoType} for the type that is located at the specified location.
|
||||
*
|
||||
* @param addr location of a go type
|
||||
* @return specialized {@link GoType} (example, GoStructType, GoArrayType, etc)
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public GoType getGoType(Address addr) throws IOException {
|
||||
return getGoType(addr.getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a go type by its go-type name, from the list of
|
||||
* {@link #discoverGoTypes(TaskMonitor) discovered} go types.
|
||||
*
|
||||
* @param typeName name string
|
||||
* @return {@link GoType}, or null if not found
|
||||
*/
|
||||
public GoType findGoType(String typeName) {
|
||||
return typeNameIndex.get(typeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Ghidra {@link DataType} that is equivalent to the named golang type.
|
||||
*
|
||||
* @param <T> expected DataType
|
||||
* @param goTypeName golang type name
|
||||
* @param clazz class of expected data type
|
||||
* @return {@link DataType} representing the named golang type, or null if not found
|
||||
*/
|
||||
public <T extends DataType> T getGhidraDataType(String goTypeName, Class<T> clazz) {
|
||||
T dt = getType(goTypeName, clazz);
|
||||
if (dt == null) {
|
||||
@ -353,22 +455,16 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return dt;
|
||||
}
|
||||
|
||||
public Address resolveTextOff(long ptrInModule, long off) {
|
||||
if (off == -1 || off == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG) {
|
||||
return null;
|
||||
}
|
||||
GoModuledata module = findContainingModule(ptrInModule);
|
||||
return module != null ? module.getText().add(off) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the currently registered struct mapping types to a gdt file.
|
||||
* Export the currently registered struct mapping types to a gdt file, producing a bootstrap
|
||||
* GDT archive.
|
||||
* <p>
|
||||
* The struct data types will either be from the current program's DWARF data, or
|
||||
* from an earlier golang.gdt (if this binary doesn't have DWARF)
|
||||
*
|
||||
* @param gdtFile
|
||||
* @throws IOException
|
||||
* @param gdtFile destination {@link File} to write the bootstrap types to
|
||||
* @param monitor {@link TaskMonitor}
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public void exportTypesToGDT(File gdtFile, TaskMonitor monitor) throws IOException {
|
||||
|
||||
@ -463,12 +559,29 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns category path that should be used to place recovered golang types.
|
||||
*
|
||||
* @return {@link CategoryPath} to use when creating recovered golang types
|
||||
*/
|
||||
public CategoryPath getRecoveredTypesCp() {
|
||||
return RECOVERED_TYPES_CP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DataType Ghidra data type} that represents the {@link GoType golang type},
|
||||
* using a cache of already recovered types to eliminate extra work and self recursion.
|
||||
*
|
||||
* @param typ the {@link GoType} to convert
|
||||
* @return Ghidra {@link DataType}
|
||||
* @throws IOException if error converting type
|
||||
*/
|
||||
public DataType getRecoveredType(GoType typ) throws IOException {
|
||||
long offset = getExistingStructureAddress(typ).getOffset();
|
||||
Address typeStructAddr = getAddressOfStructure(typ);
|
||||
if (typeStructAddr == null) {
|
||||
throw new IOException("Unable to get address of a struct mapped instance");
|
||||
}
|
||||
long offset = typeStructAddr.getOffset();
|
||||
DataType dt = cachedRecoveredDataTypes.get(offset);
|
||||
if (dt != null) {
|
||||
return dt;
|
||||
@ -478,13 +591,40 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
return dt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a mapping between a {@link GoType golang type} and a
|
||||
* {@link DataType ghidra data type}.
|
||||
* <p>
|
||||
* Useful to prepopulate the data type mapping before recursing into contained/referenced types
|
||||
* that might be self-referencing.
|
||||
*
|
||||
* @param typ {@link GoType golang type}
|
||||
* @param dt {@link DataType Ghidra type}
|
||||
* @throws IOException if golang type struct is not a valid struct mapped instance
|
||||
*/
|
||||
public void cacheRecoveredDataType(GoType typ, DataType dt) throws IOException {
|
||||
long offset = getExistingStructureAddress(typ).getOffset();
|
||||
Address typeStructAddr = getAddressOfStructure(typ);
|
||||
if (typeStructAddr == null) {
|
||||
throw new IOException("Unable to get address of a struct mapped instance");
|
||||
}
|
||||
long offset = typeStructAddr.getOffset();
|
||||
cachedRecoveredDataTypes.put(offset, dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DataType Ghidra data type} that represents the {@link GoType golang type},
|
||||
* using a cache of already recovered types to eliminate extra work and self recursion.
|
||||
*
|
||||
* @param typ the {@link GoType} to convert
|
||||
* @return Ghidra {@link DataType}
|
||||
* @throws IOException if golang type struct is not a valid struct mapped instance
|
||||
*/
|
||||
public DataType getCachedRecoveredDataType(GoType typ) throws IOException {
|
||||
long offset = getExistingStructureAddress(typ).getOffset();
|
||||
Address typeStructAddr = getAddressOfStructure(typ);
|
||||
if (typeStructAddr == null) {
|
||||
throw new IOException("Unable to get address of a struct mapped instance");
|
||||
}
|
||||
long offset = typeStructAddr.getOffset();
|
||||
return cachedRecoveredDataTypes.get(offset);
|
||||
}
|
||||
|
||||
@ -492,8 +632,9 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
* Converts all discovered golang rtti type records to Ghidra data types, placing them
|
||||
* in the program's DTM in /golang-recovered
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws CancelledException
|
||||
* @param monitor {@link TaskMonitor}
|
||||
* @throws IOException error converting a golang type to a Ghidra type
|
||||
* @throws CancelledException if the user cancelled the import
|
||||
*/
|
||||
public void recoverDataTypes(TaskMonitor monitor) throws IOException, CancelledException {
|
||||
monitor.setMessage("Converting Golang types to Ghidra data types");
|
||||
@ -510,6 +651,16 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all golang rtti types listed in the GoModuledata struct, and recurses into
|
||||
* each type to discover any types they reference.
|
||||
* <p>
|
||||
* The found types are accumulated in {@link #goTypes}.
|
||||
*
|
||||
* @param monitor {@link TaskMonitor}
|
||||
* @throws IOException if error
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public void discoverGoTypes(TaskMonitor monitor) throws IOException, CancelledException {
|
||||
GoModuledata firstModule = findFirstModuledata(monitor);
|
||||
if (firstModule == null) {
|
||||
@ -533,13 +684,88 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
}
|
||||
typeNameIndex.clear();
|
||||
for (GoType goType : goTypes.values()) {
|
||||
String typeName = goType.getBaseType().getNameString();
|
||||
String typeName = goType.getNameString();
|
||||
typeNameIndex.put(typeName, goType);
|
||||
}
|
||||
Msg.info(this, "Found %d golang types".formatted(goTypes.size()));
|
||||
initHiddenCompilerTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GoType} corresponding to an offset that is relative to the controlling
|
||||
* GoModuledata's typesOffset.
|
||||
*
|
||||
* @param ptrInModule the address of the structure that contains the offset that needs to be
|
||||
* calculated. The containing-structure's address is important because it indicates which
|
||||
* GoModuledata is the 'parent'
|
||||
* @param off offset
|
||||
* @return {@link GoType}, or null if offset is special value 0 or -1
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public GoType resolveTypeOff(long ptrInModule, long off) throws IOException {
|
||||
if (off == 0 || off == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG || off == -1) {
|
||||
return null;
|
||||
}
|
||||
GoModuledata module = findContainingModule(ptrInModule);
|
||||
return getGoType(module.getTypesOffset() + off);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Address} to an offset that is relative to the controlling
|
||||
* GoModuledata's text value.
|
||||
*
|
||||
* @param ptrInModule the address of the structure that contains the offset that needs to be
|
||||
* calculated. The containing-structure's address is important because it indicates which
|
||||
* GoModuledata is the 'parent'
|
||||
* @param off offset
|
||||
* @return {@link Address}, or null if offset was special value -1
|
||||
*/
|
||||
public Address resolveTextOff(long ptrInModule, long off) {
|
||||
if (off == -1 || off == NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG) {
|
||||
return null;
|
||||
}
|
||||
GoModuledata module = findContainingModule(ptrInModule);
|
||||
return module != null ? module.getText().add(off) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GoName} corresponding to an offset that is relative to the controlling
|
||||
* GoModuledata's typesOffset.
|
||||
* <p>
|
||||
*
|
||||
* @param ptrInModule the address of the structure that contains the offset that needs to be
|
||||
* calculated. The containing-structure's address is important because it indicates which
|
||||
* GoModuledata is the 'parent'
|
||||
* @param off offset
|
||||
* @return {@link GoName}, or null if offset was special value 0
|
||||
* @throws IOException if error reading name or unable to find containing module
|
||||
*/
|
||||
public GoName resolveNameOff(long ptrInModule, long off) throws IOException {
|
||||
if (off == 0) {
|
||||
return null;
|
||||
}
|
||||
GoModuledata module = findContainingModule(ptrInModule);
|
||||
if (module == null) {
|
||||
throw new IOException(
|
||||
"Unable to find containing module for structure at 0x%x".formatted(ptrInModule));
|
||||
}
|
||||
long nameStart = module.getTypesOffset() + off;
|
||||
return getGoName(nameStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GoName} instance at the specified offset.
|
||||
*
|
||||
* @param offset location to read
|
||||
* @return {@link GoName} instance, or null if offset was special value 0
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public GoName getGoName(long offset) throws IOException {
|
||||
return offset != 0 ? readStructure(GoName.class, offset) : null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
|
||||
private void initHiddenCompilerTypes() {
|
||||
// these structure types are what golang map and chan types actually point to.
|
||||
mapGoType = findGoType("runtime.hmap");
|
||||
@ -574,9 +800,9 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
private AddressRange getPclntabSearchRange() {
|
||||
Memory memory = program.getMemory();
|
||||
for (String blockToSearch : List.of(".noptrdata", ".rdata")) {
|
||||
MemoryBlock noptrdataBlock = memory.getBlock(blockToSearch);
|
||||
if (noptrdataBlock != null) {
|
||||
return new AddressRangeImpl(noptrdataBlock.getStart(), noptrdataBlock.getEnd());
|
||||
MemoryBlock memBlock = memory.getBlock(blockToSearch);
|
||||
if (memBlock != null) {
|
||||
return new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -585,9 +811,9 @@ public class GoRttiMapper extends DataTypeMapper {
|
||||
private AddressRange getModuledataSearchRange() {
|
||||
Memory memory = program.getMemory();
|
||||
for (String blockToSearch : List.of(".noptrdata", ".data")) {
|
||||
MemoryBlock noptrdataBlock = memory.getBlock(blockToSearch);
|
||||
if (noptrdataBlock != null) {
|
||||
return new AddressRangeImpl(noptrdataBlock.getStart(), noptrdataBlock.getEnd());
|
||||
MemoryBlock memBlock = memory.getBlock(blockToSearch);
|
||||
if (memBlock != null) {
|
||||
return new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -25,7 +25,6 @@ import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@StructureMapping(structureName = "runtime.slice")
|
||||
@ -61,10 +60,10 @@ public class GoSlice {
|
||||
/**
|
||||
* Return a artificial view of a portion of this slice's contents.
|
||||
*
|
||||
* @param startElement
|
||||
* @param elementCount
|
||||
* @param elementSize
|
||||
* @return
|
||||
* @param startElement index of element that will be the new sub-slice's starting element
|
||||
* @param elementCount number of elements to include in new sub-slice
|
||||
* @param elementSize size of an individual element
|
||||
* @return new {@link GoSlice} instance that is limited to a portion of this slice
|
||||
*/
|
||||
public GoSlice getSubSlice(long startElement, long elementCount, long elementSize) {
|
||||
return new GoSlice(array + (startElement * elementSize), elementCount, elementCount, programContext);
|
||||
@ -110,10 +109,10 @@ public class GoSlice {
|
||||
* Reads the content of the slice, treating each element as an instance of the specified
|
||||
* structure mapped class.
|
||||
*
|
||||
* @param <T>
|
||||
* @param clazz element type
|
||||
* @param <T> struct mapped type of element
|
||||
* @param clazz element type
|
||||
* @return list of instances
|
||||
* @throws IOException
|
||||
* @throws IOException if error reading an element
|
||||
*/
|
||||
public <T> List<T> readList(Class<T> clazz) throws IOException {
|
||||
return readList((reader) -> programContext.readStructure(clazz, reader));
|
||||
@ -123,10 +122,10 @@ public class GoSlice {
|
||||
* Reads the contents of the slice, treating each element as an instance of an object that can
|
||||
* be read using the supplied reading function.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> struct mapped type of element
|
||||
* @param readFunc function that will read an instance from a BinaryReader
|
||||
* @return list of instances
|
||||
* @throws IOException
|
||||
* @throws IOException if error reading an element
|
||||
*/
|
||||
public <T> List<T> readList(ReaderFunction<T> readFunc) throws IOException {
|
||||
List<T> result = new ArrayList<>();
|
||||
@ -171,12 +170,13 @@ public class GoSlice {
|
||||
* @param elementClazz structure mapped class of the element of the array
|
||||
* @param ptr boolean flag, if true the element type is really a pointer to the supplied
|
||||
* data type
|
||||
* @param session state and methods to assist marking up the program
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public void markupArray(String sliceName, Class<?> elementClazz, boolean ptr)
|
||||
throws IOException {
|
||||
public void markupArray(String sliceName, Class<?> elementClazz, boolean ptr,
|
||||
MarkupSession session) throws IOException {
|
||||
DataType dt = programContext.getStructureDataType(elementClazz);
|
||||
markupArray(sliceName, dt, ptr);
|
||||
markupArray(sliceName, dt, ptr, session);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,10 +186,11 @@ public class GoSlice {
|
||||
* @param elementType Ghidra datatype of the array elements, null ok if ptr == true
|
||||
* @param ptr boolean flag, if true the element type is really a pointer to the supplied
|
||||
* data type
|
||||
* @param session state and methods to assist marking up the program
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public void markupArray(String sliceName, DataType elementType, boolean ptr)
|
||||
throws IOException {
|
||||
public void markupArray(String sliceName, DataType elementType, boolean ptr,
|
||||
MarkupSession session) throws IOException {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
@ -200,27 +201,29 @@ public class GoSlice {
|
||||
|
||||
ArrayDataType arrayDT = new ArrayDataType(elementType, (int) cap, -1, dtm);
|
||||
Address addr = programContext.getDataAddress(array);
|
||||
programContext.markupAddress(addr, arrayDT);
|
||||
session.markupAddress(addr, arrayDT);
|
||||
if (sliceName != null) {
|
||||
programContext.labelAddress(addr, sliceName);
|
||||
session.labelAddress(addr, sliceName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks up each element of the array, useful when the elements are themselves structures.
|
||||
*
|
||||
* @param <T> structure type
|
||||
* @param clazz class of the structure type
|
||||
* @param <T> element type
|
||||
* @param clazz structure mapped class of element
|
||||
* @param session state and methods to assist marking up the program
|
||||
* @return list of element instances
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public <T> List<T> markupArrayElements(Class<T> clazz) throws IOException {
|
||||
public <T> List<T> markupArrayElements(Class<T> clazz, MarkupSession session)
|
||||
throws IOException {
|
||||
if (len == 0) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<T> elementList = readList(clazz);
|
||||
programContext.markup(elementList, true);
|
||||
session.markup(elementList, true);
|
||||
return elementList;
|
||||
}
|
||||
|
||||
@ -235,23 +238,14 @@ public class GoSlice {
|
||||
*
|
||||
* @param elementSize size of each element in the array
|
||||
* @param targetAddrs list of addresses, should be same size as this slice
|
||||
* @throws IOException
|
||||
* @param session state and methods to assist marking up the program
|
||||
* @throws IOException if error creating references
|
||||
*/
|
||||
public void markupElementReferences(int elementSize, List<Address> targetAddrs)
|
||||
throws IOException {
|
||||
public void markupElementReferences(int elementSize, List<Address> targetAddrs,
|
||||
MarkupSession session) throws IOException {
|
||||
if (!targetAddrs.isEmpty()) {
|
||||
ReferenceManager refMgr = programContext.getProgram().getReferenceManager();
|
||||
|
||||
Address srcAddr = programContext.getDataAddress(array);
|
||||
for (Address targetAddr : targetAddrs) {
|
||||
if (targetAddr != null) {
|
||||
refMgr.addMemoryReference(srcAddr, targetAddr, RefType.DATA,
|
||||
SourceType.IMPORTED, 0);
|
||||
}
|
||||
srcAddr = srcAddr.add(elementSize);
|
||||
}
|
||||
session.markupArrayElementReferences(getArrayAddress(), elementSize, targetAddrs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static long[] readUIntList(BinaryReader reader, long index, int intSize, int count)
|
||||
|
@ -15,11 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.golang.GoFunctionMultiReturn;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoSlice;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
@ -82,9 +81,9 @@ public class GoFuncType extends GoType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void additionalMarkup() throws IOException {
|
||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||
GoSlice slice = getParamListSlice();
|
||||
slice.markupArray(getStructureLabel() + "_paramlist", GoBaseType.class, true);
|
||||
slice.markupArray(getStructureLabel() + "_paramlist", GoBaseType.class, true, session);
|
||||
}
|
||||
|
||||
public String getFuncPrototypeString(String funcName) throws IOException {
|
||||
@ -103,7 +102,7 @@ public class GoFuncType extends GoType {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(paramType.getBaseType().getNameString());
|
||||
sb.append(paramType.getNameString());
|
||||
}
|
||||
sb.append(")");
|
||||
if (!outParamTypes.isEmpty()) {
|
||||
@ -113,7 +112,7 @@ public class GoFuncType extends GoType {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(paramType.getBaseType().getNameString());
|
||||
sb.append(paramType.getNameString());
|
||||
}
|
||||
sb.append(")");
|
||||
}
|
||||
|
@ -15,11 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.golang.rtti.*;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
@ -57,9 +56,9 @@ public class GoInterfaceType extends GoType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void additionalMarkup() throws IOException {
|
||||
mhdr.markupArray(null, GoIMethod.class, false);
|
||||
mhdr.markupArrayElements(GoIMethod.class);
|
||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||
mhdr.markupArray(null, GoIMethod.class, false, session);
|
||||
mhdr.markupArrayElements(GoIMethod.class, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -48,6 +48,12 @@ public enum GoKind {
|
||||
public static final int GC_PROG = (1 << 6);
|
||||
public static final int DIRECT_IFACE = (1 << 5);
|
||||
|
||||
/**
|
||||
* Parses the byte value read from the runtime._type kind field.
|
||||
*
|
||||
* @param b byte value
|
||||
* @return {@link GoKind} enum, or {@link #invalid} if bad value
|
||||
*/
|
||||
public static GoKind parseByte(int b) {
|
||||
int ordinal = b & KIND_MASK;
|
||||
return Bool.ordinal() <= ordinal && ordinal <= UnsafePointer.ordinal()
|
||||
|
@ -15,14 +15,20 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Golang type info about a specific map type.
|
||||
* <p>
|
||||
* 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")
|
||||
public class GoMapType extends GoType {
|
||||
|
||||
@ -51,6 +57,7 @@ public class GoMapType extends GoType {
|
||||
private int flags;
|
||||
|
||||
public GoMapType() {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Markup
|
||||
|
@ -15,13 +15,19 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
/**
|
||||
* Golang type information about a specific slice type.
|
||||
* <p>
|
||||
* 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")
|
||||
public class GoSliceType extends GoType {
|
||||
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoName;
|
||||
@ -26,6 +25,9 @@ import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Golang type information about a specific structure type.
|
||||
*/
|
||||
@StructureMapping(structureName = "runtime.structtype")
|
||||
public class GoStructType extends GoType {
|
||||
|
||||
@ -37,6 +39,7 @@ public class GoStructType extends GoType {
|
||||
private GoSlice fields;
|
||||
|
||||
public GoStructType() {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Markup
|
||||
@ -54,10 +57,10 @@ public class GoStructType extends GoType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void additionalMarkup() throws IOException {
|
||||
super.additionalMarkup();
|
||||
fields.markupArray(getStructureLabel() + "_fields", GoStructField.class, false);
|
||||
fields.markupArrayElements(GoStructField.class);
|
||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||
super.additionalMarkup(session);
|
||||
fields.markupArray(getStructureLabel() + "_fields", GoStructField.class, false, session);
|
||||
fields.markupArrayElements(GoStructField.class, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -92,7 +95,7 @@ public class GoStructType extends GoType {
|
||||
long offset = field.getOffset();
|
||||
long fieldSize = field.getType().getBaseType().getSize();
|
||||
sb.append("%s %s // %d..%d".formatted(field.getNameString(),
|
||||
field.getType().getBaseType().getNameString(), offset, offset + fieldSize));
|
||||
field.getType().getNameString(), offset, offset + fieldSize));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@ -138,7 +141,7 @@ public class GoStructType extends GoType {
|
||||
String comment = dtc.getComment();
|
||||
comment = comment == null ? "" : (comment + "\n");
|
||||
comment += "Omitted zero-len field: %s=%s".formatted(skippedField.getNameString(),
|
||||
skippedFieldType.getBaseType().getNameString());
|
||||
skippedFieldType.getNameString());
|
||||
dtc.setComment(comment);
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,18 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.rtti.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
|
||||
import ghidra.app.util.bin.format.golang.rtti.GoSlice;
|
||||
import ghidra.app.util.bin.format.golang.structmapping.*;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
/**
|
||||
* Common abstract base class for GoType classes
|
||||
*/
|
||||
@PlateComment()
|
||||
public abstract class GoType implements StructureMarkup<GoType> {
|
||||
private static final Map<GoKind, Class<? extends GoType>> specializedTypeClasses =
|
||||
@ -38,6 +40,15 @@ public abstract class GoType implements StructureMarkup<GoType> {
|
||||
Map.entry(GoKind.Map, GoMapType.class),
|
||||
Map.entry(GoKind.Interface, GoInterfaceType.class));
|
||||
|
||||
/**
|
||||
* Returns the specific GoType derived class that will handle the go type located at the
|
||||
* specified offset.
|
||||
*
|
||||
* @param programContext program-level mapper context
|
||||
* @param offset absolute location of go type struct
|
||||
* @return GoType class that will best handle the type struct
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public static Class<? extends GoType> getSpecializedTypeClass(GoRttiMapper programContext,
|
||||
long offset) throws IOException {
|
||||
GoTypeDetector typeDetector = programContext.readStructure(GoTypeDetector.class, offset);
|
||||
@ -59,10 +70,14 @@ public abstract class GoType implements StructureMarkup<GoType> {
|
||||
@FieldOutput
|
||||
protected GoBaseType typ;
|
||||
|
||||
public GoBaseType getBaseType() {
|
||||
protected GoBaseType getBaseType() {
|
||||
return typ;
|
||||
}
|
||||
|
||||
public String getNameString() throws IOException {
|
||||
return typ.getNameString();
|
||||
}
|
||||
|
||||
public String getDebugId() {
|
||||
return "%s@%s".formatted(
|
||||
context.getMappingInfo().getDescription(),
|
||||
@ -95,14 +110,14 @@ public abstract class GoType implements StructureMarkup<GoType> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void additionalMarkup() throws IOException {
|
||||
public void additionalMarkup(MarkupSession session) throws IOException {
|
||||
GoUncommonType uncommonType = getUncommonType();
|
||||
if (uncommonType != null) {
|
||||
GoSlice slice = uncommonType.getMethodsSlice();
|
||||
slice.markupArray(getStructureName() + "_methods", GoMethod.class, false);
|
||||
slice.markupArrayElements(GoMethod.class);
|
||||
slice.markupArray(getStructureName() + "_methods", GoMethod.class, false, session);
|
||||
slice.markupArrayElements(GoMethod.class, session);
|
||||
|
||||
programContext.labelStructure(uncommonType, typ.getNameString() + "_" +
|
||||
session.labelStructure(uncommonType, typ.getNameString() + "_" +
|
||||
programContext.getStructureDataTypeName(GoUncommonType.class));
|
||||
}
|
||||
}
|
||||
@ -153,7 +168,7 @@ public abstract class GoType implements StructureMarkup<GoType> {
|
||||
* Converts a golang RTTI type structure into a Ghidra data type.
|
||||
*
|
||||
* @return {@link DataType} that represents the golang type
|
||||
* @throws IOException
|
||||
* @throws IOException if error getting name of the type
|
||||
*/
|
||||
public DataType recoverDataType() throws IOException {
|
||||
DataType dt = Undefined.getUndefinedDataType((int) typ.getSize());
|
||||
|
@ -16,7 +16,6 @@
|
||||
package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
@ -25,18 +24,12 @@ import ghidra.app.util.bin.MemoryByteProvider;
|
||||
import ghidra.app.util.bin.format.dwarf4.next.DWARFDataTypeConflictHandler;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataUtilities.ClearDataMode;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.DataConverter;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Information about {@link StructureMapping} classes and their metadata, as well as
|
||||
* accumulated information about structure instances that have been deserialized.
|
||||
* Information about {@link StructureMapping} classes and their metadata.
|
||||
* <p>
|
||||
* To use the full might and majesty of StructureMapping(tm), a DataTypeMapper must be created. It
|
||||
* must be able to {@link #addArchiveSearchCategoryPath(CategoryPath...) find}
|
||||
@ -83,14 +76,14 @@ public class DataTypeMapper implements AutoCloseable {
|
||||
protected List<CategoryPath> programSearchCPs = new ArrayList<>();
|
||||
protected List<CategoryPath> archiveSearchCPs = new ArrayList<>();
|
||||
protected Map<Class<?>, StructureMappingInfo<?>> mappingInfo = new HashMap<>();
|
||||
protected Set<Address> markedupStructs = new HashSet<>();
|
||||
protected TaskMonitor markupTaskMonitor = TaskMonitor.DUMMY;
|
||||
|
||||
/**
|
||||
* Creates and initializes a DataTypeMapper.
|
||||
*
|
||||
* @param program
|
||||
* @param archiveGDT
|
||||
* @throws IOException
|
||||
* @param program the {@link Program} that will contain the deserialized data
|
||||
* @param archiveGDT path to a gdt data type archive that will be searched when
|
||||
* a {@link #getType(String, Class)} is called, or {@code null} if no archive
|
||||
* @throws IOException if error opening data type archive
|
||||
*/
|
||||
protected DataTypeMapper(Program program, ResourceFile archiveGDT) throws IOException {
|
||||
this.program = program;
|
||||
@ -108,39 +101,72 @@ public class DataTypeMapper implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CategoryPath location (in the program) where new data types will be created to represent
|
||||
* variable length structures.
|
||||
*
|
||||
* @return {@link CategoryPath}, default is ROOT
|
||||
*/
|
||||
public CategoryPath getDefaultVariableLengthStructCategoryPath() {
|
||||
return CategoryPath.ROOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the program.
|
||||
*
|
||||
* @return ghidra {@link Program}
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
protected BinaryReader createProgramReader() {
|
||||
MemoryByteProvider bp =
|
||||
new MemoryByteProvider(program.getMemory(), program.getImageBase().getAddressSpace());
|
||||
return new BinaryReader(bp, !program.getMemory().isBigEndian());
|
||||
/**
|
||||
* Creates a {@link MarkupSession} that is controlled by the specified {@link TaskMonitor}.
|
||||
*
|
||||
* @param monitor {@link TaskMonitor}
|
||||
* @return new {@link MarkupSession}
|
||||
*/
|
||||
public MarkupSession createMarkupSession(TaskMonitor monitor) {
|
||||
return new MarkupSession(this, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DataConverter} appropriate for the current program.
|
||||
*
|
||||
* @return {@link DataConverter}
|
||||
*/
|
||||
public DataConverter getDataConverter() {
|
||||
return DataConverter.getInstance(program.getMemory().isBigEndian());
|
||||
}
|
||||
|
||||
public DataTypeMapper addProgramSearchCategoryPath(CategoryPath... paths) {
|
||||
/**
|
||||
* Adds category paths to a search list, used when looking for a data type.
|
||||
* <p>
|
||||
* See {@link #getType(String, Class)}.
|
||||
*
|
||||
* @param paths vararg list of {@link CategoryPath}s
|
||||
*/
|
||||
public void addProgramSearchCategoryPath(CategoryPath... paths) {
|
||||
programSearchCPs.addAll(Arrays.asList(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataTypeMapper addArchiveSearchCategoryPath(CategoryPath... paths) {
|
||||
/**
|
||||
* Adds category paths to a search list, used when looking for a data type.
|
||||
* <p>
|
||||
* See {@link #getType(String, Class)}.
|
||||
*
|
||||
* @param paths vararg list of {@link CategoryPath}s
|
||||
*/
|
||||
public void addArchiveSearchCategoryPath(CategoryPath... paths) {
|
||||
archiveSearchCPs.addAll(Arrays.asList(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a class that has {@link StructureMapping structure mapping} information.
|
||||
*
|
||||
* @param <T>
|
||||
* @param clazz
|
||||
* @param <T> structure mapped class type
|
||||
* @param clazz class that represents a structure, marked with {@link StructureMapping}
|
||||
* annotation
|
||||
* @throws IOException if the class's Ghidra structure data type could not be found
|
||||
*/
|
||||
public <T> void registerStructure(Class<T> clazz) throws IOException {
|
||||
@ -162,18 +188,41 @@ public class DataTypeMapper implements AutoCloseable {
|
||||
mappingInfo.put(clazz, structMappingInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the specified {@link StructureMapping structure mapping} classes.
|
||||
*
|
||||
* @param classes list of classes to register
|
||||
* @throws IOException if a class's Ghidra structure data type could not be found
|
||||
*/
|
||||
public void registerStructures(List<Class<?>> classes) throws IOException {
|
||||
for (Class<?> clazz : classes) {
|
||||
registerStructure(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link StructureMappingInfo} for a class (that has already been registered).
|
||||
*
|
||||
* @param <T> structure mapped class type
|
||||
* @param clazz the class
|
||||
* @return {@link StructureMappingInfo} for the specified class, or null if the class was
|
||||
* not previously {@link #registerStructure(Class) registered}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> StructureMappingInfo<T> getStructureMappingInfo(Class<T> clazz) {
|
||||
StructureMappingInfo<?> smi = mappingInfo.get(clazz);
|
||||
return (StructureMappingInfo<T>) smi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link StructureMappingInfo} for an object instance.
|
||||
*
|
||||
* @param <T> structure mapped class type
|
||||
* @param structureInstance an instance of a previously registered
|
||||
* {@link StructureMapping structure mapping} class, or null
|
||||
* @return {@link StructureMappingInfo} for the instance, or null if the class was
|
||||
* not previously {@link #registerStructure(Class) registered}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> StructureMappingInfo<T> getStructureMappingInfo(T structureInstance) {
|
||||
return structureInstance != null
|
||||
@ -189,41 +238,35 @@ public class DataTypeMapper implements AutoCloseable {
|
||||
* fields
|
||||
*/
|
||||
public Structure getStructureDataType(Class<?> clazz) {
|
||||
StructureMappingInfo<?> mi = mappingInfo.get(clazz);
|
||||
return mi != null ? mi.getStructureDataType() : null;
|
||||
StructureMappingInfo<?> smi = mappingInfo.get(clazz);
|
||||
return smi != null ? smi.getStructureDataType() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the Ghidra structure that has been registered for the specified
|
||||
* structure mapped class.
|
||||
*
|
||||
* @param clazz
|
||||
* @return
|
||||
* @param clazz a structure mapped class
|
||||
* @return name of the corresponding Ghidra structure data type, or null if class was not
|
||||
* registered
|
||||
*/
|
||||
public String getStructureDataTypeName(Class<?> clazz) {
|
||||
StructureMappingInfo<?> mi = mappingInfo.get(clazz);
|
||||
return mi != null ? mi.getStructureName() : null;
|
||||
}
|
||||
|
||||
protected DataType findType(String name, List<CategoryPath> searchList, DataTypeManager dtm) {
|
||||
for (CategoryPath searchCP : searchList) {
|
||||
DataType dataType = dtm.getDataType(searchCP, name);
|
||||
if (dataType != null) {
|
||||
return dataType;
|
||||
}
|
||||
}
|
||||
return 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>
|
||||
* @param name
|
||||
* @param clazz
|
||||
* @return
|
||||
* @param <T> DataType or derived type
|
||||
* @param name {@link DataType} name
|
||||
* @param clazz expected DataType class
|
||||
* @return DataType or null if not found
|
||||
*/
|
||||
public <T extends DataType> T getType(String name, Class<T> clazz) {
|
||||
DataType dataType = findType(name, programSearchCPs, programDTM);
|
||||
@ -240,27 +283,43 @@ 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 name {@link DataType} name
|
||||
* @param clazz expected DataType class
|
||||
* @param defaultValue value to return if the requested data type was not found
|
||||
* @return DataType or {@code defaultValue} if not found
|
||||
*/
|
||||
public <T extends DataType> T getTypeOrDefault(String name, Class<T> clazz, T defaultValue) {
|
||||
T result = getType(name, clazz);
|
||||
return result != null ? result : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the program's data type manager.
|
||||
*
|
||||
* @return program's {@link DataTypeManager}
|
||||
*/
|
||||
public DataTypeManager getDTM() {
|
||||
return programDTM;
|
||||
}
|
||||
|
||||
private <T> StructureContext<T> getStructureContext(Class<T> structureClass,
|
||||
BinaryReader reader) {
|
||||
StructureMappingInfo<T> smi = getStructureMappingInfo(structureClass);
|
||||
if (smi == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown structure mapped class: " + structureClass.getSimpleName());
|
||||
}
|
||||
return new StructureContext<>(this, smi, reader);
|
||||
}
|
||||
|
||||
public <T> StructureContext<T> getExistingStructureContext(T structureInstance)
|
||||
throws IOException {
|
||||
/**
|
||||
* Returns the {@link StructureContext} of a structure mapped instance.
|
||||
*
|
||||
* @param <T> java type of a class that is structure mapped
|
||||
* @param structureInstance an existing instance of type T
|
||||
* @return {@link StructureContext} of the instance, or null if instance was null or not
|
||||
* a structure mapped object
|
||||
*/
|
||||
public <T> StructureContext<T> getStructureContextOfInstance(T structureInstance) {
|
||||
StructureMappingInfo<T> smi = structureInstance != null
|
||||
? getStructureMappingInfo(structureInstance)
|
||||
: null;
|
||||
@ -275,9 +334,8 @@ public class DataTypeMapper implements AutoCloseable {
|
||||
* @param structureInstance instance of an object that represents something in the program's
|
||||
* memory
|
||||
* @return {@link Address} of the object, or null if not found or not a supported object
|
||||
* @throws IOException
|
||||
*/
|
||||
public <T> Address getExistingStructureAddress(T structureInstance) throws IOException {
|
||||
public <T> Address getAddressOfStructure(T structureInstance) {
|
||||
StructureMappingInfo<T> smi = structureInstance != null
|
||||
? getStructureMappingInfo(structureInstance)
|
||||
: null;
|
||||
@ -289,118 +347,120 @@ public class DataTypeMapper implements AutoCloseable {
|
||||
: null;
|
||||
}
|
||||
|
||||
public void setMarkupTaskMonitor(TaskMonitor monitor) {
|
||||
this.markupTaskMonitor = Objects.requireNonNullElse(monitor, TaskMonitor.DUMMY);
|
||||
}
|
||||
|
||||
public <T> void markup(T obj, boolean nested) throws IOException {
|
||||
if (markupTaskMonitor.isCancelled()) {
|
||||
throw new IOException("Markup canceled");
|
||||
}
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
if (obj instanceof Collection<?> list) {
|
||||
for (Object listElement : list) {
|
||||
markup(listElement, nested);
|
||||
}
|
||||
}
|
||||
else if (obj.getClass().isArray()) {
|
||||
int len = Array.getLength(obj);
|
||||
for (int i = 0; i < len; i++) {
|
||||
markup(Array.get(obj, i), nested);
|
||||
}
|
||||
}
|
||||
else if (obj instanceof Iterator<?> it) {
|
||||
while (it.hasNext()) {
|
||||
Object itElement = it.next();
|
||||
markup(itElement, nested);
|
||||
}
|
||||
}
|
||||
else {
|
||||
StructureContext<T> structureContext = getExistingStructureContext(obj);
|
||||
if (structureContext == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
markupTaskMonitor.incrementProgress(1);
|
||||
structureContext.markupStructure(nested);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a structure mapped object from the current position of the specified BinaryReader.
|
||||
*
|
||||
* @param <T> type of object
|
||||
* @param structureClass structure mapped object class
|
||||
* @param structReader {@link BinaryReader} positioned at the start of an object
|
||||
* @return new object instance of type T
|
||||
* @throws IOException if error reading
|
||||
* @throws IllegalArgumentException if specified structureClass is not valid
|
||||
*/
|
||||
public <T> T readStructure(Class<T> structureClass, BinaryReader structReader)
|
||||
throws IOException {
|
||||
StructureContext<T> structureContext = getStructureContext(structureClass, structReader);
|
||||
StructureContext<T> structureContext = createStructureContext(structureClass, structReader);
|
||||
|
||||
T result = structureContext.readNewInstance();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a structure mapped object from the specified position of the program.
|
||||
*
|
||||
* @param <T> type of object
|
||||
* @param structureClass structure mapped object class
|
||||
* @param position of object
|
||||
* @return new object instance of type T
|
||||
* @throws IOException if error reading
|
||||
* @throws IllegalArgumentException if specified structureClass is not valid
|
||||
*/
|
||||
public <T> T readStructure(Class<T> structureClass, long position) throws IOException {
|
||||
return readStructure(structureClass, getReader(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a structure mapped object from the specified Address of the program.
|
||||
*
|
||||
* @param <T> type of object
|
||||
* @param structureClass structure mapped object class
|
||||
* @param address location of object
|
||||
* @return new object instance of type T
|
||||
* @throws IOException if error reading
|
||||
* @throws IllegalArgumentException if specified structureClass is not valid
|
||||
*/
|
||||
public <T> T readStructure(Class<T> structureClass, Address address) throws IOException {
|
||||
return readStructure(structureClass, getReader(address.getOffset()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BinaryReader}, at the specified position.
|
||||
*
|
||||
* @param position location in the program
|
||||
* @return new {@link BinaryReader}
|
||||
*/
|
||||
public BinaryReader getReader(long position) {
|
||||
BinaryReader reader = createProgramReader();
|
||||
reader.setPointerIndex(position);
|
||||
return reader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts an offset into an Address.
|
||||
*
|
||||
* @param offset numeric offset
|
||||
* @return {@link Address}
|
||||
*/
|
||||
public Address getDataAddress(long offset) {
|
||||
return program.getImageBase().getNewAddress(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an offset into an Address.
|
||||
*
|
||||
* @param offset numeric offset
|
||||
* @return {@link Address}
|
||||
*/
|
||||
public Address getCodeAddress(long offset) {
|
||||
return program.getImageBase().getNewAddress(offset);
|
||||
}
|
||||
|
||||
public void labelAddress(Address addr, String symbolName) throws IOException {
|
||||
try {
|
||||
SymbolTable symbolTable = getProgram().getSymbolTable();
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
if (symbols.length == 0 || symbols[0].isDynamic()) {
|
||||
symbolName = SymbolUtilities.replaceInvalidChars(symbolName, true);
|
||||
symbolTable.createLabel(addr, symbolName, SourceType.IMPORTED);
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void labelStructure(T obj, String symbolName) throws IOException {
|
||||
Address addr = getExistingStructureAddress(obj);
|
||||
labelAddress(addr, symbolName);
|
||||
}
|
||||
|
||||
public void markupAddress(Address addr, DataType dt) throws IOException {
|
||||
markupAddress(addr, dt, -1);
|
||||
}
|
||||
|
||||
public void markupAddress(Address addr, DataType dt, int length) throws IOException {
|
||||
try {
|
||||
DataUtilities.createData(program, addr, dt, length, false,
|
||||
ClearDataMode.CLEAR_ALL_DEFAULT_CONFLICT_DATA);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void markupAddressIfUndefined(Address addr, DataType dt) throws IOException {
|
||||
Data data = DataUtilities.getDataAtAddress(program, addr);
|
||||
if (data == null || Undefined.isUndefined(data.getBaseDataType())) {
|
||||
markupAddress(addr, dt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataTypeMapper { program: %s}".formatted(program.getName());
|
||||
return "DataTypeMapper { program: %s }".formatted(program.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new BinaryReader that reads bytes from the current program's memory image.
|
||||
* <p>
|
||||
* Address offsets and index offsets in the BinaryReader should be synonymous.
|
||||
*
|
||||
* @return new BinaryReader
|
||||
*/
|
||||
protected BinaryReader createProgramReader() {
|
||||
MemoryByteProvider bp =
|
||||
new MemoryByteProvider(program.getMemory(), program.getImageBase().getAddressSpace());
|
||||
return new BinaryReader(bp, !program.getMemory().isBigEndian());
|
||||
}
|
||||
|
||||
protected DataType findType(String name, List<CategoryPath> searchList, DataTypeManager dtm) {
|
||||
for (CategoryPath searchCP : searchList) {
|
||||
DataType dataType = dtm.getDataType(searchCP, name);
|
||||
if (dataType != null) {
|
||||
return dataType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private <T> StructureContext<T> createStructureContext(Class<T> structureClass,
|
||||
BinaryReader reader) throws IllegalArgumentException {
|
||||
StructureMappingInfo<T> smi = getStructureMappingInfo(structureClass);
|
||||
if (smi == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown structure mapped class: " + structureClass.getSimpleName());
|
||||
}
|
||||
return new StructureContext<>(this, smi, reader);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,13 +27,14 @@ import java.lang.annotation.Target;
|
||||
* or the return value of a specified method as the string.
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target({ FIELD })
|
||||
@Target(FIELD)
|
||||
public @interface EOLComment {
|
||||
/**
|
||||
* Name of a "getter" method that's return value will be converted to a string and used
|
||||
* Optional name of a "getter" method that's return value will be converted to a string and used
|
||||
* as the EOL comment
|
||||
*
|
||||
* @return
|
||||
* @return optional name of a getter method, or if not set, defaults to use the object's
|
||||
* {@link Object#toString() toString()} method
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
|
@ -18,15 +18,18 @@ package ghidra.app.util.bin.format.golang.structmapping;
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataTypeComponent;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
||||
/**
|
||||
* Context of an individual field that is being deserialized
|
||||
* Context of an individual field that is being deserialized, or being markedup.
|
||||
*
|
||||
* @param <T> the class that contains this field
|
||||
* @param structureContext {@link StructureContext} that contains this field
|
||||
* @param fieldInfo {@link FieldMappingInfo} immutable information about this field
|
||||
* @param dtc {@link DataTypeComponent} that this field maps to
|
||||
* @param reader {@link BinaryReader} to use when reading data, may be null if this context is
|
||||
* for markup operations instead of deserialization
|
||||
*/
|
||||
public record FieldContext<T> (
|
||||
StructureContext<T> structureContext,
|
||||
@ -34,31 +37,32 @@ public record FieldContext<T> (
|
||||
DataTypeComponent dtc,
|
||||
BinaryReader reader) {
|
||||
|
||||
/**
|
||||
* Returns the structure instance that contains this field.
|
||||
*
|
||||
* @return structure instance that contains this field
|
||||
*/
|
||||
public T getStructureInstance() {
|
||||
return structureContext.getStructureInstance();
|
||||
}
|
||||
|
||||
public DataTypeMapper getDataTypeMapper() {
|
||||
return structureContext().getDataTypeMapper();
|
||||
}
|
||||
|
||||
public void appendComment(int commentType, String prefix, String comment, String sep)
|
||||
throws IOException {
|
||||
DWARFUtil.appendComment(structureContext.getProgram(), getAddress(), commentType, prefix,
|
||||
comment, sep);
|
||||
}
|
||||
|
||||
public void addReference(Address refDest) throws IOException {
|
||||
ReferenceManager refMgr = structureContext.getProgram().getReferenceManager();
|
||||
|
||||
Address fieldAddr = getAddress();
|
||||
refMgr.addMemoryReference(fieldAddr, refDest, RefType.DATA, SourceType.IMPORTED, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of this structure field.
|
||||
*
|
||||
* @return the address of this field
|
||||
*/
|
||||
public Address getAddress() {
|
||||
return structureContext.getStructureAddress().add(dtc.getOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this java field.
|
||||
*
|
||||
* @param <R> result type
|
||||
* @param expectedType class of expected result type
|
||||
* @return value of this java field, as type R
|
||||
* @throws IOException if error getting or converting value
|
||||
*/
|
||||
public <R> R getValue(Class<R> expectedType) throws IOException {
|
||||
return fieldInfo.getValue(structureContext.getStructureInstance(), expectedType);
|
||||
}
|
||||
|
@ -29,22 +29,43 @@ import java.lang.annotation.Target;
|
||||
* <p>
|
||||
* The type of the tagged java field can be a java primitive, or a
|
||||
* {@link StructureMapping structure mapped} class.
|
||||
* <p>
|
||||
* Supported java primitive types:
|
||||
* <ul>
|
||||
* <li>long, int, short, byte
|
||||
* <li>char
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(FIELD)
|
||||
public @interface FieldMapping {
|
||||
/**
|
||||
* Overrides the field name that is matched in the structure
|
||||
*
|
||||
* @return name of the structure field to map, or unset to use the java field's name
|
||||
*/
|
||||
String fieldName() default "";
|
||||
|
||||
/**
|
||||
* Optional function that will deserialize the tagged field
|
||||
*
|
||||
* @return {@link FieldReadFunction}
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class<? extends FieldReadFunction> readFunc() default FieldReadFunction.class;
|
||||
|
||||
/**
|
||||
* Allows override the length of the structure field
|
||||
*
|
||||
* @return length of the structure field, or unset to use the field's data type
|
||||
*/
|
||||
int length() default -1;
|
||||
|
||||
/**
|
||||
* Override the signedness the underlying numeric field.
|
||||
* Override the signedness of the underlying numeric field.
|
||||
*
|
||||
* @return
|
||||
* @return {@link Signedness} enum, or unset to use the data type's normal signedness
|
||||
*/
|
||||
Signedness signedness() default Signedness.Unspecified;
|
||||
}
|
||||
|
@ -15,11 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataTypeComponent;
|
||||
@ -29,18 +28,18 @@ import ghidra.program.model.listing.CodeUnit;
|
||||
/**
|
||||
* Immutable information needed to deserialize a field in a structure mapped class.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> structure mapped class type
|
||||
*/
|
||||
public class FieldMappingInfo<T> {
|
||||
/**
|
||||
* Creates a FieldMappingInfo instance, used when the structure is not variable length.
|
||||
*
|
||||
* @param <T>
|
||||
* @param field
|
||||
* @param dtc
|
||||
* @param signedness
|
||||
* @param length
|
||||
* @return
|
||||
* @param <T> structure mapped class type
|
||||
* @param field java field
|
||||
* @param dtc Ghidra structure field
|
||||
* @param signedness {@link Signedness} enum
|
||||
* @param length override of structure field, or -1
|
||||
* @return new {@link FieldMappingInfo} instance
|
||||
*/
|
||||
public static <T> FieldMappingInfo<T> createEarlyBinding(Field field, DataTypeComponent dtc,
|
||||
Signedness signedness, int length) {
|
||||
@ -57,12 +56,12 @@ public class FieldMappingInfo<T> {
|
||||
* Creates a FieldMappingInfo instance, used when the structure is variable length and there is
|
||||
* no pre-defined Ghidra Structure data type.
|
||||
*
|
||||
* @param <T>
|
||||
* @param field
|
||||
* @param fieldName
|
||||
* @param signedness
|
||||
* @param length
|
||||
* @return
|
||||
* @param <T> structure mapped class type
|
||||
* @param field java field
|
||||
* @param fieldName name of Ghidra structure field
|
||||
* @param signedness {@link Signedness} enum
|
||||
* @param length override of structure field, or -1
|
||||
* @return new {@link FieldMappingInfo} instance
|
||||
*/
|
||||
public static <T> FieldMappingInfo<T> createLateBinding(Field field, String fieldName,
|
||||
Signedness signedness, int length) {
|
||||
@ -186,14 +185,14 @@ public class FieldMappingInfo<T> {
|
||||
|
||||
private FieldMarkupFunction<T> createCommentMarkupFunc(Method commentGetter, int commentType,
|
||||
String sep) {
|
||||
return context -> {
|
||||
return (context, session) -> {
|
||||
T obj = context.getStructureInstance();
|
||||
Object val = ReflectionHelper.callGetter(commentGetter, obj);
|
||||
if (val != null) {
|
||||
if (val instanceof Collection<?> c && c.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
context.appendComment(commentType, null, val.toString(), sep);
|
||||
session.appendComment(context, commentType, null, val.toString(), sep);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -219,6 +218,9 @@ public class FieldMappingInfo<T> {
|
||||
}
|
||||
|
||||
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)
|
||||
// TODO: floats, other primitive types(?)
|
||||
|
||||
if (destClass == Long.class || destClass == Long.TYPE) {
|
||||
return (context) -> context.fieldInfo().isUnsigned()
|
||||
@ -255,51 +257,9 @@ public class FieldMappingInfo<T> {
|
||||
.readStructure(field.getType(), context.reader());
|
||||
}
|
||||
|
||||
// @SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
// private FieldMarkupFunction<T> makeMarkupFunc(
|
||||
// Class<? extends FieldMarkupFunction> markupFuncClass, String getterName) {
|
||||
// if (markupFuncClass != FieldMarkupFunction.class) {
|
||||
// return ReflectionHelper.createInstance(markupFuncClass, this);
|
||||
// }
|
||||
//
|
||||
// if (getterName != null && !getterName.isBlank()) {
|
||||
// Method getter = ReflectionHelper.findGetter(field.getDeclaringClass(), getterName);
|
||||
// if (getter == null) {
|
||||
// throw new IllegalArgumentException(
|
||||
// "Missing FieldMarkup getter %s for %s".formatted(getterName, field));
|
||||
// }
|
||||
// getter.setAccessible(true);
|
||||
// return (context) -> markupFieldWithGetter(getter, context);
|
||||
// }
|
||||
//
|
||||
// Class<?> fieldType = field.getType();
|
||||
// if (ReflectionHelper.hasStructureMapping(fieldType)) {
|
||||
// return this::markupNestedStructure;
|
||||
// }
|
||||
//
|
||||
// Method getter = ReflectionHelper.findGetter(field.getDeclaringClass(), field.getName());
|
||||
// if (getter != null) {
|
||||
// getter.setAccessible(true);
|
||||
// return (context) -> markupFieldWithGetter(getter, context);
|
||||
// }
|
||||
//
|
||||
// throw new IllegalArgumentException("Invalid FieldMarkup: " + field);
|
||||
// }
|
||||
|
||||
// private void markupFieldWithGetter(Method getterMethod, FieldContext<T> fieldContext)
|
||||
// throws IOException {
|
||||
// Object getterValue =
|
||||
// ReflectionHelper.callGetter(getterMethod, fieldContext.getStructureInstance());
|
||||
// if (getterValue != null) {
|
||||
// if (getterValue instanceof Collection<?> c && c.isEmpty()) {
|
||||
// return;
|
||||
// }
|
||||
// fieldContext.appendComment(CodeUnit.EOL_COMMENT, "", getterValue.toString(), ";");
|
||||
// }
|
||||
// }
|
||||
|
||||
private void markupNestedStructure(FieldContext<T> fieldContext) throws IOException {
|
||||
fieldContext.getDataTypeMapper().markup(fieldContext.getValue(Object.class), true);
|
||||
private void markupNestedStructure(FieldContext<T> fieldContext, MarkupSession markupSession)
|
||||
throws IOException {
|
||||
markupSession.markup(fieldContext.getValue(Object.class), true);
|
||||
}
|
||||
|
||||
private FieldMarkupFunction<T> makeMarkupReferenceFunc(String getterName) {
|
||||
@ -307,19 +267,19 @@ public class FieldMappingInfo<T> {
|
||||
|
||||
Method getter = ReflectionHelper.requireGetter(field.getDeclaringClass(), getterName);
|
||||
getter.setAccessible(true);
|
||||
return (context) -> addRefToFieldWithGetter(getter, context);
|
||||
return (context, session) -> addRefToFieldWithGetter(getter, context, session);
|
||||
}
|
||||
|
||||
private void addRefToFieldWithGetter(Method getterMethod, FieldContext<T> fieldContext)
|
||||
throws IOException {
|
||||
private void addRefToFieldWithGetter(Method getterMethod, FieldContext<T> fieldContext,
|
||||
MarkupSession markupSession) throws IOException {
|
||||
Object getterValue =
|
||||
ReflectionHelper.callGetter(getterMethod, fieldContext.getStructureInstance());
|
||||
if (getterValue != null) {
|
||||
Address addr = getterValue instanceof Address getterAddr
|
||||
? getterAddr
|
||||
: fieldContext.getDataTypeMapper().getExistingStructureAddress(getterValue);
|
||||
: markupSession.getMappingContext().getAddressOfStructure(getterValue);
|
||||
if (addr != null) {
|
||||
fieldContext.addReference(addr);
|
||||
markupSession.addReference(fieldContext, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,19 @@ package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A function that decorates a field in a structure mapped class.
|
||||
*
|
||||
* @param <T> structure mapped class type
|
||||
*/
|
||||
public interface FieldMarkupFunction<T> {
|
||||
void markupField(FieldContext<T> fieldContext) throws IOException;
|
||||
|
||||
/**
|
||||
* Decorates the specified field.
|
||||
*
|
||||
* @param fieldContext information about the field
|
||||
* @param markupSession state and methods to assist marking up the program
|
||||
* @throws IOException thrown if error performing the markup
|
||||
*/
|
||||
void markupField(FieldContext<T> fieldContext, MarkupSession markupSession) throws IOException;
|
||||
}
|
||||
|
@ -34,17 +34,44 @@ import ghidra.program.model.data.DataType;
|
||||
@Retention(RUNTIME)
|
||||
@Target(FIELD)
|
||||
public @interface FieldOutput {
|
||||
/**
|
||||
* Overrides the default logic used to add the marked field to the structure.
|
||||
*
|
||||
* @return {@link FieldOutputFunction} class that implements custom logic
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class<? extends FieldOutputFunction> fieldOutputFunc() default FieldOutputFunction.class;
|
||||
|
||||
/**
|
||||
* Optional ordinal of the marked field in the structure that will be created.
|
||||
* <p>
|
||||
* If unset, the order of the marked fields in the java class would be preserved.
|
||||
*
|
||||
* @return integer field ordinal, or if unset, the native java field order
|
||||
*/
|
||||
int ordinal() default -1;
|
||||
|
||||
/**
|
||||
* Optional offset for the marked field to be added at.
|
||||
* <p>
|
||||
* If the structure under construction is smaller than the specified offset, padding will be
|
||||
* added to the structure. If the structure is already larger than the specified offset,
|
||||
* an error will occur.
|
||||
*
|
||||
* @return integer offset for the marked field, or if unset, the next location in the
|
||||
* structure will be used
|
||||
*/
|
||||
int offset() default -1;
|
||||
|
||||
/**
|
||||
* Specifies the name of a Ghidra {@link DataType} that will be used for this field when
|
||||
* creating a Ghidra structure.
|
||||
* <p>
|
||||
* If unset, the type of the java field will be consulted to pick a Ghidra {@link DataType}
|
||||
* for the structure field.
|
||||
*
|
||||
* @return
|
||||
* @return name of the data type to use for this field, or if unset, the java field's type
|
||||
* will be used to pick the data type
|
||||
*/
|
||||
String dataTypeName() default "";
|
||||
|
||||
@ -52,7 +79,8 @@ public @interface FieldOutput {
|
||||
* Marks this field as variable length, which will cause the Ghidra structure containing
|
||||
* this field to have a "_NN" name suffix that specifies the length of this instance.
|
||||
*
|
||||
* @return
|
||||
* @return boolean true if the marked field's length varies between instances of the same
|
||||
* structure, false if it is a fixed length field
|
||||
*/
|
||||
boolean isVariableLength() default false;
|
||||
|
||||
@ -60,7 +88,7 @@ public @interface FieldOutput {
|
||||
* Specifies a method that will return a Ghidra {@link DataType} that should be used for this
|
||||
* field when creating a Ghidra structure.
|
||||
*
|
||||
* @return
|
||||
* @return optional name of getter method that will return a Ghidra {@link DataType}
|
||||
*/
|
||||
String getter() default "";
|
||||
|
||||
|
@ -17,16 +17,23 @@ package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
|
||||
/**
|
||||
* A function that helps construct a Ghidra {@link DataType} from annotated field information
|
||||
* A function that adds a field to a Ghidra structure using annotated field information
|
||||
* found in a Java class.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> type of the structure mapped class
|
||||
*/
|
||||
public interface FieldOutputFunction<T> {
|
||||
/**
|
||||
* Adds the specified field (in {@code fieldOutputInfo}) to the structure.
|
||||
*
|
||||
* @param context {@link StructureContext}
|
||||
* @param structure {@link Structure} data type
|
||||
* @param fieldOutputInfo {@link FieldOutputInfo} field info
|
||||
* @throws IOException if error
|
||||
*/
|
||||
void addFieldToStructure(StructureContext<T> context, Structure structure,
|
||||
FieldOutputInfo<T> fieldOutputInfo) throws IOException;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import ghidra.program.model.data.*;
|
||||
* Immutable information needed to create fields in a Ghidra structure data type, using information
|
||||
* from a java field.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> structure mapped class type
|
||||
*/
|
||||
public class FieldOutputInfo<T> {
|
||||
private final FieldMappingInfo<T> fmi;
|
||||
@ -63,11 +63,11 @@ public class FieldOutputInfo<T> {
|
||||
/**
|
||||
* Returns the value of this java field.
|
||||
*
|
||||
* @param <R>
|
||||
* @param <R> type of the result value
|
||||
* @param structInstance object containing the field
|
||||
* @param expectedType expected class of the value
|
||||
* @return value of the field
|
||||
* @throws IOException
|
||||
* @return value of the field, or null if the field's value is not of expected type
|
||||
* @throws IOException if error accessing java field
|
||||
*/
|
||||
public <R> R getValue(T structInstance, Class<R> expectedType) throws IOException {
|
||||
try {
|
||||
@ -198,7 +198,7 @@ public class FieldOutputInfo<T> {
|
||||
}
|
||||
|
||||
StructureContext<?> nestedStructContext =
|
||||
context.getDataTypeMapper().getExistingStructureContext(nestedStruct);
|
||||
context.getDataTypeMapper().getStructureContextOfInstance(nestedStruct);
|
||||
if (nestedStructContext == null) {
|
||||
throw new IOException(
|
||||
"Missing StructureContext for " + nestedStruct.getClass().getSimpleName());
|
||||
|
@ -17,8 +17,22 @@ package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Functional interface to read a structure field's value.
|
||||
* <p>
|
||||
* @see #get(FieldContext)
|
||||
*
|
||||
* @param <T> type of structure mapped class that contains this field
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FieldReadFunction<T> {
|
||||
/**
|
||||
* Deserializes and returns a field's value.
|
||||
*
|
||||
* @param context context for this field
|
||||
* @return value of the field
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
Object get(FieldContext<T> context) throws IOException;
|
||||
|
||||
}
|
||||
|
@ -38,5 +38,11 @@ import ghidra.program.model.address.Address;
|
||||
@Retention(RUNTIME)
|
||||
@Target(FIELD)
|
||||
public @interface MarkupReference {
|
||||
|
||||
/**
|
||||
* Optional name of a 'getter' method to use instead of the getter for the tagged field.
|
||||
*
|
||||
* @return name of 'getter' method, if unset the field's normal getter will be used
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
|
@ -0,0 +1,375 @@
|
||||
/* ###
|
||||
* 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.structmapping;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataUtilities.ClearDataMode;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* State and methods needed for structure mapped objects to add markup, comments, labels, etc
|
||||
* to a program.
|
||||
*/
|
||||
public class MarkupSession {
|
||||
protected Program program;
|
||||
protected DataTypeMapper mappingContext;
|
||||
protected Set<Address> markedupStructs = new HashSet<>();
|
||||
protected TaskMonitor monitor;
|
||||
|
||||
/**
|
||||
* Creates a new markup session
|
||||
*
|
||||
* @param programContext program-level structure mapping context
|
||||
* @param monitor allows user to cancel
|
||||
*/
|
||||
public MarkupSession(DataTypeMapper programContext, TaskMonitor monitor) {
|
||||
this.mappingContext = programContext;
|
||||
this.monitor = monitor;
|
||||
this.program = programContext.getProgram();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Ghidra program
|
||||
*
|
||||
* @return Ghidra {@link Program}
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the program level mapping context
|
||||
*
|
||||
* @return {@link DataTypeMapper}
|
||||
*/
|
||||
public DataTypeMapper getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorates the specified object's memory using the various structure mapping tags that
|
||||
* were applied the object's class definition.
|
||||
* <p>
|
||||
* The object can be a structure mapped object, or a collection, array or iterator of structure
|
||||
* mapped objects.
|
||||
*
|
||||
* @param <T> structure mapped object type
|
||||
* @param obj structure mapped object instance
|
||||
* @param nested boolean flag, if true the specified object is contained inside another object
|
||||
* who's data type has already been laid down in memory, removing the need for this object's
|
||||
* data type to be applied to memory
|
||||
* @throws IOException if error or cancelled
|
||||
* @throws IllegalArgumentException if object instance is not a supported type
|
||||
*/
|
||||
public <T> void markup(T obj, boolean nested) throws IOException {
|
||||
if (monitor.isCancelled()) {
|
||||
throw new IOException("Markup canceled");
|
||||
}
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
if (obj instanceof Collection<?> list) {
|
||||
for (Object listElement : list) {
|
||||
markup(listElement, nested);
|
||||
}
|
||||
}
|
||||
else if (obj.getClass().isArray()) {
|
||||
int len = Array.getLength(obj);
|
||||
for (int i = 0; i < len; i++) {
|
||||
markup(Array.get(obj, i), nested);
|
||||
}
|
||||
}
|
||||
else if (obj instanceof Iterator<?> it) {
|
||||
while (it.hasNext()) {
|
||||
Object itElement = it.next();
|
||||
markup(itElement, nested);
|
||||
}
|
||||
}
|
||||
else {
|
||||
StructureContext<T> structureContext = mappingContext.getStructureContextOfInstance(obj);
|
||||
if (structureContext == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
markupStructure(structureContext, nested);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified {@link DataType} to the specified {@link Address}.
|
||||
*
|
||||
* @param addr location to place DataType
|
||||
* @param dt {@link DataType}
|
||||
* @throws IOException if error marking up address
|
||||
*/
|
||||
public void markupAddress(Address addr, DataType dt) throws IOException {
|
||||
markupAddress(addr, dt, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified {@link DataType} to the specified {@link Address}.
|
||||
*
|
||||
* @param addr location to place DataType
|
||||
* @param dt {@link DataType}
|
||||
* @param length length of the data type instance, or -1 if the data type is fixed length
|
||||
* @throws IOException if error marking up address
|
||||
*/
|
||||
public void markupAddress(Address addr, DataType dt, int length) throws IOException {
|
||||
try {
|
||||
DataUtilities.createData(program, addr, dt, length, false,
|
||||
ClearDataMode.CLEAR_ALL_DEFAULT_CONFLICT_DATA);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified {@link DataType} to the specified {@link Address}.
|
||||
*
|
||||
* @param addr location to place DataType
|
||||
* @param dt {@link DataType}
|
||||
* @throws IOException if error marking up address
|
||||
*/
|
||||
public void markupAddressIfUndefined(Address addr, DataType dt) throws IOException {
|
||||
Data data = DataUtilities.getDataAtAddress(program, addr);
|
||||
if (data == null || Undefined.isUndefined(data.getBaseDataType())) {
|
||||
markupAddress(addr, dt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a label at the specified structure mapped object's address.
|
||||
*
|
||||
* @param <T> structure mapped object type
|
||||
* @param obj structure mapped object
|
||||
* @param symbolName name
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public <T> void labelStructure(T obj, String symbolName) throws IOException {
|
||||
Address addr = mappingContext.getAddressOfStructure(obj);
|
||||
labelAddress(addr, symbolName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a label at the specified address.
|
||||
*
|
||||
* @param addr {@link Address}
|
||||
* @param symbolName name
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public void labelAddress(Address addr, String symbolName) throws IOException {
|
||||
try {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol[] symbols = symbolTable.getSymbols(addr);
|
||||
if (symbols.length == 0 || symbols[0].isDynamic()) {
|
||||
symbolName = SymbolUtilities.replaceInvalidChars(symbolName, true);
|
||||
symbolTable.createLabel(addr, symbolName, SourceType.IMPORTED);
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment to the specified field, appending to any previous values
|
||||
* already there. If the existing comment already contains the specified comment value,
|
||||
* the operation is skipped.
|
||||
*
|
||||
* @param fieldContext the field
|
||||
* @param commentType {@link CodeUnit#EOL_COMMENT}, {@link CodeUnit#PLATE_COMMENT},
|
||||
* {@link CodeUnit#POST_COMMENT}, {@link CodeUnit#PRE_COMMENT}
|
||||
* @param prefix String prefix to place in front of the comment string
|
||||
* @param comment String value to append
|
||||
* @param sep separator to use between existing comments (for example, "\n")
|
||||
* @throws IOException if error adding comment
|
||||
*/
|
||||
public void appendComment(FieldContext<?> fieldContext, int commentType, String prefix,
|
||||
String comment, String sep) throws IOException {
|
||||
DWARFUtil.appendComment(program, fieldContext.getAddress(), commentType, prefix, comment,
|
||||
sep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment to the specified structure, appending to any previous values
|
||||
* already there. If the existing comment already contains the specified comment value,
|
||||
* the operation is skipped.
|
||||
*
|
||||
* @param structureContext the structure
|
||||
* @param commentType {@link CodeUnit#EOL_COMMENT}, {@link CodeUnit#PLATE_COMMENT},
|
||||
* {@link CodeUnit#POST_COMMENT}, {@link CodeUnit#PRE_COMMENT}
|
||||
* @param prefix String prefix to place in front of the comment string
|
||||
* @param comment String value to append
|
||||
* @param sep separator to use between existing comments (for example, "\n")
|
||||
* @throws IOException if error adding comment
|
||||
*/
|
||||
public void appendComment(StructureContext<?> structureContext, int commentType, String prefix,
|
||||
String comment, String sep) throws IOException {
|
||||
DWARFUtil.appendComment(program, structureContext.getStructureAddress(), commentType,
|
||||
prefix, comment, sep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorates a structure mapped structure, and everything it contains.
|
||||
*
|
||||
* @param <T> structure mapped type
|
||||
* @param structureContext {@link StructureContext}
|
||||
* @param nested if true, it is assumed that the Ghidra data types have already been
|
||||
* placed and only markup needs to be performed.
|
||||
* @throws IOException if error marking up structure
|
||||
*/
|
||||
public <T> void markupStructure(StructureContext<T> structureContext, boolean nested)
|
||||
throws IOException {
|
||||
Address addr = structureContext.getStructureAddress();
|
||||
if (!nested && !markedupStructs.add(addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
T instance = structureContext.getStructureInstance();
|
||||
if (!nested) {
|
||||
try {
|
||||
Structure structDT = structureContext.getStructureDataType();
|
||||
markupAddress(addr, structDT);
|
||||
}
|
||||
catch (IOException e) {
|
||||
StructureMappingInfo<T> mappingInfo = structureContext.getMappingInfo();
|
||||
throw new IOException("Markup failed for structure %s at %s"
|
||||
.formatted(mappingInfo.getDescription(), addr),
|
||||
e);
|
||||
}
|
||||
|
||||
if (instance instanceof StructureMarkup<?> sm) {
|
||||
String structureLabel = sm.getStructureLabel();
|
||||
if (structureLabel != null && structureLabel.isBlank()) {
|
||||
labelAddress(addr, structureLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
markupFields(structureContext);
|
||||
|
||||
if (instance instanceof StructureMarkup<?> sm) {
|
||||
sm.additionalMarkup(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
<T> void markupFields(StructureContext<T> structureContext) throws IOException {
|
||||
T structureInstance = structureContext.getStructureInstance();
|
||||
StructureMappingInfo<T> mappingInfo = structureContext.getMappingInfo();
|
||||
for (FieldMappingInfo<T> fmi : mappingInfo.getFields()) {
|
||||
for (FieldMarkupFunction<T> func : fmi.getMarkupFuncs()) {
|
||||
FieldContext<T> fieldContext = structureContext.createFieldContext(fmi, false);
|
||||
func.markupField(fieldContext, this);
|
||||
}
|
||||
}
|
||||
if (structureInstance instanceof StructureMarkup<?> sm) {
|
||||
for (Object externalInstance : sm.getExternalInstancesToMarkup()) {
|
||||
markup(externalInstance, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (StructureMarkupFunction<T> markupFunc : mappingInfo.getMarkupFuncs()) {
|
||||
markupFunc.markupStructure(structureContext, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates references from each element of an array to a list of target addresses.
|
||||
*
|
||||
* @param arrayAddr the address of the start of the array
|
||||
* @param elementSize the size of each array element
|
||||
* @param targetAddrs list of addresses that will receive references from each array elements
|
||||
* @throws IOException if error
|
||||
*/
|
||||
public void markupArrayElementReferences(Address arrayAddr, int elementSize,
|
||||
List<Address> targetAddrs) throws IOException {
|
||||
if (!targetAddrs.isEmpty()) {
|
||||
ReferenceManager refMgr = program.getReferenceManager();
|
||||
|
||||
for (Address targetAddr : targetAddrs) {
|
||||
if (targetAddr != null) {
|
||||
refMgr.addMemoryReference(arrayAddr, targetAddr, RefType.DATA,
|
||||
SourceType.IMPORTED, 0);
|
||||
}
|
||||
arrayAddr = arrayAddr.add(elementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default function at the specified address.
|
||||
*
|
||||
* @param name name of the new function
|
||||
* @param addr address of the new function
|
||||
* @return {@link Function} that was created
|
||||
*/
|
||||
public Function createFunctionIfMissing(String name, Address addr) {
|
||||
Function function = program.getListing().getFunctionAt(addr);
|
||||
if (function == null) {
|
||||
try {
|
||||
if (!program.getMemory()
|
||||
.getLoadedAndInitializedAddressSet()
|
||||
.contains(addr)) {
|
||||
Msg.warn(this,
|
||||
"Unable to create function not contained within loaded memory: %s@%s"
|
||||
.formatted(name, addr));
|
||||
return null;
|
||||
}
|
||||
function = program.getFunctionManager()
|
||||
.createFunction(name, addr, new AddressSet(addr), SourceType.IMPORTED);
|
||||
}
|
||||
catch (OverlappingFunctionException | InvalidInputException e) {
|
||||
Msg.error(this, e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: this does nothing. re-evalulate this logic
|
||||
//mappingContext.labelAddress(addr, name);
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reference from the specified field to the specified address.
|
||||
*
|
||||
* @param fieldContext field, is the source of the reference
|
||||
* @param refDest destination address of the reference
|
||||
*/
|
||||
public void addReference(FieldContext<?> fieldContext, Address refDest) {
|
||||
ReferenceManager refMgr = program.getReferenceManager();
|
||||
|
||||
Address fieldAddr = fieldContext.getAddress();
|
||||
refMgr.addMemoryReference(fieldAddr, refDest, RefType.DATA, SourceType.IMPORTED, 0);
|
||||
}
|
||||
|
||||
}
|
@ -34,7 +34,8 @@ public @interface PlateComment {
|
||||
* Name of a "getter" method that's return value will be converted to a string and used
|
||||
* as the comment
|
||||
*
|
||||
* @return
|
||||
* @return name of a 'getter' method that will return the string to use for the comment, or
|
||||
* if unset, the containing object's "toString()" method
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
|
@ -15,12 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
@ -63,7 +62,7 @@ public class ReflectionHelper {
|
||||
* @param field reflection {@link Field}
|
||||
* @param obj java instance that contains the field
|
||||
* @param value value to write
|
||||
* @throws IOException
|
||||
* @throws IOException if error accessing field or converting value
|
||||
*/
|
||||
public static void assignField(Field field, Object obj, Object value) throws IOException {
|
||||
Class<?> fieldType = field.getType();
|
||||
@ -92,11 +91,11 @@ public class ReflectionHelper {
|
||||
* Return Ghidra data type representing an array of primitive values.
|
||||
*
|
||||
* @param array_value java array object
|
||||
* @param fieldType
|
||||
* @param length
|
||||
* @param signedness
|
||||
* @param dataTypeMapper
|
||||
* @return
|
||||
* @param fieldType class representing the java array type
|
||||
* @param length length of an element of the array, or -1
|
||||
* @param signedness {@link Signedness} enum
|
||||
* @param dataTypeMapper program level structure mapping context
|
||||
* @return Ghdira {@link ArrayDataType} representing the specified java array type
|
||||
*/
|
||||
public static DataType getArrayOutputDataType(Object array_value, Class<?> fieldType, int length,
|
||||
Signedness signedness, DataTypeMapper dataTypeMapper) {
|
||||
@ -239,6 +238,17 @@ public class ReflectionHelper {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the specified target class, using an optional context parameter
|
||||
* to the constructor.
|
||||
*
|
||||
* @param <T> type of the class to be created
|
||||
* @param <CTX> type of the context to be passed to the constructor
|
||||
* @param targetClass class to be created
|
||||
* @param optionalContext anything, or null
|
||||
* @return new instance of type T
|
||||
* @throws IllegalArgumentException if error creating instance
|
||||
*/
|
||||
public static <T, CTX> T createInstance(Class<T> targetClass, CTX optionalContext)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
@ -293,9 +303,25 @@ public class ReflectionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static void getMarkedMethods(Class<?> targetClass,
|
||||
/**
|
||||
* Returns a list of methods that have been marked with a specific annotation.
|
||||
*
|
||||
* @param targetClass class to query
|
||||
* @param annotationClass annotation to search for
|
||||
* @param methods list to accumulate results into, or null to allocate new list. Also returned
|
||||
* as the result of this function
|
||||
* @param includeParentClasses boolean flag, if true recurse into parent classes first
|
||||
* @param paramClasses list of parameters that the tagged methods should declare. Methods
|
||||
* will be skipped if they don't match
|
||||
* @return list of found methods that match the annotation and param list
|
||||
*/
|
||||
public static List<Method> getMarkedMethods(Class<?> targetClass,
|
||||
Class<? extends Annotation> annotationClass, List<Method> methods,
|
||||
boolean includeParentClasses, Class<?>... paramClasses) {
|
||||
if (methods == null) {
|
||||
methods = new ArrayList<>();
|
||||
}
|
||||
|
||||
if (includeParentClasses && targetClass.getSuperclass() != null) {
|
||||
getMarkedMethods(targetClass.getSuperclass(), annotationClass, methods,
|
||||
includeParentClasses, paramClasses);
|
||||
@ -317,6 +343,7 @@ public class ReflectionHelper {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
public static <T extends Annotation> List<T> getAnnotations(Class<?> targetClass,
|
||||
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
/**
|
||||
* Signedness attribute of a structure mapped field
|
||||
*/
|
||||
public enum Signedness {
|
||||
Unspecified, Signed, Unsigned
|
||||
}
|
||||
|
@ -18,12 +18,9 @@ package ghidra.app.util.bin.format.golang.structmapping;
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataTypeComponent;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Information about an instance of a structure that has been read from the memory of a
|
||||
@ -53,6 +50,13 @@ public class StructureContext<T> {
|
||||
protected T structureInstance;
|
||||
protected Structure structureDataType;
|
||||
|
||||
/**
|
||||
* Creates an instance of a {@link StructureContext}.
|
||||
*
|
||||
* @param dataTypeMapper mapping context for the program
|
||||
* @param mappingInfo mapping information about this structure
|
||||
* @param reader {@link BinaryReader} positioned at the start of the structure to be read
|
||||
*/
|
||||
public StructureContext(DataTypeMapper dataTypeMapper, StructureMappingInfo<T> mappingInfo,
|
||||
BinaryReader reader) {
|
||||
this.dataTypeMapper = dataTypeMapper;
|
||||
@ -62,6 +66,13 @@ public class StructureContext<T> {
|
||||
this.structureDataType = mappingInfo.getStructureDataType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the structure by deserializing the structure's marked
|
||||
* fields into java fields.
|
||||
*
|
||||
* @return new instance of structure
|
||||
* @throws IOException if error reading
|
||||
*/
|
||||
public T readNewInstance() throws IOException {
|
||||
structureInstance = mappingInfo.getInstanceCreator().get(this);
|
||||
|
||||
@ -79,7 +90,7 @@ public class StructureContext<T> {
|
||||
/**
|
||||
* Returns the {@link StructureMappingInfo} for this structure's class.
|
||||
*
|
||||
* @return
|
||||
* @return {@link StructureMappingInfo} for this structure's class
|
||||
*/
|
||||
public StructureMappingInfo<T> getMappingInfo() {
|
||||
return mappingInfo;
|
||||
@ -91,16 +102,12 @@ public class StructureContext<T> {
|
||||
* a {@link ContextField} tag on a field in your class that specifies the correct
|
||||
* DataTypeMapper type.
|
||||
*
|
||||
* @return
|
||||
* @return the program mapping context that control's this structure instance
|
||||
*/
|
||||
public DataTypeMapper getDataTypeMapper() {
|
||||
return dataTypeMapper;
|
||||
}
|
||||
|
||||
public Program getProgram() {
|
||||
return dataTypeMapper.program;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address in the program of this structure instance.
|
||||
*
|
||||
@ -113,8 +120,9 @@ public class StructureContext<T> {
|
||||
/**
|
||||
* Returns the address of an offset from the start of this structure instance.
|
||||
*
|
||||
* @param fieldOffset
|
||||
* @return
|
||||
* @param fieldOffset number of bytes from the beginning of this structure where a field (or
|
||||
* other location of interest) starts
|
||||
* @return {@link Address} of specified offset
|
||||
*/
|
||||
public Address getFieldAddress(long fieldOffset) {
|
||||
return getStructureAddress().add(fieldOffset);
|
||||
@ -123,8 +131,9 @@ public class StructureContext<T> {
|
||||
/**
|
||||
* Returns the stream location of an offset from the start of this structure instance.
|
||||
*
|
||||
* @param fieldOffset
|
||||
* @return
|
||||
* @param fieldOffset number of bytes from the beginning of this structure where a field (or
|
||||
* other location of interest) starts
|
||||
* @return absolute offset / position in the program / BinaryReader stream
|
||||
*/
|
||||
public long getFieldLocation(long fieldOffset) {
|
||||
return structureStart + fieldOffset;
|
||||
@ -133,7 +142,7 @@ public class StructureContext<T> {
|
||||
/**
|
||||
* Returns the stream location of this structure instance.
|
||||
*
|
||||
* @return
|
||||
* @return absolute offset / position in the program / BinaryReader stream of this structure
|
||||
*/
|
||||
public long getStructureStart() {
|
||||
return structureStart;
|
||||
@ -142,7 +151,8 @@ public class StructureContext<T> {
|
||||
/**
|
||||
* Returns the stream location of the end of this structure instance.
|
||||
*
|
||||
* @return
|
||||
* @return absolute offset / position in the program / BinaryReader stream of the byte after
|
||||
* this structure
|
||||
*/
|
||||
public long getStructureEnd() {
|
||||
return structureStart + getStructureLength();
|
||||
@ -151,7 +161,8 @@ public class StructureContext<T> {
|
||||
/**
|
||||
* Returns the length of this structure instance.
|
||||
*
|
||||
* @return
|
||||
* @return length of this structure, or 0 if this structure is a variable length structure
|
||||
* that does not have a fixed length
|
||||
*/
|
||||
public int getStructureLength() {
|
||||
return structureDataType != null
|
||||
@ -159,10 +170,20 @@ public class StructureContext<T> {
|
||||
: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the object instance that was deserialized.
|
||||
*
|
||||
* @return reference to deserialized structure mapped object
|
||||
*/
|
||||
public T getStructureInstance() {
|
||||
return structureInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link BinaryReader} that is used to deserialize this structure.
|
||||
*
|
||||
* @return {@link BinaryReader} that is used to deserialize this structure
|
||||
*/
|
||||
public BinaryReader getReader() {
|
||||
return reader;
|
||||
}
|
||||
@ -171,14 +192,23 @@ public class StructureContext<T> {
|
||||
* Returns an independent {@link BinaryReader} that is positioned at the start of the
|
||||
* specified field.
|
||||
*
|
||||
* @param fieldOffset
|
||||
* @return
|
||||
* @param fieldOffset number of bytes from the beginning of this structure where a field (or
|
||||
* other location of interest) starts
|
||||
* @return new {@link BinaryReader} positioned at the specified relative offset
|
||||
*/
|
||||
public BinaryReader getFieldReader(long fieldOffset) {
|
||||
return reader.clone(structureStart + fieldOffset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link FieldContext} for a specific field.
|
||||
*
|
||||
* @param fmi {@link FieldMappingInfo field} of interest
|
||||
* @param includeReader boolean flag, if true create a BinaryReader for the field, if false no
|
||||
* BinaryReader will be created
|
||||
* @return new {@link FieldContext}
|
||||
*/
|
||||
public FieldContext<T> createFieldContext(FieldMappingInfo<T> fmi, boolean includeReader) {
|
||||
DataTypeComponent dtc = fmi.getDtc(structureDataType);
|
||||
BinaryReader fieldReader = includeReader ? getFieldReader(dtc.getOffset()) : null;
|
||||
@ -187,87 +217,14 @@ public class StructureContext<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a comment at the start of this structure, appending to any previous values
|
||||
* already there.
|
||||
* Returns the Ghidra {@link Structure structure data type} that represents this object.
|
||||
* <p>
|
||||
* If this is an instance of a variable length structure mapped class, a custom structure data
|
||||
* type will be minted that exactly matches this instance's variable length fields.
|
||||
*
|
||||
* @param commentType
|
||||
* @param prefix
|
||||
* @param comment
|
||||
* @param sep
|
||||
* @throws IOException
|
||||
* @return Ghidra {@link Structure structure data type} that represents this object
|
||||
* @throws IOException if error constructing new struct data type
|
||||
*/
|
||||
public void appendComment(int commentType, String prefix, String comment, String sep)
|
||||
throws IOException {
|
||||
DWARFUtil.appendComment(dataTypeMapper.getProgram(), getStructureAddress(), commentType,
|
||||
prefix, comment, sep);
|
||||
}
|
||||
|
||||
public boolean isAlreadyMarkedup() {
|
||||
Address addr = getStructureAddress();
|
||||
Data data = getProgram().getListing().getDataContaining(addr);
|
||||
if (data != null && data.getBaseDataType() instanceof Structure) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nested if true, it is assumed that the Ghidra data types have already been
|
||||
* placed and only markup needs to be performed.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void markupStructure(boolean nested) throws IOException {
|
||||
Address addr = getStructureAddress();
|
||||
if (!nested && !dataTypeMapper.markedupStructs.add(addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nested) {
|
||||
try {
|
||||
Structure structDT = getStructureDataType();
|
||||
dataTypeMapper.markupAddress(addr, structDT);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IOException("Markup failed for structure %s at %s"
|
||||
.formatted(mappingInfo.getDescription(), getStructureAddress()),
|
||||
e);
|
||||
}
|
||||
|
||||
if (structureInstance instanceof StructureMarkup<?> sm) {
|
||||
String structureLabel = sm.getStructureLabel();
|
||||
if (structureLabel != null) {
|
||||
dataTypeMapper.labelAddress(addr, structureLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
markupFields();
|
||||
|
||||
if (structureInstance instanceof StructureMarkup<?> sm) {
|
||||
sm.additionalMarkup();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void markupFields() throws IOException {
|
||||
for (FieldMappingInfo<T> fmi : mappingInfo.getFields()) {
|
||||
for (FieldMarkupFunction<T> func : fmi.getMarkupFuncs()) {
|
||||
func.markupField(createFieldContext(fmi, false));
|
||||
}
|
||||
}
|
||||
if (structureInstance instanceof StructureMarkup<?> sm) {
|
||||
for (Object externalInstance : sm.getExternalInstancesToMarkup()) {
|
||||
dataTypeMapper.markup(externalInstance, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (StructureMarkupFunction<T> markupFunc : mappingInfo.getMarkupFuncs()) {
|
||||
markupFunc.markupStructure(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Structure getStructureDataType() throws IOException {
|
||||
if (structureDataType == null) {
|
||||
// if this is a variable length struct, a new custom struct datatype needs to be created
|
||||
|
@ -35,8 +35,8 @@ import java.lang.annotation.*;
|
||||
* will be marked up in the Ghidra program.
|
||||
* <p>
|
||||
* The tagged class must be {@link DataTypeMapper#registerStructure(Class) registered} with
|
||||
* the program context to enable the suite of structure mapped classes to work together when
|
||||
* applied to a Ghidra binary.
|
||||
* the {@link DataTypeMapper program context} to enable the suite of structure mapped classes
|
||||
* to work together when applied to a Ghidra binary.
|
||||
* <p>
|
||||
* 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
|
||||
@ -61,10 +61,16 @@ public @interface StructureMapping {
|
||||
* {@link DataTypeMapper#addProgramSearchCategoryPath(ghidra.program.model.data.CategoryPath...) program}
|
||||
* search paths.
|
||||
*
|
||||
* @return
|
||||
* @return name of a Ghidra structure data type
|
||||
*/
|
||||
String structureName();
|
||||
|
||||
/**
|
||||
* Optional reference to a 'function' (implemented via a class) that will be called to do
|
||||
* custom markup.
|
||||
*
|
||||
* @return {@link StructureMarkupFunction} class
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class<? extends StructureMarkupFunction> markupFunc() default StructureMarkupFunction.class;
|
||||
}
|
||||
|
@ -15,10 +15,9 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
@ -33,11 +32,28 @@ 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.
|
||||
*
|
||||
* @param <T> structure mapped class
|
||||
* @param targetClass structure mapped class
|
||||
* @param structDataType Ghidra {@link DataType} that defines the binary layout of the mapped
|
||||
* fields of the class, or null if this is a self-reading {@link StructureReader} class
|
||||
* @return new {@link StructureMappingInfo} for the specified class
|
||||
* @throws IllegalArgumentException if targetClass isn't tagged as a structure mapped class
|
||||
*/
|
||||
public static <T> StructureMappingInfo<T> fromClass(Class<T> targetClass,
|
||||
Structure structDataType) {
|
||||
StructureMapping sma = targetClass.getAnnotation(StructureMapping.class);
|
||||
@ -59,7 +75,7 @@ public class StructureMappingInfo<T> {
|
||||
private final List<FieldOutputInfo<T>> outputFields = new ArrayList<>();
|
||||
private final List<StructureMarkupFunction<T>> markupFuncs = new ArrayList<>();
|
||||
private final List<Field> contextFields = new ArrayList<>();
|
||||
private final List<Method> afterMethods = new ArrayList<>();
|
||||
private final List<Method> afterMethods;
|
||||
private final boolean useFieldMappingInfo;
|
||||
private Field structureContextField;
|
||||
|
||||
@ -77,17 +93,13 @@ public class StructureMappingInfo<T> {
|
||||
Collections.sort(outputFields,
|
||||
(foi1, foi2) -> Integer.compare(foi1.getOrdinal(), foi2.getOrdinal()));
|
||||
|
||||
ReflectionHelper.getMarkedMethods(targetClass, AfterStructureRead.class, afterMethods,
|
||||
true);
|
||||
afterMethods =
|
||||
ReflectionHelper.getMarkedMethods(targetClass, AfterStructureRead.class, null, true);
|
||||
|
||||
List<Method> markupGetters = new ArrayList<>();
|
||||
ReflectionHelper.getMarkedMethods(targetClass, Markup.class, markupGetters, true);
|
||||
List<Method> markupGetters =
|
||||
ReflectionHelper.getMarkedMethods(targetClass, Markup.class, null, true);
|
||||
for (Method markupGetterMethod : markupGetters) {
|
||||
markupFuncs.add(context -> {
|
||||
T obj = context.getStructureInstance();
|
||||
Object val = ReflectionHelper.callGetter(markupGetterMethod, obj);
|
||||
context.getDataTypeMapper().markup(val, false);
|
||||
});
|
||||
markupFuncs.add(createMarkupFuncFromGetter(markupGetterMethod));
|
||||
}
|
||||
|
||||
for (PlateComment pca : ReflectionHelper.getAnnotations(targetClass, PlateComment.class,
|
||||
@ -131,6 +143,13 @@ public class StructureMappingInfo<T> {
|
||||
return afterMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a structure mapped instance by assigning values to its
|
||||
* {@link FieldMapping @FieldMapping mapped} java fields.
|
||||
*
|
||||
* @param context {@link StructureContext}
|
||||
* @throws IOException if error reading the structure
|
||||
*/
|
||||
public void readStructure(StructureContext<T> context) throws IOException {
|
||||
T newInstance = context.getStructureInstance();
|
||||
if (newInstance instanceof StructureReader<?> selfReader) {
|
||||
@ -154,6 +173,15 @@ public class StructureMappingInfo<T> {
|
||||
return markupFuncs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new customized {@link Structure structure data type} for a variable length
|
||||
* structure mapped class.
|
||||
*
|
||||
* @param context {@link StructureContext} of a variable length structure mapped instance
|
||||
* @return new {@link Structure structure data type} with a name that encodes the size
|
||||
* information of the variable length fields
|
||||
* @throws IOException if error creating the Ghidra data type
|
||||
*/
|
||||
public Structure createStructureDataType(StructureContext<T> context) throws IOException {
|
||||
// used to create a structure that has variable length fields
|
||||
|
||||
@ -185,19 +213,32 @@ public class StructureMappingInfo<T> {
|
||||
return newStruct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reaches into a structure mapped instance and extracts its StructureContext field value.
|
||||
*
|
||||
* @param structureInstance instance to query
|
||||
* @return {@link StructureContext}, or null if error extracting value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public StructureContext<T> recoverStructureContext(T structureInstance) throws IOException {
|
||||
return structureContextField != null
|
||||
? ReflectionHelper.getFieldValue(structureInstance, structureContextField,
|
||||
StructureContext.class)
|
||||
: null;
|
||||
public StructureContext<T> recoverStructureContext(T structureInstance) {
|
||||
try {
|
||||
if (structureContextField != null) {
|
||||
return ReflectionHelper.getFieldValue(structureInstance, structureContextField,
|
||||
StructureContext.class);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore, drop thru return null
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes any {@link ContextField} fields in a new structure instance.
|
||||
*
|
||||
* @param context
|
||||
* @throws IOException
|
||||
* @param context {@link StructureContext}
|
||||
* @throws IOException if error assigning values to context fields in the structure mapped
|
||||
* instance
|
||||
*/
|
||||
public void assignContextFieldValues(StructureContext<T> context) throws IOException {
|
||||
Class<?> dataTypeMapperType = context.getDataTypeMapper().getClass();
|
||||
@ -230,10 +271,10 @@ public class StructureMappingInfo<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void assignField(FieldContext<T> readContext, Object value)
|
||||
private void assignField(FieldContext<T> fieldContext, Object value)
|
||||
throws IOException {
|
||||
Field field = readContext.fieldInfo().getField();
|
||||
T structureInstance = readContext.getStructureInstance();
|
||||
Field field = fieldContext.fieldInfo().getField();
|
||||
T structureInstance = fieldContext.getStructureInstance();
|
||||
|
||||
ReflectionHelper.assignField(field, structureInstance, value);
|
||||
}
|
||||
@ -321,15 +362,23 @@ public class StructureMappingInfo<T> {
|
||||
private void addPlateCommentMarkupFuncs(PlateComment pca) {
|
||||
Method commentGetter =
|
||||
ReflectionHelper.getCommentMethod(targetClass, pca.value(), "toString");
|
||||
markupFuncs.add(context -> {
|
||||
markupFuncs.add((context, session) -> {
|
||||
T obj = context.getStructureInstance();
|
||||
Object val = ReflectionHelper.callGetter(commentGetter, obj);
|
||||
if (val != null) {
|
||||
context.appendComment(CodeUnit.PLATE_COMMENT, null, val.toString(), "\n");
|
||||
session.appendComment(context, CodeUnit.PLATE_COMMENT, null, val.toString(), "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private StructureMarkupFunction<T> createMarkupFuncFromGetter(Method markupGetterMethod) {
|
||||
return (context, session) -> {
|
||||
T obj = context.getStructureInstance();
|
||||
Object val = ReflectionHelper.callGetter(markupGetterMethod, obj);
|
||||
session.markup(val, false);
|
||||
};
|
||||
}
|
||||
|
||||
private static int getStructLength(Structure struct) {
|
||||
return struct.isZeroLength() ? 0 : struct.getLength();
|
||||
}
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Optional interface that structure mapped classes can implement that allows them to control how
|
||||
@ -36,7 +35,7 @@ public interface StructureMarkup<T> {
|
||||
* Returns the name of the instance, typically retrieved from data found inside the instance.
|
||||
*
|
||||
* @return string name, or null if this instance does not have a name
|
||||
* @throws IOException
|
||||
* @throws IOException if error getting name
|
||||
*/
|
||||
default String getStructureName() throws IOException {
|
||||
return null;
|
||||
@ -46,7 +45,7 @@ public interface StructureMarkup<T> {
|
||||
* Returns a string that can be used to place a label on the instance.
|
||||
*
|
||||
* @return string to be used as a labe, or null if there is not a valid label for the instance
|
||||
* @throws IOException
|
||||
* @throws IOException if error getting label
|
||||
*/
|
||||
default String getStructureLabel() throws IOException {
|
||||
String name = getStructureName();
|
||||
@ -59,9 +58,10 @@ public interface StructureMarkup<T> {
|
||||
/**
|
||||
* Called to allow the implementor to perform custom markup of itself.
|
||||
*
|
||||
* @throws IOException
|
||||
* @param session state and methods to assist marking up the program
|
||||
* @throws IOException if error during markup
|
||||
*/
|
||||
default void additionalMarkup() throws IOException {
|
||||
default void additionalMarkup(MarkupSession session) throws IOException {
|
||||
// empty
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ public interface StructureMarkup<T> {
|
||||
* Returns a list of items that should be recursively marked up.
|
||||
*
|
||||
* @return list of structure mapped object instances that should be marked up
|
||||
* @throws IOException
|
||||
* @throws IOException if error getting instances
|
||||
*/
|
||||
default List<?> getExternalInstancesToMarkup() throws IOException {
|
||||
return List.of();
|
||||
|
@ -17,6 +17,20 @@ package ghidra.app.util.bin.format.golang.structmapping;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Function that decorates a Ghidra structure
|
||||
*
|
||||
* @param <T> structure mapped class type
|
||||
*/
|
||||
public interface StructureMarkupFunction<T> {
|
||||
void markupStructure(StructureContext<T> context) throws IOException;
|
||||
|
||||
/**
|
||||
* Decorates the specified structure.
|
||||
*
|
||||
* @param context {@link StructureContext}
|
||||
* @param markupSession state and methods to assist marking up the program
|
||||
* @throws IOException thrown if error performing the markup
|
||||
*/
|
||||
void markupStructure(StructureContext<T> context, MarkupSession markupSession)
|
||||
throws IOException;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import java.io.IOException;
|
||||
* Interface used by structure mapped classes that need to manually deserialize themselves from
|
||||
* the raw data, required when the structure contains variable length fields.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <T> structure mapped type
|
||||
*/
|
||||
public interface StructureReader<T> {
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user