cmd: merge branch 'dev.link' into master

In the dev.link branch we implemented the new object file format
and (part of) the linker improvements described in
https://golang.org/s/better-linker

The new object file is index-based and provides random access.
The linker maps the object files into read-only memory, and
access symbols on-demand using indices, as opposed to reading
all object files sequentially into the heap with the old format.

The linker carries symbol informations using indices (as opposed
to Symbol data structure). Symbols are created after the
reachability analysis, and only created for reachable symbols.
This reduces the linker's memory usage.

Linking cmd/compile, it creates ~25% fewer Symbols, and reduces
memory usage (inuse_space) by ~15%. (More results from Than.)

Currently, both the old and new object file formats are supported.
The old format is used by default. The new format can be turned
on by using the compiler/assembler/linker's -newobj flag. Note
that the flag needs to be specified consistently to all
compilations, i.e.

go build -gcflags=all=-newobj -asmflags=all=-newobj -ldflags=-newobj

Change-Id: Ia0e35306b5b9b5b19fdc7fa7c602d4ce36fa6abd
This commit is contained in:
Cherry Zhang 2019-11-05 14:57:48 -05:00
commit bbae923d20
50 changed files with 4487 additions and 352 deletions

View File

@ -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 (

View File

@ -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()
}

View File

@ -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) {

View File

@ -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)

View File

@ -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()

View File

@ -111,21 +111,7 @@ func dumpCompilerObj(bout *bio.Writer) {
dumpexport(bout)
}
func dumpLinkerObj(bout *bio.Writer) {
printObjHeader(bout)
if len(pragcgobuf) != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
fmt.Fprintf(bout, "\n$$ // cgo\n")
if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
Fatalf("serializing pragcgobuf: %v", err)
}
fmt.Fprintf(bout, "\n$$\n\n")
}
fmt.Fprintf(bout, "\n!\n")
func dumpdata() {
externs := len(externdcl)
dumpglobls()
@ -163,8 +149,24 @@ func dumpLinkerObj(bout *bio.Writer) {
}
addGCLocals()
}
obj.WriteObjFile(Ctxt, bout.Writer, myimportpath)
func dumpLinkerObj(bout *bio.Writer) {
printObjHeader(bout)
if len(pragcgobuf) != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
fmt.Fprintf(bout, "\n$$ // cgo\n")
if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
Fatalf("serializing pragcgobuf: %v", err)
}
fmt.Fprintf(bout, "\n$$\n\n")
}
fmt.Fprintf(bout, "\n!\n")
obj.WriteObjFile(Ctxt, bout, myimportpath)
}
func addptabs() {

View File

@ -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.

View File

@ -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",

View File

@ -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",

View File

@ -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.

View File

@ -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.

View File

@ -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")
}

View File

@ -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})

View File

@ -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)

View File

@ -0,0 +1,187 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package goobj
import (
"cmd/internal/goobj2"
"cmd/internal/objabi"
"fmt"
"strings"
)
// Read object file in new format. For now we still fill
// the data to the current goobj API.
func (r *objReader) readNew() {
start := uint32(r.offset)
length := r.limit - r.offset
objbytes := make([]byte, length)
r.readFull(objbytes)
rr := goobj2.NewReaderFromBytes(objbytes, false)
if rr == nil {
panic("cannot read object file")
}
// Imports
r.p.Imports = rr.Autolib()
pkglist := rr.Pkglist()
abiToVer := func(abi uint16) int64 {
var vers int64
if abi == goobj2.SymABIstatic {
// Static symbol
vers = r.p.MaxVersion
}
return vers
}
resolveSymRef := func(s goobj2.SymRef) SymID {
var i int
switch p := s.PkgIdx; p {
case goobj2.PkgIdxInvalid:
if s.SymIdx != 0 {
panic("bad sym ref")
}
return SymID{}
case goobj2.PkgIdxNone:
i = int(s.SymIdx) + rr.NSym()
case goobj2.PkgIdxBuiltin:
name, abi := goobj2.BuiltinName(int(s.SymIdx))
return SymID{name, int64(abi)}
case goobj2.PkgIdxSelf:
i = int(s.SymIdx)
default:
pkg := pkglist[p]
return SymID{fmt.Sprintf("%s.<#%d>", pkg, s.SymIdx), 0}
}
sym := goobj2.Sym{}
sym.Read(rr, rr.SymOff(i))
return SymID{sym.Name, abiToVer(sym.ABI)}
}
// Read things for the current goobj API for now.
// Symbols
pcdataBase := start + rr.PcdataBase()
n := rr.NSym() + rr.NNonpkgdef() + rr.NNonpkgref()
ndef := rr.NSym() + rr.NNonpkgdef()
for i := 0; i < n; i++ {
osym := goobj2.Sym{}
osym.Read(rr, rr.SymOff(i))
if osym.Name == "" {
continue // not a real symbol
}
// In a symbol name in an object file, "". denotes the
// prefix for the package in which the object file has been found.
// Expand it.
name := strings.ReplaceAll(osym.Name, `"".`, r.pkgprefix)
symID := SymID{Name: name, Version: abiToVer(osym.ABI)}
r.p.SymRefs = append(r.p.SymRefs, symID)
if i >= ndef {
continue // not a defined symbol from here
}
// Symbol data
dataOff := rr.DataOff(i)
siz := int64(rr.DataSize(i))
sym := Sym{
SymID: symID,
Kind: objabi.SymKind(osym.Type),
DupOK: osym.Dupok(),
Size: int64(osym.Siz),
Data: Data{int64(start + dataOff), siz},
}
r.p.Syms = append(r.p.Syms, &sym)
// Reloc
nreloc := rr.NReloc(i)
sym.Reloc = make([]Reloc, nreloc)
for j := 0; j < nreloc; j++ {
rel := goobj2.Reloc{}
rel.Read(rr, rr.RelocOff(i, j))
sym.Reloc[j] = Reloc{
Offset: int64(rel.Off),
Size: int64(rel.Siz),
Type: objabi.RelocType(rel.Type),
Add: rel.Add,
Sym: resolveSymRef(rel.Sym),
}
}
// Aux symbol info
isym := -1
funcdata := make([]goobj2.SymRef, 0, 4)
naux := rr.NAux(i)
for j := 0; j < naux; j++ {
a := goobj2.Aux{}
a.Read(rr, rr.AuxOff(i, j))
switch a.Type {
case goobj2.AuxGotype:
sym.Type = resolveSymRef(a.Sym)
case goobj2.AuxFuncInfo:
if a.Sym.PkgIdx != goobj2.PkgIdxSelf {
panic("funcinfo symbol not defined in current package")
}
isym = int(a.Sym.SymIdx)
case goobj2.AuxFuncdata:
funcdata = append(funcdata, a.Sym)
case goobj2.AuxDwarfInfo, goobj2.AuxDwarfLoc, goobj2.AuxDwarfRanges, goobj2.AuxDwarfLines:
// nothing to do
default:
panic("unknown aux type")
}
}
// Symbol Info
if isym == -1 {
continue
}
b := rr.BytesAt(rr.DataOff(isym), rr.DataSize(isym))
info := goobj2.FuncInfo{}
info.Read(b)
info.Pcdata = append(info.Pcdata, info.PcdataEnd) // for the ease of knowing where it ends
f := &Func{
Args: int64(info.Args),
Frame: int64(info.Locals),
NoSplit: info.NoSplit != 0,
Leaf: osym.Leaf(),
TopFrame: osym.TopFrame(),
PCSP: Data{int64(pcdataBase + info.Pcsp), int64(info.Pcfile - info.Pcsp)},
PCFile: Data{int64(pcdataBase + info.Pcfile), int64(info.Pcline - info.Pcfile)},
PCLine: Data{int64(pcdataBase + info.Pcline), int64(info.Pcinline - info.Pcline)},
PCInline: Data{int64(pcdataBase + info.Pcinline), int64(info.Pcdata[0] - info.Pcinline)},
PCData: make([]Data, len(info.Pcdata)-1), // -1 as we appended one above
FuncData: make([]FuncData, len(info.Funcdataoff)),
File: make([]string, len(info.File)),
InlTree: make([]InlinedCall, len(info.InlTree)),
}
sym.Func = f
for k := range f.PCData {
f.PCData[k] = Data{int64(pcdataBase + info.Pcdata[k]), int64(info.Pcdata[k+1] - info.Pcdata[k])}
}
for k := range f.FuncData {
symID := resolveSymRef(funcdata[k])
f.FuncData[k] = FuncData{symID, int64(info.Funcdataoff[k])}
}
for k := range f.File {
symID := resolveSymRef(info.File[k])
f.File[k] = symID.Name
}
for k := range f.InlTree {
inl := &info.InlTree[k]
f.InlTree[k] = InlinedCall{
Parent: int64(inl.Parent),
File: resolveSymRef(inl.File).Name,
Line: int64(inl.Line),
Func: resolveSymRef(inl.Func),
ParentPC: int64(inl.ParentPC),
}
}
}
}

View File

@ -0,0 +1,45 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package goobj2
// Builtin (compiler-generated) function references appear
// frequently. We assign special indices for them, so they
// don't need to be referenced by name.
// NBuiltin returns the number of listed builtin
// symbols.
func NBuiltin() int {
return len(builtins)
}
// BuiltinName returns the name and ABI of the i-th
// builtin symbol.
func BuiltinName(i int) (string, int) {
return builtins[i].name, builtins[i].abi
}
// BuiltinIdx returns the index of the builtin with the
// given name and abi, or -1 if it is not a builtin.
func BuiltinIdx(name string, abi int) int {
i, ok := builtinMap[name]
if !ok {
return -1
}
if builtins[i].abi != abi {
return -1
}
return i
}
//go:generate go run mkbuiltin.go
var builtinMap map[string]int
func init() {
builtinMap = make(map[string]int, len(builtins))
for i, b := range builtins {
builtinMap[b.name] = i
}
}

View File

@ -0,0 +1,194 @@
// Code generated by mkbuiltin.go. DO NOT EDIT.
package goobj2
var builtins = [...]struct {
name string
abi int
}{
{"runtime.newobject", 1},
{"runtime.panicdivide", 1},
{"runtime.panicshift", 1},
{"runtime.panicmakeslicelen", 1},
{"runtime.throwinit", 1},
{"runtime.panicwrap", 1},
{"runtime.gopanic", 1},
{"runtime.gorecover", 1},
{"runtime.goschedguarded", 1},
{"runtime.goPanicIndex", 1},
{"runtime.goPanicIndexU", 1},
{"runtime.goPanicSliceAlen", 1},
{"runtime.goPanicSliceAlenU", 1},
{"runtime.goPanicSliceAcap", 1},
{"runtime.goPanicSliceAcapU", 1},
{"runtime.goPanicSliceB", 1},
{"runtime.goPanicSliceBU", 1},
{"runtime.goPanicSlice3Alen", 1},
{"runtime.goPanicSlice3AlenU", 1},
{"runtime.goPanicSlice3Acap", 1},
{"runtime.goPanicSlice3AcapU", 1},
{"runtime.goPanicSlice3B", 1},
{"runtime.goPanicSlice3BU", 1},
{"runtime.goPanicSlice3C", 1},
{"runtime.goPanicSlice3CU", 1},
{"runtime.printbool", 1},
{"runtime.printfloat", 1},
{"runtime.printint", 1},
{"runtime.printhex", 1},
{"runtime.printuint", 1},
{"runtime.printcomplex", 1},
{"runtime.printstring", 1},
{"runtime.printpointer", 1},
{"runtime.printiface", 1},
{"runtime.printeface", 1},
{"runtime.printslice", 1},
{"runtime.printnl", 1},
{"runtime.printsp", 1},
{"runtime.printlock", 1},
{"runtime.printunlock", 1},
{"runtime.concatstring2", 1},
{"runtime.concatstring3", 1},
{"runtime.concatstring4", 1},
{"runtime.concatstring5", 1},
{"runtime.concatstrings", 1},
{"runtime.cmpstring", 1},
{"runtime.intstring", 1},
{"runtime.slicebytetostring", 1},
{"runtime.slicebytetostringtmp", 1},
{"runtime.slicerunetostring", 1},
{"runtime.stringtoslicebyte", 1},
{"runtime.stringtoslicerune", 1},
{"runtime.slicecopy", 1},
{"runtime.slicestringcopy", 1},
{"runtime.decoderune", 1},
{"runtime.countrunes", 1},
{"runtime.convI2I", 1},
{"runtime.convT16", 1},
{"runtime.convT32", 1},
{"runtime.convT64", 1},
{"runtime.convTstring", 1},
{"runtime.convTslice", 1},
{"runtime.convT2E", 1},
{"runtime.convT2Enoptr", 1},
{"runtime.convT2I", 1},
{"runtime.convT2Inoptr", 1},
{"runtime.assertE2I", 1},
{"runtime.assertE2I2", 1},
{"runtime.assertI2I", 1},
{"runtime.assertI2I2", 1},
{"runtime.panicdottypeE", 1},
{"runtime.panicdottypeI", 1},
{"runtime.panicnildottype", 1},
{"runtime.ifaceeq", 1},
{"runtime.efaceeq", 1},
{"runtime.fastrand", 1},
{"runtime.makemap64", 1},
{"runtime.makemap", 1},
{"runtime.makemap_small", 1},
{"runtime.mapaccess1", 1},
{"runtime.mapaccess1_fast32", 1},
{"runtime.mapaccess1_fast64", 1},
{"runtime.mapaccess1_faststr", 1},
{"runtime.mapaccess1_fat", 1},
{"runtime.mapaccess2", 1},
{"runtime.mapaccess2_fast32", 1},
{"runtime.mapaccess2_fast64", 1},
{"runtime.mapaccess2_faststr", 1},
{"runtime.mapaccess2_fat", 1},
{"runtime.mapassign", 1},
{"runtime.mapassign_fast32", 1},
{"runtime.mapassign_fast32ptr", 1},
{"runtime.mapassign_fast64", 1},
{"runtime.mapassign_fast64ptr", 1},
{"runtime.mapassign_faststr", 1},
{"runtime.mapiterinit", 1},
{"runtime.mapdelete", 1},
{"runtime.mapdelete_fast32", 1},
{"runtime.mapdelete_fast64", 1},
{"runtime.mapdelete_faststr", 1},
{"runtime.mapiternext", 1},
{"runtime.mapclear", 1},
{"runtime.makechan64", 1},
{"runtime.makechan", 1},
{"runtime.chanrecv1", 1},
{"runtime.chanrecv2", 1},
{"runtime.chansend1", 1},
{"runtime.closechan", 1},
{"runtime.writeBarrier", 0},
{"runtime.typedmemmove", 1},
{"runtime.typedmemclr", 1},
{"runtime.typedslicecopy", 1},
{"runtime.selectnbsend", 1},
{"runtime.selectnbrecv", 1},
{"runtime.selectnbrecv2", 1},
{"runtime.selectsetpc", 1},
{"runtime.selectgo", 1},
{"runtime.block", 1},
{"runtime.makeslice", 1},
{"runtime.makeslice64", 1},
{"runtime.growslice", 1},
{"runtime.memmove", 1},
{"runtime.memclrNoHeapPointers", 1},
{"runtime.memclrHasPointers", 1},
{"runtime.memequal", 1},
{"runtime.memequal0", 1},
{"runtime.memequal8", 1},
{"runtime.memequal16", 1},
{"runtime.memequal32", 1},
{"runtime.memequal64", 1},
{"runtime.memequal128", 1},
{"runtime.f32equal", 1},
{"runtime.f64equal", 1},
{"runtime.c64equal", 1},
{"runtime.c128equal", 1},
{"runtime.strequal", 1},
{"runtime.interequal", 1},
{"runtime.nilinterequal", 1},
{"runtime.memhash", 1},
{"runtime.memhash0", 1},
{"runtime.memhash8", 1},
{"runtime.memhash16", 1},
{"runtime.memhash32", 1},
{"runtime.memhash64", 1},
{"runtime.memhash128", 1},
{"runtime.f32hash", 1},
{"runtime.f64hash", 1},
{"runtime.c64hash", 1},
{"runtime.c128hash", 1},
{"runtime.strhash", 1},
{"runtime.interhash", 1},
{"runtime.nilinterhash", 1},
{"runtime.int64div", 1},
{"runtime.uint64div", 1},
{"runtime.int64mod", 1},
{"runtime.uint64mod", 1},
{"runtime.float64toint64", 1},
{"runtime.float64touint64", 1},
{"runtime.float64touint32", 1},
{"runtime.int64tofloat64", 1},
{"runtime.uint64tofloat64", 1},
{"runtime.uint32tofloat64", 1},
{"runtime.complex128div", 1},
{"runtime.racefuncenter", 1},
{"runtime.racefuncenterfp", 1},
{"runtime.racefuncexit", 1},
{"runtime.raceread", 1},
{"runtime.racewrite", 1},
{"runtime.racereadrange", 1},
{"runtime.racewriterange", 1},
{"runtime.msanread", 1},
{"runtime.msanwrite", 1},
{"runtime.checkptrAlignment", 1},
{"runtime.checkptrArithmetic", 1},
{"runtime.x86HasPOPCNT", 0},
{"runtime.x86HasSSE41", 0},
{"runtime.arm64HasATOMICS", 0},
{"runtime.gcWriteBarrier", 0},
{"runtime.deferproc", 1},
{"runtime.deferprocStack", 1},
{"runtime.deferreturn", 1},
{"runtime.newproc", 1},
{"runtime.morestack", 0},
{"runtime.morestackc", 0},
{"runtime.morestack_noctxt", 0},
}

View File

@ -0,0 +1,147 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package goobj2
import (
"bytes"
"encoding/binary"
)
// FuncInfo is serialized as a symbol (aux symbol). The symbol data is
// the binary encoding of the struct below.
//
// TODO: make each pcdata a separate symbol?
type FuncInfo struct {
NoSplit uint8
Args uint32
Locals uint32
Pcsp uint32
Pcfile uint32
Pcline uint32
Pcinline uint32
Pcdata []uint32
PcdataEnd uint32
Funcdataoff []uint32
File []SymRef // TODO: just use string?
InlTree []InlTreeNode
}
func (a *FuncInfo) Write(w *bytes.Buffer) {
w.WriteByte(a.NoSplit)
var b [4]byte
writeUint32 := func(x uint32) {
binary.LittleEndian.PutUint32(b[:], x)
w.Write(b[:])
}
writeUint32(a.Args)
writeUint32(a.Locals)
writeUint32(a.Pcsp)
writeUint32(a.Pcfile)
writeUint32(a.Pcline)
writeUint32(a.Pcinline)
writeUint32(uint32(len(a.Pcdata)))
for _, x := range a.Pcdata {
writeUint32(x)
}
writeUint32(a.PcdataEnd)
writeUint32(uint32(len(a.Funcdataoff)))
for _, x := range a.Funcdataoff {
writeUint32(x)
}
writeUint32(uint32(len(a.File)))
for _, f := range a.File {
writeUint32(f.PkgIdx)
writeUint32(f.SymIdx)
}
writeUint32(uint32(len(a.InlTree)))
for i := range a.InlTree {
a.InlTree[i].Write(w)
}
}
func (a *FuncInfo) Read(b []byte) {
a.NoSplit = b[0]
b = b[1:]
readUint32 := func() uint32 {
x := binary.LittleEndian.Uint32(b)
b = b[4:]
return x
}
a.Args = readUint32()
a.Locals = readUint32()
a.Pcsp = readUint32()
a.Pcfile = readUint32()
a.Pcline = readUint32()
a.Pcinline = readUint32()
pcdatalen := readUint32()
a.Pcdata = make([]uint32, pcdatalen)
for i := range a.Pcdata {
a.Pcdata[i] = readUint32()
}
a.PcdataEnd = readUint32()
funcdataofflen := readUint32()
a.Funcdataoff = make([]uint32, funcdataofflen)
for i := range a.Funcdataoff {
a.Funcdataoff[i] = readUint32()
}
filelen := readUint32()
a.File = make([]SymRef, filelen)
for i := range a.File {
a.File[i] = SymRef{readUint32(), readUint32()}
}
inltreelen := readUint32()
a.InlTree = make([]InlTreeNode, inltreelen)
for i := range a.InlTree {
b = a.InlTree[i].Read(b)
}
}
// InlTreeNode is the serialized form of FileInfo.InlTree.
type InlTreeNode struct {
Parent int32
File SymRef
Line int32
Func SymRef
ParentPC int32
}
func (inl *InlTreeNode) Write(w *bytes.Buffer) {
var b [4]byte
writeUint32 := func(x uint32) {
binary.LittleEndian.PutUint32(b[:], x)
w.Write(b[:])
}
writeUint32(uint32(inl.Parent))
writeUint32(inl.File.PkgIdx)
writeUint32(inl.File.SymIdx)
writeUint32(uint32(inl.Line))
writeUint32(inl.Func.PkgIdx)
writeUint32(inl.Func.SymIdx)
writeUint32(uint32(inl.ParentPC))
}
// Read an InlTreeNode from b, return the remaining bytes.
func (inl *InlTreeNode) Read(b []byte) []byte {
readUint32 := func() uint32 {
x := binary.LittleEndian.Uint32(b)
b = b[4:]
return x
}
inl.Parent = int32(readUint32())
inl.File = SymRef{readUint32(), readUint32()}
inl.Line = int32(readUint32())
inl.Func = SymRef{readUint32(), readUint32()}
inl.ParentPC = int32(readUint32())
return b
}

View File

@ -0,0 +1,124 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
// Generate builtinlist.go from cmd/compile/internal/gc/builtin/runtime.go.
package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
)
var stdout = flag.Bool("stdout", false, "write to stdout instead of builtinlist.go")
func main() {
flag.Parse()
var b bytes.Buffer
fmt.Fprintln(&b, "// Code generated by mkbuiltin.go. DO NOT EDIT.")
fmt.Fprintln(&b)
fmt.Fprintln(&b, "package goobj2")
mkbuiltin(&b)
out, err := format.Source(b.Bytes())
if err != nil {
log.Fatal(err)
}
if *stdout {
_, err = os.Stdout.Write(out)
} else {
err = ioutil.WriteFile("builtinlist.go", out, 0666)
}
if err != nil {
log.Fatal(err)
}
}
func mkbuiltin(w io.Writer) {
pkg := "runtime"
fset := token.NewFileSet()
path := filepath.Join("..", "..", "compile", "internal", "gc", "builtin", "runtime.go")
f, err := parser.ParseFile(fset, path, nil, 0)
if err != nil {
log.Fatal(err)
}
decls := make(map[string]bool)
fmt.Fprintf(w, "var builtins = [...]struct{ name string; abi int }{\n")
for _, decl := range f.Decls {
switch decl := decl.(type) {
case *ast.FuncDecl:
if decl.Recv != nil {
log.Fatal("methods unsupported")
}
if decl.Body != nil {
log.Fatal("unexpected function body")
}
declName := pkg + "." + decl.Name.Name
decls[declName] = true
fmt.Fprintf(w, "{%q, 1},\n", declName) // functions are ABIInternal (1)
case *ast.GenDecl:
if decl.Tok == token.IMPORT {
continue
}
if decl.Tok != token.VAR {
log.Fatal("unhandled declaration kind", decl.Tok)
}
for _, spec := range decl.Specs {
spec := spec.(*ast.ValueSpec)
if len(spec.Values) != 0 {
log.Fatal("unexpected values")
}
for _, name := range spec.Names {
declName := pkg + "." + name.Name
decls[declName] = true
fmt.Fprintf(w, "{%q, 0},\n", declName) // variables are ABI0
}
}
default:
log.Fatal("unhandled decl type", decl)
}
}
// The list above only contains ones that are used by the frontend.
// The backend may create more references of builtin functions.
// Add them.
for _, b := range extra {
name := pkg + "." + b.name
if decls[name] {
log.Fatalf("%q already added -- mkbuiltin.go out of sync?", name)
}
fmt.Fprintf(w, "{%q, %d},\n", name, b.abi)
}
fmt.Fprintln(w, "}")
}
var extra = [...]struct {
name string
abi int
}{
{"gcWriteBarrier", 0}, // asm function, ABI0
{"deferproc", 1},
{"deferprocStack", 1},
{"deferreturn", 1},
{"newproc", 1},
{"morestack", 0}, // asm function, ABI0
{"morestackc", 0}, // asm function, ABI0
{"morestack_noctxt", 0}, // asm function, ABI0
}

View File

@ -0,0 +1,587 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Go new object file format, reading and writing.
package goobj2 // TODO: replace the goobj package?
import (
"bytes"
"cmd/internal/bio"
"encoding/binary"
"errors"
"fmt"
"io"
"unsafe"
)
// New object file format.
//
// Header struct {
// Magic [...]byte // "\x00go114LD"
// Flags uint32
// // TODO: Fingerprint
// Offsets [...]uint32 // byte offset of each block below
// }
//
// Strings [...]struct {
// Len uint32
// Data [...]byte
// }
//
// Autolib [...]stringOff // imported packages (for file loading) // TODO: add fingerprints
// PkgIndex [...]stringOff // referenced packages by index
//
// DwarfFiles [...]stringOff
//
// SymbolDefs [...]struct {
// Name stringOff
// ABI uint16
// Type uint8
// Flag uint8
// Size uint32
// }
// NonPkgDefs [...]struct { // non-pkg symbol definitions
// ... // same as SymbolDefs
// }
// NonPkgRefs [...]struct { // non-pkg symbol references
// ... // same as SymbolDefs
// }
//
// RelocIndex [...]uint32 // index to Relocs
// AuxIndex [...]uint32 // index to Aux
// DataIndex [...]uint32 // offset to Data
//
// Relocs [...]struct {
// Off int32
// Size uint8
// Type uint8
// Add int64
// Sym symRef
// }
//
// Aux [...]struct {
// Type uint8
// Sym symRef
// }
//
// Data [...]byte
// Pcdata [...]byte
//
// stringOff is a uint32 (?) offset that points to the corresponding
// string, which is a uint32 length followed by that number of bytes.
//
// symRef is struct { PkgIdx, SymIdx uint32 }.
//
// Slice type (e.g. []symRef) is encoded as a length prefix (uint32)
// followed by that number of elements.
//
// The types below correspond to the encoded data structure in the
// object file.
// Symbol indexing.
//
// Each symbol is referenced with a pair of indices, { PkgIdx, SymIdx },
// as the symRef struct above.
//
// PkgIdx is either a predeclared index (see PkgIdxNone below) or
// an index of an imported package. For the latter case, PkgIdx is the
// index of the package in the PkgIndex array. 0 is an invalid index.
//
// SymIdx is the index of the symbol in the given package.
// - If PkgIdx is PkgIdxSelf, SymIdx is the index of the symbol in the
// SymbolDefs array.
// - If PkgIdx is PkgIdxNone, SymIdx is the index of the symbol in the
// NonPkgDefs array (could natually overflow to NonPkgRefs array).
// - Otherwise, SymIdx is the index of the symbol in some other package's
// SymbolDefs array.
//
// {0, 0} represents a nil symbol. Otherwise PkgIdx should not be 0.
//
// RelocIndex, AuxIndex, and DataIndex contains indices/offsets to
// Relocs/Aux/Data blocks, one element per symbol, first for all the
// defined symbols, then all the defined non-package symbols, in the
// same order of SymbolDefs/NonPkgDefs arrays. For N total defined
// symbols, the array is of length N+1. The last element is the total
// number of relocations (aux symbols, data blocks, etc.).
//
// They can be accessed by index. For the i-th symbol, its relocations
// are the RelocIndex[i]-th (inclusive) to RelocIndex[i+1]-th (exclusive)
// elements in the Relocs array. Aux/Data are likewise. (The index is
// 0-based.)
// Auxiliary symbols.
//
// Each symbol may (or may not) be associated with a number of auxiliary
// symbols. They are described in the Aux block. See Aux struct below.
// Currently a symbol's Gotype and FuncInfo are auxiliary symbols. We
// may make use of aux symbols in more cases, e.g. DWARF symbols.
// Package Index.
const (
PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols
PkgIdxBuiltin // Predefined symbols // TODO: not used for now, we could use it for compiler-generated symbols like runtime.newobject
PkgIdxSelf // Symbols defined in the current package
PkgIdxInvalid = 0
// The index of other referenced packages starts from 1.
)
// Blocks
const (
BlkAutolib = iota
BlkPkgIdx
BlkDwarfFile
BlkSymdef
BlkNonpkgdef
BlkNonpkgref
BlkRelocIdx
BlkAuxIdx
BlkDataIdx
BlkReloc
BlkAux
BlkData
BlkPcdata
NBlk
)
// File header.
// TODO: probably no need to export this.
type Header struct {
Magic string
Flags uint32
Offsets [NBlk]uint32
}
const Magic = "\x00go114LD"
func (h *Header) Write(w *Writer) {
w.RawString(h.Magic)
w.Uint32(h.Flags)
for _, x := range h.Offsets {
w.Uint32(x)
}
}
func (h *Header) Read(r *Reader) error {
b := r.BytesAt(0, len(Magic))
h.Magic = string(b)
if h.Magic != Magic {
return errors.New("wrong magic, not a Go object file")
}
off := uint32(len(h.Magic))
h.Flags = r.uint32At(off)
off += 4
for i := range h.Offsets {
h.Offsets[i] = r.uint32At(off)
off += 4
}
return nil
}
func (h *Header) Size() int {
return len(h.Magic) + 4 + 4*len(h.Offsets)
}
// Symbol definition.
type Sym struct {
Name string
ABI uint16
Type uint8
Flag uint8
Siz uint32
}
const SymABIstatic = ^uint16(0)
const (
ObjFlagShared = 1 << iota
)
const (
SymFlagDupok = 1 << iota
SymFlagLocal
SymFlagTypelink
SymFlagLeaf
SymFlagCFunc
SymFlagReflectMethod
SymFlagGoType
SymFlagTopFrame
)
func (s *Sym) Write(w *Writer) {
w.StringRef(s.Name)
w.Uint16(s.ABI)
w.Uint8(s.Type)
w.Uint8(s.Flag)
w.Uint32(s.Siz)
}
func (s *Sym) Read(r *Reader, off uint32) {
s.Name = r.StringRef(off)
s.ABI = r.uint16At(off + 4)
s.Type = r.uint8At(off + 6)
s.Flag = r.uint8At(off + 7)
s.Siz = r.uint32At(off + 8)
}
func (s *Sym) Size() int {
return 4 + 2 + 1 + 1 + 4
}
func (s *Sym) Dupok() bool { return s.Flag&SymFlagDupok != 0 }
func (s *Sym) Local() bool { return s.Flag&SymFlagLocal != 0 }
func (s *Sym) Typelink() bool { return s.Flag&SymFlagTypelink != 0 }
func (s *Sym) Leaf() bool { return s.Flag&SymFlagLeaf != 0 }
func (s *Sym) CFunc() bool { return s.Flag&SymFlagCFunc != 0 }
func (s *Sym) ReflectMethod() bool { return s.Flag&SymFlagReflectMethod != 0 }
func (s *Sym) IsGoType() bool { return s.Flag&SymFlagGoType != 0 }
func (s *Sym) TopFrame() bool { return s.Flag&SymFlagTopFrame != 0 }
// Symbol reference.
type SymRef struct {
PkgIdx uint32
SymIdx uint32
}
func (s *SymRef) Write(w *Writer) {
w.Uint32(s.PkgIdx)
w.Uint32(s.SymIdx)
}
func (s *SymRef) Read(r *Reader, off uint32) {
s.PkgIdx = r.uint32At(off)
s.SymIdx = r.uint32At(off + 4)
}
func (s *SymRef) Size() int {
return 4 + 4
}
// Relocation.
type Reloc struct {
Off int32
Siz uint8
Type uint8
Add int64
Sym SymRef
}
func (r *Reloc) Write(w *Writer) {
w.Uint32(uint32(r.Off))
w.Uint8(r.Siz)
w.Uint8(r.Type)
w.Uint64(uint64(r.Add))
r.Sym.Write(w)
}
func (o *Reloc) Read(r *Reader, off uint32) {
o.Off = r.int32At(off)
o.Siz = r.uint8At(off + 4)
o.Type = r.uint8At(off + 5)
o.Add = r.int64At(off + 6)
o.Sym.Read(r, off+14)
}
func (r *Reloc) Size() int {
return 4 + 1 + 1 + 8 + r.Sym.Size()
}
// Aux symbol info.
type Aux struct {
Type uint8
Sym SymRef
}
// Aux Type
const (
AuxGotype = iota
AuxFuncInfo
AuxFuncdata
AuxDwarfInfo
AuxDwarfLoc
AuxDwarfRanges
AuxDwarfLines
// TODO: more. Pcdata?
)
func (a *Aux) Write(w *Writer) {
w.Uint8(a.Type)
a.Sym.Write(w)
}
func (a *Aux) Read(r *Reader, off uint32) {
a.Type = r.uint8At(off)
a.Sym.Read(r, off+1)
}
func (a *Aux) Size() int {
return 1 + a.Sym.Size()
}
type Writer struct {
wr *bio.Writer
stringMap map[string]uint32
off uint32 // running offset
}
func NewWriter(wr *bio.Writer) *Writer {
return &Writer{wr: wr, stringMap: make(map[string]uint32)}
}
func (w *Writer) AddString(s string) {
if _, ok := w.stringMap[s]; ok {
return
}
w.stringMap[s] = w.off
w.Uint32(uint32(len(s)))
w.RawString(s)
}
func (w *Writer) StringRef(s string) {
off, ok := w.stringMap[s]
if !ok {
panic(fmt.Sprintf("writeStringRef: string not added: %q", s))
}
w.Uint32(off)
}
func (w *Writer) RawString(s string) {
w.wr.WriteString(s)
w.off += uint32(len(s))
}
func (w *Writer) Bytes(s []byte) {
w.wr.Write(s)
w.off += uint32(len(s))
}
func (w *Writer) Uint64(x uint64) {
var b [8]byte
binary.LittleEndian.PutUint64(b[:], x)
w.wr.Write(b[:])
w.off += 8
}
func (w *Writer) Uint32(x uint32) {
var b [4]byte
binary.LittleEndian.PutUint32(b[:], x)
w.wr.Write(b[:])
w.off += 4
}
func (w *Writer) Uint16(x uint16) {
var b [2]byte
binary.LittleEndian.PutUint16(b[:], x)
w.wr.Write(b[:])
w.off += 2
}
func (w *Writer) Uint8(x uint8) {
w.wr.WriteByte(x)
w.off++
}
func (w *Writer) Offset() uint32 {
return w.off
}
type Reader struct {
b []byte // mmapped bytes, if not nil
readonly bool // whether b is backed with read-only memory
rd io.ReaderAt
start uint32
h Header // keep block offsets
}
func NewReaderFromBytes(b []byte, readonly bool) *Reader {
r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), start: 0}
err := r.h.Read(r)
if err != nil {
return nil
}
return r
}
func (r *Reader) BytesAt(off uint32, len int) []byte {
if len == 0 {
return nil
}
end := int(off) + len
return r.b[int(off):end:end]
}
func (r *Reader) uint64At(off uint32) uint64 {
b := r.BytesAt(off, 8)
return binary.LittleEndian.Uint64(b)
}
func (r *Reader) int64At(off uint32) int64 {
return int64(r.uint64At(off))
}
func (r *Reader) uint32At(off uint32) uint32 {
b := r.BytesAt(off, 4)
return binary.LittleEndian.Uint32(b)
}
func (r *Reader) int32At(off uint32) int32 {
return int32(r.uint32At(off))
}
func (r *Reader) uint16At(off uint32) uint16 {
b := r.BytesAt(off, 2)
return binary.LittleEndian.Uint16(b)
}
func (r *Reader) uint8At(off uint32) uint8 {
b := r.BytesAt(off, 1)
return b[0]
}
func (r *Reader) StringAt(off uint32) string {
l := r.uint32At(off)
b := r.b[off+4 : off+4+l]
if r.readonly {
return toString(b) // backed by RO memory, ok to make unsafe string
}
return string(b)
}
func toString(b []byte) string {
type stringHeader struct {
str unsafe.Pointer
len int
}
if len(b) == 0 {
return ""
}
ss := stringHeader{str: unsafe.Pointer(&b[0]), len: len(b)}
s := *(*string)(unsafe.Pointer(&ss))
return s
}
func (r *Reader) StringRef(off uint32) string {
return r.StringAt(r.uint32At(off))
}
func (r *Reader) Autolib() []string {
n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / 4
s := make([]string, n)
for i := range s {
off := r.h.Offsets[BlkAutolib] + uint32(i)*4
s[i] = r.StringRef(off)
}
return s
}
func (r *Reader) Pkglist() []string {
n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / 4
s := make([]string, n)
for i := range s {
off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4
s[i] = r.StringRef(off)
}
return s
}
func (r *Reader) NPkg() int {
return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / 4
}
func (r *Reader) Pkg(i int) string {
off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4
return r.StringRef(off)
}
func (r *Reader) NDwarfFile() int {
return int(r.h.Offsets[BlkDwarfFile+1]-r.h.Offsets[BlkDwarfFile]) / 4
}
func (r *Reader) DwarfFile(i int) string {
off := r.h.Offsets[BlkDwarfFile] + uint32(i)*4
return r.StringRef(off)
}
func (r *Reader) NSym() int {
symsiz := (&Sym{}).Size()
return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / symsiz
}
func (r *Reader) NNonpkgdef() int {
symsiz := (&Sym{}).Size()
return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / symsiz
}
func (r *Reader) NNonpkgref() int {
symsiz := (&Sym{}).Size()
return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / symsiz
}
// SymOff returns the offset of the i-th symbol.
func (r *Reader) SymOff(i int) uint32 {
symsiz := (&Sym{}).Size()
return r.h.Offsets[BlkSymdef] + uint32(i*symsiz)
}
// NReloc returns the number of relocations of the i-th symbol.
func (r *Reader) NReloc(i int) int {
relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
return int(r.uint32At(relocIdxOff+4) - r.uint32At(relocIdxOff))
}
// RelocOff returns the offset of the j-th relocation of the i-th symbol.
func (r *Reader) RelocOff(i int, j int) uint32 {
relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4)
relocIdx := r.uint32At(relocIdxOff)
relocsiz := (&Reloc{}).Size()
return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(relocsiz)
}
// NAux returns the number of aux symbols of the i-th symbol.
func (r *Reader) NAux(i int) int {
auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4)
return int(r.uint32At(auxIdxOff+4) - r.uint32At(auxIdxOff))
}
// AuxOff returns the offset of the j-th aux symbol of the i-th symbol.
func (r *Reader) AuxOff(i int, j int) uint32 {
auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4)
auxIdx := r.uint32At(auxIdxOff)
auxsiz := (&Aux{}).Size()
return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(auxsiz)
}
// DataOff returns the offset of the i-th symbol's data.
func (r *Reader) DataOff(i int) uint32 {
dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4)
return r.h.Offsets[BlkData] + r.uint32At(dataIdxOff)
}
// DataSize returns the size of the i-th symbol's data.
func (r *Reader) DataSize(i int) int {
return int(r.DataOff(i+1) - r.DataOff(i))
}
// Data returns the i-th symbol's data.
func (r *Reader) Data(i int) []byte {
return r.BytesAt(r.DataOff(i), r.DataSize(i))
}
// AuxDataBase returns the base offset of the aux data block.
func (r *Reader) PcdataBase() uint32 {
return r.h.Offsets[BlkPcdata]
}
// ReadOnly returns whether r.BytesAt returns read-only bytes.
func (r *Reader) ReadOnly() bool {
return r.readonly
}
// Flags returns the flag bits read from the object file header.
func (r *Reader) Flags() uint32 {
return r.h.Flags
}

View File

@ -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{}) {

View File

@ -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)

View File

@ -0,0 +1,429 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Writing Go object files.
package obj
import (
"bytes"
"cmd/internal/bio"
"cmd/internal/goobj2"
"cmd/internal/objabi"
"fmt"
"path/filepath"
"strings"
)
// Entry point of writing new object file.
func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) {
if ctxt.Debugasm > 0 {
ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
}
genFuncInfoSyms(ctxt)
w := writer{
Writer: goobj2.NewWriter(b),
ctxt: ctxt,
pkgpath: objabi.PathToPrefix(pkgpath),
}
start := b.Offset()
w.init()
// Header
// We just reserve the space. We'll fill in the offsets later.
flags := uint32(0)
if ctxt.Flag_shared {
flags |= goobj2.ObjFlagShared
}
h := goobj2.Header{Magic: goobj2.Magic, Flags: flags}
h.Write(w.Writer)
// String table
w.StringTable()
// Autolib
h.Offsets[goobj2.BlkAutolib] = w.Offset()
for _, pkg := range ctxt.Imports {
w.StringRef(pkg)
}
// Package references
h.Offsets[goobj2.BlkPkgIdx] = w.Offset()
for _, pkg := range w.pkglist {
w.StringRef(pkg)
}
// DWARF file table
h.Offsets[goobj2.BlkDwarfFile] = w.Offset()
for _, f := range ctxt.PosTable.DebugLinesFileTable() {
w.StringRef(f)
}
// Symbol definitions
h.Offsets[goobj2.BlkSymdef] = w.Offset()
for _, s := range ctxt.defs {
w.Sym(s)
}
// Non-pkg symbol definitions
h.Offsets[goobj2.BlkNonpkgdef] = w.Offset()
for _, s := range ctxt.nonpkgdefs {
w.Sym(s)
}
// Non-pkg symbol references
h.Offsets[goobj2.BlkNonpkgref] = w.Offset()
for _, s := range ctxt.nonpkgrefs {
w.Sym(s)
}
// Reloc indexes
h.Offsets[goobj2.BlkRelocIdx] = w.Offset()
nreloc := uint32(0)
lists := [][]*LSym{ctxt.defs, ctxt.nonpkgdefs}
for _, list := range lists {
for _, s := range list {
w.Uint32(nreloc)
nreloc += uint32(len(s.R))
}
}
w.Uint32(nreloc)
// Symbol Info indexes
h.Offsets[goobj2.BlkAuxIdx] = w.Offset()
naux := uint32(0)
for _, list := range lists {
for _, s := range list {
w.Uint32(naux)
naux += uint32(nAuxSym(s))
}
}
w.Uint32(naux)
// Data indexes
h.Offsets[goobj2.BlkDataIdx] = w.Offset()
dataOff := uint32(0)
for _, list := range lists {
for _, s := range list {
w.Uint32(dataOff)
dataOff += uint32(len(s.P))
}
}
w.Uint32(dataOff)
// Relocs
h.Offsets[goobj2.BlkReloc] = w.Offset()
for _, list := range lists {
for _, s := range list {
for i := range s.R {
w.Reloc(&s.R[i])
}
}
}
// Aux symbol info
h.Offsets[goobj2.BlkAux] = w.Offset()
for _, list := range lists {
for _, s := range list {
w.Aux(s)
}
}
// Data
h.Offsets[goobj2.BlkData] = w.Offset()
for _, list := range lists {
for _, s := range list {
w.Bytes(s.P)
}
}
// Pcdata
h.Offsets[goobj2.BlkPcdata] = w.Offset()
for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms
if s.Func != nil {
pc := &s.Func.Pcln
w.Bytes(pc.Pcsp.P)
w.Bytes(pc.Pcfile.P)
w.Bytes(pc.Pcline.P)
w.Bytes(pc.Pcinline.P)
for i := range pc.Pcdata {
w.Bytes(pc.Pcdata[i].P)
}
}
}
// Fix up block offsets in the header
end := start + int64(w.Offset())
b.MustSeek(start, 0)
h.Write(w.Writer)
b.MustSeek(end, 0)
}
type writer struct {
*goobj2.Writer
ctxt *Link
pkgpath string // the package import path (escaped), "" if unknown
pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
}
// prepare package index list
func (w *writer) init() {
w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1)
w.pkglist[0] = "" // dummy invalid package for index 0
for pkg, i := range w.ctxt.pkgIdx {
w.pkglist[i] = pkg
}
}
func (w *writer) StringTable() {
w.AddString("")
for _, pkg := range w.ctxt.Imports {
w.AddString(pkg)
}
for _, pkg := range w.pkglist {
w.AddString(pkg)
}
w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
if w.pkgpath != "" {
s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
}
w.AddString(s.Name)
})
w.ctxt.traverseSyms(traverseDefs, func(s *LSym) {
if s.Type != objabi.STEXT {
return
}
pc := &s.Func.Pcln
for _, f := range pc.File {
w.AddString(filepath.ToSlash(f))
}
for _, call := range pc.InlTree.nodes {
f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
w.AddString(filepath.ToSlash(f))
}
})
for _, f := range w.ctxt.PosTable.DebugLinesFileTable() {
w.AddString(f)
}
}
func (w *writer) Sym(s *LSym) {
abi := uint16(s.ABI())
if s.Static() {
abi = goobj2.SymABIstatic
}
flag := uint8(0)
if s.DuplicateOK() {
flag |= goobj2.SymFlagDupok
}
if s.Local() {
flag |= goobj2.SymFlagLocal
}
if s.MakeTypelink() {
flag |= goobj2.SymFlagTypelink
}
if s.Leaf() {
flag |= goobj2.SymFlagLeaf
}
if s.CFunc() {
flag |= goobj2.SymFlagCFunc
}
if s.ReflectMethod() {
flag |= goobj2.SymFlagReflectMethod
}
if s.TopFrame() {
flag |= goobj2.SymFlagTopFrame
}
if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
flag |= goobj2.SymFlagGoType
}
name := s.Name
if strings.HasPrefix(name, "gofile..") {
name = filepath.ToSlash(name)
}
o := goobj2.Sym{
Name: name,
ABI: abi,
Type: uint8(s.Type),
Flag: flag,
Siz: uint32(s.Size),
}
o.Write(w.Writer)
}
func makeSymRef(s *LSym) goobj2.SymRef {
if s == nil {
return goobj2.SymRef{}
}
if s.PkgIdx == 0 || !s.Indexed() {
fmt.Printf("unindexed symbol reference: %v\n", s)
panic("unindexed symbol reference")
}
return goobj2.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
}
func (w *writer) Reloc(r *Reloc) {
o := goobj2.Reloc{
Off: r.Off,
Siz: r.Siz,
Type: uint8(r.Type),
Add: r.Add,
Sym: makeSymRef(r.Sym),
}
o.Write(w.Writer)
}
func (w *writer) Aux(s *LSym) {
if s.Gotype != nil {
o := goobj2.Aux{
Type: goobj2.AuxGotype,
Sym: makeSymRef(s.Gotype),
}
o.Write(w.Writer)
}
if s.Func != nil {
o := goobj2.Aux{
Type: goobj2.AuxFuncInfo,
Sym: makeSymRef(s.Func.FuncInfoSym),
}
o.Write(w.Writer)
for _, d := range s.Func.Pcln.Funcdata {
o := goobj2.Aux{
Type: goobj2.AuxFuncdata,
Sym: makeSymRef(d),
}
o.Write(w.Writer)
}
if s.Func.dwarfInfoSym != nil {
o := goobj2.Aux{
Type: goobj2.AuxDwarfInfo,
Sym: makeSymRef(s.Func.dwarfInfoSym),
}
o.Write(w.Writer)
}
if s.Func.dwarfLocSym != nil {
o := goobj2.Aux{
Type: goobj2.AuxDwarfLoc,
Sym: makeSymRef(s.Func.dwarfLocSym),
}
o.Write(w.Writer)
}
if s.Func.dwarfRangesSym != nil {
o := goobj2.Aux{
Type: goobj2.AuxDwarfRanges,
Sym: makeSymRef(s.Func.dwarfRangesSym),
}
o.Write(w.Writer)
}
if s.Func.dwarfDebugLinesSym != nil {
o := goobj2.Aux{
Type: goobj2.AuxDwarfLines,
Sym: makeSymRef(s.Func.dwarfDebugLinesSym),
}
o.Write(w.Writer)
}
}
}
// return the number of aux symbols s have.
func nAuxSym(s *LSym) int {
n := 0
if s.Gotype != nil {
n++
}
if s.Func != nil {
// FuncInfo is an aux symbol, each Funcdata is an aux symbol
n += 1 + len(s.Func.Pcln.Funcdata)
if s.Func.dwarfInfoSym != nil {
n++
}
if s.Func.dwarfLocSym != nil {
n++
}
if s.Func.dwarfRangesSym != nil {
n++
}
if s.Func.dwarfDebugLinesSym != nil {
n++
}
}
return n
}
// generate symbols for FuncInfo.
func genFuncInfoSyms(ctxt *Link) {
infosyms := make([]*LSym, 0, len(ctxt.Text))
var pcdataoff uint32
var b bytes.Buffer
symidx := int32(len(ctxt.defs))
for _, s := range ctxt.Text {
if s.Func == nil {
continue
}
nosplit := uint8(0)
if s.NoSplit() {
nosplit = 1
}
o := goobj2.FuncInfo{
NoSplit: nosplit,
Args: uint32(s.Func.Args),
Locals: uint32(s.Func.Locals),
}
pc := &s.Func.Pcln
o.Pcsp = pcdataoff
pcdataoff += uint32(len(pc.Pcsp.P))
o.Pcfile = pcdataoff
pcdataoff += uint32(len(pc.Pcfile.P))
o.Pcline = pcdataoff
pcdataoff += uint32(len(pc.Pcline.P))
o.Pcinline = pcdataoff
pcdataoff += uint32(len(pc.Pcinline.P))
o.Pcdata = make([]uint32, len(pc.Pcdata))
for i, pcd := range pc.Pcdata {
o.Pcdata[i] = pcdataoff
pcdataoff += uint32(len(pcd.P))
}
o.PcdataEnd = pcdataoff
o.Funcdataoff = make([]uint32, len(pc.Funcdataoff))
for i, x := range pc.Funcdataoff {
o.Funcdataoff[i] = uint32(x)
}
o.File = make([]goobj2.SymRef, len(pc.File))
for i, f := range pc.File {
fsym := ctxt.Lookup(f)
o.File[i] = makeSymRef(fsym)
}
o.InlTree = make([]goobj2.InlTreeNode, len(pc.InlTree.nodes))
for i, inl := range pc.InlTree.nodes {
f, l := linkgetlineFromPos(ctxt, inl.Pos)
fsym := ctxt.Lookup(f)
o.InlTree[i] = goobj2.InlTreeNode{
Parent: int32(inl.Parent),
File: makeSymRef(fsym),
Line: l,
Func: makeSymRef(inl.Func),
ParentPC: inl.ParentPC,
}
}
o.Write(&b)
isym := &LSym{
Type: objabi.SDATA, // for now, I don't think it matters
PkgIdx: goobj2.PkgIdxSelf,
SymIdx: symidx,
P: append([]byte(nil), b.Bytes()...),
}
isym.Set(AttrIndexed, true)
symidx++
infosyms = append(infosyms, isym)
s.Func.FuncInfoSym = isym
b.Reset()
}
ctxt.defs = append(ctxt.defs, infosyms...)
}

View File

@ -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},
}

View File

@ -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)
}
}
}
}
}
}
}

View File

@ -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++ {

View File

@ -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)

View File

@ -0,0 +1,441 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ld
import (
"bytes"
"cmd/internal/dwarf"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"container/heap"
"fmt"
"unicode"
)
var _ = fmt.Print
type workQueue []loader.Sym
// Implement container/heap.Interface.
func (q *workQueue) Len() int { return len(*q) }
func (q *workQueue) Less(i, j int) bool { return (*q)[i] < (*q)[j] }
func (q *workQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
func (q *workQueue) Push(i interface{}) { *q = append(*q, i.(loader.Sym)) }
func (q *workQueue) Pop() interface{} { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
// Functions for deadcode pass to use.
// Deadcode pass should call push/pop, not Push/Pop.
func (q *workQueue) push(i loader.Sym) { heap.Push(q, i) }
func (q *workQueue) pop() loader.Sym { return heap.Pop(q).(loader.Sym) }
func (q *workQueue) empty() bool { return len(*q) == 0 }
type deadcodePass2 struct {
ctxt *Link
ldr *loader.Loader
wq workQueue
rtmp []loader.Reloc
ifaceMethod map[methodsig]bool // methods declared in reached interfaces
markableMethods []methodref2 // methods of reached types
reflectSeen bool // whether we have seen a reflect method call
}
func (d *deadcodePass2) init() {
d.ldr.InitReachable()
d.ifaceMethod = make(map[methodsig]bool)
if d.ctxt.Reachparent != nil {
d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
}
heap.Init(&d.wq)
if d.ctxt.BuildMode == BuildModeShared {
// Mark all symbols defined in this library as reachable when
// building a shared library.
n := d.ldr.NDef()
for i := 1; i < n; i++ {
s := loader.Sym(i)
if !d.ldr.IsDup(s) {
d.mark(s, 0)
}
}
return
}
var names []string
// In a normal binary, start at main.main and the init
// functions and mark what is reachable from there.
if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
names = append(names, "main.main", "main..inittask")
} else {
// The external linker refers main symbol directly.
if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
*flagEntrySymbol = "_main"
} else {
*flagEntrySymbol = "main"
}
}
names = append(names, *flagEntrySymbol)
if d.ctxt.BuildMode == BuildModePlugin {
names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs")
// We don't keep the go.plugin.exports symbol,
// but we do keep the symbols it refers to.
exportsIdx := d.ldr.Lookup("go.plugin.exports", 0)
if exportsIdx != 0 {
d.ReadRelocs(exportsIdx)
for i := 0; i < len(d.rtmp); i++ {
d.mark(d.rtmp[i].Sym, 0)
}
}
}
}
dynexpMap := d.ctxt.cgo_export_dynamic
if d.ctxt.LinkMode == LinkExternal {
dynexpMap = d.ctxt.cgo_export_static
}
for exp := range dynexpMap {
names = append(names, exp)
}
// DWARF constant DIE symbols are not referenced, but needed by
// the dwarf pass.
if !*FlagW {
for _, lib := range d.ctxt.Library {
names = append(names, dwarf.ConstInfoPrefix+lib.Pkg)
}
}
for _, name := range names {
// Mark symbol as an data/ABI0 symbol.
d.mark(d.ldr.Lookup(name, 0), 0)
// Also mark any Go functions (internal ABI).
d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal), 0)
}
}
func (d *deadcodePass2) flood() {
symRelocs := []loader.Reloc{}
auxSyms := []loader.Sym{}
for !d.wq.empty() {
symIdx := d.wq.pop()
d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
relocs := d.ldr.Relocs(symIdx)
symRelocs = relocs.ReadAll(symRelocs)
if d.ldr.IsGoType(symIdx) {
p := d.ldr.Data(symIdx)
if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
for _, sig := range d.decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs) {
if d.ctxt.Debugvlog > 1 {
d.ctxt.Logf("reached iface method: %s\n", sig)
}
d.ifaceMethod[sig] = true
}
}
}
var methods []methodref2
for i := 0; i < relocs.Count; i++ {
r := symRelocs[i]
if r.Type == objabi.R_WEAKADDROFF {
continue
}
if r.Type == objabi.R_METHODOFF {
if i+2 >= relocs.Count {
panic("expect three consecutive R_METHODOFF relocs")
}
methods = append(methods, methodref2{src: symIdx, r: i})
i += 2
continue
}
if r.Type == objabi.R_USETYPE {
// type symbol used for DWARF. we need to load the symbol but it may not
// be otherwise reachable in the program.
// do nothing for now as we still load all type symbols.
continue
}
d.mark(r.Sym, symIdx)
}
auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms)
for i := 0; i < len(auxSyms); i++ {
d.mark(auxSyms[i], symIdx)
}
// Some host object symbols have an outer object, which acts like a
// "carrier" symbol, or it holds all the symbols for a particular
// section. We need to mark all "referenced" symbols from that carrier,
// so we make sure we're pulling in all outer symbols, and their sub
// symbols. This is not ideal, and these carrier/section symbols could
// be removed.
d.mark(d.ldr.OuterSym(symIdx), symIdx)
d.mark(d.ldr.SubSym(symIdx), symIdx)
if len(methods) != 0 {
// Decode runtime type information for type methods
// to help work out which methods can be called
// dynamically via interfaces.
methodsigs := d.decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx, symRelocs)
if len(methods) != len(methodsigs) {
panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
}
for i, m := range methodsigs {
methods[i].m = m
}
d.markableMethods = append(d.markableMethods, methods...)
}
}
}
func (d *deadcodePass2) mark(symIdx, parent loader.Sym) {
if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) {
d.wq.push(symIdx)
d.ldr.Reachable.Set(symIdx)
if d.ctxt.Reachparent != nil {
d.ldr.Reachparent[symIdx] = parent
}
if *flagDumpDep {
to := d.ldr.SymName(symIdx)
if to != "" {
from := "_"
if parent != 0 {
from = d.ldr.SymName(parent)
}
fmt.Printf("%s -> %s\n", from, to)
}
}
}
}
func (d *deadcodePass2) markMethod(m methodref2) {
d.ReadRelocs(m.src)
d.mark(d.rtmp[m.r].Sym, m.src)
d.mark(d.rtmp[m.r+1].Sym, m.src)
d.mark(d.rtmp[m.r+2].Sym, m.src)
}
func deadcode2(ctxt *Link) {
ldr := ctxt.loader
d := deadcodePass2{ctxt: ctxt, ldr: ldr}
d.init()
d.flood()
callSym := ldr.Lookup("reflect.Value.Call", sym.SymVerABIInternal)
methSym := ldr.Lookup("reflect.Value.Method", sym.SymVerABIInternal)
if ctxt.DynlinkingGo() {
// Exported methods may satisfy interfaces we don't know
// about yet when dynamically linking.
d.reflectSeen = true
}
for {
// Methods might be called via reflection. Give up on
// static analysis, mark all exported methods of
// all reachable types as reachable.
d.reflectSeen = d.reflectSeen || (callSym != 0 && ldr.Reachable.Has(callSym)) || (methSym != 0 && ldr.Reachable.Has(methSym))
// Mark all methods that could satisfy a discovered
// interface as reachable. We recheck old marked interfaces
// as new types (with new methods) may have been discovered
// in the last pass.
rem := d.markableMethods[:0]
for _, m := range d.markableMethods {
if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
d.markMethod(m)
} else {
rem = append(rem, m)
}
}
d.markableMethods = rem
if d.wq.empty() {
// No new work was discovered. Done.
break
}
d.flood()
}
n := ldr.NSym()
if ctxt.BuildMode != BuildModeShared {
// Keep a itablink if the symbol it points at is being kept.
// (When BuildModeShared, always keep itablinks.)
for i := 1; i < n; i++ {
s := loader.Sym(i)
if ldr.IsItabLink(s) {
relocs := ldr.Relocs(s)
if relocs.Count > 0 && ldr.Reachable.Has(relocs.At(0).Sym) {
ldr.Reachable.Set(s)
}
}
}
}
}
// methodref2 holds the relocations from a receiver type symbol to its
// method. There are three relocations, one for each of the fields in
// the reflect.method struct: mtyp, ifn, and tfn.
type methodref2 struct {
m methodsig
src loader.Sym // receiver type symbol
r int // the index of R_METHODOFF relocations
}
func (m methodref2) isExported() bool {
for _, r := range m.m {
return unicode.IsUpper(r)
}
panic("methodref has no signature")
}
// decodeMethodSig2 decodes an array of method signature information.
// Each element of the array is size bytes. The first 4 bytes is a
// nameOff for the method name, and the next 4 bytes is a typeOff for
// the function type.
//
// Conveniently this is the layout of both runtime.method and runtime.imethod.
func (d *deadcodePass2) decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, off, size, count int) []methodsig {
var buf bytes.Buffer
var methods []methodsig
for i := 0; i < count; i++ {
buf.WriteString(decodetypeName2(ldr, symIdx, symRelocs, off))
mtypSym := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off+4))
// FIXME: add some sort of caching here, since we may see some of the
// same symbols over time for param types.
d.ReadRelocs(mtypSym)
mp := ldr.Data(mtypSym)
buf.WriteRune('(')
inCount := decodetypeFuncInCount(arch, mp)
for i := 0; i < inCount; i++ {
if i > 0 {
buf.WriteString(", ")
}
a := d.decodetypeFuncInType2(ldr, arch, mtypSym, d.rtmp, i)
buf.WriteString(ldr.SymName(a))
}
buf.WriteString(") (")
outCount := decodetypeFuncOutCount(arch, mp)
for i := 0; i < outCount; i++ {
if i > 0 {
buf.WriteString(", ")
}
a := d.decodetypeFuncOutType2(ldr, arch, mtypSym, d.rtmp, i)
buf.WriteString(ldr.SymName(a))
}
buf.WriteRune(')')
off += size
methods = append(methods, methodsig(buf.String()))
buf.Reset()
}
return methods
}
func (d *deadcodePass2) decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
p := ldr.Data(symIdx)
if decodetypeKind(arch, p)&kindMask != kindInterface {
panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
}
rel := decodeReloc2(ldr, symIdx, symRelocs, int32(commonsize(arch)+arch.PtrSize))
if rel.Sym == 0 {
return nil
}
if rel.Sym != symIdx {
panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx)))
}
off := int(rel.Add) // array of reflect.imethod values
numMethods := int(decodetypeIfaceMethodCount(arch, p))
sizeofIMethod := 4 + 4
return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofIMethod, numMethods)
}
func (d *deadcodePass2) decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc) []methodsig {
p := ldr.Data(symIdx)
if !decodetypeHasUncommon(arch, p) {
panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
}
off := commonsize(arch) // reflect.rtype
switch decodetypeKind(arch, p) & kindMask {
case kindStruct: // reflect.structType
off += 4 * arch.PtrSize
case kindPtr: // reflect.ptrType
off += arch.PtrSize
case kindFunc: // reflect.funcType
off += arch.PtrSize // 4 bytes, pointer aligned
case kindSlice: // reflect.sliceType
off += arch.PtrSize
case kindArray: // reflect.arrayType
off += 3 * arch.PtrSize
case kindChan: // reflect.chanType
off += 2 * arch.PtrSize
case kindMap: // reflect.mapType
off += 4*arch.PtrSize + 8
case kindInterface: // reflect.interfaceType
off += 3 * arch.PtrSize
default:
// just Sizeof(rtype)
}
mcount := int(decodeInuxi(arch, p[off+4:], 2))
moff := int(decodeInuxi(arch, p[off+4+2+2:], 4))
off += moff // offset to array of reflect.method values
const sizeofMethod = 4 * 4 // sizeof reflect.method in program
return d.decodeMethodSig2(ldr, arch, symIdx, symRelocs, off, sizeofMethod, mcount)
}
func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Reloc {
for j := 0; j < len(symRelocs); j++ {
rel := symRelocs[j]
if rel.Off == off {
return rel
}
}
return loader.Reloc{}
}
func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int32) loader.Sym {
return decodeReloc2(ldr, symIdx, symRelocs, off).Sym
}
// decodetypeName2 decodes the name from a reflect.name.
func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, symRelocs []loader.Reloc, off int) string {
r := decodeRelocSym2(ldr, symIdx, symRelocs, int32(off))
if r == 0 {
return ""
}
data := ldr.Data(r)
namelen := int(uint16(data[1])<<8 | uint16(data[2]))
return string(data[3 : 3+namelen])
}
func (d *deadcodePass2) decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
uadd := commonsize(arch) + 4
if arch.PtrSize == 8 {
uadd += 4
}
if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
uadd += uncommonSize()
}
return decodeRelocSym2(ldr, symIdx, symRelocs, int32(uadd+i*arch.PtrSize))
}
func (d *deadcodePass2) decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, symRelocs []loader.Reloc, i int) loader.Sym {
return d.decodetypeFuncInType2(ldr, arch, symIdx, symRelocs, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
}
// readRelocs reads the relocations for the specified symbol into the
// deadcode relocs work array. Use with care, since the work array
// is a singleton.
func (d *deadcodePass2) ReadRelocs(symIdx loader.Sym) {
relocs := d.ldr.Relocs(symIdx)
d.rtmp = relocs.ReadAll(d.rtmp)
}

View File

@ -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

View File

@ -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.

View File

@ -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++

View File

@ -38,6 +38,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loadelf"
"cmd/link/internal/loader"
"cmd/link/internal/loadmacho"
"cmd/link/internal/loadpe"
"cmd/link/internal/loadxcoff"
@ -57,6 +58,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
)
@ -378,30 +380,25 @@ func (ctxt *Link) findLibPath(libname string) string {
}
func (ctxt *Link) loadlib() {
switch ctxt.BuildMode {
case BuildModeCShared, BuildModePlugin:
s := ctxt.Syms.Lookup("runtime.islibrary", 0)
s.Attr |= sym.AttrDuplicateOK
s.AddUint8(1)
case BuildModeCArchive:
s := ctxt.Syms.Lookup("runtime.isarchive", 0)
s.Attr |= sym.AttrDuplicateOK
s.AddUint8(1)
if *flagNewobj {
var flags uint32
switch *FlagStrictDups {
case 0:
// nothing to do
case 1, 2:
flags = loader.FlagStrictDups
default:
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
}
ctxt.loader = loader.NewLoader(flags)
}
loadinternal(ctxt, "runtime")
if ctxt.Arch.Family == sys.ARM {
loadinternal(ctxt, "math")
}
if *flagRace {
loadinternal(ctxt, "runtime/race")
}
if *flagMsan {
loadinternal(ctxt, "runtime/msan")
}
ctxt.cgo_export_static = make(map[string]bool)
ctxt.cgo_export_dynamic = make(map[string]bool)
// ctxt.Library grows during the loop, so not a range loop.
for i := 0; i < len(ctxt.Library); i++ {
i := 0
for ; i < len(ctxt.Library); i++ {
lib := ctxt.Library[i]
if lib.Shlib == "" {
if ctxt.Debugvlog > 1 {
@ -411,34 +408,35 @@ func (ctxt *Link) loadlib() {
}
}
for _, lib := range ctxt.Library {
if lib.Shlib != "" {
if ctxt.Debugvlog > 1 {
ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref)
}
ldshlibsyms(ctxt, lib.Shlib)
// load internal packages, if not already
if ctxt.Arch.Family == sys.ARM {
loadinternal(ctxt, "math")
}
if *flagRace {
loadinternal(ctxt, "runtime/race")
}
if *flagMsan {
loadinternal(ctxt, "runtime/msan")
}
loadinternal(ctxt, "runtime")
for ; i < len(ctxt.Library); i++ {
lib := ctxt.Library[i]
if lib.Shlib == "" {
loadobjfile(ctxt, lib)
}
}
iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
if *flagNewobj {
iscgo = ctxt.loader.Lookup("x_cgo_init", 0) != 0
ctxt.canUsePlugins = ctxt.loader.Lookup("plugin.Open", sym.SymVerABIInternal) != 0
} else {
iscgo = ctxt.Syms.ROLookup("x_cgo_init", 0) != nil
ctxt.canUsePlugins = ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil
}
// We now have enough information to determine the link mode.
determineLinkMode(ctxt)
// Recalculate pe parameters now that we have ctxt.LinkMode set.
if ctxt.HeadType == objabi.Hwindows {
Peinit(ctxt)
}
if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
*FlagTextAddr = 0
}
if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
toc := ctxt.Syms.Lookup(".TOC.", 0)
toc.Type = sym.SDYNIMPORT
}
if ctxt.LinkMode == LinkExternal && !iscgo && ctxt.LibraryByPkg["runtime/cgo"] == nil && !(objabi.GOOS == "darwin" && (ctxt.Arch.Family == sys.AMD64 || ctxt.Arch.Family == sys.I386)) {
// This indicates a user requested -linkmode=external.
// The startup code uses an import of runtime/cgo to decide
@ -456,7 +454,29 @@ func (ctxt *Link) loadlib() {
}
}
if ctxt.LinkMode == LinkInternal {
for _, lib := range ctxt.Library {
if lib.Shlib != "" {
if ctxt.Debugvlog > 1 {
ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref)
}
ldshlibsyms(ctxt, lib.Shlib)
}
}
if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
if *flagNewobj {
// In newobj mode, we typically create sym.Symbols later therefore
// also set cgo attributes later. However, for internal cgo linking,
// the host object loaders still work with sym.Symbols (for now),
// and they need cgo attributes set to work properly. So process
// them now.
lookup := func(name string, ver int) *sym.Symbol { return ctxt.loader.LookupOrCreate(name, ver, ctxt.Syms) }
for _, d := range ctxt.cgodata {
setCgoAttr(ctxt, lookup, d.file, d.pkg, d.directives)
}
ctxt.cgodata = nil
}
// Drop all the cgo_import_static declarations.
// Turns out we won't be needing them.
for _, s := range ctxt.Syms.Allsym {
@ -474,6 +494,155 @@ func (ctxt *Link) loadlib() {
}
}
// Conditionally load host objects, or setup for external linking.
hostobjs(ctxt)
hostlinksetup(ctxt)
if *flagNewobj {
// Add references of externally defined symbols.
ctxt.loader.LoadRefs(ctxt.Arch, ctxt.Syms)
}
// Now that we know the link mode, set the dynexp list.
if !*flagNewobj { // set this later in newobj mode
setupdynexp(ctxt)
}
if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 {
// If we have any undefined symbols in external
// objects, try to read them from the libgcc file.
any := false
for _, s := range ctxt.Syms.Allsym {
for i := range s.R {
r := &s.R[i] // Copying sym.Reloc has measurable impact on performance
if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" {
any = true
break
}
}
}
if any {
if *flagLibGCC == "" {
*flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc")
}
if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" {
// On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a".
// In this case we fail to load libgcc.a and can encounter link
// errors - see if we can find libcompiler_rt.a instead.
*flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
}
if *flagLibGCC != "none" {
hostArchive(ctxt, *flagLibGCC)
}
if ctxt.HeadType == objabi.Hwindows {
if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
hostArchive(ctxt, p)
}
if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
hostArchive(ctxt, p)
}
// Link libmsvcrt.a to resolve '__acrt_iob_func' symbol
// (see https://golang.org/issue/23649 for details).
if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" {
hostArchive(ctxt, p)
}
// TODO: maybe do something similar to peimporteddlls to collect all lib names
// and try link them all to final exe just like libmingwex.a and libmingw32.a:
/*
for:
#cgo windows LDFLAGS: -lmsvcrt -lm
import:
libmsvcrt.a libm.a
*/
}
}
}
// We've loaded all the code now.
ctxt.Loaded = true
importcycles()
if *flagNewobj {
strictDupMsgCount = ctxt.loader.NStrictDupMsgs()
}
}
// Set up dynexp list.
func setupdynexp(ctxt *Link) {
dynexpMap := ctxt.cgo_export_dynamic
if ctxt.LinkMode == LinkExternal {
dynexpMap = ctxt.cgo_export_static
}
dynexp = make([]*sym.Symbol, 0, len(dynexpMap))
for exp := range dynexpMap {
s := ctxt.Syms.Lookup(exp, 0)
dynexp = append(dynexp, s)
}
sort.Sort(byName(dynexp))
// Resolve ABI aliases in the list of cgo-exported functions.
// This is necessary because we load the ABI0 symbol for all
// cgo exports.
for i, s := range dynexp {
if s.Type != sym.SABIALIAS {
continue
}
t := resolveABIAlias(s)
t.Attr |= s.Attr
t.SetExtname(s.Extname())
dynexp[i] = t
}
ctxt.cgo_export_static = nil
ctxt.cgo_export_dynamic = nil
}
// Set up flags and special symbols depending on the platform build mode.
func (ctxt *Link) linksetup() {
switch ctxt.BuildMode {
case BuildModeCShared, BuildModePlugin:
s := ctxt.Syms.Lookup("runtime.islibrary", 0)
s.Type = sym.SNOPTRDATA
s.Attr |= sym.AttrDuplicateOK
s.AddUint8(1)
case BuildModeCArchive:
s := ctxt.Syms.Lookup("runtime.isarchive", 0)
s.Type = sym.SNOPTRDATA
s.Attr |= sym.AttrDuplicateOK
s.AddUint8(1)
}
// Recalculate pe parameters now that we have ctxt.LinkMode set.
if ctxt.HeadType == objabi.Hwindows {
Peinit(ctxt)
}
if ctxt.HeadType == objabi.Hdarwin && ctxt.LinkMode == LinkExternal {
*FlagTextAddr = 0
}
// If there are no dynamic libraries needed, gcc disables dynamic linking.
// Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
// assumes that a dynamic binary always refers to at least one dynamic library.
// Rather than be a source of test cases for glibc, disable dynamic linking
// the same way that gcc would.
//
// Exception: on OS X, programs such as Shark only work with dynamic
// binaries, so leave it enabled on OS X (Mach-O) binaries.
// Also leave it enabled on Solaris which doesn't support
// statically linked binaries.
if ctxt.BuildMode == BuildModeExe {
if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris {
*FlagD = true
}
}
if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && objabi.GOOS != "aix" {
toc := ctxt.Syms.Lookup(".TOC.", 0)
toc.Type = sym.SDYNIMPORT
}
// The Android Q linker started to complain about underalignment of the our TLS
// section. We don't actually use the section on android, so dont't
// generate it.
@ -534,98 +703,6 @@ func (ctxt *Link) loadlib() {
moduledata.Attr |= sym.AttrReachable
ctxt.Moduledata = moduledata
// Now that we know the link mode, trim the dynexp list.
x := sym.AttrCgoExportDynamic
if ctxt.LinkMode == LinkExternal {
x = sym.AttrCgoExportStatic
}
w := 0
for i := range dynexp {
if dynexp[i].Attr&x != 0 {
dynexp[w] = dynexp[i]
w++
}
}
dynexp = dynexp[:w]
// In internal link mode, read the host object files.
if ctxt.LinkMode == LinkInternal {
hostobjs(ctxt)
// If we have any undefined symbols in external
// objects, try to read them from the libgcc file.
any := false
for _, s := range ctxt.Syms.Allsym {
for i := range s.R {
r := &s.R[i] // Copying sym.Reloc has measurable impact on performance
if r.Sym != nil && r.Sym.Type == sym.SXREF && r.Sym.Name != ".got" {
any = true
break
}
}
}
if any {
if *flagLibGCC == "" {
*flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc")
}
if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" {
// On OpenBSD `clang --print-libgcc-file-name` returns "libgcc.a".
// In this case we fail to load libgcc.a and can encounter link
// errors - see if we can find libcompiler_rt.a instead.
*flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt")
}
if *flagLibGCC != "none" {
hostArchive(ctxt, *flagLibGCC)
}
if ctxt.HeadType == objabi.Hwindows {
if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
hostArchive(ctxt, p)
}
if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
hostArchive(ctxt, p)
}
// Link libmsvcrt.a to resolve '__acrt_iob_func' symbol
// (see https://golang.org/issue/23649 for details).
if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" {
hostArchive(ctxt, p)
}
// TODO: maybe do something similar to peimporteddlls to collect all lib names
// and try link them all to final exe just like libmingwex.a and libmingw32.a:
/*
for:
#cgo windows LDFLAGS: -lmsvcrt -lm
import:
libmsvcrt.a libm.a
*/
}
}
} else {
hostlinksetup(ctxt)
}
// We've loaded all the code now.
ctxt.Loaded = true
// Record whether we can use plugins.
ctxt.canUsePlugins = (ctxt.Syms.ROLookup("plugin.Open", sym.SymVerABIInternal) != nil)
// If there are no dynamic libraries needed, gcc disables dynamic linking.
// Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
// assumes that a dynamic binary always refers to at least one dynamic library.
// Rather than be a source of test cases for glibc, disable dynamic linking
// the same way that gcc would.
//
// Exception: on OS X, programs such as Shark only work with dynamic
// binaries, so leave it enabled on OS X (Mach-O) binaries.
// Also leave it enabled on Solaris which doesn't support
// statically linked binaries.
if ctxt.BuildMode == BuildModeExe {
if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris {
*FlagD = true
}
}
// If package versioning is required, generate a hash of the
// packages used in the link.
if ctxt.BuildMode == BuildModeShared || ctxt.BuildMode == BuildModePlugin || ctxt.CanUsePlugins() {
@ -643,59 +720,6 @@ func (ctxt *Link) loadlib() {
got.Attr |= sym.AttrReachable
}
}
importcycles()
// put symbols into Textp
// do it in postorder so that packages are laid down in dependency order
// internal first, then everything else
ctxt.Library = postorder(ctxt.Library)
for _, doInternal := range [2]bool{true, false} {
for _, lib := range ctxt.Library {
if isRuntimeDepPkg(lib.Pkg) != doInternal {
continue
}
ctxt.Textp = append(ctxt.Textp, lib.Textp...)
for _, s := range lib.DupTextSyms {
if !s.Attr.OnList() {
ctxt.Textp = append(ctxt.Textp, s)
s.Attr |= sym.AttrOnList
// dupok symbols may be defined in multiple packages. its
// associated package is chosen sort of arbitrarily (the
// first containing package that the linker loads). canonicalize
// it here to the package with which it will be laid down
// in text.
s.File = objabi.PathToPrefix(lib.Pkg)
}
}
}
}
if len(ctxt.Shlibs) > 0 {
// We might have overwritten some functions above (this tends to happen for the
// autogenerated type equality/hashing functions) and we don't want to generated
// pcln table entries for these any more so remove them from Textp.
textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
for _, s := range ctxt.Textp {
if s.Type != sym.SDYNIMPORT {
textp = append(textp, s)
}
}
ctxt.Textp = textp
}
// Resolve ABI aliases in the list of cgo-exported functions.
// This is necessary because we load the ABI0 symbol for all
// cgo exports.
for i, s := range dynexp {
if s.Type != sym.SABIALIAS {
continue
}
t := resolveABIAlias(s)
t.Attr |= s.Attr
t.SetExtname(s.Extname())
dynexp[i] = t
}
}
// mangleTypeSym shortens the names of symbols that represent Go types
@ -991,6 +1015,9 @@ func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType obja
}
func hostobjs(ctxt *Link) {
if ctxt.LinkMode != LinkInternal {
return
}
var h *Hostobj
for i := 0; i < len(hostobj); i++ {
@ -1665,55 +1692,107 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
if magic == 0x7f454c46 { // \x7F E L F
ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, flags, err := loadelf.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
if err != nil {
Errorf(nil, "%v", err)
return
if *flagNewobj {
ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
if err != nil {
Errorf(nil, "%v", err)
return
}
ehdr.flags = flags
ctxt.Textp = append(ctxt.Textp, textp...)
}
ehdr.flags = flags
ctxt.Textp = append(ctxt.Textp, textp...)
return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
} else {
ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, flags, err := loadelf.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn, ehdr.flags)
if err != nil {
Errorf(nil, "%v", err)
return
}
ehdr.flags = flags
ctxt.Textp = append(ctxt.Textp, textp...)
}
return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
}
if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe {
ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadmacho.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
if *flagNewobj {
ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
ctxt.Textp = append(ctxt.Textp, textp...)
return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
} else {
ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadmacho.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file)
}
if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 {
ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, rsrc, err := loadpe.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
if *flagNewobj {
ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
if rsrc != nil {
setpersrc(ctxt, rsrc)
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
if rsrc != nil {
setpersrc(ctxt, rsrc)
return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
} else {
ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, rsrc, err := loadpe.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
if rsrc != nil {
setpersrc(ctxt, rsrc)
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
ctxt.Textp = append(ctxt.Textp, textp...)
return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
}
if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) {
ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadxcoff.Load(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
if *flagNewobj {
ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
ctxt.Textp = append(ctxt.Textp, textp...)
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
} else {
ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
textp, err := loadxcoff.LoadOld(ctxt.Arch, ctxt.Syms, f, pkg, length, pn)
if err != nil {
Errorf(nil, "%v", err)
return
}
ctxt.Textp = append(ctxt.Textp, textp...)
}
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
}
/* check the header */
@ -1809,7 +1888,12 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
default:
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
}
c := objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
var c int
if *flagNewobj {
ctxt.loader.Preload(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
} else {
c = objfile.Load(ctxt.Arch, ctxt.Syms, f, lib, unit, eof-f.Offset(), pn, flags)
}
strictDupMsgCount += c
addImports(ctxt, lib, pn)
return nil
@ -1964,7 +2048,17 @@ func ldshlibsyms(ctxt *Link, shlib string) {
ver = sym.SymVerABIInternal
}
lsym := ctxt.Syms.Lookup(elfsym.Name, ver)
var lsym *sym.Symbol
if *flagNewobj {
i := ctxt.loader.AddExtSym(elfsym.Name, ver)
if i == 0 {
continue
}
lsym = ctxt.Syms.Newsym(elfsym.Name, ver)
ctxt.loader.Syms[i] = lsym
} else {
lsym = ctxt.Syms.Lookup(elfsym.Name, ver)
}
// Because loadlib above loads all .a files before loading any shared
// libraries, any non-dynimport symbols we find that duplicate symbols
// already loaded should be ignored (the symbols from the .a files
@ -1993,7 +2087,17 @@ func ldshlibsyms(ctxt *Link, shlib string) {
// mangle Go function names in the .so to include the
// ABI.
if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
alias := ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal)
var alias *sym.Symbol
if *flagNewobj {
i := ctxt.loader.AddExtSym(elfsym.Name, sym.SymVerABIInternal)
if i == 0 {
continue
}
alias = ctxt.Syms.Newsym(elfsym.Name, sym.SymVerABIInternal)
ctxt.loader.Syms[i] = alias
} else {
alias = ctxt.Syms.Lookup(elfsym.Name, sym.SymVerABIInternal)
}
if alias.Type != 0 {
continue
}
@ -2390,6 +2494,9 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6
}
case sym.SHOSTOBJ:
if !s.Attr.Reachable() {
continue
}
if ctxt.HeadType == objabi.Hwindows || ctxt.IsELF {
put(ctxt, s, s.Name, UndefinedSym, s.Value, nil)
}
@ -2586,3 +2693,49 @@ func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library
mark[lib] = visited
*order = append(*order, lib)
}
func (ctxt *Link) loadlibfull() {
// Load full symbol contents, resolve indexed references.
ctxt.loader.LoadFull(ctxt.Arch, ctxt.Syms)
// Pull the symbols out.
ctxt.loader.ExtractSymbols(ctxt.Syms)
// Load cgo directives.
for _, d := range ctxt.cgodata {
setCgoAttr(ctxt, ctxt.Syms.Lookup, d.file, d.pkg, d.directives)
}
setupdynexp(ctxt)
// Populate ctxt.Reachparent if appropriate.
if ctxt.Reachparent != nil {
for i := 0; i < len(ctxt.loader.Reachparent); i++ {
p := ctxt.loader.Reachparent[i]
if p == 0 {
continue
}
if p == loader.Sym(i) {
panic("self-cycle in reachparent")
}
sym := ctxt.loader.Syms[i]
psym := ctxt.loader.Syms[p]
ctxt.Reachparent[sym] = psym
}
}
// Drop the reference.
ctxt.loader = nil
ctxt.cgodata = nil
addToTextp(ctxt)
}
func (ctxt *Link) dumpsyms() {
for _, s := range ctxt.Syms.Allsym {
fmt.Printf("%s %s %p %v %v\n", s, s.Type, s, s.Attr.Reachable(), s.Attr.OnList())
for i := range s.R {
fmt.Println("\t", s.R[i].Type, s.R[i].Sym)
}
}
}

View File

@ -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 {

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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 }

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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
}

View File

@ -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:

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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))

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -29,9 +29,9 @@ TEXT runtimeinternalatomic·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

View File

@ -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")
}
}