diff options
author | Russ Cox <rsc@golang.org> | 2021-12-06 13:38:04 -0500 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2021-12-07 20:14:56 +0000 |
commit | b37a5391f9e452aa779205add12bd89f44e3fcf0 (patch) | |
tree | 38489628da587462da8fc40a7e39173e9aaca7e1 /src/debug | |
parent | 4300f105147dc0da9d1034704ad1cd24bedde5da (diff) | |
download | go-b37a5391f9e452aa779205add12bd89f44e3fcf0.tar.gz go-b37a5391f9e452aa779205add12bd89f44e3fcf0.zip |
cmd/link, cmd/go: make version info easier to extract
Reading the version information to date has required evaluating
two pointers to strings (which themselves contain pointers to data),
which means applying relocations, which can be very system-dependent.
To simplify the lookup, inline the string data into the build info blob.
This makes go version work on binaries built with external linking
on darwin/arm64.
Also test that at least the very basics work on a trivial binary,
even in short mode.
Change-Id: I463088c19e837ae0ce57e1278c7b72e74a80b2c4
Reviewed-on: https://go-review.googlesource.com/c/go/+/369977
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/debug')
-rw-r--r-- | src/debug/buildinfo/buildinfo.go | 54 |
1 files changed, 38 insertions, 16 deletions
diff --git a/src/debug/buildinfo/buildinfo.go b/src/debug/buildinfo/buildinfo.go index f84429a342..2c0200e8dc 100644 --- a/src/debug/buildinfo/buildinfo.go +++ b/src/debug/buildinfo/buildinfo.go @@ -146,12 +146,18 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) { } const ( buildInfoAlign = 16 - buildinfoSize = 32 + buildInfoSize = 32 ) - for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[buildInfoAlign:] { - if len(data) < 32 { + for { + i := bytes.Index(data, buildInfoMagic) + if i < 0 || len(data)-i < buildInfoSize { return "", "", errNotGoExe } + if i%buildInfoAlign == 0 && len(data)-i >= buildInfoSize { + data = data[i:] + break + } + data = data[(i+buildInfoAlign-1)&^buildInfoAlign:] } // Decode the blob. @@ -161,25 +167,33 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) { // 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]) - bigEndian := data[15] != 0 - var bo binary.ByteOrder - if bigEndian { - bo = binary.BigEndian - } else { - bo = binary.LittleEndian - } - var readPtr func([]byte) uint64 - if ptrSize == 4 { - readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) } + if data[15]&2 != 0 { + vers, data = decodeString(data[32:]) + mod, data = decodeString(data) } else { - readPtr = bo.Uint64 + bigEndian := data[15] != 0 + var bo binary.ByteOrder + if bigEndian { + bo = binary.BigEndian + } else { + bo = binary.LittleEndian + } + var readPtr func([]byte) uint64 + if ptrSize == 4 { + readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) } + } else { + readPtr = bo.Uint64 + } + vers = readString(x, ptrSize, readPtr, readPtr(data[16:])) + mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:])) } - vers = readString(x, ptrSize, readPtr, readPtr(data[16:])) if vers == "" { return "", "", errNotGoExe } - mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:])) if len(mod) >= 33 && mod[len(mod)-17] == '\n' { // Strip module framing: sentinel strings delimiting the module info. // These are cmd/go/internal/modload.infoStart and infoEnd. @@ -191,6 +205,14 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) { return vers, mod, nil } +func decodeString(data []byte) (s string, rest []byte) { + u, n := binary.Uvarint(data) + if n <= 0 || u >= uint64(len(data)-n) { + return "", nil + } + return string(data[n : uint64(n)+u]), data[uint64(n)+u:] +} + // readString returns the string at address addr in the executable x. func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string { hdr, err := x.ReadData(addr, uint64(2*ptrSize)) |