diff options
| author | Cherry Zhang <cherryyz@google.com> | 2019-11-05 14:57:48 -0500 |
|---|---|---|
| committer | Cherry Zhang <cherryyz@google.com> | 2019-11-05 14:57:48 -0500 |
| commit | bbae923d2032780851ba396147e9862d95ea4061 (patch) | |
| tree | d3535a413deac679080230c4f56284feecdd9239 /src | |
| parent | 7e71c9c3edbf5b7a8608d6f739c20420a618e0ab (diff) | |
| parent | 9cf6c65ca302ef5300ec970640dfa446d45ac0b8 (diff) | |
| download | go-bbae923d2032780851ba396147e9862d95ea4061.tar.xz | |
cmd: merge branch 'dev.link' into master
In the dev.link branch we implemented the new object file format
and (part of) the linker improvements described in
https://golang.org/s/better-linker
The new object file is index-based and provides random access.
The linker maps the object files into read-only memory, and
access symbols on-demand using indices, as opposed to reading
all object files sequentially into the heap with the old format.
The linker carries symbol informations using indices (as opposed
to Symbol data structure). Symbols are created after the
reachability analysis, and only created for reachable symbols.
This reduces the linker's memory usage.
Linking cmd/compile, it creates ~25% fewer Symbols, and reduces
memory usage (inuse_space) by ~15%. (More results from Than.)
Currently, both the old and new object file formats are supported.
The old format is used by default. The new format can be turned
on by using the compiler/assembler/linker's -newobj flag. Note
that the flag needs to be specified consistently to all
compilations, i.e.
go build -gcflags=all=-newobj -asmflags=all=-newobj -ldflags=-newobj
Change-Id: Ia0e35306b5b9b5b19fdc7fa7c602d4ce36fa6abd
Diffstat (limited to 'src')
49 files changed, 4465 insertions, 336 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) + } +} diff --git a/src/runtime/internal/atomic/sys_linux_arm.s b/src/runtime/internal/atomic/sys_linux_arm.s index 0c1cc3dc86..192be4b64f 100644 --- a/src/runtime/internal/atomic/sys_linux_arm.s +++ b/src/runtime/internal/atomic/sys_linux_arm.s @@ -29,9 +29,9 @@ TEXT runtime∕internal∕atomic·Cas(SB),NOSPLIT|NOFRAME,$0 CMP $7, R11 BLT 2(PC) JMP ·armcas(SB) - JMP ·kernelcas<>(SB) + JMP kernelcas<>(SB) -TEXT runtime∕internal∕atomic·kernelcas<>(SB),NOSPLIT,$0 +TEXT kernelcas<>(SB),NOSPLIT,$0 MOVW ptr+0(FP), R2 // trigger potential paging fault here, // because we don't know how to traceback through __kuser_cmpxchg |
