2015-02-27 22:57:28 -05:00
|
|
|
// Copyright 2009 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.
|
|
|
|
|
|
2015-04-24 20:24:49 -07:00
|
|
|
// go-specific code shared across loaders (5l, 6l, 8l).
|
|
|
|
|
|
2015-02-27 22:57:28 -05:00
|
|
|
package ld
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
2016-04-06 21:45:29 -07:00
|
|
|
"cmd/internal/bio"
|
2017-04-18 12:53:25 -07:00
|
|
|
"cmd/internal/objabi"
|
2015-02-27 22:57:28 -05:00
|
|
|
"fmt"
|
2016-04-08 18:19:10 +02:00
|
|
|
"io"
|
2015-02-27 22:57:28 -05:00
|
|
|
"os"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// go-specific code shared across loaders (5l, 6l, 8l).
|
|
|
|
|
|
|
|
|
|
// replace all "". with pkg.
|
|
|
|
|
func expandpkg(t0 string, pkg string) string {
|
|
|
|
|
return strings.Replace(t0, `"".`, pkg+".", -1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
|
// generate debugging section in binary.
|
|
|
|
|
// once the dust settles, try to move some code to
|
|
|
|
|
// libmach, so that other linkers and ar can share.
|
|
|
|
|
|
2016-08-19 22:40:38 -04:00
|
|
|
func ldpkg(ctxt *Link, f *bio.Reader, pkg string, length int64, filename string, whence int) {
|
2016-08-21 18:34:24 -04:00
|
|
|
if *flagG {
|
2015-02-27 22:57:28 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if int64(int(length)) != length {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
|
2016-08-21 18:34:24 -04:00
|
|
|
if *flagU {
|
2015-04-09 07:37:17 -04:00
|
|
|
errorexit()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-17 15:10:25 -05:00
|
|
|
// In a __.PKGDEF, we only care about the package name.
|
|
|
|
|
// Don't read all the export data.
|
|
|
|
|
if length > 1000 && whence == Pkgdef {
|
|
|
|
|
length = 1000
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 12:35:15 -05:00
|
|
|
bdata := make([]byte, length)
|
2016-04-08 18:19:10 +02:00
|
|
|
if _, err := io.ReadFull(f, bdata); err != nil {
|
2015-02-27 22:57:28 -05:00
|
|
|
fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
|
2016-08-21 18:34:24 -04:00
|
|
|
if *flagU {
|
2015-04-09 07:37:17 -04:00
|
|
|
errorexit()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
2015-03-02 12:35:15 -05:00
|
|
|
data := string(bdata)
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2016-04-26 21:50:59 -04:00
|
|
|
// process header lines
|
|
|
|
|
isSafe := false
|
|
|
|
|
isMain := false
|
|
|
|
|
for data != "" {
|
|
|
|
|
var line string
|
|
|
|
|
if i := strings.Index(data, "\n"); i >= 0 {
|
|
|
|
|
line, data = data[:i], data[i+1:]
|
|
|
|
|
} else {
|
|
|
|
|
line, data = data, ""
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-04-26 21:50:59 -04:00
|
|
|
if line == "safe" {
|
|
|
|
|
isSafe = true
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-04-26 21:50:59 -04:00
|
|
|
if line == "main" {
|
|
|
|
|
isMain = true
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-04-26 21:50:59 -04:00
|
|
|
if line == "" {
|
|
|
|
|
break
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-04-26 21:50:59 -04:00
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2016-04-26 21:50:59 -04:00
|
|
|
if whence == Pkgdef || whence == FileObj {
|
|
|
|
|
if pkg == "main" && !isMain {
|
|
|
|
|
Exitf("%s: not package main", filename)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-08-21 18:34:24 -04:00
|
|
|
if *flagU && whence != ArchiveObj && !isSafe {
|
2016-04-26 21:50:59 -04:00
|
|
|
Exitf("load of unsafe package %s", filename)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// __.PKGDEF has no cgo section - those are in the C compiler-generated object files.
|
|
|
|
|
if whence == Pkgdef {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// look for cgo section
|
2017-10-01 14:39:04 +11:00
|
|
|
p0 := strings.Index(data, "\n$$ // cgo")
|
|
|
|
|
var p1 int
|
2015-02-27 22:57:28 -05:00
|
|
|
if p0 >= 0 {
|
|
|
|
|
p0 += p1
|
|
|
|
|
i := strings.IndexByte(data[p0+1:], '\n')
|
|
|
|
|
if i < 0 {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
|
2016-08-21 18:34:24 -04:00
|
|
|
if *flagU {
|
2015-04-09 07:37:17 -04:00
|
|
|
errorexit()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
p0 += 1 + i
|
|
|
|
|
|
|
|
|
|
p1 = strings.Index(data[p0:], "\n$$")
|
|
|
|
|
if p1 < 0 {
|
|
|
|
|
p1 = strings.Index(data[p0:], "\n!\n")
|
|
|
|
|
}
|
|
|
|
|
if p1 < 0 {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
|
2016-08-21 18:34:24 -04:00
|
|
|
if *flagU {
|
2015-04-09 07:37:17 -04:00
|
|
|
errorexit()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
p1 += p0
|
|
|
|
|
|
2016-08-19 22:40:38 -04:00
|
|
|
loadcgo(ctxt, filename, pkg, data[p0:p1])
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-19 22:40:38 -04:00
|
|
|
func loadcgo(ctxt *Link, file string, pkg string, p string) {
|
2015-02-27 22:57:28 -05:00
|
|
|
var next string
|
|
|
|
|
var q string
|
|
|
|
|
var lib string
|
2016-08-19 11:35:54 -04:00
|
|
|
var s *Symbol
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2015-03-02 12:35:15 -05:00
|
|
|
p0 := ""
|
2015-02-27 22:57:28 -05:00
|
|
|
for ; p != ""; p = next {
|
|
|
|
|
if i := strings.Index(p, "\n"); i >= 0 {
|
|
|
|
|
p, next = p[:i], p[i+1:]
|
|
|
|
|
} else {
|
|
|
|
|
next = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p0 = p // save for error message
|
2017-10-01 14:39:04 +11:00
|
|
|
f := tokenize(p)
|
2015-02-27 22:57:28 -05:00
|
|
|
if len(f) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f[0] == "cgo_import_dynamic" {
|
|
|
|
|
if len(f) < 2 || len(f) > 4 {
|
|
|
|
|
goto err
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-01 14:39:04 +11:00
|
|
|
local := f[1]
|
|
|
|
|
remote := local
|
2015-02-27 22:57:28 -05:00
|
|
|
if len(f) > 2 {
|
|
|
|
|
remote = f[2]
|
|
|
|
|
}
|
|
|
|
|
lib = ""
|
|
|
|
|
if len(f) > 3 {
|
|
|
|
|
lib = f[3]
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-21 18:34:24 -04:00
|
|
|
if *FlagD {
|
2015-02-27 22:57:28 -05:00
|
|
|
fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
|
|
|
|
|
nerrors++
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if local == "_" && remote == "_" {
|
|
|
|
|
// allow #pragma dynimport _ _ "foo.so"
|
|
|
|
|
// to force a link of foo.so.
|
|
|
|
|
havedynamic = 1
|
|
|
|
|
|
2017-04-18 12:53:25 -07:00
|
|
|
if Headtype == objabi.Hdarwin {
|
2017-06-01 16:23:40 +09:00
|
|
|
machoadddynlib(lib)
|
2015-05-12 16:07:05 +12:00
|
|
|
} else {
|
|
|
|
|
dynlib = append(dynlib, lib)
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local = expandpkg(local, pkg)
|
|
|
|
|
q = ""
|
2017-09-21 19:01:27 +02:00
|
|
|
if i := strings.IndexByte(remote, '#'); i >= 0 {
|
2015-02-27 22:57:28 -05:00
|
|
|
remote, q = remote[:i], remote[i+1:]
|
|
|
|
|
}
|
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
|
|
|
s = ctxt.Syms.Lookup(local, 0)
|
2017-04-19 15:15:35 +12:00
|
|
|
if s.Type == 0 || s.Type == SXREF || s.Type == SHOSTOBJ {
|
2015-02-27 22:57:28 -05:00
|
|
|
s.Dynimplib = lib
|
|
|
|
|
s.Extname = remote
|
|
|
|
|
s.Dynimpvers = q
|
2017-04-19 15:15:35 +12:00
|
|
|
if s.Type != SHOSTOBJ {
|
|
|
|
|
s.Type = SDYNIMPORT
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
havedynamic = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f[0] == "cgo_import_static" {
|
|
|
|
|
if len(f) != 2 {
|
|
|
|
|
goto err
|
|
|
|
|
}
|
2017-10-01 14:39:04 +11:00
|
|
|
local := f[1]
|
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
|
|
|
s = ctxt.Syms.Lookup(local, 0)
|
2017-04-19 15:15:35 +12:00
|
|
|
s.Type = SHOSTOBJ
|
2015-02-27 22:57:28 -05:00
|
|
|
s.Size = 0
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" {
|
|
|
|
|
if len(f) < 2 || len(f) > 3 {
|
|
|
|
|
goto err
|
|
|
|
|
}
|
2017-10-01 14:39:04 +11:00
|
|
|
local := f[1]
|
|
|
|
|
var remote string
|
2015-02-27 22:57:28 -05:00
|
|
|
if len(f) > 2 {
|
|
|
|
|
remote = f[2]
|
|
|
|
|
} else {
|
|
|
|
|
remote = local
|
|
|
|
|
}
|
|
|
|
|
local = expandpkg(local, pkg)
|
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
|
|
|
s = ctxt.Syms.Lookup(local, 0)
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2015-04-09 10:44:05 -04:00
|
|
|
switch Buildmode {
|
2016-08-25 21:58:45 -04:00
|
|
|
case BuildmodeCShared, BuildmodeCArchive, BuildmodePlugin:
|
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
|
|
|
if s == ctxt.Syms.Lookup("main", 0) {
|
2015-04-09 10:44:05 -04:00
|
|
|
continue
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// export overrides import, for openbsd/cgo.
|
|
|
|
|
// see issue 4878.
|
|
|
|
|
if s.Dynimplib != "" {
|
|
|
|
|
s.Dynimplib = ""
|
|
|
|
|
s.Extname = ""
|
|
|
|
|
s.Dynimpvers = ""
|
|
|
|
|
s.Type = 0
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-02 07:59:49 -05:00
|
|
|
if !s.Attr.CgoExport() {
|
2015-02-27 22:57:28 -05:00
|
|
|
s.Extname = remote
|
|
|
|
|
dynexp = append(dynexp, s)
|
|
|
|
|
} else if s.Extname != remote {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname, remote)
|
|
|
|
|
nerrors++
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f[0] == "cgo_export_static" {
|
2016-03-02 07:59:49 -05:00
|
|
|
s.Attr |= AttrCgoExportStatic
|
2015-02-27 22:57:28 -05:00
|
|
|
} else {
|
2016-03-02 07:59:49 -05:00
|
|
|
s.Attr |= AttrCgoExportDynamic
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f[0] == "cgo_dynamic_linker" {
|
|
|
|
|
if len(f) != 2 {
|
|
|
|
|
goto err
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-22 22:29:24 -07:00
|
|
|
if *flagInterpreter == "" {
|
|
|
|
|
if interpreter != "" && interpreter != f[1] {
|
|
|
|
|
fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
|
2015-02-27 22:57:28 -05:00
|
|
|
nerrors++
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-22 22:29:24 -07:00
|
|
|
interpreter = f[1]
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f[0] == "cgo_ldflag" {
|
|
|
|
|
if len(f) != 2 {
|
|
|
|
|
goto err
|
|
|
|
|
}
|
|
|
|
|
ldflag = append(ldflag, f[1])
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0)
|
|
|
|
|
nerrors++
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-12 15:59:15 +12:00
|
|
|
var seenlib = make(map[string]bool)
|
2015-05-12 15:40:13 +12:00
|
|
|
|
2016-08-19 22:40:38 -04:00
|
|
|
func adddynlib(ctxt *Link, lib string) {
|
2015-05-12 16:07:05 +12:00
|
|
|
if seenlib[lib] || Linkmode == LinkExternal {
|
2015-05-12 15:40:13 +12:00
|
|
|
return
|
|
|
|
|
}
|
2015-05-12 15:59:15 +12:00
|
|
|
seenlib[lib] = true
|
2015-05-12 15:40:13 +12:00
|
|
|
|
|
|
|
|
if Iself {
|
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
|
|
|
s := ctxt.Syms.Lookup(".dynstr", 0)
|
2015-05-12 15:40:13 +12:00
|
|
|
if s.Size == 0 {
|
2016-09-20 15:31:26 +12:00
|
|
|
Addstring(s, "")
|
2015-05-12 15:40:13 +12:00
|
|
|
}
|
2016-09-20 15:31:26 +12:00
|
|
|
Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
|
2015-05-12 15:40:13 +12:00
|
|
|
} else {
|
2016-09-17 09:39:33 -04:00
|
|
|
Errorf(nil, "adddynlib: unsupported binary format")
|
2015-05-12 15:40:13 +12:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-19 11:35:54 -04:00
|
|
|
func Adddynsym(ctxt *Link, s *Symbol) {
|
2015-05-12 16:07:05 +12:00
|
|
|
if s.Dynid >= 0 || Linkmode == LinkExternal {
|
2015-05-12 15:59:15 +12:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if Iself {
|
2017-08-28 14:57:47 +09:00
|
|
|
elfadddynsym(ctxt, s)
|
2017-04-18 12:53:25 -07:00
|
|
|
} else if Headtype == objabi.Hdarwin {
|
2016-09-17 09:39:33 -04:00
|
|
|
Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname)
|
2017-04-18 12:53:25 -07:00
|
|
|
} else if Headtype == objabi.Hwindows {
|
2015-05-12 15:59:15 +12:00
|
|
|
// already taken care of
|
|
|
|
|
} else {
|
2016-09-17 09:39:33 -04:00
|
|
|
Errorf(s, "adddynsym: unsupported binary format")
|
2015-05-12 15:59:15 +12:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
cmd/link: prune unused methods
Today the linker keeps all methods of reachable types. This is
necessary if a program uses reflect.Value.Call. But while use of
reflection is widespread in Go for encoders and decoders, using
it to call a method is rare.
This CL looks for the use of reflect.Value.Call in a program, and
if it is absent, adopts a (reasonably conservative) method pruning
strategy as part of dead code elimination. Any method that is
directly called is kept, and any method that matches a used
interface's method signature is kept.
Whether or not a method body is kept is determined by the relocation
from its receiver's *rtype to its *rtype. A small change in the
compiler marks these relocations as R_METHOD so they can be easily
collected and manipulated by the linker.
As a bonus, this technique removes the text segment of methods that
have been inlined. Looking at the output of building cmd/objdump with
-ldflags=-v=2 shows that inlined methods like
runtime.(*traceAllocBlockPtr).ptr are removed from the program.
Relatively little work is necessary to do this. Linking two
examples, jujud and cmd/objdump show no more than +2% link time.
Binaries that do not use reflect.Call.Value drop 4 - 20% in size:
addr2line: -793KB (18%)
asm: -346KB (8%)
cgo: -490KB (10%)
compile: -564KB (4%)
dist: -736KB (17%)
fix: -404KB (12%)
link: -328KB (7%)
nm: -827KB (19%)
objdump: -712KB (16%)
pack: -327KB (14%)
yacc: -350KB (10%)
Binaries that do use reflect.Call.Value see a modest size decrease
of 2 - 6% thanks to pruning of unexported methods:
api: -151KB (3%)
cover: -222KB (4%)
doc: -106KB (2.5%)
pprof: -314KB (3%)
trace: -357KB (4%)
vet: -187KB (2.7%)
jujud: -4.4MB (5.8%)
cmd/go: -384KB (3.4%)
The trivial Hello example program goes from 2MB to 1.68MB:
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
Method pruning also helps when building small binaries with
"-ldflags=-s -w". The above program goes from 1.43MB to 1.2MB.
Unfortunately the linker can only tell if reflect.Value.Call has been
statically linked, not if it is dynamically used. And while use is
rare, it is linked into a very common standard library package,
text/template. The result is programs like cmd/go, which don't use
reflect.Value.Call, see limited benefit from this CL. If binary size
is important enough it may be possible to address this in future work.
For #6853.
Change-Id: Iabe90e210e813b08c3f8fd605f841f0458973396
Reviewed-on: https://go-review.googlesource.com/20483
Reviewed-by: Russ Cox <rsc@golang.org>
2016-03-07 23:45:04 -05:00
|
|
|
func fieldtrack(ctxt *Link) {
|
2015-02-27 22:57:28 -05:00
|
|
|
// record field tracking references
|
2015-03-03 22:20:16 -05:00
|
|
|
var buf bytes.Buffer
|
2016-09-20 14:59:39 +12:00
|
|
|
for _, s := range ctxt.Syms.Allsym {
|
2015-02-27 22:57:28 -05:00
|
|
|
if strings.HasPrefix(s.Name, "go.track.") {
|
2016-03-02 07:59:49 -05:00
|
|
|
s.Attr |= AttrSpecial // do not lay out in data segment
|
2017-04-28 12:22:50 +12:00
|
|
|
s.Attr |= AttrNotInSymbolTable
|
2016-03-02 07:59:49 -05:00
|
|
|
if s.Attr.Reachable() {
|
2015-03-03 22:20:16 -05:00
|
|
|
buf.WriteString(s.Name[9:])
|
2016-03-02 15:59:38 -05:00
|
|
|
for p := s.Reachparent; p != nil; p = p.Reachparent {
|
2015-03-03 22:20:16 -05:00
|
|
|
buf.WriteString("\t")
|
|
|
|
|
buf.WriteString(p.Name)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2015-03-03 22:20:16 -05:00
|
|
|
buf.WriteString("\n")
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2017-04-19 15:15:35 +12:00
|
|
|
s.Type = SCONST
|
2015-02-27 22:57:28 -05:00
|
|
|
s.Value = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-21 18:34:24 -04:00
|
|
|
if *flagFieldTrack == "" {
|
2015-02-27 22:57:28 -05:00
|
|
|
return
|
|
|
|
|
}
|
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
|
|
|
s := ctxt.Syms.Lookup(*flagFieldTrack, 0)
|
2016-03-02 07:59:49 -05:00
|
|
|
if !s.Attr.Reachable() {
|
2015-02-27 22:57:28 -05:00
|
|
|
return
|
|
|
|
|
}
|
2016-08-21 18:34:24 -04:00
|
|
|
addstrdata(ctxt, *flagFieldTrack, buf.String())
|
2017-05-10 12:05:20 -07:00
|
|
|
s.Type = SDATA
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-08-19 22:40:38 -04:00
|
|
|
func (ctxt *Link) addexport() {
|
2017-04-18 12:53:25 -07:00
|
|
|
if Headtype == objabi.Hdarwin {
|
2015-02-27 22:57:28 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-12 16:07:05 +12:00
|
|
|
for _, exp := range dynexp {
|
2016-08-19 22:40:38 -04:00
|
|
|
Adddynsym(ctxt, exp)
|
2015-05-12 16:07:05 +12:00
|
|
|
}
|
|
|
|
|
for _, lib := range dynlib {
|
2016-08-19 22:40:38 -04:00
|
|
|
adddynlib(ctxt, lib)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Pkg struct {
|
2015-03-05 17:45:11 -08:00
|
|
|
mark bool
|
|
|
|
|
checked bool
|
|
|
|
|
path string
|
2015-02-27 22:57:28 -05:00
|
|
|
impby []*Pkg
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-09 15:04:45 +10:00
|
|
|
var pkgall []*Pkg
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2015-03-05 17:45:11 -08:00
|
|
|
func (p *Pkg) cycle() *Pkg {
|
|
|
|
|
if p.checked {
|
2015-02-27 22:57:28 -05:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 17:45:11 -08:00
|
|
|
if p.mark {
|
2015-02-27 22:57:28 -05:00
|
|
|
nerrors++
|
|
|
|
|
fmt.Printf("import cycle:\n")
|
2015-03-05 17:45:11 -08:00
|
|
|
fmt.Printf("\t%s\n", p.path)
|
2015-02-27 22:57:28 -05:00
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 17:45:11 -08:00
|
|
|
p.mark = true
|
|
|
|
|
for _, q := range p.impby {
|
|
|
|
|
if bad := q.cycle(); bad != nil {
|
|
|
|
|
p.mark = false
|
|
|
|
|
p.checked = true
|
|
|
|
|
fmt.Printf("\timports %s\n", p.path)
|
2015-02-27 22:57:28 -05:00
|
|
|
if bad == p {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return bad
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 17:45:11 -08:00
|
|
|
p.checked = true
|
|
|
|
|
p.mark = false
|
2015-02-27 22:57:28 -05:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func importcycles() {
|
2015-03-05 17:45:11 -08:00
|
|
|
for _, p := range pkgall {
|
|
|
|
|
p.cycle()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|