mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
setCgoAttr takes a lookup function, but there's only a single call and setCgoAttr already has access to the lookup function passed at that call. Simplify setCgoAttr by eliminating the lookup parameter and calling the lookup function directly. For #40724. Change-Id: Ib27c0fa2b88c387e30423365f7757e3ba02cf7d5 Reviewed-on: https://go-review.googlesource.com/c/go/+/309338 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Go Bot <gobot@golang.org>
494 lines
12 KiB
Go
494 lines
12 KiB
Go
// 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, 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()
|
|
}
|
|
}
|