Commit bababde7 authored by Gerrit Code Review's avatar Gerrit Code Review

Merge "cmd: merge branch 'dev.link' into master"

parents 7a5e0fec bbae923d
......@@ -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 (
......
......@@ -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()
}
......@@ -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) {
......
......@@ -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)
......
......@@ -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()
......
......@@ -111,21 +111,7 @@ func dumpCompilerObj(bout *bio.Writer) {
dumpexport(bout)
}
func dumpLinkerObj(bout *bio.Writer) {
printObjHeader(bout)
if len(pragcgobuf) != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
fmt.Fprintf(bout, "\n$$ // cgo\n")
if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
Fatalf("serializing pragcgobuf: %v", err)
}
fmt.Fprintf(bout, "\n$$\n\n")
}
fmt.Fprintf(bout, "\n!\n")
func dumpdata() {
externs := len(externdcl)
dumpglobls()
......@@ -163,8 +149,24 @@ func dumpLinkerObj(bout *bio.Writer) {
}
addGCLocals()
}
func dumpLinkerObj(bout *bio.Writer) {
printObjHeader(bout)
if len(pragcgobuf) != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
fmt.Fprintf(bout, "\n$$ // cgo\n")
if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
Fatalf("serializing pragcgobuf: %v", err)
}
fmt.Fprintf(bout, "\n$$\n\n")
}
fmt.Fprintf(bout, "\n!\n")
obj.WriteObjFile(Ctxt, bout.Writer, myimportpath)
obj.WriteObjFile(Ctxt, bout, myimportpath)
}
func addptabs() {
......
......@@ -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.
......
......@@ -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",
......
......@@ -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",
......
......@@ -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.
......
......@@ -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.
......
......@@ -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")
}
......
......@@ -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})
......
......@@ -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)
......
// 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),
}
}
}
}
// 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
}
}
// 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},
}
// 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
}
// 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
}
// 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
}
......@@ -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{}) {
......
......@@ -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)
......
// 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...)
}
......@@ -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},
}
......
......@@ -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)
}
}
}
}
}
}
}
......@@ -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++ {
......
......@@ -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)
......
// 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)
}
......@@ -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
......
......@@ -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.
......
......@@ -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++
......
......@@ -38,6 +38,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loadelf"
"cmd/link/internal/loader"
"cmd/link/internal/loadmacho"
"cmd/link/internal/loadpe"
"cmd/link/internal/loadxcoff"
......@@ -57,6 +58,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
)
......@@ -378,18 +380,35 @@ func (ctxt *Link) findLibPath(libname string) string {
}
func (ctxt *Link) loadlib() {
switch ctxt.BuildMode {
case BuildModeCShared, BuildModePlugin:
s := ctxt.Syms.Lookup("runtime.islibrary", 0)
s.Attr |= sym.AttrDuplicateOK
s.AddUint8(1)
case BuildModeCArchive:
s := ctxt.Syms.Lookup("runtime.isarchive", 0)
s.Attr |= sym.AttrDuplicateOK
s.AddUint8(1)
if *flagNewobj {
var flags uint32
switch *FlagStrictDups {
case 0:
// nothing to do
case 1, 2:
flags = loader.FlagStrictDups
default:
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
}
ctxt.loader = loader.NewLoader(flags)
}
loadinternal(ctxt, "runtime")
ctxt.cgo_export_static = make(map[string]bool)
ctxt.cgo_export_dynamic = make(map[string]bool)
// ctxt.Library grows during the loop, so not a range loop.
i := 0
for ; i < len(ctxt.Library); i++ {
lib := ctxt.Library[i]
if lib.Shlib == "" {
if ctxt.Debugvlog > 1 {
ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref)
}
loadobjfile(ctxt, lib)
}
}
// load internal packages, if not already
if ctxt.Arch.Family == sys.ARM {
loadinternal(ctxt, "math")
}
......@@ -399,46 +418,25 @@ func (ctxt *Link) loadlib() {
if *flagMsan {
loadinternal(ctxt, "runtime/msan")
}
// ctxt.Library grows during the loop, so not a range loop.
for i := 0; i < len(ctxt.Library); i++ {
loadinternal(ctxt, "runtime")
for ; i < len(ctxt.Library); i++ {
lib := ctxt.Library[i]
if lib.Shlib == "" {
if ctxt.Debugvlog > 1 {
ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref)
}
loadobjfile(ctxt, lib)
}
}
for _, lib := range ctxt.Library {
if lib.Shlib != "" {
if ctxt.Debugvlog > 1 {
ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref)
}
ldshlibsyms(ctxt, lib.Shlib)
}
if *flagNewobj {
iscgo = ctxt.loader.Lookup("x_cgo_init", 0) != 0
ctxt.canUsePlugins = ctxt.loader.Lookup("plugin.Open", sym.SymVerABIInternal) != 0
} else {
iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
ctxt.canUsePlugins = ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil
}
iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
// We now have enough information to determine the link mode.
determineLinkMode(ctxt)
// Recalculate pe parameters now that we have ctxt.LinkMode set.
if ctxt.HeadType == objabi.Hwindows {
Peinit(ctxt)
}
if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
*FlagTextAddr = 0
}
if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
toc := ctxt.Syms.Lookup(".TOC.", 0)
toc.Type = sym.SDYNIMPORT
}
if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) {
// This indicates a user requested -linkmode=external.
// The startup code uses an import of runtime/cgo to decide
......@@ -456,7 +454,29 @@ func (ctxt *Link) loadlib() {
}
}
if ctxt.LinkMode == LinkInternal {
for _, lib := range ctxt.Library {
if lib.Shlib != "" {
if ctxt.Debugvlog > 1 {
ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref)
}
ldshlibsyms(ctxt, lib.Shlib)
}
}
if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
if *flagNewobj {
// In newobj mode, we typically create sym.Symbols later therefore
// also set cgo attributes later. However, for internal cgo linking,
// the host object loaders still work with sym.Symbols (for now),
// and they need cgo attributes set to work properly. So process
// them now.
lookup := func(name string, ver int) *sym.Symbol { return ctxt.loader.LookupOrCreate(name, ver, ctxt.Syms) }
for _, d := range ctxt.cgodata {
setCgoAttr(ctxt, lookup, d.file, d.pkg, d.directives)
}
ctxt.cgodata = nil
}
// Drop all the cgo_import_static declarations.
// Turns out we won't be needing them.
for _, s := range ctxt.Syms.Allsym {
......@@ -474,85 +494,21 @@ func (ctxt *Link) loadlib() {
}
}
// The Android Q linker started to complain about underalignment of the our TLS
// section. We don't actually use the section on android, so dont't
// generate it.
if objabi.GOOS != "android" {
tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0)
// Conditionally load host objects, or setup for external linking.
hostobjs(ctxt)
hostlinksetup(ctxt)
// runtime.tlsg is used for external linking on platforms that do not define
// a variable to hold g in assembly (currently only intel).
if tlsg.Type == 0 {
tlsg.Type = sym.STLSBSS
tlsg.Size = int64(ctxt.Arch.PtrSize)
} else if tlsg.Type != sym.SDYNIMPORT {
Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type)
}
tlsg.Attr |= sym.AttrReachable
ctxt.Tlsg = tlsg
if *flagNewobj {
// Add references of externally defined symbols.
ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms)
}
var moduledata *sym.Symbol
if ctxt.BuildMode == BuildModePlugin {
moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0)
moduledata.Attr |= sym.AttrLocal
} else {
moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
}
if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT {
// If the module (toolchain-speak for "executable or shared
// library") we are linking contains the runtime package, it
// will define the runtime.firstmoduledata symbol and we
// truncate it back to 0 bytes so we can define its entire
// contents in symtab.go:symtab().
moduledata.Size = 0
// In addition, on ARM, the runtime depends on the linker
// recording the value of GOARM.
if ctxt.Arch.Family == sys.ARM {
s := ctxt.Syms.Lookup("runtime.goarm", 0)
s.Type = sym.SDATA
s.Size = 0
s.AddUint8(uint8(objabi.GOARM))
}
if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0)
s.Type = sym.SDATA
s.Size = 0
s.AddUint8(1)
}
} else {
// If OTOH the module does not contain the runtime package,
// create a local symbol for the moduledata.
moduledata = ctxt.Syms.Lookup("local.moduledata", 0)
moduledata.Attr |= sym.AttrLocal
// Now that we know the link mode, set the dynexp list.
if !*flagNewobj { // set this later in newobj mode
setupdynexp(ctxt)
}
// In all cases way we mark the moduledata as noptrdata to hide it from
// the GC.
moduledata.Type = sym.SNOPTRDATA
moduledata.Attr |= sym.AttrReachable
ctxt.Moduledata = moduledata
// Now that we know the link mode, trim the dynexp list.
x := sym.AttrCgoExportDynamic
if ctxt.LinkMode == LinkExternal {
x = sym.AttrCgoExportStatic
}
w := 0
for i := range dynexp {
if dynexp[i].Attr&x != 0 {
dynexp[w] = dynexp[i]
w++
}
}
dynexp = dynexp[:w]
// In internal link mode, read the host object files.
if ctxt.LinkMode == LinkInternal {
hostobjs(ctxt)
if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
// If we have any undefined symbols in external
// objects, try to read them from the libgcc file.
any := false
......@@ -600,15 +556,71 @@ func (ctxt *Link) loadlib() {
*/
}
}
} else {
hostlinksetup(ctxt)
}
// We've loaded all the code now.
ctxt.Loaded = true
// Record whether we can use plugins.
ctxt.canUsePlugins = (ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil)
importcycles()
if *flagNewobj {
strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
}
}
// Set up dynexp list.
func setupdynexp(ctxt *Link) {
dynexpMap := ctxt.cgo_export_dynamic
if ctxt.LinkMode == LinkExternal {
dynexpMap = ctxt.cgo_export_static
}
dynexp = make([]*sym.Symbol, 0, len(dynexpMap))
for exp := range dynexpMap {
s := ctxt.Syms.Lookup(exp, 0)
dynexp = append(dynexp, s)
}
sort.Sort(byName(dynexp))
// Resolve ABI aliases in the list of cgo-exported functions.
// This is necessary because we load the ABI0 symbol for all
// cgo exports.
for i, s := range dynexp {
if s.Type != sym.SABIALIAS {
continue
}
t := resolveABIAlias(s)
t.Attr |= s.Attr
t.SetExtname(s.Extname())
dynexp[i] = t
}
ctxt.cgo_export_static = nil
ctxt.cgo_export_dynamic = nil
}
// Set up flags and special symbols depending on the platform build mode.
func (ctxt *Link) linksetup() {
switch ctxt.BuildMode {
case BuildModeCShared, BuildModePlugin:
s := ctxt.Syms.Lookup("runtime.islibrary", 0)
s.Type = sym.SNOPTRDATA
s.Attr |= sym.AttrDuplicateOK
s.AddUint8(1)
case BuildModeCArchive:
s := ctxt.Syms.Lookup("runtime.isarchive", 0)
s.Type = sym.SNOPTRDATA
s.Attr |= sym.AttrDuplicateOK
s.AddUint8(1)
}
// Recalculate pe parameters now that we have ctxt.LinkMode set.
if ctxt.HeadType == objabi.Hwindows {
Peinit(ctxt)
}
if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
*FlagTextAddr = 0
}
// If there are no dynamic libraries needed, gcc disables dynamic linking.
// Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
......@@ -626,6 +638,71 @@ func (ctxt *Link) loadlib() {
}
}
if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
toc := ctxt.Syms.Lookup(".TOC.", 0)
toc.Type = sym.SDYNIMPORT
}
// The Android Q linker started to complain about underalignment of the our TLS
// section. We don't actually use the section on android, so dont't
// generate it.
if objabi.GOOS != "android" {
tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0)
// runtime.tlsg is used for external linking on platforms that do not define
// a variable to hold g in assembly (currently only intel).
if tlsg.Type == 0 {
tlsg.Type = sym.STLSBSS
tlsg.Size = int64(ctxt.Arch.PtrSize)
} else if tlsg.Type != sym.SDYNIMPORT {
Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type)
}
tlsg.Attr |= sym.AttrReachable
ctxt.Tlsg = tlsg
}
var moduledata *sym.Symbol
if ctxt.BuildMode == BuildModePlugin {
moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0)
moduledata.Attr |= sym.AttrLocal
} else {
moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
}
if moduledata.Type != 0 && moduledata.Type != sym.SDYNIMPORT {
// If the module (toolchain-speak for "executable or shared
// library") we are linking contains the runtime package, it
// will define the runtime.firstmoduledata symbol and we
// truncate it back to 0 bytes so we can define its entire
// contents in symtab.go:symtab().
moduledata.Size = 0
// In addition, on ARM, the runtime depends on the linker
// recording the value of GOARM.
if ctxt.Arch.Family == sys.ARM {
s := ctxt.Syms.Lookup("runtime.goarm", 0)
s.Type = sym.SDATA
s.Size = 0
s.AddUint8(uint8(objabi.GOARM))
}
if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0)
s.Type = sym.SDATA
s.Size = 0
s.AddUint8(1)
}
} else {
// If OTOH the module does not contain the runtime package,
// create a local symbol for the moduledata.
moduledata = ctxt.Syms.Lookup("local.moduledata", 0)
moduledata.Attr |= sym.AttrLocal
}
// In all cases way we mark the moduledata as noptrdata to hide it from
// the GC.
moduledata.Type = sym.SNOPTRDATA
moduledata.Attr |= sym.AttrReachable
ctxt.Moduledata = moduledata
// If package versioning is required, generate a hash of the
// packages used in the link.
if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
......@@ -643,59 +720,6 @@ func (ctxt *Link) loadlib() {
got.Attr |= sym.AttrReachable
}
}
importcycles()
// put symbols into Textp
// do it in postorder so that packages are laid down in dependency order
// internal first, then everything else
ctxt.Library = postorder(ctxt.Library)
for _, doInternal := range [2]bool{true, false} {
for _, lib := range ctxt.Library {
if isRuntimeDepPkg(lib.Pkg) != doInternal {
continue
}
ctxt.Textp = append(ctxt.Textp, lib.Textp...)
for _, s := range lib.DupTextSyms {
if !s.Attr.OnList() {
ctxt.Textp = append(ctxt.Textp, s)
s.Attr |= sym.AttrOnList
// dupok symbols may be defined in multiple packages. its
// associated package is chosen sort of arbitrarily (the
// first containing package that the linker loads). canonicalize
// it here to the package with which it will be laid down
// in text.
s.File = objabi.PathToPrefix(lib.Pkg)
}
}
}
}
if len(ctxt.Shlibs) > 0 {
// We might have overwritten some functions above (this tends to happen for the
// autogenerated type equality/hashing functions) and we don't want to generated
// pcln table entries for these any more so remove them from Textp.
textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
for _, s := range ctxt.Textp {
if s.Type != sym.SDYNIMPORT {
textp = append(textp, s)
}
}
ctxt.Textp = textp
}
// Resolve ABI aliases in the list of cgo-exported functions.
// This is necessary because we load the ABI0 symbol for all
// cgo exports.
for i, s := range dynexp {
if s.Type != sym.SABIALIAS {
continue
}
t := resolveABIAlias(s)
t.Attr |= s.Attr
t.SetExtname(s.Extname())
dynexp[i] = t
}
}
// mangleTypeSym shortens the names of symbols that represent Go types
......@@ -991,6 +1015,9 @@ func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType obja
}
func hostobjs(ctxt *Link) {
if ctxt.LinkMode != LinkInternal {
return
}
var h *Hostobj
for i := 0; i < len(hostobj); i++ {
......@@ -1665,55 +1692,107 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
if magic == 0x7f454c46 { // \x7F E L F
ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, flags, err := loadelf.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
if err != nil {
Errorf(nil, "%v", err)
return
if *flagNewobj {
ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
if err != nil {
Errorf(nil, "%v", err)
return
}
ehdr.flags = flags
ctxt.Textp = append(ctxt.Textp, textp...)
}
return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
} else {
ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, flags, err := loadelf.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
if err != nil {
Errorf(nil, "%v", err)
return
}
ehdr.flags = flags
ctxt.Textp = append(ctxt.Textp, textp...)
}
ehdr.flags = flags
ctxt.Textp = append(ctxt.Textp, textp...)
return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
}
if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe {
ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadmacho.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
if *flagNewobj {
ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
} else {
ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadmacho.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
ctxt.Textp = append(ctxt.Textp, textp...)
return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
}
if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 {
ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, rsrc, err := loadpe.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
if *flagNewobj {
ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
if rsrc != nil {
setpersrc(ctxt, rsrc)
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
if rsrc != nil {
setpersrc(ctxt, rsrc)
return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
} else {
ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, rsrc, err := loadpe.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
if rsrc != nil {
setpersrc(ctxt, rsrc)
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
ctxt.Textp = append(ctxt.Textp, textp...)
return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
}
if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) {
ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadxcoff.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
if *flagNewobj {
ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
} else {
ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadxcoff.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
ctxt.Textp = append(ctxt.Textp, textp...)
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
/* check the header */
......@@ -1809,7 +1888,12 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
default:
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
}
c := objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
var c int
if *flagNewobj {
ctxt.loader.Preload(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
} else {
c = objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
}
strictDupMsgCount += c
addImports(ctxt, lib, pn)
return nil
......@@ -1964,7 +2048,17 @@ func ldshlibsyms(ctxt *Link, shlib string) {
ver = sym.SymVerABIInternal
}
lsym := ctxt.Syms.Lookup(elfsym.Name, ver)
var lsym *sym.Symbol
if *flagNewobj {
i := ctxt.loader.AddExtSym(elfsym.Name, ver)
if i == 0 {
continue
}
lsym = ctxt.Syms.Newsym(elfsym.Name, ver)
ctxt.loader.Syms[i] = lsym
} else {
lsym = ctxt.Syms.Lookup(elfsym.Name, ver)
}
// Because loadlib above loads all .a files before loading any shared
// libraries, any non-dynimport symbols we find that duplicate symbols
// already loaded should be ignored (the symbols from the .a files
......@@ -1993,7 +2087,17 @@ func ldshlibsyms(ctxt *Link, shlib string) {
// mangle Go function names in the .so to include the
// ABI.
if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
alias := ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal)
var alias *sym.Symbol
if *flagNewobj {
i := ctxt.loader.AddExtSym(elfsym.Name, sym.SymVerABIInternal)
if i == 0 {
continue
}
alias = ctxt.Syms.Newsym(elfsym.Name, sym.SymVerABIInternal)
ctxt.loader.Syms[i] = alias
} else {
alias = ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal)
}
if alias.Type != 0 {
continue
}
......@@ -2388,6 +2492,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)
}
......@@ -2584,3 +2691,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)
}
}
}
......@@ -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 {
......
......@@ -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)
......
......@@ -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))
......
......@@ -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)
......
......@@ -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 }
......@@ -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
}
......
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package loader
import (
"bytes"
"cmd/internal/bio"
"cmd/internal/dwarf"
"cmd/internal/goobj2"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/sym"
"fmt"
"log"
"os"
"sort"
"strconv"
"strings"
)
var _ = fmt.Print
// Sym encapsulates a global symbol index, used to identify a specific
// Go symbol. The 0-valued Sym is corresponds to an invalid symbol.
type Sym int
// Relocs encapsulates the set of relocations on a given symbol; an
// instance of this type is returned by the Loader Relocs() method.
type Relocs struct {
Count int // number of relocs
li int // local index of symbol whose relocs we're examining
r *oReader // object reader for containing package
l *Loader // loader
ext *sym.Symbol // external symbol if not nil
}
// Reloc contains the payload for a specific relocation.
// TODO: replace this with sym.Reloc, once we change the
// relocation target from "*sym.Symbol" to "loader.Sym" in sym.Reloc.
type Reloc struct {
Off int32 // offset to rewrite
Size uint8 // number of bytes to rewrite: 0, 1, 2, or 4
Type objabi.RelocType // the relocation type
Add int64 // addend
Sym Sym // global index of symbol the reloc addresses
}
// oReader is a wrapper type of obj.Reader, along with some
// extra information.
// TODO: rename to objReader once the old one is gone?
type oReader struct {
*goobj2.Reader
unit *sym.CompilationUnit
version int // version of static symbol
flags uint32 // read from object file
pkgprefix string
rcache []Sym // cache mapping local PkgNone symbol to resolved Sym
}
type objIdx struct {
r *oReader
i Sym // start index
e Sym // end index
}
type nameVer struct {
name string
v int
}
type bitmap []uint32
// set the i-th bit.
func (bm bitmap) Set(i Sym) {
n, r := uint(i)/32, uint(i)%32
bm[n] |= 1 << r
}
// whether the i-th bit is set.
func (bm bitmap) Has(i Sym) bool {
n, r := uint(i)/32, uint(i)%32
return bm[n]&(1<<r) != 0
}
func makeBitmap(n int) bitmap {
return make(bitmap, (n+31)/32)
}
// A Loader loads new object files and resolves indexed symbol references.
type Loader struct {
start map[*oReader]Sym // map from object file to its start index
objs []objIdx // sorted by start index (i.e. objIdx.i)
max Sym // current max index
extStart Sym // from this index on, the symbols are externally defined
extSyms []nameVer // externally defined symbols
builtinSyms []Sym // global index of builtin symbols
ocache int // index (into 'objs') of most recent lookup
symsByName [2]map[string]Sym // map symbol name to index, two maps are for ABI0 and ABIInternal
extStaticSyms map[nameVer]Sym // externally defined static symbols, keyed by name
overwrite map[Sym]Sym // overwrite[i]=j if symbol j overwrites symbol i
itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
objByPkg map[string]*oReader // map package path to its Go object reader
Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now.
Reachable bitmap // bitmap of reachable symbols, indexed by global index
// Used to implement field tracking; created during deadcode if
// field tracking is enabled. Reachparent[K] contains the index of
// the symbol that triggered the marking of symbol K as live.
Reachparent []Sym
relocBatch []sym.Reloc // for bulk allocation of relocations
flags uint32
strictDupMsgs int // number of strict-dup warning/errors, when FlagStrictDups is enabled
}
const (
// Loader.flags
FlagStrictDups = 1 << iota
)
func NewLoader(flags uint32) *Loader {
nbuiltin := goobj2.NBuiltin()
return &Loader{
start: make(map[*oReader]Sym),
objs: []objIdx{{nil, 0, 0}},
symsByName: [2]map[string]Sym{make(map[string]Sym), make(map[string]Sym)},
objByPkg: make(map[string]*oReader),
overwrite: make(map[Sym]Sym),
itablink: make(map[Sym]struct{}),
extStaticSyms: make(map[nameVer]Sym),
builtinSyms: make([]Sym, nbuiltin),
flags: flags,
}
}
// Return the start index in the global index space for a given object file.
func (l *Loader) startIndex(r *oReader) Sym {
return l.start[r]
}
// Add object file r, return the start index.
func (l *Loader) addObj(pkg string, r *oReader) Sym {
if _, ok := l.start[r]; ok {
panic("already added")
}
pkg = objabi.PathToPrefix(pkg) // the object file contains escaped package path
if _, ok := l.objByPkg[pkg]; !ok {
l.objByPkg[pkg] = r
}
n := r.NSym() + r.NNonpkgdef()
i := l.max + 1
l.start[r] = i
l.objs = append(l.objs, objIdx{r, i, i + Sym(n) - 1})
l.max += Sym(n)
return i
}
// Add a symbol with a given index, return if it is added.
func (l *Loader) AddSym(name string, ver int, i Sym, r *oReader, dupok bool, typ sym.SymKind) bool {
if l.extStart != 0 {
panic("AddSym called after AddExtSym is called")
}
if ver == r.version {
// Static symbol. Add its global index but don't
// add to name lookup table, as it cannot be
// referenced by name.
return true
}
if oldi, ok := l.symsByName[ver][name]; ok {
if dupok {
if l.flags&FlagStrictDups != 0 {
l.checkdup(name, i, r, oldi)
}
return false
}
oldr, li := l.toLocal(oldi)
oldsym := goobj2.Sym{}
oldsym.Read(oldr.Reader, oldr.SymOff(li))
if oldsym.Dupok() {
return false
}
overwrite := r.DataSize(int(i-l.startIndex(r))) != 0
if overwrite {
// new symbol overwrites old symbol.
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type)]
if !oldtyp.IsData() && r.DataSize(li) == 0 {
log.Fatalf("duplicated definition of symbol " + name)
}
l.overwrite[oldi] = i
} else {
// old symbol overwrites new symbol.
if typ != sym.SDATA && typ != sym.SNOPTRDATA && typ != sym.SBSS && typ != sym.SNOPTRBSS { // only allow overwriting data symbol
log.Fatalf("duplicated definition of symbol " + name)
}
l.overwrite[i] = oldi
return false
}
}
l.symsByName[ver][name] = i
return true
}
// Add an external symbol (without index). Return the index of newly added
// symbol, or 0 if not added.
func (l *Loader) AddExtSym(name string, ver int) Sym {
static := ver >= sym.SymVerStatic
if static {
if _, ok := l.extStaticSyms[nameVer{name, ver}]; ok {
return 0
}
} else {
if _, ok := l.symsByName[ver][name]; ok {
return 0
}
}
i := l.max + 1
if static {
l.extStaticSyms[nameVer{name, ver}] = i
} else {
l.symsByName[ver][name] = i
}
l.max++
if l.extStart == 0 {
l.extStart = i
}
l.extSyms = append(l.extSyms, nameVer{name, ver})
l.growSyms(int(i))
return i
}
func (l *Loader) IsExternal(i Sym) bool {
return l.extStart != 0 && i >= l.extStart
}
// Ensure Syms slice has enough space.
func (l *Loader) growSyms(i int) {
n := len(l.Syms)
if n > i {
return
}
l.Syms = append(l.Syms, make([]*sym.Symbol, i+1-n)...)
}
// Convert a local index to a global index.
func (l *Loader) toGlobal(r *oReader, i int) Sym {
g := l.startIndex(r) + Sym(i)
if ov, ok := l.overwrite[g]; ok {
return ov
}
return g
}
// Convert a global index to a local index.
func (l *Loader) toLocal(i Sym) (*oReader, int) {
if ov, ok := l.overwrite[i]; ok {
i = ov
}
if l.IsExternal(i) {
return nil, int(i - l.extStart)
}
oc := l.ocache
if oc != 0 && i >= l.objs[oc].i && i <= l.objs[oc].e {
return l.objs[oc].r, int(i - l.objs[oc].i)
}
// Search for the local object holding index i.
// Below k is the first one that has its start index > i,
// so k-1 is the one we want.
k := sort.Search(len(l.objs), func(k int) bool {
return l.objs[k].i > i
})
l.ocache = k - 1
return l.objs[k-1].r, int(i - l.objs[k-1].i)
}
// rcacheGet checks for a valid entry for 's' in the readers cache,
// where 's' is a local PkgIdxNone ref or def, or zero if
// the cache is empty or doesn't contain a value for 's'.
func (or *oReader) rcacheGet(symIdx uint32) Sym {
if len(or.rcache) > 0 {
return or.rcache[symIdx]
}
return 0
}
// rcacheSet installs a new entry in the oReader's PkgNone
// resolver cache for the specified PkgIdxNone ref or def,
// allocating a new cache if needed.
func (or *oReader) rcacheSet(symIdx uint32, gsym Sym) {
if len(or.rcache) == 0 {
or.rcache = make([]Sym, or.NNonpkgdef()+or.NNonpkgref())
}
or.rcache[symIdx] = gsym
}
// Resolve a local symbol reference. Return global index.
func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym {
var rr *oReader
switch p := s.PkgIdx; p {
case goobj2.PkgIdxInvalid:
if s.SymIdx != 0 {
panic("bad sym ref")
}
return 0
case goobj2.PkgIdxNone:
// Check for cached version first
if cached := r.rcacheGet(s.SymIdx); cached != 0 {
return cached
}
// Resolve by name
i := int(s.SymIdx) + r.NSym()
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
v := abiToVer(osym.ABI, r.version)
gsym := l.Lookup(name, v)
// Add to cache, then return.
r.rcacheSet(s.SymIdx, gsym)
return gsym
case goobj2.PkgIdxBuiltin:
return l.builtinSyms[s.SymIdx]
case goobj2.PkgIdxSelf:
rr = r
default:
pkg := r.Pkg(int(p))
var ok bool
rr, ok = l.objByPkg[pkg]
if !ok {
log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
}
}
return l.toGlobal(rr, int(s.SymIdx))
}
// Look up a symbol by name, return global index, or 0 if not found.
// This is more like Syms.ROLookup than Lookup -- it doesn't create
// new symbol.
func (l *Loader) Lookup(name string, ver int) Sym {
if ver >= sym.SymVerStatic {
return l.extStaticSyms[nameVer{name, ver}]
}
return l.symsByName[ver][name]
}
// Returns whether i is a dup of another symbol, and i is not
// "primary", i.e. Lookup i by name will not return i.
func (l *Loader) IsDup(i Sym) bool {
if _, ok := l.overwrite[i]; ok {
return true
}
if l.IsExternal(i) {
return false
}
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
if !osym.Dupok() {
return false
}
if osym.Name == "" {
return false // Unnamed aux symbol cannot be dup.
}
if osym.ABI == goobj2.SymABIstatic {
return false // Static symbol cannot be dup.
}
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
ver := abiToVer(osym.ABI, r.version)
return l.symsByName[ver][name] != i
}
// Check that duplicate symbols have same contents.
func (l *Loader) checkdup(name string, i Sym, r *oReader, dup Sym) {
li := int(i - l.startIndex(r))
p := r.Data(li)
if strings.HasPrefix(name, "go.info.") {
p, _ = patchDWARFName1(p, r)
}
rdup, ldup := l.toLocal(dup)
pdup := rdup.Data(ldup)
if strings.HasPrefix(name, "go.info.") {
pdup, _ = patchDWARFName1(pdup, rdup)
}
if bytes.Equal(p, pdup) {
return
}
reason := "same length but different contents"
if len(p) != len(pdup) {
reason = fmt.Sprintf("new length %d != old length %d", len(p), len(pdup))
}
fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.unit.Lib, name, rdup.unit.Lib, reason)
// For the moment, whitelist DWARF subprogram DIEs for
// auto-generated wrapper functions. What seems to happen
// here is that we get different line numbers on formal
// params; I am guessing that the pos is being inherited
// from the spot where the wrapper is needed.
whitelist := strings.HasPrefix(name, "go.info.go.interface") ||
strings.HasPrefix(name, "go.info.go.builtin") ||
strings.HasPrefix(name, "go.debuglines")
if !whitelist {
l.strictDupMsgs++
}
}
func (l *Loader) NStrictDupMsgs() int { return l.strictDupMsgs }
// Number of total symbols.
func (l *Loader) NSym() int {
return int(l.max + 1)
}
// Number of defined Go symbols.
func (l *Loader) NDef() int {
return int(l.extStart)
}
// Returns the raw (unpatched) name of the i-th symbol.
func (l *Loader) RawSymName(i Sym) string {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return s.Name
}
return ""
}
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return osym.Name
}
// Returns the (patched) name of the i-th symbol.
func (l *Loader) SymName(i Sym) string {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return s.Name // external name should already be patched?
}
return ""
}
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
}
// Returns the type of the i-th symbol.
func (l *Loader) SymType(i Sym) sym.SymKind {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return s.Type
}
return 0
}
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
}
// Returns the attributes of the i-th symbol.
func (l *Loader) SymAttr(i Sym) uint8 {
if l.IsExternal(i) {
// TODO: do something? External symbols have different representation of attributes. For now, ReflectMethod is the only thing matters and it cannot be set by external symbol.
return 0
}
r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return osym.Flag
}
// Returns whether the i-th symbol has ReflectMethod attribute set.
func (l *Loader) IsReflectMethod(i Sym) bool {
return l.SymAttr(i)&goobj2.SymFlagReflectMethod != 0
}
// Returns whether this is a Go type symbol.
func (l *Loader) IsGoType(i Sym) bool {
return l.SymAttr(i)&goobj2.SymFlagGoType != 0
}
// Returns whether this is a "go.itablink.*" symbol.
func (l *Loader) IsItabLink(i Sym) bool {
if _, ok := l.itablink[i]; ok {
return true
}
return false
}
// Returns the symbol content of the i-th symbol. i is global index.
func (l *Loader) Data(i Sym) []byte {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return s.P
}
return nil
}
r, li := l.toLocal(i)
return r.Data(li)
}
// Returns the number of aux symbols given a global index.
func (l *Loader) NAux(i Sym) int {
if l.IsExternal(i) {
return 0
}
r, li := l.toLocal(i)
return r.NAux(li)
}
// Returns the referred symbol of the j-th aux symbol of the i-th
// symbol.
func (l *Loader) AuxSym(i Sym, j int) Sym {
if l.IsExternal(i) {
return 0
}
r, li := l.toLocal(i)
a := goobj2.Aux{}
a.Read(r.Reader, r.AuxOff(li, j))
return l.resolve(r, a.Sym)
}
// ReadAuxSyms reads the aux symbol ids for the specified symbol into the
// slice passed as a parameter. If the slice capacity is not large enough, a new
// larger slice will be allocated. Final slice is returned.
func (l *Loader) ReadAuxSyms(symIdx Sym, dst []Sym) []Sym {
if l.IsExternal(symIdx) {
return dst[:0]
}
naux := l.NAux(symIdx)
if naux == 0 {
return dst[:0]
}
if cap(dst) < naux {
dst = make([]Sym, naux)
}
dst = dst[:0]
r, li := l.toLocal(symIdx)
for i := 0; i < naux; i++ {
a := goobj2.Aux{}
a.Read(r.Reader, r.AuxOff(li, i))
dst = append(dst, l.resolve(r, a.Sym))
}
return dst
}
// OuterSym gets the outer symbol for host object loaded symbols.
func (l *Loader) OuterSym(i Sym) Sym {
sym := l.Syms[i]
if sym != nil && sym.Outer != nil {
outer := sym.Outer
return l.Lookup(outer.Name, int(outer.Version))
}
return 0
}
// SubSym gets the subsymbol for host object loaded symbols.
func (l *Loader) SubSym(i Sym) Sym {
sym := l.Syms[i]
if sym != nil && sym.Sub != nil {
sub := sym.Sub
return l.Lookup(sub.Name, int(sub.Version))
}
return 0
}
// Initialize Reachable bitmap for running deadcode pass.
func (l *Loader) InitReachable() {
l.Reachable = makeBitmap(l.NSym())
}
// At method returns the j-th reloc for a global symbol.
func (relocs *Relocs) At(j int) Reloc {
if relocs.ext != nil {
rel := &relocs.ext.R[j]
return Reloc{
Off: rel.Off,
Size: rel.Siz,
Type: rel.Type,
Add: rel.Add,
Sym: relocs.l.Lookup(rel.Sym.Name, int(rel.Sym.Version)),
}
}
rel := goobj2.Reloc{}
rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j))
target := relocs.l.resolve(relocs.r, rel.Sym)
return Reloc{
Off: rel.Off,
Size: rel.Siz,
Type: objabi.RelocType(rel.Type),
Add: rel.Add,
Sym: target,
}
}
// ReadAll method reads all relocations for a symbol into the
// specified slice. If the slice capacity is not large enough, a new
// larger slice will be allocated. Final slice is returned.
func (relocs *Relocs) ReadAll(dst []Reloc) []Reloc {
if relocs.Count == 0 {
return dst[:0]
}
if cap(dst) < relocs.Count {
dst = make([]Reloc, relocs.Count)
}
dst = dst[:0]
if relocs.ext != nil {
for i := 0; i < relocs.Count; i++ {
erel := &relocs.ext.R[i]
rel := Reloc{
Off: erel.Off,
Size: erel.Siz,
Type: erel.Type,
Add: erel.Add,
Sym: relocs.l.Lookup(erel.Sym.Name, int(erel.Sym.Version)),
}
dst = append(dst, rel)
}
return dst
}
off := relocs.r.RelocOff(relocs.li, 0)
for i := 0; i < relocs.Count; i++ {
rel := goobj2.Reloc{}
rel.Read(relocs.r.Reader, off)
off += uint32(rel.Size())
target := relocs.l.resolve(relocs.r, rel.Sym)
dst = append(dst, Reloc{
Off: rel.Off,
Size: rel.Siz,
Type: objabi.RelocType(rel.Type),
Add: rel.Add,
Sym: target,
})
}
return dst
}
// Relocs returns a Relocs object for the given global sym.
func (l *Loader) Relocs(i Sym) Relocs {
if l.IsExternal(i) {
if s := l.Syms[i]; s != nil {
return Relocs{Count: len(s.R), l: l, ext: s}
}
return Relocs{}
}
r, li := l.toLocal(i)
return l.relocs(r, li)
}
// Relocs returns a Relocs object given a local sym index and reader.
func (l *Loader) relocs(r *oReader, li int) Relocs {
return Relocs{
Count: r.NReloc(li),
li: li,
r: r,
l: l,
}
}
// Preload a package: add autolibs, add symbols to the symbol table.
// Does not read symbol data yet.
func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
roObject, readonly, err := f.Slice(uint64(length))
if err != nil {
log.Fatal("cannot read object file:", err)
}
r := goobj2.NewReaderFromBytes(roObject, readonly)
if r == nil {
panic("cannot read object file")
}
localSymVersion := syms.IncVersion()
pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
or := &oReader{r, unit, localSymVersion, r.Flags(), pkgprefix, nil}
// Autolib
lib.ImportStrings = append(lib.ImportStrings, r.Autolib()...)
// DWARF file table
nfile := r.NDwarfFile()
unit.DWARFFileTable = make([]string, nfile)
for i := range unit.DWARFFileTable {
unit.DWARFFileTable[i] = r.DwarfFile(i)
}
istart := l.addObj(lib.Pkg, or)
ndef := r.NSym()
nnonpkgdef := r.NNonpkgdef()
for i, n := 0, ndef+nnonpkgdef; i < n; i++ {
osym := goobj2.Sym{}
osym.Read(r, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", pkgprefix, -1)
if name == "" {
continue // don't add unnamed aux symbol
}
v := abiToVer(osym.ABI, localSymVersion)
dupok := osym.Dupok()
added := l.AddSym(name, v, istart+Sym(i), or, dupok, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
if added && strings.HasPrefix(name, "go.itablink.") {
l.itablink[istart+Sym(i)] = struct{}{}
}
if added && strings.HasPrefix(name, "runtime.") {
if bi := goobj2.BuiltinIdx(name, v); bi != -1 {
// This is a definition of a builtin symbol. Record where it is.
l.builtinSyms[bi] = istart + Sym(i)
}
}
}
// The caller expects us consuming all the data
f.MustSeek(length, os.SEEK_CUR)
}
// Make sure referenced symbols are added. Most of them should already be added.
// This should only be needed for referenced external symbols.
func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) {
for _, o := range l.objs[1:] {
loadObjRefs(l, o.r, arch, syms)
}
}
func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch, syms *sym.Symbols) {
ndef := r.NSym() + r.NNonpkgdef()
for i, n := 0, r.NNonpkgref(); i < n; i++ {
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(ndef+i))
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
v := abiToVer(osym.ABI, r.version)
l.AddExtSym(name, v)
}
}
func abiToVer(abi uint16, localSymVersion int) int {
var v int
if abi == goobj2.SymABIstatic {
// Static
v = localSymVersion
} else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
// Note that data symbols are "ABI0", which maps to version 0.
v = abiver
} else {
log.Fatalf("invalid symbol ABI: %d", abi)
}
return v
}
func preprocess(arch *sys.Arch, s *sym.Symbol) {
if s.Name != "" && s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
x, err := strconv.ParseUint(s.Name[5:], 16, 64)
if err != nil {
log.Panicf("failed to parse $-symbol %s: %v", s.Name, err)
}
s.Type = sym.SRODATA
s.Attr |= sym.AttrLocal
switch s.Name[:5] {
case "$f32.":
if uint64(uint32(x)) != x {
log.Panicf("$-symbol %s too large: %d", s.Name, x)
}
s.AddUint32(arch, uint32(x))
case "$f64.", "$i64.":
s.AddUint64(arch, x)
default:
log.Panicf("unrecognized $-symbol: %s", s.Name)
}
}
}
// Load full contents.
func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
// create all Symbols first.
l.growSyms(l.NSym())
nr := 0 // total number of sym.Reloc's we'll need
for _, o := range l.objs[1:] {
nr += loadObjSyms(l, syms, o.r)
}
// allocate a single large slab of relocations for all live symbols
l.relocBatch = make([]sym.Reloc, nr)
// external symbols
for i := l.extStart; i <= l.max; i++ {
if s := l.Syms[i]; s != nil {
s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
continue // already loaded from external object
}
nv := l.extSyms[i-l.extStart]
if l.Reachable.Has(i) || strings.HasPrefix(nv.name, "gofile..") { // XXX file symbols are used but not marked
s := syms.Newsym(nv.name, nv.v)
preprocess(arch, s)
s.Attr.Set(sym.AttrReachable, l.Reachable.Has(i))
l.Syms[i] = s
}
}
// load contents of defined symbols
for _, o := range l.objs[1:] {
loadObjFull(l, o.r)
}
// Resolve ABI aliases for external symbols. This is only
// needed for internal cgo linking.
// (The old code does this in deadcode, but deadcode2 doesn't
// do this.)
for i := l.extStart; i <= l.max; i++ {
if s := l.Syms[i]; s != nil && s.Attr.Reachable() {
for ri := range s.R {
r := &s.R[ri]
if r.Sym != nil && r.Sym.Type == sym.SABIALIAS {
r.Sym = r.Sym.R[0].Sym
}
}
}
}
}
// ExtractSymbols grabs the symbols out of the loader for work that hasn't been
// ported to the new symbol type.
func (l *Loader) ExtractSymbols(syms *sym.Symbols) {
// Nil out overwritten symbols.
// Overwritten Go symbols aren't a problem (as they're lazy loaded), but
// symbols loaded from host object loaders are fully loaded, and we might
// have multiple symbols with the same name. This loop nils them out.
for oldI := range l.overwrite {
l.Syms[oldI] = nil
}
// For now, add all symbols to ctxt.Syms.
for _, s := range l.Syms {
if s != nil && s.Name != "" {
syms.Add(s)
}
}
}
// addNewSym adds a new sym.Symbol to the i-th index in the list of symbols.
func (l *Loader) addNewSym(i Sym, syms *sym.Symbols, name string, ver int, unit *sym.CompilationUnit, t sym.SymKind) *sym.Symbol {
s := syms.Newsym(name, ver)
if s.Type != 0 && s.Type != sym.SXREF {
fmt.Println("symbol already processed:", unit.Lib, i, s)
panic("symbol already processed")
}
if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
t = s.Type
}
s.Type = t
s.Unit = unit
l.growSyms(int(i))
l.Syms[i] = s
return s
}
// loadObjSyms creates sym.Symbol objects for the live Syms in the
// object corresponding to object reader "r". Return value is the
// number of sym.Reloc entries required for all the new symbols.
func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) int {
istart := l.startIndex(r)
nr := 0
for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
// If it's been previously loaded in host object loading, we don't need to do it again.
if s := l.Syms[istart+Sym(i)]; s != nil {
// Mark symbol as reachable as it wasn't marked as such before.
s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i)))
nr += r.NReloc(i)
continue
}
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
if name == "" {
continue
}
ver := abiToVer(osym.ABI, r.version)
if osym.ABI != goobj2.SymABIstatic && l.symsByName[ver][name] != istart+Sym(i) {
continue
}
t := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
if t == sym.SXREF {
log.Fatalf("bad sxref")
}
if t == 0 {
log.Fatalf("missing type for %s in %s", name, r.unit.Lib)
}
if !l.Reachable.Has(istart+Sym(i)) && !(t == sym.SRODATA && strings.HasPrefix(name, "type.")) && name != "runtime.addmoduledata" && name != "runtime.lastmoduledatap" {
// No need to load unreachable symbols.
// XXX some type symbol's content may be needed in DWARF code, but they are not marked.
// XXX reference to runtime.addmoduledata may be generated later by the linker in plugin mode.
continue
}
s := l.addNewSym(istart+Sym(i), syms, name, ver, r.unit, t)
s.Attr.Set(sym.AttrReachable, l.Reachable.Has(istart+Sym(i)))
nr += r.NReloc(i)
}
return nr
}
// funcInfoSym records the sym.Symbol for a function, along with a copy
// of the corresponding goobj2.Sym and the index of its FuncInfo aux sym.
// We use this to delay populating FuncInfo until we can batch-allocate
// slices for their sub-objects.
type funcInfoSym struct {
s *sym.Symbol // sym.Symbol for a live function
osym goobj2.Sym // object file symbol data for that function
isym int // global symbol index of FuncInfo aux sym for func
}
// funcAllocInfo records totals/counts for all functions in an objfile;
// used to help with bulk allocation of sym.Symbol sub-objects.
type funcAllocInfo struct {
symPtr uint32 // number of *sym.Symbol's needed in file slices
inlCall uint32 // number of sym.InlinedCall's needed in inltree slices
pcData uint32 // number of sym.Pcdata's needed in pdata slices
fdOff uint32 // number of int64's needed in all Funcdataoff slices
}
// LoadSymbol loads a single symbol by name.
// This function should only be used by the host object loaders.
// NB: This function does NOT set the symbol as reachable.
func (l *Loader) LoadSymbol(name string, version int, syms *sym.Symbols) *sym.Symbol {
global := l.Lookup(name, version)
// If we're already loaded, bail.
if global != 0 && int(global) < len(l.Syms) && l.Syms[global] != nil {
return l.Syms[global]
}
// Read the symbol.
r, i := l.toLocal(global)
istart := l.startIndex(r)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(int(i)))
if l.symsByName[version][name] != istart+Sym(i) {
return nil
}
return l.addNewSym(istart+Sym(i), syms, name, version, r.unit, sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)])
}
// LookupOrCreate looks up a symbol by name, and creates one if not found.
// Either way, it will also create a sym.Symbol for it, if not already.
// This should only be called when interacting with parts of the linker
// that still works on sym.Symbols (i.e. internal cgo linking, for now).
func (l *Loader) LookupOrCreate(name string, version int, syms *sym.Symbols) *sym.Symbol {
i := l.Lookup(name, version)
if i != 0 {
// symbol exists
if int(i) < len(l.Syms) && l.Syms[i] != nil {
return l.Syms[i] // already loaded
}
if l.IsExternal(i) {
panic("Can't load an external symbol.")
}
return l.LoadSymbol(name, version, syms)
}
i = l.AddExtSym(name, version)
s := syms.Newsym(name, version)
l.Syms[i] = s
return s
}
func loadObjFull(l *Loader, r *oReader) {
lib := r.unit.Lib
istart := l.startIndex(r)
resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
i := l.resolve(r, s)
return l.Syms[i]
}
funcs := []funcInfoSym{}
fdsyms := []*sym.Symbol{}
var funcAllocCounts funcAllocInfo
pcdataBase := r.PcdataBase()
rslice := []Reloc{}
for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(i))
name := strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
if name == "" {
continue
}
ver := abiToVer(osym.ABI, r.version)
dupok := osym.Dupok()
if dupok {
if dupsym := l.symsByName[ver][name]; dupsym != istart+Sym(i) {
if l.Reachable.Has(dupsym) {
// A dupok symbol is resolved to another package. We still need
// to record its presence in the current package, as the trampoline
// pass expects packages are laid out in dependency order.
s := l.Syms[dupsym]
if s.Type == sym.STEXT {
lib.DupTextSyms = append(lib.DupTextSyms, s)
}
}
continue
}
}
s := l.Syms[istart+Sym(i)]
if s == nil {
continue
}
if s.Name != name { // Sanity check. We can remove it in the final version.
fmt.Println("name mismatch:", lib, i, s.Name, name)
panic("name mismatch")
}
local := osym.Local()
makeTypelink := osym.Typelink()
size := osym.Siz
// Symbol data
s.P = r.Data(i)
s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
// Relocs
relocs := l.relocs(r, i)
rslice = relocs.ReadAll(rslice)
batch := l.relocBatch
s.R = batch[:relocs.Count:relocs.Count]
l.relocBatch = batch[relocs.Count:]
for j := range s.R {
r := rslice[j]
rs := r.Sym
sz := r.Size
rt := r.Type
if rt == objabi.R_METHODOFF {
if l.Reachable.Has(rs) {
rt = objabi.R_ADDROFF
} else {
sz = 0
rs = 0
}
}
if rt == objabi.R_WEAKADDROFF && !l.Reachable.Has(rs) {
rs = 0
sz = 0
}
if rs != 0 && l.SymType(rs) == sym.SABIALIAS {
rsrelocs := l.Relocs(rs)
rs = rsrelocs.At(0).Sym
}
s.R[j] = sym.Reloc{
Off: r.Off,
Siz: sz,
Type: rt,
Add: r.Add,
Sym: l.Syms[rs],
}
}
// Aux symbol info
isym := -1
naux := r.NAux(i)
for j := 0; j < naux; j++ {
a := goobj2.Aux{}
a.Read(r.Reader, r.AuxOff(i, j))
switch a.Type {
case goobj2.AuxGotype:
typ := resolveSymRef(a.Sym)
if typ != nil {
s.Gotype = typ
}
case goobj2.AuxFuncdata:
fdsyms = append(fdsyms, resolveSymRef(a.Sym))
case goobj2.AuxFuncInfo:
if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
panic("funcinfo symbol not defined in current package")
}
isym = int(a.Sym.SymIdx)
case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
// ignored for now
default:
panic("unknown aux type")
}
}
s.File = r.pkgprefix[:len(r.pkgprefix)-1]
if dupok {
s.Attr |= sym.AttrDuplicateOK
}
if s.Size < int64(size) {
s.Size = int64(size)
}
s.Attr.Set(sym.AttrLocal, local)
s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
if s.Type == sym.SDWARFINFO {
// For DWARF symbols, replace `"".` to actual package prefix
// in the symbol content.
// TODO: maybe we should do this in the compiler and get rid
// of this.
patchDWARFName(s, r)
}
if s.Type != sym.STEXT {
continue
}
if isym == -1 {
continue
}
// Record function sym and associated info for additional
// processing in the loop below.
fwis := funcInfoSym{s: s, isym: isym, osym: osym}
funcs = append(funcs, fwis)
// Read the goobj2.FuncInfo for this text symbol so that we can
// collect allocation counts. We'll read it again in the loop
// below.
b := r.Data(isym)
info := goobj2.FuncInfo{}
info.Read(b)
funcAllocCounts.symPtr += uint32(len(info.File))
funcAllocCounts.pcData += uint32(len(info.Pcdata))
funcAllocCounts.inlCall += uint32(len(info.InlTree))
funcAllocCounts.fdOff += uint32(len(info.Funcdataoff))
}
// At this point we can do batch allocation of the sym.FuncInfo's,
// along with the slices of sub-objects they use.
fiBatch := make([]sym.FuncInfo, len(funcs))
inlCallBatch := make([]sym.InlinedCall, funcAllocCounts.inlCall)
symPtrBatch := make([]*sym.Symbol, funcAllocCounts.symPtr)
pcDataBatch := make([]sym.Pcdata, funcAllocCounts.pcData)
fdOffBatch := make([]int64, funcAllocCounts.fdOff)
// Populate FuncInfo contents for func symbols.
for fi := 0; fi < len(funcs); fi++ {
s := funcs[fi].s
isym := funcs[fi].isym
osym := funcs[fi].osym
s.FuncInfo = &fiBatch[0]
fiBatch = fiBatch[1:]
b := r.Data(isym)
info := goobj2.FuncInfo{}
info.Read(b)
if info.NoSplit != 0 {
s.Attr |= sym.AttrNoSplit
}
if osym.ReflectMethod() {
s.Attr |= sym.AttrReflectMethod
}
if r.Flags()&goobj2.ObjFlagShared != 0 {
s.Attr |= sym.AttrShared
}
if osym.TopFrame() {
s.Attr |= sym.AttrTopFrame
}
pc := s.FuncInfo
if len(info.Funcdataoff) != 0 {
nfd := len(info.Funcdataoff)
pc.Funcdata = fdsyms[:nfd:nfd]
fdsyms = fdsyms[nfd:]
}
info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
pc.Args = int32(info.Args)
pc.Locals = int32(info.Locals)
npc := len(info.Pcdata) - 1 // -1 as we appended one above
pc.Pcdata = pcDataBatch[:npc:npc]
pcDataBatch = pcDataBatch[npc:]
nfd := len(info.Funcdataoff)
pc.Funcdataoff = fdOffBatch[:nfd:nfd]
fdOffBatch = fdOffBatch[nfd:]
nsp := len(info.File)
pc.File = symPtrBatch[:nsp:nsp]
symPtrBatch = symPtrBatch[nsp:]
nic := len(info.InlTree)
pc.InlTree = inlCallBatch[:nic:nic]
inlCallBatch = inlCallBatch[nic:]
pc.Pcsp.P = r.BytesAt(pcdataBase+info.Pcsp, int(info.Pcfile-info.Pcsp))
pc.Pcfile.P = r.BytesAt(pcdataBase+info.Pcfile, int(info.Pcline-info.Pcfile))
pc.Pcline.P = r.BytesAt(pcdataBase+info.Pcline, int(info.Pcinline-info.Pcline))
pc.Pcinline.P = r.BytesAt(pcdataBase+info.Pcinline, int(info.Pcdata[0]-info.Pcinline))
for k := range pc.Pcdata {
pc.Pcdata[k].P = r.BytesAt(pcdataBase+info.Pcdata[k], int(info.Pcdata[k+1]-info.Pcdata[k]))
}
for k := range pc.Funcdataoff {
pc.Funcdataoff[k] = int64(info.Funcdataoff[k])
}
for k := range pc.File {
pc.File[k] = resolveSymRef(info.File[k])
}
for k := range pc.InlTree {
inl := &info.InlTree[k]
pc.InlTree[k] = sym.InlinedCall{
Parent: inl.Parent,
File: resolveSymRef(inl.File),
Line: inl.Line,
Func: l.SymName(l.resolve(r, inl.Func)),
ParentPC: inl.ParentPC,
}
}
dupok := osym.Dupok()
if !dupok {
if s.Attr.OnList() {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr.Set(sym.AttrOnList, true)
lib.Textp = append(lib.Textp, s)
} else {
// there may be a dup in another package
// put into a temp list and add to text later
lib.DupTextSyms = append(lib.DupTextSyms, s)
}
}
}
var emptyPkg = []byte(`"".`)
func patchDWARFName1(p []byte, r *oReader) ([]byte, int) {
// This is kind of ugly. Really the package name should not
// even be included here.
if len(p) < 1 || p[0] != dwarf.DW_ABRV_FUNCTION {
return p, -1
}
e := bytes.IndexByte(p, 0)
if e == -1 {
return p, -1
}
if !bytes.Contains(p[:e], emptyPkg) {
return p, -1
}
pkgprefix := []byte(r.pkgprefix)
patched := bytes.Replace(p[:e], emptyPkg, pkgprefix, -1)
return append(patched, p[e:]...), e
}
func patchDWARFName(s *sym.Symbol, r *oReader) {
patched, e := patchDWARFName1(s.P, r)
if e == -1 {
return
}
s.P = patched
s.Attr.Set(sym.AttrReadOnly, false)
delta := int64(len(s.P)) - s.Size
s.Size = int64(len(s.P))
for i := range s.R {
r := &s.R[i]
if r.Off > int32(e) {
r.Off += int32(delta)
}
}
}
// For debugging.
func (l *Loader) Dump() {
fmt.Println("objs")
for _, obj := range l.objs {
if obj.r != nil {
fmt.Println(obj.i, obj.r.unit.Lib)
}
}
fmt.Println("syms")
for i, s := range l.Syms {
if i == 0 {
continue
}
if s != nil {
fmt.Println(i, s, s.Type)
} else {
fmt.Println(i, l.SymName(Sym(i)), "<not loaded>")
}
}
fmt.Println("overwrite:", l.overwrite)
fmt.Println("symsByName")
for name, i := range l.symsByName[0] {
fmt.Println(i, name, 0)
}
for name, i := range l.symsByName[1] {
fmt.Println(i, name, 1)
}
}
......@@ -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
}
......
......@@ -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:
......
......@@ -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)
}
......
......@@ -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()
}
......
......@@ -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")
}
......
......@@ -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)
}
......
......@@ -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))
......
......@@ -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
}
......@@ -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)
}
}
......@@ -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 runtimeinternalatomic·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
......
......@@ -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")
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment