]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: make it easy to find binary versions
authorRuss Cox <rsc@golang.org>
Tue, 23 Apr 2019 03:02:37 +0000 (23:02 -0400)
committerRuss Cox <rsc@golang.org>
Fri, 26 Apr 2019 13:51:52 +0000 (13:51 +0000)
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>
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/elf.go
src/cmd/link/internal/ld/macho.go
src/cmd/link/internal/ld/main.go
src/cmd/link/internal/sym/symkind.go

index b4a76af3284a03b947b17eb1ff55855f84365435..3b551db85305f9bb14a63bf1a7ca9174848558dc 100644 (file)
@@ -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,
@@ -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)
index 3995a9423d3cce811994b565dc28718e33b2e7eb..5a3098ce854f9275cb106ec96cc71874c667d6fc 100644 (file)
@@ -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
@@ -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")
index 6ebae160b1378ef45d2557947cb8daf155381b33..f577ed1fc3ca8b0d0aad8d6fa9658c0cb2bfc4dc 100644 (file)
@@ -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) {
index e0725a13841079aa5e5e44a90bc4546b190364f5..f47e35301c68558a2a4638b3c175e8e8ce770cea 100644 (file)
@@ -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)
index 82e4b9eda48c443a9894bf7bef527bda734f9c26..dbc275073267b1d3620cc19a4144a413142feb67 100644 (file)
@@ -81,6 +81,8 @@ const (
        SPCLNTAB
 
        // Writable sections.
+       SFirstWritable
+       SBUILDINFO
        SELFSECT
        SMACHO
        SMACHOGOT