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() +} - obj.WriteObjFile(Ctxt, bout.Writer, myimportpath) +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, 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,30 +380,25 @@ 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") - if ctxt.Arch.Family == sys.ARM { - loadinternal(ctxt, "math") - } - if *flagRace { - loadinternal(ctxt, "runtime/race") - } - if *flagMsan { - loadinternal(ctxt, "runtime/msan") - } + 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. - for i := 0; i < len(ctxt.Library); i++ { + i := 0 + for ; i < len(ctxt.Library); i++ { lib := ctxt.Library[i] if lib.Shlib == "" { if ctxt.Debugvlog > 1 { @@ -411,34 +408,35 @@ func (ctxt *Link) loadlib() { } } - 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) + // load internal packages, if not already + if ctxt.Arch.Family == sys.ARM { + loadinternal(ctxt, "math") + } + if *flagRace { + loadinternal(ctxt, "runtime/race") + } + if *flagMsan { + loadinternal(ctxt, "runtime/msan") + } + loadinternal(ctxt, "runtime") + for ; i < len(ctxt.Library); i++ { + lib := ctxt.Library[i] + if lib.Shlib == "" { + loadobjfile(ctxt, lib) } } - iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil + 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 + } // 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,6 +494,155 @@ func (ctxt *Link) loadlib() { } } + // Conditionally load host objects, or setup for external linking. + hostobjs(ctxt) + hostlinksetup(ctxt) + + if *flagNewobj { + // Add references of externally defined symbols. + ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms) + } + + // Now that we know the link mode, set the dynexp list. + if !*flagNewobj { // set this later in newobj mode + setupdynexp(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 + for _, s := range ctxt.Syms.Allsym { + for i := range s.R { + r := &s.R[i] // Copying sym.Reloc has measurable impact on performance + if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" { + any = true + break + } + } + } + if any { + if *flagLibGCC == "" { + *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc") + } + if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" { + // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a". + // In this case we fail to load libgcc.a and can encounter link + // errors - see if we can find libcompiler_rt.a instead. + *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") + } + if *flagLibGCC != "none" { + hostArchive(ctxt, *flagLibGCC) + } + if ctxt.HeadType == objabi.Hwindows { + if p := ctxt.findLibPath("libmingwex.a"); p != "none" { + hostArchive(ctxt, p) + } + if p := ctxt.findLibPath("libmingw32.a"); p != "none" { + hostArchive(ctxt, p) + } + // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol + // (see https://golang.org/issue/23649 for details). + if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" { + hostArchive(ctxt, p) + } + // TODO: maybe do something similar to peimporteddlls to collect all lib names + // and try link them all to final exe just like libmingwex.a and libmingw32.a: + /* + for: + #cgo windows LDFLAGS: -lmsvcrt -lm + import: + libmsvcrt.a libm.a + */ + } + } + } + + // We've loaded all the code now. + ctxt.Loaded = true + + 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) + // assumes that a dynamic binary always refers to at least one dynamic library. + // Rather than be a source of test cases for glibc, disable dynamic linking + // the same way that gcc would. + // + // Exception: on OS X, programs such as Shark only work with dynamic + // binaries, so leave it enabled on OS X (Mach-O) binaries. + // Also leave it enabled on Solaris which doesn't support + // statically linked binaries. + if ctxt.BuildMode == BuildModeExe { + if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris { + *FlagD = true + } + } + + 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. @@ -534,98 +703,6 @@ func (ctxt *Link) loadlib() { 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 we have any undefined symbols in external - // objects, try to read them from the libgcc file. - any := false - for _, s := range ctxt.Syms.Allsym { - for i := range s.R { - r := &s.R[i] // Copying sym.Reloc has measurable impact on performance - if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" { - any = true - break - } - } - } - if any { - if *flagLibGCC == "" { - *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc") - } - if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" { - // On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a". - // In this case we fail to load libgcc.a and can encounter link - // errors - see if we can find libcompiler_rt.a instead. - *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") - } - if *flagLibGCC != "none" { - hostArchive(ctxt, *flagLibGCC) - } - if ctxt.HeadType == objabi.Hwindows { - if p := ctxt.findLibPath("libmingwex.a"); p != "none" { - hostArchive(ctxt, p) - } - if p := ctxt.findLibPath("libmingw32.a"); p != "none" { - hostArchive(ctxt, p) - } - // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol - // (see https://golang.org/issue/23649 for details). - if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" { - hostArchive(ctxt, p) - } - // TODO: maybe do something similar to peimporteddlls to collect all lib names - // and try link them all to final exe just like libmingwex.a and libmingw32.a: - /* - for: - #cgo windows LDFLAGS: -lmsvcrt -lm - import: - libmsvcrt.a libm.a - */ - } - } - } 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) - - // 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) - // assumes that a dynamic binary always refers to at least one dynamic library. - // Rather than be a source of test cases for glibc, disable dynamic linking - // the same way that gcc would. - // - // Exception: on OS X, programs such as Shark only work with dynamic - // binaries, so leave it enabled on OS X (Mach-O) binaries. - // Also leave it enabled on Solaris which doesn't support - // statically linked binaries. - if ctxt.BuildMode == BuildModeExe { - if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris { - *FlagD = true - } - } - // 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...) } - 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...) + } + 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...) } - 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...) + } + 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...) } - 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...) + } + 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<= 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)), "") + } + } + 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 diff --git a/test/linkx.go b/test/linkx.go index 520a065182..4f85b241a9 100644 --- a/test/linkx.go +++ b/test/linkx.go @@ -29,4 +29,10 @@ func main() { fmt.Println(overwrite) fmt.Println(overwritecopy) fmt.Println(arraycopy[1]) + + // Check non-string symbols are not overwritten. + // This also make them used. + if b || x != 0 { + panic("b or x overwritten") + } }