// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // go-specific code shared across loaders (5l, 6l, 8l). package ld import ( "bytes" "cmd/internal/bio" "cmd/internal/objabi" "cmd/link/internal/sym" "fmt" "io" "os" "strings" ) // go-specific code shared across loaders (5l, 6l, 8l). // replace all "". with pkg. func expandpkg(t0 string, pkg string) string { return strings.Replace(t0, `"".`, pkg+".", -1) } // TODO: // generate debugging section in binary. // once the dust settles, try to move some code to // libmach, so that other linkers and ar can share. func ldpkg(ctxt *Link, f *bio.Reader, pkg string, length int64, filename string, whence int) { if *flagG { return } if int64(int(length)) != length { fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) if *flagU { errorexit() } return } // In a __.PKGDEF, we only care about the package name. // Don't read all the export data. if length > 1000 && whence == Pkgdef { length = 1000 } bdata := make([]byte, length) if _, err := io.ReadFull(f, bdata); err != nil { fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) if *flagU { errorexit() } return } data := string(bdata) // process header lines isSafe := false isMain := false for data != "" { var line string if i := strings.Index(data, "\n"); i >= 0 { line, data = data[:i], data[i+1:] } else { line, data = data, "" } if line == "safe" { isSafe = true } if line == "main" { isMain = true } if line == "" { break } } if whence == Pkgdef || whence == FileObj { if pkg == "main" && !isMain { Exitf("%s: not package main", filename) } if *flagU && whence != ArchiveObj && !isSafe { Exitf("load of unsafe package %s", filename) } } // __.PKGDEF has no cgo section - those are in the C compiler-generated object files. if whence == Pkgdef { return } // look for cgo section p0 := strings.Index(data, "\n$$ // cgo") var p1 int if p0 >= 0 { p0 += p1 i := strings.IndexByte(data[p0+1:], '\n') if i < 0 { fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) if *flagU { errorexit() } return } p0 += 1 + i p1 = strings.Index(data[p0:], "\n$$") if p1 < 0 { p1 = strings.Index(data[p0:], "\n!\n") } if p1 < 0 { fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) if *flagU { errorexit() } return } p1 += p0 loadcgo(ctxt, filename, pkg, data[p0:p1]) } } func loadcgo(ctxt *Link, file string, pkg string, p string) { var next string var q string var lib string var s *sym.Symbol p0 := "" for ; p != ""; p = next { if i := strings.Index(p, "\n"); i >= 0 { p, next = p[:i], p[i+1:] } else { next = "" } p0 = p // save for error message f := tokenize(p) if len(f) == 0 { continue } if f[0] == "cgo_import_dynamic" { if len(f) < 2 || len(f) > 4 { goto err } local := f[1] remote := local if len(f) > 2 { remote = f[2] } lib = "" if len(f) > 3 { lib = f[3] } if *FlagD { fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file) nerrors++ return } if local == "_" && remote == "_" { // allow #pragma dynimport _ _ "foo.so" // to force a link of foo.so. havedynamic = 1 if ctxt.HeadType == objabi.Hdarwin { machoadddynlib(lib, ctxt.LinkMode) } else { dynlib = append(dynlib, lib) } continue } local = expandpkg(local, pkg) q = "" if i := strings.Index(remote, "#"); i >= 0 { remote, q = remote[:i], remote[i+1:] } s = ctxt.Syms.Lookup(local, 0) if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SHOSTOBJ { s.Dynimplib = lib s.Extname = remote s.Dynimpvers = q if s.Type != sym.SHOSTOBJ { s.Type = sym.SDYNIMPORT } havedynamic = 1 } continue } if f[0] == "cgo_import_static" { if len(f) != 2 { goto err } local := f[1] s = ctxt.Syms.Lookup(local, 0) s.Type = sym.SHOSTOBJ s.Size = 0 continue } if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" { if len(f) < 2 || len(f) > 3 { goto err } local := f[1] var remote string if len(f) > 2 { remote = f[2] } else { remote = local } local = expandpkg(local, pkg) s = ctxt.Syms.Lookup(local, 0) switch ctxt.BuildMode { case BuildModeCShared, BuildModeCArchive, BuildModePlugin: if s == ctxt.Syms.Lookup("main", 0) { continue } } // export overrides import, for openbsd/cgo. // see issue 4878. if s.Dynimplib != "" { s.Dynimplib = "" s.Extname = "" s.Dynimpvers = "" s.Type = 0 } if !s.Attr.CgoExport() { s.Extname = remote dynexp = append(dynexp, s) } else if s.Extname != remote { fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname, remote) nerrors++ return } if f[0] == "cgo_export_static" { s.Attr |= sym.AttrCgoExportStatic } else { s.Attr |= sym.AttrCgoExportDynamic } continue } if f[0] == "cgo_dynamic_linker" { if len(f) != 2 { goto err } if *flagInterpreter == "" { if interpreter != "" && interpreter != f[1] { fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1]) nerrors++ return } interpreter = f[1] } continue } if f[0] == "cgo_ldflag" { if len(f) != 2 { goto err } ldflag = append(ldflag, f[1]) continue } } return err: fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0) nerrors++ } var seenlib = make(map[string]bool) func adddynlib(ctxt *Link, lib string) { if seenlib[lib] || ctxt.LinkMode == LinkExternal { return } seenlib[lib] = true if ctxt.IsELF { s := ctxt.Syms.Lookup(".dynstr", 0) if s.Size == 0 { Addstring(s, "") } Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib))) } else { Errorf(nil, "adddynlib: unsupported binary format") } } func Adddynsym(ctxt *Link, s *sym.Symbol) { if s.Dynid >= 0 || ctxt.LinkMode == LinkExternal { return } if ctxt.IsELF { elfadddynsym(ctxt, s) } else if ctxt.HeadType == objabi.Hdarwin { Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname) } else if ctxt.HeadType == objabi.Hwindows { // already taken care of } else { Errorf(s, "adddynsym: unsupported binary format") } } func fieldtrack(ctxt *Link) { // record field tracking references var buf bytes.Buffer for _, s := range ctxt.Syms.Allsym { if strings.HasPrefix(s.Name, "go.track.") { s.Attr |= sym.AttrSpecial // do not lay out in data segment s.Attr |= sym.AttrNotInSymbolTable if s.Attr.Reachable() { buf.WriteString(s.Name[9:]) for p := s.Reachparent; p != nil; p = p.Reachparent { buf.WriteString("\t") buf.WriteString(p.Name) } buf.WriteString("\n") } s.Type = sym.SCONST s.Value = 0 } } if *flagFieldTrack == "" { return } s := ctxt.Syms.ROLookup(*flagFieldTrack, 0) if s == nil || !s.Attr.Reachable() { return } s.Type = sym.SDATA addstrdata(ctxt, *flagFieldTrack, buf.String()) } func (ctxt *Link) addexport() { if ctxt.HeadType == objabi.Hdarwin { return } for _, exp := range dynexp { Adddynsym(ctxt, exp) } for _, lib := range dynlib { adddynlib(ctxt, lib) } } type Pkg struct { mark bool checked bool path string impby []*Pkg } var pkgall []*Pkg func (p *Pkg) cycle() *Pkg { if p.checked { return nil } if p.mark { nerrors++ fmt.Printf("import cycle:\n") fmt.Printf("\t%s\n", p.path) return p } p.mark = true for _, q := range p.impby { if bad := q.cycle(); bad != nil { p.mark = false p.checked = true fmt.Printf("\timports %s\n", p.path) if bad == p { return nil } return bad } } p.checked = true p.mark = false return nil } func importcycles() { for _, p := range pkgall { p.cycle() } }