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