aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/asm/internal/flags/flags.go1
-rw-r--r--src/cmd/asm/main.go10
-rw-r--r--src/cmd/compile/internal/gc/iexport.go19
-rw-r--r--src/cmd/compile/internal/gc/iimport.go17
-rw-r--r--src/cmd/compile/internal/gc/main.go7
-rw-r--r--src/cmd/compile/internal/gc/obj.go34
-rw-r--r--src/cmd/compile/internal/types/sym.go11
-rw-r--r--src/cmd/dist/buildtool.go2
-rw-r--r--src/cmd/dist/test.go2
-rw-r--r--src/cmd/go/alldocs.go4
-rw-r--r--src/cmd/go/internal/work/build.go4
-rw-r--r--src/cmd/go/internal/work/init.go1
-rw-r--r--src/cmd/internal/dwarf/dwarf.go8
-rw-r--r--src/cmd/internal/goobj/read.go8
-rw-r--r--src/cmd/internal/goobj/readnew.go187
-rw-r--r--src/cmd/internal/goobj2/builtin.go45
-rw-r--r--src/cmd/internal/goobj2/builtinlist.go194
-rw-r--r--src/cmd/internal/goobj2/funcinfo.go147
-rw-r--r--src/cmd/internal/goobj2/mkbuiltin.go124
-rw-r--r--src/cmd/internal/goobj2/objfile.go587
-rw-r--r--src/cmd/internal/obj/link.go24
-rw-r--r--src/cmd/internal/obj/objfile.go14
-rw-r--r--src/cmd/internal/obj/objfile2.go429
-rw-r--r--src/cmd/internal/obj/sizeof_test.go2
-rw-r--r--src/cmd/internal/obj/sym.go176
-rw-r--r--src/cmd/link/internal/ld/data.go6
-rw-r--r--src/cmd/link/internal/ld/deadcode.go75
-rw-r--r--src/cmd/link/internal/ld/deadcode2.go441
-rw-r--r--src/cmd/link/internal/ld/decodesym.go56
-rw-r--r--src/cmd/link/internal/ld/dwarf.go14
-rw-r--r--src/cmd/link/internal/ld/go.go45
-rw-r--r--src/cmd/link/internal/ld/lib.go563
-rw-r--r--src/cmd/link/internal/ld/link.go13
-rw-r--r--src/cmd/link/internal/ld/main.go8
-rw-r--r--src/cmd/link/internal/ld/pcln.go4
-rw-r--r--src/cmd/link/internal/ld/symtab.go2
-rw-r--r--src/cmd/link/internal/ld/util.go7
-rw-r--r--src/cmd/link/internal/loadelf/ldelf.go35
-rw-r--r--src/cmd/link/internal/loader/loader.go1308
-rw-r--r--src/cmd/link/internal/loadmacho/ldmacho.go21
-rw-r--r--src/cmd/link/internal/loadpe/ldpe.go30
-rw-r--r--src/cmd/link/internal/loadxcoff/ldxcoff.go25
-rw-r--r--src/cmd/link/internal/objfile/objfile.go2
-rw-r--r--src/cmd/link/internal/ppc64/asm.go2
-rw-r--r--src/cmd/link/internal/sym/symbol.go2
-rw-r--r--src/cmd/link/internal/sym/symbols.go11
-rw-r--r--src/cmd/link/internal/sym/symkind.go5
-rw-r--r--src/cmd/link/link_test.go65
48 files changed, 4463 insertions, 334 deletions
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index 5fe3fd9d53..fad87b221a 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -23,6 +23,7 @@ var (
Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries")
AllErrors = flag.Bool("e", false, "no limit on number of errors reported")
SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble")
+ Newobj = flag.Bool("newobj", false, "use new object file format")
)
var (
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index fc6acc74c0..6b0a609071 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -40,18 +40,18 @@ func main() {
}
ctxt.Flag_dynlink = *flags.Dynlink
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
+ ctxt.Flag_newobj = *flags.Newobj
ctxt.Bso = bufio.NewWriter(os.Stdout)
defer ctxt.Bso.Flush()
architecture.Init(ctxt)
// Create object file, write header.
- out, err := os.Create(*flags.OutputFile)
+ buf, err := bio.Create(*flags.OutputFile)
if err != nil {
log.Fatal(err)
}
- defer bio.MustClose(out)
- buf := bufio.NewWriter(bio.MustWriter(out))
+ defer buf.Close()
if !*flags.SymABIs {
fmt.Fprintf(buf, "go object %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version)
@@ -83,6 +83,7 @@ func main() {
}
}
if ok && !*flags.SymABIs {
+ ctxt.NumberSyms(true)
obj.WriteObjFile(ctxt, buf, "")
}
if !ok || diag {
@@ -91,9 +92,8 @@ func main() {
} else {
log.Print("assembly failed")
}
- out.Close()
+ buf.Close()
os.Remove(*flags.OutputFile)
os.Exit(1)
}
- buf.Flush()
}
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go
index 54b87ab1e4..259b70a69f 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/gc/iexport.go
@@ -203,6 +203,7 @@ import (
"bufio"
"bytes"
"cmd/compile/internal/types"
+ "cmd/internal/goobj2"
"cmd/internal/src"
"encoding/binary"
"fmt"
@@ -945,10 +946,12 @@ func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) }
func (w *exportWriter) varExt(n *Node) {
w.linkname(n.Sym)
+ w.symIdx(n.Sym)
}
func (w *exportWriter) funcExt(n *Node) {
w.linkname(n.Sym)
+ w.symIdx(n.Sym)
// Escape analysis.
for _, fs := range types.RecvsParams {
@@ -987,6 +990,22 @@ func (w *exportWriter) linkname(s *types.Sym) {
w.string(s.Linkname)
}
+func (w *exportWriter) symIdx(s *types.Sym) {
+ if Ctxt.Flag_newobj {
+ lsym := s.Linksym()
+ if lsym.PkgIdx > goobj2.PkgIdxSelf || (lsym.PkgIdx == goobj2.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
+ // Don't export index for non-package symbols, linkname'd symbols,
+ // and symbols without an index. They can only be referenced by
+ // name.
+ w.int64(-1)
+ } else {
+ // For a defined symbol, export its index.
+ // For re-exporting an imported symbol, pass its index through.
+ w.int64(int64(lsym.SymIdx))
+ }
+ }
+}
+
// Inline bodies.
func (w *exportWriter) stmtList(list Nodes) {
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
index 64c554d187..824648acb6 100644
--- a/src/cmd/compile/internal/gc/iimport.go
+++ b/src/cmd/compile/internal/gc/iimport.go
@@ -10,6 +10,7 @@ package gc
import (
"cmd/compile/internal/types"
"cmd/internal/bio"
+ "cmd/internal/obj"
"cmd/internal/src"
"encoding/binary"
"fmt"
@@ -651,10 +652,12 @@ func (r *importReader) byte() byte {
func (r *importReader) varExt(n *Node) {
r.linkname(n.Sym)
+ r.symIdx(n.Sym)
}
func (r *importReader) funcExt(n *Node) {
r.linkname(n.Sym)
+ r.symIdx(n.Sym)
// Escape analysis.
for _, fs := range types.RecvsParams {
@@ -683,6 +686,20 @@ func (r *importReader) linkname(s *types.Sym) {
s.Linkname = r.string()
}
+func (r *importReader) symIdx(s *types.Sym) {
+ if Ctxt.Flag_newobj {
+ lsym := s.Linksym()
+ idx := int32(r.int64())
+ if idx != -1 {
+ if s.Linkname != "" {
+ Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
+ }
+ lsym.SymIdx = idx
+ lsym.Set(obj.AttrIndexed, true)
+ }
+ }
+}
+
func (r *importReader) doInline(n *Node) {
if len(n.Func.Inl.Body) != 0 {
Fatalf("%v already has inline body", n)
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 2d427be539..721ebeed6f 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -260,6 +260,7 @@ func Main(archInit func(*Arch)) {
if supportsDynlink(thearch.LinkArch.Arch) {
flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
+ flag.BoolVar(&Ctxt.Flag_linkshared, "linkshared", false, "generate code that will be linked against Go shared libraries")
}
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
@@ -274,12 +275,14 @@ func Main(archInit func(*Arch)) {
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
+ flag.BoolVar(&Ctxt.Flag_newobj, "newobj", false, "use new object file format")
+
objabi.Flagparse(usage)
// Record flags that affect the build result. (And don't
// record flags that don't, since that would cause spurious
// changes in the binary.)
- recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes")
+ recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "newobj")
if smallFrames {
maxStackVarSize = 128 * 1024
@@ -746,6 +749,8 @@ func Main(archInit func(*Arch)) {
// Write object data to disk.
timings.Start("be", "dumpobj")
+ dumpdata()
+ Ctxt.NumberSyms(false)
dumpobj()
if asmhdr != "" {
dumpasmhdr()
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 83371fabf5..a3e82cf699 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -111,21 +111,7 @@ func dumpCompilerObj(bout *bio.Writer) {
dumpexport(bout)
}
-func dumpLinkerObj(bout *bio.Writer) {
- printObjHeader(bout)
-
- if len(pragcgobuf) != 0 {
- // write empty export section; must be before cgo section
- fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
- fmt.Fprintf(bout, "\n$$ // cgo\n")
- if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
- Fatalf("serializing pragcgobuf: %v", err)
- }
- fmt.Fprintf(bout, "\n$$\n\n")
- }
-
- fmt.Fprintf(bout, "\n!\n")
-
+func dumpdata() {
externs := len(externdcl)
dumpglobls()
@@ -163,8 +149,24 @@ func dumpLinkerObj(bout *bio.Writer) {
}
addGCLocals()
+}
+
+func dumpLinkerObj(bout *bio.Writer) {
+ printObjHeader(bout)
+
+ if len(pragcgobuf) != 0 {
+ // write empty export section; must be before cgo section
+ fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
+ fmt.Fprintf(bout, "\n$$ // cgo\n")
+ if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
+ Fatalf("serializing pragcgobuf: %v", err)
+ }
+ fmt.Fprintf(bout, "\n$$\n\n")
+ }
+
+ fmt.Fprintf(bout, "\n!\n")
- obj.WriteObjFile(Ctxt, bout.Writer, myimportpath)
+ obj.WriteObjFile(Ctxt, bout, myimportpath)
}
func addptabs() {
diff --git a/src/cmd/compile/internal/types/sym.go b/src/cmd/compile/internal/types/sym.go
index c9dd9f399e..07bce4d5cd 100644
--- a/src/cmd/compile/internal/types/sym.go
+++ b/src/cmd/compile/internal/types/sym.go
@@ -80,11 +80,18 @@ func (sym *Sym) Linksym() *obj.LSym {
if sym == nil {
return nil
}
+ initPkg := func(r *obj.LSym) {
+ if sym.Linkname != "" {
+ r.Pkg = "_"
+ } else {
+ r.Pkg = sym.Pkg.Prefix
+ }
+ }
if sym.Func() {
// This is a function symbol. Mark it as "internal ABI".
- return Ctxt.LookupABI(sym.LinksymName(), obj.ABIInternal)
+ return Ctxt.LookupABIInit(sym.LinksymName(), obj.ABIInternal, initPkg)
}
- return Ctxt.Lookup(sym.LinksymName())
+ return Ctxt.LookupInit(sym.LinksymName(), initPkg)
}
// Less reports whether symbol a is ordered before symbol b.
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index f27ea17230..2458b439a8 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -54,6 +54,7 @@ var bootstrapDirs = []string{
"cmd/internal/gcprog",
"cmd/internal/dwarf",
"cmd/internal/edit",
+ "cmd/internal/goobj2",
"cmd/internal/objabi",
"cmd/internal/obj",
"cmd/internal/obj/arm",
@@ -72,6 +73,7 @@ var bootstrapDirs = []string{
"cmd/link/internal/arm64",
"cmd/link/internal/ld",
"cmd/link/internal/loadelf",
+ "cmd/link/internal/loader",
"cmd/link/internal/loadmacho",
"cmd/link/internal/loadpe",
"cmd/link/internal/loadxcoff",
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index a5ab25102b..cc54554a1d 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -585,7 +585,7 @@ func (t *tester) registerTests() {
},
})
// Also test a cgo package.
- if t.cgoEnabled {
+ if t.cgoEnabled && t.internalLink() {
t.tests = append(t.tests, distTest{
name: "pie_internal_cgo",
heading: "internal linking of -buildmode=pie",
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index a6af7738b5..fad2d9f0fe 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -145,8 +145,8 @@
// -ldflags '[pattern=]arg list'
// arguments to pass on each go tool link invocation.
// -linkshared
-// link against shared libraries previously created with
-// -buildmode=shared.
+// build code that will be linked against shared libraries previously
+// created with -buildmode=shared.
// -mod mode
// module download mode to use: readonly or vendor.
// See 'go help modules' for more.
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 7dd8104683..45dd165ce0 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -97,8 +97,8 @@ and test commands:
-ldflags '[pattern=]arg list'
arguments to pass on each go tool link invocation.
-linkshared
- link against shared libraries previously created with
- -buildmode=shared.
+ build code that will be linked against shared libraries previously
+ created with -buildmode=shared.
-mod mode
module download mode to use: readonly or vendor.
See 'go help modules' for more.
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index 55f6d4644a..ba3168a2c8 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -224,6 +224,7 @@ func buildModeInit() {
base.Fatalf("-linkshared not supported on %s\n", platform)
}
codegenArg = "-dynlink"
+ forcedGcflags = append(forcedGcflags, "-linkshared")
// TODO(mwhudson): remove -w when that gets fixed in linker.
forcedLdflags = append(forcedLdflags, "-linkshared", "-w")
}
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index 740b04f606..56b44a1ab5 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -1372,7 +1372,13 @@ func PutDefaultFunc(ctxt Context, s *FnState) error {
abbrev := DW_ABRV_FUNCTION
Uleb128put(ctxt, s.Info, int64(abbrev))
- putattr(ctxt, s.Info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(s.Name)), s.Name)
+ // Expand '"".' to import path.
+ name := s.Name
+ if s.Importpath != "" {
+ name = strings.Replace(name, "\"\".", objabi.PathToPrefix(s.Importpath)+".", -1)
+ }
+
+ putattr(ctxt, s.Info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go
index 0c70b8cd9f..e61e95dcc8 100644
--- a/src/cmd/internal/goobj/read.go
+++ b/src/cmd/internal/goobj/read.go
@@ -502,6 +502,14 @@ func (r *objReader) parseObject(prefix []byte) error {
}
// TODO: extract OS + build ID if/when we need it
+ p, err := r.peek(8)
+ if err != nil {
+ return err
+ }
+ if bytes.Equal(p, []byte("\x00go114LD")) {
+ r.readNew()
+ return nil
+ }
r.readFull(r.tmp[:8])
if !bytes.Equal(r.tmp[:8], []byte("\x00go114ld")) {
return r.error(errCorruptObject)
diff --git a/src/cmd/internal/goobj/readnew.go b/src/cmd/internal/goobj/readnew.go
new file mode 100644
index 0000000000..3f9d0d1db6
--- /dev/null
+++ b/src/cmd/internal/goobj/readnew.go
@@ -0,0 +1,187 @@
+// Copyright 2019 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 goobj
+
+import (
+ "cmd/internal/goobj2"
+ "cmd/internal/objabi"
+ "fmt"
+ "strings"
+)
+
+// Read object file in new format. For now we still fill
+// the data to the current goobj API.
+func (r *objReader) readNew() {
+ start := uint32(r.offset)
+
+ length := r.limit - r.offset
+ objbytes := make([]byte, length)
+ r.readFull(objbytes)
+ rr := goobj2.NewReaderFromBytes(objbytes, false)
+ if rr == nil {
+ panic("cannot read object file")
+ }
+
+ // Imports
+ r.p.Imports = rr.Autolib()
+
+ pkglist := rr.Pkglist()
+
+ abiToVer := func(abi uint16) int64 {
+ var vers int64
+ if abi == goobj2.SymABIstatic {
+ // Static symbol
+ vers = r.p.MaxVersion
+ }
+ return vers
+ }
+
+ resolveSymRef := func(s goobj2.SymRef) SymID {
+ var i int
+ switch p := s.PkgIdx; p {
+ case goobj2.PkgIdxInvalid:
+ if s.SymIdx != 0 {
+ panic("bad sym ref")
+ }
+ return SymID{}
+ case goobj2.PkgIdxNone:
+ i = int(s.SymIdx) + rr.NSym()
+ case goobj2.PkgIdxBuiltin:
+ name, abi := goobj2.BuiltinName(int(s.SymIdx))
+ return SymID{name, int64(abi)}
+ case goobj2.PkgIdxSelf:
+ i = int(s.SymIdx)
+ default:
+ pkg := pkglist[p]
+ return SymID{fmt.Sprintf("%s.<#%d>", pkg, s.SymIdx), 0}
+ }
+ sym := goobj2.Sym{}
+ sym.Read(rr, rr.SymOff(i))
+ return SymID{sym.Name, abiToVer(sym.ABI)}
+ }
+
+ // Read things for the current goobj API for now.
+
+ // Symbols
+ pcdataBase := start + rr.PcdataBase()
+ n := rr.NSym() + rr.NNonpkgdef() + rr.NNonpkgref()
+ ndef := rr.NSym() + rr.NNonpkgdef()
+ for i := 0; i < n; i++ {
+ osym := goobj2.Sym{}
+ osym.Read(rr, rr.SymOff(i))
+ if osym.Name == "" {
+ continue // not a real symbol
+ }
+ // In a symbol name in an object file, "". denotes the
+ // prefix for the package in which the object file has been found.
+ // Expand it.
+ name := strings.ReplaceAll(osym.Name, `"".`, r.pkgprefix)
+ symID := SymID{Name: name, Version: abiToVer(osym.ABI)}
+ r.p.SymRefs = append(r.p.SymRefs, symID)
+
+ if i >= ndef {
+ continue // not a defined symbol from here
+ }
+
+ // Symbol data
+ dataOff := rr.DataOff(i)
+ siz := int64(rr.DataSize(i))
+
+ sym := Sym{
+ SymID: symID,
+ Kind: objabi.SymKind(osym.Type),
+ DupOK: osym.Dupok(),
+ Size: int64(osym.Siz),
+ Data: Data{int64(start + dataOff), siz},
+ }
+ r.p.Syms = append(r.p.Syms, &sym)
+
+ // Reloc
+ nreloc := rr.NReloc(i)
+ sym.Reloc = make([]Reloc, nreloc)
+ for j := 0; j < nreloc; j++ {
+ rel := goobj2.Reloc{}
+ rel.Read(rr, rr.RelocOff(i, j))
+ sym.Reloc[j] = Reloc{
+ Offset: int64(rel.Off),
+ Size: int64(rel.Siz),
+ Type: objabi.RelocType(rel.Type),
+ Add: rel.Add,
+ Sym: resolveSymRef(rel.Sym),
+ }
+ }
+
+ // Aux symbol info
+ isym := -1
+ funcdata := make([]goobj2.SymRef, 0, 4)
+ naux := rr.NAux(i)
+ for j := 0; j < naux; j++ {
+ a := goobj2.Aux{}
+ a.Read(rr, rr.AuxOff(i, j))
+ switch a.Type {
+ case goobj2.AuxGotype:
+ sym.Type = resolveSymRef(a.Sym)
+ case goobj2.AuxFuncInfo:
+ if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
+ panic("funcinfo symbol not defined in current package")
+ }
+ isym = int(a.Sym.SymIdx)
+ case goobj2.AuxFuncdata:
+ funcdata = append(funcdata, a.Sym)
+ case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
+ // nothing to do
+ default:
+ panic("unknown aux type")
+ }
+ }
+
+ // Symbol Info
+ if isym == -1 {
+ continue
+ }
+ b := rr.BytesAt(rr.DataOff(isym), rr.DataSize(isym))
+ info := goobj2.FuncInfo{}
+ info.Read(b)
+
+ info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
+ f := &Func{
+ Args: int64(info.Args),
+ Frame: int64(info.Locals),
+ NoSplit: info.NoSplit != 0,
+ Leaf: osym.Leaf(),
+ TopFrame: osym.TopFrame(),
+ PCSP: Data{int64(pcdataBase + info.Pcsp), int64(info.Pcfile - info.Pcsp)},
+ PCFile: Data{int64(pcdataBase + info.Pcfile), int64(info.Pcline - info.Pcfile)},
+ PCLine: Data{int64(pcdataBase + info.Pcline), int64(info.Pcinline - info.Pcline)},
+ PCInline: Data{int64(pcdataBase + info.Pcinline), int64(info.Pcdata[0] - info.Pcinline)},
+ PCData: make([]Data, len(info.Pcdata)-1), // -1 as we appended one above
+ FuncData: make([]FuncData, len(info.Funcdataoff)),
+ File: make([]string, len(info.File)),
+ InlTree: make([]InlinedCall, len(info.InlTree)),
+ }
+ sym.Func = f
+ for k := range f.PCData {
+ f.PCData[k] = Data{int64(pcdataBase + info.Pcdata[k]), int64(info.Pcdata[k+1] - info.Pcdata[k])}
+ }
+ for k := range f.FuncData {
+ symID := resolveSymRef(funcdata[k])
+ f.FuncData[k] = FuncData{symID, int64(info.Funcdataoff[k])}
+ }
+ for k := range f.File {
+ symID := resolveSymRef(info.File[k])
+ f.File[k] = symID.Name
+ }
+ for k := range f.InlTree {
+ inl := &info.InlTree[k]
+ f.InlTree[k] = InlinedCall{
+ Parent: int64(inl.Parent),
+ File: resolveSymRef(inl.File).Name,
+ Line: int64(inl.Line),
+ Func: resolveSymRef(inl.Func),
+ ParentPC: int64(inl.ParentPC),
+ }
+ }
+ }
+}
diff --git a/src/cmd/internal/goobj2/builtin.go b/src/cmd/internal/goobj2/builtin.go
new file mode 100644
index 0000000000..65f9dd5d95
--- /dev/null
+++ b/src/cmd/internal/goobj2/builtin.go
@@ -0,0 +1,45 @@
+// Copyright 2019 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 goobj2
+
+// Builtin (compiler-generated) function references appear
+// frequently. We assign special indices for them, so they
+// don't need to be referenced by name.
+
+// NBuiltin returns the number of listed builtin
+// symbols.
+func NBuiltin() int {
+ return len(builtins)
+}
+
+// BuiltinName returns the name and ABI of the i-th
+// builtin symbol.
+func BuiltinName(i int) (string, int) {
+ return builtins[i].name, builtins[i].abi
+}
+
+// BuiltinIdx returns the index of the builtin with the
+// given name and abi, or -1 if it is not a builtin.
+func BuiltinIdx(name string, abi int) int {
+ i, ok := builtinMap[name]
+ if !ok {
+ return -1
+ }
+ if builtins[i].abi != abi {
+ return -1
+ }
+ return i
+}
+
+//go:generate go run mkbuiltin.go
+
+var builtinMap map[string]int
+
+func init() {
+ builtinMap = make(map[string]int, len(builtins))
+ for i, b := range builtins {
+ builtinMap[b.name] = i
+ }
+}
diff --git a/src/cmd/internal/goobj2/builtinlist.go b/src/cmd/internal/goobj2/builtinlist.go
new file mode 100644
index 0000000000..bcab6f2e0b
--- /dev/null
+++ b/src/cmd/internal/goobj2/builtinlist.go
@@ -0,0 +1,194 @@
+// Code generated by mkbuiltin.go. DO NOT EDIT.
+
+package goobj2
+
+var builtins = [...]struct {
+ name string
+ abi int
+}{
+ {"runtime.newobject", 1},
+ {"runtime.panicdivide", 1},
+ {"runtime.panicshift", 1},
+ {"runtime.panicmakeslicelen", 1},
+ {"runtime.throwinit", 1},
+ {"runtime.panicwrap", 1},
+ {"runtime.gopanic", 1},
+ {"runtime.gorecover", 1},
+ {"runtime.goschedguarded", 1},
+ {"runtime.goPanicIndex", 1},
+ {"runtime.goPanicIndexU", 1},
+ {"runtime.goPanicSliceAlen", 1},
+ {"runtime.goPanicSliceAlenU", 1},
+ {"runtime.goPanicSliceAcap", 1},
+ {"runtime.goPanicSliceAcapU", 1},
+ {"runtime.goPanicSliceB", 1},
+ {"runtime.goPanicSliceBU", 1},
+ {"runtime.goPanicSlice3Alen", 1},
+ {"runtime.goPanicSlice3AlenU", 1},
+ {"runtime.goPanicSlice3Acap", 1},
+ {"runtime.goPanicSlice3AcapU", 1},
+ {"runtime.goPanicSlice3B", 1},
+ {"runtime.goPanicSlice3BU", 1},
+ {"runtime.goPanicSlice3C", 1},
+ {"runtime.goPanicSlice3CU", 1},
+ {"runtime.printbool", 1},
+ {"runtime.printfloat", 1},
+ {"runtime.printint", 1},
+ {"runtime.printhex", 1},
+ {"runtime.printuint", 1},
+ {"runtime.printcomplex", 1},
+ {"runtime.printstring", 1},
+ {"runtime.printpointer", 1},
+ {"runtime.printiface", 1},
+ {"runtime.printeface", 1},
+ {"runtime.printslice", 1},
+ {"runtime.printnl", 1},
+ {"runtime.printsp", 1},
+ {"runtime.printlock", 1},
+ {"runtime.printunlock", 1},
+ {"runtime.concatstring2", 1},
+ {"runtime.concatstring3", 1},
+ {"runtime.concatstring4", 1},
+ {"runtime.concatstring5", 1},
+ {"runtime.concatstrings", 1},
+ {"runtime.cmpstring", 1},
+ {"runtime.intstring", 1},
+ {"runtime.slicebytetostring", 1},
+ {"runtime.slicebytetostringtmp", 1},
+ {"runtime.slicerunetostring", 1},
+ {"runtime.stringtoslicebyte", 1},
+ {"runtime.stringtoslicerune", 1},
+ {"runtime.slicecopy", 1},
+ {"runtime.slicestringcopy", 1},
+ {"runtime.decoderune", 1},
+ {"runtime.countrunes", 1},
+ {"runtime.convI2I", 1},
+ {"runtime.convT16", 1},
+ {"runtime.convT32", 1},
+ {"runtime.convT64", 1},
+ {"runtime.convTstring", 1},
+ {"runtime.convTslice", 1},
+ {"runtime.convT2E", 1},
+ {"runtime.convT2Enoptr", 1},
+ {"runtime.convT2I", 1},
+ {"runtime.convT2Inoptr", 1},
+ {"runtime.assertE2I", 1},
+ {"runtime.assertE2I2", 1},
+ {"runtime.assertI2I", 1},
+ {"runtime.assertI2I2", 1},
+ {"runtime.panicdottypeE", 1},
+ {"runtime.panicdottypeI", 1},
+ {"runtime.panicnildottype", 1},
+ {"runtime.ifaceeq", 1},
+ {"runtime.efaceeq", 1},
+ {"runtime.fastrand", 1},
+ {"runtime.makemap64", 1},
+ {"runtime.makemap", 1},
+ {"runtime.makemap_small", 1},
+ {"runtime.mapaccess1", 1},
+ {"runtime.mapaccess1_fast32", 1},
+ {"runtime.mapaccess1_fast64", 1},
+ {"runtime.mapaccess1_faststr", 1},
+ {"runtime.mapaccess1_fat", 1},
+ {"runtime.mapaccess2", 1},
+ {"runtime.mapaccess2_fast32", 1},
+ {"runtime.mapaccess2_fast64", 1},
+ {"runtime.mapaccess2_faststr", 1},
+ {"runtime.mapaccess2_fat", 1},
+ {"runtime.mapassign", 1},
+ {"runtime.mapassign_fast32", 1},
+ {"runtime.mapassign_fast32ptr", 1},
+ {"runtime.mapassign_fast64", 1},
+ {"runtime.mapassign_fast64ptr", 1},
+ {"runtime.mapassign_faststr", 1},
+ {"runtime.mapiterinit", 1},
+ {"runtime.mapdelete", 1},
+ {"runtime.mapdelete_fast32", 1},
+ {"runtime.mapdelete_fast64", 1},
+ {"runtime.mapdelete_faststr", 1},
+ {"runtime.mapiternext", 1},
+ {"runtime.mapclear", 1},
+ {"runtime.makechan64", 1},
+ {"runtime.makechan", 1},
+ {"runtime.chanrecv1", 1},
+ {"runtime.chanrecv2", 1},
+ {"runtime.chansend1", 1},
+ {"runtime.closechan", 1},
+ {"runtime.writeBarrier", 0},
+ {"runtime.typedmemmove", 1},
+ {"runtime.typedmemclr", 1},
+ {"runtime.typedslicecopy", 1},
+ {"runtime.selectnbsend", 1},
+ {"runtime.selectnbrecv", 1},
+ {"runtime.selectnbrecv2", 1},
+ {"runtime.selectsetpc", 1},
+ {"runtime.selectgo", 1},
+ {"runtime.block", 1},
+ {"runtime.makeslice", 1},
+ {"runtime.makeslice64", 1},
+ {"runtime.growslice", 1},
+ {"runtime.memmove", 1},
+ {"runtime.memclrNoHeapPointers", 1},
+ {"runtime.memclrHasPointers", 1},
+ {"runtime.memequal", 1},
+ {"runtime.memequal0", 1},
+ {"runtime.memequal8", 1},
+ {"runtime.memequal16", 1},
+ {"runtime.memequal32", 1},
+ {"runtime.memequal64", 1},
+ {"runtime.memequal128", 1},
+ {"runtime.f32equal", 1},
+ {"runtime.f64equal", 1},
+ {"runtime.c64equal", 1},
+ {"runtime.c128equal", 1},
+ {"runtime.strequal", 1},
+ {"runtime.interequal", 1},
+ {"runtime.nilinterequal", 1},
+ {"runtime.memhash", 1},
+ {"runtime.memhash0", 1},
+ {"runtime.memhash8", 1},
+ {"runtime.memhash16", 1},
+ {"runtime.memhash32", 1},
+ {"runtime.memhash64", 1},
+ {"runtime.memhash128", 1},
+ {"runtime.f32hash", 1},
+ {"runtime.f64hash", 1},
+ {"runtime.c64hash", 1},
+ {"runtime.c128hash", 1},
+ {"runtime.strhash", 1},
+ {"runtime.interhash", 1},
+ {"runtime.nilinterhash", 1},
+ {"runtime.int64div", 1},
+ {"runtime.uint64div", 1},
+ {"runtime.int64mod", 1},
+ {"runtime.uint64mod", 1},
+ {"runtime.float64toint64", 1},
+ {"runtime.float64touint64", 1},
+ {"runtime.float64touint32", 1},
+ {"runtime.int64tofloat64", 1},
+ {"runtime.uint64tofloat64", 1},
+ {"runtime.uint32tofloat64", 1},
+ {"runtime.complex128div", 1},
+ {"runtime.racefuncenter", 1},
+ {"runtime.racefuncenterfp", 1},
+ {"runtime.racefuncexit", 1},
+ {"runtime.raceread", 1},
+ {"runtime.racewrite", 1},
+ {"runtime.racereadrange", 1},
+ {"runtime.racewriterange", 1},
+ {"runtime.msanread", 1},
+ {"runtime.msanwrite", 1},
+ {"runtime.checkptrAlignment", 1},
+ {"runtime.checkptrArithmetic", 1},
+ {"runtime.x86HasPOPCNT", 0},
+ {"runtime.x86HasSSE41", 0},
+ {"runtime.arm64HasATOMICS", 0},
+ {"runtime.gcWriteBarrier", 0},
+ {"runtime.deferproc", 1},
+ {"runtime.deferprocStack", 1},
+ {"runtime.deferreturn", 1},
+ {"runtime.newproc", 1},
+ {"runtime.morestack", 0},
+ {"runtime.morestackc", 0},
+ {"runtime.morestack_noctxt", 0},
+}
diff --git a/src/cmd/internal/goobj2/funcinfo.go b/src/cmd/internal/goobj2/funcinfo.go
new file mode 100644
index 0000000000..8620931970
--- /dev/null
+++ b/src/cmd/internal/goobj2/funcinfo.go
@@ -0,0 +1,147 @@
+// Copyright 2019 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 goobj2
+
+import (
+ "bytes"
+ "encoding/binary"
+)
+
+// FuncInfo is serialized as a symbol (aux symbol). The symbol data is
+// the binary encoding of the struct below.
+//
+// TODO: make each pcdata a separate symbol?
+type FuncInfo struct {
+ NoSplit uint8
+
+ Args uint32
+ Locals uint32
+
+ Pcsp uint32
+ Pcfile uint32
+ Pcline uint32
+ Pcinline uint32
+ Pcdata []uint32
+ PcdataEnd uint32
+ Funcdataoff []uint32
+ File []SymRef // TODO: just use string?
+
+ InlTree []InlTreeNode
+}
+
+func (a *FuncInfo) Write(w *bytes.Buffer) {
+ w.WriteByte(a.NoSplit)
+
+ var b [4]byte
+ writeUint32 := func(x uint32) {
+ binary.LittleEndian.PutUint32(b[:], x)
+ w.Write(b[:])
+ }
+
+ writeUint32(a.Args)
+ writeUint32(a.Locals)
+
+ writeUint32(a.Pcsp)
+ writeUint32(a.Pcfile)
+ writeUint32(a.Pcline)
+ writeUint32(a.Pcinline)
+ writeUint32(uint32(len(a.Pcdata)))
+ for _, x := range a.Pcdata {
+ writeUint32(x)
+ }
+ writeUint32(a.PcdataEnd)
+ writeUint32(uint32(len(a.Funcdataoff)))
+ for _, x := range a.Funcdataoff {
+ writeUint32(x)
+ }
+ writeUint32(uint32(len(a.File)))
+ for _, f := range a.File {
+ writeUint32(f.PkgIdx)
+ writeUint32(f.SymIdx)
+ }
+ writeUint32(uint32(len(a.InlTree)))
+ for i := range a.InlTree {
+ a.InlTree[i].Write(w)
+ }
+}
+
+func (a *FuncInfo) Read(b []byte) {
+ a.NoSplit = b[0]
+ b = b[1:]
+
+ readUint32 := func() uint32 {
+ x := binary.LittleEndian.Uint32(b)
+ b = b[4:]
+ return x
+ }
+
+ a.Args = readUint32()
+ a.Locals = readUint32()
+
+ a.Pcsp = readUint32()
+ a.Pcfile = readUint32()
+ a.Pcline = readUint32()
+ a.Pcinline = readUint32()
+ pcdatalen := readUint32()
+ a.Pcdata = make([]uint32, pcdatalen)
+ for i := range a.Pcdata {
+ a.Pcdata[i] = readUint32()
+ }
+ a.PcdataEnd = readUint32()
+ funcdataofflen := readUint32()
+ a.Funcdataoff = make([]uint32, funcdataofflen)
+ for i := range a.Funcdataoff {
+ a.Funcdataoff[i] = readUint32()
+ }
+ filelen := readUint32()
+ a.File = make([]SymRef, filelen)
+ for i := range a.File {
+ a.File[i] = SymRef{readUint32(), readUint32()}
+ }
+ inltreelen := readUint32()
+ a.InlTree = make([]InlTreeNode, inltreelen)
+ for i := range a.InlTree {
+ b = a.InlTree[i].Read(b)
+ }
+}
+
+// InlTreeNode is the serialized form of FileInfo.InlTree.
+type InlTreeNode struct {
+ Parent int32
+ File SymRef
+ Line int32
+ Func SymRef
+ ParentPC int32
+}
+
+func (inl *InlTreeNode) Write(w *bytes.Buffer) {
+ var b [4]byte
+ writeUint32 := func(x uint32) {
+ binary.LittleEndian.PutUint32(b[:], x)
+ w.Write(b[:])
+ }
+ writeUint32(uint32(inl.Parent))
+ writeUint32(inl.File.PkgIdx)
+ writeUint32(inl.File.SymIdx)
+ writeUint32(uint32(inl.Line))
+ writeUint32(inl.Func.PkgIdx)
+ writeUint32(inl.Func.SymIdx)
+ writeUint32(uint32(inl.ParentPC))
+}
+
+// Read an InlTreeNode from b, return the remaining bytes.
+func (inl *InlTreeNode) Read(b []byte) []byte {
+ readUint32 := func() uint32 {
+ x := binary.LittleEndian.Uint32(b)
+ b = b[4:]
+ return x
+ }
+ inl.Parent = int32(readUint32())
+ inl.File = SymRef{readUint32(), readUint32()}
+ inl.Line = int32(readUint32())
+ inl.Func = SymRef{readUint32(), readUint32()}
+ inl.ParentPC = int32(readUint32())
+ return b
+}
diff --git a/src/cmd/internal/goobj2/mkbuiltin.go b/src/cmd/internal/goobj2/mkbuiltin.go
new file mode 100644
index 0000000000..0061aeb237
--- /dev/null
+++ b/src/cmd/internal/goobj2/mkbuiltin.go
@@ -0,0 +1,124 @@
+// Copyright 2019 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.
+
+// +build ignore
+
+// Generate builtinlist.go from cmd/compile/internal/gc/builtin/runtime.go.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+)
+
+var stdout = flag.Bool("stdout", false, "write to stdout instead of builtinlist.go")
+
+func main() {
+ flag.Parse()
+
+ var b bytes.Buffer
+ fmt.Fprintln(&b, "// Code generated by mkbuiltin.go. DO NOT EDIT.")
+ fmt.Fprintln(&b)
+ fmt.Fprintln(&b, "package goobj2")
+
+ mkbuiltin(&b)
+
+ out, err := format.Source(b.Bytes())
+ if err != nil {
+ log.Fatal(err)
+ }
+ if *stdout {
+ _, err = os.Stdout.Write(out)
+ } else {
+ err = ioutil.WriteFile("builtinlist.go", out, 0666)
+ }
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func mkbuiltin(w io.Writer) {
+ pkg := "runtime"
+ fset := token.NewFileSet()
+ path := filepath.Join("..", "..", "compile", "internal", "gc", "builtin", "runtime.go")
+ f, err := parser.ParseFile(fset, path, nil, 0)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ decls := make(map[string]bool)
+
+ fmt.Fprintf(w, "var builtins = [...]struct{ name string; abi int }{\n")
+ for _, decl := range f.Decls {
+ switch decl := decl.(type) {
+ case *ast.FuncDecl:
+ if decl.Recv != nil {
+ log.Fatal("methods unsupported")
+ }
+ if decl.Body != nil {
+ log.Fatal("unexpected function body")
+ }
+ declName := pkg + "." + decl.Name.Name
+ decls[declName] = true
+ fmt.Fprintf(w, "{%q, 1},\n", declName) // functions are ABIInternal (1)
+ case *ast.GenDecl:
+ if decl.Tok == token.IMPORT {
+ continue
+ }
+ if decl.Tok != token.VAR {
+ log.Fatal("unhandled declaration kind", decl.Tok)
+ }
+ for _, spec := range decl.Specs {
+ spec := spec.(*ast.ValueSpec)
+ if len(spec.Values) != 0 {
+ log.Fatal("unexpected values")
+ }
+ for _, name := range spec.Names {
+ declName := pkg + "." + name.Name
+ decls[declName] = true
+ fmt.Fprintf(w, "{%q, 0},\n", declName) // variables are ABI0
+ }
+ }
+ default:
+ log.Fatal("unhandled decl type", decl)
+ }
+ }
+
+ // The list above only contains ones that are used by the frontend.
+ // The backend may create more references of builtin functions.
+ // Add them.
+ for _, b := range extra {
+ name := pkg + "." + b.name
+ if decls[name] {
+ log.Fatalf("%q already added -- mkbuiltin.go out of sync?", name)
+ }
+ fmt.Fprintf(w, "{%q, %d},\n", name, b.abi)
+ }
+ fmt.Fprintln(w, "}")
+}
+
+var extra = [...]struct {
+ name string
+ abi int
+}{
+ {"gcWriteBarrier", 0}, // asm function, ABI0
+ {"deferproc", 1},
+ {"deferprocStack", 1},
+ {"deferreturn", 1},
+ {"newproc", 1},
+ {"morestack", 0}, // asm function, ABI0
+ {"morestackc", 0}, // asm function, ABI0
+ {"morestack_noctxt", 0}, // asm function, ABI0
+}
diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go
new file mode 100644
index 0000000000..4c364b0c54
--- /dev/null
+++ b/src/cmd/internal/goobj2/objfile.go
@@ -0,0 +1,587 @@
+// Copyright 2019 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 new object file format, reading and writing.
+
+package goobj2 // TODO: replace the goobj package?
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "unsafe"
+)
+
+// New object file format.
+//
+// Header struct {
+// Magic [...]byte // "\x00go114LD"
+// Flags uint32
+// // TODO: Fingerprint
+// Offsets [...]uint32 // byte offset of each block below
+// }
+//
+// Strings [...]struct {
+// Len uint32
+// Data [...]byte
+// }
+//
+// Autolib [...]stringOff // imported packages (for file loading) // TODO: add fingerprints
+// PkgIndex [...]stringOff // referenced packages by index
+//
+// DwarfFiles [...]stringOff
+//
+// SymbolDefs [...]struct {
+// Name stringOff
+// ABI uint16
+// Type uint8
+// Flag uint8
+// Size uint32
+// }
+// NonPkgDefs [...]struct { // non-pkg symbol definitions
+// ... // same as SymbolDefs
+// }
+// NonPkgRefs [...]struct { // non-pkg symbol references
+// ... // same as SymbolDefs
+// }
+//
+// RelocIndex [...]uint32 // index to Relocs
+// AuxIndex [...]uint32 // index to Aux
+// DataIndex [...]uint32 // offset to Data
+//
+// Relocs [...]struct {
+// Off int32
+// Size uint8
+// Type uint8
+// Add int64
+// Sym symRef
+// }
+//
+// Aux [...]struct {
+// Type uint8
+// Sym symRef
+// }
+//
+// Data [...]byte
+// Pcdata [...]byte
+//
+// stringOff is a uint32 (?) offset that points to the corresponding
+// string, which is a uint32 length followed by that number of bytes.
+//
+// symRef is struct { PkgIdx, SymIdx uint32 }.
+//
+// Slice type (e.g. []symRef) is encoded as a length prefix (uint32)
+// followed by that number of elements.
+//
+// The types below correspond to the encoded data structure in the
+// object file.
+
+// Symbol indexing.
+//
+// Each symbol is referenced with a pair of indices, { PkgIdx, SymIdx },
+// as the symRef struct above.
+//
+// PkgIdx is either a predeclared index (see PkgIdxNone below) or
+// an index of an imported package. For the latter case, PkgIdx is the
+// index of the package in the PkgIndex array. 0 is an invalid index.
+//
+// SymIdx is the index of the symbol in the given package.
+// - If PkgIdx is PkgIdxSelf, SymIdx is the index of the symbol in the
+// SymbolDefs array.
+// - If PkgIdx is PkgIdxNone, SymIdx is the index of the symbol in the
+// NonPkgDefs array (could natually overflow to NonPkgRefs array).
+// - Otherwise, SymIdx is the index of the symbol in some other package's
+// SymbolDefs array.
+//
+// {0, 0} represents a nil symbol. Otherwise PkgIdx should not be 0.
+//
+// RelocIndex, AuxIndex, and DataIndex contains indices/offsets to
+// Relocs/Aux/Data blocks, one element per symbol, first for all the
+// defined symbols, then all the defined non-package symbols, in the
+// same order of SymbolDefs/NonPkgDefs arrays. For N total defined
+// symbols, the array is of length N+1. The last element is the total
+// number of relocations (aux symbols, data blocks, etc.).
+//
+// They can be accessed by index. For the i-th symbol, its relocations
+// are the RelocIndex[i]-th (inclusive) to RelocIndex[i+1]-th (exclusive)
+// elements in the Relocs array. Aux/Data are likewise. (The index is
+// 0-based.)
+
+// Auxiliary symbols.
+//
+// Each symbol may (or may not) be associated with a number of auxiliary
+// symbols. They are described in the Aux block. See Aux struct below.
+// Currently a symbol's Gotype and FuncInfo are auxiliary symbols. We
+// may make use of aux symbols in more cases, e.g. DWARF symbols.
+
+// Package Index.
+const (
+ PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols
+ PkgIdxBuiltin // Predefined symbols // TODO: not used for now, we could use it for compiler-generated symbols like runtime.newobject
+ PkgIdxSelf // Symbols defined in the current package
+ PkgIdxInvalid = 0
+ // The index of other referenced packages starts from 1.
+)
+
+// Blocks
+const (
+ BlkAutolib = iota
+ BlkPkgIdx
+ BlkDwarfFile
+ BlkSymdef
+ BlkNonpkgdef
+ BlkNonpkgref
+ BlkRelocIdx
+ BlkAuxIdx
+ BlkDataIdx
+ BlkReloc
+ BlkAux
+ BlkData
+ BlkPcdata
+ NBlk
+)
+
+// File header.
+// TODO: probably no need to export this.
+type Header struct {
+ Magic string
+ Flags uint32
+ Offsets [NBlk]uint32
+}
+
+const Magic = "\x00go114LD"
+
+func (h *Header) Write(w *Writer) {
+ w.RawString(h.Magic)
+ w.Uint32(h.Flags)
+ for _, x := range h.Offsets {
+ w.Uint32(x)
+ }
+}
+
+func (h *Header) Read(r *Reader) error {
+ b := r.BytesAt(0, len(Magic))
+ h.Magic = string(b)
+ if h.Magic != Magic {
+ return errors.New("wrong magic, not a Go object file")
+ }
+ off := uint32(len(h.Magic))
+ h.Flags = r.uint32At(off)
+ off += 4
+ for i := range h.Offsets {
+ h.Offsets[i] = r.uint32At(off)
+ off += 4
+ }
+ return nil
+}
+
+func (h *Header) Size() int {
+ return len(h.Magic) + 4 + 4*len(h.Offsets)
+}
+
+// Symbol definition.
+type Sym struct {
+ Name string
+ ABI uint16
+ Type uint8
+ Flag uint8
+ Siz uint32
+}
+
+const SymABIstatic = ^uint16(0)
+
+const (
+ ObjFlagShared = 1 << iota
+)
+
+const (
+ SymFlagDupok = 1 << iota
+ SymFlagLocal
+ SymFlagTypelink
+ SymFlagLeaf
+ SymFlagCFunc
+ SymFlagReflectMethod
+ SymFlagGoType
+ SymFlagTopFrame
+)
+
+func (s *Sym) Write(w *Writer) {
+ w.StringRef(s.Name)
+ w.Uint16(s.ABI)
+ w.Uint8(s.Type)
+ w.Uint8(s.Flag)
+ w.Uint32(s.Siz)
+}
+
+func (s *Sym) Read(r *Reader, off uint32) {
+ s.Name = r.StringRef(off)
+ s.ABI = r.uint16At(off + 4)
+ s.Type = r.uint8At(off + 6)
+ s.Flag = r.uint8At(off + 7)
+ s.Siz = r.uint32At(off + 8)
+}
+
+func (s *Sym) Size() int {
+ return 4 + 2 + 1 + 1 + 4
+}
+
+func (s *Sym) Dupok() bool { return s.Flag&SymFlagDupok != 0 }
+func (s *Sym) Local() bool { return s.Flag&SymFlagLocal != 0 }
+func (s *Sym) Typelink() bool { return s.Flag&SymFlagTypelink != 0 }
+func (s *Sym) Leaf() bool { return s.Flag&SymFlagLeaf != 0 }
+func (s *Sym) CFunc() bool { return s.Flag&SymFlagCFunc != 0 }
+func (s *Sym) ReflectMethod() bool { return s.Flag&SymFlagReflectMethod != 0 }
+func (s *Sym) IsGoType() bool { return s.Flag&SymFlagGoType != 0 }
+func (s *Sym) TopFrame() bool { return s.Flag&SymFlagTopFrame != 0 }
+
+// Symbol reference.
+type SymRef struct {
+ PkgIdx uint32
+ SymIdx uint32
+}
+
+func (s *SymRef) Write(w *Writer) {
+ w.Uint32(s.PkgIdx)
+ w.Uint32(s.SymIdx)
+}
+
+func (s *SymRef) Read(r *Reader, off uint32) {
+ s.PkgIdx = r.uint32At(off)
+ s.SymIdx = r.uint32At(off + 4)
+}
+
+func (s *SymRef) Size() int {
+ return 4 + 4
+}
+
+// Relocation.
+type Reloc struct {
+ Off int32
+ Siz uint8
+ Type uint8
+ Add int64
+ Sym SymRef
+}
+
+func (r *Reloc) Write(w *Writer) {
+ w.Uint32(uint32(r.Off))
+ w.Uint8(r.Siz)
+ w.Uint8(r.Type)
+ w.Uint64(uint64(r.Add))
+ r.Sym.Write(w)
+}
+
+func (o *Reloc) Read(r *Reader, off uint32) {
+ o.Off = r.int32At(off)
+ o.Siz = r.uint8At(off + 4)
+ o.Type = r.uint8At(off + 5)
+ o.Add = r.int64At(off + 6)
+ o.Sym.Read(r, off+14)
+}
+
+func (r *Reloc) Size() int {
+ return 4 + 1 + 1 + 8 + r.Sym.Size()
+}
+
+// Aux symbol info.
+type Aux struct {
+ Type uint8
+ Sym SymRef
+}
+
+// Aux Type
+const (
+ AuxGotype = iota
+ AuxFuncInfo
+ AuxFuncdata
+ AuxDwarfInfo
+ AuxDwarfLoc
+ AuxDwarfRanges
+ AuxDwarfLines
+
+ // TODO: more. Pcdata?
+)
+
+func (a *Aux) Write(w *Writer) {
+ w.Uint8(a.Type)
+ a.Sym.Write(w)
+}
+
+func (a *Aux) Read(r *Reader, off uint32) {
+ a.Type = r.uint8At(off)
+ a.Sym.Read(r, off+1)
+}
+
+func (a *Aux) Size() int {
+ return 1 + a.Sym.Size()
+}
+
+type Writer struct {
+ wr *bio.Writer
+ stringMap map[string]uint32
+ off uint32 // running offset
+}
+
+func NewWriter(wr *bio.Writer) *Writer {
+ return &Writer{wr: wr, stringMap: make(map[string]uint32)}
+}
+
+func (w *Writer) AddString(s string) {
+ if _, ok := w.stringMap[s]; ok {
+ return
+ }
+ w.stringMap[s] = w.off
+ w.Uint32(uint32(len(s)))
+ w.RawString(s)
+}
+
+func (w *Writer) StringRef(s string) {
+ off, ok := w.stringMap[s]
+ if !ok {
+ panic(fmt.Sprintf("writeStringRef: string not added: %q", s))
+ }
+ w.Uint32(off)
+}
+
+func (w *Writer) RawString(s string) {
+ w.wr.WriteString(s)
+ w.off += uint32(len(s))
+}
+
+func (w *Writer) Bytes(s []byte) {
+ w.wr.Write(s)
+ w.off += uint32(len(s))
+}
+
+func (w *Writer) Uint64(x uint64) {
+ var b [8]byte
+ binary.LittleEndian.PutUint64(b[:], x)
+ w.wr.Write(b[:])
+ w.off += 8
+}
+
+func (w *Writer) Uint32(x uint32) {
+ var b [4]byte
+ binary.LittleEndian.PutUint32(b[:], x)
+ w.wr.Write(b[:])
+ w.off += 4
+}
+
+func (w *Writer) Uint16(x uint16) {
+ var b [2]byte
+ binary.LittleEndian.PutUint16(b[:], x)
+ w.wr.Write(b[:])
+ w.off += 2
+}
+
+func (w *Writer) Uint8(x uint8) {
+ w.wr.WriteByte(x)
+ w.off++
+}
+
+func (w *Writer) Offset() uint32 {
+ return w.off
+}
+
+type Reader struct {
+ b []byte // mmapped bytes, if not nil
+ readonly bool // whether b is backed with read-only memory
+
+ rd io.ReaderAt
+ start uint32
+ h Header // keep block offsets
+}
+
+func NewReaderFromBytes(b []byte, readonly bool) *Reader {
+ r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), start: 0}
+ err := r.h.Read(r)
+ if err != nil {
+ return nil
+ }
+ return r
+}
+
+func (r *Reader) BytesAt(off uint32, len int) []byte {
+ if len == 0 {
+ return nil
+ }
+ end := int(off) + len
+ return r.b[int(off):end:end]
+}
+
+func (r *Reader) uint64At(off uint32) uint64 {
+ b := r.BytesAt(off, 8)
+ return binary.LittleEndian.Uint64(b)
+}
+
+func (r *Reader) int64At(off uint32) int64 {
+ return int64(r.uint64At(off))
+}
+
+func (r *Reader) uint32At(off uint32) uint32 {
+ b := r.BytesAt(off, 4)
+ return binary.LittleEndian.Uint32(b)
+}
+
+func (r *Reader) int32At(off uint32) int32 {
+ return int32(r.uint32At(off))
+}
+
+func (r *Reader) uint16At(off uint32) uint16 {
+ b := r.BytesAt(off, 2)
+ return binary.LittleEndian.Uint16(b)
+}
+
+func (r *Reader) uint8At(off uint32) uint8 {
+ b := r.BytesAt(off, 1)
+ return b[0]
+}
+
+func (r *Reader) StringAt(off uint32) string {
+ l := r.uint32At(off)
+ b := r.b[off+4 : off+4+l]
+ if r.readonly {
+ return toString(b) // backed by RO memory, ok to make unsafe string
+ }
+ return string(b)
+}
+
+func toString(b []byte) string {
+ type stringHeader struct {
+ str unsafe.Pointer
+ len int
+ }
+
+ if len(b) == 0 {
+ return ""
+ }
+ ss := stringHeader{str: unsafe.Pointer(&b[0]), len: len(b)}
+ s := *(*string)(unsafe.Pointer(&ss))
+ return s
+}
+
+func (r *Reader) StringRef(off uint32) string {
+ return r.StringAt(r.uint32At(off))
+}
+
+func (r *Reader) Autolib() []string {
+ n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / 4
+ s := make([]string, n)
+ for i := range s {
+ off := r.h.Offsets[BlkAutolib] + uint32(i)*4
+ s[i] = r.StringRef(off)
+ }
+ return s
+}
+
+func (r *Reader) Pkglist() []string {
+ n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / 4
+ s := make([]string, n)
+ for i := range s {
+ off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4
+ s[i] = r.StringRef(off)
+ }
+ return s
+}
+
+func (r *Reader) NPkg() int {
+ return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / 4
+}
+
+func (r *Reader) Pkg(i int) string {
+ off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4
+ return r.StringRef(off)
+}
+
+func (r *Reader) NDwarfFile() int {
+ return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / 4
+}
+
+func (r *Reader) DwarfFile(i int) string {
+ off := r.h.Offsets[BlkDwarfFile] + uint32(i)*4
+ return r.StringRef(off)
+}
+
+func (r *Reader) NSym() int {
+ symsiz := (&Sym{}).Size()
+ return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / symsiz
+}
+
+func (r *Reader) NNonpkgdef() int {
+ symsiz := (&Sym{}).Size()
+ return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / symsiz
+}
+
+func (r *Reader) NNonpkgref() int {
+ symsiz := (&Sym{}).Size()
+ return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / symsiz
+}
+
+// SymOff returns the offset of the i-th symbol.
+func (r *Reader) SymOff(i int) uint32 {
+ symsiz := (&Sym{}).Size()
+ return r.h.Offsets[BlkSymdef] + uint32(i*symsiz)
+}
+
+// NReloc returns the number of relocations of the i-th symbol.
+func (r *Reader) NReloc(i int) int {
+ relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
+ return int(r.uint32At(relocIdxOff+4) - r.uint32At(relocIdxOff))
+}
+
+// RelocOff returns the offset of the j-th relocation of the i-th symbol.
+func (r *Reader) RelocOff(i int, j int) uint32 {
+ relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
+ relocIdx := r.uint32At(relocIdxOff)
+ relocsiz := (&Reloc{}).Size()
+ return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(relocsiz)
+}
+
+// NAux returns the number of aux symbols of the i-th symbol.
+func (r *Reader) NAux(i int) int {
+ auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4)
+ return int(r.uint32At(auxIdxOff+4) - r.uint32At(auxIdxOff))
+}
+
+// AuxOff returns the offset of the j-th aux symbol of the i-th symbol.
+func (r *Reader) AuxOff(i int, j int) uint32 {
+ auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4)
+ auxIdx := r.uint32At(auxIdxOff)
+ auxsiz := (&Aux{}).Size()
+ return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(auxsiz)
+}
+
+// DataOff returns the offset of the i-th symbol's data.
+func (r *Reader) DataOff(i int) uint32 {
+ dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4)
+ return r.h.Offsets[BlkData] + r.uint32At(dataIdxOff)
+}
+
+// DataSize returns the size of the i-th symbol's data.
+func (r *Reader) DataSize(i int) int {
+ return int(r.DataOff(i+1) - r.DataOff(i))
+}
+
+// Data returns the i-th symbol's data.
+func (r *Reader) Data(i int) []byte {
+ return r.BytesAt(r.DataOff(i), r.DataSize(i))
+}
+
+// AuxDataBase returns the base offset of the aux data block.
+func (r *Reader) PcdataBase() uint32 {
+ return r.h.Offsets[BlkPcdata]
+}
+
+// ReadOnly returns whether r.BytesAt returns read-only bytes.
+func (r *Reader) ReadOnly() bool {
+ return r.readonly
+}
+
+// Flags returns the flag bits read from the object file header.
+func (r *Reader) Flags() uint32 {
+ return r.h.Flags
+}
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 468e9402ee..76c6f261a3 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -388,6 +388,10 @@ type LSym struct {
R []Reloc
Func *FuncInfo
+
+ Pkg string
+ PkgIdx int32
+ SymIdx int32 // TODO: replace RefIdx
}
// A FuncInfo contains extra fields for STEXT symbols.
@@ -410,6 +414,8 @@ type FuncInfo struct {
GCRegs *LSym
StackObjects *LSym
OpenCodedDeferInfo *LSym
+
+ FuncInfoSym *LSym
}
type InlMark struct {
@@ -461,7 +467,7 @@ const (
)
// Attribute is a set of symbol attributes.
-type Attribute uint16
+type Attribute uint32
const (
AttrDuplicateOK Attribute = 1 << iota
@@ -502,6 +508,10 @@ const (
// keep unwinding beyond this frame.
AttrTopFrame
+ // Indexed indicates this symbol has been assigned with an index (when using the
+ // new object file format).
+ AttrIndexed
+
// attrABIBase is the value at which the ABI is encoded in
// Attribute. This must be last; all bits after this are
// assumed to be an ABI value.
@@ -525,6 +535,7 @@ func (a Attribute) NoFrame() bool { return a&AttrNoFrame != 0 }
func (a Attribute) Static() bool { return a&AttrStatic != 0 }
func (a Attribute) WasInlined() bool { return a&AttrWasInlined != 0 }
func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 }
+func (a Attribute) Indexed() bool { return a&AttrIndexed != 0 }
func (a *Attribute) Set(flag Attribute, value bool) {
if value {
@@ -559,6 +570,7 @@ var textAttrStrings = [...]struct {
{bit: AttrStatic, s: "STATIC"},
{bit: AttrWasInlined, s: ""},
{bit: AttrTopFrame, s: "TOPFRAME"},
+ {bit: AttrIndexed, s: ""},
}
// TextAttrString formats a for printing in as part of a TEXT prog.
@@ -637,8 +649,10 @@ type Link struct {
Debugpcln string
Flag_shared bool
Flag_dynlink bool
+ Flag_linkshared bool
Flag_optimize bool
Flag_locationlists bool
+ Flag_newobj bool // use new object file format
Bso *bufio.Writer
Pathname string
hashmu sync.Mutex // protects hash, funchash
@@ -672,6 +686,14 @@ type Link struct {
// TODO(austin): Replace this with ABI wrappers once the ABIs
// actually diverge.
ABIAliases []*LSym
+
+ // pkgIdx maps package path to index. The index is used for
+ // symbol reference in the object file.
+ pkgIdx map[string]int32
+
+ defs []*LSym // list of defined symbols in the current package
+ nonpkgdefs []*LSym // list of defined non-package symbols
+ nonpkgrefs []*LSym // list of referenced non-package symbols
}
func (ctxt *Link) Diag(format string, args ...interface{}) {
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index ab5627c0dd..76fbc58f10 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -8,6 +8,7 @@ package obj
import (
"bufio"
+ "cmd/internal/bio"
"cmd/internal/dwarf"
"cmd/internal/objabi"
"cmd/internal/sys"
@@ -80,7 +81,13 @@ func newObjWriter(ctxt *Link, b *bufio.Writer, pkgpath string) *objWriter {
}
}
-func WriteObjFile(ctxt *Link, b *bufio.Writer, pkgpath string) {
+func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) {
+ if ctxt.Flag_newobj {
+ WriteObjFile2(ctxt, bout, pkgpath)
+ return
+ }
+
+ b := bout.Writer
w := newObjWriter(ctxt, b, pkgpath)
// Magic header
@@ -221,8 +228,7 @@ func (w *objWriter) writeRefs(s *LSym) {
}
}
-func (w *objWriter) writeSymDebug(s *LSym) {
- ctxt := w.ctxt
+func (ctxt *Link) writeSymDebug(s *LSym) {
fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
if s.Type != 0 {
fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
@@ -302,7 +308,7 @@ func (w *objWriter) writeSymDebug(s *LSym) {
func (w *objWriter) writeSym(s *LSym) {
ctxt := w.ctxt
if ctxt.Debugasm > 0 {
- w.writeSymDebug(s)
+ w.ctxt.writeSymDebug(s)
}
w.wr.WriteByte(symPrefix)
diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go
new file mode 100644
index 0000000000..69019e033d
--- /dev/null
+++ b/src/cmd/internal/obj/objfile2.go
@@ -0,0 +1,429 @@
+// Copyright 2019 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.
+
+// Writing Go object files.
+
+package obj
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/goobj2"
+ "cmd/internal/objabi"
+ "fmt"
+ "path/filepath"
+ "strings"
+)
+
+// Entry point of writing new object file.
+func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) {
+ if ctxt.Debugasm > 0 {
+ ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
+ }
+
+ genFuncInfoSyms(ctxt)
+
+ w := writer{
+ Writer: goobj2.NewWriter(b),
+ ctxt: ctxt,
+ pkgpath: objabi.PathToPrefix(pkgpath),
+ }
+
+ start := b.Offset()
+ w.init()
+
+ // Header
+ // We just reserve the space. We'll fill in the offsets later.
+ flags := uint32(0)
+ if ctxt.Flag_shared {
+ flags |= goobj2.ObjFlagShared
+ }
+ h := goobj2.Header{Magic: goobj2.Magic, Flags: flags}
+ h.Write(w.Writer)
+
+ // String table
+ w.StringTable()
+
+ // Autolib
+ h.Offsets[goobj2.BlkAutolib] = w.Offset()
+ for _, pkg := range ctxt.Imports {
+ w.StringRef(pkg)
+ }
+
+ // Package references
+ h.Offsets[goobj2.BlkPkgIdx] = w.Offset()
+ for _, pkg := range w.pkglist {
+ w.StringRef(pkg)
+ }
+
+ // DWARF file table
+ h.Offsets[goobj2.BlkDwarfFile] = w.Offset()
+ for _, f := range ctxt.PosTable.DebugLinesFileTable() {
+ w.StringRef(f)
+ }
+
+ // Symbol definitions
+ h.Offsets[goobj2.BlkSymdef] = w.Offset()
+ for _, s := range ctxt.defs {
+ w.Sym(s)
+ }
+
+ // Non-pkg symbol definitions
+ h.Offsets[goobj2.BlkNonpkgdef] = w.Offset()
+ for _, s := range ctxt.nonpkgdefs {
+ w.Sym(s)
+ }
+
+ // Non-pkg symbol references
+ h.Offsets[goobj2.BlkNonpkgref] = w.Offset()
+ for _, s := range ctxt.nonpkgrefs {
+ w.Sym(s)
+ }
+
+ // Reloc indexes
+ h.Offsets[goobj2.BlkRelocIdx] = w.Offset()
+ nreloc := uint32(0)
+ lists := [][]*LSym{ctxt.defs, ctxt.nonpkgdefs}
+ for _, list := range lists {
+ for _, s := range list {
+ w.Uint32(nreloc)
+ nreloc += uint32(len(s.R))
+ }
+ }
+ w.Uint32(nreloc)
+
+ // Symbol Info indexes
+ h.Offsets[goobj2.BlkAuxIdx] = w.Offset()
+ naux := uint32(0)
+ for _, list := range lists {
+ for _, s := range list {
+ w.Uint32(naux)
+ naux += uint32(nAuxSym(s))
+ }
+ }
+ w.Uint32(naux)
+
+ // Data indexes
+ h.Offsets[goobj2.BlkDataIdx] = w.Offset()
+ dataOff := uint32(0)
+ for _, list := range lists {
+ for _, s := range list {
+ w.Uint32(dataOff)
+ dataOff += uint32(len(s.P))
+ }
+ }
+ w.Uint32(dataOff)
+
+ // Relocs
+ h.Offsets[goobj2.BlkReloc] = w.Offset()
+ for _, list := range lists {
+ for _, s := range list {
+ for i := range s.R {
+ w.Reloc(&s.R[i])
+ }
+ }
+ }
+
+ // Aux symbol info
+ h.Offsets[goobj2.BlkAux] = w.Offset()
+ for _, list := range lists {
+ for _, s := range list {
+ w.Aux(s)
+ }
+ }
+
+ // Data
+ h.Offsets[goobj2.BlkData] = w.Offset()
+ for _, list := range lists {
+ for _, s := range list {
+ w.Bytes(s.P)
+ }
+ }
+
+ // Pcdata
+ h.Offsets[goobj2.BlkPcdata] = w.Offset()
+ for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms
+ if s.Func != nil {
+ pc := &s.Func.Pcln
+ w.Bytes(pc.Pcsp.P)
+ w.Bytes(pc.Pcfile.P)
+ w.Bytes(pc.Pcline.P)
+ w.Bytes(pc.Pcinline.P)
+ for i := range pc.Pcdata {
+ w.Bytes(pc.Pcdata[i].P)
+ }
+ }
+ }
+
+ // Fix up block offsets in the header
+ end := start + int64(w.Offset())
+ b.MustSeek(start, 0)
+ h.Write(w.Writer)
+ b.MustSeek(end, 0)
+}
+
+type writer struct {
+ *goobj2.Writer
+ ctxt *Link
+ pkgpath string // the package import path (escaped), "" if unknown
+ pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
+}
+
+// prepare package index list
+func (w *writer) init() {
+ w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1)
+ w.pkglist[0] = "" // dummy invalid package for index 0
+ for pkg, i := range w.ctxt.pkgIdx {
+ w.pkglist[i] = pkg
+ }
+}
+
+func (w *writer) StringTable() {
+ w.AddString("")
+ for _, pkg := range w.ctxt.Imports {
+ w.AddString(pkg)
+ }
+ for _, pkg := range w.pkglist {
+ w.AddString(pkg)
+ }
+ w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
+ if w.pkgpath != "" {
+ s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
+ }
+ w.AddString(s.Name)
+ })
+ w.ctxt.traverseSyms(traverseDefs, func(s *LSym) {
+ if s.Type != objabi.STEXT {
+ return
+ }
+ pc := &s.Func.Pcln
+ for _, f := range pc.File {
+ w.AddString(filepath.ToSlash(f))
+ }
+ for _, call := range pc.InlTree.nodes {
+ f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
+ w.AddString(filepath.ToSlash(f))
+ }
+ })
+ for _, f := range w.ctxt.PosTable.DebugLinesFileTable() {
+ w.AddString(f)
+ }
+}
+
+func (w *writer) Sym(s *LSym) {
+ abi := uint16(s.ABI())
+ if s.Static() {
+ abi = goobj2.SymABIstatic
+ }
+ flag := uint8(0)
+ if s.DuplicateOK() {
+ flag |= goobj2.SymFlagDupok
+ }
+ if s.Local() {
+ flag |= goobj2.SymFlagLocal
+ }
+ if s.MakeTypelink() {
+ flag |= goobj2.SymFlagTypelink
+ }
+ if s.Leaf() {
+ flag |= goobj2.SymFlagLeaf
+ }
+ if s.CFunc() {
+ flag |= goobj2.SymFlagCFunc
+ }
+ if s.ReflectMethod() {
+ flag |= goobj2.SymFlagReflectMethod
+ }
+ if s.TopFrame() {
+ flag |= goobj2.SymFlagTopFrame
+ }
+ if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
+ flag |= goobj2.SymFlagGoType
+ }
+ name := s.Name
+ if strings.HasPrefix(name, "gofile..") {
+ name = filepath.ToSlash(name)
+ }
+ o := goobj2.Sym{
+ Name: name,
+ ABI: abi,
+ Type: uint8(s.Type),
+ Flag: flag,
+ Siz: uint32(s.Size),
+ }
+ o.Write(w.Writer)
+}
+
+func makeSymRef(s *LSym) goobj2.SymRef {
+ if s == nil {
+ return goobj2.SymRef{}
+ }
+ if s.PkgIdx == 0 || !s.Indexed() {
+ fmt.Printf("unindexed symbol reference: %v\n", s)
+ panic("unindexed symbol reference")
+ }
+ return goobj2.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
+}
+
+func (w *writer) Reloc(r *Reloc) {
+ o := goobj2.Reloc{
+ Off: r.Off,
+ Siz: r.Siz,
+ Type: uint8(r.Type),
+ Add: r.Add,
+ Sym: makeSymRef(r.Sym),
+ }
+ o.Write(w.Writer)
+}
+
+func (w *writer) Aux(s *LSym) {
+ if s.Gotype != nil {
+ o := goobj2.Aux{
+ Type: goobj2.AuxGotype,
+ Sym: makeSymRef(s.Gotype),
+ }
+ o.Write(w.Writer)
+ }
+ if s.Func != nil {
+ o := goobj2.Aux{
+ Type: goobj2.AuxFuncInfo,
+ Sym: makeSymRef(s.Func.FuncInfoSym),
+ }
+ o.Write(w.Writer)
+
+ for _, d := range s.Func.Pcln.Funcdata {
+ o := goobj2.Aux{
+ Type: goobj2.AuxFuncdata,
+ Sym: makeSymRef(d),
+ }
+ o.Write(w.Writer)
+ }
+
+ if s.Func.dwarfInfoSym != nil {
+ o := goobj2.Aux{
+ Type: goobj2.AuxDwarfInfo,
+ Sym: makeSymRef(s.Func.dwarfInfoSym),
+ }
+ o.Write(w.Writer)
+ }
+ if s.Func.dwarfLocSym != nil {
+ o := goobj2.Aux{
+ Type: goobj2.AuxDwarfLoc,
+ Sym: makeSymRef(s.Func.dwarfLocSym),
+ }
+ o.Write(w.Writer)
+ }
+ if s.Func.dwarfRangesSym != nil {
+ o := goobj2.Aux{
+ Type: goobj2.AuxDwarfRanges,
+ Sym: makeSymRef(s.Func.dwarfRangesSym),
+ }
+ o.Write(w.Writer)
+ }
+ if s.Func.dwarfDebugLinesSym != nil {
+ o := goobj2.Aux{
+ Type: goobj2.AuxDwarfLines,
+ Sym: makeSymRef(s.Func.dwarfDebugLinesSym),
+ }
+ o.Write(w.Writer)
+ }
+ }
+}
+
+// return the number of aux symbols s have.
+func nAuxSym(s *LSym) int {
+ n := 0
+ if s.Gotype != nil {
+ n++
+ }
+ if s.Func != nil {
+ // FuncInfo is an aux symbol, each Funcdata is an aux symbol
+ n += 1 + len(s.Func.Pcln.Funcdata)
+ if s.Func.dwarfInfoSym != nil {
+ n++
+ }
+ if s.Func.dwarfLocSym != nil {
+ n++
+ }
+ if s.Func.dwarfRangesSym != nil {
+ n++
+ }
+ if s.Func.dwarfDebugLinesSym != nil {
+ n++
+ }
+ }
+ return n
+}
+
+// generate symbols for FuncInfo.
+func genFuncInfoSyms(ctxt *Link) {
+ infosyms := make([]*LSym, 0, len(ctxt.Text))
+ var pcdataoff uint32
+ var b bytes.Buffer
+ symidx := int32(len(ctxt.defs))
+ for _, s := range ctxt.Text {
+ if s.Func == nil {
+ continue
+ }
+ nosplit := uint8(0)
+ if s.NoSplit() {
+ nosplit = 1
+ }
+ o := goobj2.FuncInfo{
+ NoSplit: nosplit,
+ Args: uint32(s.Func.Args),
+ Locals: uint32(s.Func.Locals),
+ }
+ pc := &s.Func.Pcln
+ o.Pcsp = pcdataoff
+ pcdataoff += uint32(len(pc.Pcsp.P))
+ o.Pcfile = pcdataoff
+ pcdataoff += uint32(len(pc.Pcfile.P))
+ o.Pcline = pcdataoff
+ pcdataoff += uint32(len(pc.Pcline.P))
+ o.Pcinline = pcdataoff
+ pcdataoff += uint32(len(pc.Pcinline.P))
+ o.Pcdata = make([]uint32, len(pc.Pcdata))
+ for i, pcd := range pc.Pcdata {
+ o.Pcdata[i] = pcdataoff
+ pcdataoff += uint32(len(pcd.P))
+ }
+ o.PcdataEnd = pcdataoff
+ o.Funcdataoff = make([]uint32, len(pc.Funcdataoff))
+ for i, x := range pc.Funcdataoff {
+ o.Funcdataoff[i] = uint32(x)
+ }
+ o.File = make([]goobj2.SymRef, len(pc.File))
+ for i, f := range pc.File {
+ fsym := ctxt.Lookup(f)
+ o.File[i] = makeSymRef(fsym)
+ }
+ o.InlTree = make([]goobj2.InlTreeNode, len(pc.InlTree.nodes))
+ for i, inl := range pc.InlTree.nodes {
+ f, l := linkgetlineFromPos(ctxt, inl.Pos)
+ fsym := ctxt.Lookup(f)
+ o.InlTree[i] = goobj2.InlTreeNode{
+ Parent: int32(inl.Parent),
+ File: makeSymRef(fsym),
+ Line: l,
+ Func: makeSymRef(inl.Func),
+ ParentPC: inl.ParentPC,
+ }
+ }
+
+ o.Write(&b)
+ isym := &LSym{
+ Type: objabi.SDATA, // for now, I don't think it matters
+ PkgIdx: goobj2.PkgIdxSelf,
+ SymIdx: symidx,
+ P: append([]byte(nil), b.Bytes()...),
+ }
+ isym.Set(AttrIndexed, true)
+ symidx++
+ infosyms = append(infosyms, isym)
+ s.Func.FuncInfoSym = isym
+ b.Reset()
+ }
+ ctxt.defs = append(ctxt.defs, infosyms...)
+}
diff --git a/src/cmd/internal/obj/sizeof_test.go b/src/cmd/internal/obj/sizeof_test.go
index 05da9cc1e6..b5e170c694 100644
--- a/src/cmd/internal/obj/sizeof_test.go
+++ b/src/cmd/internal/obj/sizeof_test.go
@@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) {
_64bit uintptr // size on 64bit platforms
}{
{Addr{}, 32, 48},
- {LSym{}, 56, 104},
+ {LSym{}, 76, 128},
{Prog{}, 132, 200},
}
diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go
index 15a501c3aa..3ef886651f 100644
--- a/src/cmd/internal/obj/sym.go
+++ b/src/cmd/internal/obj/sym.go
@@ -32,10 +32,12 @@
package obj
import (
+ "cmd/internal/goobj2"
"cmd/internal/objabi"
"fmt"
"log"
"math"
+ "sort"
)
func Linknew(arch *LinkArch) *Link {
@@ -78,6 +80,13 @@ func (ctxt *Link) LookupStatic(name string) *LSym {
// LookupABI looks up a symbol with the given ABI.
// If it does not exist, it creates it.
func (ctxt *Link) LookupABI(name string, abi ABI) *LSym {
+ return ctxt.LookupABIInit(name, abi, nil)
+}
+
+// LookupABI looks up a symbol with the given ABI.
+// If it does not exist, it creates it and
+// passes it to init for one-time initialization.
+func (ctxt *Link) LookupABIInit(name string, abi ABI, init func(s *LSym)) *LSym {
var hash map[string]*LSym
switch abi {
case ABI0:
@@ -94,6 +103,9 @@ func (ctxt *Link) LookupABI(name string, abi ABI) *LSym {
s = &LSym{Name: name}
s.SetABI(abi)
hash[name] = s
+ if init != nil {
+ init(s)
+ }
}
ctxt.hashmu.Unlock()
return s
@@ -147,3 +159,167 @@ func (ctxt *Link) Int64Sym(i int64) *LSym {
s.Set(AttrLocal, true)
})
}
+
+// Assign index to symbols.
+// asm is set to true if this is called by the assembler (i.e. not the compiler),
+// in which case all the symbols are non-package (for now).
+func (ctxt *Link) NumberSyms(asm bool) {
+ if !ctxt.Flag_newobj {
+ return
+ }
+
+ if ctxt.Headtype == objabi.Haix {
+ // Data must be sorted to keep a constant order in TOC symbols.
+ // As they are created during Progedit, two symbols can be switched between
+ // two different compilations. Therefore, BuildID will be different.
+ // TODO: find a better place and optimize to only sort TOC symbols
+ sort.Slice(ctxt.Data, func(i, j int) bool {
+ return ctxt.Data[i].Name < ctxt.Data[j].Name
+ })
+ }
+
+ ctxt.pkgIdx = make(map[string]int32)
+ ctxt.defs = []*LSym{}
+ ctxt.nonpkgdefs = []*LSym{}
+
+ var idx, nonpkgidx int32 = 0, 0
+ ctxt.traverseSyms(traverseDefs, func(s *LSym) {
+ if isNonPkgSym(ctxt, asm, s) {
+ s.PkgIdx = goobj2.PkgIdxNone
+ s.SymIdx = nonpkgidx
+ if nonpkgidx != int32(len(ctxt.nonpkgdefs)) {
+ panic("bad index")
+ }
+ ctxt.nonpkgdefs = append(ctxt.nonpkgdefs, s)
+ nonpkgidx++
+ } else {
+ s.PkgIdx = goobj2.PkgIdxSelf
+ s.SymIdx = idx
+ if idx != int32(len(ctxt.defs)) {
+ panic("bad index")
+ }
+ ctxt.defs = append(ctxt.defs, s)
+ idx++
+ }
+ s.Set(AttrIndexed, true)
+ })
+
+ ipkg := int32(1) // 0 is invalid index
+ nonpkgdef := nonpkgidx
+ ctxt.traverseSyms(traverseRefs|traverseAux, func(rs *LSym) {
+ if rs.PkgIdx != goobj2.PkgIdxInvalid {
+ return
+ }
+ if !ctxt.Flag_linkshared {
+ // Assign special index for builtin symbols.
+ // Don't do it when linking against shared libraries, as the runtime
+ // may be in a different library.
+ if i := goobj2.BuiltinIdx(rs.Name, int(rs.ABI())); i != -1 {
+ rs.PkgIdx = goobj2.PkgIdxBuiltin
+ rs.SymIdx = int32(i)
+ rs.Set(AttrIndexed, true)
+ return
+ }
+ }
+ pkg := rs.Pkg
+ if pkg == "" || pkg == "\"\"" || pkg == "_" || !rs.Indexed() {
+ rs.PkgIdx = goobj2.PkgIdxNone
+ rs.SymIdx = nonpkgidx
+ rs.Set(AttrIndexed, true)
+ if nonpkgidx != nonpkgdef+int32(len(ctxt.nonpkgrefs)) {
+ panic("bad index")
+ }
+ ctxt.nonpkgrefs = append(ctxt.nonpkgrefs, rs)
+ nonpkgidx++
+ return
+ }
+ if k, ok := ctxt.pkgIdx[pkg]; ok {
+ rs.PkgIdx = k
+ return
+ }
+ rs.PkgIdx = ipkg
+ ctxt.pkgIdx[pkg] = ipkg
+ ipkg++
+ })
+}
+
+// Returns whether s is a non-package symbol, which needs to be referenced
+// by name instead of by index.
+func isNonPkgSym(ctxt *Link, asm bool, s *LSym) bool {
+ if asm && !s.Static() {
+ // asm symbols are referenced by name only, except static symbols
+ // which are file-local and can be referenced by index.
+ return true
+ }
+ if ctxt.Flag_linkshared {
+ // The referenced symbol may be in a different shared library so
+ // the linker cannot see its index.
+ return true
+ }
+ if s.Pkg == "_" {
+ // The frontend uses package "_" to mark symbols that should not
+ // be referenced by index, e.g. linkname'd symbols.
+ return true
+ }
+ if s.DuplicateOK() {
+ // Dupok symbol needs to be dedup'd by name.
+ return true
+ }
+ return false
+}
+
+type traverseFlag uint32
+
+const (
+ traverseDefs traverseFlag = 1 << iota
+ traverseRefs
+ traverseAux
+
+ traverseAll = traverseDefs | traverseRefs | traverseAux
+)
+
+// Traverse symbols based on flag, call fn for each symbol.
+func (ctxt *Link) traverseSyms(flag traverseFlag, fn func(*LSym)) {
+ lists := [][]*LSym{ctxt.Text, ctxt.Data, ctxt.ABIAliases}
+ for _, list := range lists {
+ for _, s := range list {
+ if flag&traverseDefs != 0 {
+ fn(s)
+ }
+ if flag&traverseRefs != 0 {
+ for _, r := range s.R {
+ if r.Sym != nil {
+ fn(r.Sym)
+ }
+ }
+ }
+ if flag&traverseAux != 0 {
+ if s.Gotype != nil {
+ fn(s.Gotype)
+ }
+ if s.Type == objabi.STEXT {
+ pc := &s.Func.Pcln
+ for _, d := range pc.Funcdata {
+ if d != nil {
+ fn(d)
+ }
+ }
+ for _, f := range pc.File {
+ if fsym := ctxt.Lookup(f); fsym != nil {
+ fn(fsym)
+ }
+ }
+ for _, call := range pc.InlTree.nodes {
+ if call.Func != nil {
+ fn(call.Func)
+ }
+ f, _ := linkgetlineFromPos(ctxt, call.Pos)
+ if fsym := ctxt.Lookup(f); fsym != nil {
+ fn(fsym)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index f4aa78f45c..b009f36064 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -402,7 +402,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
case objabi.R_ADDRCUOFF:
// debug_range and debug_loc elements use this relocation type to get an
// offset from the start of the compile unit.
- o = Symaddr(r.Sym) + r.Add - Symaddr(r.Sym.Unit.Lib.Textp[0])
+ o = Symaddr(r.Sym) + r.Add - Symaddr(r.Sym.Unit.Textp[0])
// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
case objabi.R_GOTPCREL:
@@ -1086,13 +1086,13 @@ func (p *GCProg) AddSym(s *sym.Symbol) {
}
ptrsize := int64(p.ctxt.Arch.PtrSize)
- nptr := decodetypePtrdata(p.ctxt.Arch, typ) / ptrsize
+ nptr := decodetypePtrdata(p.ctxt.Arch, typ.P) / ptrsize
if debugGCProg {
fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr)
}
- if decodetypeUsegcprog(p.ctxt.Arch, typ) == 0 {
+ if decodetypeUsegcprog(p.ctxt.Arch, typ.P) == 0 {
// Copy pointers from mask into program.
mask := decodetypeGcmask(p.ctxt, typ)
for i := int64(0); i < nptr; i++ {
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 0bc6cc457a..e79207e2b8 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -46,6 +46,15 @@ import (
//
// Any unreached text symbols are removed from ctxt.Textp.
func deadcode(ctxt *Link) {
+ if ctxt.Debugvlog != 0 {
+ ctxt.Logf("deadcode\n")
+ }
+
+ if *flagNewobj {
+ deadcode2(ctxt)
+ return
+ }
+
d := &deadcodepass{
ctxt: ctxt,
ifaceMethod: make(map[methodsig]bool),
@@ -114,22 +123,70 @@ func deadcode(ctxt *Link) {
}
}
- for _, lib := range ctxt.Library {
- lib.Textp = lib.Textp[:0]
- }
+ addToTextp(ctxt)
+}
+func addToTextp(ctxt *Link) {
// Remove dead text but keep file information (z symbols).
- textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
+ textp := []*sym.Symbol{}
for _, s := range ctxt.Textp {
if s.Attr.Reachable() {
- if s.Unit != nil {
- s.Unit.Lib.Textp = append(s.Unit.Lib.Textp, s)
- s.Unit.Textp = append(s.Unit.Textp, s)
- }
textp = append(textp, s)
}
}
+
+ // Put reachable text symbols into Textp.
+ // do it in postorder so that packages are laid down in dependency order
+ // internal first, then everything else
+ ctxt.Library = postorder(ctxt.Library)
+ for _, doInternal := range [2]bool{true, false} {
+ for _, lib := range ctxt.Library {
+ if isRuntimeDepPkg(lib.Pkg) != doInternal {
+ continue
+ }
+ libtextp := lib.Textp[:0]
+ for _, s := range lib.Textp {
+ if s.Attr.Reachable() {
+ textp = append(textp, s)
+ libtextp = append(libtextp, s)
+ if s.Unit != nil {
+ s.Unit.Textp = append(s.Unit.Textp, s)
+ }
+ }
+ }
+ for _, s := range lib.DupTextSyms {
+ if s.Attr.Reachable() && !s.Attr.OnList() {
+ textp = append(textp, s)
+ libtextp = append(libtextp, s)
+ if s.Unit != nil {
+ s.Unit.Textp = append(s.Unit.Textp, s)
+ }
+ s.Attr |= sym.AttrOnList
+ // dupok symbols may be defined in multiple packages. its
+ // associated package is chosen sort of arbitrarily (the
+ // first containing package that the linker loads). canonicalize
+ // it here to the package with which it will be laid down
+ // in text.
+ s.File = objabi.PathToPrefix(lib.Pkg)
+ }
+ }
+ lib.Textp = libtextp
+ }
+ }
ctxt.Textp = textp
+
+ if len(ctxt.Shlibs) > 0 {
+ // We might have overwritten some functions above (this tends to happen for the
+ // autogenerated type equality/hashing functions) and we don't want to generated
+ // pcln table entries for these any more so remove them from Textp.
+ textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
+ for _, s := range ctxt.Textp {
+ if s.Type != sym.SDYNIMPORT {
+ textp = append(textp, s)
+ }
+ }
+ ctxt.Textp = textp
+ }
}
// methodref holds the relocations from a receiver type symbol to its
@@ -274,7 +331,7 @@ func (d *deadcodepass) flood() {
// later will give a better error than deadcode.
continue
}
- if decodetypeKind(d.ctxt.Arch, s)&kindMask == kindInterface {
+ if decodetypeKind(d.ctxt.Arch, s.P)&kindMask == kindInterface {
for _, sig := range decodeIfaceMethods(d.ctxt.Arch, s) {
if d.ctxt.Debugvlog > 1 {
d.ctxt.Logf("reached iface method: %s\n", sig)
diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go
new file mode 100644
index 0000000000..cb6bb05492
--- /dev/null
+++ b/src/cmd/link/internal/ld/deadcode2.go
@@ -0,0 +1,441 @@
+// Copyright 2019 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/dwarf"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "container/heap"
+ "fmt"
+ "unicode"
+)
+
+var _ = fmt.Print
+
+type workQueue []loader.Sym
+
+// Implement container/heap.Interface.
+func (q *workQueue) Len() int { return len(*q) }
+func (q *workQueue) Less(i, j int) bool { return (*q)[i] < (*q)[j] }
+func (q *workQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
+func (q *workQueue) Push(i interface{}) { *q = append(*q, i.(loader.Sym)) }
+func (q *workQueue) Pop() interface{} { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
+
+// Functions for deadcode pass to use.
+// Deadcode pass should call push/pop, not Push/Pop.
+func (q *workQueue) push(i loader.Sym) { heap.Push(q, i) }
+func (q *workQueue) pop() loader.Sym { return heap.Pop(q).(loader.Sym) }
+func (q *workQueue) empty() bool { return len(*q) == 0 }
+
+type deadcodePass2 struct {
+ ctxt *Link
+ ldr *loader.Loader
+ wq workQueue
+ rtmp []loader.Reloc
+
+ ifaceMethod map[methodsig]bool // methods declared in reached interfaces
+ markableMethods []methodref2 // methods of reached types
+ reflectSeen bool // whether we have seen a reflect method call
+}
+
+func (d *deadcodePass2) init() {
+ d.ldr.InitReachable()
+ d.ifaceMethod = make(map[methodsig]bool)
+ if d.ctxt.Reachparent != nil {
+ d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
+ }
+ heap.Init(&d.wq)
+
+ if d.ctxt.BuildMode == BuildModeShared {
+ // Mark all symbols defined in this library as reachable when
+ // building a shared library.
+ n := d.ldr.NDef()
+ for i := 1; i < n; i++ {
+ s := loader.Sym(i)
+ if !d.ldr.IsDup(s) {
+ d.mark(s, 0)
+ }
+ }
+ return
+ }
+
+ var names []string
+
+ // In a normal binary, start at main.main and the init
+ // functions and mark what is reachable from there.
+ if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+ names = append(names, "main.main", "main..inittask")
+ } else {
+ // The external linker refers main symbol directly.
+ if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
+ if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
+ *flagEntrySymbol = "_main"
+ } else {
+ *flagEntrySymbol = "main"
+ }
+ }
+ names = append(names, *flagEntrySymbol)
+ if d.ctxt.BuildMode == BuildModePlugin {
+ names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs")
+
+ // We don't keep the go.plugin.exports symbol,
+ // but we do keep the symbols it refers to.
+ exportsIdx := d.ldr.Lookup("go.plugin.exports", 0)
+ if exportsIdx != 0 {
+ d.ReadRelocs(exportsIdx)
+ for i := 0; i < len(d.rtmp); i++ {
+ d.mark(d.rtmp[i].Sym, 0)
+ }
+ }
+ }
+ }
+
+ dynexpMap := d.ctxt.cgo_export_dynamic
+ if d.ctxt.LinkMode == LinkExternal {
+ dynexpMap = d.ctxt.cgo_export_static
+ }
+ for exp := range dynexpMap {
+ names = append(names, exp)
+ }
+
+ // DWARF constant DIE symbols are not referenced, but needed by
+ // the dwarf pass.
+ if !*FlagW {
+ for _, lib := range d.ctxt.Library {
+ names = append(names, dwarf.ConstInfoPrefix+lib.Pkg)
+ }
+ }
+
+ for _, name := range names {
+ // Mark symbol as an data/ABI0 symbol.
+ d.mark(d.ldr.Lookup(name, 0), 0)
+ // Also mark any Go functions (internal ABI).
+ d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal), 0)
+ }
+}
+
+func (d *deadcodePass2) flood() {
+ symRelocs := []loader.Reloc{}
+ auxSyms := []loader.Sym{}
+ for !d.wq.empty() {
+ symIdx := d.wq.pop()
+
+ d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
+
+ relocs := d.ldr.Relocs(symIdx)
+ symRelocs = relocs.ReadAll(symRelocs)
+
+ if d.ldr.IsGoType(symIdx) {
+ p := d.ldr.Data(symIdx)
+ if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
+ for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) {
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("reached iface method: %s\n", sig)
+ }
+ d.ifaceMethod[sig] = true
+ }
+ }
+ }
+
+ var methods []methodref2
+ for i := 0; i < relocs.Count; i++ {
+ r := symRelocs[i]
+ if r.Type == objabi.R_WEAKADDROFF {
+ continue
+ }
+ if r.Type == objabi.R_METHODOFF {
+ if i+2 >= relocs.Count {
+ panic("expect three consecutive R_METHODOFF relocs")
+ }
+ methods = append(methods, methodref2{src: symIdx, r: i})
+ i += 2
+ continue
+ }
+ if r.Type == objabi.R_USETYPE {
+ // type symbol used for DWARF. we need to load the symbol but it may not
+ // be otherwise reachable in the program.
+ // do nothing for now as we still load all type symbols.
+ continue
+ }
+ d.mark(r.Sym, symIdx)
+ }
+ auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms)
+ for i := 0; i < len(auxSyms); i++ {
+ d.mark(auxSyms[i], symIdx)
+ }
+ // Some host object symbols have an outer object, which acts like a
+ // "carrier" symbol, or it holds all the symbols for a particular
+ // section. We need to mark all "referenced" symbols from that carrier,
+ // so we make sure we're pulling in all outer symbols, and their sub
+ // symbols. This is not ideal, and these carrier/section symbols could
+ // be removed.
+ d.mark(d.ldr.OuterSym(symIdx), symIdx)
+ d.mark(d.ldr.SubSym(symIdx), symIdx)
+
+ if len(methods) != 0 {
+ // Decode runtime type information for type methods
+ // to help work out which methods can be called
+ // dynamically via interfaces.
+ methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs)
+ if len(methods) != len(methodsigs) {
+ panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
+ }
+ for i, m := range methodsigs {
+ methods[i].m = m
+ }
+ d.markableMethods = append(d.markableMethods, methods...)
+ }
+ }
+}
+
+func (d *deadcodePass2) mark(symIdx, parent loader.Sym) {
+ if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) {
+ d.wq.push(symIdx)
+ d.ldr.Reachable.Set(symIdx)
+ if d.ctxt.Reachparent != nil {
+ d.ldr.Reachparent[symIdx] = parent
+ }
+ if *flagDumpDep {
+ to := d.ldr.SymName(symIdx)
+ if to != "" {
+ from := "_"
+ if parent != 0 {
+ from = d.ldr.SymName(parent)
+ }
+ fmt.Printf("%s -> %s\n", from, to)
+ }
+ }
+ }
+}
+
+func (d *deadcodePass2) markMethod(m methodref2) {
+ d.ReadRelocs(m.src)
+ d.mark(d.rtmp[m.r].Sym, m.src)
+ d.mark(d.rtmp[m.r+1].Sym, m.src)
+ d.mark(d.rtmp[m.r+2].Sym, m.src)
+}
+
+func deadcode2(ctxt *Link) {
+ ldr := ctxt.loader
+ d := deadcodePass2{ctxt: ctxt, ldr: ldr}
+ d.init()
+ d.flood()
+
+ callSym := ldr.Lookup("reflect.Value.Call", sym.SymVerABIInternal)
+ methSym := ldr.Lookup("reflect.Value.Method", sym.SymVerABIInternal)
+ if ctxt.DynlinkingGo() {
+ // Exported methods may satisfy interfaces we don't know
+ // about yet when dynamically linking.
+ d.reflectSeen = true
+ }
+
+ for {
+ // Methods might be called via reflection. Give up on
+ // static analysis, mark all exported methods of
+ // all reachable types as reachable.
+ d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.Reachable.Has(callSym)) || (methSym != 0 && ldr.Reachable.Has(methSym))
+
+ // Mark all methods that could satisfy a discovered
+ // interface as reachable. We recheck old marked interfaces
+ // as new types (with new methods) may have been discovered
+ // in the last pass.
+ rem := d.markableMethods[:0]
+ for _, m := range d.markableMethods {
+ if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
+ d.markMethod(m)
+ } else {
+ rem = append(rem, m)
+ }
+ }
+ d.markableMethods = rem
+
+ if d.wq.empty() {
+ // No new work was discovered. Done.
+ break
+ }
+ d.flood()
+ }
+
+ n := ldr.NSym()
+
+ if ctxt.BuildMode != BuildModeShared {
+ // Keep a itablink if the symbol it points at is being kept.
+ // (When BuildModeShared, always keep itablinks.)
+ for i := 1; i < n; i++ {
+ s := loader.Sym(i)
+ if ldr.IsItabLink(s) {
+ relocs := ldr.Relocs(s)
+ if relocs.Count > 0 && ldr.Reachable.Has(relocs.At(0).Sym) {
+ ldr.Reachable.Set(s)
+ }
+ }
+ }
+ }
+}
+
+// methodref2 holds the relocations from a receiver type symbol to its
+// method. There are three relocations, one for each of the fields in
+// the reflect.method struct: mtyp, ifn, and tfn.
+type methodref2 struct {
+ m methodsig
+ src loader.Sym // receiver type symbol
+ r int // the index of R_METHODOFF relocations
+}
+
+func (m methodref2) isExported() bool {
+ for _, r := range m.m {
+ return unicode.IsUpper(r)
+ }
+ panic("methodref has no signature")
+}
+
+// decodeMethodSig2 decodes an array of method signature information.
+// Each element of the array is size bytes. The first 4 bytes is a
+// nameOff for the method name, and the next 4 bytes is a typeOff for
+// the function type.
+//
+// Conveniently this is the layout of both runtime.method and runtime.imethod.
+func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, off, size, count int) []methodsig {
+ var buf bytes.Buffer
+ var methods []methodsig
+ for i := 0; i < count; i++ {
+ buf.WriteString(decodetypeName2(ldr, symIdx, symRelocs, off))
+ mtypSym := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off+4))
+ // FIXME: add some sort of caching here, since we may see some of the
+ // same symbols over time for param types.
+ d.ReadRelocs(mtypSym)
+ mp := ldr.Data(mtypSym)
+
+ buf.WriteRune('(')
+ inCount := decodetypeFuncInCount(arch, mp)
+ for i := 0; i < inCount; i++ {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ a := d.decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i)
+ buf.WriteString(ldr.SymName(a))
+ }
+ buf.WriteString(") (")
+ outCount := decodetypeFuncOutCount(arch, mp)
+ for i := 0; i < outCount; i++ {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ a := d.decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i)
+ buf.WriteString(ldr.SymName(a))
+ }
+ buf.WriteRune(')')
+
+ off += size
+ methods = append(methods, methodsig(buf.String()))
+ buf.Reset()
+ }
+ return methods
+}
+
+func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
+ p := ldr.Data(symIdx)
+ if decodetypeKind(arch, p)&kindMask != kindInterface {
+ panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
+ }
+ rel := decodeReloc2(ldr, symIdx, symRelocs, int32(commonsize(arch)+arch.PtrSize))
+ if rel.Sym == 0 {
+ return nil
+ }
+ if rel.Sym != symIdx {
+ panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx)))
+ }
+ off := int(rel.Add) // array of reflect.imethod values
+ numMethods := int(decodetypeIfaceMethodCount(arch, p))
+ sizeofIMethod := 4 + 4
+ return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofIMethod, numMethods)
+}
+
+func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
+ p := ldr.Data(symIdx)
+ if !decodetypeHasUncommon(arch, p) {
+ panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
+ }
+ off := commonsize(arch) // reflect.rtype
+ switch decodetypeKind(arch, p) & kindMask {
+ case kindStruct: // reflect.structType
+ off += 4 * arch.PtrSize
+ case kindPtr: // reflect.ptrType
+ off += arch.PtrSize
+ case kindFunc: // reflect.funcType
+ off += arch.PtrSize // 4 bytes, pointer aligned
+ case kindSlice: // reflect.sliceType
+ off += arch.PtrSize
+ case kindArray: // reflect.arrayType
+ off += 3 * arch.PtrSize
+ case kindChan: // reflect.chanType
+ off += 2 * arch.PtrSize
+ case kindMap: // reflect.mapType
+ off += 4*arch.PtrSize + 8
+ case kindInterface: // reflect.interfaceType
+ off += 3 * arch.PtrSize
+ default:
+ // just Sizeof(rtype)
+ }
+
+ mcount := int(decodeInuxi(arch, p[off+4:], 2))
+ moff := int(decodeInuxi(arch, p[off+4+2+2:], 4))
+ off += moff // offset to array of reflect.method values
+ const sizeofMethod = 4 * 4 // sizeof reflect.method in program
+ return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofMethod, mcount)
+}
+
+func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Reloc {
+ for j := 0; j < len(symRelocs); j++ {
+ rel := symRelocs[j]
+ if rel.Off == off {
+ return rel
+ }
+ }
+ return loader.Reloc{}
+}
+
+func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym {
+ return decodeReloc2(ldr, symIdx, symRelocs, off).Sym
+}
+
+// decodetypeName2 decodes the name from a reflect.name.
+func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string {
+ r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off))
+ if r == 0 {
+ return ""
+ }
+
+ data := ldr.Data(r)
+ namelen := int(uint16(data[1])<<8 | uint16(data[2]))
+ return string(data[3 : 3+namelen])
+}
+
+func (d *deadcodePass2) decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
+ uadd := commonsize(arch) + 4
+ if arch.PtrSize == 8 {
+ uadd += 4
+ }
+ if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
+ uadd += uncommonSize()
+ }
+ return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize))
+}
+
+func (d *deadcodePass2) decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
+ return d.decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
+}
+
+// readRelocs reads the relocations for the specified symbol into the
+// deadcode relocs work array. Use with care, since the work array
+// is a singleton.
+func (d *deadcodePass2) ReadRelocs(symIdx loader.Sym) {
+ relocs := d.ldr.Relocs(symIdx)
+ d.rtmp = relocs.ReadAll(d.rtmp)
+}
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index 3afb38948f..3271c85157 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -65,28 +65,28 @@ func structfieldSize(arch *sys.Arch) int { return 3 * arch.PtrSize } // ru
func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype
// Type.commonType.kind
-func decodetypeKind(arch *sys.Arch, s *sym.Symbol) uint8 {
- return s.P[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f
+func decodetypeKind(arch *sys.Arch, p []byte) uint8 {
+ return p[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f
}
// Type.commonType.kind
-func decodetypeUsegcprog(arch *sys.Arch, s *sym.Symbol) uint8 {
- return s.P[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f
+func decodetypeUsegcprog(arch *sys.Arch, p []byte) uint8 {
+ return p[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f
}
// Type.commonType.size
-func decodetypeSize(arch *sys.Arch, s *sym.Symbol) int64 {
- return int64(decodeInuxi(arch, s.P, arch.PtrSize)) // 0x8 / 0x10
+func decodetypeSize(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p, arch.PtrSize)) // 0x8 / 0x10
}
// Type.commonType.ptrdata
-func decodetypePtrdata(arch *sys.Arch, s *sym.Symbol) int64 {
- return int64(decodeInuxi(arch, s.P[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10
+func decodetypePtrdata(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10
}
// Type.commonType.tflag
-func decodetypeHasUncommon(arch *sys.Arch, s *sym.Symbol) bool {
- return s.P[2*arch.PtrSize+4]&tflagUncommon != 0
+func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool {
+ return p[2*arch.PtrSize+4]&tflagUncommon != 0
}
// Find the elf.Section of a given shared library that contains a given address.
@@ -138,7 +138,7 @@ func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 {
func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte {
if s.Type == sym.SDYNIMPORT {
addr := decodetypeGcprogShlib(ctxt, s)
- ptrdata := decodetypePtrdata(ctxt.Arch, s)
+ ptrdata := decodetypePtrdata(ctxt.Arch, s.P)
sect := findShlibSection(ctxt, s.File, addr)
if sect != nil {
r := make([]byte, ptrdata/int64(ctxt.Arch.PtrSize))
@@ -181,17 +181,17 @@ func decodetypeChanElem(arch *sys.Arch, s *sym.Symbol) *sym.Symbol {
}
// Type.FuncType.dotdotdot
-func decodetypeFuncDotdotdot(arch *sys.Arch, s *sym.Symbol) bool {
- return uint16(decodeInuxi(arch, s.P[commonsize(arch)+2:], 2))&(1<<15) != 0
+func decodetypeFuncDotdotdot(arch *sys.Arch, p []byte) bool {
+ return uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2))&(1<<15) != 0
}
// Type.FuncType.inCount
-func decodetypeFuncInCount(arch *sys.Arch, s *sym.Symbol) int {
- return int(decodeInuxi(arch, s.P[commonsize(arch):], 2))
+func decodetypeFuncInCount(arch *sys.Arch, p []byte) int {
+ return int(decodeInuxi(arch, p[commonsize(arch):], 2))
}
-func decodetypeFuncOutCount(arch *sys.Arch, s *sym.Symbol) int {
- return int(uint16(decodeInuxi(arch, s.P[commonsize(arch)+2:], 2)) & (1<<15 - 1))
+func decodetypeFuncOutCount(arch *sys.Arch, p []byte) int {
+ return int(uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2)) & (1<<15 - 1))
}
func decodetypeFuncInType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
@@ -199,14 +199,14 @@ func decodetypeFuncInType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
if arch.PtrSize == 8 {
uadd += 4
}
- if decodetypeHasUncommon(arch, s) {
+ if decodetypeHasUncommon(arch, s.P) {
uadd += uncommonSize()
}
return decodeRelocSym(s, int32(uadd+i*arch.PtrSize))
}
func decodetypeFuncOutType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
- return decodetypeFuncInType(arch, s, i+decodetypeFuncInCount(arch, s))
+ return decodetypeFuncInType(arch, s, i+decodetypeFuncInCount(arch, s.P))
}
// Type.StructType.fields.Slice::length
@@ -216,7 +216,7 @@ func decodetypeStructFieldCount(arch *sys.Arch, s *sym.Symbol) int {
func decodetypeStructFieldArrayOff(arch *sys.Arch, s *sym.Symbol, i int) int {
off := commonsize(arch) + 4*arch.PtrSize
- if decodetypeHasUncommon(arch, s) {
+ if decodetypeHasUncommon(arch, s.P) {
off += uncommonSize()
}
off += i * structfieldSize(arch)
@@ -264,8 +264,8 @@ func decodetypeStructFieldOffsAnon(arch *sys.Arch, s *sym.Symbol, i int) int64 {
}
// InterfaceType.methods.length
-func decodetypeIfaceMethodCount(arch *sys.Arch, s *sym.Symbol) int64 {
- return int64(decodeInuxi(arch, s.P[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
+func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 {
+ return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
}
// methodsig is a fully qualified typed method signature, like
@@ -299,7 +299,7 @@ func decodeMethodSig(arch *sys.Arch, s *sym.Symbol, off, size, count int) []meth
mtypSym := decodeRelocSym(s, int32(off+4))
buf.WriteRune('(')
- inCount := decodetypeFuncInCount(arch, mtypSym)
+ inCount := decodetypeFuncInCount(arch, mtypSym.P)
for i := 0; i < inCount; i++ {
if i > 0 {
buf.WriteString(", ")
@@ -307,7 +307,7 @@ func decodeMethodSig(arch *sys.Arch, s *sym.Symbol, off, size, count int) []meth
buf.WriteString(decodetypeFuncInType(arch, mtypSym, i).Name)
}
buf.WriteString(") (")
- outCount := decodetypeFuncOutCount(arch, mtypSym)
+ outCount := decodetypeFuncOutCount(arch, mtypSym.P)
for i := 0; i < outCount; i++ {
if i > 0 {
buf.WriteString(", ")
@@ -324,7 +324,7 @@ func decodeMethodSig(arch *sys.Arch, s *sym.Symbol, off, size, count int) []meth
}
func decodeIfaceMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
- if decodetypeKind(arch, s)&kindMask != kindInterface {
+ if decodetypeKind(arch, s.P)&kindMask != kindInterface {
panic(fmt.Sprintf("symbol %q is not an interface", s.Name))
}
r := decodeReloc(s, int32(commonsize(arch)+arch.PtrSize))
@@ -335,17 +335,17 @@ func decodeIfaceMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", s.Name))
}
off := int(r.Add) // array of reflect.imethod values
- numMethods := int(decodetypeIfaceMethodCount(arch, s))
+ numMethods := int(decodetypeIfaceMethodCount(arch, s.P))
sizeofIMethod := 4 + 4
return decodeMethodSig(arch, s, off, sizeofIMethod, numMethods)
}
func decodetypeMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
- if !decodetypeHasUncommon(arch, s) {
+ if !decodetypeHasUncommon(arch, s.P) {
panic(fmt.Sprintf("no methods on %q", s.Name))
}
off := commonsize(arch) // reflect.rtype
- switch decodetypeKind(arch, s) & kindMask {
+ switch decodetypeKind(arch, s.P) & kindMask {
case kindStruct: // reflect.structType
off += 4 * arch.PtrSize
case kindPtr: // reflect.ptrType
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index e4ee58aa73..f5af90b028 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -422,8 +422,8 @@ func defgotype(ctxt *Link, gotype *sym.Symbol) *sym.Symbol {
func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
name := gotype.Name[5:] // could also decode from Type.string
- kind := decodetypeKind(ctxt.Arch, gotype)
- bytesize := decodetypeSize(ctxt.Arch, gotype)
+ kind := decodetypeKind(ctxt.Arch, gotype.P)
+ bytesize := decodetypeSize(ctxt.Arch, gotype.P)
var die, typedefdie *dwarf.DWDie
switch kind {
@@ -488,17 +488,17 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
typedefdie = dotypedef(ctxt, &dwtypes, name, die)
- nfields := decodetypeFuncInCount(ctxt.Arch, gotype)
+ nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P)
for i := 0; i < nfields; i++ {
s := decodetypeFuncInType(ctxt.Arch, gotype, i)
fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
}
- if decodetypeFuncDotdotdot(ctxt.Arch, gotype) {
+ if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) {
newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
}
- nfields = decodetypeFuncOutCount(ctxt.Arch, gotype)
+ nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P)
for i := 0; i < nfields; i++ {
s := decodetypeFuncOutType(ctxt.Arch, gotype, i)
fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
@@ -508,7 +508,7 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
case objabi.KindInterface:
die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
typedefdie = dotypedef(ctxt, &dwtypes, name, die)
- nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype))
+ nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P))
var s *sym.Symbol
if nfields == 0 {
s = lookupOrDiag(ctxt, "type.runtime.eface")
@@ -733,7 +733,7 @@ func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
gotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol)
keytype := decodetypeMapKey(ctxt.Arch, gotype)
valtype := decodetypeMapValue(ctxt.Arch, gotype)
- keysize, valsize := decodetypeSize(ctxt.Arch, keytype), decodetypeSize(ctxt.Arch, valtype)
+ keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P)
keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype))
// compute size info like hashmap.c does.
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
index 37adeb7701..21457fdfc8 100644
--- a/src/cmd/link/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -110,7 +110,6 @@ func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename s
return
}
p1 += p0
-
loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
}
}
@@ -123,6 +122,39 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
return
}
+ // Find cgo_export symbols. They are roots in the deadcode pass.
+ for _, f := range directives {
+ switch f[0] {
+ case "cgo_export_static", "cgo_export_dynamic":
+ if len(f) < 2 || len(f) > 3 {
+ continue
+ }
+ local := f[1]
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
+ if local == "main" {
+ continue
+ }
+ }
+ local = expandpkg(local, pkg)
+ if f[0] == "cgo_export_static" {
+ ctxt.cgo_export_static[local] = true
+ } else {
+ ctxt.cgo_export_dynamic[local] = true
+ }
+ }
+ }
+
+ if *flagNewobj {
+ // Record the directives. We'll process them later after Symbols are created.
+ ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
+ } else {
+ setCgoAttr(ctxt, ctxt.Syms.Lookup, file, pkg, directives)
+ }
+}
+
+// Set symbol attributes or flags based on cgo directives.
+func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, pkg string, directives [][]string) {
for _, f := range directives {
switch f[0] {
case "cgo_import_dynamic":
@@ -164,8 +196,8 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
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 := lookup(local, 0)
+ if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS || s.Type == sym.SHOSTOBJ {
s.SetDynimplib(lib)
s.SetExtname(remote)
s.SetDynimpvers(q)
@@ -183,7 +215,7 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
}
local := f[1]
- s := ctxt.Syms.Lookup(local, 0)
+ s := lookup(local, 0)
s.Type = sym.SHOSTOBJ
s.Size = 0
continue
@@ -204,11 +236,11 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
// functions. Link.loadlib will resolve any
// ABI aliases we find here (since we may not
// yet know it's an alias).
- s := ctxt.Syms.Lookup(local, 0)
+ s := lookup(local, 0)
switch ctxt.BuildMode {
case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
- if s == ctxt.Syms.Lookup("main", 0) {
+ if s == lookup("main", 0) {
continue
}
}
@@ -223,7 +255,6 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
if !s.Attr.CgoExport() {
s.SetExtname(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++
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 63987bb14a..6db159cdcd 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -38,6 +38,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loadelf"
+ "cmd/link/internal/loader"
"cmd/link/internal/loadmacho"
"cmd/link/internal/loadpe"
"cmd/link/internal/loadxcoff"
@@ -57,6 +58,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
+ "sort"
"strings"
"sync"
)
@@ -378,18 +380,35 @@ func (ctxt *Link) findLibPath(libname string) string {
}
func (ctxt *Link) loadlib() {
- switch ctxt.BuildMode {
- case BuildModeCShared, BuildModePlugin:
- s := ctxt.Syms.Lookup("runtime.islibrary", 0)
- s.Attr |= sym.AttrDuplicateOK
- s.AddUint8(1)
- case BuildModeCArchive:
- s := ctxt.Syms.Lookup("runtime.isarchive", 0)
- s.Attr |= sym.AttrDuplicateOK
- s.AddUint8(1)
+ if *flagNewobj {
+ var flags uint32
+ switch *FlagStrictDups {
+ case 0:
+ // nothing to do
+ case 1, 2:
+ flags = loader.FlagStrictDups
+ default:
+ log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
+ }
+ ctxt.loader = loader.NewLoader(flags)
}
- loadinternal(ctxt, "runtime")
+ ctxt.cgo_export_static = make(map[string]bool)
+ ctxt.cgo_export_dynamic = make(map[string]bool)
+
+ // ctxt.Library grows during the loop, so not a range loop.
+ i := 0
+ for ; i < len(ctxt.Library); i++ {
+ lib := ctxt.Library[i]
+ if lib.Shlib == "" {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref)
+ }
+ loadobjfile(ctxt, lib)
+ }
+ }
+
+ // load internal packages, if not already
if ctxt.Arch.Family == sys.ARM {
loadinternal(ctxt, "math")
}
@@ -399,46 +418,25 @@ func (ctxt *Link) loadlib() {
if *flagMsan {
loadinternal(ctxt, "runtime/msan")
}
-
- // ctxt.Library grows during the loop, so not a range loop.
- for i := 0; i < len(ctxt.Library); i++ {
+ loadinternal(ctxt, "runtime")
+ for ; i < len(ctxt.Library); i++ {
lib := ctxt.Library[i]
if lib.Shlib == "" {
- if ctxt.Debugvlog > 1 {
- ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref)
- }
loadobjfile(ctxt, lib)
}
}
- for _, lib := range ctxt.Library {
- if lib.Shlib != "" {
- if ctxt.Debugvlog > 1 {
- ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref)
- }
- ldshlibsyms(ctxt, lib.Shlib)
- }
+ if *flagNewobj {
+ iscgo = ctxt.loader.Lookup("x_cgo_init", 0) != 0
+ ctxt.canUsePlugins = ctxt.loader.Lookup("plugin.Open", sym.SymVerABIInternal) != 0
+ } else {
+ iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
+ ctxt.canUsePlugins = ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil
}
- iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
-
// We now have enough information to determine the link mode.
determineLinkMode(ctxt)
- // Recalculate pe parameters now that we have ctxt.LinkMode set.
- if ctxt.HeadType == objabi.Hwindows {
- Peinit(ctxt)
- }
-
- if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
- *FlagTextAddr = 0
- }
-
- if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
- toc := ctxt.Syms.Lookup(".TOC.", 0)
- toc.Type = sym.SDYNIMPORT
- }
-
if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) {
// This indicates a user requested -linkmode=external.
// The startup code uses an import of runtime/cgo to decide
@@ -456,7 +454,29 @@ func (ctxt *Link) loadlib() {
}
}
- if ctxt.LinkMode == LinkInternal {
+ for _, lib := range ctxt.Library {
+ if lib.Shlib != "" {
+ if ctxt.Debugvlog > 1 {
+ ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref)
+ }
+ ldshlibsyms(ctxt, lib.Shlib)
+ }
+ }
+
+ if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
+ if *flagNewobj {
+ // In newobj mode, we typically create sym.Symbols later therefore
+ // also set cgo attributes later. However, for internal cgo linking,
+ // the host object loaders still work with sym.Symbols (for now),
+ // and they need cgo attributes set to work properly. So process
+ // them now.
+ lookup := func(name string, ver int) *sym.Symbol { return ctxt.loader.LookupOrCreate(name, ver, ctxt.Syms) }
+ for _, d := range ctxt.cgodata {
+ setCgoAttr(ctxt, lookup, d.file, d.pkg, d.directives)
+ }
+ ctxt.cgodata = nil
+ }
+
// Drop all the cgo_import_static declarations.
// Turns out we won't be needing them.
for _, s := range ctxt.Syms.Allsym {
@@ -474,85 +494,21 @@ func (ctxt *Link) loadlib() {
}
}
- // The Android Q linker started to complain about underalignment of the our TLS
- // section. We don't actually use the section on android, so dont't
- // generate it.
- if objabi.GOOS != "android" {
- tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0)
+ // Conditionally load host objects, or setup for external linking.
+ hostobjs(ctxt)
+ hostlinksetup(ctxt)
- // runtime.tlsg is used for external linking on platforms that do not define
- // a variable to hold g in assembly (currently only intel).
- if tlsg.Type == 0 {
- tlsg.Type = sym.STLSBSS
- tlsg.Size = int64(ctxt.Arch.PtrSize)
- } else if tlsg.Type != sym.SDYNIMPORT {
- Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type)
- }
- tlsg.Attr |= sym.AttrReachable
- ctxt.Tlsg = tlsg
+ if *flagNewobj {
+ // Add references of externally defined symbols.
+ ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms)
}
- var moduledata *sym.Symbol
- if ctxt.BuildMode == BuildModePlugin {
- moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0)
- moduledata.Attr |= sym.AttrLocal
- } else {
- moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
- }
- if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT {
- // If the module (toolchain-speak for "executable or shared
- // library") we are linking contains the runtime package, it
- // will define the runtime.firstmoduledata symbol and we
- // truncate it back to 0 bytes so we can define its entire
- // contents in symtab.go:symtab().
- moduledata.Size = 0
-
- // In addition, on ARM, the runtime depends on the linker
- // recording the value of GOARM.
- if ctxt.Arch.Family == sys.ARM {
- s := ctxt.Syms.Lookup("runtime.goarm", 0)
- s.Type = sym.SDATA
- s.Size = 0
- s.AddUint8(uint8(objabi.GOARM))
- }
-
- if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
- s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0)
- s.Type = sym.SDATA
- s.Size = 0
- s.AddUint8(1)
- }
- } else {
- // If OTOH the module does not contain the runtime package,
- // create a local symbol for the moduledata.
- moduledata = ctxt.Syms.Lookup("local.moduledata", 0)
- moduledata.Attr |= sym.AttrLocal
+ // Now that we know the link mode, set the dynexp list.
+ if !*flagNewobj { // set this later in newobj mode
+ setupdynexp(ctxt)
}
- // In all cases way we mark the moduledata as noptrdata to hide it from
- // the GC.
- moduledata.Type = sym.SNOPTRDATA
- moduledata.Attr |= sym.AttrReachable
- ctxt.Moduledata = moduledata
-
- // Now that we know the link mode, trim the dynexp list.
- x := sym.AttrCgoExportDynamic
-
- if ctxt.LinkMode == LinkExternal {
- x = sym.AttrCgoExportStatic
- }
- w := 0
- for i := range dynexp {
- if dynexp[i].Attr&x != 0 {
- dynexp[w] = dynexp[i]
- w++
- }
- }
- dynexp = dynexp[:w]
-
- // In internal link mode, read the host object files.
- if ctxt.LinkMode == LinkInternal {
- hostobjs(ctxt)
+ if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
// If we have any undefined symbols in external
// objects, try to read them from the libgcc file.
any := false
@@ -600,15 +556,71 @@ func (ctxt *Link) loadlib() {
*/
}
}
- } else {
- hostlinksetup(ctxt)
}
// We've loaded all the code now.
ctxt.Loaded = true
- // Record whether we can use plugins.
- ctxt.canUsePlugins = (ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil)
+ importcycles()
+
+ if *flagNewobj {
+ strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
+ }
+}
+
+// Set up dynexp list.
+func setupdynexp(ctxt *Link) {
+ dynexpMap := ctxt.cgo_export_dynamic
+ if ctxt.LinkMode == LinkExternal {
+ dynexpMap = ctxt.cgo_export_static
+ }
+ dynexp = make([]*sym.Symbol, 0, len(dynexpMap))
+ for exp := range dynexpMap {
+ s := ctxt.Syms.Lookup(exp, 0)
+ dynexp = append(dynexp, s)
+ }
+ sort.Sort(byName(dynexp))
+
+ // Resolve ABI aliases in the list of cgo-exported functions.
+ // This is necessary because we load the ABI0 symbol for all
+ // cgo exports.
+ for i, s := range dynexp {
+ if s.Type != sym.SABIALIAS {
+ continue
+ }
+ t := resolveABIAlias(s)
+ t.Attr |= s.Attr
+ t.SetExtname(s.Extname())
+ dynexp[i] = t
+ }
+
+ ctxt.cgo_export_static = nil
+ ctxt.cgo_export_dynamic = nil
+}
+
+// Set up flags and special symbols depending on the platform build mode.
+func (ctxt *Link) linksetup() {
+ switch ctxt.BuildMode {
+ case BuildModeCShared, BuildModePlugin:
+ s := ctxt.Syms.Lookup("runtime.islibrary", 0)
+ s.Type = sym.SNOPTRDATA
+ s.Attr |= sym.AttrDuplicateOK
+ s.AddUint8(1)
+ case BuildModeCArchive:
+ s := ctxt.Syms.Lookup("runtime.isarchive", 0)
+ s.Type = sym.SNOPTRDATA
+ s.Attr |= sym.AttrDuplicateOK
+ s.AddUint8(1)
+ }
+
+ // Recalculate pe parameters now that we have ctxt.LinkMode set.
+ if ctxt.HeadType == objabi.Hwindows {
+ Peinit(ctxt)
+ }
+
+ if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
+ *FlagTextAddr = 0
+ }
// If there are no dynamic libraries needed, gcc disables dynamic linking.
// Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
@@ -626,6 +638,71 @@ func (ctxt *Link) loadlib() {
}
}
+ if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
+ toc := ctxt.Syms.Lookup(".TOC.", 0)
+ toc.Type = sym.SDYNIMPORT
+ }
+
+ // The Android Q linker started to complain about underalignment of the our TLS
+ // section. We don't actually use the section on android, so dont't
+ // generate it.
+ if objabi.GOOS != "android" {
+ tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0)
+
+ // runtime.tlsg is used for external linking on platforms that do not define
+ // a variable to hold g in assembly (currently only intel).
+ if tlsg.Type == 0 {
+ tlsg.Type = sym.STLSBSS
+ tlsg.Size = int64(ctxt.Arch.PtrSize)
+ } else if tlsg.Type != sym.SDYNIMPORT {
+ Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type)
+ }
+ tlsg.Attr |= sym.AttrReachable
+ ctxt.Tlsg = tlsg
+ }
+
+ var moduledata *sym.Symbol
+ if ctxt.BuildMode == BuildModePlugin {
+ moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0)
+ moduledata.Attr |= sym.AttrLocal
+ } else {
+ moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
+ }
+ if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT {
+ // If the module (toolchain-speak for "executable or shared
+ // library") we are linking contains the runtime package, it
+ // will define the runtime.firstmoduledata symbol and we
+ // truncate it back to 0 bytes so we can define its entire
+ // contents in symtab.go:symtab().
+ moduledata.Size = 0
+
+ // In addition, on ARM, the runtime depends on the linker
+ // recording the value of GOARM.
+ if ctxt.Arch.Family == sys.ARM {
+ s := ctxt.Syms.Lookup("runtime.goarm", 0)
+ s.Type = sym.SDATA
+ s.Size = 0
+ s.AddUint8(uint8(objabi.GOARM))
+ }
+
+ if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
+ s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0)
+ s.Type = sym.SDATA
+ s.Size = 0
+ s.AddUint8(1)
+ }
+ } else {
+ // If OTOH the module does not contain the runtime package,
+ // create a local symbol for the moduledata.
+ moduledata = ctxt.Syms.Lookup("local.moduledata", 0)
+ moduledata.Attr |= sym.AttrLocal
+ }
+ // In all cases way we mark the moduledata as noptrdata to hide it from
+ // the GC.
+ moduledata.Type = sym.SNOPTRDATA
+ moduledata.Attr |= sym.AttrReachable
+ ctxt.Moduledata = moduledata
+
// If package versioning is required, generate a hash of the
// packages used in the link.
if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
@@ -643,59 +720,6 @@ func (ctxt *Link) loadlib() {
got.Attr |= sym.AttrReachable
}
}
-
- importcycles()
-
- // put symbols into Textp
- // do it in postorder so that packages are laid down in dependency order
- // internal first, then everything else
- ctxt.Library = postorder(ctxt.Library)
- for _, doInternal := range [2]bool{true, false} {
- for _, lib := range ctxt.Library {
- if isRuntimeDepPkg(lib.Pkg) != doInternal {
- continue
- }
- ctxt.Textp = append(ctxt.Textp, lib.Textp...)
- for _, s := range lib.DupTextSyms {
- if !s.Attr.OnList() {
- ctxt.Textp = append(ctxt.Textp, s)
- s.Attr |= sym.AttrOnList
- // dupok symbols may be defined in multiple packages. its
- // associated package is chosen sort of arbitrarily (the
- // first containing package that the linker loads). canonicalize
- // it here to the package with which it will be laid down
- // in text.
- s.File = objabi.PathToPrefix(lib.Pkg)
- }
- }
- }
- }
-
- if len(ctxt.Shlibs) > 0 {
- // We might have overwritten some functions above (this tends to happen for the
- // autogenerated type equality/hashing functions) and we don't want to generated
- // pcln table entries for these any more so remove them from Textp.
- textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
- for _, s := range ctxt.Textp {
- if s.Type != sym.SDYNIMPORT {
- textp = append(textp, s)
- }
- }
- ctxt.Textp = textp
- }
-
- // Resolve ABI aliases in the list of cgo-exported functions.
- // This is necessary because we load the ABI0 symbol for all
- // cgo exports.
- for i, s := range dynexp {
- if s.Type != sym.SABIALIAS {
- continue
- }
- t := resolveABIAlias(s)
- t.Attr |= s.Attr
- t.SetExtname(s.Extname())
- dynexp[i] = t
- }
}
// mangleTypeSym shortens the names of symbols that represent Go types
@@ -991,6 +1015,9 @@ func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType obja
}
func hostobjs(ctxt *Link) {
+ if ctxt.LinkMode != LinkInternal {
+ return
+ }
var h *Hostobj
for i := 0; i < len(hostobj); i++ {
@@ -1665,55 +1692,107 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
if magic == 0x7f454c46 { // \x7F E L F
- ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
- textp, flags, err := loadelf.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
- if err != nil {
- Errorf(nil, "%v", err)
- return
+ if *flagNewobj {
+ ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ehdr.flags = flags
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
+ } else {
+ ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, flags, err := loadelf.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ehdr.flags = flags
+ ctxt.Textp = append(ctxt.Textp, textp...)
}
- ehdr.flags = flags
- ctxt.Textp = append(ctxt.Textp, textp...)
+ return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
}
- return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
}
if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe {
- ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
- textp, err := loadmacho.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
- if err != nil {
- Errorf(nil, "%v", err)
- return
+ if *flagNewobj {
+ ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
+ } else {
+ ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadmacho.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
}
- ctxt.Textp = append(ctxt.Textp, textp...)
+ return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
}
- return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
}
if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 {
- ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
- textp, rsrc, err := loadpe.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
- if err != nil {
- Errorf(nil, "%v", err)
- return
+ if *flagNewobj {
+ ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ if rsrc != nil {
+ setpersrc(ctxt, rsrc)
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
}
- if rsrc != nil {
- setpersrc(ctxt, rsrc)
+ return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
+ } else {
+ ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, rsrc, err := loadpe.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ if rsrc != nil {
+ setpersrc(ctxt, rsrc)
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
}
- ctxt.Textp = append(ctxt.Textp, textp...)
+ return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
}
- return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
}
if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) {
- ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
- textp, err := loadxcoff.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
- if err != nil {
- Errorf(nil, "%v", err)
- return
+ if *flagNewobj {
+ ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
+ }
+ return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
+ } else {
+ ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+ textp, err := loadxcoff.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
+ if err != nil {
+ Errorf(nil, "%v", err)
+ return
+ }
+ ctxt.Textp = append(ctxt.Textp, textp...)
}
- ctxt.Textp = append(ctxt.Textp, textp...)
+ return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
- return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
/* check the header */
@@ -1809,7 +1888,12 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
default:
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
}
- c := objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
+ var c int
+ if *flagNewobj {
+ ctxt.loader.Preload(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
+ } else {
+ c = objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
+ }
strictDupMsgCount += c
addImports(ctxt, lib, pn)
return nil
@@ -1964,7 +2048,17 @@ func ldshlibsyms(ctxt *Link, shlib string) {
ver = sym.SymVerABIInternal
}
- lsym := ctxt.Syms.Lookup(elfsym.Name, ver)
+ var lsym *sym.Symbol
+ if *flagNewobj {
+ i := ctxt.loader.AddExtSym(elfsym.Name, ver)
+ if i == 0 {
+ continue
+ }
+ lsym = ctxt.Syms.Newsym(elfsym.Name, ver)
+ ctxt.loader.Syms[i] = lsym
+ } else {
+ lsym = ctxt.Syms.Lookup(elfsym.Name, ver)
+ }
// Because loadlib above loads all .a files before loading any shared
// libraries, any non-dynimport symbols we find that duplicate symbols
// already loaded should be ignored (the symbols from the .a files
@@ -1993,7 +2087,17 @@ func ldshlibsyms(ctxt *Link, shlib string) {
// mangle Go function names in the .so to include the
// ABI.
if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
- alias := ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal)
+ var alias *sym.Symbol
+ if *flagNewobj {
+ i := ctxt.loader.AddExtSym(elfsym.Name, sym.SymVerABIInternal)
+ if i == 0 {
+ continue
+ }
+ alias = ctxt.Syms.Newsym(elfsym.Name, sym.SymVerABIInternal)
+ ctxt.loader.Syms[i] = alias
+ } else {
+ alias = ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal)
+ }
if alias.Type != 0 {
continue
}
@@ -2390,6 +2494,9 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6
}
case sym.SHOSTOBJ:
+ if !s.Attr.Reachable() {
+ continue
+ }
if ctxt.HeadType == objabi.Hwindows || ctxt.IsELF {
put(ctxt, s, s.Name, UndefinedSym, s.Value, nil)
}
@@ -2586,3 +2693,49 @@ func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library
mark[lib] = visited
*order = append(*order, lib)
}
+
+func (ctxt *Link) loadlibfull() {
+ // Load full symbol contents, resolve indexed references.
+ ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms)
+
+ // Pull the symbols out.
+ ctxt.loader.ExtractSymbols(ctxt.Syms)
+
+ // Load cgo directives.
+ for _, d := range ctxt.cgodata {
+ setCgoAttr(ctxt, ctxt.Syms.Lookup, d.file, d.pkg, d.directives)
+ }
+
+ setupdynexp(ctxt)
+
+ // Populate ctxt.Reachparent if appropriate.
+ if ctxt.Reachparent != nil {
+ for i := 0; i < len(ctxt.loader.Reachparent); i++ {
+ p := ctxt.loader.Reachparent[i]
+ if p == 0 {
+ continue
+ }
+ if p == loader.Sym(i) {
+ panic("self-cycle in reachparent")
+ }
+ sym := ctxt.loader.Syms[i]
+ psym := ctxt.loader.Syms[p]
+ ctxt.Reachparent[sym] = psym
+ }
+ }
+
+ // Drop the reference.
+ ctxt.loader = nil
+ ctxt.cgodata = nil
+
+ addToTextp(ctxt)
+}
+
+func (ctxt *Link) dumpsyms() {
+ for _, s := range ctxt.Syms.Allsym {
+ fmt.Printf("%s %s %p %v %v\n", s, s.Type, s, s.Attr.Reachable(), s.Attr.OnList())
+ for i := range s.R {
+ fmt.Println("\t", s.R[i].Type, s.R[i].Sym)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 53092d2e8f..124f7d9001 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -35,6 +35,7 @@ import (
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"fmt"
@@ -96,6 +97,18 @@ type Link struct {
runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen.
relocbuf []byte // temporary buffer for applying relocations
+
+ loader *loader.Loader
+ cgodata []cgodata // cgo directives to load, three strings are args for loadcgo
+
+ cgo_export_static map[string]bool
+ cgo_export_dynamic map[string]bool
+}
+
+type cgodata struct {
+ file string
+ pkg string
+ directives [][]string
}
type unresolvedSymKey struct {
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index d37ef36e66..9f9395b757 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -87,6 +87,7 @@ var (
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
+ flagNewobj = flag.Bool("newobj", false, "use new object file format")
FlagRound = flag.Int("R", -1, "set address rounding `quantum`")
FlagTextAddr = flag.Int64("T", -1, "set text segment `address`")
@@ -208,8 +209,13 @@ func Main(arch *sys.Arch, theArch Arch) {
}
ctxt.loadlib()
- ctxt.dostrdata()
deadcode(ctxt)
+ if *flagNewobj {
+ ctxt.loadlibfull() // XXX do it here for now
+ }
+ ctxt.linksetup()
+ ctxt.dostrdata()
+
dwarfGenerateDebugInfo(ctxt)
if objabi.Fieldtrack_enabled != 0 {
fieldtrack(ctxt)
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 8048695b3d..818da34d6d 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -305,10 +305,10 @@ func (ctxt *Link) pclntab() {
// appears in the Pcfile table. In that case, this assigns
// the outer file a number.
numberfile(ctxt, call.File)
- nameoff := nameToOffset(call.Func.Name)
+ nameoff := nameToOffset(call.Func)
inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent))
- inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func.Name, call.Func.File)))
+ inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func, "")))
// byte 3 is unused
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line))
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index 9ed5db0557..bba623eb48 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -694,7 +694,7 @@ func (ctxt *Link) symtab() {
// creating the moduledata from scratch and it does not have a
// compiler-provided size, so read it from the type data.
moduledatatype := ctxt.Syms.ROLookup("type.runtime.moduledata", 0)
- moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype)
+ moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype.P)
moduledata.Grow(moduledata.Size)
lastmoduledatap := ctxt.Syms.Lookup("runtime.lastmoduledatap", 0)
diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go
index 8d041677f0..9d236db766 100644
--- a/src/cmd/link/internal/ld/util.go
+++ b/src/cmd/link/internal/ld/util.go
@@ -88,3 +88,10 @@ func contains(s []string, v string) bool {
}
return false
}
+
+// implements sort.Interface, for sorting symbols by name.
+type byName []*sym.Symbol
+
+func (s byName) Len() int { return len(s) }
+func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name }
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go
index e895056bb2..072eaf00c8 100644
--- a/src/cmd/link/internal/loadelf/ldelf.go
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -10,6 +10,7 @@ import (
"cmd/internal/bio"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
"encoding/binary"
@@ -451,7 +452,20 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags
return found, ehdrFlags, nil
}
-// Load loads the ELF file pn from f.
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) {
+ newSym := func(name string, version int) *sym.Symbol {
+ return l.LookupOrCreate(name, version, syms)
+ }
+ return load(arch, syms.IncVersion(), newSym, newSym, f, pkg, length, pn, flags)
+}
+
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) {
+ return load(arch, syms.IncVersion(), syms.Newsym, syms.Lookup, f, pkg, length, pn, flags)
+}
+
+type lookupFunc func(string, int) *sym.Symbol
+
+// load loads the ELF file pn from f.
// Symbols are written into syms, and a slice of the text symbols is returned.
//
// On ARM systems, Load will attempt to determine what ELF header flags to
@@ -459,12 +473,11 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags
// parameter initEhdrFlags contains the current header flags for the output
// object, and the returned ehdrFlags contains what this Load function computes.
// TODO: find a better place for this logic.
-func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []*sym.Symbol, ehdrFlags uint32, err error) {
+func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []*sym.Symbol, ehdrFlags uint32, err error) {
errorf := func(str string, args ...interface{}) ([]*sym.Symbol, uint32, error) {
return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
}
- localSymVersion := syms.IncVersion()
base := f.Offset()
var hdrbuf [64]uint8
@@ -715,7 +728,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
}
sectsymNames[name] = true
- s := syms.Lookup(name, localSymVersion)
+ s := lookup(name, localSymVersion)
switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) {
default:
@@ -754,7 +767,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
for i := 1; i < elfobj.nsymtab; i++ {
var elfsym ElfSym
- if err := readelfsym(arch, syms, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
+ if err := readelfsym(newSym, lookup, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
return errorf("%s: malformed elf file: %v", pn, err)
}
symbols[i] = elfsym.sym
@@ -925,7 +938,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
rp.Sym = nil
} else {
var elfsym ElfSym
- if err := readelfsym(arch, syms, elfobj, int(info>>32), &elfsym, 0, 0); err != nil {
+ if err := readelfsym(newSym, lookup, arch, elfobj, int(info>>32), &elfsym, 0, 0); err != nil {
return errorf("malformed elf file: %v", err)
}
elfsym.sym = symbols[info>>32]
@@ -1002,7 +1015,7 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
return nil
}
-func readelfsym(arch *sys.Arch, syms *sym.Symbols, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
+func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
if i >= elfobj.nsymtab || i < 0 {
err = fmt.Errorf("invalid elf symbol index")
return err
@@ -1052,7 +1065,7 @@ func readelfsym(arch *sys.Arch, syms *sym.Symbols, elfobj *ElfObj, i int, elfsym
switch elfsym.bind {
case ElfSymBindGlobal:
if needSym != 0 {
- s = syms.Lookup(elfsym.name, 0)
+ s = lookup(elfsym.name, 0)
// for global scoped hidden symbols we should insert it into
// symbol hash table, but mark them as hidden.
@@ -1077,7 +1090,7 @@ func readelfsym(arch *sys.Arch, syms *sym.Symbols, elfobj *ElfObj, i int, elfsym
// We need to be able to look this up,
// so put it in the hash table.
if needSym != 0 {
- s = syms.Lookup(elfsym.name, localSymVersion)
+ s = lookup(elfsym.name, localSymVersion)
s.Attr |= sym.AttrVisibilityHidden
}
@@ -1088,14 +1101,14 @@ func readelfsym(arch *sys.Arch, syms *sym.Symbols, elfobj *ElfObj, i int, elfsym
// local names and hidden global names are unique
// and should only be referenced by their index, not name, so we
// don't bother to add them into the hash table
- s = syms.Newsym(elfsym.name, localSymVersion)
+ s = newSym(elfsym.name, localSymVersion)
s.Attr |= sym.AttrVisibilityHidden
}
case ElfSymBindWeak:
if needSym != 0 {
- s = syms.Lookup(elfsym.name, 0)
+ s = lookup(elfsym.name, 0)
if elfsym.other == 2 {
s.Attr |= sym.AttrVisibilityHidden
}
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
new file mode 100644
index 0000000000..c0fa5fa7ce
--- /dev/null
+++ b/src/cmd/link/internal/loader/loader.go
@@ -0,0 +1,1308 @@
+// Copyright 2019 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 loader
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/dwarf"
+ "cmd/internal/goobj2"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/sym"
+ "fmt"
+ "log"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+var _ = fmt.Print
+
+// Sym encapsulates a global symbol index, used to identify a specific
+// Go symbol. The 0-valued Sym is corresponds to an invalid symbol.
+type Sym int
+
+// Relocs encapsulates the set of relocations on a given symbol; an
+// instance of this type is returned by the Loader Relocs() method.
+type Relocs struct {
+ Count int // number of relocs
+
+ li int // local index of symbol whose relocs we're examining
+ r *oReader // object reader for containing package
+ l *Loader // loader
+
+ ext *sym.Symbol // external symbol if not nil
+}
+
+// Reloc contains the payload for a specific relocation.
+// TODO: replace this with sym.Reloc, once we change the
+// relocation target from "*sym.Symbol" to "loader.Sym" in sym.Reloc.
+type Reloc struct {
+ Off int32 // offset to rewrite
+ Size uint8 // number of bytes to rewrite: 0, 1, 2, or 4
+ Type objabi.RelocType // the relocation type
+ Add int64 // addend
+ Sym Sym // global index of symbol the reloc addresses
+}
+
+// oReader is a wrapper type of obj.Reader, along with some
+// extra information.
+// TODO: rename to objReader once the old one is gone?
+type oReader struct {
+ *goobj2.Reader
+ unit *sym.CompilationUnit
+ version int // version of static symbol
+ flags uint32 // read from object file
+ pkgprefix string
+ rcache []Sym // cache mapping local PkgNone symbol to resolved Sym
+}
+
+type objIdx struct {
+ r *oReader
+ i Sym // start index
+ e Sym // end index
+}
+
+type nameVer struct {
+ name string
+ v int
+}
+
+type bitmap []uint32
+
+// set the i-th bit.
+func (bm bitmap) Set(i Sym) {
+ n, r := uint(i)/32, uint(i)%32
+ bm[n] |= 1 << r
+}
+
+// whether the i-th bit is set.
+func (bm bitmap) Has(i Sym) bool {
+ n, r := uint(i)/32, uint(i)%32
+ return bm[n]&(1<<r) != 0
+}
+
+func makeBitmap(n int) bitmap {
+ return make(bitmap, (n+31)/32)
+}
+
+// A Loader loads new object files and resolves indexed symbol references.
+type Loader struct {
+ start map[*oReader]Sym // map from object file to its start index
+ objs []objIdx // sorted by start index (i.e. objIdx.i)
+ max Sym // current max index
+ extStart Sym // from this index on, the symbols are externally defined
+ extSyms []nameVer // externally defined symbols
+ builtinSyms []Sym // global index of builtin symbols
+ ocache int // index (into 'objs') of most recent lookup
+
+ symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal
+ extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name
+ overwrite map[Sym]Sym // overwrite[i]=j if symbol j overwrites symbol i
+
+ itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
+
+ objByPkg map[string]*oReader // map package path to its Go object reader
+
+ Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now.
+
+ Reachable bitmap // bitmap of reachable symbols, indexed by global index
+
+ // Used to implement field tracking; created during deadcode if
+ // field tracking is enabled. Reachparent[K] contains the index of
+ // the symbol that triggered the marking of symbol K as live.
+ Reachparent []Sym
+
+ relocBatch []sym.Reloc // for bulk allocation of relocations
+
+ flags uint32
+
+ strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
+}
+
+const (
+ // Loader.flags
+ FlagStrictDups = 1 << iota
+)
+
+func NewLoader(flags uint32) *Loader {
+ nbuiltin := goobj2.NBuiltin()
+ return &Loader{
+ start: make(map[*oReader]Sym),
+ objs: []objIdx{{nil, 0, 0}},
+ symsByName: [2]map[string]Sym{make(map[string]Sym), make(map[string]Sym)},
+ objByPkg: make(map[string]*oReader),
+ overwrite: make(map[Sym]Sym),
+ itablink: make(map[Sym]struct{}),
+ extStaticSyms: make(map[nameVer]Sym),
+ builtinSyms: make([]Sym, nbuiltin),
+ flags: flags,
+ }
+}
+
+// Return the start index in the global index space for a given object file.
+func (l *Loader) startIndex(r *oReader) Sym {
+ return l.start[r]
+}
+
+// Add object file r, return the start index.
+func (l *Loader) addObj(pkg string, r *oReader) Sym {
+ if _, ok := l.start[r]; ok {
+ panic("already added")
+ }
+ pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
+ if _, ok := l.objByPkg[pkg]; !ok {
+ l.objByPkg[pkg] = r
+ }
+ n := r.NSym() + r.NNonpkgdef()
+ i := l.max + 1
+ l.start[r] = i
+ l.objs = append(l.objs, objIdx{r, i, i + Sym(n) - 1})
+ l.max += Sym(n)
+ return i
+}
+
+// Add a symbol with a given index, return if it is added.
+func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ sym.SymKind) bool {
+ if l.extStart != 0 {
+ panic("AddSym called after AddExtSym is called")
+ }
+ if ver == r.version {
+ // Static symbol. Add its global index but don't
+ // add to name lookup table, as it cannot be
+ // referenced by name.
+ return true
+ }
+ if oldi, ok := l.symsByName[ver][name]; ok {
+ if dupok {
+ if l.flags&FlagStrictDups != 0 {
+ l.checkdup(name, i, r, oldi)
+ }
+ return false
+ }
+ oldr, li := l.toLocal(oldi)
+ oldsym := goobj2.Sym{}
+ oldsym.Read(oldr.Reader, oldr.SymOff(li))
+ if oldsym.Dupok() {
+ return false
+ }
+ overwrite := r.DataSize(int(i-l.startIndex(r))) != 0
+ if overwrite {
+ // new symbol overwrites old symbol.
+ oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type)]
+ if !oldtyp.IsData() && r.DataSize(li) == 0 {
+ log.Fatalf("duplicated definition of symbol " + name)
+ }
+ l.overwrite[oldi] = i
+ } else {
+ // old symbol overwrites new symbol.
+ if typ != sym.SDATA && typ != sym.SNOPTRDATA && typ != sym.SBSS && typ != sym.SNOPTRBSS { // only allow overwriting data symbol
+ log.Fatalf("duplicated definition of symbol " + name)
+ }
+ l.overwrite[i] = oldi
+ return false
+ }
+ }
+ l.symsByName[ver][name] = i
+ return true
+}
+
+// Add an external symbol (without index). Return the index of newly added
+// symbol, or 0 if not added.
+func (l *Loader) AddExtSym(name string, ver int) Sym {
+ static := ver >= sym.SymVerStatic
+ if static {
+ if _, ok := l.extStaticSyms[nameVer{name, ver}]; ok {
+ return 0
+ }
+ } else {
+ if _, ok := l.symsByName[ver][name]; ok {
+ return 0
+ }
+ }
+ i := l.max + 1
+ if static {
+ l.extStaticSyms[nameVer{name, ver}] = i
+ } else {
+ l.symsByName[ver][name] = i
+ }
+ l.max++
+ if l.extStart == 0 {
+ l.extStart = i
+ }
+ l.extSyms = append(l.extSyms, nameVer{name, ver})
+ l.growSyms(int(i))
+ return i
+}
+
+func (l *Loader) IsExternal(i Sym) bool {
+ return l.extStart != 0 && i >= l.extStart
+}
+
+// Ensure Syms slice has enough space.
+func (l *Loader) growSyms(i int) {
+ n := len(l.Syms)
+ if n > i {
+ return
+ }
+ l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...)
+}
+
+// Convert a local index to a global index.
+func (l *Loader) toGlobal(r *oReader, i int) Sym {
+ g := l.startIndex(r) + Sym(i)
+ if ov, ok := l.overwrite[g]; ok {
+ return ov
+ }
+ return g
+}
+
+// Convert a global index to a local index.
+func (l *Loader) toLocal(i Sym) (*oReader, int) {
+ if ov, ok := l.overwrite[i]; ok {
+ i = ov
+ }
+ if l.IsExternal(i) {
+ return nil, int(i - l.extStart)
+ }
+ oc := l.ocache
+ if oc != 0 && i >= l.objs[oc].i && i <= l.objs[oc].e {
+ return l.objs[oc].r, int(i - l.objs[oc].i)
+ }
+ // Search for the local object holding index i.
+ // Below k is the first one that has its start index > i,
+ // so k-1 is the one we want.
+ k := sort.Search(len(l.objs), func(k int) bool {
+ return l.objs[k].i > i
+ })
+ l.ocache = k - 1
+ return l.objs[k-1].r, int(i - l.objs[k-1].i)
+}
+
+// rcacheGet checks for a valid entry for 's' in the readers cache,
+// where 's' is a local PkgIdxNone ref or def, or zero if
+// the cache is empty or doesn't contain a value for 's'.
+func (or *oReader) rcacheGet(symIdx uint32) Sym {
+ if len(or.rcache) > 0 {
+ return or.rcache[symIdx]
+ }
+ return 0
+}
+
+// rcacheSet installs a new entry in the oReader's PkgNone
+// resolver cache for the specified PkgIdxNone ref or def,
+// allocating a new cache if needed.
+func (or *oReader) rcacheSet(symIdx uint32, gsym Sym) {
+ if len(or.rcache) == 0 {
+ or.rcache = make([]Sym, or.NNonpkgdef()+or.NNonpkgref())
+ }
+ or.rcache[symIdx] = gsym
+}
+
+// Resolve a local symbol reference. Return global index.
+func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym {
+ var rr *oReader
+ switch p := s.PkgIdx; p {
+ case goobj2.PkgIdxInvalid:
+ if s.SymIdx != 0 {
+ panic("bad sym ref")
+ }
+ return 0
+ case goobj2.PkgIdxNone:
+ // Check for cached version first
+ if cached := r.rcacheGet(s.SymIdx); cached != 0 {
+ return cached
+ }
+ // Resolve by name
+ i := int(s.SymIdx) + r.NSym()
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(i))
+ name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+ v := abiToVer(osym.ABI, r.version)
+ gsym := l.Lookup(name, v)
+ // Add to cache, then return.
+ r.rcacheSet(s.SymIdx, gsym)
+ return gsym
+ case goobj2.PkgIdxBuiltin:
+ return l.builtinSyms[s.SymIdx]
+ case goobj2.PkgIdxSelf:
+ rr = r
+ default:
+ pkg := r.Pkg(int(p))
+ var ok bool
+ rr, ok = l.objByPkg[pkg]
+ if !ok {
+ log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
+ }
+ }
+ return l.toGlobal(rr, int(s.SymIdx))
+}
+
+// Look up a symbol by name, return global index, or 0 if not found.
+// This is more like Syms.ROLookup than Lookup -- it doesn't create
+// new symbol.
+func (l *Loader) Lookup(name string, ver int) Sym {
+ if ver >= sym.SymVerStatic {
+ return l.extStaticSyms[nameVer{name, ver}]
+ }
+ return l.symsByName[ver][name]
+}
+
+// Returns whether i is a dup of another symbol, and i is not
+// "primary", i.e. Lookup i by name will not return i.
+func (l *Loader) IsDup(i Sym) bool {
+ if _, ok := l.overwrite[i]; ok {
+ return true
+ }
+ if l.IsExternal(i) {
+ return false
+ }
+ r, li := l.toLocal(i)
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(li))
+ if !osym.Dupok() {
+ return false
+ }
+ if osym.Name == "" {
+ return false // Unnamed aux symbol cannot be dup.
+ }
+ if osym.ABI == goobj2.SymABIstatic {
+ return false // Static symbol cannot be dup.
+ }
+ name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+ ver := abiToVer(osym.ABI, r.version)
+ return l.symsByName[ver][name] != i
+}
+
+// Check that duplicate symbols have same contents.
+func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) {
+ li := int(i - l.startIndex(r))
+ p := r.Data(li)
+ if strings.HasPrefix(name, "go.info.") {
+ p, _ = patchDWARFName1(p, r)
+ }
+ rdup, ldup := l.toLocal(dup)
+ pdup := rdup.Data(ldup)
+ if strings.HasPrefix(name, "go.info.") {
+ pdup, _ = patchDWARFName1(pdup, rdup)
+ }
+ if bytes.Equal(p, pdup) {
+ return
+ }
+ reason := "same length but different contents"
+ if len(p) != len(pdup) {
+ reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
+ }
+ fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)
+
+ // For the moment, whitelist DWARF subprogram DIEs for
+ // auto-generated wrapper functions. What seems to happen
+ // here is that we get different line numbers on formal
+ // params; I am guessing that the pos is being inherited
+ // from the spot where the wrapper is needed.
+ whitelist := strings.HasPrefix(name, "go.info.go.interface") ||
+ strings.HasPrefix(name, "go.info.go.builtin") ||
+ strings.HasPrefix(name, "go.debuglines")
+ if !whitelist {
+ l.strictDupMsgs++
+ }
+}
+
+func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
+
+// Number of total symbols.
+func (l *Loader) NSym() int {
+ return int(l.max + 1)
+}
+
+// Number of defined Go symbols.
+func (l *Loader) NDef() int {
+ return int(l.extStart)
+}
+
+// Returns the raw (unpatched) name of the i-th symbol.
+func (l *Loader) RawSymName(i Sym) string {
+ if l.IsExternal(i) {
+ if s := l.Syms[i]; s != nil {
+ return s.Name
+ }
+ return ""
+ }
+ r, li := l.toLocal(i)
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(li))
+ return osym.Name
+}
+
+// Returns the (patched) name of the i-th symbol.
+func (l *Loader) SymName(i Sym) string {
+ if l.IsExternal(i) {
+ if s := l.Syms[i]; s != nil {
+ return s.Name // external name should already be patched?
+ }
+ return ""
+ }
+ r, li := l.toLocal(i)
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(li))
+ return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+}
+
+// Returns the type of the i-th symbol.
+func (l *Loader) SymType(i Sym) sym.SymKind {
+ if l.IsExternal(i) {
+ if s := l.Syms[i]; s != nil {
+ return s.Type
+ }
+ return 0
+ }
+ r, li := l.toLocal(i)
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(li))
+ return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+}
+
+// Returns the attributes of the i-th symbol.
+func (l *Loader) SymAttr(i Sym) uint8 {
+ if l.IsExternal(i) {
+ // TODO: do something? External symbols have different representation of attributes. For now, ReflectMethod is the only thing matters and it cannot be set by external symbol.
+ return 0
+ }
+ r, li := l.toLocal(i)
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(li))
+ return osym.Flag
+}
+
+// Returns whether the i-th symbol has ReflectMethod attribute set.
+func (l *Loader) IsReflectMethod(i Sym) bool {
+ return l.SymAttr(i)&goobj2.SymFlagReflectMethod != 0
+}
+
+// Returns whether this is a Go type symbol.
+func (l *Loader) IsGoType(i Sym) bool {
+ return l.SymAttr(i)&goobj2.SymFlagGoType != 0
+}
+
+// Returns whether this is a "go.itablink.*" symbol.
+func (l *Loader) IsItabLink(i Sym) bool {
+ if _, ok := l.itablink[i]; ok {
+ return true
+ }
+ return false
+}
+
+// Returns the symbol content of the i-th symbol. i is global index.
+func (l *Loader) Data(i Sym) []byte {
+ if l.IsExternal(i) {
+ if s := l.Syms[i]; s != nil {
+ return s.P
+ }
+ return nil
+ }
+ r, li := l.toLocal(i)
+ return r.Data(li)
+}
+
+// Returns the number of aux symbols given a global index.
+func (l *Loader) NAux(i Sym) int {
+ if l.IsExternal(i) {
+ return 0
+ }
+ r, li := l.toLocal(i)
+ return r.NAux(li)
+}
+
+// Returns the referred symbol of the j-th aux symbol of the i-th
+// symbol.
+func (l *Loader) AuxSym(i Sym, j int) Sym {
+ if l.IsExternal(i) {
+ return 0
+ }
+ r, li := l.toLocal(i)
+ a := goobj2.Aux{}
+ a.Read(r.Reader, r.AuxOff(li, j))
+ return l.resolve(r, a.Sym)
+}
+
+// ReadAuxSyms reads the aux symbol ids for the specified symbol into the
+// slice passed as a parameter. If the slice capacity is not large enough, a new
+// larger slice will be allocated. Final slice is returned.
+func (l *Loader) ReadAuxSyms(symIdx Sym, dst []Sym) []Sym {
+ if l.IsExternal(symIdx) {
+ return dst[:0]
+ }
+ naux := l.NAux(symIdx)
+ if naux == 0 {
+ return dst[:0]
+ }
+
+ if cap(dst) < naux {
+ dst = make([]Sym, naux)
+ }
+ dst = dst[:0]
+
+ r, li := l.toLocal(symIdx)
+ for i := 0; i < naux; i++ {
+ a := goobj2.Aux{}
+ a.Read(r.Reader, r.AuxOff(li, i))
+ dst = append(dst, l.resolve(r, a.Sym))
+ }
+
+ return dst
+}
+
+// OuterSym gets the outer symbol for host object loaded symbols.
+func (l *Loader) OuterSym(i Sym) Sym {
+ sym := l.Syms[i]
+ if sym != nil && sym.Outer != nil {
+ outer := sym.Outer
+ return l.Lookup(outer.Name, int(outer.Version))
+ }
+ return 0
+}
+
+// SubSym gets the subsymbol for host object loaded symbols.
+func (l *Loader) SubSym(i Sym) Sym {
+ sym := l.Syms[i]
+ if sym != nil && sym.Sub != nil {
+ sub := sym.Sub
+ return l.Lookup(sub.Name, int(sub.Version))
+ }
+ return 0
+}
+
+// Initialize Reachable bitmap for running deadcode pass.
+func (l *Loader) InitReachable() {
+ l.Reachable = makeBitmap(l.NSym())
+}
+
+// At method returns the j-th reloc for a global symbol.
+func (relocs *Relocs) At(j int) Reloc {
+ if relocs.ext != nil {
+ rel := &relocs.ext.R[j]
+ return Reloc{
+ Off: rel.Off,
+ Size: rel.Siz,
+ Type: rel.Type,
+ Add: rel.Add,
+ Sym: relocs.l.Lookup(rel.Sym.Name, int(rel.Sym.Version)),
+ }
+ }
+ rel := goobj2.Reloc{}
+ rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j))
+ target := relocs.l.resolve(relocs.r, rel.Sym)
+ return Reloc{
+ Off: rel.Off,
+ Size: rel.Siz,
+ Type: objabi.RelocType(rel.Type),
+ Add: rel.Add,
+ Sym: target,
+ }
+}
+
+// ReadAll method reads all relocations for a symbol into the
+// specified slice. If the slice capacity is not large enough, a new
+// larger slice will be allocated. Final slice is returned.
+func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc {
+ if relocs.Count == 0 {
+ return dst[:0]
+ }
+
+ if cap(dst) < relocs.Count {
+ dst = make([]Reloc, relocs.Count)
+ }
+ dst = dst[:0]
+
+ if relocs.ext != nil {
+ for i := 0; i < relocs.Count; i++ {
+ erel := &relocs.ext.R[i]
+ rel := Reloc{
+ Off: erel.Off,
+ Size: erel.Siz,
+ Type: erel.Type,
+ Add: erel.Add,
+ Sym: relocs.l.Lookup(erel.Sym.Name, int(erel.Sym.Version)),
+ }
+ dst = append(dst, rel)
+ }
+ return dst
+ }
+
+ off := relocs.r.RelocOff(relocs.li, 0)
+ for i := 0; i < relocs.Count; i++ {
+ rel := goobj2.Reloc{}
+ rel.Read(relocs.r.Reader, off)
+ off += uint32(rel.Size())
+ target := relocs.l.resolve(relocs.r, rel.Sym)
+ dst = append(dst, Reloc{
+ Off: rel.Off,
+ Size: rel.Siz,
+ Type: objabi.RelocType(rel.Type),
+ Add: rel.Add,
+ Sym: target,
+ })
+ }
+ return dst
+}
+
+// Relocs returns a Relocs object for the given global sym.
+func (l *Loader) Relocs(i Sym) Relocs {
+ if l.IsExternal(i) {
+ if s := l.Syms[i]; s != nil {
+ return Relocs{Count: len(s.R), l: l, ext: s}
+ }
+ return Relocs{}
+ }
+ r, li := l.toLocal(i)
+ return l.relocs(r, li)
+}
+
+// Relocs returns a Relocs object given a local sym index and reader.
+func (l *Loader) relocs(r *oReader, li int) Relocs {
+ return Relocs{
+ Count: r.NReloc(li),
+ li: li,
+ r: r,
+ l: l,
+ }
+}
+
+// Preload a package: add autolibs, add symbols to the symbol table.
+// Does not read symbol data yet.
+func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
+ roObject, readonly, err := f.Slice(uint64(length))
+ if err != nil {
+ log.Fatal("cannot read object file:", err)
+ }
+ r := goobj2.NewReaderFromBytes(roObject, readonly)
+ if r == nil {
+ panic("cannot read object file")
+ }
+ localSymVersion := syms.IncVersion()
+ pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
+ or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, nil}
+
+ // Autolib
+ lib.ImportStrings = append(lib.ImportStrings, r.Autolib()...)
+
+ // DWARF file table
+ nfile := r.NDwarfFile()
+ unit.DWARFFileTable = make([]string, nfile)
+ for i := range unit.DWARFFileTable {
+ unit.DWARFFileTable[i] = r.DwarfFile(i)
+ }
+
+ istart := l.addObj(lib.Pkg, or)
+
+ ndef := r.NSym()
+ nnonpkgdef := r.NNonpkgdef()
+ for i, n := 0, ndef+nnonpkgdef; i < n; i++ {
+ osym := goobj2.Sym{}
+ osym.Read(r, r.SymOff(i))
+ name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1)
+ if name == "" {
+ continue // don't add unnamed aux symbol
+ }
+ v := abiToVer(osym.ABI, localSymVersion)
+ dupok := osym.Dupok()
+ added := l.AddSym(name, v, istart+Sym(i), or, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
+ if added && strings.HasPrefix(name, "go.itablink.") {
+ l.itablink[istart+Sym(i)] = struct{}{}
+ }
+ if added && strings.HasPrefix(name, "runtime.") {
+ if bi := goobj2.BuiltinIdx(name, v); bi != -1 {
+ // This is a definition of a builtin symbol. Record where it is.
+ l.builtinSyms[bi] = istart + Sym(i)
+ }
+ }
+ }
+
+ // The caller expects us consuming all the data
+ f.MustSeek(length, os.SEEK_CUR)
+}
+
+// Make sure referenced symbols are added. Most of them should already be added.
+// This should only be needed for referenced external symbols.
+func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) {
+ for _, o := range l.objs[1:] {
+ loadObjRefs(l, o.r, arch, syms)
+ }
+}
+
+func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) {
+ ndef := r.NSym() + r.NNonpkgdef()
+ for i, n := 0, r.NNonpkgref(); i < n; i++ {
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(ndef+i))
+ name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+ v := abiToVer(osym.ABI, r.version)
+ l.AddExtSym(name, v)
+ }
+}
+
+func abiToVer(abi uint16, localSymVersion int) int {
+ var v int
+ if abi == goobj2.SymABIstatic {
+ // Static
+ v = localSymVersion
+ } else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
+ // Note that data symbols are "ABI0", which maps to version 0.
+ v = abiver
+ } else {
+ log.Fatalf("invalid symbol ABI: %d", abi)
+ }
+ return v
+}
+
+func preprocess(arch *sys.Arch, s *sym.Symbol) {
+ if s.Name != "" && s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
+ x, err := strconv.ParseUint(s.Name[5:], 16, 64)
+ if err != nil {
+ log.Panicf("failed to parse $-symbol %s: %v", s.Name, err)
+ }
+ s.Type = sym.SRODATA
+ s.Attr |= sym.AttrLocal
+ switch s.Name[:5] {
+ case "$f32.":
+ if uint64(uint32(x)) != x {
+ log.Panicf("$-symbol %s too large: %d", s.Name, x)
+ }
+ s.AddUint32(arch, uint32(x))
+ case "$f64.", "$i64.":
+ s.AddUint64(arch, x)
+ default:
+ log.Panicf("unrecognized $-symbol: %s", s.Name)
+ }
+ }
+}
+
+// Load full contents.
+func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
+ // create all Symbols first.
+ l.growSyms(l.NSym())
+
+ nr := 0 // total number of sym.Reloc's we'll need
+ for _, o := range l.objs[1:] {
+ nr += loadObjSyms(l, syms, o.r)
+ }
+
+ // allocate a single large slab of relocations for all live symbols
+ l.relocBatch = make([]sym.Reloc, nr)
+
+ // external symbols
+ for i := l.extStart; i <= l.max; i++ {
+ if s := l.Syms[i]; s != nil {
+ s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
+ continue // already loaded from external object
+ }
+ nv := l.extSyms[i-l.extStart]
+ if l.Reachable.Has(i) || strings.HasPrefix(nv.name, "gofile..") { // XXX file symbols are used but not marked
+ s := syms.Newsym(nv.name, nv.v)
+ preprocess(arch, s)
+ s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
+ l.Syms[i] = s
+ }
+ }
+
+ // load contents of defined symbols
+ for _, o := range l.objs[1:] {
+ loadObjFull(l, o.r)
+ }
+
+ // Resolve ABI aliases for external symbols. This is only
+ // needed for internal cgo linking.
+ // (The old code does this in deadcode, but deadcode2 doesn't
+ // do this.)
+ for i := l.extStart; i <= l.max; i++ {
+ if s := l.Syms[i]; s != nil && s.Attr.Reachable() {
+ for ri := range s.R {
+ r := &s.R[ri]
+ if r.Sym != nil && r.Sym.Type == sym.SABIALIAS {
+ r.Sym = r.Sym.R[0].Sym
+ }
+ }
+ }
+ }
+}
+
+// ExtractSymbols grabs the symbols out of the loader for work that hasn't been
+// ported to the new symbol type.
+func (l *Loader) ExtractSymbols(syms *sym.Symbols) {
+ // Nil out overwritten symbols.
+ // Overwritten Go symbols aren't a problem (as they're lazy loaded), but
+ // symbols loaded from host object loaders are fully loaded, and we might
+ // have multiple symbols with the same name. This loop nils them out.
+ for oldI := range l.overwrite {
+ l.Syms[oldI] = nil
+ }
+
+ // For now, add all symbols to ctxt.Syms.
+ for _, s := range l.Syms {
+ if s != nil && s.Name != "" {
+ syms.Add(s)
+ }
+ }
+
+}
+
+// addNewSym adds a new sym.Symbol to the i-th index in the list of symbols.
+func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
+ s := syms.Newsym(name, ver)
+ if s.Type != 0 && s.Type != sym.SXREF {
+ fmt.Println("symbol already processed:", unit.Lib, i, s)
+ panic("symbol already processed")
+ }
+ if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
+ t = s.Type
+ }
+ s.Type = t
+ s.Unit = unit
+ l.growSyms(int(i))
+ l.Syms[i] = s
+ return s
+}
+
+// loadObjSyms creates sym.Symbol objects for the live Syms in the
+// object corresponding to object reader "r". Return value is the
+// number of sym.Reloc entries required for all the new symbols.
+func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
+ istart := l.startIndex(r)
+ nr := 0
+
+ for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
+ // If it's been previously loaded in host object loading, we don't need to do it again.
+ if s := l.Syms[istart+Sym(i)]; s != nil {
+ // Mark symbol as reachable as it wasn't marked as such before.
+ s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i)))
+ nr += r.NReloc(i)
+ continue
+ }
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(i))
+ name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+ if name == "" {
+ continue
+ }
+ ver := abiToVer(osym.ABI, r.version)
+ if osym.ABI != goobj2.SymABIstatic && l.symsByName[ver][name] != istart+Sym(i) {
+ continue
+ }
+
+ t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
+ if t == sym.SXREF {
+ log.Fatalf("bad sxref")
+ }
+ if t == 0 {
+ log.Fatalf("missing type for %s in %s", name, r.unit.Lib)
+ }
+ if !l.Reachable.Has(istart+Sym(i)) && !(t == sym.SRODATA && strings.HasPrefix(name, "type.")) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" {
+ // No need to load unreachable symbols.
+ // XXX some type symbol's content may be needed in DWARF code, but they are not marked.
+ // XXX reference to runtime.addmoduledata may be generated later by the linker in plugin mode.
+ continue
+ }
+
+ s := l.addNewSym(istart+Sym(i), syms, name, ver, r.unit, t)
+ s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i)))
+ nr += r.NReloc(i)
+ }
+ return nr
+}
+
+// funcInfoSym records the sym.Symbol for a function, along with a copy
+// of the corresponding goobj2.Sym and the index of its FuncInfo aux sym.
+// We use this to delay populating FuncInfo until we can batch-allocate
+// slices for their sub-objects.
+type funcInfoSym struct {
+ s *sym.Symbol // sym.Symbol for a live function
+ osym goobj2.Sym // object file symbol data for that function
+ isym int // global symbol index of FuncInfo aux sym for func
+}
+
+// funcAllocInfo records totals/counts for all functions in an objfile;
+// used to help with bulk allocation of sym.Symbol sub-objects.
+type funcAllocInfo struct {
+ symPtr uint32 // number of *sym.Symbol's needed in file slices
+ inlCall uint32 // number of sym.InlinedCall's needed in inltree slices
+ pcData uint32 // number of sym.Pcdata's needed in pdata slices
+ fdOff uint32 // number of int64's needed in all Funcdataoff slices
+}
+
+// LoadSymbol loads a single symbol by name.
+// This function should only be used by the host object loaders.
+// NB: This function does NOT set the symbol as reachable.
+func (l *Loader) LoadSymbol(name string, version int, syms *sym.Symbols) *sym.Symbol {
+ global := l.Lookup(name, version)
+
+ // If we're already loaded, bail.
+ if global != 0 && int(global) < len(l.Syms) && l.Syms[global] != nil {
+ return l.Syms[global]
+ }
+
+ // Read the symbol.
+ r, i := l.toLocal(global)
+ istart := l.startIndex(r)
+
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(int(i)))
+ if l.symsByName[version][name] != istart+Sym(i) {
+ return nil
+ }
+
+ return l.addNewSym(istart+Sym(i), syms, name, version, r.unit, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
+}
+
+// LookupOrCreate looks up a symbol by name, and creates one if not found.
+// Either way, it will also create a sym.Symbol for it, if not already.
+// This should only be called when interacting with parts of the linker
+// that still works on sym.Symbols (i.e. internal cgo linking, for now).
+func (l *Loader) LookupOrCreate(name string, version int, syms *sym.Symbols) *sym.Symbol {
+ i := l.Lookup(name, version)
+ if i != 0 {
+ // symbol exists
+ if int(i) < len(l.Syms) && l.Syms[i] != nil {
+ return l.Syms[i] // already loaded
+ }
+ if l.IsExternal(i) {
+ panic("Can't load an external symbol.")
+ }
+ return l.LoadSymbol(name, version, syms)
+ }
+ i = l.AddExtSym(name, version)
+ s := syms.Newsym(name, version)
+ l.Syms[i] = s
+ return s
+}
+
+func loadObjFull(l *Loader, r *oReader) {
+ lib := r.unit.Lib
+ istart := l.startIndex(r)
+
+ resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
+ i := l.resolve(r, s)
+ return l.Syms[i]
+ }
+
+ funcs := []funcInfoSym{}
+ fdsyms := []*sym.Symbol{}
+ var funcAllocCounts funcAllocInfo
+ pcdataBase := r.PcdataBase()
+ rslice := []Reloc{}
+ for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
+ osym := goobj2.Sym{}
+ osym.Read(r.Reader, r.SymOff(i))
+ name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
+ if name == "" {
+ continue
+ }
+ ver := abiToVer(osym.ABI, r.version)
+ dupok := osym.Dupok()
+ if dupok {
+ if dupsym := l.symsByName[ver][name]; dupsym != istart+Sym(i) {
+ if l.Reachable.Has(dupsym) {
+ // A dupok symbol is resolved to another package. We still need
+ // to record its presence in the current package, as the trampoline
+ // pass expects packages are laid out in dependency order.
+ s := l.Syms[dupsym]
+ if s.Type == sym.STEXT {
+ lib.DupTextSyms = append(lib.DupTextSyms, s)
+ }
+ }
+ continue
+ }
+ }
+
+ s := l.Syms[istart+Sym(i)]
+ if s == nil {
+ continue
+ }
+ if s.Name != name { // Sanity check. We can remove it in the final version.
+ fmt.Println("name mismatch:", lib, i, s.Name, name)
+ panic("name mismatch")
+ }
+
+ local := osym.Local()
+ makeTypelink := osym.Typelink()
+ size := osym.Siz
+
+ // Symbol data
+ s.P = r.Data(i)
+ s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
+
+ // Relocs
+ relocs := l.relocs(r, i)
+ rslice = relocs.ReadAll(rslice)
+ batch := l.relocBatch
+ s.R = batch[:relocs.Count:relocs.Count]
+ l.relocBatch = batch[relocs.Count:]
+ for j := range s.R {
+ r := rslice[j]
+ rs := r.Sym
+ sz := r.Size
+ rt := r.Type
+ if rt == objabi.R_METHODOFF {
+ if l.Reachable.Has(rs) {
+ rt = objabi.R_ADDROFF
+ } else {
+ sz = 0
+ rs = 0
+ }
+ }
+ if rt == objabi.R_WEAKADDROFF && !l.Reachable.Has(rs) {
+ rs = 0
+ sz = 0
+ }
+ if rs != 0 && l.SymType(rs) == sym.SABIALIAS {
+ rsrelocs := l.Relocs(rs)
+ rs = rsrelocs.At(0).Sym
+ }
+ s.R[j] = sym.Reloc{
+ Off: r.Off,
+ Siz: sz,
+ Type: rt,
+ Add: r.Add,
+ Sym: l.Syms[rs],
+ }
+ }
+
+ // Aux symbol info
+ isym := -1
+ naux := r.NAux(i)
+ for j := 0; j < naux; j++ {
+ a := goobj2.Aux{}
+ a.Read(r.Reader, r.AuxOff(i, j))
+ switch a.Type {
+ case goobj2.AuxGotype:
+ typ := resolveSymRef(a.Sym)
+ if typ != nil {
+ s.Gotype = typ
+ }
+ case goobj2.AuxFuncdata:
+ fdsyms = append(fdsyms, resolveSymRef(a.Sym))
+ case goobj2.AuxFuncInfo:
+ if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
+ panic("funcinfo symbol not defined in current package")
+ }
+ isym = int(a.Sym.SymIdx)
+ case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
+ // ignored for now
+ default:
+ panic("unknown aux type")
+ }
+ }
+
+ s.File = r.pkgprefix[:len(r.pkgprefix)-1]
+ if dupok {
+ s.Attr |= sym.AttrDuplicateOK
+ }
+ if s.Size < int64(size) {
+ s.Size = int64(size)
+ }
+ s.Attr.Set(sym.AttrLocal, local)
+ s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
+
+ if s.Type == sym.SDWARFINFO {
+ // For DWARF symbols, replace `"".` to actual package prefix
+ // in the symbol content.
+ // TODO: maybe we should do this in the compiler and get rid
+ // of this.
+ patchDWARFName(s, r)
+ }
+
+ if s.Type != sym.STEXT {
+ continue
+ }
+
+ if isym == -1 {
+ continue
+ }
+
+ // Record function sym and associated info for additional
+ // processing in the loop below.
+ fwis := funcInfoSym{s: s, isym: isym, osym: osym}
+ funcs = append(funcs, fwis)
+
+ // Read the goobj2.FuncInfo for this text symbol so that we can
+ // collect allocation counts. We'll read it again in the loop
+ // below.
+ b := r.Data(isym)
+ info := goobj2.FuncInfo{}
+ info.Read(b)
+ funcAllocCounts.symPtr += uint32(len(info.File))
+ funcAllocCounts.pcData += uint32(len(info.Pcdata))
+ funcAllocCounts.inlCall += uint32(len(info.InlTree))
+ funcAllocCounts.fdOff += uint32(len(info.Funcdataoff))
+ }
+
+ // At this point we can do batch allocation of the sym.FuncInfo's,
+ // along with the slices of sub-objects they use.
+ fiBatch := make([]sym.FuncInfo, len(funcs))
+ inlCallBatch := make([]sym.InlinedCall, funcAllocCounts.inlCall)
+ symPtrBatch := make([]*sym.Symbol, funcAllocCounts.symPtr)
+ pcDataBatch := make([]sym.Pcdata, funcAllocCounts.pcData)
+ fdOffBatch := make([]int64, funcAllocCounts.fdOff)
+
+ // Populate FuncInfo contents for func symbols.
+ for fi := 0; fi < len(funcs); fi++ {
+ s := funcs[fi].s
+ isym := funcs[fi].isym
+ osym := funcs[fi].osym
+
+ s.FuncInfo = &fiBatch[0]
+ fiBatch = fiBatch[1:]
+
+ b := r.Data(isym)
+ info := goobj2.FuncInfo{}
+ info.Read(b)
+
+ if info.NoSplit != 0 {
+ s.Attr |= sym.AttrNoSplit
+ }
+ if osym.ReflectMethod() {
+ s.Attr |= sym.AttrReflectMethod
+ }
+ if r.Flags()&goobj2.ObjFlagShared != 0 {
+ s.Attr |= sym.AttrShared
+ }
+ if osym.TopFrame() {
+ s.Attr |= sym.AttrTopFrame
+ }
+
+ pc := s.FuncInfo
+
+ if len(info.Funcdataoff) != 0 {
+ nfd := len(info.Funcdataoff)
+ pc.Funcdata = fdsyms[:nfd:nfd]
+ fdsyms = fdsyms[nfd:]
+ }
+
+ info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
+ pc.Args = int32(info.Args)
+ pc.Locals = int32(info.Locals)
+
+ npc := len(info.Pcdata) - 1 // -1 as we appended one above
+ pc.Pcdata = pcDataBatch[:npc:npc]
+ pcDataBatch = pcDataBatch[npc:]
+
+ nfd := len(info.Funcdataoff)
+ pc.Funcdataoff = fdOffBatch[:nfd:nfd]
+ fdOffBatch = fdOffBatch[nfd:]
+
+ nsp := len(info.File)
+ pc.File = symPtrBatch[:nsp:nsp]
+ symPtrBatch = symPtrBatch[nsp:]
+
+ nic := len(info.InlTree)
+ pc.InlTree = inlCallBatch[:nic:nic]
+ inlCallBatch = inlCallBatch[nic:]
+
+ pc.Pcsp.P = r.BytesAt(pcdataBase+info.Pcsp, int(info.Pcfile-info.Pcsp))
+ pc.Pcfile.P = r.BytesAt(pcdataBase+info.Pcfile, int(info.Pcline-info.Pcfile))
+ pc.Pcline.P = r.BytesAt(pcdataBase+info.Pcline, int(info.Pcinline-info.Pcline))
+ pc.Pcinline.P = r.BytesAt(pcdataBase+info.Pcinline, int(info.Pcdata[0]-info.Pcinline))
+ for k := range pc.Pcdata {
+ pc.Pcdata[k].P = r.BytesAt(pcdataBase+info.Pcdata[k], int(info.Pcdata[k+1]-info.Pcdata[k]))
+ }
+ for k := range pc.Funcdataoff {
+ pc.Funcdataoff[k] = int64(info.Funcdataoff[k])
+ }
+ for k := range pc.File {
+ pc.File[k] = resolveSymRef(info.File[k])
+ }
+ for k := range pc.InlTree {
+ inl := &info.InlTree[k]
+ pc.InlTree[k] = sym.InlinedCall{
+ Parent: inl.Parent,
+ File: resolveSymRef(inl.File),
+ Line: inl.Line,
+ Func: l.SymName(l.resolve(r, inl.Func)),
+ ParentPC: inl.ParentPC,
+ }
+ }
+
+ dupok := osym.Dupok()
+ if !dupok {
+ if s.Attr.OnList() {
+ log.Fatalf("symbol %s listed multiple times", s.Name)
+ }
+ s.Attr.Set(sym.AttrOnList, true)
+ lib.Textp = append(lib.Textp, s)
+ } else {
+ // there may be a dup in another package
+ // put into a temp list and add to text later
+ lib.DupTextSyms = append(lib.DupTextSyms, s)
+ }
+ }
+}
+
+var emptyPkg = []byte(`"".`)
+
+func patchDWARFName1(p []byte, r *oReader) ([]byte, int) {
+ // This is kind of ugly. Really the package name should not
+ // even be included here.
+ if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION {
+ return p, -1
+ }
+ e := bytes.IndexByte(p, 0)
+ if e == -1 {
+ return p, -1
+ }
+ if !bytes.Contains(p[:e], emptyPkg) {
+ return p, -1
+ }
+ pkgprefix := []byte(r.pkgprefix)
+ patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1)
+ return append(patched, p[e:]...), e
+}
+
+func patchDWARFName(s *sym.Symbol, r *oReader) {
+ patched, e := patchDWARFName1(s.P, r)
+ if e == -1 {
+ return
+ }
+ s.P = patched
+ s.Attr.Set(sym.AttrReadOnly, false)
+ delta := int64(len(s.P)) - s.Size
+ s.Size = int64(len(s.P))
+ for i := range s.R {
+ r := &s.R[i]
+ if r.Off > int32(e) {
+ r.Off += int32(delta)
+ }
+ }
+}
+
+// For debugging.
+func (l *Loader) Dump() {
+ fmt.Println("objs")
+ for _, obj := range l.objs {
+ if obj.r != nil {
+ fmt.Println(obj.i, obj.r.unit.Lib)
+ }
+ }
+ fmt.Println("syms")
+ for i, s := range l.Syms {
+ if i == 0 {
+ continue
+ }
+ if s != nil {
+ fmt.Println(i, s, s.Type)
+ } else {
+ fmt.Println(i, l.SymName(Sym(i)), "<not loaded>")
+ }
+ }
+ fmt.Println("overwrite:", l.overwrite)
+ fmt.Println("symsByName")
+ for name, i := range l.symsByName[0] {
+ fmt.Println(i, name, 0)
+ }
+ for name, i := range l.symsByName[1] {
+ fmt.Println(i, name, 1)
+ }
+}
diff --git a/src/cmd/link/internal/loadmacho/ldmacho.go b/src/cmd/link/internal/loadmacho/ldmacho.go
index c303752992..85a1ebc631 100644
--- a/src/cmd/link/internal/loadmacho/ldmacho.go
+++ b/src/cmd/link/internal/loadmacho/ldmacho.go
@@ -10,6 +10,7 @@ import (
"cmd/internal/bio"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"encoding/binary"
"fmt"
@@ -423,14 +424,24 @@ func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
return 0
}
-// Load loads the Mach-O file pn from f.
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) ([]*sym.Symbol, error) {
+ newSym := func(name string, version int) *sym.Symbol {
+ return l.LookupOrCreate(name, version, syms)
+ }
+ return load(arch, syms.IncVersion(), newSym, f, pkg, length, pn)
+}
+
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+ return load(arch, syms.IncVersion(), syms.Lookup, f, pkg, length, pn)
+}
+
+// load the Mach-O file pn from f.
// Symbols are written into syms, and a slice of the text symbols is returned.
-func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Symbol, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
}
- localSymVersion := syms.IncVersion()
base := f.Offset()
var hdr [7 * 4]uint8
@@ -562,7 +573,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
continue
}
name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
- s := syms.Lookup(name, localSymVersion)
+ s := lookup(name, localSymVersion)
if s.Type != 0 {
return errorf("duplicate %s/%s", sect.segname, sect.name)
}
@@ -610,7 +621,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
if machsym.type_&N_EXT == 0 {
v = localSymVersion
}
- s := syms.Lookup(name, v)
+ s := lookup(name, v)
if machsym.type_&N_EXT == 0 {
s.Attr |= sym.AttrDuplicateOK
}
diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go
index a41a7901a9..353f6e0863 100644
--- a/src/cmd/link/internal/loadpe/ldpe.go
+++ b/src/cmd/link/internal/loadpe/ldpe.go
@@ -9,6 +9,7 @@ import (
"cmd/internal/bio"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/pe"
"encoding/binary"
@@ -144,12 +145,21 @@ func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) {
return n, nil
}
-// Load loads the PE file pn from input.
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
+ lookup := func(name string, version int) *sym.Symbol {
+ return l.LookupOrCreate(name, version, syms)
+ }
+ return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
+}
+
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
+ return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn)
+}
+
+// load loads the PE file pn from input.
// Symbols are written into syms, and a slice of the text symbols is returned.
// If an .rsrc section is found, its symbol is returned as rsrc.
-func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
- localSymVersion := syms.IncVersion()
-
+func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
sectsyms := make(map[*pe.Section]*sym.Symbol)
sectdata := make(map[*pe.Section][]byte)
@@ -181,7 +191,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
}
name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
- s := syms.Lookup(name, localSymVersion)
+ s := lookup(name, localSymVersion)
switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
@@ -239,7 +249,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
}
pesym := &f.COFFSymbols[r.SymbolTableIndex]
- gosym, err := readpesym(arch, syms, f, pesym, sectsyms, localSymVersion)
+ gosym, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
if err != nil {
return nil, nil, err
}
@@ -351,7 +361,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
}
}
- s, err := readpesym(arch, syms, f, pesym, sectsyms, localSymVersion)
+ s, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
if err != nil {
return nil, nil, err
}
@@ -435,7 +445,7 @@ func issect(s *pe.COFFSymbol) bool {
return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
}
-func readpesym(arch *sys.Arch, syms *sym.Symbols, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]*sym.Symbol, localSymVersion int) (*sym.Symbol, error) {
+func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]*sym.Symbol, localSymVersion int) (*sym.Symbol, error) {
symname, err := pesym.FullName(f.StringTable)
if err != nil {
return nil, err
@@ -481,10 +491,10 @@ func readpesym(arch *sys.Arch, syms *sym.Symbols, f *pe.File, pesym *pe.COFFSymb
case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
switch pesym.StorageClass {
case IMAGE_SYM_CLASS_EXTERNAL: //global
- s = syms.Lookup(name, 0)
+ s = lookup(name, 0)
case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
- s = syms.Lookup(name, localSymVersion)
+ s = lookup(name, localSymVersion)
s.Attr |= sym.AttrDuplicateOK
default:
diff --git a/src/cmd/link/internal/loadxcoff/ldxcoff.go b/src/cmd/link/internal/loadxcoff/ldxcoff.go
index f52b23ce6a..759b1769dd 100644
--- a/src/cmd/link/internal/loadxcoff/ldxcoff.go
+++ b/src/cmd/link/internal/loadxcoff/ldxcoff.go
@@ -9,6 +9,7 @@ import (
"cmd/internal/bio"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"errors"
"fmt"
@@ -38,13 +39,25 @@ func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) {
return n, nil
}
-// Load loads the Xcoff file pn from f.
+// Load loads xcoff files with the indexed object files.
+func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+ lookup := func(name string, version int) *sym.Symbol {
+ return l.LookupOrCreate(name, version, syms)
+ }
+ return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
+}
+
+// LoadOld uses the old version of object loading.
+func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+ return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn)
+}
+
+// loads the Xcoff file pn from f.
// Symbols are written into syms, and a slice of the text symbols is returned.
-func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
+func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
}
- localSymVersion := syms.IncVersion()
var ldSections []*ldSection
@@ -62,7 +75,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
lds := new(ldSection)
lds.Section = *sect
name := fmt.Sprintf("%s(%s)", pkg, lds.Name)
- s := syms.Lookup(name, localSymVersion)
+ s := lookup(name, localSymVersion)
switch lds.Type {
default:
@@ -100,7 +113,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
continue
}
- s := syms.Lookup(sx.Name, 0)
+ s := lookup(sx.Name, 0)
// Text symbol
if s.Type == sym.STEXT {
@@ -122,7 +135,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
for i, rx := range sect.Relocs {
r := &rs[i]
- r.Sym = syms.Lookup(rx.Symbol.Name, 0)
+ r.Sym = lookup(rx.Symbol.Name, 0)
if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
}
diff --git a/src/cmd/link/internal/objfile/objfile.go b/src/cmd/link/internal/objfile/objfile.go
index 3a4ba8224c..a15d3c3e07 100644
--- a/src/cmd/link/internal/objfile/objfile.go
+++ b/src/cmd/link/internal/objfile/objfile.go
@@ -369,7 +369,7 @@ overwrite:
pc.InlTree[i].Parent = r.readInt32()
pc.InlTree[i].File = r.readSymIndex()
pc.InlTree[i].Line = r.readInt32()
- pc.InlTree[i].Func = r.readSymIndex()
+ pc.InlTree[i].Func = r.readSymIndex().Name
pc.InlTree[i].ParentPC = r.readInt32()
}
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index 45731c0638..9fbcff551a 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -550,7 +550,7 @@ func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
const prefix = "TOC."
var tarSym *sym.Symbol
if strings.HasPrefix(r.Sym.Name, prefix) {
- tarSym = ctxt.Syms.ROLookup(strings.TrimPrefix(r.Sym.Name, prefix), 0)
+ tarSym = r.Sym.R[0].Sym
} else {
ld.Errorf(s, "archreloctoc called for a symbol without TOC anchor")
}
diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go
index 698f8ee653..e9819a064f 100644
--- a/src/cmd/link/internal/sym/symbol.go
+++ b/src/cmd/link/internal/sym/symbol.go
@@ -534,7 +534,7 @@ type InlinedCall struct {
Parent int32 // index of parent in InlTree
File *Symbol // file of the inlined call
Line int32 // line number of the inlined call
- Func *Symbol // function that was inlined
+ Func string // name of the function that was inlined
ParentPC int32 // PC of the instruction just before the inlined body (offset from function start)
}
diff --git a/src/cmd/link/internal/sym/symbols.go b/src/cmd/link/internal/sym/symbols.go
index f0fcf2361b..e772496534 100644
--- a/src/cmd/link/internal/sym/symbols.go
+++ b/src/cmd/link/internal/sym/symbols.go
@@ -86,6 +86,17 @@ func (syms *Symbols) ROLookup(name string, v int) *Symbol {
return syms.hash[v][name]
}
+// Add an existing symbol to the symbol table.
+func (syms *Symbols) Add(s *Symbol) {
+ name := s.Name
+ v := int(s.Version)
+ m := syms.hash[v]
+ if _, ok := m[name]; ok {
+ panic(name + " already added")
+ }
+ m[name] = s
+}
+
// Allocate a new version (i.e. symbol namespace).
func (syms *Symbols) IncVersion() int {
syms.hash = append(syms.hash, make(map[string]*Symbol))
diff --git a/src/cmd/link/internal/sym/symkind.go b/src/cmd/link/internal/sym/symkind.go
index 95311be0cc..1933dd7b21 100644
--- a/src/cmd/link/internal/sym/symkind.go
+++ b/src/cmd/link/internal/sym/symkind.go
@@ -161,3 +161,8 @@ var RelROMap = map[SymKind]SymKind{
SRODATA: SRODATARELRO,
SFUNCTAB: SFUNCTABRELRO,
}
+
+// IsData returns true if the type is a data type.
+func (t SymKind) IsData() bool {
+ return t == SDATA || t == SNOPTRDATA || t == SBSS || t == SNOPTRBSS
+}
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index 155fd8bce3..92830fe8b3 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -376,3 +376,68 @@ func TestIssue34788Android386TLSSequence(t *testing.T) {
}
}
}
+
+const testStrictDupGoSrc = `
+package main
+func f()
+func main() { f() }
+`
+
+const testStrictDupAsmSrc1 = `
+#include "textflag.h"
+TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
+ RET
+`
+
+const testStrictDupAsmSrc2 = `
+#include "textflag.h"
+TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
+ JMP 0(PC)
+`
+
+func TestStrictDup(t *testing.T) {
+ // Check that -strictdups flag works.
+ testenv.MustHaveGoBuild(t)
+
+ tmpdir, err := ioutil.TempDir("", "TestStrictDup")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "x.go")
+ err = ioutil.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ src = filepath.Join(tmpdir, "a.s")
+ err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc1), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ src = filepath.Join(tmpdir, "b.s")
+ err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc2), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("linking with -strictdups=1 failed: %v", err)
+ }
+ if !bytes.Contains(out, []byte("mismatched payload")) {
+ t.Errorf("unexpected output:\n%s", out)
+ }
+
+ cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
+ cmd.Dir = tmpdir
+ out, err = cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("linking with -strictdups=2 did not fail")
+ }
+ if !bytes.Contains(out, []byte("mismatched payload")) {
+ t.Errorf("unexpected output:\n%s", out)
+ }
+}