summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal/ld/go.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal/ld/go.go')
-rw-r--r--src/cmd/link/internal/ld/go.go499
1 files changed, 499 insertions, 0 deletions
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
new file mode 100644
index 0000000..9dfb0f7
--- /dev/null
+++ b/src/cmd/link/internal/ld/go.go
@@ -0,0 +1,499 @@
+// 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 (
+ "cmd/internal/bio"
+ "cmd/internal/obj"
+ "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).
+
+// 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
+ }
+
+ // 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
+ }
+
+ 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) > 4 {
+ break
+ }
+ local := f[1]
+ remote := local
+ if len(f) > 2 {
+ remote = f[2]
+ }
+ // The compiler adds a fourth argument giving
+ // the definition ABI of function symbols.
+ abi := obj.ABI0
+ if len(f) > 3 {
+ var ok bool
+ abi, ok = obj.ParseABI(f[3])
+ if !ok {
+ fmt.Fprintf(os.Stderr, "%s: bad ABI in cgo_export directive %s\n", os.Args[0], f)
+ nerrors++
+ return
+ }
+ }
+
+ s := l.LookupOrCreateSym(local, sym.ABIToVersion(abi))
+
+ 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
+ }
+
+ // Mark exported symbols and also add them to
+ // the lists used for roots in the deadcode pass.
+ if f[0] == "cgo_export_static" {
+ if ctxt.LinkMode == LinkExternal && !l.AttrCgoExportStatic(s) {
+ // Static cgo exports appear
+ // in the exported symbol table.
+ ctxt.dynexp = append(ctxt.dynexp, s)
+ }
+ if ctxt.LinkMode == LinkInternal {
+ // For internal linking, we're
+ // responsible for resolving
+ // relocations from host objects.
+ // Record the right Go symbol
+ // version to use.
+ l.AddCgoExport(s)
+ }
+ l.SetAttrCgoExportStatic(s, true)
+ } else {
+ if ctxt.LinkMode == LinkInternal && !l.AttrCgoExportDynamic(s) {
+ // Dynamic cgo exports appear
+ // in the exported symbol table.
+ ctxt.dynexp = append(ctxt.dynexp, s)
+ }
+ 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 strings.Builder
+ 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
+ }
+
+ // Add dynamic symbols.
+ for _, s := range ctxt.dynexp {
+ // Consistency check.
+ if !ctxt.loader.AttrReachable(s) {
+ panic("dynexp entry not reachable")
+ }
+
+ Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, s)
+ }
+
+ 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()
+ }
+}