...

Source file src/pkg/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		"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	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	
   114			loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
   115		}
   116	}
   117	
   118	func loadcgo(ctxt *Link, file string, pkg string, p string) {
   119		var directives [][]string
   120		if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
   121			fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
   122			nerrors++
   123			return
   124		}
   125	
   126		for _, f := range directives {
   127			switch f[0] {
   128			case "cgo_import_dynamic":
   129				if len(f) < 2 || len(f) > 4 {
   130					break
   131				}
   132	
   133				local := f[1]
   134				remote := local
   135				if len(f) > 2 {
   136					remote = f[2]
   137				}
   138				lib := ""
   139				if len(f) > 3 {
   140					lib = f[3]
   141				}
   142	
   143				if *FlagD {
   144					fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
   145					nerrors++
   146					return
   147				}
   148	
   149				if local == "_" && remote == "_" {
   150					// allow #pragma dynimport _ _ "foo.so"
   151					// to force a link of foo.so.
   152					havedynamic = 1
   153	
   154					if ctxt.HeadType == objabi.Hdarwin {
   155						machoadddynlib(lib, ctxt.LinkMode)
   156					} else {
   157						dynlib = append(dynlib, lib)
   158					}
   159					continue
   160				}
   161	
   162				local = expandpkg(local, pkg)
   163				q := ""
   164				if i := strings.Index(remote, "#"); i >= 0 {
   165					remote, q = remote[:i], remote[i+1:]
   166				}
   167				s := ctxt.Syms.Lookup(local, 0)
   168				if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SHOSTOBJ {
   169					s.SetDynimplib(lib)
   170					s.SetExtname(remote)
   171					s.SetDynimpvers(q)
   172					if s.Type != sym.SHOSTOBJ {
   173						s.Type = sym.SDYNIMPORT
   174					}
   175					havedynamic = 1
   176				}
   177	
   178				continue
   179	
   180			case "cgo_import_static":
   181				if len(f) != 2 {
   182					break
   183				}
   184				local := f[1]
   185	
   186				s := ctxt.Syms.Lookup(local, 0)
   187				s.Type = sym.SHOSTOBJ
   188				s.Size = 0
   189				continue
   190	
   191			case "cgo_export_static", "cgo_export_dynamic":
   192				if len(f) < 2 || len(f) > 3 {
   193					break
   194				}
   195				local := f[1]
   196				remote := local
   197				if len(f) > 2 {
   198					remote = f[2]
   199				}
   200				local = expandpkg(local, pkg)
   201	
   202				// The compiler arranges for an ABI0 wrapper
   203				// to be available for all cgo-exported
   204				// functions. Link.loadlib will resolve any
   205				// ABI aliases we find here (since we may not
   206				// yet know it's an alias).
   207				s := ctxt.Syms.Lookup(local, 0)
   208	
   209				switch ctxt.BuildMode {
   210				case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
   211					if s == ctxt.Syms.Lookup("main", 0) {
   212						continue
   213					}
   214				}
   215	
   216				// export overrides import, for openbsd/cgo.
   217				// see issue 4878.
   218				if s.Dynimplib() != "" {
   219					s.ResetDyninfo()
   220					s.SetExtname("")
   221					s.Type = 0
   222				}
   223	
   224				if !s.Attr.CgoExport() {
   225					s.SetExtname(remote)
   226					dynexp = append(dynexp, s)
   227				} else if s.Extname() != remote {
   228					fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname(), remote)
   229					nerrors++
   230					return
   231				}
   232	
   233				if f[0] == "cgo_export_static" {
   234					s.Attr |= sym.AttrCgoExportStatic
   235				} else {
   236					s.Attr |= sym.AttrCgoExportDynamic
   237				}
   238				continue
   239	
   240			case "cgo_dynamic_linker":
   241				if len(f) != 2 {
   242					break
   243				}
   244	
   245				if *flagInterpreter == "" {
   246					if interpreter != "" && interpreter != f[1] {
   247						fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
   248						nerrors++
   249						return
   250					}
   251	
   252					interpreter = f[1]
   253				}
   254				continue
   255	
   256			case "cgo_ldflag":
   257				if len(f) != 2 {
   258					break
   259				}
   260				ldflag = append(ldflag, f[1])
   261				continue
   262			}
   263	
   264			fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
   265			nerrors++
   266		}
   267	}
   268	
   269	var seenlib = make(map[string]bool)
   270	
   271	func adddynlib(ctxt *Link, lib string) {
   272		if seenlib[lib] || ctxt.LinkMode == LinkExternal {
   273			return
   274		}
   275		seenlib[lib] = true
   276	
   277		if ctxt.IsELF {
   278			s := ctxt.Syms.Lookup(".dynstr", 0)
   279			if s.Size == 0 {
   280				Addstring(s, "")
   281			}
   282			Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
   283		} else {
   284			Errorf(nil, "adddynlib: unsupported binary format")
   285		}
   286	}
   287	
   288	func Adddynsym(ctxt *Link, s *sym.Symbol) {
   289		if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal {
   290			return
   291		}
   292	
   293		if ctxt.IsELF {
   294			elfadddynsym(ctxt, s)
   295		} else if ctxt.HeadType == objabi.Hdarwin {
   296			Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname())
   297		} else if ctxt.HeadType == objabi.Hwindows {
   298			// already taken care of
   299		} else {
   300			Errorf(s, "adddynsym: unsupported binary format")
   301		}
   302	}
   303	
   304	func fieldtrack(ctxt *Link) {
   305		// record field tracking references
   306		var buf bytes.Buffer
   307		for _, s := range ctxt.Syms.Allsym {
   308			if strings.HasPrefix(s.Name, "go.track.") {
   309				s.Attr |= sym.AttrSpecial // do not lay out in data segment
   310				s.Attr |= sym.AttrNotInSymbolTable
   311				if s.Attr.Reachable() {
   312					buf.WriteString(s.Name[9:])
   313					for p := ctxt.Reachparent[s]; p != nil; p = ctxt.Reachparent[p] {
   314						buf.WriteString("\t")
   315						buf.WriteString(p.Name)
   316					}
   317					buf.WriteString("\n")
   318				}
   319	
   320				s.Type = sym.SCONST
   321				s.Value = 0
   322			}
   323		}
   324	
   325		if *flagFieldTrack == "" {
   326			return
   327		}
   328		s := ctxt.Syms.ROLookup(*flagFieldTrack, 0)
   329		if s == nil || !s.Attr.Reachable() {
   330			return
   331		}
   332		s.Type = sym.SDATA
   333		addstrdata(ctxt, *flagFieldTrack, buf.String())
   334	}
   335	
   336	func (ctxt *Link) addexport() {
   337		// TODO(aix)
   338		if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
   339			return
   340		}
   341	
   342		for _, exp := range dynexp {
   343			Adddynsym(ctxt, exp)
   344		}
   345		for _, lib := range dynlib {
   346			adddynlib(ctxt, lib)
   347		}
   348	}
   349	
   350	type Pkg struct {
   351		mark    bool
   352		checked bool
   353		path    string
   354		impby   []*Pkg
   355	}
   356	
   357	var pkgall []*Pkg
   358	
   359	func (p *Pkg) cycle() *Pkg {
   360		if p.checked {
   361			return nil
   362		}
   363	
   364		if p.mark {
   365			nerrors++
   366			fmt.Printf("import cycle:\n")
   367			fmt.Printf("\t%s\n", p.path)
   368			return p
   369		}
   370	
   371		p.mark = true
   372		for _, q := range p.impby {
   373			if bad := q.cycle(); bad != nil {
   374				p.mark = false
   375				p.checked = true
   376				fmt.Printf("\timports %s\n", p.path)
   377				if bad == p {
   378					return nil
   379				}
   380				return bad
   381			}
   382		}
   383	
   384		p.checked = true
   385		p.mark = false
   386		return nil
   387	}
   388	
   389	func importcycles() {
   390		for _, p := range pkgall {
   391			p.cycle()
   392		}
   393	}
   394	

View as plain text