Merge remote-tracking branch 'origin/GP-6528_dev747368_golang_1.26'

(Closes #9013)
This commit is contained in:
Ryan Kurtz
2026-03-04 13:02:01 -05:00
10 changed files with 79 additions and 34 deletions
@@ -102,6 +102,7 @@ data/typeinfo/golang/go1.22.0.json||GHIDRA||||END|
data/typeinfo/golang/go1.23.0.json||GHIDRA||||END| data/typeinfo/golang/go1.23.0.json||GHIDRA||||END|
data/typeinfo/golang/go1.24.0.json||GHIDRA||||END| data/typeinfo/golang/go1.24.0.json||GHIDRA||||END|
data/typeinfo/golang/go1.25.0.json||GHIDRA||||END| data/typeinfo/golang/go1.25.0.json||GHIDRA||||END|
data/typeinfo/golang/go1.26.0.json||GHIDRA||||END|
data/typeinfo/golang/golang_1.15_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.15_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/golang/golang_1.16_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.16_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/golang/golang_1.17_anybit_any.gdt||GHIDRA||||END| data/typeinfo/golang/golang_1.17_anybit_any.gdt||GHIDRA||||END|
File diff suppressed because one or more lines are too long
@@ -37,10 +37,14 @@ import ghidra.util.Msg;
* <p> * <p>
* Similar to {@link NoteGoBuildId}, but re-implemented here because of the different * Similar to {@link NoteGoBuildId}, but re-implemented here because of the different
* serialization used. * serialization used.
*
* TODO: stop using 83 byte fixed len for buildid string, use leading / trailing quotes
*/ */
public class GoBuildId { public class GoBuildId {
private static final byte[] GO_BUILDID_MAGIC = private static final byte[] GO_BUILDID_MAGIC =
"\u00ff Go build ID: \"".getBytes(StandardCharsets.ISO_8859_1); "\u00ff Go build ID: \"".getBytes(StandardCharsets.ISO_8859_1);
private static final byte[] GO_BUIlDID_TRAILING_MAGIC =
"\"\n \u00ff".getBytes(StandardCharsets.ISO_8859_1);
private static final int BUILDID_STR_LEN = 83; private static final int BUILDID_STR_LEN = 83;
public static ItemWithAddress<GoBuildId> findBuildId(Program program) { public static ItemWithAddress<GoBuildId> findBuildId(Program program) {
@@ -64,7 +68,14 @@ public class GoBuildId {
if (!Arrays.equals(magic, GO_BUILDID_MAGIC)) { if (!Arrays.equals(magic, GO_BUILDID_MAGIC)) {
return null; return null;
} }
String buildIdStr = br.readNextAsciiString(BUILDID_STR_LEN); String buildIdStr = br.readNextAsciiString(BUILDID_STR_LEN);
byte[] trailingMagic = br.readNextByteArray(GO_BUIlDID_TRAILING_MAGIC.length);
if (!Arrays.equals(trailingMagic, GO_BUIlDID_TRAILING_MAGIC)) {
return null;
}
return new GoBuildId(buildIdStr); return new GoBuildId(buildIdStr);
} }
catch (IOException e) { catch (IOException e) {
@@ -81,7 +92,8 @@ public class GoBuildId {
* @return GoBuildId instance, or null if not present * @return GoBuildId instance, or null if not present
*/ */
public static GoBuildId read(InputStream is) { public static GoBuildId read(InputStream is) {
byte[] buffer = new byte[GO_BUILDID_MAGIC.length + BUILDID_STR_LEN]; byte[] buffer =
new byte[GO_BUILDID_MAGIC.length + BUILDID_STR_LEN + GO_BUIlDID_TRAILING_MAGIC.length];
try { try {
int bytesRead = is.read(buffer); int bytesRead = is.read(buffer);
if (bytesRead == buffer.length) { if (bytesRead == buffer.length) {
@@ -127,6 +139,8 @@ public class GoBuildId {
new StructureDataType(GoConstants.GOLANG_CATEGORYPATH, "GoBuildId", 0, dtm); new StructureDataType(GoConstants.GOLANG_CATEGORYPATH, "GoBuildId", 0, dtm);
result.add(StringDataType.dataType, GO_BUILDID_MAGIC.length, "magic", null); result.add(StringDataType.dataType, GO_BUILDID_MAGIC.length, "magic", null);
result.add(StringDataType.dataType, BUILDID_STR_LEN, "buildId", null); result.add(StringDataType.dataType, BUILDID_STR_LEN, "buildId", null);
result.add(StringDataType.dataType, GO_BUIlDID_TRAILING_MAGIC.length, "trailing_magic",
null);
return result; return result;
} }
@@ -412,14 +412,17 @@ public class GoModuledata implements StructureMarkup<GoModuledata> {
* @return new GoModuledata instance, or null if not found * @return new GoModuledata instance, or null if not found
* @throws IOException if error reading found structure * @throws IOException if error reading found structure
*/ */
/* package */ static GoModuledata getFirstModuledata(GoRttiMapper context) /* package */ static GoModuledata getFirstModuledata(GoRttiMapper context) throws IOException {
throws IOException {
Program program = context.getProgram(); Program program = context.getProgram();
Symbol firstModuleDataSymbol = GoRttiMapper.getGoSymbol(program, "runtime.firstmoduledata"); MemoryBlock memblk = context.getGoSection("go.module");
if (firstModuleDataSymbol == null) { if (memblk != null) {
return null; return context.readStructure(GoModuledata.class, memblk.getStart());
} }
return context.readStructure(GoModuledata.class, firstModuleDataSymbol.getAddress()); Symbol firstModuleDataSymbol = GoRttiMapper.getGoSymbol(program, "runtime.firstmoduledata");
if (firstModuleDataSymbol != null) {
return context.readStructure(GoModuledata.class, firstModuleDataSymbol.getAddress());
}
return null;
} }
/** /**
@@ -177,7 +177,7 @@ public class GoPcHeader {
@FieldMapping @FieldMapping
private byte ptrSize; private byte ptrSize;
@FieldMapping(presentWhen = "1.18+") @FieldMapping(presentWhen = "1.18-1.25")
@MarkupReference @MarkupReference
private long textStart; // should be same as offset of ".text" private long textStart; // should be same as offset of ".text"
@@ -216,7 +216,7 @@ public class GoPcHeader {
* {@return the address of where the text area starts} * {@return the address of where the text area starts}
*/ */
public Address getTextStart() { public Address getTextStart() {
return programContext.getDataAddress(textStart); return textStart != 0 ? programContext.getDataAddress(textStart) : null;
} }
/** /**
@@ -76,7 +76,7 @@ import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
* </ul> * </ul>
*/ */
public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContext { public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContext {
public static final GoVerRange SUPPORTED_VERSIONS = GoVerRange.parse("1.15-1.25"); public static final GoVerRange SUPPORTED_VERSIONS = GoVerRange.parse("1.15-1.26");
private static final List<String> SYMBOL_SEARCH_PREFIXES = List.of("", "_" /* macho symbols */); private static final List<String> SYMBOL_SEARCH_PREFIXES = List.of("", "_" /* macho symbols */);
private static final List<String> SECTION_PREFIXES = private static final List<String> SECTION_PREFIXES =
@@ -83,7 +83,7 @@ public class GoBaseType implements StructureVerifier {
* {@return the {@link GoTypeFlag}s assigned to this type definition} * {@return the {@link GoTypeFlag}s assigned to this type definition}
*/ */
public Set<GoTypeFlag> getFlags() { public Set<GoTypeFlag> getFlags() {
return GoTypeFlag.parseFlags(tflag); return GoTypeFlag.parseFlags(tflag, programContext.getGoVer());
} }
/** /**
@@ -98,7 +98,7 @@ public class GoBaseType implements StructureVerifier {
* structure} * structure}
*/ */
public boolean hasUncommonType() { public boolean hasUncommonType() {
return GoTypeFlag.Uncommon.isSet(tflag); return GoTypeFlag.Uncommon.isSet(tflag, programContext.getGoVer());
} }
/** /**
@@ -115,7 +115,9 @@ public class GoBaseType implements StructureVerifier {
*/ */
public String getName() { public String getName() {
String s = programContext.getSafeName(this::getGoName, this, "").getName(); String s = programContext.getSafeName(this::getGoName, this, "").getName();
return GoTypeFlag.ExtraStar.isSet(tflag) && s.startsWith("*") ? s.substring(1) : s; return GoTypeFlag.ExtraStar.isSet(tflag, programContext.getGoVer()) && s.startsWith("*")
? s.substring(1)
: s;
} }
/** /**
@@ -42,20 +42,24 @@ public class GoMapType extends GoType {
@MarkupReference("getElement") @MarkupReference("getElement")
private long elem; // ptr to type private long elem; // ptr to type
@FieldMapping @FieldMapping(presentWhen = "-1.25")
@MarkupReference("getBucket") @MarkupReference("getBucket")
private long bucket; // ptr to type private long bucket; // ptr to type
@FieldMapping(presentWhen = "1.26-")
@MarkupReference("getGroup")
private long group; // ptr to group type
@FieldMapping @FieldMapping
private long hasher; // pointer to "func(Pointer, pointer) pointer" private long hasher; // pointer to "func(Pointer, pointer) pointer"
@FieldMapping @FieldMapping(presentWhen = "-1.25")
private int keysize; private int keysize;
@FieldMapping(fieldName = {"elemsize", "ValueSize"}) @FieldMapping(fieldName = { "elemsize", "ValueSize" }, presentWhen = "-1.25")
private int elemsize; private int elemsize;
@FieldMapping @FieldMapping(presentWhen = "-1.25")
private int bucketsize; private int bucketsize;
@FieldMapping @FieldMapping
@@ -95,7 +99,18 @@ public class GoMapType extends GoType {
*/ */
@Markup @Markup
public GoType getBucket() throws IOException { public GoType getBucket() throws IOException {
return programContext.getGoTypes().getType(bucket); return bucket != 0 ? programContext.getGoTypes().getType(bucket) : null;
}
/**
* Returns the GoType that defines the map's group, referenced by the group field's markup annotation
*
* @return GoType that defines the map's group
* @throws IOException if error reading data
*/
@Markup
public GoType getGroup() throws IOException {
return group != 0 ? programContext.getGoTypes().getType(group) : null;
} }
@Override @Override
@@ -26,11 +26,16 @@ import ghidra.app.util.bin.format.golang.GoVerRange;
*/ */
public enum GoTypeFlag { public enum GoTypeFlag {
Uncommon(1 << 0, GoVerRange.ALL), // 1 Uncommon(1 << 0, GoVerRange.ALL), // 1
ExtraStar(1 << 1, GoVerRange.ALL), // 2 ExtraStar(1 << 1, GoVerRange.ALL), // 2
Named(1 << 2, GoVerRange.ALL), // 4 Named(1 << 2, GoVerRange.ALL), // 4
RegularMemory(1 << 3, GoVerRange.ALL), // 8 RegularMemory(1 << 3, GoVerRange.ALL), // 8
UnrolledBitmap(1 << 4, GoVerRange.parse("1.22-")); // 16 UnrolledBitmap(1 << 4, GoVerRange.parse("1.22-1.23")), // 16
GCMaskOnDemand(1 << 4, GoVerRange.parse("1.24-")), // 16
// FUTURE: "value of this type is stored directly in the data field of an interface instead of
// indirectly", same as testing Size_ == PtrBytes == goarch.PtrSize
DirectIFace(1 << 5, GoVerRange.parse("1.24-")); // 32
private final int value; private final int value;
private GoVerRange validVersions; private GoVerRange validVersions;
@@ -44,8 +49,16 @@ public enum GoTypeFlag {
return value; return value;
} }
public boolean isSet(int i) { /**
return (i & value) != 0; * Returns true if this enum instance is set in the supplied integer
* (for the specified go version)
*
* @param i int packed flag
* @param ver version of this binary
* @return boolean true if this flag is set
*/
public boolean isSet(int i, GoVer ver) {
return validVersions.contains(ver) && (i & value) != 0;
} }
//---------------------------------------------------------- //----------------------------------------------------------
@@ -53,19 +66,18 @@ public enum GoTypeFlag {
private static final GoTypeFlag[] lookupvalues = values(); private static final GoTypeFlag[] lookupvalues = values();
public static boolean isValid(int b, GoVer ver) { public static boolean isValid(int b, GoVer ver) {
int maxMask = 0;
for (GoTypeFlag flag : lookupvalues) { for (GoTypeFlag flag : lookupvalues) {
if (flag.validVersions.contains(ver)) { if (flag.validVersions.contains(ver)) {
maxMask |= flag.value; b &= ~flag.value;
} }
} }
return b <= maxMask; return b == 0;
} }
public static Set<GoTypeFlag> parseFlags(int b) { public static Set<GoTypeFlag> parseFlags(int b, GoVer ver) {
EnumSet<GoTypeFlag> result = EnumSet.noneOf(GoTypeFlag.class); EnumSet<GoTypeFlag> result = EnumSet.noneOf(GoTypeFlag.class);
for (GoTypeFlag flag : lookupvalues) { for (GoTypeFlag flag : lookupvalues) {
if (flag.isSet(b)) { if (flag.isSet(b, ver)) {
result.add(flag); result.add(flag);
} }
} }
@@ -331,14 +331,11 @@ public class GoApiSnapshotGeneratorTest extends AbstractGenericTest {
* Build a frankenstein goroot directory using the guts of a go install (liveGoRoot) and * Build a frankenstein goroot directory using the guts of a go install (liveGoRoot) and
* the src directory from a specific tagged version of a go git repo directory. * the src directory from a specific tagged version of a go git repo directory.
* *
* @param goRepoDir path to a golang git repo
* @param liveGoRoot path to an installed golang instance
* @param ver go version to checkout from the git repo and copy to the new directory * @param ver go version to checkout from the git repo and copy to the new directory
* @return path to newly created goroot directory (under the workDir) * @return path to newly created goroot directory (under the workDir)
* @throws IOException if failure * @throws IOException if failure
*/ */
File createVersionedGoRoot(GoVer ver) File createVersionedGoRoot(GoVer ver) throws IOException {
throws IOException {
String verTagName = getGoRepoTagName(GOLANG_REPO_DIR, ver); String verTagName = getGoRepoTagName(GOLANG_REPO_DIR, ver);
execGitCmd(GOLANG_REPO_DIR, null, "checkout", "-q", verTagName); execGitCmd(GOLANG_REPO_DIR, null, "checkout", "-q", verTagName);