github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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  	"github.com/gagliardetto/golang-go/cmd/internal/bio"
    12  	"github.com/gagliardetto/golang-go/cmd/internal/objabi"
    13  	"github.com/gagliardetto/golang-go/cmd/link/internal/sym"
    14  	"encoding/json"
    15  	"fmt"
    16  	"io"
    17  	"os"
    18  	"strings"
    19  )
    20  
    21  // go-specific code shared across loaders (5l, 6l, 8l).
    22  
    23  // replace all "". with pkg.
    24  func expandpkg(t0 string, pkg string) string {
    25  	return strings.Replace(t0, `"".`, pkg+".", -1)
    26  }
    27  
    28  func resolveABIAlias(s *sym.Symbol) *sym.Symbol {
    29  	if s.Type != sym.SABIALIAS {
    30  		return s
    31  	}
    32  	target := s.R[0].Sym
    33  	if target.Type == sym.SABIALIAS {
    34  		panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", s, target))
    35  	}
    36  	return target
    37  }
    38  
    39  // TODO:
    40  //	generate debugging section in binary.
    41  //	once the dust settles, try to move some code to
    42  //		libmach, so that other linkers and ar can share.
    43  
    44  func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) {
    45  	if *flagG {
    46  		return
    47  	}
    48  
    49  	if int64(int(length)) != length {
    50  		fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
    51  		if *flagU {
    52  			errorexit()
    53  		}
    54  		return
    55  	}
    56  
    57  	bdata := make([]byte, length)
    58  	if _, err := io.ReadFull(f, bdata); err != nil {
    59  		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
    60  		if *flagU {
    61  			errorexit()
    62  		}
    63  		return
    64  	}
    65  	data := string(bdata)
    66  
    67  	// process header lines
    68  	for data != "" {
    69  		var line string
    70  		if i := strings.Index(data, "\n"); i >= 0 {
    71  			line, data = data[:i], data[i+1:]
    72  		} else {
    73  			line, data = data, ""
    74  		}
    75  		if line == "safe" {
    76  			lib.Safe = true
    77  		}
    78  		if line == "main" {
    79  			lib.Main = true
    80  		}
    81  		if line == "" {
    82  			break
    83  		}
    84  	}
    85  
    86  	// look for cgo section
    87  	p0 := strings.Index(data, "\n$$  // cgo")
    88  	var p1 int
    89  	if p0 >= 0 {
    90  		p0 += p1
    91  		i := strings.IndexByte(data[p0+1:], '\n')
    92  		if i < 0 {
    93  			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
    94  			if *flagU {
    95  				errorexit()
    96  			}
    97  			return
    98  		}
    99  		p0 += 1 + i
   100  
   101  		p1 = strings.Index(data[p0:], "\n$$")
   102  		if p1 < 0 {
   103  			p1 = strings.Index(data[p0:], "\n!\n")
   104  		}
   105  		if p1 < 0 {
   106  			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
   107  			if *flagU {
   108  				errorexit()
   109  			}
   110  			return
   111  		}
   112  		p1 += p0
   113  		loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
   114  	}
   115  }
   116  
   117  func loadcgo(ctxt *Link, file string, pkg string, p string) {
   118  	var directives [][]string
   119  	if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
   120  		fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
   121  		nerrors++
   122  		return
   123  	}
   124  
   125  	// Find cgo_export symbols. They are roots in the deadcode pass.
   126  	for _, f := range directives {
   127  		switch f[0] {
   128  		case "cgo_export_static", "cgo_export_dynamic":
   129  			if len(f) < 2 || len(f) > 3 {
   130  				continue
   131  			}
   132  			local := f[1]
   133  			switch ctxt.BuildMode {
   134  			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
   135  				if local == "main" {
   136  					continue
   137  				}
   138  			}
   139  			local = expandpkg(local, pkg)
   140  			if f[0] == "cgo_export_static" {
   141  				ctxt.cgo_export_static[local] = true
   142  			} else {
   143  				ctxt.cgo_export_dynamic[local] = true
   144  			}
   145  		}
   146  	}
   147  
   148  	if *flagNewobj {
   149  		// Record the directives. We'll process them later after Symbols are created.
   150  		ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
   151  	} else {
   152  		setCgoAttr(ctxt, ctxt.Syms.Lookup, file, pkg, directives)
   153  	}
   154  }
   155  
   156  // Set symbol attributes or flags based on cgo directives.
   157  func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, pkg string, directives [][]string) {
   158  	for _, f := range directives {
   159  		switch f[0] {
   160  		case "cgo_import_dynamic":
   161  			if len(f) < 2 || len(f) > 4 {
   162  				break
   163  			}
   164  
   165  			local := f[1]
   166  			remote := local
   167  			if len(f) > 2 {
   168  				remote = f[2]
   169  			}
   170  			lib := ""
   171  			if len(f) > 3 {
   172  				lib = f[3]
   173  			}
   174  
   175  			if *FlagD {
   176  				fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
   177  				nerrors++
   178  				return
   179  			}
   180  
   181  			if local == "_" && remote == "_" {
   182  				// allow #pragma dynimport _ _ "foo.so"
   183  				// to force a link of foo.so.
   184  				havedynamic = 1
   185  
   186  				if ctxt.HeadType == objabi.Hdarwin {
   187  					machoadddynlib(lib, ctxt.LinkMode)
   188  				} else {
   189  					dynlib = append(dynlib, lib)
   190  				}
   191  				continue
   192  			}
   193  
   194  			local = expandpkg(local, pkg)
   195  			q := ""
   196  			if i := strings.Index(remote, "#"); i >= 0 {
   197  				remote, q = remote[:i], remote[i+1:]
   198  			}
   199  			s := lookup(local, 0)
   200  			if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS || s.Type == sym.SHOSTOBJ {
   201  				s.SetDynimplib(lib)
   202  				s.SetExtname(remote)
   203  				s.SetDynimpvers(q)
   204  				if s.Type != sym.SHOSTOBJ {
   205  					s.Type = sym.SDYNIMPORT
   206  				}
   207  				havedynamic = 1
   208  			}
   209  
   210  			continue
   211  
   212  		case "cgo_import_static":
   213  			if len(f) != 2 {
   214  				break
   215  			}
   216  			local := f[1]
   217  
   218  			s := lookup(local, 0)
   219  			s.Type = sym.SHOSTOBJ
   220  			s.Size = 0
   221  			continue
   222  
   223  		case "cgo_export_static", "cgo_export_dynamic":
   224  			if len(f) < 2 || len(f) > 3 {
   225  				break
   226  			}
   227  			local := f[1]
   228  			remote := local
   229  			if len(f) > 2 {
   230  				remote = f[2]
   231  			}
   232  			local = expandpkg(local, pkg)
   233  
   234  			// The compiler arranges for an ABI0 wrapper
   235  			// to be available for all cgo-exported
   236  			// functions. Link.loadlib will resolve any
   237  			// ABI aliases we find here (since we may not
   238  			// yet know it's an alias).
   239  			s := lookup(local, 0)
   240  
   241  			switch ctxt.BuildMode {
   242  			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
   243  				if s == lookup("main", 0) {
   244  					continue
   245  				}
   246  			}
   247  
   248  			// export overrides import, for openbsd/cgo.
   249  			// see issue 4878.
   250  			if s.Dynimplib() != "" {
   251  				s.ResetDyninfo()
   252  				s.SetExtname("")
   253  				s.Type = 0
   254  			}
   255  
   256  			if !s.Attr.CgoExport() {
   257  				s.SetExtname(remote)
   258  			} else if s.Extname() != remote {
   259  				fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname(), remote)
   260  				nerrors++
   261  				return
   262  			}
   263  
   264  			if f[0] == "cgo_export_static" {
   265  				s.Attr |= sym.AttrCgoExportStatic
   266  			} else {
   267  				s.Attr |= sym.AttrCgoExportDynamic
   268  			}
   269  			continue
   270  
   271  		case "cgo_dynamic_linker":
   272  			if len(f) != 2 {
   273  				break
   274  			}
   275  
   276  			if *flagInterpreter == "" {
   277  				if interpreter != "" && interpreter != f[1] {
   278  					fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
   279  					nerrors++
   280  					return
   281  				}
   282  
   283  				interpreter = f[1]
   284  			}
   285  			continue
   286  
   287  		case "cgo_ldflag":
   288  			if len(f) != 2 {
   289  				break
   290  			}
   291  			ldflag = append(ldflag, f[1])
   292  			continue
   293  		}
   294  
   295  		fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
   296  		nerrors++
   297  	}
   298  }
   299  
   300  var seenlib = make(map[string]bool)
   301  
   302  func adddynlib(ctxt *Link, lib string) {
   303  	if seenlib[lib] || ctxt.LinkMode == LinkExternal {
   304  		return
   305  	}
   306  	seenlib[lib] = true
   307  
   308  	if ctxt.IsELF {
   309  		s := ctxt.Syms.Lookup(".dynstr", 0)
   310  		if s.Size == 0 {
   311  			Addstring(s, "")
   312  		}
   313  		Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
   314  	} else {
   315  		Errorf(nil, "adddynlib: unsupported binary format")
   316  	}
   317  }
   318  
   319  func Adddynsym(ctxt *Link, s *sym.Symbol) {
   320  	if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal {
   321  		return
   322  	}
   323  
   324  	if ctxt.IsELF {
   325  		elfadddynsym(ctxt, s)
   326  	} else if ctxt.HeadType == objabi.Hdarwin {
   327  		Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname())
   328  	} else if ctxt.HeadType == objabi.Hwindows {
   329  		// already taken care of
   330  	} else {
   331  		Errorf(s, "adddynsym: unsupported binary format")
   332  	}
   333  }
   334  
   335  func fieldtrack(ctxt *Link) {
   336  	// record field tracking references
   337  	var buf bytes.Buffer
   338  	for _, s := range ctxt.Syms.Allsym {
   339  		if strings.HasPrefix(s.Name, "go.track.") {
   340  			s.Attr |= sym.AttrSpecial // do not lay out in data segment
   341  			s.Attr |= sym.AttrNotInSymbolTable
   342  			if s.Attr.Reachable() {
   343  				buf.WriteString(s.Name[9:])
   344  				for p := ctxt.Reachparent[s]; p != nil; p = ctxt.Reachparent[p] {
   345  					buf.WriteString("\t")
   346  					buf.WriteString(p.Name)
   347  				}
   348  				buf.WriteString("\n")
   349  			}
   350  
   351  			s.Type = sym.SCONST
   352  			s.Value = 0
   353  		}
   354  	}
   355  
   356  	if *flagFieldTrack == "" {
   357  		return
   358  	}
   359  	s := ctxt.Syms.ROLookup(*flagFieldTrack, 0)
   360  	if s == nil || !s.Attr.Reachable() {
   361  		return
   362  	}
   363  	s.Type = sym.SDATA
   364  	addstrdata(ctxt, *flagFieldTrack, buf.String())
   365  }
   366  
   367  func (ctxt *Link) addexport() {
   368  	// Track undefined external symbols during external link.
   369  	if ctxt.LinkMode == LinkExternal {
   370  		for _, s := range ctxt.Syms.Allsym {
   371  			if !s.Attr.Reachable() || s.Attr.Special() || s.Attr.SubSymbol() {
   372  				continue
   373  			}
   374  			if s.Type != sym.STEXT {
   375  				continue
   376  			}
   377  			for i := range s.R {
   378  				r := &s.R[i]
   379  				if r.Sym != nil && r.Sym.Type == sym.Sxxx {
   380  					r.Sym.Type = sym.SUNDEFEXT
   381  				}
   382  			}
   383  		}
   384  	}
   385  
   386  	// TODO(aix)
   387  	if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
   388  		return
   389  	}
   390  
   391  	for _, exp := range dynexp {
   392  		Adddynsym(ctxt, exp)
   393  	}
   394  	for _, lib := range dynlib {
   395  		adddynlib(ctxt, lib)
   396  	}
   397  }
   398  
   399  type Pkg struct {
   400  	mark    bool
   401  	checked bool
   402  	path    string
   403  	impby   []*Pkg
   404  }
   405  
   406  var pkgall []*Pkg
   407  
   408  func (p *Pkg) cycle() *Pkg {
   409  	if p.checked {
   410  		return nil
   411  	}
   412  
   413  	if p.mark {
   414  		nerrors++
   415  		fmt.Printf("import cycle:\n")
   416  		fmt.Printf("\t%s\n", p.path)
   417  		return p
   418  	}
   419  
   420  	p.mark = true
   421  	for _, q := range p.impby {
   422  		if bad := q.cycle(); bad != nil {
   423  			p.mark = false
   424  			p.checked = true
   425  			fmt.Printf("\timports %s\n", p.path)
   426  			if bad == p {
   427  				return nil
   428  			}
   429  			return bad
   430  		}
   431  	}
   432  
   433  	p.checked = true
   434  	p.mark = false
   435  	return nil
   436  }
   437  
   438  func importcycles() {
   439  	for _, p := range pkgall {
   440  		p.cycle()
   441  	}
   442  }