blob: cd85c8b96e7440c0a5ed6246d58aadbc808500f1 [file] [log] [blame]
// 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.
package ld
import (
"bytes"
"cmd/internal/obj"
"fmt"
"os"
"strings"
"unicode/utf8"
)
// 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)
}
// 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).
// accumulate all type information from .6 files.
// check for inconsistencies.
// 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.
/*
* package import data
*/
type Import struct {
hash *Import
prefix string
name string
def string
file string
}
const (
NIHASH = 1024
)
var ihash [NIHASH]*Import
var nimport int
func hashstr(name string) int {
var h uint32
var cp string
h = 0
for cp = name; cp != ""; cp = cp[1:] {
h = h*1119 + uint32(cp[0])
}
h &= 0xffffff
return int(h)
}
func ilookup(name string) *Import {
var h int
var x *Import
h = hashstr(name) % NIHASH
for x = ihash[h]; x != nil; x = x.hash {
if x.name[0] == name[0] && x.name == name {
return x
}
}
x = new(Import)
x.name = name
x.hash = ihash[h]
ihash[h] = x
nimport++
return x
}
func ldpkg(f *Biobuf, pkg string, length int64, filename string, whence int) {
var bdata []byte
var data string
var p0, p1 int
var name string
if Debug['g'] != 0 {
return
}
if int64(int(length)) != length {
fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
if Debug['u'] != 0 {
Errorexit()
}
return
}
bdata = make([]byte, length)
if int64(Bread(f, bdata)) != length {
fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
if Debug['u'] != 0 {
Errorexit()
}
return
}
data = string(bdata)
// first \n$$ marks beginning of exports - skip rest of line
p0 = strings.Index(data, "\n$$")
if p0 < 0 {
if Debug['u'] != 0 && whence != ArchiveObj {
fmt.Fprintf(os.Stderr, "%s: cannot find export data in %s\n", os.Args[0], filename)
Errorexit()
}
return
}
p0 += 3
for p0 < len(data) && data[0] != '\n' {
p0++
}
// second marks end of exports / beginning of local data
p1 = strings.Index(data[p0:], "\n$$")
if p1 < 0 {
fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename)
if Debug['u'] != 0 {
Errorexit()
}
return
}
p1 += p0
for p0 < p1 && (data[p0] == ' ' || data[0] == '\t' || data[0] == '\n') {
p0++
}
if p0 < p1 {
if !strings.HasPrefix(data[p0:], "package ") {
fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:])
if Debug['u'] != 0 {
Errorexit()
}
return
}
p0 += 8
for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') {
p0++
}
name = data[p0:]
for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' {
p0++
}
if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) {
fmt.Fprintf(os.Stderr, "%s: load of unsafe package %s\n", os.Args[0], filename)
nerrors++
Errorexit()
}
name = name[:p1-p0]
if p0 < p1 {
if data[p0] == '\n' {
p0++
} else {
p0++
for p0 < p1 && data[p0] != '\n' {
p0++
}
}
}
if pkg == "main" && name != "main" {
fmt.Fprintf(os.Stderr, "%s: %s: not package main (package %s)\n", os.Args[0], filename, name)
nerrors++
Errorexit()
}
loadpkgdata(filename, pkg, data[p0:p1])
}
// __.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[p1:], "\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 Debug['u'] != 0 {
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 Debug['u'] != 0 {
Errorexit()
}
return
}
p1 += p0
loadcgo(filename, pkg, data[p0:p1])
}
}
func loadpkgdata(file string, pkg string, data string) {
var p string
var prefix string
var name string
var def string
var x *Import
file = file
p = data
for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 {
x = ilookup(name)
if x.prefix == "" {
x.prefix = prefix
x.def = def
x.file = file
} else if x.prefix != prefix {
fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name)
fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", x.file, x.prefix, name)
fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", file, prefix, name)
nerrors++
} else if x.def != def {
fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name)
fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", x.file, x.prefix, name, x.def)
fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", file, prefix, name, def)
nerrors++
}
}
}
func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int {
var p string
var prefix string
var name string
var def string
var meth string
var inquote bool
// skip white space
p = *pp
loop:
for len(p) > 0 && (p[0] == ' ' || p[0] == '\t' || p[0] == '\n') {
p = p[1:]
}
if len(p) == 0 || strings.HasPrefix(p, "$$\n") {
return 0
}
// prefix: (var|type|func|const)
prefix = p
if len(p) < 7 {
return -1
}
if strings.HasPrefix(p, "var ") {
p = p[4:]
} else if strings.HasPrefix(p, "type ") {
p = p[5:]
} else if strings.HasPrefix(p, "func ") {
p = p[5:]
} else if strings.HasPrefix(p, "const ") {
p = p[6:]
} else if strings.HasPrefix(p, "import ") {
p = p[7:]
for len(p) > 0 && p[0] != ' ' {
p = p[1:]
}
p = p[1:]
name := p
for len(p) > 0 && p[0] != '\n' {
p = p[1:]
}
if len(p) == 0 {
fmt.Fprintf(os.Stderr, "%s: %s: confused in import line\n", os.Args[0], file)
nerrors++
return -1
}
name = name[:len(name)-len(p)]
p = p[1:]
imported(pkg, name)
goto loop
} else {
fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix)
nerrors++
return -1
}
prefix = prefix[:len(prefix)-len(p)-1]
// name: a.b followed by space
name = p
inquote = false
for len(p) > 0 {
if p[0] == ' ' && !inquote {
break
}
if p[0] == '\\' {
p = p[1:]
} else if p[0] == '"' {
inquote = !inquote
}
p = p[1:]
}
if len(p) == 0 {
return -1
}
name = name[:len(name)-len(p)]
p = p[1:]
// def: free form to new line
def = p
for len(p) > 0 && p[0] != '\n' {
p = p[1:]
}
if len(p) == 0 {
return -1
}
def = def[:len(def)-len(p)]
var defbuf *bytes.Buffer
p = p[1:]
// include methods on successive lines in def of named type
for parsemethod(&p, &meth) > 0 {
if defbuf == nil {
defbuf = new(bytes.Buffer)
defbuf.WriteString(def)
}
defbuf.WriteString("\n\t")
defbuf.WriteString(meth)
}
if defbuf != nil {
def = defbuf.String()
}
name = expandpkg(name, pkg)
def = expandpkg(def, pkg)
// done
*pp = p
*prefixp = prefix
*namep = name
*defp = def
return 1
}
func parsemethod(pp *string, methp *string) int {
var p string
// skip white space
p = *pp
for len(p) > 0 && (p[0] == ' ' || p[0] == '\t') {
p = p[1:]
}
if len(p) == 0 {
return 0
}
// might be a comment about the method
if strings.HasPrefix(p, "//") {
goto useline
}
// if it says "func (", it's a method
if strings.HasPrefix(p, "func (") {
goto useline
}
return 0
// definition to end of line
useline:
*methp = p
for len(p) > 0 && p[0] != '\n' {
p = p[1:]
}
if len(p) == 0 {
fmt.Fprintf(os.Stderr, "%s: lost end of line in method definition\n", os.Args[0])
*pp = ""
return -1
}
*methp = (*methp)[:len(*methp)-len(p)]
*pp = p[1:]
return 1
}
func loadcgo(file string, pkg string, p string) {
var next string
var p0 string
var q string
var f []string
var local string
var remote string
var lib string
var s *LSym
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 Debug['d'] != 0 {
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
Thearch.Adddynlib(lib)
continue
}
local = expandpkg(local, pkg)
q = ""
if i := strings.Index(remote, "#"); i >= 0 {
remote, q = remote[:i], remote[i+1:]
}
s = Linklookup(Ctxt, 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 = Linklookup(Ctxt, local, 0)
s.Type = SHOSTOBJ
s.Size = 0
continue
}
if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" {
// TODO: Remove once we know Windows is okay.
if f[0] == "cgo_export_static" && HEADTYPE == Hwindows {
continue
}
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 = Linklookup(Ctxt, local, 0)
if Flag_shared != 0 && s == Linklookup(Ctxt, "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.Cgoexport == 0 {
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.Cgoexport |= CgoExportStatic
} else {
s.Cgoexport |= CgoExportDynamic
}
if local != f[1] {
}
continue
}
if f[0] == "cgo_dynamic_linker" {
if len(f) != 2 {
goto err
}
if Debug['I'] == 0 {
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 markq *LSym
var emarkq *LSym
func mark1(s *LSym, parent *LSym) {
if s == nil || s.Reachable {
return
}
if strings.HasPrefix(s.Name, "go.weak.") {
return
}
s.Reachable = true
s.Reachparent = parent
if markq == nil {
markq = s
} else {
emarkq.Queue = s
}
emarkq = s
}
func mark(s *LSym) {
mark1(s, nil)
}
func markflood() {
var a *Auto
var s *LSym
var i int
for s = markq; s != nil; s = s.Queue {
if s.Type == STEXT {
if Debug['v'] > 1 {
fmt.Fprintf(&Bso, "marktext %s\n", s.Name)
}
for a = s.Autom; a != nil; a = a.Link {
mark1(a.Gotype, s)
}
}
for i = 0; i < len(s.R); i++ {
mark1(s.R[i].Sym, s)
}
if s.Pcln != nil {
for i = 0; i < s.Pcln.Nfuncdata; i++ {
mark1(s.Pcln.Funcdata[i], s)
}
}
mark1(s.Gotype, s)
mark1(s.Sub, s)
mark1(s.Outer, s)
}
}
var markextra = []string{
"runtime.morestack",
"runtime.morestackx",
"runtime.morestack00",
"runtime.morestack10",
"runtime.morestack01",
"runtime.morestack11",
"runtime.morestack8",
"runtime.morestack16",
"runtime.morestack24",
"runtime.morestack32",
"runtime.morestack40",
"runtime.morestack48",
// on arm, lock in the div/mod helpers too
"_div",
"_divu",
"_mod",
"_modu",
}
func deadcode() {
var i int
var s *LSym
var last *LSym
var p *LSym
var fmt_ string
if Debug['v'] != 0 {
fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime())
}
mark(Linklookup(Ctxt, INITENTRY, 0))
for i = 0; i < len(markextra); i++ {
mark(Linklookup(Ctxt, markextra[i], 0))
}
for i = 0; i < len(dynexp); i++ {
mark(dynexp[i])
}
markflood()
// keep each beginning with 'typelink.' if the symbol it points at is being kept.
for s = Ctxt.Allsym; s != nil; s = s.Allsym {
if strings.HasPrefix(s.Name, "go.typelink.") {
s.Reachable = len(s.R) == 1 && s.R[0].Sym.Reachable
}
}
// remove dead text but keep file information (z symbols).
last = nil
for s = Ctxt.Textp; s != nil; s = s.Next {
if !s.Reachable {
continue
}
// NOTE: Removing s from old textp and adding to new, shorter textp.
if last == nil {
Ctxt.Textp = s
} else {
last.Next = s
}
last = s
}
if last == nil {
Ctxt.Textp = nil
} else {
last.Next = nil
}
for s = Ctxt.Allsym; s != nil; s = s.Allsym {
if strings.HasPrefix(s.Name, "go.weak.") {
s.Special = 1 // do not lay out in data segment
s.Reachable = true
s.Hide = 1
}
}
// record field tracking references
fmt_ = ""
for s = Ctxt.Allsym; s != nil; s = s.Allsym {
if strings.HasPrefix(s.Name, "go.track.") {
s.Special = 1 // do not lay out in data segment
s.Hide = 1
if s.Reachable {
fmt_ += fmt.Sprintf("%s", s.Name[9:])
for p = s.Reachparent; p != nil; p = p.Reachparent {
fmt_ += fmt.Sprintf("\t%s", p.Name)
}
fmt_ += fmt.Sprintf("\n")
}
s.Type = SCONST
s.Value = 0
}
}
if tracksym == "" {
return
}
s = Linklookup(Ctxt, tracksym, 0)
if !s.Reachable {
return
}
addstrdata(tracksym, fmt_)
}
func doweak() {
var s *LSym
var t *LSym
// resolve weak references only if
// target symbol will be in binary anyway.
for s = Ctxt.Allsym; s != nil; s = s.Allsym {
if strings.HasPrefix(s.Name, "go.weak.") {
t = Linkrlookup(Ctxt, s.Name[8:], int(s.Version))
if t != nil && t.Type != 0 && t.Reachable {
s.Value = t.Value
s.Type = t.Type
s.Outer = t
} else {
s.Type = SCONST
s.Value = 0
}
continue
}
}
}
func addexport() {
var i int
if HEADTYPE == Hdarwin {
return
}
for i = 0; i < len(dynexp); i++ {
Thearch.Adddynsym(Ctxt, dynexp[i])
}
}
/* %Z from gc, for quoting import paths */
func Zconv(s string, flag int) string {
// NOTE: Keep in sync with gc Zconv.
var n int
var fp string
for i := 0; i < len(s); i += n {
var r rune
r, n = utf8.DecodeRuneInString(s[i:])
switch r {
case utf8.RuneError:
if n == 1 {
fp += fmt.Sprintf("\\x%02x", s[i])
break
}
fallthrough
// fall through
default:
if r < ' ' {
fp += fmt.Sprintf("\\x%02x", r)
break
}
fp += string(r)
case '\t':
fp += "\\t"
case '\n':
fp += "\\n"
case '"',
'\\':
fp += `\` + string(r)
case 0xFEFF: // BOM, basically disallowed in source code
fp += "\\uFEFF"
}
}
return fp
}
type Pkg struct {
mark uint8
checked uint8
next *Pkg
path_ string
impby []*Pkg
all *Pkg
}
var phash [1024]*Pkg
var pkgall *Pkg
func getpkg(path_ string) *Pkg {
var p *Pkg
var h int
h = hashstr(path_) % len(phash)
for p = phash[h]; p != nil; p = p.next {
if p.path_ == path_ {
return p
}
}
p = new(Pkg)
p.path_ = path_
p.next = phash[h]
phash[h] = p
p.all = pkgall
pkgall = p
return p
}
func imported(pkg string, import_ string) {
var p *Pkg
var i *Pkg
// everyone imports runtime, even runtime.
if import_ == "\"runtime\"" {
return
}
pkg = fmt.Sprintf("\"%v\"", Zconv(pkg, 0)) // turn pkg path into quoted form, freed below
p = getpkg(pkg)
i = getpkg(import_)
i.impby = append(i.impby, p)
}
func cycle(p *Pkg) *Pkg {
var i int
var bad *Pkg
if p.checked != 0 {
return nil
}
if p.mark != 0 {
nerrors++
fmt.Printf("import cycle:\n")
fmt.Printf("\t%s\n", p.path_)
return p
}
p.mark = 1
for i = 0; i < len(p.impby); i++ {
bad = cycle(p.impby[i])
if bad != nil {
p.mark = 0
p.checked = 1
fmt.Printf("\timports %s\n", p.path_)
if bad == p {
return nil
}
return bad
}
}
p.checked = 1
p.mark = 0
return nil
}
func importcycles() {
var p *Pkg
for p = pkgall; p != nil; p = p.all {
cycle(p)
}
}
func setlinkmode(arg string) {
if arg == "internal" {
Linkmode = LinkInternal
} else if arg == "external" {
Linkmode = LinkExternal
} else if arg == "auto" {
Linkmode = LinkAuto
} else {
fmt.Fprintf(os.Stderr, "unknown link mode -linkmode %s\n", arg)
Errorexit()
}
}