From d6b06f025475d62c9d4615d23b463af4395b0c23 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 22 Apr 2019 23:02:37 -0400 Subject: [PATCH] cmd/link: make it easy to find binary versions 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 ". For #31624. Change-Id: I78ea8c52fe1686b5cc5a829ca5f198104d10ebf0 Reviewed-on: https://go-review.googlesource.com/c/go/+/173342 Run-TryBot: Russ Cox TryBot-Result: Gobot Gobot Reviewed-by: Jay Conrod Reviewed-by: Brad Fitzpatrick --- src/cmd/link/internal/ld/data.go | 34 ++++++++++++++++++++++++++++ src/cmd/link/internal/ld/elf.go | 2 ++ src/cmd/link/internal/ld/macho.go | 2 +- src/cmd/link/internal/ld/main.go | 1 + src/cmd/link/internal/sym/symkind.go | 2 ++ 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index b4a76af328..3b551db853 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -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) diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 3995a9423d..5a3098ce85 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -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") diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index 6ebae160b1..f577ed1fc3 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -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) { diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index e0725a1384..f47e35301c 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -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) diff --git a/src/cmd/link/internal/sym/symkind.go b/src/cmd/link/internal/sym/symkind.go index 82e4b9eda4..dbc2750732 100644 --- a/src/cmd/link/internal/sym/symkind.go +++ b/src/cmd/link/internal/sym/symkind.go @@ -81,6 +81,8 @@ const ( SPCLNTAB // Writable sections. + SFirstWritable + SBUILDINFO SELFSECT SMACHO SMACHOGOT -- 2.50.0