| // 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/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 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 |
| } |
| |
| // 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() |
| } |
| } |