github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/ld/go.go (about)

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