github.com/aloncn/graphics-go@v0.0.1/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  	"strings"
    15  )
    16  
    17  // go-specific code shared across loaders (5l, 6l, 8l).
    18  
    19  // replace all "". with pkg.
    20  func expandpkg(t0 string, pkg string) string {
    21  	return strings.Replace(t0, `"".`, pkg+".", -1)
    22  }
    23  
    24  // TODO:
    25  //	generate debugging section in binary.
    26  //	once the dust settles, try to move some code to
    27  //		libmach, so that other linkers and ar can share.
    28  
    29  func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) {
    30  	var p0, p1 int
    31  
    32  	if Debug['g'] != 0 {
    33  		return
    34  	}
    35  
    36  	if int64(int(length)) != length {
    37  		fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
    38  		if Debug['u'] != 0 {
    39  			errorexit()
    40  		}
    41  		return
    42  	}
    43  
    44  	// In a __.PKGDEF, we only care about the package name.
    45  	// Don't read all the export data.
    46  	if length > 1000 && whence == Pkgdef {
    47  		length = 1000
    48  	}
    49  
    50  	bdata := make([]byte, length)
    51  	if int64(obj.Bread(f, bdata)) != length {
    52  		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
    53  		if Debug['u'] != 0 {
    54  			errorexit()
    55  		}
    56  		return
    57  	}
    58  	data := string(bdata)
    59  
    60  	// first \n$$ marks beginning of exports - skip rest of line
    61  	p0 = strings.Index(data, "\n$$")
    62  	if p0 < 0 {
    63  		if Debug['u'] != 0 && whence != ArchiveObj {
    64  			Exitf("cannot find export data in %s", filename)
    65  		}
    66  		return
    67  	}
    68  
    69  	// \n$$B marks the beginning of binary export data - don't skip over the B
    70  	p0 += 3
    71  	for p0 < len(data) && data[p0] != '\n' && data[p0] != 'B' {
    72  		p0++
    73  	}
    74  
    75  	// second marks end of exports / beginning of local data
    76  	p1 = strings.Index(data[p0:], "\n$$\n")
    77  	if p1 < 0 && whence == Pkgdef {
    78  		p1 = len(data) - p0
    79  	}
    80  	if p1 < 0 {
    81  		fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename)
    82  		if Debug['u'] != 0 {
    83  			errorexit()
    84  		}
    85  		return
    86  	}
    87  	p1 += p0
    88  
    89  	for p0 < p1 && data[p0] != 'B' && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') {
    90  		p0++
    91  	}
    92  	// don't check this section if we have binary (B) export data
    93  	// TODO fix this eventually
    94  	if p0 < p1 && data[p0] != 'B' {
    95  		if !strings.HasPrefix(data[p0:], "package ") {
    96  			fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:])
    97  			if Debug['u'] != 0 {
    98  				errorexit()
    99  			}
   100  			return
   101  		}
   102  
   103  		p0 += 8
   104  		for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') {
   105  			p0++
   106  		}
   107  		pname := p0
   108  		for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' {
   109  			p0++
   110  		}
   111  		if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) {
   112  			Exitf("load of unsafe package %s", filename)
   113  		}
   114  
   115  		name := data[pname:p0]
   116  		for p0 < p1 && data[p0] != '\n' {
   117  			p0++
   118  		}
   119  		if p0 < p1 {
   120  			p0++
   121  		}
   122  
   123  		if pkg == "main" && name != "main" {
   124  			Exitf("%s: not package main (package %s)", filename, name)
   125  		}
   126  	}
   127  
   128  	// __.PKGDEF has no cgo section - those are in the C compiler-generated object files.
   129  	if whence == Pkgdef {
   130  		return
   131  	}
   132  
   133  	// look for cgo section
   134  	p0 = strings.Index(data[p1:], "\n$$  // cgo")
   135  	if p0 >= 0 {
   136  		p0 += p1
   137  		i := strings.IndexByte(data[p0+1:], '\n')
   138  		if i < 0 {
   139  			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
   140  			if Debug['u'] != 0 {
   141  				errorexit()
   142  			}
   143  			return
   144  		}
   145  		p0 += 1 + i
   146  
   147  		p1 = strings.Index(data[p0:], "\n$$")
   148  		if p1 < 0 {
   149  			p1 = strings.Index(data[p0:], "\n!\n")
   150  		}
   151  		if p1 < 0 {
   152  			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
   153  			if Debug['u'] != 0 {
   154  				errorexit()
   155  			}
   156  			return
   157  		}
   158  		p1 += p0
   159  
   160  		loadcgo(filename, pkg, data[p0:p1])
   161  	}
   162  }
   163  
   164  func loadcgo(file string, pkg string, p string) {
   165  	var next string
   166  	var q string
   167  	var f []string
   168  	var local string
   169  	var remote string
   170  	var lib string
   171  	var s *LSym
   172  
   173  	p0 := ""
   174  	for ; p != ""; p = next {
   175  		if i := strings.Index(p, "\n"); i >= 0 {
   176  			p, next = p[:i], p[i+1:]
   177  		} else {
   178  			next = ""
   179  		}
   180  
   181  		p0 = p // save for error message
   182  		f = tokenize(p)
   183  		if len(f) == 0 {
   184  			continue
   185  		}
   186  
   187  		if f[0] == "cgo_import_dynamic" {
   188  			if len(f) < 2 || len(f) > 4 {
   189  				goto err
   190  			}
   191  
   192  			local = f[1]
   193  			remote = local
   194  			if len(f) > 2 {
   195  				remote = f[2]
   196  			}
   197  			lib = ""
   198  			if len(f) > 3 {
   199  				lib = f[3]
   200  			}
   201  
   202  			if Debug['d'] != 0 {
   203  				fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
   204  				nerrors++
   205  				return
   206  			}
   207  
   208  			if local == "_" && remote == "_" {
   209  				// allow #pragma dynimport _ _ "foo.so"
   210  				// to force a link of foo.so.
   211  				havedynamic = 1
   212  
   213  				if HEADTYPE == obj.Hdarwin {
   214  					Machoadddynlib(lib)
   215  				} else {
   216  					dynlib = append(dynlib, lib)
   217  				}
   218  				continue
   219  			}
   220  
   221  			local = expandpkg(local, pkg)
   222  			q = ""
   223  			if i := strings.Index(remote, "#"); i >= 0 {
   224  				remote, q = remote[:i], remote[i+1:]
   225  			}
   226  			s = Linklookup(Ctxt, local, 0)
   227  			if local != f[1] {
   228  			}
   229  			if s.Type == 0 || s.Type == obj.SXREF || s.Type == obj.SHOSTOBJ {
   230  				s.Dynimplib = lib
   231  				s.Extname = remote
   232  				s.Dynimpvers = q
   233  				if s.Type != obj.SHOSTOBJ {
   234  					s.Type = obj.SDYNIMPORT
   235  				}
   236  				havedynamic = 1
   237  			}
   238  
   239  			continue
   240  		}
   241  
   242  		if f[0] == "cgo_import_static" {
   243  			if len(f) != 2 {
   244  				goto err
   245  			}
   246  			local = f[1]
   247  			s = Linklookup(Ctxt, local, 0)
   248  			s.Type = obj.SHOSTOBJ
   249  			s.Size = 0
   250  			continue
   251  		}
   252  
   253  		if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" {
   254  			if len(f) < 2 || len(f) > 3 {
   255  				goto err
   256  			}
   257  			local = f[1]
   258  			if len(f) > 2 {
   259  				remote = f[2]
   260  			} else {
   261  				remote = local
   262  			}
   263  			local = expandpkg(local, pkg)
   264  			s = Linklookup(Ctxt, local, 0)
   265  
   266  			switch Buildmode {
   267  			case BuildmodeCShared, BuildmodeCArchive:
   268  				if s == Linklookup(Ctxt, "main", 0) {
   269  					continue
   270  				}
   271  			}
   272  
   273  			// export overrides import, for openbsd/cgo.
   274  			// see issue 4878.
   275  			if s.Dynimplib != "" {
   276  				s.Dynimplib = ""
   277  				s.Extname = ""
   278  				s.Dynimpvers = ""
   279  				s.Type = 0
   280  			}
   281  
   282  			if s.Cgoexport == 0 {
   283  				s.Extname = remote
   284  				dynexp = append(dynexp, s)
   285  			} else if s.Extname != remote {
   286  				fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname, remote)
   287  				nerrors++
   288  				return
   289  			}
   290  
   291  			if f[0] == "cgo_export_static" {
   292  				s.Cgoexport |= CgoExportStatic
   293  			} else {
   294  				s.Cgoexport |= CgoExportDynamic
   295  			}
   296  			if local != f[1] {
   297  			}
   298  			continue
   299  		}
   300  
   301  		if f[0] == "cgo_dynamic_linker" {
   302  			if len(f) != 2 {
   303  				goto err
   304  			}
   305  
   306  			if Debug['I'] == 0 {
   307  				if interpreter != "" && interpreter != f[1] {
   308  					fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
   309  					nerrors++
   310  					return
   311  				}
   312  
   313  				interpreter = f[1]
   314  			}
   315  
   316  			continue
   317  		}
   318  
   319  		if f[0] == "cgo_ldflag" {
   320  			if len(f) != 2 {
   321  				goto err
   322  			}
   323  			ldflag = append(ldflag, f[1])
   324  			continue
   325  		}
   326  	}
   327  
   328  	return
   329  
   330  err:
   331  	fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0)
   332  	nerrors++
   333  }
   334  
   335  var seenlib = make(map[string]bool)
   336  
   337  func adddynlib(lib string) {
   338  	if seenlib[lib] || Linkmode == LinkExternal {
   339  		return
   340  	}
   341  	seenlib[lib] = true
   342  
   343  	if Iself {
   344  		s := Linklookup(Ctxt, ".dynstr", 0)
   345  		if s.Size == 0 {
   346  			Addstring(s, "")
   347  		}
   348  		Elfwritedynent(Linklookup(Ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
   349  	} else {
   350  		Diag("adddynlib: unsupported binary format")
   351  	}
   352  }
   353  
   354  func Adddynsym(ctxt *Link, s *LSym) {
   355  	if s.Dynid >= 0 || Linkmode == LinkExternal {
   356  		return
   357  	}
   358  
   359  	if Iself {
   360  		Elfadddynsym(ctxt, s)
   361  	} else if HEADTYPE == obj.Hdarwin {
   362  		Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
   363  	} else if HEADTYPE == obj.Hwindows {
   364  		// already taken care of
   365  	} else {
   366  		Diag("adddynsym: unsupported binary format")
   367  	}
   368  }
   369  
   370  var markq *LSym
   371  
   372  var emarkq *LSym
   373  
   374  func mark1(s *LSym, parent *LSym) {
   375  	if s == nil || s.Reachable {
   376  		return
   377  	}
   378  	if strings.HasPrefix(s.Name, "go.weak.") {
   379  		return
   380  	}
   381  	s.Reachable = true
   382  	s.Reachparent = parent
   383  	if markq == nil {
   384  		markq = s
   385  	} else {
   386  		emarkq.Queue = s
   387  	}
   388  	emarkq = s
   389  }
   390  
   391  func mark(s *LSym) {
   392  	mark1(s, nil)
   393  }
   394  
   395  func markflood() {
   396  	var a *Auto
   397  	var i int
   398  
   399  	for s := markq; s != nil; s = s.Queue {
   400  		if s.Type == obj.STEXT {
   401  			if Debug['v'] > 1 {
   402  				fmt.Fprintf(&Bso, "marktext %s\n", s.Name)
   403  			}
   404  			for a = s.Autom; a != nil; a = a.Link {
   405  				mark1(a.Gotype, s)
   406  			}
   407  		}
   408  
   409  		for i = 0; i < len(s.R); i++ {
   410  			mark1(s.R[i].Sym, s)
   411  		}
   412  		if s.Pcln != nil {
   413  			for i = 0; i < s.Pcln.Nfuncdata; i++ {
   414  				mark1(s.Pcln.Funcdata[i], s)
   415  			}
   416  		}
   417  
   418  		mark1(s.Gotype, s)
   419  		mark1(s.Sub, s)
   420  		mark1(s.Outer, s)
   421  	}
   422  }
   423  
   424  var markextra = []string{
   425  	"runtime.morestack",
   426  	"runtime.morestackx",
   427  	"runtime.morestack00",
   428  	"runtime.morestack10",
   429  	"runtime.morestack01",
   430  	"runtime.morestack11",
   431  	"runtime.morestack8",
   432  	"runtime.morestack16",
   433  	"runtime.morestack24",
   434  	"runtime.morestack32",
   435  	"runtime.morestack40",
   436  	"runtime.morestack48",
   437  	// on arm, lock in the div/mod helpers too
   438  	"_div",
   439  	"_divu",
   440  	"_mod",
   441  	"_modu",
   442  }
   443  
   444  func deadcode() {
   445  	if Debug['v'] != 0 {
   446  		fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime())
   447  	}
   448  
   449  	if Buildmode == BuildmodeShared {
   450  		// Mark all symbols defined in this library as reachable when
   451  		// building a shared library.
   452  		for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   453  			if s.Type != 0 && s.Type != obj.SDYNIMPORT {
   454  				mark(s)
   455  			}
   456  		}
   457  		markflood()
   458  	} else {
   459  		mark(Linklookup(Ctxt, INITENTRY, 0))
   460  		if Linkshared && Buildmode == BuildmodeExe {
   461  			mark(Linkrlookup(Ctxt, "main.main", 0))
   462  			mark(Linkrlookup(Ctxt, "main.init", 0))
   463  		}
   464  		for i := 0; i < len(markextra); i++ {
   465  			mark(Linklookup(Ctxt, markextra[i], 0))
   466  		}
   467  
   468  		for i := 0; i < len(dynexp); i++ {
   469  			mark(dynexp[i])
   470  		}
   471  		markflood()
   472  
   473  		// keep each beginning with 'typelink.' if the symbol it points at is being kept.
   474  		for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   475  			if strings.HasPrefix(s.Name, "go.typelink.") {
   476  				s.Reachable = len(s.R) == 1 && s.R[0].Sym.Reachable
   477  			}
   478  		}
   479  
   480  		// remove dead text but keep file information (z symbols).
   481  		var last *LSym
   482  
   483  		for s := Ctxt.Textp; s != nil; s = s.Next {
   484  			if !s.Reachable {
   485  				continue
   486  			}
   487  
   488  			// NOTE: Removing s from old textp and adding to new, shorter textp.
   489  			if last == nil {
   490  				Ctxt.Textp = s
   491  			} else {
   492  				last.Next = s
   493  			}
   494  			last = s
   495  		}
   496  
   497  		if last == nil {
   498  			Ctxt.Textp = nil
   499  			Ctxt.Etextp = nil
   500  		} else {
   501  			last.Next = nil
   502  			Ctxt.Etextp = last
   503  		}
   504  	}
   505  
   506  	for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   507  		if strings.HasPrefix(s.Name, "go.weak.") {
   508  			s.Special = 1 // do not lay out in data segment
   509  			s.Reachable = true
   510  			s.Hide = 1
   511  		}
   512  	}
   513  
   514  	// record field tracking references
   515  	var buf bytes.Buffer
   516  	var p *LSym
   517  	for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   518  		if strings.HasPrefix(s.Name, "go.track.") {
   519  			s.Special = 1 // do not lay out in data segment
   520  			s.Hide = 1
   521  			if s.Reachable {
   522  				buf.WriteString(s.Name[9:])
   523  				for p = s.Reachparent; p != nil; p = p.Reachparent {
   524  					buf.WriteString("\t")
   525  					buf.WriteString(p.Name)
   526  				}
   527  				buf.WriteString("\n")
   528  			}
   529  
   530  			s.Type = obj.SCONST
   531  			s.Value = 0
   532  		}
   533  	}
   534  
   535  	if tracksym == "" {
   536  		return
   537  	}
   538  	s := Linklookup(Ctxt, tracksym, 0)
   539  	if !s.Reachable {
   540  		return
   541  	}
   542  	addstrdata(tracksym, buf.String())
   543  }
   544  
   545  func doweak() {
   546  	var t *LSym
   547  
   548  	// resolve weak references only if
   549  	// target symbol will be in binary anyway.
   550  	for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   551  		if strings.HasPrefix(s.Name, "go.weak.") {
   552  			t = Linkrlookup(Ctxt, s.Name[8:], int(s.Version))
   553  			if t != nil && t.Type != 0 && t.Reachable {
   554  				s.Value = t.Value
   555  				s.Type = t.Type
   556  				s.Outer = t
   557  			} else {
   558  				s.Type = obj.SCONST
   559  				s.Value = 0
   560  			}
   561  
   562  			continue
   563  		}
   564  	}
   565  }
   566  
   567  func addexport() {
   568  	if HEADTYPE == obj.Hdarwin {
   569  		return
   570  	}
   571  
   572  	for _, exp := range dynexp {
   573  		Adddynsym(Ctxt, exp)
   574  	}
   575  	for _, lib := range dynlib {
   576  		adddynlib(lib)
   577  	}
   578  }
   579  
   580  type Pkg struct {
   581  	mark    bool
   582  	checked bool
   583  	path    string
   584  	impby   []*Pkg
   585  }
   586  
   587  var (
   588  	// pkgmap records the imported-by relationship between packages.
   589  	// Entries are keyed by package path (e.g., "runtime" or "net/url").
   590  	pkgmap = map[string]*Pkg{}
   591  
   592  	pkgall []*Pkg
   593  )
   594  
   595  func lookupPkg(path string) *Pkg {
   596  	if p, ok := pkgmap[path]; ok {
   597  		return p
   598  	}
   599  	p := &Pkg{path: path}
   600  	pkgmap[path] = p
   601  	pkgall = append(pkgall, p)
   602  	return p
   603  }
   604  
   605  // imported records that package pkg imports package imp.
   606  func imported(pkg, imp string) {
   607  	// everyone imports runtime, even runtime.
   608  	if imp == "runtime" {
   609  		return
   610  	}
   611  
   612  	p := lookupPkg(pkg)
   613  	i := lookupPkg(imp)
   614  	i.impby = append(i.impby, p)
   615  }
   616  
   617  func (p *Pkg) cycle() *Pkg {
   618  	if p.checked {
   619  		return nil
   620  	}
   621  
   622  	if p.mark {
   623  		nerrors++
   624  		fmt.Printf("import cycle:\n")
   625  		fmt.Printf("\t%s\n", p.path)
   626  		return p
   627  	}
   628  
   629  	p.mark = true
   630  	for _, q := range p.impby {
   631  		if bad := q.cycle(); bad != nil {
   632  			p.mark = false
   633  			p.checked = true
   634  			fmt.Printf("\timports %s\n", p.path)
   635  			if bad == p {
   636  				return nil
   637  			}
   638  			return bad
   639  		}
   640  	}
   641  
   642  	p.checked = true
   643  	p.mark = false
   644  	return nil
   645  }
   646  
   647  func importcycles() {
   648  	for _, p := range pkgall {
   649  		p.cycle()
   650  	}
   651  }
   652  
   653  func setlinkmode(arg string) {
   654  	if arg == "internal" {
   655  		Linkmode = LinkInternal
   656  	} else if arg == "external" {
   657  		Linkmode = LinkExternal
   658  	} else if arg == "auto" {
   659  		Linkmode = LinkAuto
   660  	} else {
   661  		Exitf("unknown link mode -linkmode %s", arg)
   662  	}
   663  }