s := ldr.CreateSymForUpdate("go:buildinfo", 0)
s.SetType(sym.SBUILDINFO)
s.SetAlign(16)
+
// The \xff is invalid UTF-8, meant to make it less likely
// to find one of these accidentally.
- const prefix = "\xff Go buildinf:" // 14 bytes, plus 2 data bytes filled in below
+ const prefix = "\xff Go buildinf:" // 14 bytes, plus 1 data byte filled in below
+
+ // Header is always 32-bytes, a hold-over from before
+ // https://go.dev/cl/369977.
data := make([]byte, 32)
copy(data, prefix)
data[len(prefix)] = byte(ctxt.Arch.PtrSize)
data[len(prefix)+1] |= 2 // signals new pointer-free format
data = appendString(data, strdata["runtime.buildVersion"])
data = appendString(data, strdata["runtime.modinfo"])
- // MacOS linker gets very upset if the size os not a multiple of alignment.
+ // MacOS linker gets very upset if the size is not a multiple of alignment.
for len(data)%16 != 0 {
data = append(data, 0)
}
//go:linkname errNotGoExe
var errNotGoExe = errors.New("not a Go executable")
-// The build info blob left by the linker is identified by
-// a 16-byte header, consisting of buildInfoMagic (14 bytes),
-// the binary's pointer size (1 byte),
-// and whether the binary is big endian (1 byte).
+// The build info blob left by the linker is identified by a 32-byte header,
+// consisting of buildInfoMagic (14 bytes), followed by version-dependent
+// fields.
var buildInfoMagic = []byte("\xff Go buildinf:")
// ReadFile returns build information embedded in a Go binary
return "", "", err
}
const (
- buildInfoAlign = 16
- buildInfoSize = 32
+ buildInfoAlign = 16
+ buildInfoHeaderSize = 32
+
+ ptrSizeOffset = 14
+ flagsOffset = 15
+ versPtrOffset = 16
+
+ flagsEndianMask = 0x1
+ flagsEndianLittle = 0x0
+ flagsEndianBig = 0x1
+
+ flagsVersionMask = 0x2
+ flagsVersionPtr = 0x0
+ flagsVersionInl = 0x2
)
for {
i := bytes.Index(data, buildInfoMagic)
- if i < 0 || len(data)-i < buildInfoSize {
+ if i < 0 || len(data)-i < buildInfoHeaderSize {
return "", "", errNotGoExe
}
- if i%buildInfoAlign == 0 && len(data)-i >= buildInfoSize {
+ if i%buildInfoAlign == 0 && len(data)-i >= buildInfoHeaderSize {
data = data[i:]
break
}
data = data[(i+buildInfoAlign-1)&^(buildInfoAlign-1):]
}
- // Decode the blob.
- // The first 14 bytes are buildInfoMagic.
- // The next two bytes indicate pointer size in bytes (4 or 8) and endianness
- // (0 for little, 1 for big).
- // Two virtual addresses to Go strings follow that: runtime.buildVersion,
- // and runtime.modinfo.
- // On 32-bit platforms, the last 8 bytes are unused.
- // If the endianness has the 2 bit set, then the pointers are zero
- // and the 32-byte header is followed by varint-prefixed string data
- // for the two string values we care about.
- ptrSize := int(data[14])
- if data[15]&2 != 0 {
- vers, data = decodeString(data[32:])
+ // Decode the blob. The blob is a 32-byte header, optionally followed
+ // by 2 varint-prefixed string contents.
+ //
+ // type buildInfoHeader struct {
+ // magic [14]byte
+ // ptrSize uint8 // used if flagsVersionPtr
+ // flags uint8
+ // versPtr targetUintptr // used if flagsVersionPtr
+ // modPtr targetUintptr // used if flagsVersionPtr
+ // }
+ //
+ // The version bit of the flags field determines the details of the format.
+ //
+ // Prior to 1.18, the flags version bit is flagsVersionPtr. In this
+ // case, the header includes pointers to the version and modinfo Go
+ // strings in the header. The ptrSize field indicates the size of the
+ // pointers and the endian bit of the flag indicates the pointer
+ // endianness.
+ //
+ // Since 1.18, the flags version bit is flagsVersionInl. In this case,
+ // the header is followed by the string contents inline as
+ // length-prefixed (as varint) string contents. First is the version
+ // string, followed immediately by the modinfo string.
+ flags := data[flagsOffset]
+ if flags&flagsVersionMask == flagsVersionInl {
+ vers, data = decodeString(data[buildInfoHeaderSize:])
mod, data = decodeString(data)
} else {
- bigEndian := data[15] != 0
+ // flagsVersionPtr (<1.18)
+ ptrSize := int(data[ptrSizeOffset])
+ bigEndian := flags&flagsEndianMask == flagsEndianBig
var bo binary.ByteOrder
if bigEndian {
bo = binary.BigEndian
} else {
return "", "", errNotGoExe
}
- vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))
- mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
+ vers = readString(x, ptrSize, readPtr, readPtr(data[versPtrOffset:]))
+ mod = readString(x, ptrSize, readPtr, readPtr(data[versPtrOffset+ptrSize:]))
}
if vers == "" {
return "", "", errNotGoExe