github.com/bir3/gocompiler@v0.3.205/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  	"github.com/bir3/gocompiler/src/cmd/internal/bio"
    11  	"github.com/bir3/gocompiler/src/cmd/internal/obj"
    12  	"github.com/bir3/gocompiler/src/cmd/internal/objabi"
    13  	"github.com/bir3/gocompiler/src/cmd/internal/sys"
    14  	"github.com/bir3/gocompiler/src/cmd/link/internal/loader"
    15  	"github.com/bir3/gocompiler/src/cmd/link/internal/sym"
    16  	"debug/elf"
    17  	"encoding/json"
    18  	"fmt"
    19  	"io"
    20  	"os"
    21  	"sort"
    22  	"strconv"
    23  	"strings"
    24  )
    25  
    26  // go-specific code shared across loaders (5l, 6l, 8l).
    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  func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) {
    34  	if *flagG {
    35  		return
    36  	}
    37  
    38  	if int64(int(length)) != length {
    39  		fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
    40  		return
    41  	}
    42  
    43  	bdata := make([]byte, length)
    44  	if _, err := io.ReadFull(f, bdata); err != nil {
    45  		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
    46  		return
    47  	}
    48  	data := string(bdata)
    49  
    50  	// process header lines
    51  	for data != "" {
    52  		var line string
    53  		if i := strings.Index(data, "\n"); i >= 0 {
    54  			line, data = data[:i], data[i+1:]
    55  		} else {
    56  			line, data = data, ""
    57  		}
    58  		if line == "main" {
    59  			lib.Main = true
    60  		}
    61  		if line == "" {
    62  			break
    63  		}
    64  	}
    65  
    66  	// look for cgo section
    67  	p0 := strings.Index(data, "\n$$  // cgo")
    68  	var p1 int
    69  	if p0 >= 0 {
    70  		p0 += p1
    71  		i := strings.IndexByte(data[p0+1:], '\n')
    72  		if i < 0 {
    73  			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
    74  			return
    75  		}
    76  		p0 += 1 + i
    77  
    78  		p1 = strings.Index(data[p0:], "\n$$")
    79  		if p1 < 0 {
    80  			p1 = strings.Index(data[p0:], "\n!\n")
    81  		}
    82  		if p1 < 0 {
    83  			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
    84  			return
    85  		}
    86  		p1 += p0
    87  		loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
    88  	}
    89  }
    90  
    91  func loadcgo(ctxt *Link, file string, pkg string, p string) {
    92  	var directives [][]string
    93  	if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
    94  		fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
    95  		nerrors++
    96  		return
    97  	}
    98  
    99  	// Record the directives. We'll process them later after Symbols are created.
   100  	ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
   101  }
   102  
   103  // Set symbol attributes or flags based on cgo directives.
   104  // Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'.
   105  func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) {
   106  	l := ctxt.loader
   107  	for _, f := range directives {
   108  		switch f[0] {
   109  		case "cgo_import_dynamic":
   110  			if len(f) < 2 || len(f) > 4 {
   111  				break
   112  			}
   113  
   114  			local := f[1]
   115  			remote := local
   116  			if len(f) > 2 {
   117  				remote = f[2]
   118  			}
   119  			lib := ""
   120  			if len(f) > 3 {
   121  				lib = f[3]
   122  			}
   123  
   124  			if *FlagD {
   125  				fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
   126  				nerrors++
   127  				return
   128  			}
   129  
   130  			if local == "_" && remote == "_" {
   131  				// allow #pragma dynimport _ _ "foo.so"
   132  				// to force a link of foo.so.
   133  				havedynamic = 1
   134  
   135  				if ctxt.HeadType == objabi.Hdarwin {
   136  					machoadddynlib(lib, ctxt.LinkMode)
   137  				} else {
   138  					dynlib = append(dynlib, lib)
   139  				}
   140  				continue
   141  			}
   142  
   143  			q := ""
   144  			if i := strings.Index(remote, "#"); i >= 0 {
   145  				remote, q = remote[:i], remote[i+1:]
   146  			}
   147  			s := l.LookupOrCreateSym(local, 0)
   148  			st := l.SymType(s)
   149  			if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ {
   150  				l.SetSymDynimplib(s, lib)
   151  				l.SetSymExtname(s, remote)
   152  				l.SetSymDynimpvers(s, q)
   153  				if st != sym.SHOSTOBJ {
   154  					su := l.MakeSymbolUpdater(s)
   155  					su.SetType(sym.SDYNIMPORT)
   156  				} else {
   157  					hostObjSyms[s] = struct{}{}
   158  				}
   159  				havedynamic = 1
   160  				if lib != "" && ctxt.IsDarwin() {
   161  					machoadddynlib(lib, ctxt.LinkMode)
   162  				}
   163  			}
   164  
   165  			continue
   166  
   167  		case "cgo_import_static":
   168  			if len(f) != 2 {
   169  				break
   170  			}
   171  			local := f[1]
   172  
   173  			s := l.LookupOrCreateSym(local, 0)
   174  			su := l.MakeSymbolUpdater(s)
   175  			su.SetType(sym.SHOSTOBJ)
   176  			su.SetSize(0)
   177  			hostObjSyms[s] = struct{}{}
   178  			continue
   179  
   180  		case "cgo_export_static", "cgo_export_dynamic":
   181  			if len(f) < 2 || len(f) > 4 {
   182  				break
   183  			}
   184  			local := f[1]
   185  			remote := local
   186  			if len(f) > 2 {
   187  				remote = f[2]
   188  			}
   189  			// The compiler adds a fourth argument giving
   190  			// the definition ABI of function symbols.
   191  			abi := obj.ABI0
   192  			if len(f) > 3 {
   193  				var ok bool
   194  				abi, ok = obj.ParseABI(f[3])
   195  				if !ok {
   196  					fmt.Fprintf(os.Stderr, "%s: bad ABI in cgo_export directive %s\n", os.Args[0], f)
   197  					nerrors++
   198  					return
   199  				}
   200  			}
   201  
   202  			s := l.LookupOrCreateSym(local, sym.ABIToVersion(abi))
   203  
   204  			if l.SymType(s) == sym.SHOSTOBJ {
   205  				hostObjSyms[s] = struct{}{}
   206  			}
   207  
   208  			switch ctxt.BuildMode {
   209  			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
   210  				if s == l.Lookup("main", 0) {
   211  					continue
   212  				}
   213  			}
   214  
   215  			// export overrides import, for openbsd/cgo.
   216  			// see issue 4878.
   217  			if l.SymDynimplib(s) != "" {
   218  				l.SetSymDynimplib(s, "")
   219  				l.SetSymDynimpvers(s, "")
   220  				l.SetSymExtname(s, "")
   221  				var su *loader.SymbolBuilder
   222  				su = l.MakeSymbolUpdater(s)
   223  				su.SetType(0)
   224  			}
   225  
   226  			if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) {
   227  				l.SetSymExtname(s, remote)
   228  			} else if l.SymExtname(s) != remote {
   229  				fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote)
   230  				nerrors++
   231  				return
   232  			}
   233  
   234  			// Mark exported symbols and also add them to
   235  			// the lists used for roots in the deadcode pass.
   236  			if f[0] == "cgo_export_static" {
   237  				if ctxt.LinkMode == LinkExternal && !l.AttrCgoExportStatic(s) {
   238  					// Static cgo exports appear
   239  					// in the exported symbol table.
   240  					ctxt.dynexp = append(ctxt.dynexp, s)
   241  				}
   242  				if ctxt.LinkMode == LinkInternal {
   243  					// For internal linking, we're
   244  					// responsible for resolving
   245  					// relocations from host objects.
   246  					// Record the right Go symbol
   247  					// version to use.
   248  					l.AddCgoExport(s)
   249  				}
   250  				l.SetAttrCgoExportStatic(s, true)
   251  			} else {
   252  				if ctxt.LinkMode == LinkInternal && !l.AttrCgoExportDynamic(s) {
   253  					// Dynamic cgo exports appear
   254  					// in the exported symbol table.
   255  					ctxt.dynexp = append(ctxt.dynexp, s)
   256  				}
   257  				l.SetAttrCgoExportDynamic(s, true)
   258  			}
   259  
   260  			continue
   261  
   262  		case "cgo_dynamic_linker":
   263  			if len(f) != 2 {
   264  				break
   265  			}
   266  
   267  			if *flagInterpreter == "" {
   268  				if interpreter != "" && interpreter != f[1] {
   269  					fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
   270  					nerrors++
   271  					return
   272  				}
   273  
   274  				interpreter = f[1]
   275  			}
   276  			continue
   277  
   278  		case "cgo_ldflag":
   279  			if len(f) != 2 {
   280  				break
   281  			}
   282  			ldflag = append(ldflag, f[1])
   283  			continue
   284  		}
   285  
   286  		fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
   287  		nerrors++
   288  	}
   289  	return
   290  }
   291  
   292  // openbsdTrimLibVersion indicates whether a shared library is
   293  // versioned and if it is, returns the unversioned name. The
   294  // OpenBSD library naming scheme is lib<name>.so.<major>.<minor>
   295  func openbsdTrimLibVersion(lib string) (string, bool) {
   296  	parts := strings.Split(lib, ".")
   297  	if len(parts) != 4 {
   298  		return "", false
   299  	}
   300  	if parts[1] != "so" {
   301  		return "", false
   302  	}
   303  	if _, err := strconv.Atoi(parts[2]); err != nil {
   304  		return "", false
   305  	}
   306  	if _, err := strconv.Atoi(parts[3]); err != nil {
   307  		return "", false
   308  	}
   309  	return fmt.Sprintf("%s.%s", parts[0], parts[1]), true
   310  }
   311  
   312  // dedupLibrariesOpenBSD dedups a list of shared libraries, treating versioned
   313  // and unversioned libraries as equivalents. Versioned libraries are preferred
   314  // and retained over unversioned libraries. This avoids the situation where
   315  // the use of cgo results in a DT_NEEDED for a versioned library (for example,
   316  // libc.so.96.1), while a dynamic import specifies an unversioned library (for
   317  // example, libc.so) - this would otherwise result in two DT_NEEDED entries
   318  // for the same library, resulting in a failure when ld.so attempts to load
   319  // the Go binary.
   320  func dedupLibrariesOpenBSD(ctxt *Link, libs []string) []string {
   321  	libraries := make(map[string]string)
   322  	for _, lib := range libs {
   323  		if name, ok := openbsdTrimLibVersion(lib); ok {
   324  			// Record unversioned name as seen.
   325  			seenlib[name] = true
   326  			libraries[name] = lib
   327  		} else if _, ok := libraries[lib]; !ok {
   328  			libraries[lib] = lib
   329  		}
   330  	}
   331  
   332  	libs = nil
   333  	for _, lib := range libraries {
   334  		libs = append(libs, lib)
   335  	}
   336  	sort.Strings(libs)
   337  
   338  	return libs
   339  }
   340  
   341  func dedupLibraries(ctxt *Link, libs []string) []string {
   342  	if ctxt.Target.IsOpenbsd() {
   343  		return dedupLibrariesOpenBSD(ctxt, libs)
   344  	}
   345  	return libs
   346  }
   347  
   348  var seenlib = make(map[string]bool)
   349  
   350  func adddynlib(ctxt *Link, lib string) {
   351  	if seenlib[lib] || ctxt.LinkMode == LinkExternal {
   352  		return
   353  	}
   354  	seenlib[lib] = true
   355  
   356  	if ctxt.IsELF {
   357  		dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr)
   358  		if dsu.Size() == 0 {
   359  			dsu.Addstring("")
   360  		}
   361  		du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic)
   362  		Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib)))
   363  	} else {
   364  		Errorf(nil, "adddynlib: unsupported binary format")
   365  	}
   366  }
   367  
   368  func Adddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
   369  	if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal {
   370  		return
   371  	}
   372  
   373  	if target.IsELF {
   374  		elfadddynsym(ldr, target, syms, s)
   375  	} else if target.HeadType == objabi.Hdarwin {
   376  		ldr.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s))
   377  	} else if target.HeadType == objabi.Hwindows {
   378  		// already taken care of
   379  	} else {
   380  		ldr.Errorf(s, "adddynsym: unsupported binary format")
   381  	}
   382  }
   383  
   384  func fieldtrack(arch *sys.Arch, l *loader.Loader) {
   385  	var buf strings.Builder
   386  	for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ {
   387  		if name := l.SymName(i); strings.HasPrefix(name, "go:track.") {
   388  			if l.AttrReachable(i) {
   389  				l.SetAttrSpecial(i, true)
   390  				l.SetAttrNotInSymbolTable(i, true)
   391  				buf.WriteString(name[9:])
   392  				for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] {
   393  					buf.WriteString("\t")
   394  					buf.WriteString(l.SymName(p))
   395  				}
   396  				buf.WriteString("\n")
   397  			}
   398  		}
   399  	}
   400  	l.Reachparent = nil // we are done with it
   401  	if *flagFieldTrack == "" {
   402  		return
   403  	}
   404  	s := l.Lookup(*flagFieldTrack, 0)
   405  	if s == 0 || !l.AttrReachable(s) {
   406  		return
   407  	}
   408  	bld := l.MakeSymbolUpdater(s)
   409  	bld.SetType(sym.SDATA)
   410  	addstrdata(arch, l, *flagFieldTrack, buf.String())
   411  }
   412  
   413  func (ctxt *Link) addexport() {
   414  	// Track undefined external symbols during external link.
   415  	if ctxt.LinkMode == LinkExternal {
   416  		for _, s := range ctxt.Textp {
   417  			if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) {
   418  				continue
   419  			}
   420  			relocs := ctxt.loader.Relocs(s)
   421  			for i := 0; i < relocs.Count(); i++ {
   422  				if rs := relocs.At(i).Sym(); rs != 0 {
   423  					if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) {
   424  						// sanity check
   425  						if len(ctxt.loader.Data(rs)) != 0 {
   426  							panic("expected no data on undef symbol")
   427  						}
   428  						su := ctxt.loader.MakeSymbolUpdater(rs)
   429  						su.SetType(sym.SUNDEFEXT)
   430  					}
   431  				}
   432  			}
   433  		}
   434  	}
   435  
   436  	// TODO(aix)
   437  	if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
   438  		return
   439  	}
   440  
   441  	// Add dynamic symbols.
   442  	for _, s := range ctxt.dynexp {
   443  		// Consistency check.
   444  		if !ctxt.loader.AttrReachable(s) {
   445  			panic("dynexp entry not reachable")
   446  		}
   447  
   448  		Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, s)
   449  	}
   450  
   451  	for _, lib := range dedupLibraries(ctxt, dynlib) {
   452  		adddynlib(ctxt, lib)
   453  	}
   454  }
   455  
   456  type Pkg struct {
   457  	mark    bool
   458  	checked bool
   459  	path    string
   460  	impby   []*Pkg
   461  }
   462  
   463  var pkgall []*Pkg
   464  
   465  func (p *Pkg) cycle() *Pkg {
   466  	if p.checked {
   467  		return nil
   468  	}
   469  
   470  	if p.mark {
   471  		nerrors++
   472  		fmt.Printf("import cycle:\n")
   473  		fmt.Printf("\t%s\n", p.path)
   474  		return p
   475  	}
   476  
   477  	p.mark = true
   478  	for _, q := range p.impby {
   479  		if bad := q.cycle(); bad != nil {
   480  			p.mark = false
   481  			p.checked = true
   482  			fmt.Printf("\timports %s\n", p.path)
   483  			if bad == p {
   484  				return nil
   485  			}
   486  			return bad
   487  		}
   488  	}
   489  
   490  	p.checked = true
   491  	p.mark = false
   492  	return nil
   493  }
   494  
   495  func importcycles() {
   496  	for _, p := range pkgall {
   497  		p.cycle()
   498  	}
   499  }