blob: fbc7a78d0ed299b98811419fb77c32366e3c96c0 [file] [log] [blame]
// 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)
// 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, ""
}
if line == "main" {
lib.Main = true
}
if line == "" {
break
}
}
// 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, lookup func(string, int) loader.Sym, 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 := lookup(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 := lookup(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 := lookup(local, 0)
if l.SymType(s) == sym.SHOSTOBJ {
hostObjSyms[s] = struct{}{}
}
switch ctxt.BuildMode {
case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
if s == 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()
}
}