]> Cypherpunks repositories - gostls13.git/commitdiff
debug/buildinfo: improve format documentation
authorMichael Pratt <mpratt@google.com>
Fri, 26 Jul 2024 14:02:55 +0000 (10:02 -0400)
committerGopher Robot <gobot@golang.org>
Mon, 29 Jul 2024 15:32:53 +0000 (15:32 +0000)
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>

src/cmd/link/internal/ld/data.go
src/debug/buildinfo/buildinfo.go

index 0f1289cccc8404084c9aa93d1949620e7d2809cc..92a8656c355d1445507a1a1cfeddf7ef935ebb10 100644 (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)
        }
index 1dd70a9f33a41ce198813437905a6eb3eb64e110..8338f03fa580de81f4f87895883a7cbbb127c746 100644 (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