diff --git a/Gopkg.lock b/Gopkg.lock index cdbc01a..7e2249f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -19,9 +19,15 @@ packages = ["arm/armasm","ppc64/ppc64asm","x86/x86asm"] revision = "f185940480d2c897e1ca8cf2f1be122e1258341b" +[[projects]] + name = "rsc.io/goversion" + packages = ["version"] + revision = "84cd9d26d7efce780a7aaafd0944961371da741f" + version = "v1.0.0" + [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "c464a3d83956b677421b9fc797a6c77aa6e94a28120ee71d2b19860a5ff39c20" + inputs-digest = "4b494828d559e89fc1fe70b9bfdd0f5c8ad62ee16258a3b2bcf1028c140faa32" solver-name = "gps-cdcl" solver-version = 1 diff --git a/vendor/rsc.io/goversion/LICENSE b/vendor/rsc.io/goversion/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/vendor/rsc.io/goversion/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/rsc.io/goversion/README.md b/vendor/rsc.io/goversion/README.md new file mode 100644 index 0000000..7f9a536 --- /dev/null +++ b/vendor/rsc.io/goversion/README.md @@ -0,0 +1,37 @@ +# rsc.io/goversion + +Goversion scans a directory tree and, for every executable it finds, prints +the Go version used to build that executable. + +Usage: + + goversion [-crypto] [-v] path... + +The list of paths can be individual files or directories; if the latter, +goversion scans all files in the directory tree, not following symlinks. + +Goversion scans inside of tar or gzipped tar archives that it finds (named +`*.tar`, `*.tar.gz`, or `*.tgz`), but not recursively. + +The `-crypto` flag causes goversion to print additional information about the +crypto libraries linked into each executable. + +The `-v` flag causes goversion to print information about every file it +considers. + +## Example + +Scan /usr/bin for Go binaries and print their versions: + + $ goversion /usr/bin + /usr/bin/containerd go1.7.4 + /usr/bin/containerd-shim go1.7.4 + /usr/bin/ctr go1.7.4 + /usr/bin/docker go1.7.4 + /usr/bin/docker-proxy go1.7.4 + /usr/bin/dockerd go1.7.4 + /usr/bin/kbfsfuse go1.8.3 + /usr/bin/kbnm go1.8.3 + /usr/bin/keybase go1.8.3 + /usr/bin/snap go1.7.4 + /usr/bin/snapctl go1.7.4 diff --git a/vendor/rsc.io/goversion/goversion b/vendor/rsc.io/goversion/goversion new file mode 100755 index 0000000..9442024 Binary files /dev/null and b/vendor/rsc.io/goversion/goversion differ diff --git a/vendor/rsc.io/goversion/main.go b/vendor/rsc.io/goversion/main.go new file mode 100644 index 0000000..79184c2 --- /dev/null +++ b/vendor/rsc.io/goversion/main.go @@ -0,0 +1,215 @@ +// Copyright 2017 The Go Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Goversion scans a directory tree and, for every executable it finds, +// prints the Go version used to build that executable. +// +// Usage: +// +// goversion [-crypto] [-v] path... +// +// The list of paths can be individual files or directories; if the latter, +// goversion scans all files in the directory tree, not following symlinks. +// +// Goversion scans inside of tar or gzipped tar archives that it finds +// (named *.tar, *.tar.gz, or *.tgz), but not recursively. +// +// The -crypto flag causes goversion to print additional information +// about the crypto libraries linked into each executable. +// +// The -v flag causes goversion to print information about every +// file it considers. +// +// Example +// +// Scan /usr/bin for Go binaries and print their versions: +// +// $ goversion /usr/bin +// /usr/bin/containerd go1.7.4 +// /usr/bin/containerd-shim go1.7.4 +// /usr/bin/ctr go1.7.4 +// /usr/bin/docker go1.7.4 +// /usr/bin/docker-proxy go1.7.4 +// /usr/bin/dockerd go1.7.4 +// /usr/bin/kbfsfuse go1.8.3 +// /usr/bin/kbnm go1.8.3 +// /usr/bin/keybase go1.8.3 +// /usr/bin/snap go1.7.4 +// /usr/bin/snapctl go1.7.4 +// +package main // import "rsc.io/goversion" + +import ( + "archive/tar" + "compress/gzip" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "rsc.io/goversion/version" +) + +var ( + crypto = flag.Bool("crypto", false, "check kind of crypto library") + verbose = flag.Bool("v", false, "print verbose information") +) + +func usage() { + fmt.Fprintf(os.Stderr, "usage: goversion [-crypto] [-v] path...\n") + flag.PrintDefaults() + os.Exit(2) +} + +func main() { + log.SetFlags(0) + log.SetPrefix("goversion: ") + flag.Usage = usage + flag.Parse() + if flag.NArg() == 0 { + usage() + } + + for _, file := range flag.Args() { + info, err := os.Stat(file) + if err != nil { + log.Print(err) + continue + } + if info.IsDir() { + scandir(file) + } else { + scanfile(file, file, info, true) + } + } +} + +func scandir(dir string) { + filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if info.Mode().IsRegular() { + scanfile(path, path, info, *verbose) + } + return nil + }) +} + +func scanfile(file, diskFile string, info os.FileInfo, mustPrint bool) { + if strings.HasSuffix(file, ".tar") { + if file != diskFile { + fmt.Fprintf(os.Stderr, "%s: not scanning tar recursively\n", file) + return + } + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: scanning tar archive\n", file) + } + scantar(file, info) + return + } + if strings.HasSuffix(file, ".tar.gz") || strings.HasSuffix(file, ".tgz") { + if file != diskFile { + fmt.Fprintf(os.Stderr, "%s: not scanning tgz recursively\n", file) + return + } + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: scanning tgz archive\n", file) + } + scantar(file, info) + return + } + if info.Mode()&os.ModeSymlink != 0 { + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: symlink\n", file) + } + return + } + if file == diskFile && info.Mode()&0111 == 0 { + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: not executable\n", file) + } + return + } + v, err := version.ReadExe(diskFile) + if err != nil { + if mustPrint { + fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) + } + return + } + + buildVersion := v.Release + if *crypto { + switch { + case v.BoringCrypto && v.StandardCrypto: + buildVersion += " (boring AND standard crypto!!!)" + case v.BoringCrypto: + buildVersion += " (boring crypto)" + case v.StandardCrypto: + buildVersion += " (standard crypto)" + } + } + fmt.Printf("%s %s\n", file, buildVersion) +} + +type Version struct { + Release string + BoringCrypto bool + StandardCrypto bool +} + +func scantar(file string, info os.FileInfo) { + f, err := os.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) + return + } + defer f.Close() + var r io.Reader = f + if strings.HasSuffix(file, "z") { + z, err := gzip.NewReader(r) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) + return + } + defer z.Close() + r = z + } + tr := tar.NewReader(r) + for { + hdr, err := tr.Next() + if err != nil { + break + } + if hdr.Typeflag != tar.TypeReg { + if *verbose { + fmt.Fprintf(os.Stderr, "%s/%s: not regular file\n", file, hdr.Name) + } + continue + } + if hdr.Mode&0111 == 0 { + if *verbose { + fmt.Fprintf(os.Stderr, "%s/%s: not executable\n", file, hdr.Name) + } + continue + } + + // executable but not special + tmp, err := ioutil.TempFile("", "goversion-") + if err != nil { + log.Fatal(err) + } + io.Copy(tmp, tr) + tmpName := tmp.Name() + info, err := tmp.Stat() + if err != nil { + log.Fatal(err) + } + tmp.Close() + scanfile(file+"/"+hdr.Name, tmpName, info, *verbose) + os.Remove(tmpName) + } +} diff --git a/vendor/rsc.io/goversion/version/asm.go b/vendor/rsc.io/goversion/version/asm.go new file mode 100644 index 0000000..17ffd96 --- /dev/null +++ b/vendor/rsc.io/goversion/version/asm.go @@ -0,0 +1,294 @@ +// Copyright 2017 The Go Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package version + +import ( + "encoding/binary" + "flag" + "fmt" + "os" +) + +type matcher [][]uint32 + +const ( + pWild uint32 = 0xff00 + pAddr uint32 = 0x10000 + pEnd uint32 = 0x20000 + + opMaybe = 1 + iota + opMust + opDone + opAnchor = 0x100 + opSub8 = 0x200 + opFlags = opAnchor | opSub8 +) + +var amd64Matcher = matcher{ + {opMaybe, + // _rt0_amd64_linux: + // lea 0x8(%rsp), %rsi + // mov (%rsp), %rdi + // lea ADDR(%rip), %rax # main + // jmpq *%rax + 0x48, 0x8d, 0x74, 0x24, 0x08, + 0x48, 0x8b, 0x3c, 0x24, 0x48, + 0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd, + 0xff, 0xe0, + }, + {opMaybe, + // _rt0_amd64_linux: + // lea 0x8(%rsp), %rsi + // mov (%rsp), %rdi + // mov $ADDR, %eax # main + // jmpq *%rax + 0x48, 0x8d, 0x74, 0x24, 0x08, + 0x48, 0x8b, 0x3c, 0x24, + 0xb8, pWild | pAddr, pWild, pWild, pWild, + 0xff, 0xe0, + }, + {opMaybe, + // _start (toward end) + // lea __libc_csu_fini(%rip), %r8 + // lea __libc_csu_init(%rip), %rcx + // lea ADDR(%rip), %rdi # main + // callq *xxx(%rip) + 0x4c, 0x8d, 0x05, pWild, pWild, pWild, pWild, + 0x48, 0x8d, 0x0d, pWild, pWild, pWild, pWild, + 0x48, 0x8d, 0x3d, pWild | pAddr, pWild, pWild, pWild | pEnd, + 0xff, 0x15, + }, + {opMaybe, + // _start (toward end) + // push %rsp (1) + // mov $__libc_csu_fini, %r8 (7) + // mov $__libc_csu_init, %rcx (7) + // mov $ADDR, %rdi # main (7) + // callq *xxx(%rip) + 0x54, + 0x49, 0xc7, 0xc0, pWild, pWild, pWild, pWild, + 0x48, 0xc7, 0xc1, pWild, pWild, pWild, pWild, + 0x48, 0xc7, 0xc7, pAddr | pWild, pWild, pWild, pWild, + }, + {opMaybe | opAnchor, + // main: + // lea ADDR(%rip), %rax # rt0_go + // jmpq *%rax + 0x48, 0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd, + 0xff, 0xe0, + }, + {opMaybe | opAnchor, + // main: + // mov $ADDR, %eax + // jmpq *%rax + 0xb8, pWild | pAddr, pWild, pWild, pWild, + 0xff, 0xe0, + }, + {opMust | opAnchor, + // rt0_go: + // mov %rdi, %rax + // mov %rsi, %rbx + // sub %0x27, %rsp + // and $0xfffffffffffffff0,%rsp + // mov %rax,0x10(%rsp) + // mov %rbx,0x18(%rsp) + 0x48, 0x89, 0xf8, + 0x48, 0x89, 0xf3, + 0x48, 0x83, 0xec, 0x27, + 0x48, 0x83, 0xe4, 0xf0, + 0x48, 0x89, 0x44, 0x24, 0x10, + 0x48, 0x89, 0x5c, 0x24, 0x18, + }, + {opMust, + // later in rt0_go: + // mov %eax, (%rsp) + // mov 0x18(%rsp), %rax + // mov %rax, 0x8(%rsp) + // callq runtime.args + // callq runtime.osinit + // callq runtime.schedinit (ADDR) + // lea mainPC(%rip), %rax + 0x89, 0x04, 0x24, + 0x48, 0x8b, 0x44, 0x24, 0x18, + 0x48, 0x89, 0x44, 0x24, 0x08, + 0xe8, pWild, pWild, pWild, pWild, + 0xe8, pWild, pWild, pWild, pWild, + 0xe8, pWild, pWild, pWild, pWild, + }, + {opMaybe, + // later in rt0_go: + // mov %eax, (%rsp) + // mov 0x18(%rsp), %rax + // mov %rax, 0x8(%rsp) + // callq runtime.args + // callq runtime.osinit + // callq runtime.schedinit (ADDR) + // lea other(%rip), %rdi + 0x89, 0x04, 0x24, + 0x48, 0x8b, 0x44, 0x24, 0x18, + 0x48, 0x89, 0x44, 0x24, 0x08, + 0xe8, pWild, pWild, pWild, pWild, + 0xe8, pWild, pWild, pWild, pWild, + 0xe8, pWild | pAddr, pWild, pWild, pWild | pEnd, + 0x48, 0x8d, 0x05, + }, + {opMaybe, + // later in rt0_go: + // mov %eax, (%rsp) + // mov 0x18(%rsp), %rax + // mov %rax, 0x8(%rsp) + // callq runtime.args + // callq runtime.osinit + // callq runtime.hashinit + // callq runtime.schedinit (ADDR) + // pushq $main.main + 0x89, 0x04, 0x24, + 0x48, 0x8b, 0x44, 0x24, 0x18, + 0x48, 0x89, 0x44, 0x24, 0x08, + 0xe8, pWild, pWild, pWild, pWild, + 0xe8, pWild, pWild, pWild, pWild, + 0xe8, pWild, pWild, pWild, pWild, + 0xe8, pWild | pAddr, pWild, pWild, pWild | pEnd, + 0x68, + }, + {opDone | opSub8, + // schedinit (toward end) + // mov ADDR(%rip), %rax + // test %rax, %rax + // jne + // movq $0x7, ADDR(%rip) + // + 0x48, 0x8b, 0x05, pWild, pWild, pWild, pWild, + 0x48, 0x85, 0xc0, + 0x75, pWild, + 0x48, 0xc7, 0x05, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00 | pEnd, + }, + {opDone | opSub8, + // schedinit (toward end) + // mov ADDR(%rip), %rbx + // cmp $0x0, %rbx + // jne + // lea "unknown"(%rip), %rbx + // mov %rbx, ADDR(%rip) + // movq $7, (ADDR+8)(%rip) + 0x48, 0x8b, 0x1d, pWild, pWild, pWild, pWild, + 0x48, 0x83, 0xfb, 0x00, + 0x75, pWild, + 0x48, 0x8d, 0x1d, pWild, pWild, pWild, pWild, + 0x48, 0x89, 0x1d, pWild, pWild, pWild, pWild, + 0x48, 0xc7, 0x05, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00 | pEnd, + }, + {opDone, + // schedinit (toward end) + // cmpq $0x0, ADDR(%rip) + // jne + // lea "unknown"(%rip), %rax + // mov %rax, ADDR(%rip) + // lea ADDR(%rip), %rax + // movq $7, 8(%rax) + 0x48, 0x83, 0x3d, pWild | pAddr, pWild, pWild, pWild, 0x00, + 0x75, pWild, + 0x48, 0x8d, 0x05, pWild, pWild, pWild, pWild, + 0x48, 0x89, 0x05, pWild, pWild, pWild, pWild, + 0x48, 0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd, + 0x48, 0xc7, 0x40, 0x08, 0x07, 0x00, 0x00, 0x00, + }, + {opDone, + // test %eax, %eax + // jne + // lea "unknown"(RIP), %rax + // mov %rax, ADDR(%rip) + 0x48, 0x85, 0xc0, 0x75, pWild, 0x48, 0x8d, 0x05, pWild, pWild, pWild, pWild, 0x48, 0x89, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd, + }, +} + +var debugMatch = flag.Bool("d", false, "print debug information") + +func (m matcher) match(f exe, addr uint64) (uint64, bool) { + data, err := f.ReadData(addr, 512) + if err != nil { + return 0, false + } +Matchers: + for pc, p := range m { + op := p[0] + p = p[1:] + Search: + for i := 0; i <= len(data)-len(p); i++ { + a := -1 + e := -1 + if i > 0 && op&opAnchor != 0 { + break + } + for j := 0; j < len(p); j++ { + b := byte(p[j]) + m := byte(p[j] >> 8) + if data[i+j]&^m != b { + continue Search + } + if p[j]&pAddr != 0 { + a = j + } + if p[j]&pEnd != 0 { + e = j + 1 + } + } + // matched + if *debugMatch { + fmt.Fprintf(os.Stderr, "match (%d) %#x+%d %x %x\n", pc, addr, i, p, data[i:i+len(p)]) + } + if a != -1 { + val := uint64(int32(binary.LittleEndian.Uint32(data[i+a:]))) + if e == -1 { + addr = val + } else { + addr += uint64(i+e) + val + } + if op&opSub8 != 0 { + addr -= 8 + } + } + if op&^opFlags == opDone { + if *debugMatch { + fmt.Fprintf(os.Stderr, "done %x\n", addr) + } + return addr, true + } + if a != -1 { + // changed addr, so reload + data, err = f.ReadData(addr, 512) + if err != nil { + return 0, false + } + } + continue Matchers + } + // not matched + if *debugMatch { + fmt.Fprintf(os.Stderr, "no match (%d) %#x %x %x\n", pc, addr, p, data[:32]) + } + if op&^opFlags == opMust { + return 0, false + } + } + // ran off end of matcher + return 0, false +} + +func readBuildVersionX86Asm(f exe) (isGo bool, buildVersion string) { + entry := f.Entry() + if entry == 0 { + return + } + addr, ok := amd64Matcher.match(f, entry) + if !ok { + return + } + v, err := readBuildVersion(f, addr, 16) + if err != nil { + return + } + return true, v +} diff --git a/vendor/rsc.io/goversion/version/exe.go b/vendor/rsc.io/goversion/version/exe.go new file mode 100644 index 0000000..834512b --- /dev/null +++ b/vendor/rsc.io/goversion/version/exe.go @@ -0,0 +1,251 @@ +// Copyright 2017 The Go Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package version + +import ( + "bytes" + "debug/elf" + "debug/macho" + "debug/pe" + "encoding/binary" + "fmt" + "io" + "os" +) + +type sym struct { + Name string + Addr uint64 + Size uint64 +} + +type exe interface { + AddrSize() int // bytes + ReadData(addr, size uint64) ([]byte, error) + Symbols() ([]sym, error) + SectionNames() []string + Close() error + ByteOrder() binary.ByteOrder + Entry() uint64 +} + +func openExe(file string) (exe, error) { + f, err := os.Open(file) + if err != nil { + return nil, err + } + data := make([]byte, 16) + if _, err := io.ReadFull(f, data); err != nil { + return nil, err + } + f.Seek(0, 0) + if bytes.HasPrefix(data, []byte("\x7FELF")) { + e, err := elf.NewFile(f) + if err != nil { + f.Close() + return nil, err + } + return &elfExe{f, e}, nil + } + if bytes.HasPrefix(data, []byte("MZ")) { + e, err := pe.NewFile(f) + if err != nil { + f.Close() + return nil, err + } + return &peExe{f, e}, nil + } + if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { + e, err := macho.NewFile(f) + if err != nil { + f.Close() + return nil, err + } + return &machoExe{f, e}, nil + } + return nil, fmt.Errorf("unrecognized executable format") +} + +type elfExe struct { + os *os.File + f *elf.File +} + +func (x *elfExe) AddrSize() int { return 0 } + +func (x *elfExe) ByteOrder() binary.ByteOrder { return x.f.ByteOrder } + +func (x *elfExe) Close() error { + return x.os.Close() +} + +func (x *elfExe) Entry() uint64 { return x.f.Entry } + +func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) { + data := make([]byte, size) + for _, prog := range x.f.Progs { + if prog.Vaddr <= addr && addr+size-1 <= prog.Vaddr+prog.Filesz-1 { + _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)) + if err != nil { + return nil, err + } + return data, nil + } + } + return nil, fmt.Errorf("address not mapped") +} + +func (x *elfExe) Symbols() ([]sym, error) { + syms, err := x.f.Symbols() + if err != nil { + return nil, err + } + var out []sym + for _, s := range syms { + out = append(out, sym{s.Name, s.Value, s.Size}) + } + return out, nil +} + +func (x *elfExe) SectionNames() []string { + var names []string + for _, sect := range x.f.Sections { + names = append(names, sect.Name) + } + return names +} + +type peExe struct { + os *os.File + f *pe.File +} + +func (x *peExe) imageBase() uint64 { + switch oh := x.f.OptionalHeader.(type) { + case *pe.OptionalHeader32: + return uint64(oh.ImageBase) + case *pe.OptionalHeader64: + return oh.ImageBase + } + return 0 +} + +func (x *peExe) AddrSize() int { + if x.f.Machine == pe.IMAGE_FILE_MACHINE_AMD64 { + return 8 + } + return 4 +} + +func (x *peExe) ByteOrder() binary.ByteOrder { return binary.LittleEndian } + +func (x *peExe) Close() error { + return x.os.Close() +} + +func (x *peExe) Entry() uint64 { + switch oh := x.f.OptionalHeader.(type) { + case *pe.OptionalHeader32: + return uint64(oh.ImageBase + oh.AddressOfEntryPoint) + case *pe.OptionalHeader64: + return oh.ImageBase + uint64(oh.AddressOfEntryPoint) + } + return 0 +} + +func (x *peExe) ReadData(addr, size uint64) ([]byte, error) { + addr -= x.imageBase() + data := make([]byte, size) + for _, sect := range x.f.Sections { + if uint64(sect.VirtualAddress) <= addr && addr+size-1 <= uint64(sect.VirtualAddress+sect.Size-1) { + _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) + if err != nil { + return nil, err + } + return data, nil + } + } + return nil, fmt.Errorf("address not mapped") +} + +func (x *peExe) Symbols() ([]sym, error) { + base := x.imageBase() + var out []sym + for _, s := range x.f.Symbols { + if s.SectionNumber <= 0 || int(s.SectionNumber) > len(x.f.Sections) { + continue + } + sect := x.f.Sections[s.SectionNumber-1] + out = append(out, sym{s.Name, uint64(s.Value) + base + uint64(sect.VirtualAddress), 0}) + } + return out, nil +} + +func (x *peExe) SectionNames() []string { + var names []string + for _, sect := range x.f.Sections { + names = append(names, sect.Name) + } + return names +} + +type machoExe struct { + os *os.File + f *macho.File +} + +func (x *machoExe) AddrSize() int { + if x.f.Cpu&0x01000000 != 0 { + return 8 + } + return 4 +} + +func (x *machoExe) ByteOrder() binary.ByteOrder { return x.f.ByteOrder } + +func (x *machoExe) Close() error { + return x.os.Close() +} + +func (x *machoExe) Entry() uint64 { + return 0 +} + +func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) { + data := make([]byte, size) + for _, load := range x.f.Loads { + seg, ok := load.(*macho.Segment) + if !ok { + continue + } + if seg.Addr <= addr && addr+size-1 <= seg.Addr+seg.Filesz-1 { + if seg.Name == "__PAGEZERO" { + continue + } + _, err := seg.ReadAt(data, int64(addr-seg.Addr)) + if err != nil { + return nil, err + } + return data, nil + } + } + return nil, fmt.Errorf("address not mapped") +} + +func (x *machoExe) Symbols() ([]sym, error) { + var out []sym + for _, s := range x.f.Symtab.Syms { + out = append(out, sym{s.Name, s.Value, 0}) + } + return out, nil +} + +func (x *machoExe) SectionNames() []string { + var names []string + for _, sect := range x.f.Sections { + names = append(names, sect.Name) + } + return names +} diff --git a/vendor/rsc.io/goversion/version/read.go b/vendor/rsc.io/goversion/version/read.go new file mode 100644 index 0000000..47c7c9b --- /dev/null +++ b/vendor/rsc.io/goversion/version/read.go @@ -0,0 +1,128 @@ +// Copyright 2017 The Go Authors. All Rights Reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package version reports the Go version used to build program executables. +package version + +import ( + "errors" + "fmt" + "strings" +) + +// Version is the information reported by ReadExe. +type Version struct { + Release string // Go version (runtime.Version in the program) + BoringCrypto bool // program uses BoringCrypto + StandardCrypto bool // program uses standard crypto (replaced by BoringCrypto) +} + +// ReadExe reports information about the Go version used to build +// the program executable named by file. +func ReadExe(file string) (Version, error) { + var v Version + f, err := openExe(file) + if err != nil { + return v, err + } + defer f.Close() + isGo := false + for _, name := range f.SectionNames() { + if name == ".note.go.buildid" { + isGo = true + } + } + syms, symsErr := f.Symbols() + isGccgo := false + for _, sym := range syms { + name := sym.Name + if name == "runtime.main" || name == "main.main" { + isGo = true + } + if strings.HasPrefix(name, "runtime.") && strings.HasSuffix(name, "$descriptor") { + isGccgo = true + } + if name == "runtime.buildVersion" { + isGo = true + release, err := readBuildVersion(f, sym.Addr, sym.Size) + if err != nil { + return v, err + } + v.Release = release + + } + if strings.Contains(name, "_Cfunc__goboringcrypto_") { + v.BoringCrypto = true + } + for _, s := range standardCryptoNames { + if strings.Contains(name, s) { + v.StandardCrypto = true + } + } + } + + if *debugMatch { + v.Release = "" + } + if v.Release == "" { + g, release := readBuildVersionX86Asm(f) + if g { + isGo = true + v.Release = release + } + } + if isGccgo && v.Release == "" { + isGo = true + v.Release = "gccgo (version unknown)" + } + if !isGo && symsErr != nil { + return v, symsErr + } + + if !isGo { + return v, errors.New("not a Go executable") + } + if v.Release == "" { + v.Release = "unknown Go version" + } + return v, nil +} + +var standardCryptoNames = []string{ + "crypto/sha1.(*digest)", + "crypto/sha256.(*digest)", + "crypto/rand.(*devReader)", + "crypto/rsa.encrypt", + "crypto/rsa.decrypt", +} + +func readBuildVersion(f exe, addr, size uint64) (string, error) { + if size == 0 { + size = uint64(f.AddrSize() * 2) + } + if size != 8 && size != 16 { + return "", fmt.Errorf("invalid size for runtime.buildVersion") + } + data, err := f.ReadData(addr, size) + if err != nil { + return "", fmt.Errorf("reading runtime.buildVersion: %v", err) + } + + if size == 8 { + addr = uint64(f.ByteOrder().Uint32(data)) + size = uint64(f.ByteOrder().Uint32(data[4:])) + } else { + addr = f.ByteOrder().Uint64(data) + size = f.ByteOrder().Uint64(data[8:]) + } + if size > 1000 { + return "", fmt.Errorf("implausible string size %d for runtime.buildVersion", size) + } + + data, err = f.ReadData(addr, size) + if err != nil { + return "", fmt.Errorf("reading runtime.buildVersion string data: %v", err) + } + return string(data), nil +}