Black Lives Matter. Support the Equal Justice Initiative.

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

Documentation: cmd/link/internal/ld

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // go-specific code shared across loaders (5l, 6l, 8l).
     6  
     7  package ld
     8  
     9  import (
    10  	"bytes"
    11  	"cmd/internal/bio"
    12  	"cmd/internal/objabi"
    13  	"cmd/internal/sys"
    14  	"cmd/link/internal/loader"
    15  	"cmd/link/internal/sym"
    16  	"debug/elf"
    17  	"encoding/json"
    18  	"fmt"
    19  	"io"
    20  	"os"
    21  	"sort"
    22  	"strconv"
    23  	"strings"
    24  )
    25  
    26  // go-specific code shared across loaders (5l, 6l, 8l).
    27  
    28  // replace all "". with pkg.
    29  func expandpkg(t0 string, pkg string) string {
    30  	return strings.Replace(t0, `"".`, pkg+".", -1)
    31  }
    32  
    33  // TODO:
    34  //	generate debugging section in binary.
    35  //	once the dust settles, try to move some code to
    36  //		libmach, so that other linkers and ar can share.
    37  
    38  func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) {
    39  	if *flagG {
    40  		return
    41  	}
    42  
    43  	if int64(int(length)) != length {
    44  		fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
    45  		return
    46  	}
    47  
    48  	bdata := make([]byte, length)
    49  	if _, err := io.ReadFull(f, bdata); err != nil {
    50  		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
    51  		return
    52  	}
    53  	data := string(bdata)
    54  
    55  	// process header lines
    56  	for data != "" {
    57  		var line string
    58  		if i := strings.Index(data, "\n"); i >= 0 {
    59  			line, data = data[:i], data[i+1:]
    60  		} else {
    61  			line, data = data, ""
    62  		}
    63  		if line == "main" {
    64  			lib.Main = true
    65  		}
    66  		if line == "" {
    67  			break
    68  		}
    69  	}
    70  
    71  	// look for cgo section
    72  	p0 := strings.Index(data, "\n$$  // cgo")
    73  	var p1 int
    74  	if p0 >= 0 {
    75  		p0 += p1
    76  		i := strings.IndexByte(data[p0+1:], '\n')
    77  		if i < 0 {
    78  			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
    79  			return
    80  		}
    81  		p0 += 1 + i
    82  
    83  		p1 = strings.Index(data[p0:], "\n$$")
    84  		if p1 < 0 {
    85  			p1 = strings.Index(data[p0:], "\n!\n")
    86  		}
    87  		if p1 < 0 {
    88  			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
    89  			return
    90  		}
    91  		p1 += p0
    92  		loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
    93  	}
    94  }
    95  
    96  func loadcgo(ctxt *Link, file string, pkg string, p string) {
    97  	var directives [][]string
    98  	if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
    99  		fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
   100  		nerrors++
   101  		return
   102  	}
   103  
   104  	// Find cgo_export symbols. They are roots in the deadcode pass.
   105  	for _, f := range directives {
   106  		switch f[0] {
   107  		case "cgo_export_static", "cgo_export_dynamic":
   108  			if len(f) < 2 || len(f) > 3 {
   109  				continue
   110  			}
   111  			local := f[1]
   112  			switch ctxt.BuildMode {
   113  			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
   114  				if local == "main" {
   115  					continue
   116  				}
   117  			}
   118  			local = expandpkg(local, pkg)
   119  			if f[0] == "cgo_export_static" {
   120  				ctxt.cgo_export_static[local] = true
   121  			} else {
   122  				ctxt.cgo_export_dynamic[local] = true
   123  			}
   124  		}
   125  	}
   126  
   127  	// Record the directives. We'll process them later after Symbols are created.
   128  	ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
   129  }
   130  
   131  // Set symbol attributes or flags based on cgo directives.
   132  // Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'.
   133  func setCgoAttr(ctxt *Link, lookup func(string, int) loader.Sym, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) {
   134  	l := ctxt.loader
   135  	for _, f := range directives {
   136  		switch f[0] {
   137  		case "cgo_import_dynamic":
   138  			if len(f) < 2 || len(f) > 4 {
   139  				break
   140  			}
   141  
   142  			local := f[1]
   143  			remote := local
   144  			if len(f) > 2 {
   145  				remote = f[2]
   146  			}
   147  			lib := ""
   148  			if len(f) > 3 {
   149  				lib = f[3]
   150  			}
   151  
   152  			if *FlagD {
   153  				fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
   154  				nerrors++
   155  				return
   156  			}
   157  
   158  			if local == "_" && remote == "_" {
   159  				// allow #pragma dynimport _ _ "foo.so"
   160  				// to force a link of foo.so.
   161  				havedynamic = 1
   162  
   163  				if ctxt.HeadType == objabi.Hdarwin {
   164  					machoadddynlib(lib, ctxt.LinkMode)
   165  				} else {
   166  					dynlib = append(dynlib, lib)
   167  				}
   168  				continue
   169  			}
   170  
   171  			local = expandpkg(local, pkg)
   172  			q := ""
   173  			if i := strings.Index(remote, "#"); i >= 0 {
   174  				remote, q = remote[:i], remote[i+1:]
   175  			}
   176  			s := lookup(local, 0)
   177  			st := l.SymType(s)
   178  			if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ {
   179  				l.SetSymDynimplib(s, lib)
   180  				l.SetSymExtname(s, remote)
   181  				l.SetSymDynimpvers(s, q)
   182  				if st != sym.SHOSTOBJ {
   183  					su := l.MakeSymbolUpdater(s)
   184  					su.SetType(sym.SDYNIMPORT)
   185  				} else {
   186  					hostObjSyms[s] = struct{}{}
   187  				}
   188  				havedynamic = 1
   189  				if lib != "" && ctxt.IsDarwin() {
   190  					machoadddynlib(lib, ctxt.LinkMode)
   191  				}
   192  			}
   193  
   194  			continue
   195  
   196  		case "cgo_import_static":
   197  			if len(f) != 2 {
   198  				break
   199  			}
   200  			local := f[1]
   201  
   202  			s := lookup(local, 0)
   203  			su := l.MakeSymbolUpdater(s)
   204  			su.SetType(sym.SHOSTOBJ)
   205  			su.SetSize(0)
   206  			hostObjSyms[s] = struct{}{}
   207  			continue
   208  
   209  		case "cgo_export_static", "cgo_export_dynamic":
   210  			if len(f) < 2 || len(f) > 3 {
   211  				break
   212  			}
   213  			local := f[1]
   214  			remote := local
   215  			if len(f) > 2 {
   216  				remote = f[2]
   217  			}
   218  			local = expandpkg(local, pkg)
   219  
   220  			// The compiler arranges for an ABI0 wrapper
   221  			// to be available for all cgo-exported
   222  			// functions. Link.loadlib will resolve any
   223  			// ABI aliases we find here (since we may not
   224  			// yet know it's an alias).
   225  			s := lookup(local, 0)
   226  
   227  			if l.SymType(s) == sym.SHOSTOBJ {
   228  				hostObjSyms[s] = struct{}{}
   229  			}
   230  
   231  			switch ctxt.BuildMode {
   232  			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
   233  				if s == lookup("main", 0) {
   234  					continue
   235  				}
   236  			}
   237  
   238  			// export overrides import, for openbsd/cgo.
   239  			// see issue 4878.
   240  			if l.SymDynimplib(s) != "" {
   241  				l.SetSymDynimplib(s, "")
   242  				l.SetSymDynimpvers(s, "")
   243  				l.SetSymExtname(s, "")
   244  				var su *loader.SymbolBuilder
   245  				su = l.MakeSymbolUpdater(s)
   246  				su.SetType(0)
   247  			}
   248  
   249  			if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) {
   250  				l.SetSymExtname(s, remote)
   251  			} else if l.SymExtname(s) != remote {
   252  				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)
   253  				nerrors++
   254  				return
   255  			}
   256  
   257  			if f[0] == "cgo_export_static" {
   258  				l.SetAttrCgoExportStatic(s, true)
   259  			} else {
   260  				l.SetAttrCgoExportDynamic(s, true)
   261  			}
   262  			continue
   263  
   264  		case "cgo_dynamic_linker":
   265  			if len(f) != 2 {
   266  				break
   267  			}
   268  
   269  			if *flagInterpreter == "" {
   270  				if interpreter != "" && interpreter != f[1] {
   271  					fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
   272  					nerrors++
   273  					return
   274  				}
   275  
   276  				interpreter = f[1]
   277  			}
   278  			continue
   279  
   280  		case "cgo_ldflag":
   281  			if len(f) != 2 {
   282  				break
   283  			}
   284  			ldflag = append(ldflag, f[1])
   285  			continue
   286  		}
   287  
   288  		fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
   289  		nerrors++
   290  	}
   291  	return
   292  }
   293  
   294  // openbsdTrimLibVersion indicates whether a shared library is
   295  // versioned and if it is, returns the unversioned name. The
   296  // OpenBSD library naming scheme is lib<name>.so.<major>.<minor>
   297  func openbsdTrimLibVersion(lib string) (string, bool) {
   298  	parts := strings.Split(lib, ".")
   299  	if len(parts) != 4 {
   300  		return "", false
   301  	}
   302  	if parts[1] != "so" {
   303  		return "", false
   304  	}
   305  	if _, err := strconv.Atoi(parts[2]); err != nil {
   306  		return "", false
   307  	}
   308  	if _, err := strconv.Atoi(parts[3]); err != nil {
   309  		return "", false
   310  	}
   311  	return fmt.Sprintf("%s.%s", parts[0], parts[1]), true
   312  }
   313  
   314  // dedupLibrariesOpenBSD dedups a list of shared libraries, treating versioned
   315  // and unversioned libraries as equivalents. Versioned libraries are preferred
   316  // and retained over unversioned libraries. This avoids the situation where
   317  // the use of cgo results in a DT_NEEDED for a versioned library (for example,
   318  // libc.so.96.1), while a dynamic import specifies an unversioned library (for
   319  // example, libc.so) - this would otherwise result in two DT_NEEDED entries
   320  // for the same library, resulting in a failure when ld.so attempts to load
   321  // the Go binary.
   322  func dedupLibrariesOpenBSD(ctxt *Link, libs []string) []string {
   323  	libraries := make(map[string]string)
   324  	for _, lib := range libs {
   325  		if name, ok := openbsdTrimLibVersion(lib); ok {
   326  			// Record unversioned name as seen.
   327  			seenlib[name] = true
   328  			libraries[name] = lib
   329  		} else if _, ok := libraries[lib]; !ok {
   330  			libraries[lib] = lib
   331  		}
   332  	}
   333  
   334  	libs = nil
   335  	for _, lib := range libraries {
   336  		libs = append(libs, lib)
   337  	}
   338  	sort.Strings(libs)
   339  
   340  	return libs
   341  }
   342  
   343  func dedupLibraries(ctxt *Link, libs []string) []string {
   344  	if ctxt.Target.IsOpenbsd() {
   345  		return dedupLibrariesOpenBSD(ctxt, libs)
   346  	}
   347  	return libs
   348  }
   349  
   350  var seenlib = make(map[string]bool)
   351  
   352  func adddynlib(ctxt *Link, lib string) {
   353  	if seenlib[lib] || ctxt.LinkMode == LinkExternal {
   354  		return
   355  	}
   356  	seenlib[lib] = true
   357  
   358  	if ctxt.IsELF {
   359  		dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr)
   360  		if dsu.Size() == 0 {
   361  			dsu.Addstring("")
   362  		}
   363  		du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic)
   364  		Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib)))
   365  	} else {
   366  		Errorf(nil, "adddynlib: unsupported binary format")
   367  	}
   368  }
   369  
   370  func Adddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
   371  	if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal {
   372  		return
   373  	}
   374  
   375  	if target.IsELF {
   376  		elfadddynsym(ldr, target, syms, s)
   377  	} else if target.HeadType == objabi.Hdarwin {
   378  		ldr.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s))
   379  	} else if target.HeadType == objabi.Hwindows {
   380  		// already taken care of
   381  	} else {
   382  		ldr.Errorf(s, "adddynsym: unsupported binary format")
   383  	}
   384  }
   385  
   386  func fieldtrack(arch *sys.Arch, l *loader.Loader) {
   387  	var buf bytes.Buffer
   388  	for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ {
   389  		if name := l.SymName(i); strings.HasPrefix(name, "go.track.") {
   390  			if l.AttrReachable(i) {
   391  				l.SetAttrSpecial(i, true)
   392  				l.SetAttrNotInSymbolTable(i, true)
   393  				buf.WriteString(name[9:])
   394  				for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] {
   395  					buf.WriteString("\t")
   396  					buf.WriteString(l.SymName(p))
   397  				}
   398  				buf.WriteString("\n")
   399  			}
   400  		}
   401  	}
   402  	l.Reachparent = nil // we are done with it
   403  	if *flagFieldTrack == "" {
   404  		return
   405  	}
   406  	s := l.Lookup(*flagFieldTrack, 0)
   407  	if s == 0 || !l.AttrReachable(s) {
   408  		return
   409  	}
   410  	bld := l.MakeSymbolUpdater(s)
   411  	bld.SetType(sym.SDATA)
   412  	addstrdata(arch, l, *flagFieldTrack, buf.String())
   413  }
   414  
   415  func (ctxt *Link) addexport() {
   416  	// Track undefined external symbols during external link.
   417  	if ctxt.LinkMode == LinkExternal {
   418  		for _, s := range ctxt.Textp {
   419  			if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) {
   420  				continue
   421  			}
   422  			relocs := ctxt.loader.Relocs(s)
   423  			for i := 0; i < relocs.Count(); i++ {
   424  				if rs := relocs.At(i).Sym(); rs != 0 {
   425  					if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) {
   426  						// sanity check
   427  						if len(ctxt.loader.Data(rs)) != 0 {
   428  							panic("expected no data on undef symbol")
   429  						}
   430  						su := ctxt.loader.MakeSymbolUpdater(rs)
   431  						su.SetType(sym.SUNDEFEXT)
   432  					}
   433  				}
   434  			}
   435  		}
   436  	}
   437  
   438  	// TODO(aix)
   439  	if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
   440  		return
   441  	}
   442  
   443  	for _, exp := range ctxt.dynexp {
   444  		Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, exp)
   445  	}
   446  	for _, lib := range dedupLibraries(ctxt, dynlib) {
   447  		adddynlib(ctxt, lib)
   448  	}
   449  }
   450  
   451  type Pkg struct {
   452  	mark    bool
   453  	checked bool
   454  	path    string
   455  	impby   []*Pkg
   456  }
   457  
   458  var pkgall []*Pkg
   459  
   460  func (p *Pkg) cycle() *Pkg {
   461  	if p.checked {
   462  		return nil
   463  	}
   464  
   465  	if p.mark {
   466  		nerrors++
   467  		fmt.Printf("import cycle:\n")
   468  		fmt.Printf("\t%s\n", p.path)
   469  		return p
   470  	}
   471  
   472  	p.mark = true
   473  	for _, q := range p.impby {
   474  		if bad := q.cycle(); bad != nil {
   475  			p.mark = false
   476  			p.checked = true
   477  			fmt.Printf("\timports %s\n", p.path)
   478  			if bad == p {
   479  				return nil
   480  			}
   481  			return bad
   482  		}
   483  	}
   484  
   485  	p.checked = true
   486  	p.mark = false
   487  	return nil
   488  }
   489  
   490  func importcycles() {
   491  	for _, p := range pkgall {
   492  		p.cycle()
   493  	}
   494  }
   495  

View as plain text