| // 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" |
| "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) { |
| var p0, p1 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") |
| 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 f []string |
| var local string |
| var remote string |
| var lib string |
| var s *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 Headtype == objabi.Hdarwin { |
| Machoadddynlib(lib) |
| } 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 local != f[1] { |
| } |
| if s.Type == 0 || s.Type == SXREF || s.Type == SHOSTOBJ { |
| s.Dynimplib = lib |
| s.Extname = remote |
| s.Dynimpvers = q |
| if s.Type != SHOSTOBJ { |
| s.Type = 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 = 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] |
| if len(f) > 2 { |
| remote = f[2] |
| } else { |
| remote = local |
| } |
| local = expandpkg(local, pkg) |
| s = ctxt.Syms.Lookup(local, 0) |
| |
| switch 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 |= AttrCgoExportStatic |
| } else { |
| s.Attr |= AttrCgoExportDynamic |
| } |
| if local != f[1] { |
| } |
| 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] || Linkmode == LinkExternal { |
| return |
| } |
| seenlib[lib] = true |
| |
| if 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 *Symbol) { |
| if s.Dynid >= 0 || Linkmode == LinkExternal { |
| return |
| } |
| |
| if Iself { |
| Elfadddynsym(ctxt, s) |
| } else if Headtype == objabi.Hdarwin { |
| Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname) |
| } else if 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 |= AttrSpecial // do not lay out in data segment |
| s.Attr |= 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 = SCONST |
| s.Value = 0 |
| } |
| } |
| |
| if *flagFieldTrack == "" { |
| return |
| } |
| s := ctxt.Syms.Lookup(*flagFieldTrack, 0) |
| if !s.Attr.Reachable() { |
| return |
| } |
| addstrdata(ctxt, *flagFieldTrack, buf.String()) |
| s.Type = SDATA |
| } |
| |
| func (ctxt *Link) addexport() { |
| if 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() |
| } |
| } |