Source file src/cmd/link/internal/ld/go.go

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

View as plain text