GP-6645 fix NPE in apisnapshot funcdef creation, chan types, xml

Replace missing types in a funcdef's return with an empty stub struct.
Handle chan types better.
Don't re-read the golang register info xml file every time a function is
fixed.
Fix macho go1.26 failure to find the first moduledata.
This commit is contained in:
dev747368
2026-04-01 23:19:30 +00:00
parent e69bd82102
commit 3f5178aff7
5 changed files with 93 additions and 16 deletions
@@ -302,10 +302,14 @@ public class GolangStringAnalyzer extends AbstractAnalyzer {
throws IOException, CancelledException {
// test to see if its a slice first because strings can kinda look like slices (a pointer
// then a length field).
boolean isUndefined3x =
DataUtilities.isUndefinedRange(program, addr, addr.add(sliceStructLen));
boolean isDefPtr = isDefaultPointer(addr);
boolean isUndefined3x = (isDefPtr && DataUtilities.isUndefinedRange(program,
addr.add(goBinary.getPtrSize()), addr.add(sliceStructLen - 1))) ||
DataUtilities.isUndefinedRange(program, addr, addr.add(sliceStructLen - 1));
boolean isUndefined2x = isUndefined3x ||
DataUtilities.isUndefinedRange(program, addr, addr.add(stringStructLen));
(isDefPtr && DataUtilities.isUndefinedRange(program,
addr.add(goBinary.getPtrSize()), addr.add(stringStructLen - 1))) ||
DataUtilities.isUndefinedRange(program, addr, addr.add(stringStructLen - 1));
Object newObj = isUndefined3x ? tryReadSliceStruct(addr) : null;
if (newObj == null && isUndefined2x) {
@@ -323,6 +327,12 @@ public class GolangStringAnalyzer extends AbstractAnalyzer {
return newObj;
}
private boolean isDefaultPointer(Address addr) {
Data data = program.getListing().getDataAt(addr);
return data != null && data.getDataType() instanceof DataType dt &&
((dt instanceof Pointer ptr && ptr.getDataType() == null) || Undefined.isUndefined(dt));
}
private GoString tryReadStringStruct(AddressSetView stringDataRange, Address addr) {
try {
GoString goString = goBinary.readStructure(GoString.class, addr);
@@ -61,6 +61,18 @@ public class GoParamStorageAllocator {
lang.getLanguageDescription().getSize());
}
public GoParamStorageAllocator(GoRegisterInfo callspecInfo, Program program) {
Language lang = program.getLanguage();
this.callspecInfo = callspecInfo;
this.stackOffset = callspecInfo.getStackInitialOffset();
this.regs = List.of(callspecInfo.getIntRegisters(), callspecInfo.getFloatRegisters());
this.isBigEndian = lang.isBigEndian();
this.archDescription =
"%s_%d".formatted(lang.getLanguageDescription().getProcessor().toString(),
lang.getLanguageDescription().getSize());
}
private GoParamStorageAllocator(List<List<Register>> regs, int[] nextReg,
GoRegisterInfo callspecInfo, long stackOffset, boolean isBigEndian,
String archDescription) {
@@ -414,7 +414,7 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
*/
/* package */ static GoModuledata getFirstModuledata(GoRttiMapper context) throws IOException {
Program program = context.getProgram();
MemoryBlock memblk = context.getGoSection("go.module");
MemoryBlock memblk = context.getFirstGoSection("go.module", "go_module");
if (memblk != null) {
return context.readStructure(GoModuledata.class, memblk.getStart());
}
@@ -236,8 +236,8 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
return null;
}
public static MemoryBlock getFirstGoSection(Program program, String... blockNames) {
for (String blockToSearch : blockNames) {
public static MemoryBlock getFirstGoSection(Program program, String... sectionNames) {
for (String blockToSearch : sectionNames) {
MemoryBlock memBlock = getGoSection(program, blockToSearch);
if (memBlock != null) {
return memBlock;
@@ -522,7 +522,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
* {@return new {@link GoParamStorageAllocator} param storage allocator instance}
*/
public GoParamStorageAllocator newStorageAllocator() {
GoParamStorageAllocator storageAllocator = new GoParamStorageAllocator(program, goVer);
GoParamStorageAllocator storageAllocator = new GoParamStorageAllocator(regInfo, program);
return storageAllocator;
}
@@ -709,7 +709,12 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
GoFuncDef snapshotFuncdef =
apiSnapshot.getFuncdef(symbolName.getStrippedSymbolString());
if (snapshotFuncdef != null) {
return createFuncDefFromApiSnapshot(symbolName, recvType, snapshotFuncdef);
try {
return createFuncDefFromApiSnapshot(symbolName, recvType, snapshotFuncdef);
}
catch (IOException e) {
// fail, fall thru to catch-all at bottom
}
}
}
@@ -1197,4 +1202,8 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
public MemoryBlock getGoSection(String sectionName) {
return getGoSection(program, sectionName);
}
public MemoryBlock getFirstGoSection(String... sectionNames) {
return getFirstGoSection(program, sectionNames);
}
}
@@ -53,6 +53,14 @@ public class GoTypeManager {
")" + // group=1
"(.*)" // everything else, group=2
);
private static final Pattern CHAN_TYPENAME_REGEX = Pattern.compile(
"(<-)?" + // maybe <-
"chan" + // 'chan'
"(<-)?" + // maybe <-
" " + // mandatory ' '
"(.+)" // everything else, group 3
);
//@formatter:on
static class TypeRec {
@@ -194,8 +202,7 @@ public class GoTypeManager {
long gapStart = t1.end;
while (t2.start - gapStart > typeStructMinSize) {
GoType goType = readTypeUnchecked(gapStart);
if (goType == null ||
!(goType instanceof GoStructType && goType.getSymbolName().isAnonType())) {
if (goType == null) {
gapStart += typeStructAlign;
continue;
}
@@ -440,6 +447,9 @@ public class GoTypeManager {
else if (typeName.startsWith("map[")) { // not handled by splitTypeName()
result = newTypeRecFromDT(typeName, createSpecializedMapDT(typeName));
}
else if (CHAN_TYPENAME_REGEX.matcher(typeName) instanceof Matcher m && m.matches()) {
result = newTypeRecFromDT(typeName, createSpecializedChanDT(typeName, m.group(3)));
}
else {
GoKind primitiveTypeKind = GoKind.parseTypename(typeName);
GoTypeDef typeDef;
@@ -475,6 +485,8 @@ public class GoTypeManager {
return convertStructDef(typeName, struct);
case GoFuncTypeDef func:
return convertFuncDef(typeName, func);
case GoInterfaceDef iface:
return convertIfaceDef(typeName, iface);
default:
throw new IOException("Go unhandled type definition: " + typeDef.toString());
}
@@ -512,11 +524,15 @@ public class GoTypeManager {
returnDT = findDataType(func.Results.get(0).DataType);
}
else {
List<DataType> paramDataTypes = new ArrayList<>();
List<DataType> returnDataTypes = new ArrayList<>();
for (GoNameTypePair outParam : func.Results) {
paramDataTypes.add(findDataType(outParam.DataType));
DataType dt = findDataType(outParam.DataType);
if (dt == null) {
dt = new StructureDataType(cp, ".missing_" + outParam.DataType, 0);
}
returnDataTypes.add(dt);
}
returnDT = getFuncMultiReturn(paramDataTypes);
returnDT = getFuncMultiReturn(returnDataTypes);
}
funcDef.setArguments(params.toArray(ParameterDefinition[]::new));
@@ -525,6 +541,21 @@ public class GoTypeManager {
return result;
}
private TypeRec convertIfaceDef(GoSymbolName typeName, GoInterfaceDef iface)
throws IOException {
TypeRec baseIface = findTypeRec("interface {}");
if (baseIface == null) {
throw new IOException("Missing type info for base interface {}");
}
String name = typeName.asString();
if (baseIface.name.equals(name)) {
return baseIface;
}
CategoryPath cp = getCP(typeName);
TypeDef td = new TypedefDataType(cp, name, baseIface.recoveredDT);
return newTypeRecFromDT(name, td);
}
private TypeRec convertBasicDef(GoSymbolName typeName, GoBasicDef basicDef) throws IOException {
GoKind kind = GoKind.parseTypename(basicDef.DataType);
if (kind == null) {
@@ -581,6 +612,7 @@ public class GoTypeManager {
GoNameTypePair field = structDef.Fields.get(i);
DataType dtcDT = findDataType(field.DataType);
if (dtcDT == null) {
dtcDT = findDataType(field.DataType);
throw new IOException("Failed to get type for field [%d %s: %s] in %s"
.formatted(i, field.Name, field.DataType, typeName));
}
@@ -879,6 +911,21 @@ public class GoTypeManager {
return voidPtrDT;
}
public DataType createSpecializedChanDT(String fullChanTypeName, String elementTypeName) {
try {
Structure hchanStruct = findDataType("runtime.hchan", Structure.class);
if (hchanStruct != null) {
GoSymbolName typeSymbolName = GoSymbolName.parseTypeName(elementTypeName);
return new TypedefDataType(getCP(typeSymbolName), fullChanTypeName,
dtm.getPointer(hchanStruct), dtm);
}
}
catch (IOException e) {
// fall thru
}
return voidPtrDT;
}
/**
* {@return data type that represents a generic Go slice}
* @throws IOException
@@ -1076,7 +1123,7 @@ public class GoTypeManager {
}
throw new IOException("Unknown type prefix: " + name);
}
else if (name.startsWith("map[") || name.startsWith("chan ")) {
else if (name.startsWith("map[") || CHAN_TYPENAME_REGEX.matcher(name).matches()) {
return new LengthAlignment(ptrSize, align(ptrSize));
}
else {
@@ -1112,8 +1159,7 @@ public class GoTypeManager {
return new LengthAlignment(ptrSize * 2, align(ptrSize));
}
private LengthAlignment getDataTypeLength(GoStructDef structDef)
throws IOException {
private LengthAlignment getDataTypeLength(GoStructDef structDef) throws IOException {
int len = 0;
int align = 1;
for (GoNameTypePair field : structDef.Fields) {