github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/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/bio"
    12  	"cmd/internal/objabi"
    13  	"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  // 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  		if *flagU {
    41  			errorexit()
    42  		}
    43  		return
    44  	}
    45  
    46  	bdata := make([]byte, length)
    47  	if _, err := io.ReadFull(f, bdata); err != nil {
    48  		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
    49  		if *flagU {
    50  			errorexit()
    51  		}
    52  		return
    53  	}
    54  	data := string(bdata)
    55  
    56  	// process header lines
    57  	for data != "" {
    58  		var line string
    59  		if i := strings.Index(data, "\n"); i >= 0 {
    60  			line, data = data[:i], data[i+1:]
    61  		} else {
    62  			line, data = data, ""
    63  		}
    64  		if line == "safe" {
    65  			lib.Safe = true
    66  		}
    67  		if line == "main" {
    68  			lib.Main = true
    69  		}
    70  		if line == "" {
    71  			break
    72  		}
    73  	}
    74  
    75  	// look for cgo section
    76  	p0 := strings.Index(data, "\n$$  // cgo")
    77  	var p1 int
    78  	if p0 >= 0 {
    79  		p0 += p1
    80  		i := strings.IndexByte(data[p0+1:], '\n')
    81  		if i < 0 {
    82  			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
    83  			if *flagU {
    84  				errorexit()
    85  			}
    86  			return
    87  		}
    88  		p0 += 1 + i
    89  
    90  		p1 = strings.Index(data[p0:], "\n$$")
    91  		if p1 < 0 {
    92  			p1 = strings.Index(data[p0:], "\n!\n")
    93  		}
    94  		if p1 < 0 {
    95  			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
    96  			if *flagU {
    97  				errorexit()
    98  			}
    99  			return
   100  		}
   101  		p1 += p0
   102  
   103  		loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
   104  	}
   105  }
   106  
   107  func loadcgo(ctxt *Link, file string, pkg string, p string) {
   108  	var directives [][]string
   109  	if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
   110  		fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
   111  		nerrors++
   112  		return
   113  	}
   114  
   115  	for _, f := range directives {
   116  		switch f[0] {
   117  		case "cgo_import_dynamic":
   118  			if len(f) < 2 || len(f) > 4 {
   119  				break
   120  			}
   121  
   122  			local := f[1]
   123  			remote := local
   124  			if len(f) > 2 {
   125  				remote = f[2]
   126  			}
   127  			lib := ""
   128  			if len(f) > 3 {
   129  				lib = f[3]
   130  			}
   131  
   132  			if *FlagD {
   133  				fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
   134  				nerrors++
   135  				return
   136  			}
   137  
   138  			if local == "_" && remote == "_" {
   139  				// allow #pragma dynimport _ _ "foo.so"
   140  				// to force a link of foo.so.
   141  				havedynamic = 1
   142  
   143  				if ctxt.HeadType == objabi.Hdarwin {
   144  					machoadddynlib(lib, ctxt.LinkMode)
   145  				} else {
   146  					dynlib = append(dynlib, lib)
   147  				}
   148  				continue
   149  			}
   150  
   151  			local = expandpkg(local, pkg)
   152  			q := ""
   153  			if i := strings.Index(remote, "#"); i >= 0 {
   154  				remote, q = remote[:i], remote[i+1:]
   155  			}
   156  			s := ctxt.Syms.Lookup(local, 0)
   157  			if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SHOSTOBJ {
   158  				s.SetDynimplib(lib)
   159  				s.SetExtname(remote)
   160  				s.SetDynimpvers(q)
   161  				if s.Type != sym.SHOSTOBJ {
   162  					s.Type = sym.SDYNIMPORT
   163  				}
   164  				havedynamic = 1
   165  			}
   166  			if ctxt.HeadType == objabi.Haix {
   167  				xcoffadddynimpsym(ctxt, s)
   168  			}
   169  
   170  			continue
   171  
   172  		case "cgo_import_static":
   173  			if len(f) != 2 {
   174  				break
   175  			}
   176  			local := f[1]
   177  
   178  			s := ctxt.Syms.Lookup(local, 0)
   179  			s.Type = sym.SHOSTOBJ
   180  			s.Size = 0
   181  			continue
   182  
   183  		case "cgo_export_static", "cgo_export_dynamic":
   184  			if len(f) < 2 || len(f) > 3 {
   185  				break
   186  			}
   187  			local := f[1]
   188  			remote := local
   189  			if len(f) > 2 {
   190  				remote = f[2]
   191  			}
   192  			local = expandpkg(local, pkg)
   193  
   194  			s := ctxt.Syms.Lookup(local, 0)
   195  
   196  			switch ctxt.BuildMode {
   197  			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
   198  				if s == ctxt.Syms.Lookup("main", 0) {
   199  					continue
   200  				}
   201  			}
   202  
   203  			// export overrides import, for openbsd/cgo.
   204  			// see issue 4878.
   205  			if s.Dynimplib() != "" {
   206  				s.ResetDyninfo()
   207  				s.SetExtname("")
   208  				s.Type = 0
   209  			}
   210  
   211  			if !s.Attr.CgoExport() {
   212  				s.SetExtname(remote)
   213  				dynexp = append(dynexp, s)
   214  			} else if s.Extname() != remote {
   215  				fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname(), remote)
   216  				nerrors++
   217  				return
   218  			}
   219  
   220  			if f[0] == "cgo_export_static" {
   221  				s.Attr |= sym.AttrCgoExportStatic
   222  			} else {
   223  				s.Attr |= sym.AttrCgoExportDynamic
   224  			}
   225  			continue
   226  
   227  		case "cgo_dynamic_linker":
   228  			if len(f) != 2 {
   229  				break
   230  			}
   231  
   232  			if *flagInterpreter == "" {
   233  				if interpreter != "" && interpreter != f[1] {
   234  					fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
   235  					nerrors++
   236  					return
   237  				}
   238  
   239  				interpreter = f[1]
   240  			}
   241  			continue
   242  
   243  		case "cgo_ldflag":
   244  			if len(f) != 2 {
   245  				break
   246  			}
   247  			ldflag = append(ldflag, f[1])
   248  			continue
   249  		}
   250  
   251  		fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
   252  		nerrors++
   253  	}
   254  }
   255  
   256  var seenlib = make(map[string]bool)
   257  
   258  func adddynlib(ctxt *Link, lib string) {
   259  	if seenlib[lib] || ctxt.LinkMode == LinkExternal {
   260  		return
   261  	}
   262  	seenlib[lib] = true
   263  
   264  	if ctxt.IsELF {
   265  		s := ctxt.Syms.Lookup(".dynstr", 0)
   266  		if s.Size == 0 {
   267  			Addstring(s, "")
   268  		}
   269  		Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
   270  	} else {
   271  		Errorf(nil, "adddynlib: unsupported binary format")
   272  	}
   273  }
   274  
   275  func Adddynsym(ctxt *Link, s *sym.Symbol) {
   276  	if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal {
   277  		return
   278  	}
   279  
   280  	if ctxt.IsELF {
   281  		elfadddynsym(ctxt, s)
   282  	} else if ctxt.HeadType == objabi.Hdarwin {
   283  		Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname())
   284  	} else if ctxt.HeadType == objabi.Hwindows {
   285  		// already taken care of
   286  	} else {
   287  		Errorf(s, "adddynsym: unsupported binary format")
   288  	}
   289  }
   290  
   291  func fieldtrack(ctxt *Link) {
   292  	// record field tracking references
   293  	var buf bytes.Buffer
   294  	for _, s := range ctxt.Syms.Allsym {
   295  		if strings.HasPrefix(s.Name, "go.track.") {
   296  			s.Attr |= sym.AttrSpecial // do not lay out in data segment
   297  			s.Attr |= sym.AttrNotInSymbolTable
   298  			if s.Attr.Reachable() {
   299  				buf.WriteString(s.Name[9:])
   300  				for p := ctxt.Reachparent[s]; p != nil; p = ctxt.Reachparent[p] {
   301  					buf.WriteString("\t")
   302  					buf.WriteString(p.Name)
   303  				}
   304  				buf.WriteString("\n")
   305  			}
   306  
   307  			s.Type = sym.SCONST
   308  			s.Value = 0
   309  		}
   310  	}
   311  
   312  	if *flagFieldTrack == "" {
   313  		return
   314  	}
   315  	s := ctxt.Syms.ROLookup(*flagFieldTrack, 0)
   316  	if s == nil || !s.Attr.Reachable() {
   317  		return
   318  	}
   319  	s.Type = sym.SDATA
   320  	addstrdata(ctxt, *flagFieldTrack, buf.String())
   321  }
   322  
   323  func (ctxt *Link) addexport() {
   324  	// TODO(aix)
   325  	if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
   326  		return
   327  	}
   328  
   329  	for _, exp := range dynexp {
   330  		Adddynsym(ctxt, exp)
   331  	}
   332  	for _, lib := range dynlib {
   333  		adddynlib(ctxt, lib)
   334  	}
   335  }
   336  
   337  type Pkg struct {
   338  	mark    bool
   339  	checked bool
   340  	path    string
   341  	impby   []*Pkg
   342  }
   343  
   344  var pkgall []*Pkg
   345  
   346  func (p *Pkg) cycle() *Pkg {
   347  	if p.checked {
   348  		return nil
   349  	}
   350  
   351  	if p.mark {
   352  		nerrors++
   353  		fmt.Printf("import cycle:\n")
   354  		fmt.Printf("\t%s\n", p.path)
   355  		return p
   356  	}
   357  
   358  	p.mark = true
   359  	for _, q := range p.impby {
   360  		if bad := q.cycle(); bad != nil {
   361  			p.mark = false
   362  			p.checked = true
   363  			fmt.Printf("\timports %s\n", p.path)
   364  			if bad == p {
   365  				return nil
   366  			}
   367  			return bad
   368  		}
   369  	}
   370  
   371  	p.checked = true
   372  	p.mark = false
   373  	return nil
   374  }
   375  
   376  func importcycles() {
   377  	for _, p := range pkgall {
   378  		p.cycle()
   379  	}
   380  }