github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/cmd/link/internal/ld/go.go (about)

     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/obj"
    12  	"fmt"
    13  	"os"
    14  	"strconv"
    15  	"strings"
    16  )
    17  
    18  // go-specific code shared across loaders (5l, 6l, 8l).
    19  
    20  // replace all "". with pkg.
    21  func expandpkg(t0 string, pkg string) string {
    22  	return strings.Replace(t0, `"".`, pkg+".", -1)
    23  }
    24  
    25  // accumulate all type information from .6 files.
    26  // check for inconsistencies.
    27  
    28  // TODO:
    29  //	generate debugging section in binary.
    30  //	once the dust settles, try to move some code to
    31  //		libmach, so that other linkers and ar can share.
    32  
    33  /*
    34   *	package import data
    35   */
    36  type Import struct {
    37  	prefix string // "type", "var", "func", "const"
    38  	name   string
    39  	def    string
    40  	file   string
    41  }
    42  
    43  // importmap records type information about imported symbols to detect inconsistencies.
    44  // Entries are keyed by qualified symbol name (e.g., "runtime.Callers" or "net/url.Error").
    45  var importmap = map[string]*Import{}
    46  
    47  func lookupImport(name string) *Import {
    48  	if x, ok := importmap[name]; ok {
    49  		return x
    50  	}
    51  	x := &Import{name: name}
    52  	importmap[name] = x
    53  	return x
    54  }
    55  
    56  func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) {
    57  	var p0, p1 int
    58  
    59  	if Debug['g'] != 0 {
    60  		return
    61  	}
    62  
    63  	if int64(int(length)) != length {
    64  		fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
    65  		if Debug['u'] != 0 {
    66  			errorexit()
    67  		}
    68  		return
    69  	}
    70  
    71  	bdata := make([]byte, length)
    72  	if int64(obj.Bread(f, bdata)) != length {
    73  		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
    74  		if Debug['u'] != 0 {
    75  			errorexit()
    76  		}
    77  		return
    78  	}
    79  	data := string(bdata)
    80  
    81  	// first \n$$ marks beginning of exports - skip rest of line
    82  	p0 = strings.Index(data, "\n$$")
    83  	if p0 < 0 {
    84  		if Debug['u'] != 0 && whence != ArchiveObj {
    85  			Exitf("cannot find export data in %s", filename)
    86  		}
    87  		return
    88  	}
    89  
    90  	p0 += 3
    91  	for p0 < len(data) && data[p0] != '\n' {
    92  		p0++
    93  	}
    94  
    95  	// second marks end of exports / beginning of local data
    96  	p1 = strings.Index(data[p0:], "\n$$")
    97  	if p1 < 0 {
    98  		fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename)
    99  		if Debug['u'] != 0 {
   100  			errorexit()
   101  		}
   102  		return
   103  	}
   104  	p1 += p0
   105  
   106  	for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') {
   107  		p0++
   108  	}
   109  	if p0 < p1 {
   110  		if !strings.HasPrefix(data[p0:], "package ") {
   111  			fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:])
   112  			if Debug['u'] != 0 {
   113  				errorexit()
   114  			}
   115  			return
   116  		}
   117  
   118  		p0 += 8
   119  		for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') {
   120  			p0++
   121  		}
   122  		pname := p0
   123  		for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' {
   124  			p0++
   125  		}
   126  		if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) {
   127  			Exitf("load of unsafe package %s", filename)
   128  		}
   129  
   130  		name := data[pname:p0]
   131  		for p0 < p1 && data[p0] != '\n' {
   132  			p0++
   133  		}
   134  		if p0 < p1 {
   135  			p0++
   136  		}
   137  
   138  		if pkg == "main" && name != "main" {
   139  			Exitf("%s: not package main (package %s)", filename, name)
   140  		}
   141  
   142  		loadpkgdata(filename, pkg, data[p0:p1])
   143  	}
   144  
   145  	// __.PKGDEF has no cgo section - those are in the C compiler-generated object files.
   146  	if whence == Pkgdef {
   147  		return
   148  	}
   149  
   150  	// look for cgo section
   151  	p0 = strings.Index(data[p1:], "\n$$  // cgo")
   152  	if p0 >= 0 {
   153  		p0 += p1
   154  		i := strings.IndexByte(data[p0+1:], '\n')
   155  		if i < 0 {
   156  			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
   157  			if Debug['u'] != 0 {
   158  				errorexit()
   159  			}
   160  			return
   161  		}
   162  		p0 += 1 + i
   163  
   164  		p1 = strings.Index(data[p0:], "\n$$")
   165  		if p1 < 0 {
   166  			p1 = strings.Index(data[p0:], "\n!\n")
   167  		}
   168  		if p1 < 0 {
   169  			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
   170  			if Debug['u'] != 0 {
   171  				errorexit()
   172  			}
   173  			return
   174  		}
   175  		p1 += p0
   176  
   177  		loadcgo(filename, pkg, data[p0:p1])
   178  	}
   179  }
   180  
   181  func loadpkgdata(file string, pkg string, data string) {
   182  	var prefix string
   183  	var name string
   184  	var def string
   185  
   186  	p := data
   187  	for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 {
   188  		x := lookupImport(name)
   189  		if x.prefix == "" {
   190  			x.prefix = prefix
   191  			x.def = def
   192  			x.file = file
   193  		} else if x.prefix != prefix {
   194  			fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name)
   195  			fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", x.file, x.prefix, name)
   196  			fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", file, prefix, name)
   197  			nerrors++
   198  		} else if x.def != def {
   199  			fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name)
   200  			fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", x.file, x.prefix, name, x.def)
   201  			fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", file, prefix, name, def)
   202  			nerrors++
   203  		}
   204  	}
   205  }
   206  
   207  func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int {
   208  	// skip white space
   209  	p := *pp
   210  
   211  loop:
   212  	for len(p) > 0 && (p[0] == ' ' || p[0] == '\t' || p[0] == '\n') {
   213  		p = p[1:]
   214  	}
   215  	if len(p) == 0 || strings.HasPrefix(p, "$$\n") {
   216  		return 0
   217  	}
   218  
   219  	// prefix: (var|type|func|const)
   220  	prefix := p
   221  
   222  	if len(p) < 7 {
   223  		return -1
   224  	}
   225  	if strings.HasPrefix(p, "var ") {
   226  		p = p[4:]
   227  	} else if strings.HasPrefix(p, "type ") {
   228  		p = p[5:]
   229  	} else if strings.HasPrefix(p, "func ") {
   230  		p = p[5:]
   231  	} else if strings.HasPrefix(p, "const ") {
   232  		p = p[6:]
   233  	} else if strings.HasPrefix(p, "import ") {
   234  		p = p[7:]
   235  		for len(p) > 0 && p[0] != ' ' {
   236  			p = p[1:]
   237  		}
   238  		p = p[1:]
   239  		line := p
   240  		for len(p) > 0 && p[0] != '\n' {
   241  			p = p[1:]
   242  		}
   243  		if len(p) == 0 {
   244  			fmt.Fprintf(os.Stderr, "%s: %s: confused in import line\n", os.Args[0], file)
   245  			nerrors++
   246  			return -1
   247  		}
   248  		line = line[:len(line)-len(p)]
   249  		line = strings.TrimSuffix(line, " // indirect")
   250  		path, err := strconv.Unquote(line)
   251  		if err != nil {
   252  			fmt.Fprintf(os.Stderr, "%s: %s: confused in import path: %q\n", os.Args[0], file, line)
   253  			nerrors++
   254  			return -1
   255  		}
   256  		p = p[1:]
   257  		imported(pkg, path)
   258  		goto loop
   259  	} else {
   260  		fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix)
   261  		nerrors++
   262  		return -1
   263  	}
   264  
   265  	prefix = prefix[:len(prefix)-len(p)-1]
   266  
   267  	// name: a.b followed by space
   268  	name := p
   269  
   270  	inquote := false
   271  	for len(p) > 0 {
   272  		if p[0] == ' ' && !inquote {
   273  			break
   274  		}
   275  
   276  		if p[0] == '\\' {
   277  			p = p[1:]
   278  		} else if p[0] == '"' {
   279  			inquote = !inquote
   280  		}
   281  
   282  		p = p[1:]
   283  	}
   284  
   285  	if len(p) == 0 {
   286  		return -1
   287  	}
   288  	name = name[:len(name)-len(p)]
   289  	p = p[1:]
   290  
   291  	// def: free form to new line
   292  	def := p
   293  
   294  	for len(p) > 0 && p[0] != '\n' {
   295  		p = p[1:]
   296  	}
   297  	if len(p) == 0 {
   298  		return -1
   299  	}
   300  	def = def[:len(def)-len(p)]
   301  	var defbuf *bytes.Buffer
   302  	p = p[1:]
   303  
   304  	// include methods on successive lines in def of named type
   305  	var meth string
   306  	for parsemethod(&p, &meth) > 0 {
   307  		if defbuf == nil {
   308  			defbuf = new(bytes.Buffer)
   309  			defbuf.WriteString(def)
   310  		}
   311  		defbuf.WriteString("\n\t")
   312  		defbuf.WriteString(meth)
   313  	}
   314  	if defbuf != nil {
   315  		def = defbuf.String()
   316  	}
   317  
   318  	name = expandpkg(name, pkg)
   319  	def = expandpkg(def, pkg)
   320  
   321  	// done
   322  	*pp = p
   323  
   324  	*prefixp = prefix
   325  	*namep = name
   326  	*defp = def
   327  	return 1
   328  }
   329  
   330  func parsemethod(pp *string, methp *string) int {
   331  	// skip white space
   332  	p := *pp
   333  
   334  	for len(p) > 0 && (p[0] == ' ' || p[0] == '\t') {
   335  		p = p[1:]
   336  	}
   337  	if len(p) == 0 {
   338  		return 0
   339  	}
   340  
   341  	// might be a comment about the method
   342  	if strings.HasPrefix(p, "//") {
   343  		goto useline
   344  	}
   345  
   346  	// if it says "func (", it's a method
   347  	if strings.HasPrefix(p, "func (") {
   348  		goto useline
   349  	}
   350  	return 0
   351  
   352  	// definition to end of line
   353  useline:
   354  	*methp = p
   355  
   356  	for len(p) > 0 && p[0] != '\n' {
   357  		p = p[1:]
   358  	}
   359  	if len(p) == 0 {
   360  		fmt.Fprintf(os.Stderr, "%s: lost end of line in method definition\n", os.Args[0])
   361  		*pp = ""
   362  		return -1
   363  	}
   364  
   365  	*methp = (*methp)[:len(*methp)-len(p)]
   366  	*pp = p[1:]
   367  	return 1
   368  }
   369  
   370  func loadcgo(file string, pkg string, p string) {
   371  	var next string
   372  	var q string
   373  	var f []string
   374  	var local string
   375  	var remote string
   376  	var lib string
   377  	var s *LSym
   378  
   379  	p0 := ""
   380  	for ; p != ""; p = next {
   381  		if i := strings.Index(p, "\n"); i >= 0 {
   382  			p, next = p[:i], p[i+1:]
   383  		} else {
   384  			next = ""
   385  		}
   386  
   387  		p0 = p // save for error message
   388  		f = tokenize(p)
   389  		if len(f) == 0 {
   390  			continue
   391  		}
   392  
   393  		if f[0] == "cgo_import_dynamic" {
   394  			if len(f) < 2 || len(f) > 4 {
   395  				goto err
   396  			}
   397  
   398  			local = f[1]
   399  			remote = local
   400  			if len(f) > 2 {
   401  				remote = f[2]
   402  			}
   403  			lib = ""
   404  			if len(f) > 3 {
   405  				lib = f[3]
   406  			}
   407  
   408  			if Debug['d'] != 0 {
   409  				fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
   410  				nerrors++
   411  				return
   412  			}
   413  
   414  			if local == "_" && remote == "_" {
   415  				// allow #pragma dynimport _ _ "foo.so"
   416  				// to force a link of foo.so.
   417  				havedynamic = 1
   418  
   419  				if HEADTYPE == obj.Hdarwin {
   420  					Machoadddynlib(lib)
   421  				} else {
   422  					dynlib = append(dynlib, lib)
   423  				}
   424  				continue
   425  			}
   426  
   427  			local = expandpkg(local, pkg)
   428  			q = ""
   429  			if i := strings.Index(remote, "#"); i >= 0 {
   430  				remote, q = remote[:i], remote[i+1:]
   431  			}
   432  			s = Linklookup(Ctxt, local, 0)
   433  			if local != f[1] {
   434  			}
   435  			if s.Type == 0 || s.Type == obj.SXREF || s.Type == obj.SHOSTOBJ {
   436  				s.Dynimplib = lib
   437  				s.Extname = remote
   438  				s.Dynimpvers = q
   439  				if s.Type != obj.SHOSTOBJ {
   440  					s.Type = obj.SDYNIMPORT
   441  				}
   442  				havedynamic = 1
   443  			}
   444  
   445  			continue
   446  		}
   447  
   448  		if f[0] == "cgo_import_static" {
   449  			if len(f) != 2 {
   450  				goto err
   451  			}
   452  			local = f[1]
   453  			s = Linklookup(Ctxt, local, 0)
   454  			s.Type = obj.SHOSTOBJ
   455  			s.Size = 0
   456  			continue
   457  		}
   458  
   459  		if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" {
   460  			if len(f) < 2 || len(f) > 3 {
   461  				goto err
   462  			}
   463  			local = f[1]
   464  			if len(f) > 2 {
   465  				remote = f[2]
   466  			} else {
   467  				remote = local
   468  			}
   469  			local = expandpkg(local, pkg)
   470  			s = Linklookup(Ctxt, local, 0)
   471  
   472  			switch Buildmode {
   473  			case BuildmodeCShared, BuildmodeCArchive:
   474  				if s == Linklookup(Ctxt, "main", 0) {
   475  					continue
   476  				}
   477  			}
   478  
   479  			// export overrides import, for openbsd/cgo.
   480  			// see issue 4878.
   481  			if s.Dynimplib != "" {
   482  				s.Dynimplib = ""
   483  				s.Extname = ""
   484  				s.Dynimpvers = ""
   485  				s.Type = 0
   486  			}
   487  
   488  			if s.Cgoexport == 0 {
   489  				s.Extname = remote
   490  				dynexp = append(dynexp, s)
   491  			} else if s.Extname != remote {
   492  				fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname, remote)
   493  				nerrors++
   494  				return
   495  			}
   496  
   497  			if f[0] == "cgo_export_static" {
   498  				s.Cgoexport |= CgoExportStatic
   499  			} else {
   500  				s.Cgoexport |= CgoExportDynamic
   501  			}
   502  			if local != f[1] {
   503  			}
   504  			continue
   505  		}
   506  
   507  		if f[0] == "cgo_dynamic_linker" {
   508  			if len(f) != 2 {
   509  				goto err
   510  			}
   511  
   512  			if Debug['I'] == 0 {
   513  				if interpreter != "" && interpreter != f[1] {
   514  					fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
   515  					nerrors++
   516  					return
   517  				}
   518  
   519  				interpreter = f[1]
   520  			}
   521  
   522  			continue
   523  		}
   524  
   525  		if f[0] == "cgo_ldflag" {
   526  			if len(f) != 2 {
   527  				goto err
   528  			}
   529  			ldflag = append(ldflag, f[1])
   530  			continue
   531  		}
   532  	}
   533  
   534  	return
   535  
   536  err:
   537  	fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0)
   538  	nerrors++
   539  }
   540  
   541  var seenlib = make(map[string]bool)
   542  
   543  func adddynlib(lib string) {
   544  	if seenlib[lib] || Linkmode == LinkExternal {
   545  		return
   546  	}
   547  	seenlib[lib] = true
   548  
   549  	if Iself {
   550  		s := Linklookup(Ctxt, ".dynstr", 0)
   551  		if s.Size == 0 {
   552  			Addstring(s, "")
   553  		}
   554  		Elfwritedynent(Linklookup(Ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
   555  	} else {
   556  		Diag("adddynlib: unsupported binary format")
   557  	}
   558  }
   559  
   560  func Adddynsym(ctxt *Link, s *LSym) {
   561  	if s.Dynid >= 0 || Linkmode == LinkExternal {
   562  		return
   563  	}
   564  
   565  	if Iself {
   566  		Elfadddynsym(ctxt, s)
   567  	} else if HEADTYPE == obj.Hdarwin {
   568  		Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
   569  	} else if HEADTYPE == obj.Hwindows {
   570  		// already taken care of
   571  	} else {
   572  		Diag("adddynsym: unsupported binary format")
   573  	}
   574  }
   575  
   576  var markq *LSym
   577  
   578  var emarkq *LSym
   579  
   580  func mark1(s *LSym, parent *LSym) {
   581  	if s == nil || s.Reachable {
   582  		return
   583  	}
   584  	if strings.HasPrefix(s.Name, "go.weak.") {
   585  		return
   586  	}
   587  	s.Reachable = true
   588  	s.Reachparent = parent
   589  	if markq == nil {
   590  		markq = s
   591  	} else {
   592  		emarkq.Queue = s
   593  	}
   594  	emarkq = s
   595  }
   596  
   597  func mark(s *LSym) {
   598  	mark1(s, nil)
   599  }
   600  
   601  func markflood() {
   602  	var a *Auto
   603  	var i int
   604  
   605  	for s := markq; s != nil; s = s.Queue {
   606  		if s.Type == obj.STEXT {
   607  			if Debug['v'] > 1 {
   608  				fmt.Fprintf(&Bso, "marktext %s\n", s.Name)
   609  			}
   610  			for a = s.Autom; a != nil; a = a.Link {
   611  				mark1(a.Gotype, s)
   612  			}
   613  		}
   614  
   615  		for i = 0; i < len(s.R); i++ {
   616  			mark1(s.R[i].Sym, s)
   617  		}
   618  		if s.Pcln != nil {
   619  			for i = 0; i < s.Pcln.Nfuncdata; i++ {
   620  				mark1(s.Pcln.Funcdata[i], s)
   621  			}
   622  		}
   623  
   624  		mark1(s.Gotype, s)
   625  		mark1(s.Sub, s)
   626  		mark1(s.Outer, s)
   627  	}
   628  }
   629  
   630  var markextra = []string{
   631  	"runtime.morestack",
   632  	"runtime.morestackx",
   633  	"runtime.morestack00",
   634  	"runtime.morestack10",
   635  	"runtime.morestack01",
   636  	"runtime.morestack11",
   637  	"runtime.morestack8",
   638  	"runtime.morestack16",
   639  	"runtime.morestack24",
   640  	"runtime.morestack32",
   641  	"runtime.morestack40",
   642  	"runtime.morestack48",
   643  	// on arm, lock in the div/mod helpers too
   644  	"_div",
   645  	"_divu",
   646  	"_mod",
   647  	"_modu",
   648  }
   649  
   650  func deadcode() {
   651  	if Debug['v'] != 0 {
   652  		fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime())
   653  	}
   654  
   655  	if Buildmode == BuildmodeShared {
   656  		// Mark all symbols defined in this library as reachable when
   657  		// building a shared library.
   658  		for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   659  			if s.Type != 0 && s.Type != obj.SDYNIMPORT {
   660  				mark(s)
   661  			}
   662  		}
   663  		markflood()
   664  	} else {
   665  		mark(Linklookup(Ctxt, INITENTRY, 0))
   666  		if Linkshared && Buildmode == BuildmodeExe {
   667  			mark(Linkrlookup(Ctxt, "main.main", 0))
   668  			mark(Linkrlookup(Ctxt, "main.init", 0))
   669  		}
   670  		for i := 0; i < len(markextra); i++ {
   671  			mark(Linklookup(Ctxt, markextra[i], 0))
   672  		}
   673  
   674  		for i := 0; i < len(dynexp); i++ {
   675  			mark(dynexp[i])
   676  		}
   677  		markflood()
   678  
   679  		// keep each beginning with 'typelink.' if the symbol it points at is being kept.
   680  		for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   681  			if strings.HasPrefix(s.Name, "go.typelink.") {
   682  				s.Reachable = len(s.R) == 1 && s.R[0].Sym.Reachable
   683  			}
   684  		}
   685  
   686  		// remove dead text but keep file information (z symbols).
   687  		var last *LSym
   688  
   689  		for s := Ctxt.Textp; s != nil; s = s.Next {
   690  			if !s.Reachable {
   691  				continue
   692  			}
   693  
   694  			// NOTE: Removing s from old textp and adding to new, shorter textp.
   695  			if last == nil {
   696  				Ctxt.Textp = s
   697  			} else {
   698  				last.Next = s
   699  			}
   700  			last = s
   701  		}
   702  
   703  		if last == nil {
   704  			Ctxt.Textp = nil
   705  			Ctxt.Etextp = nil
   706  		} else {
   707  			last.Next = nil
   708  			Ctxt.Etextp = last
   709  		}
   710  	}
   711  
   712  	for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   713  		if strings.HasPrefix(s.Name, "go.weak.") {
   714  			s.Special = 1 // do not lay out in data segment
   715  			s.Reachable = true
   716  			s.Hide = 1
   717  		}
   718  	}
   719  
   720  	// record field tracking references
   721  	var buf bytes.Buffer
   722  	var p *LSym
   723  	for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   724  		if strings.HasPrefix(s.Name, "go.track.") {
   725  			s.Special = 1 // do not lay out in data segment
   726  			s.Hide = 1
   727  			if s.Reachable {
   728  				buf.WriteString(s.Name[9:])
   729  				for p = s.Reachparent; p != nil; p = p.Reachparent {
   730  					buf.WriteString("\t")
   731  					buf.WriteString(p.Name)
   732  				}
   733  				buf.WriteString("\n")
   734  			}
   735  
   736  			s.Type = obj.SCONST
   737  			s.Value = 0
   738  		}
   739  	}
   740  
   741  	if tracksym == "" {
   742  		return
   743  	}
   744  	s := Linklookup(Ctxt, tracksym, 0)
   745  	if !s.Reachable {
   746  		return
   747  	}
   748  	addstrdata(tracksym, buf.String())
   749  }
   750  
   751  func doweak() {
   752  	var t *LSym
   753  
   754  	// resolve weak references only if
   755  	// target symbol will be in binary anyway.
   756  	for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   757  		if strings.HasPrefix(s.Name, "go.weak.") {
   758  			t = Linkrlookup(Ctxt, s.Name[8:], int(s.Version))
   759  			if t != nil && t.Type != 0 && t.Reachable {
   760  				s.Value = t.Value
   761  				s.Type = t.Type
   762  				s.Outer = t
   763  			} else {
   764  				s.Type = obj.SCONST
   765  				s.Value = 0
   766  			}
   767  
   768  			continue
   769  		}
   770  	}
   771  }
   772  
   773  func addexport() {
   774  	if HEADTYPE == obj.Hdarwin {
   775  		return
   776  	}
   777  
   778  	for _, exp := range dynexp {
   779  		Adddynsym(Ctxt, exp)
   780  	}
   781  	for _, lib := range dynlib {
   782  		adddynlib(lib)
   783  	}
   784  }
   785  
   786  type Pkg struct {
   787  	mark    bool
   788  	checked bool
   789  	path    string
   790  	impby   []*Pkg
   791  }
   792  
   793  var (
   794  	// pkgmap records the imported-by relationship between packages.
   795  	// Entries are keyed by package path (e.g., "runtime" or "net/url").
   796  	pkgmap = map[string]*Pkg{}
   797  
   798  	pkgall []*Pkg
   799  )
   800  
   801  func lookupPkg(path string) *Pkg {
   802  	if p, ok := pkgmap[path]; ok {
   803  		return p
   804  	}
   805  	p := &Pkg{path: path}
   806  	pkgmap[path] = p
   807  	pkgall = append(pkgall, p)
   808  	return p
   809  }
   810  
   811  // imported records that package pkg imports package imp.
   812  func imported(pkg, imp string) {
   813  	// everyone imports runtime, even runtime.
   814  	if imp == "runtime" {
   815  		return
   816  	}
   817  
   818  	p := lookupPkg(pkg)
   819  	i := lookupPkg(imp)
   820  	i.impby = append(i.impby, p)
   821  }
   822  
   823  func (p *Pkg) cycle() *Pkg {
   824  	if p.checked {
   825  		return nil
   826  	}
   827  
   828  	if p.mark {
   829  		nerrors++
   830  		fmt.Printf("import cycle:\n")
   831  		fmt.Printf("\t%s\n", p.path)
   832  		return p
   833  	}
   834  
   835  	p.mark = true
   836  	for _, q := range p.impby {
   837  		if bad := q.cycle(); bad != nil {
   838  			p.mark = false
   839  			p.checked = true
   840  			fmt.Printf("\timports %s\n", p.path)
   841  			if bad == p {
   842  				return nil
   843  			}
   844  			return bad
   845  		}
   846  	}
   847  
   848  	p.checked = true
   849  	p.mark = false
   850  	return nil
   851  }
   852  
   853  func importcycles() {
   854  	for _, p := range pkgall {
   855  		p.cycle()
   856  	}
   857  }
   858  
   859  func setlinkmode(arg string) {
   860  	if arg == "internal" {
   861  		Linkmode = LinkInternal
   862  	} else if arg == "external" {
   863  		Linkmode = LinkExternal
   864  	} else if arg == "auto" {
   865  		Linkmode = LinkAuto
   866  	} else {
   867  		Exitf("unknown link mode -linkmode %s", arg)
   868  	}
   869  }