1
0
mirror of https://github.com/golang/go synced 2024-11-22 05:24:39 -07:00

debug/buildinfo: improve format documentation

Existing documentation is a bit sparse, and more importantly focuses
almost entirely on the old pre-1.18 format, with the new format as an
afterthought. Since the new format is the primary format, make it more
prominent.

Updates #68592.

Change-Id: I108ecde1b33650b4812fa5d278b08cb9197f6329
Reviewed-on: https://go-review.googlesource.com/c/go/+/601456
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Michael Pratt <mpratt@google.com>
This commit is contained in:
Michael Pratt 2024-07-26 10:02:55 -04:00 committed by Gopher Robot
parent 705f9848ef
commit d531e344ae
2 changed files with 56 additions and 26 deletions

View File

@ -2351,9 +2351,13 @@ func (ctxt *Link) buildinfo() {
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)
@ -2364,7 +2368,7 @@ func (ctxt *Link) buildinfo() {
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)
}

View File

@ -52,10 +52,9 @@ var errUnrecognizedFormat = errors.New("unrecognized file format")
//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
@ -171,37 +170,64 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) {
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
@ -216,8 +242,8 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) {
} 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