Home | History | Annotate | Download | only in 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/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 }
    870