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

495 lines
12 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"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"encoding/json"
"fmt"
"io"
"os"
"sort"
"strconv"
"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, lib *sym.Library, length int64, filename string) {
if *flagG {
return
}
if int64(int(length)) != length {
fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
return
}
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)
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
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 == "main" {
lib.Main = 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
}
// 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)
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)
return
}
p1 += p0
loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
}
}
func loadcgo(ctxt *Link, file string, pkg string, p string) {
var directives [][]string
if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
nerrors++
return
}
// Find cgo_export symbols. They are roots in the deadcode pass.
for _, f := range directives {
switch f[0] {
case "cgo_export_static", "cgo_export_dynamic":
if len(f) < 2 || len(f) > 3 {
continue
}
local := f[1]
switch ctxt.BuildMode {
case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
if local == "main" {
continue
}
}
local = expandpkg(local, pkg)
if f[0] == "cgo_export_static" {
ctxt.cgo_export_static[local] = true
} else {
ctxt.cgo_export_dynamic[local] = true
}
}
}
// Record the directives. We'll process them later after Symbols are created.
ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
}
// Set symbol attributes or flags based on cgo directives.
// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'.
func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) {
l := ctxt.loader
for _, f := range directives {
switch f[0] {
case "cgo_import_dynamic":
if len(f) < 2 || len(f) > 4 {
break
}
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 ctxt.HeadType == objabi.Hdarwin {
machoadddynlib(lib, ctxt.LinkMode)
} else {
dynlib = append(dynlib, lib)
}
continue
}
local = expandpkg(local, pkg)
q := ""
if i := strings.Index(remote, "#"); i >= 0 {
remote, q = remote[:i], remote[i+1:]
}
s := l.LookupOrCreateSym(local, 0)
st := l.SymType(s)
if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ {
l.SetSymDynimplib(s, lib)
l.SetSymExtname(s, remote)
l.SetSymDynimpvers(s, q)
if st != sym.SHOSTOBJ {
su := l.MakeSymbolUpdater(s)
su.SetType(sym.SDYNIMPORT)
} else {
hostObjSyms[s] = struct{}{}
}
havedynamic = 1
if lib != "" && ctxt.IsDarwin() {
machoadddynlib(lib, ctxt.LinkMode)
}
}
continue
case "cgo_import_static":
if len(f) != 2 {
break
}
local := f[1]
s := l.LookupOrCreateSym(local, 0)
su := l.MakeSymbolUpdater(s)
su.SetType(sym.SHOSTOBJ)
su.SetSize(0)
hostObjSyms[s] = struct{}{}
continue
case "cgo_export_static", "cgo_export_dynamic":
if len(f) < 2 || len(f) > 3 {
break
}
local := f[1]
remote := local
if len(f) > 2 {
remote = f[2]
}
local = expandpkg(local, pkg)
// The compiler arranges for an ABI0 wrapper
// to be available for all cgo-exported
// functions. Link.loadlib will resolve any
// ABI aliases we find here (since we may not
// yet know it's an alias).
s := l.LookupOrCreateSym(local, 0)
if l.SymType(s) == sym.SHOSTOBJ {
hostObjSyms[s] = struct{}{}
}
switch ctxt.BuildMode {
case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
if s == l.Lookup("main", 0) {
continue
}
}
// export overrides import, for openbsd/cgo.
// see issue 4878.
if l.SymDynimplib(s) != "" {
l.SetSymDynimplib(s, "")
l.SetSymDynimpvers(s, "")
l.SetSymExtname(s, "")
var su *loader.SymbolBuilder
su = l.MakeSymbolUpdater(s)
su.SetType(0)
}
if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) {
l.SetSymExtname(s, remote)
} else if l.SymExtname(s) != remote {
fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote)
nerrors++
return
}
if f[0] == "cgo_export_static" {
l.SetAttrCgoExportStatic(s, true)
} else {
l.SetAttrCgoExportDynamic(s, true)
}
continue
case "cgo_dynamic_linker":
if len(f) != 2 {
break
}
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
case "cgo_ldflag":
if len(f) != 2 {
break
}
ldflag = append(ldflag, f[1])
continue
}
fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
nerrors++
}
return
}
// openbsdTrimLibVersion indicates whether a shared library is
// versioned and if it is, returns the unversioned name. The
// OpenBSD library naming scheme is lib<name>.so.<major>.<minor>
func openbsdTrimLibVersion(lib string) (string, bool) {
parts := strings.Split(lib, ".")
if len(parts) != 4 {
return "", false
}
if parts[1] != "so" {
return "", false
}
if _, err := strconv.Atoi(parts[2]); err != nil {
return "", false
}
if _, err := strconv.Atoi(parts[3]); err != nil {
return "", false
}
return fmt.Sprintf("%s.%s", parts[0], parts[1]), true
}
// dedupLibrariesOpenBSD dedups a list of shared libraries, treating versioned
// and unversioned libraries as equivalents. Versioned libraries are preferred
// and retained over unversioned libraries. This avoids the situation where
// the use of cgo results in a DT_NEEDED for a versioned library (for example,
// libc.so.96.1), while a dynamic import specifies an unversioned library (for
// example, libc.so) - this would otherwise result in two DT_NEEDED entries
// for the same library, resulting in a failure when ld.so attempts to load
// the Go binary.
func dedupLibrariesOpenBSD(ctxt *Link, libs []string) []string {
libraries := make(map[string]string)
for _, lib := range libs {
if name, ok := openbsdTrimLibVersion(lib); ok {
// Record unversioned name as seen.
seenlib[name] = true
libraries[name] = lib
} else if _, ok := libraries[lib]; !ok {
libraries[lib] = lib
}
}
libs = nil
for _, lib := range libraries {
libs = append(libs, lib)
}
sort.Strings(libs)
return libs
}
func dedupLibraries(ctxt *Link, libs []string) []string {
if ctxt.Target.IsOpenbsd() {
return dedupLibrariesOpenBSD(ctxt, libs)
}
return libs
}
var seenlib = make(map[string]bool)
func adddynlib(ctxt *Link, lib string) {
if seenlib[lib] || ctxt.LinkMode == LinkExternal {
return
}
seenlib[lib] = true
if ctxt.IsELF {
dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr)
if dsu.Size() == 0 {
dsu.Addstring("")
}
du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic)
Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib)))
} else {
Errorf(nil, "adddynlib: unsupported binary format")
}
}
func Adddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal {
return
}
if target.IsELF {
elfadddynsym(ldr, target, syms, s)
} else if target.HeadType == objabi.Hdarwin {
ldr.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s))
} else if target.HeadType == objabi.Hwindows {
// already taken care of
} else {
ldr.Errorf(s, "adddynsym: unsupported binary format")
}
}
func fieldtrack(arch *sys.Arch, l *loader.Loader) {
var buf bytes.Buffer
for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ {
if name := l.SymName(i); strings.HasPrefix(name, "go.track.") {
if l.AttrReachable(i) {
l.SetAttrSpecial(i, true)
l.SetAttrNotInSymbolTable(i, true)
buf.WriteString(name[9:])
for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] {
buf.WriteString("\t")
buf.WriteString(l.SymName(p))
}
buf.WriteString("\n")
}
}
}
l.Reachparent = nil // we are done with it
if *flagFieldTrack == "" {
return
}
s := l.Lookup(*flagFieldTrack, 0)
if s == 0 || !l.AttrReachable(s) {
return
}
bld := l.MakeSymbolUpdater(s)
bld.SetType(sym.SDATA)
addstrdata(arch, l, *flagFieldTrack, buf.String())
}
func (ctxt *Link) addexport() {
// Track undefined external symbols during external link.
if ctxt.LinkMode == LinkExternal {
for _, s := range ctxt.Textp {
if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) {
continue
}
relocs := ctxt.loader.Relocs(s)
for i := 0; i < relocs.Count(); i++ {
if rs := relocs.At(i).Sym(); rs != 0 {
if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) {
// sanity check
if len(ctxt.loader.Data(rs)) != 0 {
panic("expected no data on undef symbol")
}
su := ctxt.loader.MakeSymbolUpdater(rs)
su.SetType(sym.SUNDEFEXT)
}
}
}
}
}
// TODO(aix)
if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
return
}
for _, exp := range ctxt.dynexp {
Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, exp)
}
for _, lib := range dedupLibraries(ctxt, 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()
}
}