Skip to content

Commit

Permalink
cmd/link: make it easy to find binary versions
Browse files Browse the repository at this point in the history
It is useful to be able to dig the Go version out of a binary,
even a stripped binary. rsc.io/goversion does this for x86
binaries by disassembling the binary to find runtime/proc.go's
startup-time reference to runtime.buildVersion's address.
That approach is quite fragile: the implementation doesn't
work for non-x86 and must be updated as the generated
code changes.

rsc.io/goversion finds the module version string by looking
for random 16-byte framing around the actual string.
This is less fragile but fairly kludgy and requires scanning
the entire data segment.

cmd/buildid finds the build ID by looking for an ELF note
or else falling back to scanning the beginning of the text
segment for a magic string. This has proved quite reliable
and doesn't require scanning much of the binary.

This CL makes it possible to find the Go and module versions
using a scan more like the build ID scan: a symbol early in
the writable data segment starts with a magic number and
then has pointers to the two string variables.

Setting up for "go version <binary>".

For #31624.

Change-Id: I78ea8c52fe1686b5cc5a829ca5f198104d10ebf0
Reviewed-on: https://go-review.googlesource.com/c/go/+/173342
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information
rsc committed Apr 26, 2019
1 parent a62887a commit d6b06f0
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 1 deletion.
34 changes: 34 additions & 0 deletions src/cmd/link/internal/ld/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,7 @@ func (ctxt *Link) dodata() {

// Writable data sections that do not need any specialized handling.
writable := []sym.SymKind{
sym.SBUILDINFO,
sym.SELFSECT,
sym.SMACHO,
sym.SMACHOGOT,
Expand Down Expand Up @@ -1963,6 +1964,39 @@ func (ctxt *Link) textbuildid() {
ctxt.Textp[0] = s
}

func (ctxt *Link) buildinfo() {
if ctxt.linkShared || ctxt.BuildMode == BuildModePlugin {
// -linkshared and -buildmode=plugin get confused
// about the relocations in go.buildinfo
// pointing at the other data sections.
// The version information is only available in executables.
return
}

s := ctxt.Syms.Lookup(".go.buildinfo", 0)
s.Attr |= sym.AttrReachable
s.Type = sym.SBUILDINFO
s.Align = 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
data := make([]byte, 32)
copy(data, prefix)
data[len(prefix)] = byte(ctxt.Arch.PtrSize)
data[len(prefix)+1] = 0
if ctxt.Arch.ByteOrder == binary.BigEndian {
data[len(prefix)+1] = 1
}
s.P = data
s.Size = int64(len(s.P))
s1 := ctxt.Syms.Lookup("runtime.buildVersion", 0)
s2 := ctxt.Syms.Lookup("runtime.modinfo", 0)
s.R = []sym.Reloc{
{Off: 16, Siz: uint8(ctxt.Arch.PtrSize), Type: objabi.R_ADDR, Sym: s1},
{Off: 16 + int32(ctxt.Arch.PtrSize), Siz: uint8(ctxt.Arch.PtrSize), Type: objabi.R_ADDR, Sym: s2},
}
}

// assign addresses to text
func (ctxt *Link) textaddress() {
addsection(ctxt.Arch, &Segtext, ".text", 05)
Expand Down
2 changes: 2 additions & 0 deletions src/cmd/link/internal/ld/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,7 @@ func (ctxt *Link) doelf() {
Addstring(shstrtab, ".data")
Addstring(shstrtab, ".bss")
Addstring(shstrtab, ".noptrbss")
Addstring(shstrtab, ".go.buildinfo")

// generate .tbss section for dynamic internal linker or external
// linking, so that various binutils could correctly calculate
Expand Down Expand Up @@ -1485,6 +1486,7 @@ func (ctxt *Link) doelf() {
if ctxt.UseRelro() {
Addstring(shstrtab, elfRelType+".data.rel.ro")
}
Addstring(shstrtab, elfRelType+".go.buildinfo")

// add a .note.GNU-stack section to mark the stack as non-executable
Addstring(shstrtab, ".note.GNU-stack")
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/link/internal/ld/macho.go
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ func machoShouldExport(ctxt *Link, s *sym.Symbol) bool {
if strings.HasPrefix(s.Name, "go.link.pkghash") {
return true
}
return s.Type >= sym.SELFSECT // only writable sections
return s.Type >= sym.SFirstWritable // only writable sections
}

func machosymtab(ctxt *Link) {
Expand Down
1 change: 1 addition & 0 deletions src/cmd/link/internal/ld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ func Main(arch *sys.Arch, theArch Arch) {
ctxt.findfunctab()
ctxt.typelink()
ctxt.symtab()
ctxt.buildinfo()
ctxt.dodata()
order := ctxt.address()
dwarfcompress(ctxt)
Expand Down
2 changes: 2 additions & 0 deletions src/cmd/link/internal/sym/symkind.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ const (
SPCLNTAB

// Writable sections.
SFirstWritable
SBUILDINFO
SELFSECT
SMACHO
SMACHOGOT
Expand Down

0 comments on commit d6b06f0

Please sign in to comment.