GP-5455 golang interface method calling and decl

Improve how golang interface methods are handled.

Model the 'vtable' (runtime.itab) for a golang interface so each method
declared by the interface gets a funcdef that specifies the method's
params.
This commit is contained in:
dev747368 2024-11-12 10:36:35 -05:00
parent ecfd6d39d8
commit 2d3922d41f
29 changed files with 301 additions and 182 deletions

View file

@ -27,7 +27,7 @@ import ghidra.app.util.bin.format.dwarf.line.DWARFLine.SourceFileAddr;
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider; import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider;
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory; import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.CommentType;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -63,7 +63,7 @@ public class DWARFLineInfoCommentScript extends GhidraScript {
List<SourceFileAddr> allSFA = cu.getLine().getAllSourceFileAddrInfo(cu, reader); List<SourceFileAddr> allSFA = cu.getLine().getAllSourceFileAddrInfo(cu, reader);
for (SourceFileAddr sfa : allSFA) { for (SourceFileAddr sfa : allSFA) {
Address addr = dprog.getCodeAddress(sfa.address()); Address addr = dprog.getCodeAddress(sfa.address());
DWARFUtil.appendComment(currentProgram, addr, CodeUnit.EOL_COMMENT, "", DWARFUtil.appendComment(currentProgram, addr, CommentType.EOL, "",
"%s:%d".formatted(sfa.fileName(), sfa.lineNum()), ";"); "%s:%d".formatted(sfa.fileName(), sfa.lineNum()), ";");
count++; count++;
} }

View file

@ -302,13 +302,8 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
functionSignatureFromMethod++; functionSignatureFromMethod++;
} }
GoParamStorageAllocator storageAllocator = goBinary.newStorageAllocator(); GoFunctionFixup ff = new GoFunctionFixup(func, funcDefResult.funcDef(),
goBinary.getCallingConventionFor(funcdata), goBinary.newStorageAllocator());
boolean regAbi = !storageAllocator.isAbi0Mode() && !goBinary.isGolangAbi0Func(func);
String ccName = regAbi ? abiIntCCName : null;
GoFunctionFixup ff =
new GoFunctionFixup(func, funcDefResult.funcDef(), ccName, storageAllocator);
try { try {
ff.apply(); ff.apply();
@ -933,7 +928,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer {
@Override @Override
public boolean applyTo(Program program, TaskMonitor monitor) { public boolean applyTo(Program program, TaskMonitor monitor) {
if (goBinary.newStorageAllocator().isAbi0Mode()) { if (!goBinary.getRegInfo().hasAbiInternalParamRegisters()) {
// If abi0 mode, don't even bother because currently only handles rtti passed via // If abi0 mode, don't even bother because currently only handles rtti passed via
// register. // register.
return true; return true;

View file

@ -367,7 +367,7 @@ public class DWARFFunctionImporter {
// because this is a zero-length data type (ie. array[0]), // because this is a zero-length data type (ie. array[0]),
// don't create a variable at the location since it will prevent other elements // don't create a variable at the location since it will prevent other elements
// from occupying the same offset // from occupying the same offset
appendComment(address, CodeUnit.PRE_COMMENT, appendComment(address, CommentType.PRE,
"Zero length variable: %s: %s".formatted(name, dataType.getDisplayName()), "\n"); "Zero length variable: %s: %s".formatted(name, dataType.getDisplayName()), "\n");
return; return;
@ -384,13 +384,13 @@ public class DWARFFunctionImporter {
} }
if (dataType instanceof Dynamic || dataType instanceof FactoryDataType) { if (dataType instanceof Dynamic || dataType instanceof FactoryDataType) {
appendComment(address, CodeUnit.EOL_COMMENT, appendComment(address, CommentType.EOL,
"Unsupported dynamic data type: " + dataType, "\n"); "Unsupported dynamic data type: " + dataType, "\n");
dataType = Undefined.getUndefinedDataType(1); dataType = Undefined.getUndefinedDataType(1);
} }
DWARFDataInstanceHelper dih = new DWARFDataInstanceHelper(currentProgram); DWARFDataInstanceHelper dih = new DWARFDataInstanceHelper(currentProgram);
if (!dih.isDataTypeCompatibleWithAddress(dataType, address)) { if (!dih.isDataTypeCompatibleWithAddress(dataType, address)) {
appendComment(address, CodeUnit.EOL_COMMENT, appendComment(address, CommentType.EOL,
"Could not place DWARF static variable %s: %s @%s because existing data type conflicts." "Could not place DWARF static variable %s: %s @%s because existing data type conflicts."
.formatted(name, dataType.getName(), address), .formatted(name, dataType.getName(), address),
"\n"); "\n");
@ -414,8 +414,7 @@ public class DWARFFunctionImporter {
} }
if (globalVar.sourceInfo != null) { if (globalVar.sourceInfo != null) {
appendComment(address, CodeUnit.EOL_COMMENT, globalVar.sourceInfo.getDescriptionStr(), appendComment(address, CommentType.EOL, globalVar.sourceInfo.getDescriptionStr(), "\n");
"\n");
} }
} }
@ -437,7 +436,7 @@ public class DWARFFunctionImporter {
if (importOptions.isOutputLexicalBlockComments()) { if (importOptions.isOutputLexicalBlockComments()) {
boolean disjoint = blockRanges.getListCount() > 1; boolean disjoint = blockRanges.getListCount() > 1;
DWARFName dni = prog.getName(diea); DWARFName dni = prog.getName(diea);
appendComment(blockStart, CodeUnit.PRE_COMMENT, appendComment(blockStart, CommentType.PRE,
"Begin: %s%s".formatted(dni.getName(), disjoint ? " - Disjoint" : ""), "\n"); "Begin: %s%s".formatted(dni.getName(), disjoint ? " - Disjoint" : ""), "\n");
} }
} }
@ -471,22 +470,23 @@ public class DWARFFunctionImporter {
long inlineFuncLen = range.getLength(); long inlineFuncLen = range.getLength();
boolean isShort = inlineFuncLen < INLINE_FUNC_SHORT_LEN; boolean isShort = inlineFuncLen < INLINE_FUNC_SHORT_LEN;
if (isShort) { if (isShort) {
appendComment(range.getMinAddress(), CodeUnit.EOL_COMMENT, appendComment(range.getMinAddress(), CommentType.EOL,
"inline " + funcDef.getPrototypeString(), "; "); "inline " + funcDef.getPrototypeString(), "; ");
} }
else { else {
appendComment(range.getMinAddress(), CodeUnit.PRE_COMMENT, appendComment(range.getMinAddress(), CommentType.PRE,
"Begin: inline " + funcDef.getPrototypeString(), "\n"); "Begin: inline " + funcDef.getPrototypeString(), "\n");
} }
} }
} }
private void appendComment(Address address, int commentType, String comment, String sep) { private void appendComment(Address address, CommentType commentType, String comment,
String sep) {
DWARFUtil.appendComment(currentProgram, address, commentType, "", comment, sep); DWARFUtil.appendComment(currentProgram, address, commentType, "", comment, sep);
} }
private void appendPlateComment(Address address, String prefix, String comment) { private void appendPlateComment(Address address, String prefix, String comment) {
DWARFUtil.appendComment(currentProgram, address, CodeUnit.PLATE_COMMENT, prefix, comment, DWARFUtil.appendComment(currentProgram, address, CommentType.PLATE, prefix, comment,
"\n"); "\n");
} }
@ -572,7 +572,7 @@ public class DWARFFunctionImporter {
String locationInfo = DWARFSourceInfo.getDescriptionStr(diea); String locationInfo = DWARFSourceInfo.getDescriptionStr(diea);
if (locationInfo != null) { if (locationInfo != null) {
appendComment(address, CodeUnit.EOL_COMMENT, locationInfo, "; "); appendComment(address, CommentType.EOL, locationInfo, "; ");
} }
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {

View file

@ -346,7 +346,7 @@ public class DWARFUtil {
dtc.setComment(prev + description); dtc.setComment(prev + description);
} }
public static void appendComment(Program program, Address address, int commentType, public static void appendComment(Program program, Address address, CommentType commentType,
String prefix, String comment, String sep) { String prefix, String comment, String sep) {
if (comment == null || comment.isBlank()) { if (comment == null || comment.isBlank()) {
return; return;
@ -359,7 +359,7 @@ public class DWARFUtil {
return; return;
} }
} }
AppendCommentCmd cmd = new AppendCommentCmd(address, commentType, AppendCommentCmd cmd = new AppendCommentCmd(address, commentType.ordinal(),
Objects.requireNonNullElse(prefix, "") + comment, sep); Objects.requireNonNullElse(prefix, "") + comment, sep);
cmd.applyTo(program); cmd.applyTo(program);
} }

View file

@ -28,8 +28,6 @@ import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function.FunctionUpdateType; import ghidra.program.model.listing.Function.FunctionUpdateType;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolType;
import ghidra.program.model.symbol.SymbolUtilities; import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
@ -57,7 +55,7 @@ public class GoFunctionFixup {
this.newSignature = func.getSignature(); this.newSignature = func.getSignature();
this.newCallingConv = null; this.newCallingConv = null;
if (isGolangAbi0Func(func)) { if (GoRttiMapper.isAbi0Func(func.getEntryPoint(), program)) {
// Some (typically lower level) functions in the binary will be marked with a // Some (typically lower level) functions in the binary will be marked with a
// symbol that ends in the string "abi0". // symbol that ends in the string "abi0".
// Throw away all registers and force stack allocation for everything // Throw away all registers and force stack allocation for everything
@ -331,20 +329,6 @@ public class GoFunctionFixup {
} }
} }
public static boolean isGolangAbi0Func(Function func) {
Address funcAddr = func.getEntryPoint();
for (Symbol symbol : func.getProgram().getSymbolTable().getSymbolsAsIterator(funcAddr)) {
if (symbol.getSymbolType() == SymbolType.LABEL ||
symbol.getSymbolType() == SymbolType.FUNCTION) {
String labelName = symbol.getName();
if (labelName.endsWith("abi0")) {
return true;
}
}
}
return false;
}
private boolean isInLocalVarStorageArea(long stackOffset) { private boolean isInLocalVarStorageArea(long stackOffset) {
boolean paramsHavePositiveOffset = program.getCompilerSpec().stackGrowsNegative(); boolean paramsHavePositiveOffset = program.getCompilerSpec().stackGrowsNegative();
return (paramsHavePositiveOffset && stackOffset < 0) || return (paramsHavePositiveOffset && stackOffset < 0) ||

View file

@ -153,17 +153,17 @@ public class GoFunctionMultiReturn {
private record StackComponentInfo(DataTypeComponent dtc, int ordinal, String comment) {} private record StackComponentInfo(DataTypeComponent dtc, int ordinal, String comment) {}
private void regenerateMultireturnStruct(Structure struct, DataTypeManager dtm, private void regenerateMultireturnStruct(Structure newStruct, DataTypeManager dtm,
GoParamStorageAllocator storageAllocator) { GoParamStorageAllocator storageAllocator) {
String name = getComponentsInOriginalOrder(struct).stream() String name = getComponentsInOriginalOrder(newStruct).stream()
.map(dtc -> dtc.getDataType().getName()) .map(dtc -> dtc.getDataType().getName())
.collect(Collectors.joining(";", SHORT_MULTIVALUE_RETURNTYPE_PREFIX, .collect(Collectors.joining(";", SHORT_MULTIVALUE_RETURNTYPE_PREFIX,
SHORT_MULTIVALUE_RETURNTYPE_SUFFIX)); SHORT_MULTIVALUE_RETURNTYPE_SUFFIX));
if (struct.getName().equals(TMP_NAME)) { if (newStruct.getName().equals(TMP_NAME)) {
try { try {
struct.setName(name); newStruct.setName(name);
} }
catch (InvalidNameException | DuplicateNameException e) { catch (InvalidNameException | DuplicateNameException e) {
// should not happen // should not happen
@ -171,15 +171,15 @@ public class GoFunctionMultiReturn {
} }
if (storageAllocator == null) { if (storageAllocator == null) {
this.struct = struct; this.struct = newStruct;
for (DataTypeComponent dtc : getComponentsInOriginalOrder(struct)) { for (DataTypeComponent dtc : getComponentsInOriginalOrder(newStruct)) {
stackStorageComponents.add(dtc); stackStorageComponents.add(dtc);
} }
return; return;
} }
Structure adjustedStruct = new StructureDataType(struct.getCategoryPath(), Structure adjustedStruct = new StructureDataType(newStruct.getCategoryPath(),
name + "_" + storageAllocator.getArchDescription(), 0, dtm); name + "_" + storageAllocator.getArchDescription(), 0, dtm);
adjustedStruct.setPackingEnabled(true); adjustedStruct.setPackingEnabled(true);
adjustedStruct.setExplicitPackingValue(1); adjustedStruct.setExplicitPackingValue(1);
@ -187,7 +187,7 @@ public class GoFunctionMultiReturn {
storageAllocator = storageAllocator.clone(); storageAllocator = storageAllocator.clone();
List<StackComponentInfo> stackResults = new ArrayList<>(); List<StackComponentInfo> stackResults = new ArrayList<>();
int compNum = 0; int compNum = 0;
for (DataTypeComponent dtc : getComponentsInOriginalOrder(struct)) { for (DataTypeComponent dtc : getComponentsInOriginalOrder(newStruct)) {
List<Register> regs = storageAllocator.getRegistersFor(dtc.getDataType()); List<Register> regs = storageAllocator.getRegistersFor(dtc.getDataType());
if (regs == null || regs.isEmpty()) { if (regs == null || regs.isEmpty()) {
long stackOffset = storageAllocator.getStackAllocation(dtc.getDataType()); long stackOffset = storageAllocator.getStackAllocation(dtc.getDataType());
@ -215,8 +215,8 @@ public class GoFunctionMultiReturn {
} }
boolean isEquiv = DWARFDataTypeConflictHandler.INSTANCE.resolveConflict(adjustedStruct, boolean isEquiv = DWARFDataTypeConflictHandler.INSTANCE.resolveConflict(adjustedStruct,
struct) == ConflictResult.USE_EXISTING; newStruct) == ConflictResult.USE_EXISTING;
this.struct = isEquiv ? struct : adjustedStruct; this.struct = isEquiv ? newStruct : adjustedStruct;
} }
private static int getOrdinalNumber(DataTypeComponent dtc) { private static int getOrdinalNumber(DataTypeComponent dtc) {

View file

@ -261,12 +261,12 @@ public class GoParamStorageAllocator {
return false; return false;
} }
if (dt instanceof Structure struct) { if (dt instanceof Structure struct) {
DataTypeComponent prevDTC = null; // DataTypeComponent prevDTC = null;
for (DataTypeComponent dtc : struct.getDefinedComponents()) { for (DataTypeComponent dtc : struct.getDefinedComponents()) {
int padding = prevDTC != null ? dtc.getOffset() - prevDTC.getOffset() : 0; // int padding = prevDTC != null ? dtc.getOffset() - prevDTC.getOffset() : 0;
if (padding != 0) { // if (padding != 0) {
//
} // }
if (!countRegistersFor(dtc.getDataType(), result)) { if (!countRegistersFor(dtc.getDataType(), result)) {
return false; return false;
} }

View file

@ -107,6 +107,10 @@ public class GoRegisterInfo {
return stackInitialOffset; return stackInitialOffset;
} }
public boolean hasAbiInternalParamRegisters() {
return !intRegisters.isEmpty() || !floatRegisters.isEmpty();
}
public List<Variable> getDuffzeroParams(Program program) { public List<Variable> getDuffzeroParams(Program program) {
if (duffzeroDestParam == null) { if (duffzeroDestParam == null) {
return List.of(); return List.of();

View file

@ -17,13 +17,9 @@ package ghidra.app.util.bin.format.golang;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Objects;
import org.jdom.Document; import org.jdom.*;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder; import org.jdom.input.SAXBuilder;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
@ -143,7 +139,6 @@ public class GoRegisterInfoManager {
Element zeroRegElem = regInfoElem.getChild("zero_register"); Element zeroRegElem = regInfoElem.getChild("zero_register");
Element duffZeroElem = regInfoElem.getChild("duffzero"); Element duffZeroElem = regInfoElem.getChild("duffzero");
Element closureContextElem = regInfoElem.getChild("closurecontext"); Element closureContextElem = regInfoElem.getChild("closurecontext");
Element gcWriteBarrierElem = regInfoElem.getChild("gcwritebarrier");
if (intRegsElem == null || floatRegsElem == null || stackElem == null || if (intRegsElem == null || floatRegsElem == null || stackElem == null ||
goRoutineElem == null || zeroRegElem == null || duffZeroElem == null || goRoutineElem == null || zeroRegElem == null || duffZeroElem == null ||
closureContextElem == null) { closureContextElem == null) {

View file

@ -15,32 +15,17 @@
*/ */
package ghidra.app.util.bin.format.golang; package ghidra.app.util.bin.format.golang;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ghidra.app.util.bin.format.dwarf.DIEAggregate; import ghidra.app.util.bin.format.dwarf.*;
import ghidra.app.util.bin.format.dwarf.DWARFException;
import ghidra.app.util.bin.format.dwarf.DWARFFunction;
import ghidra.app.util.bin.format.dwarf.DWARFFunction.CommitMode; import ghidra.app.util.bin.format.dwarf.DWARFFunction.CommitMode;
import ghidra.app.util.bin.format.dwarf.DWARFSourceLanguage;
import ghidra.app.util.bin.format.dwarf.DWARFUtil;
import ghidra.app.util.bin.format.dwarf.DWARFVariable;
import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup; import ghidra.app.util.bin.format.dwarf.funcfixup.DWARFFunctionFixup;
import ghidra.app.util.bin.format.golang.rtti.GoFuncData; import ghidra.app.util.bin.format.golang.rtti.GoFuncData;
import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper; import ghidra.app.util.bin.format.golang.rtti.GoRttiMapper;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.CategoryPath; import ghidra.program.model.data.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeComponent;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.VoidDataType;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SymbolType; import ghidra.program.model.symbol.SymbolType;
import ghidra.util.classfinder.ExtensionPointProperties; import ghidra.util.classfinder.ExtensionPointProperties;
@ -301,7 +286,7 @@ public class GolangDWARFFunctionFixup implements DWARFFunctionFixup {
} }
private void appendComment(Function func, String prefix, String comment) { private void appendComment(Function func, String prefix, String comment) {
DWARFUtil.appendComment(goBinary.getProgram(), func.getEntryPoint(), CodeUnit.PLATE_COMMENT, DWARFUtil.appendComment(goBinary.getProgram(), func.getEntryPoint(), CommentType.PLATE,
prefix, comment, "\n"); prefix, comment, "\n");
} }
} }

View file

@ -49,8 +49,8 @@ public class GolangElfInfoProducer implements ElfInfoProducer {
private ElfLoadHelper elfLoadHelper; private ElfLoadHelper elfLoadHelper;
@Override @Override
public void init(ElfLoadHelper elfLoadHelper) { public void init(ElfLoadHelper newElfLoadHelper) {
this.elfLoadHelper = elfLoadHelper; this.elfLoadHelper = newElfLoadHelper;
} }
@Override @Override

View file

@ -19,10 +19,8 @@ import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import ghidra.app.util.bin.format.golang.rtti.types.GoIMethod; import ghidra.app.util.bin.format.golang.rtti.types.*;
import ghidra.app.util.bin.format.golang.rtti.types.GoIMethod.GoIMethodInfo; import ghidra.app.util.bin.format.golang.rtti.types.GoIMethod.GoIMethodInfo;
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.*; import ghidra.app.util.bin.format.golang.structmapping.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
@ -167,6 +165,9 @@ public class GoItab implements StructureMarkup<GoItab> {
@Override @Override
public void additionalMarkup(MarkupSession session) throws IOException { public void additionalMarkup(MarkupSession session) throws IOException {
// TODO: would be nice if we could override the base structure data type used to markup
// ourself, and use a specialized itab (as created by the GoInterfaceType).
GoSlice funSlice = getFunSlice(); GoSlice funSlice = getFunSlice();
List<Address> funcAddrs = Arrays.stream(funSlice.readUIntList(programContext.getPtrSize())) List<Address> funcAddrs = Arrays.stream(funSlice.readUIntList(programContext.getPtrSize()))
.mapToObj(offset -> programContext.getCodeAddress(offset)) .mapToObj(offset -> programContext.getCodeAddress(offset))
@ -200,5 +201,21 @@ public class GoItab implements StructureMarkup<GoItab> {
} }
} }
public void discoverGoTypes(Set<Long> discoveredTypes) {
try {
GoInterfaceType ifaceType = getInterfaceType();
if (ifaceType != null) {
ifaceType.discoverGoTypes(discoveredTypes);
}
GoType type = getType();
if (type != null) {
type.discoverGoTypes(discoveredTypes);
}
}
catch (IOException e) {
// fail, don't discover the ref'd types
}
}
} }

View file

@ -205,7 +205,7 @@ public class GoPcHeader {
/** /**
* Returns true if this pcln structure contains a textStart value (only present >= 1.18) * Returns true if this pcln structure contains a textStart value (only present >= 1.18)
* @return * @return boolean true if struct has textstart value
*/ */
public boolean hasTextStart() { public boolean hasTextStart() {
return textStart != 0; return textStart != 0;

View file

@ -153,6 +153,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
* Creates a {@link GoRttiMapper} representing the specified program. * Creates a {@link GoRttiMapper} representing the specified program.
* *
* @param program {@link Program} * @param program {@link Program}
* @param monitor {@link TaskMonitor}
* @return new {@link GoRttiMapper}, or null if basic golang information is not found in the * @return new {@link GoRttiMapper}, or null if basic golang information is not found in the
* binary * binary
* @throws BootstrapInfoException if it is a golang binary and has an unsupported or * @throws BootstrapInfoException if it is a golang binary and has an unsupported or
@ -359,6 +360,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
private final GoVer goVer; private final GoVer goVer;
private final int ptrSize; private final int ptrSize;
private final GoRegisterInfo regInfo; private final GoRegisterInfo regInfo;
private final String defaultCCName;
private final List<GoModuledata> modules = new ArrayList<>(); private final List<GoModuledata> modules = new ArrayList<>();
private Map<Address, GoFuncData> funcdataByAddr = new HashMap<>(); private Map<Address, GoFuncData> funcdataByAddr = new HashMap<>();
private Map<String, GoFuncData> funcdataByName = new HashMap<>(); private Map<String, GoFuncData> funcdataByName = new HashMap<>();
@ -376,6 +378,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
* @param goVer version of go * @param goVer version of go
* @param archiveGDT path to the matching golang bootstrap gdt data type file, or null * @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 * if not present and types recovered via DWARF should be used instead
* @param apiSnapshot json func signatures and data types
* @throws IOException if error linking a structure mapped structure to its matching * @throws IOException if error linking a structure mapped structure to its matching
* ghidra structure, which is a programming error or a corrupted bootstrap gdt * ghidra structure, which is a programming error or a corrupted bootstrap gdt
* @throws BootstrapInfoException if there is no matching bootstrap gdt for this specific * @throws BootstrapInfoException if there is no matching bootstrap gdt for this specific
@ -393,6 +396,10 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
this.buildInfo = buildInfo; this.buildInfo = buildInfo;
this.goVer = goVer; this.goVer = goVer;
this.ptrSize = ptrSize; this.ptrSize = ptrSize;
this.defaultCCName = regInfo.hasAbiInternalParamRegisters() &&
hasCallingConvention(GOLANG_ABI_INTERNAL_CALLINGCONVENTION_NAME)
? GOLANG_ABI_INTERNAL_CALLINGCONVENTION_NAME
: null;
reader = super.createProgramReader(); reader = super.createProgramReader();
@ -598,8 +605,11 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
* @return boolean true if function uses abi0 calling convention * @return boolean true if function uses abi0 calling convention
*/ */
public boolean isGolangAbi0Func(Function func) { public boolean isGolangAbi0Func(Function func) {
Address funcAddr = func.getEntryPoint(); return isAbi0Func(func.getEntryPoint(), program);
for (Symbol symbol : func.getProgram().getSymbolTable().getSymbolsAsIterator(funcAddr)) { }
public static boolean isAbi0Func(Address funcEntry, Program program) {
for (Symbol symbol : program.getSymbolTable().getSymbolsAsIterator(funcEntry)) {
if (symbol.getSymbolType() == SymbolType.LABEL) { if (symbol.getSymbolType() == SymbolType.LABEL) {
String labelName = symbol.getName(); String labelName = symbol.getName();
if (labelName.endsWith("abi0")) { if (labelName.endsWith("abi0")) {
@ -610,6 +620,14 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
return false; return false;
} }
public String getCallingConventionFor(GoFuncData func) {
// TODO: this logic needs work. Currently we are not strongly declaring functions
// as abi0.
return defaultCCName != null && !isAbi0Func(func.getFuncAddress(), program)
? defaultCCName
: null;
}
/** /**
* Returns true if the specified calling convention is defined for the program. * Returns true if the specified calling convention is defined for the program.
* @param ccName calling convention name * @param ccName calling convention name
@ -619,6 +637,10 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
return program.getFunctionManager().getCallingConvention(ccName) != null; return program.getFunctionManager().getCallingConvention(ccName) != null;
} }
public String getDefaultCallingConventionName() {
return defaultCCName;
}
@Override @Override
public MarkupSession createMarkupSession(TaskMonitor monitor) { public MarkupSession createMarkupSession(TaskMonitor monitor) {
UnknownProgressWrappingTaskMonitor upwtm = new UnknownProgressWrappingTaskMonitor(monitor); UnknownProgressWrappingTaskMonitor upwtm = new UnknownProgressWrappingTaskMonitor(monitor);
@ -869,7 +891,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
FROM_ANALYSIS, FROM_ANALYSIS,
CLOSURE, CLOSURE,
METHOD_WRAPPER METHOD_WRAPPER
}; }
public record FuncDefResult(FunctionDefinition funcDef, GoType recvType, Set<FuncDefFlags> flags, public record FuncDefResult(FunctionDefinition funcDef, GoType recvType, Set<FuncDefFlags> flags,
String funcDefStr, GoSymbolName symbolName) { String funcDefStr, GoSymbolName symbolName) {
@ -878,7 +900,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
/** /**
* Returns function definition information for a func. * Returns function definition information for a func.
* *
* @param funcData * @param funcData {@link GoFuncData} representing a go func
* @return {@link FuncDefResult} record, or null if no information could be found or * @return {@link FuncDefResult} record, or null if no information could be found or
* generated * generated
* @throws IOException if error reading type info * @throws IOException if error reading type info

View file

@ -121,7 +121,7 @@ public record GoSymbolName(String symbolName, String packagePath, String package
return result != null ? result : new GoSymbolName(s); return result != null ? result : new GoSymbolName(s);
} }
public static GoSymbolName _parse(String s) { private static GoSymbolName _parse(String s) {
if (s.startsWith("go:")) { if (s.startsWith("go:")) {
// don't try to parse "go:...." symbols // don't try to parse "go:...." symbols
return null; return null;
@ -451,9 +451,9 @@ public record GoSymbolName(String symbolName, String packagePath, String package
* <p> * <p>
* Nesting is delimited by '(', '{', '[' chars and their matching closing element. * Nesting is delimited by '(', '{', '[' chars and their matching closing element.
* *
* @param s * @param s string to split
* @param splitChar * @param splitChar char to split the string on
* @return * @return list of strings that are each part of the original string
*/ */
private static List<String> splitNestedStringOn(String s, char splitChar) { private static List<String> splitNestedStringOn(String s, char splitChar) {
// TODO: may also have to skip chars in quoted field comments // TODO: may also have to skip chars in quoted field comments

View file

@ -74,7 +74,7 @@ public class GoTypeManager {
private DataType int32DT; private DataType int32DT;
private DataType uint32DT; private DataType uint32DT;
private DataType uint8DT; private DataType uint8DT;
private DataType stringDT; private DataType voidPtrDT;
public GoTypeManager(GoRttiMapper goBinary, GoApiSnapshot apiSnapshot) { public GoTypeManager(GoRttiMapper goBinary, GoApiSnapshot apiSnapshot) {
this.goBinary = goBinary; this.goBinary = goBinary;
@ -89,6 +89,7 @@ public class GoTypeManager {
* @throws IOException if error reading data or cancelled * @throws IOException if error reading data or cancelled
*/ */
public void init(TaskMonitor monitor) throws IOException { public void init(TaskMonitor monitor) throws IOException {
this.voidPtrDT = dtm.getPointer(VoidDataType.dataType);
this.uintptrDT = goBinary.getTypeOrDefault("uintptr", DataType.class, this.uintptrDT = goBinary.getTypeOrDefault("uintptr", DataType.class,
AbstractIntegerDataType.getUnsignedDataType(goBinary.getPtrSize(), dtm)); AbstractIntegerDataType.getUnsignedDataType(goBinary.getPtrSize(), dtm));
this.uintDT = goBinary.getTypeOrDefault("uint", DataType.class, this.uintDT = goBinary.getTypeOrDefault("uint", DataType.class,
@ -100,7 +101,6 @@ public class GoTypeManager {
AbstractIntegerDataType.getUnsignedDataType(4, null)); AbstractIntegerDataType.getUnsignedDataType(4, null));
this.uint8DT = goBinary.getTypeOrDefault("uint8", DataType.class, this.uint8DT = goBinary.getTypeOrDefault("uint8", DataType.class,
AbstractIntegerDataType.getUnsignedDataType(1, null)); AbstractIntegerDataType.getUnsignedDataType(1, null));
this.stringDT = goBinary.getTypeOrDefault("string", Structure.class, null);
this.genericDictDT = dtm.getPointer(dtm.getPointer(uintptrDT)); this.genericDictDT = dtm.getPointer(dtm.getPointer(uintptrDT));
@ -141,9 +141,13 @@ public class GoTypeManager {
rec.interfaces = new ArrayList<>(); rec.interfaces = new ArrayList<>();
} }
rec.interfaces.add(itab); rec.interfaces.add(itab);
TypeRec ifaceRec = getTypeRec(itab.getInterfaceType());
if (ifaceRec.interfaces == null) {
ifaceRec.interfaces = new ArrayList<>();
}
ifaceRec.interfaces.add(itab);
itab.getInterfaceType().discoverGoTypes(discoveredTypes); itab.discoverGoTypes(discoveredTypes);
itab.getType().discoverGoTypes(discoveredTypes);
} }
findUnindexedClosureStructTypes(monitor); findUnindexedClosureStructTypes(monitor);
@ -197,6 +201,7 @@ public class GoTypeManager {
gapStart += typeStructAlign; gapStart += typeStructAlign;
continue; continue;
} }
@SuppressWarnings("unused")
TypeRec newTypeRec = getTypeRec(goType); // add to index TypeRec newTypeRec = getTypeRec(goType); // add to index
gapStart = getAlignedEndOfTypeInfo(goType, typeStructAlign); gapStart = getAlignedEndOfTypeInfo(goType, typeStructAlign);
foundCount++; foundCount++;
@ -273,7 +278,7 @@ public class GoTypeManager {
} }
/** /**
* Finds a go type by its go-type name * Finds a go type by its go-type name, from the list of discovered go types.
* *
* @param typeName name string * @param typeName name string
* @return {@link GoType}, or null if not found * @return {@link GoType}, or null if not found
@ -421,7 +426,7 @@ public class GoTypeManager {
/** /**
* Returns the go type that represents a generic map argument value. * Returns the go type that represents a generic map argument value.
* *
* @return * @return {@link GoType}
*/ */
public GoType getMapArgGoType() { public GoType getMapArgGoType() {
return mapArgGoType; return mapArgGoType;
@ -439,12 +444,16 @@ public class GoTypeManager {
/** /**
* Returns the go type that represents a generic chan argument value. * Returns the go type that represents a generic chan argument value.
* *
* @return * @return golang type for chan args
*/ */
public GoType getChanArgGoType() { public GoType getChanArgGoType() {
return chanArgGoType; return chanArgGoType;
} }
public DataType getUint8DT() {
return uint8DT;
}
public DataType getUintDT() { public DataType getUintDT() {
return uintDT; return uintDT;
} }
@ -476,6 +485,10 @@ public class GoTypeManager {
return uint32DT; return uint32DT;
} }
public DataType getVoidPtrDT() {
return voidPtrDT;
}
/** /**
* Returns the name of a gotype. * Returns the name of a gotype.
* *
@ -507,15 +520,14 @@ public class GoTypeManager {
*/ */
public List<GoItab> getInterfacesImplementedByType(GoType type) { public List<GoItab> getInterfacesImplementedByType(GoType type) {
TypeRec rec = getTypeRec(type); TypeRec rec = getTypeRec(type);
return rec.interfaces != null ? rec.interfaces : List.of(); List<GoItab> itabs = rec.interfaces != null ? rec.interfaces : List.of();
return itabs.stream().filter(itab -> itab._type == type.getTypeOffset()).toList();
} }
public void markTypeWithInterface(GoItab itab) throws IOException { public List<GoItab> getTypesThatImplementInterface(GoInterfaceType iface) {
TypeRec rec = getTypeRec(itab.getType()); TypeRec rec = getTypeRec(iface);
if (rec.interfaces == null) { List<GoItab> itabs = rec.interfaces != null ? rec.interfaces : List.of();
rec.interfaces = new ArrayList<>(); return itabs.stream().filter(itab -> itab.inter == iface.getTypeOffset()).toList();
}
rec.interfaces.add(itab);
} }
/** /**
@ -620,8 +632,7 @@ public class GoTypeManager {
/** /**
* Returns category path that should be used to place recovered golang types. * Returns category path that should be used to place recovered golang types.
* @param symbolName {@link GoSymbolName} to convert to a category path
* @param symbolName the symbol from which to get the package path
* @return {@link CategoryPath} to use when creating recovered golang types * @return {@link CategoryPath} to use when creating recovered golang types
*/ */
public CategoryPath getCP(GoSymbolName symbolName) { public CategoryPath getCP(GoSymbolName symbolName) {
@ -650,6 +661,10 @@ public class GoTypeManager {
return goBinary.getStructureDataType(GoIface.class); return goBinary.getStructureDataType(GoIface.class);
} }
public Structure getGenericITabDT() {
return goBinary.getStructureDataType(GoItab.class);
}
public DataType getMethodClosureType(String recvType) throws IOException { public DataType getMethodClosureType(String recvType) throws IOException {
//struct struct { F uintptr; R *atomic.Uint64 } //struct struct { F uintptr; R *atomic.Uint64 }
GoType closureType = findGoType("struct { F uintptr; R %s }".formatted(recvType)); GoType closureType = findGoType("struct { F uintptr; R %s }".formatted(recvType));
@ -679,8 +694,7 @@ public class GoTypeManager {
funcDef.setArguments(params); funcDef.setArguments(params);
closureDT.add(dtm.getPointer(funcDef), "F", null); closureDT.add(dtm.getPointer(funcDef), "F", null);
closureDT.add(new ArrayDataType(AbstractIntegerDataType.getUnsignedDataType(1, dtm), 0), closureDT.add(new ArrayDataType(uint8DT, 0), "context", null);
"context", null);
defaultClosureType = defaultClosureType =
(Structure) dtm.addDataType(closureDT, DataTypeConflictHandler.DEFAULT_HANDLER); (Structure) dtm.addDataType(closureDT, DataTypeConflictHandler.DEFAULT_HANDLER);
@ -701,7 +715,7 @@ public class GoTypeManager {
funcDef.setArguments(params); funcDef.setArguments(params);
closureDT.add(dtm.getPointer(funcDef), "F", null); closureDT.add(dtm.getPointer(funcDef), "F", null);
closureDT.add(dtm.getPointer(null), "R", "method receiver"); closureDT.add(voidPtrDT, "R", "method receiver");
defaultMethodWrapperType = defaultMethodWrapperType =
(Structure) dtm.addDataType(closureDT, DataTypeConflictHandler.DEFAULT_HANDLER); (Structure) dtm.addDataType(closureDT, DataTypeConflictHandler.DEFAULT_HANDLER);
@ -718,7 +732,7 @@ public class GoTypeManager {
public GoType getSubstitutionType(String typeName) { public GoType getSubstitutionType(String typeName) {
if (typeName.startsWith("*")) { if (typeName.startsWith("*")) {
return new GoTypeBridge(typeName, dtm.getPointer(null), goBinary); return new GoTypeBridge(typeName, getVoidPtrDT(), goBinary);
} }
else if (typeName.startsWith("[]") || typeName.equals("runtime.slice")) { else if (typeName.startsWith("[]") || typeName.equals("runtime.slice")) {
return new GoTypeBridge(typeName, getGenericSliceDT(), goBinary); return new GoTypeBridge(typeName, getGenericSliceDT(), goBinary);

View file

@ -56,7 +56,7 @@ public class GoChanType extends GoType {
if (chanGoType == null) { if (chanGoType == null) {
// if we couldn't find the underlying/hidden runtime.hchan struct type, just return // if we couldn't find the underlying/hidden runtime.hchan struct type, just return
// a void* // a void*
return programContext.getDTM().getPointer(null); return goTypes.getVoidPtrDT();
} }
DataType chanDT = goTypes.getGhidraDataType(chanGoType); DataType chanDT = goTypes.getGhidraDataType(chanGoType);

View file

@ -188,9 +188,15 @@ public class GoFuncType extends GoType {
CategoryPath cp = goTypes.getCP(this); CategoryPath cp = goTypes.getCP(this);
StructureDataType struct = new StructureDataType(cp, name, (int) typ.getSize(), dtm); StructureDataType struct = new StructureDataType(cp, name, (int) typ.getSize(), dtm);
DataType structPtr = dtm.getPointer(struct);
// pre-push an empty struct into the cache to prevent endless recursive loops FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(cp, name + "_F", dtm);
goTypes.cacheRecoveredDataType(this, struct); struct.replace(0, dtm.getPointer(funcDef), -1, "F", null);
struct.add(new ArrayDataType(goTypes.getUint8DT(), 0), "context", null);
struct.setToDefaultPacking();
// pre-push an partially constructed struct into the cache to prevent endless recursive loops
goTypes.cacheRecoveredDataType(this, structPtr);
List<GoType> paramTypes = getParamTypes(); List<GoType> paramTypes = getParamTypes();
List<GoType> inParamTypes = paramTypes.subList(0, inCount); List<GoType> inParamTypes = paramTypes.subList(0, inCount);
@ -214,24 +220,19 @@ public class GoFuncType extends GoType {
} }
else { else {
List<DataType> paramDataTypes = new ArrayList<>(); List<DataType> paramDataTypes = new ArrayList<>();
for (GoType typ : outParamTypes) { for (GoType outParamType : outParamTypes) {
paramDataTypes.add(goTypes.getGhidraDataType(typ)); paramDataTypes.add(goTypes.getGhidraDataType(outParamType));
} }
returnDT = goTypes.getFuncMultiReturn(paramDataTypes); returnDT = goTypes.getFuncMultiReturn(paramDataTypes);
} }
FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(cp, name + "_F", dtm);
funcDef.setArguments(params.toArray(ParameterDefinition[]::new)); funcDef.setArguments(params.toArray(ParameterDefinition[]::new));
funcDef.setReturnType(returnDT); funcDef.setReturnType(returnDT);
struct.replace(0, dtm.getPointer(funcDef), -1, "F", null);
struct.add(new ArrayDataType(AbstractIntegerDataType.getUnsignedDataType(1, dtm), 0),
"context", null);
struct.setToDefaultPacking();
// TODO: typ.getSize() should be ptrsize, and struct size should also be ptrsize // TODO: typ.getSize() should be ptrsize, and struct size should also be ptrsize
return dtm.getPointer(struct); return structPtr;
} }
public FunctionDefinition getFunctionSignature(GoTypeManager goTypes) throws IOException { public FunctionDefinition getFunctionSignature(GoTypeManager goTypes) throws IOException {
@ -242,6 +243,10 @@ public class GoFuncType extends GoType {
closureStructDT.getComponent(0).getDataType() instanceof Pointer funcdefPtr && closureStructDT.getComponent(0).getDataType() instanceof Pointer funcdefPtr &&
funcdefPtr.getDataType() instanceof FunctionDefinition fd ? fd : null; funcdefPtr.getDataType() instanceof FunctionDefinition fd ? fd : null;
if (funcdef == null) {
throw new IOException("Unable to extract function sig for " + this.toString());
}
List<ParameterDefinition> newArgs = new ArrayList<>(); List<ParameterDefinition> newArgs = new ArrayList<>();
ParameterDefinition[] oldArgs = funcdef.getArguments(); ParameterDefinition[] oldArgs = funcdef.getArguments();
for (int i = 1; i < oldArgs.length; i++) { for (int i = 1; i < oldArgs.length; i++) {

View file

@ -16,10 +16,14 @@
package ghidra.app.util.bin.format.golang.rtti.types; package ghidra.app.util.bin.format.golang.rtti.types;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.format.golang.GoConstants;
import ghidra.app.util.bin.format.golang.rtti.*; import ghidra.app.util.bin.format.golang.rtti.*;
import ghidra.app.util.bin.format.golang.structmapping.*; import ghidra.app.util.bin.format.golang.structmapping.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
@StructureMapping(structureName = {"runtime.imethod", "internal/abi.Imethod"}) @StructureMapping(structureName = {"runtime.imethod", "internal/abi.Imethod"})
public class GoIMethod implements StructureMarkup<GoIMethod> { public class GoIMethod implements StructureMarkup<GoIMethod> {
@ -50,8 +54,11 @@ public class GoIMethod implements StructureMarkup<GoIMethod> {
} }
@Markup @Markup
public GoType getType() throws IOException { public GoFuncType getType() throws IOException {
return programContext.getGoTypes().resolveTypeOff(context.getStructureStart(), ityp); return programContext.getGoTypes()
.resolveTypeOff(context.getStructureStart(), ityp) instanceof GoFuncType funcType
? funcType
: null;
} }
@Override @Override
@ -70,6 +77,24 @@ public class GoIMethod implements StructureMarkup<GoIMethod> {
getStructureContext()); getStructureContext());
} }
public FunctionDefinition getFunctionDefinition(boolean isGeneric, GoTypeManager goTypes)
throws IOException {
GoFuncType methodFuncDefType = getType();
if (methodFuncDefType == null) {
return null;
}
FunctionDefinition funcdef = methodFuncDefType.getFunctionSignature(goTypes);
List<ParameterDefinition> params = new ArrayList<>(List.of(funcdef.getArguments()));
params.add(0, new ParameterDefinitionImpl(GoConstants.GOLANG_RECEIVER_PARAM_NAME,
goTypes.getVoidPtrDT(), null));
if (isGeneric) {
params.add(1, new ParameterDefinitionImpl(GoConstants.GOLANG_GENERICS_PARAM_NAME,
goTypes.getGenericDictDT(), null));
}
funcdef.setArguments(params.toArray(ParameterDefinition[]::new));
return funcdef;
}
public static class GoIMethodInfo extends MethodInfo { public static class GoIMethodInfo extends MethodInfo {
GoItab itab; GoItab itab;
GoIMethod imethod; GoIMethod imethod;

View file

@ -21,9 +21,10 @@ import java.util.Set;
import ghidra.app.util.bin.format.golang.rtti.*; import ghidra.app.util.bin.format.golang.rtti.*;
import ghidra.app.util.bin.format.golang.structmapping.*; import ghidra.app.util.bin.format.golang.structmapping.*;
import ghidra.program.model.data.DataType; import ghidra.app.util.viewer.field.AddressAnnotatedStringHandler;
import ghidra.program.model.data.TypedefDataType; import ghidra.program.model.data.*;
import ghidra.util.exception.CancelledException; import ghidra.util.InvalidNameException;
import ghidra.util.exception.*;
/** /**
* A {@link GoType} structure that defines a golang interface. * A {@link GoType} structure that defines a golang interface.
@ -81,13 +82,55 @@ public class GoInterfaceType extends GoType {
@Override @Override
public DataType recoverDataType(GoTypeManager goTypes) throws IOException { public DataType recoverDataType(GoTypeManager goTypes) throws IOException {
DataType dt = programContext.getStructureDataType(GoIface.class); Structure genericIfaceDT = programContext.getStructureDataType(GoIface.class);
String name = goTypes.getTypeName(this); CategoryPath ifaceCP = goTypes.getCP(this);
if (!dt.getName().equals(name)) { String ifaceName = goTypes.getTypeName(this);
dt = new TypedefDataType(goTypes.getCP(this), name, dt, goTypes.getDTM()); StructureDataType ifaceDT =
new StructureDataType(ifaceCP, ifaceName, genericIfaceDT.getLength(), goTypes.getDTM());
ifaceDT.replaceWith(genericIfaceDT);
goTypes.cacheRecoveredDataType(this, ifaceDT);
Structure itabStruct = getSpecializedITabStruct(ifaceCP, ifaceName, goTypes);
int itabComponentOrdinal = 0; // TODO: hacky
DataTypeComponentImpl genericItabDTC = ifaceDT.getComponent(itabComponentOrdinal);
ifaceDT.replace(itabComponentOrdinal, goTypes.getDTM().getPointer(itabStruct), -1,
genericItabDTC.getFieldName(), null);
return ifaceDT;
}
public Structure getSpecializedITabStruct(CategoryPath ifaceCP, String ifaceName,
GoTypeManager goTypes) throws IOException {
DataTypeManager dtm = goTypes.getDTM();
Structure genericItabStruct = goTypes.getGenericITabDT();
StructureDataType itabStruct = new StructureDataType(ifaceCP, ifaceName + "_itab", 0, dtm);
itabStruct.replaceWith(genericItabStruct);
int funDTCOrdinal = 4; // a bit of a hack, could also lookup by name "Fun"
//DataTypeComponentImpl funDtc = itabStruct.getComponent(funDTCOrdinal);
itabStruct.delete(funDTCOrdinal);
CategoryPath funcsCP = ifaceCP.extend(itabStruct.getName() + "_funcs");
for (GoIMethod imethod : getMethods()) {
FunctionDefinition methodFuncDef = imethod.getFunctionDefinition(false, goTypes);
try {
methodFuncDef.setNameAndCategory(funcsCP, imethod.getName());
itabStruct.add(dtm.getPointer(methodFuncDef), imethod.getName(), null);
methodFuncDef
.setCallingConvention(programContext.getDefaultCallingConventionName());
}
catch (InvalidNameException | DuplicateNameException | InvalidInputException e) {
throw new IOException("Error creating itab for " + ifaceName, e);
}
} }
return dt;
return itabStruct;
} }
@Override @Override
@ -105,6 +148,27 @@ public class GoInterfaceType extends GoType {
return sb.toString(); return sb.toString();
} }
protected String getTypesThatImplementInterfaceString() {
StringBuilder sb = new StringBuilder();
for (GoItab goItab : programContext.getGoTypes().getTypesThatImplementInterface(this)) {
if (!sb.isEmpty()) {
sb.append("\n");
}
try {
GoType type = goItab.getType();
sb.append(AddressAnnotatedStringHandler.createAddressAnnotationString(
type.getStructureContext().getStructureAddress(),
type.getFullyQualifiedName()));
sb.append(AddressAnnotatedStringHandler.createAddressAnnotationString(
goItab.getStructureContext().getStructureAddress(), "[itab]"));
}
catch (IOException e) {
sb.append("bad type info");
}
}
return sb.toString();
}
@Override @Override
public boolean discoverGoTypes(Set<Long> discoveredTypes) throws IOException { public boolean discoverGoTypes(Set<Long> discoveredTypes) throws IOException {
if (!super.discoverGoTypes(discoveredTypes)) { if (!super.discoverGoTypes(discoveredTypes)) {
@ -124,4 +188,15 @@ public class GoInterfaceType extends GoType {
return super.isValid() && typ.getSize() == programContext.getPtrSize() * 2; // runtime.iface? return super.isValid() && typ.getSize() == programContext.getPtrSize() * 2; // runtime.iface?
} }
@Override
public String toString() {
String s = super.toString();
String implementations = getTypesThatImplementInterfaceString();
if (!implementations.isEmpty()) {
s += "\n\n// Implemented by:\n" + implementations;
}
return s;
}
} }

View file

@ -103,7 +103,7 @@ public class GoMapType extends GoType {
if (mapGoType == null) { if (mapGoType == null) {
// if we couldn't find the underlying/hidden runtime.hmap struct type, just return // if we couldn't find the underlying/hidden runtime.hmap struct type, just return
// a void* // a void*
return goTypes.getDTM().getPointer(null); return goTypes.getVoidPtrDT();
} }
DataType mapDT = goTypes.getGhidraDataType(mapGoType); DataType mapDT = goTypes.getGhidraDataType(mapGoType);
Pointer ptrMapDt = goTypes.getDTM().getPointer(mapDT); Pointer ptrMapDt = goTypes.getDTM().getPointer(mapDT);

View file

@ -43,25 +43,24 @@ public class GoPlainType extends GoType implements StructureReader<GoType> {
@Override @Override
public DataType recoverDataType(GoTypeManager goTypes) throws IOException { public DataType recoverDataType(GoTypeManager goTypes) throws IOException {
DataTypeManager dtm = goTypes.getDTM(); DataTypeManager dtm = goTypes.getDTM();
int ptrSize = programContext.getPtrSize();
DataType dt = switch (typ.getKind()) { DataType dt = switch (typ.getKind()) {
case Bool -> BooleanDataType.dataType; case Bool -> BooleanDataType.dataType;
case Float32 -> AbstractFloatDataType.getFloatDataType(32 / 8, null); case Float32 -> AbstractFloatDataType.getFloatDataType(32 / 8, null);
case Float64 -> AbstractFloatDataType.getFloatDataType(64 / 8, null); case Float64 -> AbstractFloatDataType.getFloatDataType(64 / 8, null);
case Int -> AbstractIntegerDataType.getSignedDataType(programContext.getPtrSize(), dtm); case Int -> AbstractIntegerDataType.getSignedDataType(ptrSize, dtm);
case Int8 -> AbstractIntegerDataType.getSignedDataType(8 / 8, null); case Int8 -> AbstractIntegerDataType.getSignedDataType(8 / 8, null);
case Int16 -> AbstractIntegerDataType.getSignedDataType(16 / 8, null); case Int16 -> AbstractIntegerDataType.getSignedDataType(16 / 8, null);
case Int32 -> AbstractIntegerDataType.getSignedDataType(32 / 8, null); case Int32 -> AbstractIntegerDataType.getSignedDataType(32 / 8, null);
case Int64 -> AbstractIntegerDataType.getSignedDataType(64 / 8, null); case Int64 -> AbstractIntegerDataType.getSignedDataType(64 / 8, null);
case Uint -> AbstractIntegerDataType.getUnsignedDataType(programContext.getPtrSize(), case Uint -> AbstractIntegerDataType.getUnsignedDataType(ptrSize, dtm);
dtm);
case Uint8 -> AbstractIntegerDataType.getUnsignedDataType(8 / 8, null); case Uint8 -> AbstractIntegerDataType.getUnsignedDataType(8 / 8, null);
case Uint16 -> AbstractIntegerDataType.getUnsignedDataType(16 / 8, null); case Uint16 -> AbstractIntegerDataType.getUnsignedDataType(16 / 8, null);
case Uint32 -> AbstractIntegerDataType.getUnsignedDataType(32 / 8, null); case Uint32 -> AbstractIntegerDataType.getUnsignedDataType(32 / 8, null);
case Uint64 -> AbstractIntegerDataType.getUnsignedDataType(64 / 8, null); case Uint64 -> AbstractIntegerDataType.getUnsignedDataType(64 / 8, null);
case Uintptr -> AbstractIntegerDataType.getUnsignedDataType(programContext.getPtrSize(), case Uintptr -> AbstractIntegerDataType.getUnsignedDataType(ptrSize, dtm);
dtm);
case String -> programContext.getStructureDataType(GoString.class); case String -> programContext.getStructureDataType(GoString.class);
case UnsafePointer -> dtm.getPointer(null); case UnsafePointer -> goTypes.getVoidPtrDT();
default -> null; default -> null;
}; };
if (dt == null) { if (dt == null) {

View file

@ -135,10 +135,10 @@ public class GoStructType extends GoType {
List<GoStructField> fieldList = getFields(); List<GoStructField> fieldList = getFields();
for (int i = 0; i < fieldList.size(); i++) { for (int i = 0; i < fieldList.size(); i++) {
GoStructField field = fieldList.get(i); GoStructField field = fieldList.get(i);
GoStructField nextField = i < fieldList.size() - 1 ? fieldList.get(i + 1) : null; // GoStructField nextField = i < fieldList.size() - 1 ? fieldList.get(i + 1) : null;
long availSpace = nextField != null // long availSpace = nextField != null
? nextField.getOffset() - field.getOffset() // ? nextField.getOffset() - field.getOffset()
: typ.getSize() - field.getOffset(); // : typ.getSize() - field.getOffset();
GoType fieldType = field.getType(); GoType fieldType = field.getType();
long fieldSize = fieldType.getBaseType().getSize(); long fieldSize = fieldType.getBaseType().getSize();

View file

@ -304,7 +304,10 @@ public abstract class GoType implements StructureMarkup<GoType>, StructureVerifi
/** /**
* Converts a golang RTTI type structure into a Ghidra data type. * Converts a golang RTTI type structure into a Ghidra data type.
* <p>
* This default implementation just creates an opaque blob of the appropriate size
* *
* @param goTypes {@link GoTypeManager}
* @return {@link DataType} that represents the golang type * @return {@link DataType} that represents the golang type
* @throws IOException if error getting name of the type * @throws IOException if error getting name of the type
*/ */

View file

@ -22,7 +22,7 @@ import java.util.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.CommentType;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
/** /**
@ -107,9 +107,9 @@ public class FieldMappingInfo<T> {
} }
public DataTypeComponent findDtc(Structure struct) { public DataTypeComponent findDtc(Structure struct) {
for (DataTypeComponent dtc : struct.getDefinedComponents()) { for (DataTypeComponent testDtc : struct.getDefinedComponents()) {
if (dtcFieldName.equals(dtc.getFieldName())) { if (dtcFieldName.equals(testDtc.getFieldName())) {
return dtc; return testDtc;
} }
} }
return null; return null;
@ -173,19 +173,19 @@ public class FieldMappingInfo<T> {
if (pca != null) { if (pca != null) {
Method commentGetter = Method commentGetter =
ReflectionHelper.getCommentMethod(clazz, pca.value(), field.getName()); ReflectionHelper.getCommentMethod(clazz, pca.value(), field.getName());
markupFuncs.add(createCommentMarkupFunc(commentGetter, CodeUnit.PLATE_COMMENT, "\n")); markupFuncs.add(createCommentMarkupFunc(commentGetter, CommentType.PLATE, "\n"));
} }
EOLComment eca = field.getAnnotation(EOLComment.class); EOLComment eca = field.getAnnotation(EOLComment.class);
if (eca != null) { if (eca != null) {
Method commentGetter = Method commentGetter =
ReflectionHelper.getCommentMethod(clazz, eca.value(), field.getName()); ReflectionHelper.getCommentMethod(clazz, eca.value(), field.getName());
markupFuncs.add(createCommentMarkupFunc(commentGetter, CodeUnit.EOL_COMMENT, ";")); markupFuncs.add(createCommentMarkupFunc(commentGetter, CommentType.EOL, ";"));
} }
} }
private FieldMarkupFunction<T> createCommentMarkupFunc(Method commentGetter, int commentType, private FieldMarkupFunction<T> createCommentMarkupFunc(Method commentGetter,
String sep) { CommentType commentType, String sep) {
return (context, session) -> { return (context, session) -> {
T obj = context.getStructureInstance(); T obj = context.getStructureInstance();
Object val = ReflectionHelper.callGetter(commentGetter, obj); Object val = ReflectionHelper.callGetter(commentGetter, obj);

View file

@ -242,14 +242,13 @@ public class MarkupSession {
* the operation is skipped. * the operation is skipped.
* *
* @param fieldContext the field * @param fieldContext the field
* @param commentType {@link CodeUnit#EOL_COMMENT}, {@link CodeUnit#PLATE_COMMENT}, * @param commentType {@link CommentType} enum
* {@link CodeUnit#POST_COMMENT}, {@link CodeUnit#PRE_COMMENT}
* @param prefix String prefix to place in front of the comment string * @param prefix String prefix to place in front of the comment string
* @param comment String value to append * @param comment String value to append
* @param sep separator to use between existing comments (for example, "\n") * @param sep separator to use between existing comments (for example, "\n")
* @throws IOException if error adding comment * @throws IOException if error adding comment
*/ */
public void appendComment(FieldContext<?> fieldContext, int commentType, String prefix, public void appendComment(FieldContext<?> fieldContext, CommentType commentType, String prefix,
String comment, String sep) throws IOException { String comment, String sep) throws IOException {
DWARFUtil.appendComment(program, fieldContext.getAddress(), commentType, prefix, comment, DWARFUtil.appendComment(program, fieldContext.getAddress(), commentType, prefix, comment,
sep); sep);
@ -261,22 +260,21 @@ public class MarkupSession {
* the operation is skipped. * the operation is skipped.
* *
* @param structureContext the structure * @param structureContext the structure
* @param commentType {@link CodeUnit#EOL_COMMENT}, {@link CodeUnit#PLATE_COMMENT}, * @param commentType {@link CommentType} enum
* {@link CodeUnit#POST_COMMENT}, {@link CodeUnit#PRE_COMMENT}
* @param prefix String prefix to place in front of the comment string * @param prefix String prefix to place in front of the comment string
* @param comment String value to append * @param comment String value to append
* @param sep separator to use between existing comments (for example, "\n") * @param sep separator to use between existing comments (for example, "\n")
* @throws IOException if error adding comment * @throws IOException if error adding comment
*/ */
public void appendComment(StructureContext<?> structureContext, int commentType, String prefix, public void appendComment(StructureContext<?> structureContext, CommentType commentType,
String comment, String sep) throws IOException { String prefix, String comment, String sep) throws IOException {
DWARFUtil.appendComment(program, structureContext.getStructureAddress(), commentType, DWARFUtil.appendComment(program, structureContext.getStructureAddress(), commentType,
prefix, comment, sep); prefix, comment, sep);
} }
public void appendComment(Function func, String prefix, String comment) { public void appendComment(Function func, String prefix, String comment) {
if (func != null) { if (func != null) {
DWARFUtil.appendComment(program, func.getEntryPoint(), CodeUnit.PLATE_COMMENT, prefix, DWARFUtil.appendComment(program, func.getEntryPoint(), CommentType.PLATE, prefix,
comment, "\n"); comment, "\n");
} }
} }

View file

@ -16,16 +16,11 @@
package ghidra.app.util.bin.format.golang.structmapping; package ghidra.app.util.bin.format.golang.structmapping;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.*;
import ghidra.program.model.data.DataTypeComponent; import ghidra.program.model.listing.CommentType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.StructureDataType;
import ghidra.program.model.listing.CodeUnit;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
@ -324,6 +319,7 @@ public class StructureMappingInfo<T> {
? FieldMappingInfo.createEarlyBinding(field, dtc, signedness, length) ? FieldMappingInfo.createEarlyBinding(field, dtc, signedness, length)
: FieldMappingInfo.createLateBinding(field, fieldNames[0], signedness, length); : FieldMappingInfo.createLateBinding(field, fieldNames[0], signedness, length);
@SuppressWarnings("rawtypes")
Class<? extends FieldReadFunction> fieldReadFuncClass = Class<? extends FieldReadFunction> fieldReadFuncClass =
fma != null ? fma.readFunc() : FieldReadFunction.class; fma != null ? fma.readFunc() : FieldReadFunction.class;
String setterNameOverride = fma != null ? fma.setter() : null; String setterNameOverride = fma != null ? fma.setter() : null;
@ -381,7 +377,7 @@ public class StructureMappingInfo<T> {
T obj = context.getStructureInstance(); T obj = context.getStructureInstance();
Object val = ReflectionHelper.callGetter(commentGetter, obj); Object val = ReflectionHelper.callGetter(commentGetter, obj);
if (val != null) { if (val != null) {
session.appendComment(context, CodeUnit.PLATE_COMMENT, null, val.toString(), "\n"); session.appendComment(context, CommentType.PLATE, null, val.toString(), "\n");
} }
}); });
} }

View file

@ -32,7 +32,7 @@ public interface StructureMarkupFunction<T> {
* @param context {@link StructureContext} * @param context {@link StructureContext}
* @param markupSession state and methods to assist marking up the program * @param markupSession state and methods to assist marking up the program
* @throws IOException thrown if error performing the markup * @throws IOException thrown if error performing the markup
* @throws CancelledException * @throws CancelledException if cancelled
*/ */
void markupStructure(StructureContext<T> context, MarkupSession markupSession) void markupStructure(StructureContext<T> context, MarkupSession markupSession)
throws IOException, CancelledException; throws IOException, CancelledException;

View file

@ -25,9 +25,11 @@ import java.io.IOException;
*/ */
public interface StructureReader<T> { public interface StructureReader<T> {
/** /**
* Called after an instance has been created and its context has been initialized. * Called after an instance has been created and its context has been initialized, to give
* the struct a chance to deserialize itself using the BinaryReaders and such found in the
* context information.
* *
* @throws IOException * @throws IOException if error deserializing data for this struct
*/ */
void readStructure() throws IOException; void readStructure() throws IOException;
} }