go/src/cmd/link/internal/ld/go.go

416 lines
7.9 KiB
Go
Raw Normal View History

// 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.
// go-specific code shared across loaders (5l, 6l, 8l).
package ld
import (
"bytes"
"cmd/internal/bio"
"cmd/internal/objabi"
"fmt"
"io"
"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.
func ldpkg(ctxt *Link, f *bio.Reader, pkg string, length int64, filename string, whence int) {
if *flagG {
return
}
if int64(int(length)) != length {
fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
if *flagU {
errorexit()
}
return
}
// In a __.PKGDEF, we only care about the package name.
// Don't read all the export data.
if length > 1000 && whence == Pkgdef {
length = 1000
}
bdata := make([]byte, length)
if _, err := io.ReadFull(f, bdata); err != nil {
fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
if *flagU {
errorexit()
}
return
}
data := string(bdata)
cmd/compile: add -linkobj flag to allow writing object file in two parts This flag is experimental and the semantics may change even after Go 1.7 is released. There are no changes to code not using the flag. The first part is for reading by future compiles. The second part is for reading by the final link step. Splitting the file this way allows distributed build systems to ship the compile-input part only to compile steps and the linker-input part only to linker steps. The first part is basically just the export data, and the second part is basically everything else. The overall files still have the same broad structure, so that existing tools will work with both halves. It's just that various pieces are empty in the two halves. This also copies the two bits of data the linker needed from export data into the object header proper, so that the linker doesn't need any export data at all. That eliminates a TODO that was left for switching to the binary export data. (Now the linker doesn't need to know about the switch.) The default is still to write out a combined output file. Nothing changes unless you pass -linkobj to the compiler. There is no support in the go command for -linkobj, since the go command doesn't copy objects around. The expectation is that other build systems (like bazel, say) might take advantage of this. The header adjustment and the option for the split output was intended as part of the zip archives, but the zip archives have been cut from Go 1.7. Doing this to the current archives both unblocks one step in the switch to binary export data and enables alternate build systems to experiment with the new flag using the Go 1.7 release. Change-Id: I8b6eab25b8a22b0a266ba0ac6d31e594f3d117f3 Reviewed-on: https://go-review.googlesource.com/22500 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
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, ""
}
cmd/compile: add -linkobj flag to allow writing object file in two parts This flag is experimental and the semantics may change even after Go 1.7 is released. There are no changes to code not using the flag. The first part is for reading by future compiles. The second part is for reading by the final link step. Splitting the file this way allows distributed build systems to ship the compile-input part only to compile steps and the linker-input part only to linker steps. The first part is basically just the export data, and the second part is basically everything else. The overall files still have the same broad structure, so that existing tools will work with both halves. It's just that various pieces are empty in the two halves. This also copies the two bits of data the linker needed from export data into the object header proper, so that the linker doesn't need any export data at all. That eliminates a TODO that was left for switching to the binary export data. (Now the linker doesn't need to know about the switch.) The default is still to write out a combined output file. Nothing changes unless you pass -linkobj to the compiler. There is no support in the go command for -linkobj, since the go command doesn't copy objects around. The expectation is that other build systems (like bazel, say) might take advantage of this. The header adjustment and the option for the split output was intended as part of the zip archives, but the zip archives have been cut from Go 1.7. Doing this to the current archives both unblocks one step in the switch to binary export data and enables alternate build systems to experiment with the new flag using the Go 1.7 release. Change-Id: I8b6eab25b8a22b0a266ba0ac6d31e594f3d117f3 Reviewed-on: https://go-review.googlesource.com/22500 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
2016-04-26 21:50:59 -04:00
if line == "safe" {
isSafe = true
}
cmd/compile: add -linkobj flag to allow writing object file in two parts This flag is experimental and the semantics may change even after Go 1.7 is released. There are no changes to code not using the flag. The first part is for reading by future compiles. The second part is for reading by the final link step. Splitting the file this way allows distributed build systems to ship the compile-input part only to compile steps and the linker-input part only to linker steps. The first part is basically just the export data, and the second part is basically everything else. The overall files still have the same broad structure, so that existing tools will work with both halves. It's just that various pieces are empty in the two halves. This also copies the two bits of data the linker needed from export data into the object header proper, so that the linker doesn't need any export data at all. That eliminates a TODO that was left for switching to the binary export data. (Now the linker doesn't need to know about the switch.) The default is still to write out a combined output file. Nothing changes unless you pass -linkobj to the compiler. There is no support in the go command for -linkobj, since the go command doesn't copy objects around. The expectation is that other build systems (like bazel, say) might take advantage of this. The header adjustment and the option for the split output was intended as part of the zip archives, but the zip archives have been cut from Go 1.7. Doing this to the current archives both unblocks one step in the switch to binary export data and enables alternate build systems to experiment with the new flag using the Go 1.7 release. Change-Id: I8b6eab25b8a22b0a266ba0ac6d31e594f3d117f3 Reviewed-on: https://go-review.googlesource.com/22500 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
2016-04-26 21:50:59 -04:00
if line == "main" {
isMain = true
}
cmd/compile: add -linkobj flag to allow writing object file in two parts This flag is experimental and the semantics may change even after Go 1.7 is released. There are no changes to code not using the flag. The first part is for reading by future compiles. The second part is for reading by the final link step. Splitting the file this way allows distributed build systems to ship the compile-input part only to compile steps and the linker-input part only to linker steps. The first part is basically just the export data, and the second part is basically everything else. The overall files still have the same broad structure, so that existing tools will work with both halves. It's just that various pieces are empty in the two halves. This also copies the two bits of data the linker needed from export data into the object header proper, so that the linker doesn't need any export data at all. That eliminates a TODO that was left for switching to the binary export data. (Now the linker doesn't need to know about the switch.) The default is still to write out a combined output file. Nothing changes unless you pass -linkobj to the compiler. There is no support in the go command for -linkobj, since the go command doesn't copy objects around. The expectation is that other build systems (like bazel, say) might take advantage of this. The header adjustment and the option for the split output was intended as part of the zip archives, but the zip archives have been cut from Go 1.7. Doing this to the current archives both unblocks one step in the switch to binary export data and enables alternate build systems to experiment with the new flag using the Go 1.7 release. Change-Id: I8b6eab25b8a22b0a266ba0ac6d31e594f3d117f3 Reviewed-on: https://go-review.googlesource.com/22500 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
2016-04-26 21:50:59 -04:00
if line == "" {
break
}
cmd/compile: add -linkobj flag to allow writing object file in two parts This flag is experimental and the semantics may change even after Go 1.7 is released. There are no changes to code not using the flag. The first part is for reading by future compiles. The second part is for reading by the final link step. Splitting the file this way allows distributed build systems to ship the compile-input part only to compile steps and the linker-input part only to linker steps. The first part is basically just the export data, and the second part is basically everything else. The overall files still have the same broad structure, so that existing tools will work with both halves. It's just that various pieces are empty in the two halves. This also copies the two bits of data the linker needed from export data into the object header proper, so that the linker doesn't need any export data at all. That eliminates a TODO that was left for switching to the binary export data. (Now the linker doesn't need to know about the switch.) The default is still to write out a combined output file. Nothing changes unless you pass -linkobj to the compiler. There is no support in the go command for -linkobj, since the go command doesn't copy objects around. The expectation is that other build systems (like bazel, say) might take advantage of this. The header adjustment and the option for the split output was intended as part of the zip archives, but the zip archives have been cut from Go 1.7. Doing this to the current archives both unblocks one step in the switch to binary export data and enables alternate build systems to experiment with the new flag using the Go 1.7 release. Change-Id: I8b6eab25b8a22b0a266ba0ac6d31e594f3d117f3 Reviewed-on: https://go-review.googlesource.com/22500 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
2016-04-26 21:50:59 -04:00
}
cmd/compile: add -linkobj flag to allow writing object file in two parts This flag is experimental and the semantics may change even after Go 1.7 is released. There are no changes to code not using the flag. The first part is for reading by future compiles. The second part is for reading by the final link step. Splitting the file this way allows distributed build systems to ship the compile-input part only to compile steps and the linker-input part only to linker steps. The first part is basically just the export data, and the second part is basically everything else. The overall files still have the same broad structure, so that existing tools will work with both halves. It's just that various pieces are empty in the two halves. This also copies the two bits of data the linker needed from export data into the object header proper, so that the linker doesn't need any export data at all. That eliminates a TODO that was left for switching to the binary export data. (Now the linker doesn't need to know about the switch.) The default is still to write out a combined output file. Nothing changes unless you pass -linkobj to the compiler. There is no support in the go command for -linkobj, since the go command doesn't copy objects around. The expectation is that other build systems (like bazel, say) might take advantage of this. The header adjustment and the option for the split output was intended as part of the zip archives, but the zip archives have been cut from Go 1.7. Doing this to the current archives both unblocks one step in the switch to binary export data and enables alternate build systems to experiment with the new flag using the Go 1.7 release. Change-Id: I8b6eab25b8a22b0a266ba0ac6d31e594f3d117f3 Reviewed-on: https://go-review.googlesource.com/22500 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
2016-04-26 21:50:59 -04:00
if whence == Pkgdef || whence == FileObj {
if pkg == "main" && !isMain {
Exitf("%s: not package main", filename)
}
if *flagU && whence != ArchiveObj && !isSafe {
cmd/compile: add -linkobj flag to allow writing object file in two parts This flag is experimental and the semantics may change even after Go 1.7 is released. There are no changes to code not using the flag. The first part is for reading by future compiles. The second part is for reading by the final link step. Splitting the file this way allows distributed build systems to ship the compile-input part only to compile steps and the linker-input part only to linker steps. The first part is basically just the export data, and the second part is basically everything else. The overall files still have the same broad structure, so that existing tools will work with both halves. It's just that various pieces are empty in the two halves. This also copies the two bits of data the linker needed from export data into the object header proper, so that the linker doesn't need any export data at all. That eliminates a TODO that was left for switching to the binary export data. (Now the linker doesn't need to know about the switch.) The default is still to write out a combined output file. Nothing changes unless you pass -linkobj to the compiler. There is no support in the go command for -linkobj, since the go command doesn't copy objects around. The expectation is that other build systems (like bazel, say) might take advantage of this. The header adjustment and the option for the split output was intended as part of the zip archives, but the zip archives have been cut from Go 1.7. Doing this to the current archives both unblocks one step in the switch to binary export data and enables alternate build systems to experiment with the new flag using the Go 1.7 release. Change-Id: I8b6eab25b8a22b0a266ba0ac6d31e594f3d117f3 Reviewed-on: https://go-review.googlesource.com/22500 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
2016-04-26 21:50:59 -04:00
Exitf("load of unsafe package %s", filename)
}
}
// __.PKGDEF has no cgo section - those are in the C compiler-generated object files.
if whence == Pkgdef {
return
}
// look for cgo section
p0 := strings.Index(data, "\n$$ // cgo")
var p1 int
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)
if *flagU {
errorexit()
}
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)
if *flagU {
errorexit()
}
return
}
p1 += p0
loadcgo(ctxt, filename, pkg, data[p0:p1])
}
}
func loadcgo(ctxt *Link, file string, pkg string, p string) {
var next string
var q string
var lib string
var s *Symbol
p0 := ""
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
f := tokenize(p)
if len(f) == 0 {
continue
}
if f[0] == "cgo_import_dynamic" {
if len(f) < 2 || len(f) > 4 {
goto err
}
local := f[1]
remote := local
if len(f) > 2 {
remote = f[2]
}
lib = ""
if len(f) > 3 {
lib = f[3]
}
if *FlagD {
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
if Headtype == objabi.Hdarwin {
machoadddynlib(lib)
} else {
dynlib = append(dynlib, lib)
}
continue
}
local = expandpkg(local, pkg)
q = ""
if i := strings.IndexByte(remote, '#'); i >= 0 {
remote, q = remote[:i], remote[i+1:]
}
s = ctxt.Syms.Lookup(local, 0)
if s.Type == 0 || s.Type == SXREF || s.Type == SHOSTOBJ {
s.Dynimplib = lib
s.Extname = remote
s.Dynimpvers = q
if s.Type != SHOSTOBJ {
s.Type = SDYNIMPORT
}
havedynamic = 1
}
continue
}
if f[0] == "cgo_import_static" {
if len(f) != 2 {
goto err
}
local := f[1]
s = ctxt.Syms.Lookup(local, 0)
s.Type = SHOSTOBJ
s.Size = 0
continue
}
if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" {
if len(f) < 2 || len(f) > 3 {
goto err
}
local := f[1]
var remote string
if len(f) > 2 {
remote = f[2]
} else {
remote = local
}
local = expandpkg(local, pkg)
s = ctxt.Syms.Lookup(local, 0)
switch Buildmode {
case BuildmodeCShared, BuildmodeCArchive, BuildmodePlugin:
if s == ctxt.Syms.Lookup("main", 0) {
continue
}
}
// export overrides import, for openbsd/cgo.
// see issue 4878.
if s.Dynimplib != "" {
s.Dynimplib = ""
s.Extname = ""
s.Dynimpvers = ""
s.Type = 0
}
if !s.Attr.CgoExport() {
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" {
s.Attr |= AttrCgoExportStatic
} else {
s.Attr |= AttrCgoExportDynamic
}
continue
}
if f[0] == "cgo_dynamic_linker" {
if len(f) != 2 {
goto err
}
if *flagInterpreter == "" {
if interpreter != "" && interpreter != f[1] {
fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
nerrors++
return
}
interpreter = f[1]
}
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++
}
var seenlib = make(map[string]bool)
func adddynlib(ctxt *Link, lib string) {
if seenlib[lib] || Linkmode == LinkExternal {
return
}
seenlib[lib] = true
if Iself {
s := ctxt.Syms.Lookup(".dynstr", 0)
if s.Size == 0 {
Addstring(s, "")
}
Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
} else {
Errorf(nil, "adddynlib: unsupported binary format")
}
}
func Adddynsym(ctxt *Link, s *Symbol) {
if s.Dynid >= 0 || Linkmode == LinkExternal {
return
}
if Iself {
elfadddynsym(ctxt, s)
} else if Headtype == objabi.Hdarwin {
Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname)
} else if Headtype == objabi.Hwindows {
// already taken care of
} else {
Errorf(s, "adddynsym: unsupported binary format")
}
}
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) {
// record field tracking references
var buf bytes.Buffer
for _, s := range ctxt.Syms.Allsym {
if strings.HasPrefix(s.Name, "go.track.") {
s.Attr |= AttrSpecial // do not lay out in data segment
s.Attr |= AttrNotInSymbolTable
if s.Attr.Reachable() {
buf.WriteString(s.Name[9:])
for p := s.Reachparent; p != nil; p = p.Reachparent {
buf.WriteString("\t")
buf.WriteString(p.Name)
}
buf.WriteString("\n")
}
s.Type = SCONST
s.Value = 0
}
}
if *flagFieldTrack == "" {
return
}
s := ctxt.Syms.Lookup(*flagFieldTrack, 0)
if !s.Attr.Reachable() {
return
}
addstrdata(ctxt, *flagFieldTrack, buf.String())
s.Type = SDATA
}
func (ctxt *Link) addexport() {
if Headtype == objabi.Hdarwin {
return
}
for _, exp := range dynexp {
Adddynsym(ctxt, exp)
}
for _, lib := range dynlib {
adddynlib(ctxt, lib)
}
}
type Pkg struct {
mark bool
checked bool
path string
impby []*Pkg
}
var pkgall []*Pkg
func (p *Pkg) cycle() *Pkg {
if p.checked {
return nil
}
if p.mark {
nerrors++
fmt.Printf("import cycle:\n")
fmt.Printf("\t%s\n", p.path)
return p
}
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)
if bad == p {
return nil
}
return bad
}
}
p.checked = true
p.mark = false
return nil
}
func importcycles() {
for _, p := range pkgall {
p.cycle()
}
}