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:
parent
705f9848ef
commit
d531e344ae
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user