1
0
mirror of https://github.com/golang/go synced 2024-11-22 10:24:41 -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 := ldr.CreateSymForUpdate("go:buildinfo", 0)
s.SetType(sym.SBUILDINFO) s.SetType(sym.SBUILDINFO)
s.SetAlign(16) s.SetAlign(16)
// The \xff is invalid UTF-8, meant to make it less likely // The \xff is invalid UTF-8, meant to make it less likely
// to find one of these accidentally. // 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) data := make([]byte, 32)
copy(data, prefix) copy(data, prefix)
data[len(prefix)] = byte(ctxt.Arch.PtrSize) 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[len(prefix)+1] |= 2 // signals new pointer-free format
data = appendString(data, strdata["runtime.buildVersion"]) data = appendString(data, strdata["runtime.buildVersion"])
data = appendString(data, strdata["runtime.modinfo"]) 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 { for len(data)%16 != 0 {
data = append(data, 0) data = append(data, 0)
} }

View File

@ -52,10 +52,9 @@ var errUnrecognizedFormat = errors.New("unrecognized file format")
//go:linkname errNotGoExe //go:linkname errNotGoExe
var errNotGoExe = errors.New("not a Go executable") var errNotGoExe = errors.New("not a Go executable")
// The build info blob left by the linker is identified by // The build info blob left by the linker is identified by a 32-byte header,
// a 16-byte header, consisting of buildInfoMagic (14 bytes), // consisting of buildInfoMagic (14 bytes), followed by version-dependent
// the binary's pointer size (1 byte), // fields.
// and whether the binary is big endian (1 byte).
var buildInfoMagic = []byte("\xff Go buildinf:") var buildInfoMagic = []byte("\xff Go buildinf:")
// ReadFile returns build information embedded in a Go binary // 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 return "", "", err
} }
const ( const (
buildInfoAlign = 16 buildInfoAlign = 16
buildInfoSize = 32 buildInfoHeaderSize = 32
ptrSizeOffset = 14
flagsOffset = 15
versPtrOffset = 16
flagsEndianMask = 0x1
flagsEndianLittle = 0x0
flagsEndianBig = 0x1
flagsVersionMask = 0x2
flagsVersionPtr = 0x0
flagsVersionInl = 0x2
) )
for { for {
i := bytes.Index(data, buildInfoMagic) i := bytes.Index(data, buildInfoMagic)
if i < 0 || len(data)-i < buildInfoSize { if i < 0 || len(data)-i < buildInfoHeaderSize {
return "", "", errNotGoExe return "", "", errNotGoExe
} }
if i%buildInfoAlign == 0 && len(data)-i >= buildInfoSize { if i%buildInfoAlign == 0 && len(data)-i >= buildInfoHeaderSize {
data = data[i:] data = data[i:]
break break
} }
data = data[(i+buildInfoAlign-1)&^(buildInfoAlign-1):] data = data[(i+buildInfoAlign-1)&^(buildInfoAlign-1):]
} }
// Decode the blob. // Decode the blob. The blob is a 32-byte header, optionally followed
// The first 14 bytes are buildInfoMagic. // by 2 varint-prefixed string contents.
// The next two bytes indicate pointer size in bytes (4 or 8) and endianness //
// (0 for little, 1 for big). // type buildInfoHeader struct {
// Two virtual addresses to Go strings follow that: runtime.buildVersion, // magic [14]byte
// and runtime.modinfo. // ptrSize uint8 // used if flagsVersionPtr
// On 32-bit platforms, the last 8 bytes are unused. // flags uint8
// If the endianness has the 2 bit set, then the pointers are zero // versPtr targetUintptr // used if flagsVersionPtr
// and the 32-byte header is followed by varint-prefixed string data // modPtr targetUintptr // used if flagsVersionPtr
// for the two string values we care about. // }
ptrSize := int(data[14]) //
if data[15]&2 != 0 { // The version bit of the flags field determines the details of the format.
vers, data = decodeString(data[32:]) //
// 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) mod, data = decodeString(data)
} else { } else {
bigEndian := data[15] != 0 // flagsVersionPtr (<1.18)
ptrSize := int(data[ptrSizeOffset])
bigEndian := flags&flagsEndianMask == flagsEndianBig
var bo binary.ByteOrder var bo binary.ByteOrder
if bigEndian { if bigEndian {
bo = binary.BigEndian bo = binary.BigEndian
@ -216,8 +242,8 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) {
} else { } else {
return "", "", errNotGoExe return "", "", errNotGoExe
} }
vers = readString(x, ptrSize, readPtr, readPtr(data[16:])) vers = readString(x, ptrSize, readPtr, readPtr(data[versPtrOffset:]))
mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:])) mod = readString(x, ptrSize, readPtr, readPtr(data[versPtrOffset+ptrSize:]))
} }
if vers == "" { if vers == "" {
return "", "", errNotGoExe return "", "", errNotGoExe