mirror of https://github.com/golang/go.git
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:
commit
bbae923d20
|
|
@ -23,6 +23,7 @@ var (
|
|||
Dynlink = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries")
|
||||
AllErrors = flag.Bool("e", false, "no limit on number of errors reported")
|
||||
SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble")
|
||||
Newobj = flag.Bool("newobj", false, "use new object file format")
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -40,18 +40,18 @@ func main() {
|
|||
}
|
||||
ctxt.Flag_dynlink = *flags.Dynlink
|
||||
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
|
||||
ctxt.Flag_newobj = *flags.Newobj
|
||||
ctxt.Bso = bufio.NewWriter(os.Stdout)
|
||||
defer ctxt.Bso.Flush()
|
||||
|
||||
architecture.Init(ctxt)
|
||||
|
||||
// Create object file, write header.
|
||||
out, err := os.Create(*flags.OutputFile)
|
||||
buf, err := bio.Create(*flags.OutputFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer bio.MustClose(out)
|
||||
buf := bufio.NewWriter(bio.MustWriter(out))
|
||||
defer buf.Close()
|
||||
|
||||
if !*flags.SymABIs {
|
||||
fmt.Fprintf(buf, "go object %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version)
|
||||
|
|
@ -83,6 +83,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
if ok && !*flags.SymABIs {
|
||||
ctxt.NumberSyms(true)
|
||||
obj.WriteObjFile(ctxt, buf, "")
|
||||
}
|
||||
if !ok || diag {
|
||||
|
|
@ -91,9 +92,8 @@ func main() {
|
|||
} else {
|
||||
log.Print("assembly failed")
|
||||
}
|
||||
out.Close()
|
||||
buf.Close()
|
||||
os.Remove(*flags.OutputFile)
|
||||
os.Exit(1)
|
||||
}
|
||||
buf.Flush()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/goobj2"
|
||||
"cmd/internal/src"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
|
@ -945,10 +946,12 @@ func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) }
|
|||
|
||||
func (w *exportWriter) varExt(n *Node) {
|
||||
w.linkname(n.Sym)
|
||||
w.symIdx(n.Sym)
|
||||
}
|
||||
|
||||
func (w *exportWriter) funcExt(n *Node) {
|
||||
w.linkname(n.Sym)
|
||||
w.symIdx(n.Sym)
|
||||
|
||||
// Escape analysis.
|
||||
for _, fs := range types.RecvsParams {
|
||||
|
|
@ -987,6 +990,22 @@ func (w *exportWriter) linkname(s *types.Sym) {
|
|||
w.string(s.Linkname)
|
||||
}
|
||||
|
||||
func (w *exportWriter) symIdx(s *types.Sym) {
|
||||
if Ctxt.Flag_newobj {
|
||||
lsym := s.Linksym()
|
||||
if lsym.PkgIdx > goobj2.PkgIdxSelf || (lsym.PkgIdx == goobj2.PkgIdxInvalid && !lsym.Indexed()) || s.Linkname != "" {
|
||||
// Don't export index for non-package symbols, linkname'd symbols,
|
||||
// and symbols without an index. They can only be referenced by
|
||||
// name.
|
||||
w.int64(-1)
|
||||
} else {
|
||||
// For a defined symbol, export its index.
|
||||
// For re-exporting an imported symbol, pass its index through.
|
||||
w.int64(int64(lsym.SymIdx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inline bodies.
|
||||
|
||||
func (w *exportWriter) stmtList(list Nodes) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package gc
|
|||
import (
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/bio"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/src"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
|
@ -651,10 +652,12 @@ func (r *importReader) byte() byte {
|
|||
|
||||
func (r *importReader) varExt(n *Node) {
|
||||
r.linkname(n.Sym)
|
||||
r.symIdx(n.Sym)
|
||||
}
|
||||
|
||||
func (r *importReader) funcExt(n *Node) {
|
||||
r.linkname(n.Sym)
|
||||
r.symIdx(n.Sym)
|
||||
|
||||
// Escape analysis.
|
||||
for _, fs := range types.RecvsParams {
|
||||
|
|
@ -683,6 +686,20 @@ func (r *importReader) linkname(s *types.Sym) {
|
|||
s.Linkname = r.string()
|
||||
}
|
||||
|
||||
func (r *importReader) symIdx(s *types.Sym) {
|
||||
if Ctxt.Flag_newobj {
|
||||
lsym := s.Linksym()
|
||||
idx := int32(r.int64())
|
||||
if idx != -1 {
|
||||
if s.Linkname != "" {
|
||||
Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
|
||||
}
|
||||
lsym.SymIdx = idx
|
||||
lsym.Set(obj.AttrIndexed, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *importReader) doInline(n *Node) {
|
||||
if len(n.Func.Inl.Body) != 0 {
|
||||
Fatalf("%v already has inline body", n)
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ func Main(archInit func(*Arch)) {
|
|||
if supportsDynlink(thearch.LinkArch.Arch) {
|
||||
flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
|
||||
flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
|
||||
flag.BoolVar(&Ctxt.Flag_linkshared, "linkshared", false, "generate code that will be linked against Go shared libraries")
|
||||
}
|
||||
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
|
||||
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
|
||||
|
|
@ -274,12 +275,14 @@ func Main(archInit func(*Arch)) {
|
|||
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
|
||||
flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
|
||||
flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
|
||||
flag.BoolVar(&Ctxt.Flag_newobj, "newobj", false, "use new object file format")
|
||||
|
||||
objabi.Flagparse(usage)
|
||||
|
||||
// Record flags that affect the build result. (And don't
|
||||
// record flags that don't, since that would cause spurious
|
||||
// changes in the binary.)
|
||||
recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes")
|
||||
recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "dwarfbasentries", "smallframes", "newobj")
|
||||
|
||||
if smallFrames {
|
||||
maxStackVarSize = 128 * 1024
|
||||
|
|
@ -746,6 +749,8 @@ func Main(archInit func(*Arch)) {
|
|||
|
||||
// Write object data to disk.
|
||||
timings.Start("be", "dumpobj")
|
||||
dumpdata()
|
||||
Ctxt.NumberSyms(false)
|
||||
dumpobj()
|
||||
if asmhdr != "" {
|
||||
dumpasmhdr()
|
||||
|
|
|
|||
|
|
@ -111,21 +111,7 @@ func dumpCompilerObj(bout *bio.Writer) {
|
|||
dumpexport(bout)
|
||||
}
|
||||
|
||||
func dumpLinkerObj(bout *bio.Writer) {
|
||||
printObjHeader(bout)
|
||||
|
||||
if len(pragcgobuf) != 0 {
|
||||
// write empty export section; must be before cgo section
|
||||
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
|
||||
fmt.Fprintf(bout, "\n$$ // cgo\n")
|
||||
if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
|
||||
Fatalf("serializing pragcgobuf: %v", err)
|
||||
}
|
||||
fmt.Fprintf(bout, "\n$$\n\n")
|
||||
}
|
||||
|
||||
fmt.Fprintf(bout, "\n!\n")
|
||||
|
||||
func dumpdata() {
|
||||
externs := len(externdcl)
|
||||
|
||||
dumpglobls()
|
||||
|
|
@ -163,8 +149,24 @@ func dumpLinkerObj(bout *bio.Writer) {
|
|||
}
|
||||
|
||||
addGCLocals()
|
||||
}
|
||||
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -80,11 +80,18 @@ func (sym *Sym) Linksym() *obj.LSym {
|
|||
if sym == nil {
|
||||
return nil
|
||||
}
|
||||
initPkg := func(r *obj.LSym) {
|
||||
if sym.Linkname != "" {
|
||||
r.Pkg = "_"
|
||||
} else {
|
||||
r.Pkg = sym.Pkg.Prefix
|
||||
}
|
||||
}
|
||||
if sym.Func() {
|
||||
// This is a function symbol. Mark it as "internal ABI".
|
||||
return Ctxt.LookupABI(sym.LinksymName(), obj.ABIInternal)
|
||||
return Ctxt.LookupABIInit(sym.LinksymName(), obj.ABIInternal, initPkg)
|
||||
}
|
||||
return Ctxt.Lookup(sym.LinksymName())
|
||||
return Ctxt.LookupInit(sym.LinksymName(), initPkg)
|
||||
}
|
||||
|
||||
// Less reports whether symbol a is ordered before symbol b.
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ var bootstrapDirs = []string{
|
|||
"cmd/internal/gcprog",
|
||||
"cmd/internal/dwarf",
|
||||
"cmd/internal/edit",
|
||||
"cmd/internal/goobj2",
|
||||
"cmd/internal/objabi",
|
||||
"cmd/internal/obj",
|
||||
"cmd/internal/obj/arm",
|
||||
|
|
@ -72,6 +73,7 @@ var bootstrapDirs = []string{
|
|||
"cmd/link/internal/arm64",
|
||||
"cmd/link/internal/ld",
|
||||
"cmd/link/internal/loadelf",
|
||||
"cmd/link/internal/loader",
|
||||
"cmd/link/internal/loadmacho",
|
||||
"cmd/link/internal/loadpe",
|
||||
"cmd/link/internal/loadxcoff",
|
||||
|
|
|
|||
|
|
@ -585,7 +585,7 @@ func (t *tester) registerTests() {
|
|||
},
|
||||
})
|
||||
// Also test a cgo package.
|
||||
if t.cgoEnabled {
|
||||
if t.cgoEnabled && t.internalLink() {
|
||||
t.tests = append(t.tests, distTest{
|
||||
name: "pie_internal_cgo",
|
||||
heading: "internal linking of -buildmode=pie",
|
||||
|
|
|
|||
|
|
@ -145,8 +145,8 @@
|
|||
// -ldflags '[pattern=]arg list'
|
||||
// arguments to pass on each go tool link invocation.
|
||||
// -linkshared
|
||||
// link against shared libraries previously created with
|
||||
// -buildmode=shared.
|
||||
// build code that will be linked against shared libraries previously
|
||||
// created with -buildmode=shared.
|
||||
// -mod mode
|
||||
// module download mode to use: readonly or vendor.
|
||||
// See 'go help modules' for more.
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@ and test commands:
|
|||
-ldflags '[pattern=]arg list'
|
||||
arguments to pass on each go tool link invocation.
|
||||
-linkshared
|
||||
link against shared libraries previously created with
|
||||
-buildmode=shared.
|
||||
build code that will be linked against shared libraries previously
|
||||
created with -buildmode=shared.
|
||||
-mod mode
|
||||
module download mode to use: readonly or vendor.
|
||||
See 'go help modules' for more.
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ func buildModeInit() {
|
|||
base.Fatalf("-linkshared not supported on %s\n", platform)
|
||||
}
|
||||
codegenArg = "-dynlink"
|
||||
forcedGcflags = append(forcedGcflags, "-linkshared")
|
||||
// TODO(mwhudson): remove -w when that gets fixed in linker.
|
||||
forcedLdflags = append(forcedLdflags, "-linkshared", "-w")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1372,7 +1372,13 @@ func PutDefaultFunc(ctxt Context, s *FnState) error {
|
|||
abbrev := DW_ABRV_FUNCTION
|
||||
Uleb128put(ctxt, s.Info, int64(abbrev))
|
||||
|
||||
putattr(ctxt, s.Info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(s.Name)), s.Name)
|
||||
// Expand '"".' to import path.
|
||||
name := s.Name
|
||||
if s.Importpath != "" {
|
||||
name = strings.Replace(name, "\"\".", objabi.PathToPrefix(s.Importpath)+".", -1)
|
||||
}
|
||||
|
||||
putattr(ctxt, s.Info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
|
||||
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC)
|
||||
putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC)
|
||||
putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
|
||||
|
|
|
|||
|
|
@ -502,6 +502,14 @@ func (r *objReader) parseObject(prefix []byte) error {
|
|||
}
|
||||
// TODO: extract OS + build ID if/when we need it
|
||||
|
||||
p, err := r.peek(8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes.Equal(p, []byte("\x00go114LD")) {
|
||||
r.readNew()
|
||||
return nil
|
||||
}
|
||||
r.readFull(r.tmp[:8])
|
||||
if !bytes.Equal(r.tmp[:8], []byte("\x00go114ld")) {
|
||||
return r.error(errCorruptObject)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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},
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -388,6 +388,10 @@ type LSym struct {
|
|||
R []Reloc
|
||||
|
||||
Func *FuncInfo
|
||||
|
||||
Pkg string
|
||||
PkgIdx int32
|
||||
SymIdx int32 // TODO: replace RefIdx
|
||||
}
|
||||
|
||||
// A FuncInfo contains extra fields for STEXT symbols.
|
||||
|
|
@ -410,6 +414,8 @@ type FuncInfo struct {
|
|||
GCRegs *LSym
|
||||
StackObjects *LSym
|
||||
OpenCodedDeferInfo *LSym
|
||||
|
||||
FuncInfoSym *LSym
|
||||
}
|
||||
|
||||
type InlMark struct {
|
||||
|
|
@ -461,7 +467,7 @@ const (
|
|||
)
|
||||
|
||||
// Attribute is a set of symbol attributes.
|
||||
type Attribute uint16
|
||||
type Attribute uint32
|
||||
|
||||
const (
|
||||
AttrDuplicateOK Attribute = 1 << iota
|
||||
|
|
@ -502,6 +508,10 @@ const (
|
|||
// keep unwinding beyond this frame.
|
||||
AttrTopFrame
|
||||
|
||||
// Indexed indicates this symbol has been assigned with an index (when using the
|
||||
// new object file format).
|
||||
AttrIndexed
|
||||
|
||||
// attrABIBase is the value at which the ABI is encoded in
|
||||
// Attribute. This must be last; all bits after this are
|
||||
// assumed to be an ABI value.
|
||||
|
|
@ -525,6 +535,7 @@ func (a Attribute) NoFrame() bool { return a&AttrNoFrame != 0 }
|
|||
func (a Attribute) Static() bool { return a&AttrStatic != 0 }
|
||||
func (a Attribute) WasInlined() bool { return a&AttrWasInlined != 0 }
|
||||
func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 }
|
||||
func (a Attribute) Indexed() bool { return a&AttrIndexed != 0 }
|
||||
|
||||
func (a *Attribute) Set(flag Attribute, value bool) {
|
||||
if value {
|
||||
|
|
@ -559,6 +570,7 @@ var textAttrStrings = [...]struct {
|
|||
{bit: AttrStatic, s: "STATIC"},
|
||||
{bit: AttrWasInlined, s: ""},
|
||||
{bit: AttrTopFrame, s: "TOPFRAME"},
|
||||
{bit: AttrIndexed, s: ""},
|
||||
}
|
||||
|
||||
// TextAttrString formats a for printing in as part of a TEXT prog.
|
||||
|
|
@ -637,8 +649,10 @@ type Link struct {
|
|||
Debugpcln string
|
||||
Flag_shared bool
|
||||
Flag_dynlink bool
|
||||
Flag_linkshared bool
|
||||
Flag_optimize bool
|
||||
Flag_locationlists bool
|
||||
Flag_newobj bool // use new object file format
|
||||
Bso *bufio.Writer
|
||||
Pathname string
|
||||
hashmu sync.Mutex // protects hash, funchash
|
||||
|
|
@ -672,6 +686,14 @@ type Link struct {
|
|||
// TODO(austin): Replace this with ABI wrappers once the ABIs
|
||||
// actually diverge.
|
||||
ABIAliases []*LSym
|
||||
|
||||
// pkgIdx maps package path to index. The index is used for
|
||||
// symbol reference in the object file.
|
||||
pkgIdx map[string]int32
|
||||
|
||||
defs []*LSym // list of defined symbols in the current package
|
||||
nonpkgdefs []*LSym // list of defined non-package symbols
|
||||
nonpkgrefs []*LSym // list of referenced non-package symbols
|
||||
}
|
||||
|
||||
func (ctxt *Link) Diag(format string, args ...interface{}) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package obj
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"cmd/internal/bio"
|
||||
"cmd/internal/dwarf"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
|
|
@ -80,7 +81,13 @@ func newObjWriter(ctxt *Link, b *bufio.Writer, pkgpath string) *objWriter {
|
|||
}
|
||||
}
|
||||
|
||||
func WriteObjFile(ctxt *Link, b *bufio.Writer, pkgpath string) {
|
||||
func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) {
|
||||
if ctxt.Flag_newobj {
|
||||
WriteObjFile2(ctxt, bout, pkgpath)
|
||||
return
|
||||
}
|
||||
|
||||
b := bout.Writer
|
||||
w := newObjWriter(ctxt, b, pkgpath)
|
||||
|
||||
// Magic header
|
||||
|
|
@ -221,8 +228,7 @@ func (w *objWriter) writeRefs(s *LSym) {
|
|||
}
|
||||
}
|
||||
|
||||
func (w *objWriter) writeSymDebug(s *LSym) {
|
||||
ctxt := w.ctxt
|
||||
func (ctxt *Link) writeSymDebug(s *LSym) {
|
||||
fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
|
||||
if s.Type != 0 {
|
||||
fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
|
||||
|
|
@ -302,7 +308,7 @@ func (w *objWriter) writeSymDebug(s *LSym) {
|
|||
func (w *objWriter) writeSym(s *LSym) {
|
||||
ctxt := w.ctxt
|
||||
if ctxt.Debugasm > 0 {
|
||||
w.writeSymDebug(s)
|
||||
w.ctxt.writeSymDebug(s)
|
||||
}
|
||||
|
||||
w.wr.WriteByte(symPrefix)
|
||||
|
|
|
|||
|
|
@ -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...)
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) {
|
|||
_64bit uintptr // size on 64bit platforms
|
||||
}{
|
||||
{Addr{}, 32, 48},
|
||||
{LSym{}, 56, 104},
|
||||
{LSym{}, 76, 128},
|
||||
{Prog{}, 132, 200},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,12 @@
|
|||
package obj
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj2"
|
||||
"cmd/internal/objabi"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func Linknew(arch *LinkArch) *Link {
|
||||
|
|
@ -78,6 +80,13 @@ func (ctxt *Link) LookupStatic(name string) *LSym {
|
|||
// LookupABI looks up a symbol with the given ABI.
|
||||
// If it does not exist, it creates it.
|
||||
func (ctxt *Link) LookupABI(name string, abi ABI) *LSym {
|
||||
return ctxt.LookupABIInit(name, abi, nil)
|
||||
}
|
||||
|
||||
// LookupABI looks up a symbol with the given ABI.
|
||||
// If it does not exist, it creates it and
|
||||
// passes it to init for one-time initialization.
|
||||
func (ctxt *Link) LookupABIInit(name string, abi ABI, init func(s *LSym)) *LSym {
|
||||
var hash map[string]*LSym
|
||||
switch abi {
|
||||
case ABI0:
|
||||
|
|
@ -94,6 +103,9 @@ func (ctxt *Link) LookupABI(name string, abi ABI) *LSym {
|
|||
s = &LSym{Name: name}
|
||||
s.SetABI(abi)
|
||||
hash[name] = s
|
||||
if init != nil {
|
||||
init(s)
|
||||
}
|
||||
}
|
||||
ctxt.hashmu.Unlock()
|
||||
return s
|
||||
|
|
@ -147,3 +159,167 @@ func (ctxt *Link) Int64Sym(i int64) *LSym {
|
|||
s.Set(AttrLocal, true)
|
||||
})
|
||||
}
|
||||
|
||||
// Assign index to symbols.
|
||||
// asm is set to true if this is called by the assembler (i.e. not the compiler),
|
||||
// in which case all the symbols are non-package (for now).
|
||||
func (ctxt *Link) NumberSyms(asm bool) {
|
||||
if !ctxt.Flag_newobj {
|
||||
return
|
||||
}
|
||||
|
||||
if ctxt.Headtype == objabi.Haix {
|
||||
// Data must be sorted to keep a constant order in TOC symbols.
|
||||
// As they are created during Progedit, two symbols can be switched between
|
||||
// two different compilations. Therefore, BuildID will be different.
|
||||
// TODO: find a better place and optimize to only sort TOC symbols
|
||||
sort.Slice(ctxt.Data, func(i, j int) bool {
|
||||
return ctxt.Data[i].Name < ctxt.Data[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
ctxt.pkgIdx = make(map[string]int32)
|
||||
ctxt.defs = []*LSym{}
|
||||
ctxt.nonpkgdefs = []*LSym{}
|
||||
|
||||
var idx, nonpkgidx int32 = 0, 0
|
||||
ctxt.traverseSyms(traverseDefs, func(s *LSym) {
|
||||
if isNonPkgSym(ctxt, asm, s) {
|
||||
s.PkgIdx = goobj2.PkgIdxNone
|
||||
s.SymIdx = nonpkgidx
|
||||
if nonpkgidx != int32(len(ctxt.nonpkgdefs)) {
|
||||
panic("bad index")
|
||||
}
|
||||
ctxt.nonpkgdefs = append(ctxt.nonpkgdefs, s)
|
||||
nonpkgidx++
|
||||
} else {
|
||||
s.PkgIdx = goobj2.PkgIdxSelf
|
||||
s.SymIdx = idx
|
||||
if idx != int32(len(ctxt.defs)) {
|
||||
panic("bad index")
|
||||
}
|
||||
ctxt.defs = append(ctxt.defs, s)
|
||||
idx++
|
||||
}
|
||||
s.Set(AttrIndexed, true)
|
||||
})
|
||||
|
||||
ipkg := int32(1) // 0 is invalid index
|
||||
nonpkgdef := nonpkgidx
|
||||
ctxt.traverseSyms(traverseRefs|traverseAux, func(rs *LSym) {
|
||||
if rs.PkgIdx != goobj2.PkgIdxInvalid {
|
||||
return
|
||||
}
|
||||
if !ctxt.Flag_linkshared {
|
||||
// Assign special index for builtin symbols.
|
||||
// Don't do it when linking against shared libraries, as the runtime
|
||||
// may be in a different library.
|
||||
if i := goobj2.BuiltinIdx(rs.Name, int(rs.ABI())); i != -1 {
|
||||
rs.PkgIdx = goobj2.PkgIdxBuiltin
|
||||
rs.SymIdx = int32(i)
|
||||
rs.Set(AttrIndexed, true)
|
||||
return
|
||||
}
|
||||
}
|
||||
pkg := rs.Pkg
|
||||
if pkg == "" || pkg == "\"\"" || pkg == "_" || !rs.Indexed() {
|
||||
rs.PkgIdx = goobj2.PkgIdxNone
|
||||
rs.SymIdx = nonpkgidx
|
||||
rs.Set(AttrIndexed, true)
|
||||
if nonpkgidx != nonpkgdef+int32(len(ctxt.nonpkgrefs)) {
|
||||
panic("bad index")
|
||||
}
|
||||
ctxt.nonpkgrefs = append(ctxt.nonpkgrefs, rs)
|
||||
nonpkgidx++
|
||||
return
|
||||
}
|
||||
if k, ok := ctxt.pkgIdx[pkg]; ok {
|
||||
rs.PkgIdx = k
|
||||
return
|
||||
}
|
||||
rs.PkgIdx = ipkg
|
||||
ctxt.pkgIdx[pkg] = ipkg
|
||||
ipkg++
|
||||
})
|
||||
}
|
||||
|
||||
// Returns whether s is a non-package symbol, which needs to be referenced
|
||||
// by name instead of by index.
|
||||
func isNonPkgSym(ctxt *Link, asm bool, s *LSym) bool {
|
||||
if asm && !s.Static() {
|
||||
// asm symbols are referenced by name only, except static symbols
|
||||
// which are file-local and can be referenced by index.
|
||||
return true
|
||||
}
|
||||
if ctxt.Flag_linkshared {
|
||||
// The referenced symbol may be in a different shared library so
|
||||
// the linker cannot see its index.
|
||||
return true
|
||||
}
|
||||
if s.Pkg == "_" {
|
||||
// The frontend uses package "_" to mark symbols that should not
|
||||
// be referenced by index, e.g. linkname'd symbols.
|
||||
return true
|
||||
}
|
||||
if s.DuplicateOK() {
|
||||
// Dupok symbol needs to be dedup'd by name.
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type traverseFlag uint32
|
||||
|
||||
const (
|
||||
traverseDefs traverseFlag = 1 << iota
|
||||
traverseRefs
|
||||
traverseAux
|
||||
|
||||
traverseAll = traverseDefs | traverseRefs | traverseAux
|
||||
)
|
||||
|
||||
// Traverse symbols based on flag, call fn for each symbol.
|
||||
func (ctxt *Link) traverseSyms(flag traverseFlag, fn func(*LSym)) {
|
||||
lists := [][]*LSym{ctxt.Text, ctxt.Data, ctxt.ABIAliases}
|
||||
for _, list := range lists {
|
||||
for _, s := range list {
|
||||
if flag&traverseDefs != 0 {
|
||||
fn(s)
|
||||
}
|
||||
if flag&traverseRefs != 0 {
|
||||
for _, r := range s.R {
|
||||
if r.Sym != nil {
|
||||
fn(r.Sym)
|
||||
}
|
||||
}
|
||||
}
|
||||
if flag&traverseAux != 0 {
|
||||
if s.Gotype != nil {
|
||||
fn(s.Gotype)
|
||||
}
|
||||
if s.Type == objabi.STEXT {
|
||||
pc := &s.Func.Pcln
|
||||
for _, d := range pc.Funcdata {
|
||||
if d != nil {
|
||||
fn(d)
|
||||
}
|
||||
}
|
||||
for _, f := range pc.File {
|
||||
if fsym := ctxt.Lookup(f); fsym != nil {
|
||||
fn(fsym)
|
||||
}
|
||||
}
|
||||
for _, call := range pc.InlTree.nodes {
|
||||
if call.Func != nil {
|
||||
fn(call.Func)
|
||||
}
|
||||
f, _ := linkgetlineFromPos(ctxt, call.Pos)
|
||||
if fsym := ctxt.Lookup(f); fsym != nil {
|
||||
fn(fsym)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
|
|||
case objabi.R_ADDRCUOFF:
|
||||
// debug_range and debug_loc elements use this relocation type to get an
|
||||
// offset from the start of the compile unit.
|
||||
o = Symaddr(r.Sym) + r.Add - Symaddr(r.Sym.Unit.Lib.Textp[0])
|
||||
o = Symaddr(r.Sym) + r.Add - Symaddr(r.Sym.Unit.Textp[0])
|
||||
|
||||
// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
|
||||
case objabi.R_GOTPCREL:
|
||||
|
|
@ -1086,13 +1086,13 @@ func (p *GCProg) AddSym(s *sym.Symbol) {
|
|||
}
|
||||
|
||||
ptrsize := int64(p.ctxt.Arch.PtrSize)
|
||||
nptr := decodetypePtrdata(p.ctxt.Arch, typ) / ptrsize
|
||||
nptr := decodetypePtrdata(p.ctxt.Arch, typ.P) / ptrsize
|
||||
|
||||
if debugGCProg {
|
||||
fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr)
|
||||
}
|
||||
|
||||
if decodetypeUsegcprog(p.ctxt.Arch, typ) == 0 {
|
||||
if decodetypeUsegcprog(p.ctxt.Arch, typ.P) == 0 {
|
||||
// Copy pointers from mask into program.
|
||||
mask := decodetypeGcmask(p.ctxt, typ)
|
||||
for i := int64(0); i < nptr; i++ {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,15 @@ import (
|
|||
//
|
||||
// Any unreached text symbols are removed from ctxt.Textp.
|
||||
func deadcode(ctxt *Link) {
|
||||
if ctxt.Debugvlog != 0 {
|
||||
ctxt.Logf("deadcode\n")
|
||||
}
|
||||
|
||||
if *flagNewobj {
|
||||
deadcode2(ctxt)
|
||||
return
|
||||
}
|
||||
|
||||
d := &deadcodepass{
|
||||
ctxt: ctxt,
|
||||
ifaceMethod: make(map[methodsig]bool),
|
||||
|
|
@ -114,22 +123,70 @@ func deadcode(ctxt *Link) {
|
|||
}
|
||||
}
|
||||
|
||||
for _, lib := range ctxt.Library {
|
||||
lib.Textp = lib.Textp[:0]
|
||||
}
|
||||
addToTextp(ctxt)
|
||||
}
|
||||
|
||||
func addToTextp(ctxt *Link) {
|
||||
// Remove dead text but keep file information (z symbols).
|
||||
textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
|
||||
textp := []*sym.Symbol{}
|
||||
for _, s := range ctxt.Textp {
|
||||
if s.Attr.Reachable() {
|
||||
if s.Unit != nil {
|
||||
s.Unit.Lib.Textp = append(s.Unit.Lib.Textp, s)
|
||||
s.Unit.Textp = append(s.Unit.Textp, s)
|
||||
}
|
||||
textp = append(textp, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Put reachable text symbols into Textp.
|
||||
// do it in postorder so that packages are laid down in dependency order
|
||||
// internal first, then everything else
|
||||
ctxt.Library = postorder(ctxt.Library)
|
||||
for _, doInternal := range [2]bool{true, false} {
|
||||
for _, lib := range ctxt.Library {
|
||||
if isRuntimeDepPkg(lib.Pkg) != doInternal {
|
||||
continue
|
||||
}
|
||||
libtextp := lib.Textp[:0]
|
||||
for _, s := range lib.Textp {
|
||||
if s.Attr.Reachable() {
|
||||
textp = append(textp, s)
|
||||
libtextp = append(libtextp, s)
|
||||
if s.Unit != nil {
|
||||
s.Unit.Textp = append(s.Unit.Textp, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, s := range lib.DupTextSyms {
|
||||
if s.Attr.Reachable() && !s.Attr.OnList() {
|
||||
textp = append(textp, s)
|
||||
libtextp = append(libtextp, s)
|
||||
if s.Unit != nil {
|
||||
s.Unit.Textp = append(s.Unit.Textp, s)
|
||||
}
|
||||
s.Attr |= sym.AttrOnList
|
||||
// dupok symbols may be defined in multiple packages. its
|
||||
// associated package is chosen sort of arbitrarily (the
|
||||
// first containing package that the linker loads). canonicalize
|
||||
// it here to the package with which it will be laid down
|
||||
// in text.
|
||||
s.File = objabi.PathToPrefix(lib.Pkg)
|
||||
}
|
||||
}
|
||||
lib.Textp = libtextp
|
||||
}
|
||||
}
|
||||
ctxt.Textp = textp
|
||||
|
||||
if len(ctxt.Shlibs) > 0 {
|
||||
// We might have overwritten some functions above (this tends to happen for the
|
||||
// autogenerated type equality/hashing functions) and we don't want to generated
|
||||
// pcln table entries for these any more so remove them from Textp.
|
||||
textp := make([]*sym.Symbol, 0, len(ctxt.Textp))
|
||||
for _, s := range ctxt.Textp {
|
||||
if s.Type != sym.SDYNIMPORT {
|
||||
textp = append(textp, s)
|
||||
}
|
||||
}
|
||||
ctxt.Textp = textp
|
||||
}
|
||||
}
|
||||
|
||||
// methodref holds the relocations from a receiver type symbol to its
|
||||
|
|
@ -274,7 +331,7 @@ func (d *deadcodepass) flood() {
|
|||
// later will give a better error than deadcode.
|
||||
continue
|
||||
}
|
||||
if decodetypeKind(d.ctxt.Arch, s)&kindMask == kindInterface {
|
||||
if decodetypeKind(d.ctxt.Arch, s.P)&kindMask == kindInterface {
|
||||
for _, sig := range decodeIfaceMethods(d.ctxt.Arch, s) {
|
||||
if d.ctxt.Debugvlog > 1 {
|
||||
d.ctxt.Logf("reached iface method: %s\n", sig)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -65,28 +65,28 @@ func structfieldSize(arch *sys.Arch) int { return 3 * arch.PtrSize } // ru
|
|||
func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype
|
||||
|
||||
// Type.commonType.kind
|
||||
func decodetypeKind(arch *sys.Arch, s *sym.Symbol) uint8 {
|
||||
return s.P[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f
|
||||
func decodetypeKind(arch *sys.Arch, p []byte) uint8 {
|
||||
return p[2*arch.PtrSize+7] & objabi.KindMask // 0x13 / 0x1f
|
||||
}
|
||||
|
||||
// Type.commonType.kind
|
||||
func decodetypeUsegcprog(arch *sys.Arch, s *sym.Symbol) uint8 {
|
||||
return s.P[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f
|
||||
func decodetypeUsegcprog(arch *sys.Arch, p []byte) uint8 {
|
||||
return p[2*arch.PtrSize+7] & objabi.KindGCProg // 0x13 / 0x1f
|
||||
}
|
||||
|
||||
// Type.commonType.size
|
||||
func decodetypeSize(arch *sys.Arch, s *sym.Symbol) int64 {
|
||||
return int64(decodeInuxi(arch, s.P, arch.PtrSize)) // 0x8 / 0x10
|
||||
func decodetypeSize(arch *sys.Arch, p []byte) int64 {
|
||||
return int64(decodeInuxi(arch, p, arch.PtrSize)) // 0x8 / 0x10
|
||||
}
|
||||
|
||||
// Type.commonType.ptrdata
|
||||
func decodetypePtrdata(arch *sys.Arch, s *sym.Symbol) int64 {
|
||||
return int64(decodeInuxi(arch, s.P[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10
|
||||
func decodetypePtrdata(arch *sys.Arch, p []byte) int64 {
|
||||
return int64(decodeInuxi(arch, p[arch.PtrSize:], arch.PtrSize)) // 0x8 / 0x10
|
||||
}
|
||||
|
||||
// Type.commonType.tflag
|
||||
func decodetypeHasUncommon(arch *sys.Arch, s *sym.Symbol) bool {
|
||||
return s.P[2*arch.PtrSize+4]&tflagUncommon != 0
|
||||
func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool {
|
||||
return p[2*arch.PtrSize+4]&tflagUncommon != 0
|
||||
}
|
||||
|
||||
// Find the elf.Section of a given shared library that contains a given address.
|
||||
|
|
@ -138,7 +138,7 @@ func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 {
|
|||
func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte {
|
||||
if s.Type == sym.SDYNIMPORT {
|
||||
addr := decodetypeGcprogShlib(ctxt, s)
|
||||
ptrdata := decodetypePtrdata(ctxt.Arch, s)
|
||||
ptrdata := decodetypePtrdata(ctxt.Arch, s.P)
|
||||
sect := findShlibSection(ctxt, s.File, addr)
|
||||
if sect != nil {
|
||||
r := make([]byte, ptrdata/int64(ctxt.Arch.PtrSize))
|
||||
|
|
@ -181,17 +181,17 @@ func decodetypeChanElem(arch *sys.Arch, s *sym.Symbol) *sym.Symbol {
|
|||
}
|
||||
|
||||
// Type.FuncType.dotdotdot
|
||||
func decodetypeFuncDotdotdot(arch *sys.Arch, s *sym.Symbol) bool {
|
||||
return uint16(decodeInuxi(arch, s.P[commonsize(arch)+2:], 2))&(1<<15) != 0
|
||||
func decodetypeFuncDotdotdot(arch *sys.Arch, p []byte) bool {
|
||||
return uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2))&(1<<15) != 0
|
||||
}
|
||||
|
||||
// Type.FuncType.inCount
|
||||
func decodetypeFuncInCount(arch *sys.Arch, s *sym.Symbol) int {
|
||||
return int(decodeInuxi(arch, s.P[commonsize(arch):], 2))
|
||||
func decodetypeFuncInCount(arch *sys.Arch, p []byte) int {
|
||||
return int(decodeInuxi(arch, p[commonsize(arch):], 2))
|
||||
}
|
||||
|
||||
func decodetypeFuncOutCount(arch *sys.Arch, s *sym.Symbol) int {
|
||||
return int(uint16(decodeInuxi(arch, s.P[commonsize(arch)+2:], 2)) & (1<<15 - 1))
|
||||
func decodetypeFuncOutCount(arch *sys.Arch, p []byte) int {
|
||||
return int(uint16(decodeInuxi(arch, p[commonsize(arch)+2:], 2)) & (1<<15 - 1))
|
||||
}
|
||||
|
||||
func decodetypeFuncInType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
|
||||
|
|
@ -199,14 +199,14 @@ func decodetypeFuncInType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
|
|||
if arch.PtrSize == 8 {
|
||||
uadd += 4
|
||||
}
|
||||
if decodetypeHasUncommon(arch, s) {
|
||||
if decodetypeHasUncommon(arch, s.P) {
|
||||
uadd += uncommonSize()
|
||||
}
|
||||
return decodeRelocSym(s, int32(uadd+i*arch.PtrSize))
|
||||
}
|
||||
|
||||
func decodetypeFuncOutType(arch *sys.Arch, s *sym.Symbol, i int) *sym.Symbol {
|
||||
return decodetypeFuncInType(arch, s, i+decodetypeFuncInCount(arch, s))
|
||||
return decodetypeFuncInType(arch, s, i+decodetypeFuncInCount(arch, s.P))
|
||||
}
|
||||
|
||||
// Type.StructType.fields.Slice::length
|
||||
|
|
@ -216,7 +216,7 @@ func decodetypeStructFieldCount(arch *sys.Arch, s *sym.Symbol) int {
|
|||
|
||||
func decodetypeStructFieldArrayOff(arch *sys.Arch, s *sym.Symbol, i int) int {
|
||||
off := commonsize(arch) + 4*arch.PtrSize
|
||||
if decodetypeHasUncommon(arch, s) {
|
||||
if decodetypeHasUncommon(arch, s.P) {
|
||||
off += uncommonSize()
|
||||
}
|
||||
off += i * structfieldSize(arch)
|
||||
|
|
@ -264,8 +264,8 @@ func decodetypeStructFieldOffsAnon(arch *sys.Arch, s *sym.Symbol, i int) int64 {
|
|||
}
|
||||
|
||||
// InterfaceType.methods.length
|
||||
func decodetypeIfaceMethodCount(arch *sys.Arch, s *sym.Symbol) int64 {
|
||||
return int64(decodeInuxi(arch, s.P[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
|
||||
func decodetypeIfaceMethodCount(arch *sys.Arch, p []byte) int64 {
|
||||
return int64(decodeInuxi(arch, p[commonsize(arch)+2*arch.PtrSize:], arch.PtrSize))
|
||||
}
|
||||
|
||||
// methodsig is a fully qualified typed method signature, like
|
||||
|
|
@ -299,7 +299,7 @@ func decodeMethodSig(arch *sys.Arch, s *sym.Symbol, off, size, count int) []meth
|
|||
mtypSym := decodeRelocSym(s, int32(off+4))
|
||||
|
||||
buf.WriteRune('(')
|
||||
inCount := decodetypeFuncInCount(arch, mtypSym)
|
||||
inCount := decodetypeFuncInCount(arch, mtypSym.P)
|
||||
for i := 0; i < inCount; i++ {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
|
|
@ -307,7 +307,7 @@ func decodeMethodSig(arch *sys.Arch, s *sym.Symbol, off, size, count int) []meth
|
|||
buf.WriteString(decodetypeFuncInType(arch, mtypSym, i).Name)
|
||||
}
|
||||
buf.WriteString(") (")
|
||||
outCount := decodetypeFuncOutCount(arch, mtypSym)
|
||||
outCount := decodetypeFuncOutCount(arch, mtypSym.P)
|
||||
for i := 0; i < outCount; i++ {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
|
|
@ -324,7 +324,7 @@ func decodeMethodSig(arch *sys.Arch, s *sym.Symbol, off, size, count int) []meth
|
|||
}
|
||||
|
||||
func decodeIfaceMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
|
||||
if decodetypeKind(arch, s)&kindMask != kindInterface {
|
||||
if decodetypeKind(arch, s.P)&kindMask != kindInterface {
|
||||
panic(fmt.Sprintf("symbol %q is not an interface", s.Name))
|
||||
}
|
||||
r := decodeReloc(s, int32(commonsize(arch)+arch.PtrSize))
|
||||
|
|
@ -335,17 +335,17 @@ func decodeIfaceMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
|
|||
panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", s.Name))
|
||||
}
|
||||
off := int(r.Add) // array of reflect.imethod values
|
||||
numMethods := int(decodetypeIfaceMethodCount(arch, s))
|
||||
numMethods := int(decodetypeIfaceMethodCount(arch, s.P))
|
||||
sizeofIMethod := 4 + 4
|
||||
return decodeMethodSig(arch, s, off, sizeofIMethod, numMethods)
|
||||
}
|
||||
|
||||
func decodetypeMethods(arch *sys.Arch, s *sym.Symbol) []methodsig {
|
||||
if !decodetypeHasUncommon(arch, s) {
|
||||
if !decodetypeHasUncommon(arch, s.P) {
|
||||
panic(fmt.Sprintf("no methods on %q", s.Name))
|
||||
}
|
||||
off := commonsize(arch) // reflect.rtype
|
||||
switch decodetypeKind(arch, s) & kindMask {
|
||||
switch decodetypeKind(arch, s.P) & kindMask {
|
||||
case kindStruct: // reflect.structType
|
||||
off += 4 * arch.PtrSize
|
||||
case kindPtr: // reflect.ptrType
|
||||
|
|
|
|||
|
|
@ -422,8 +422,8 @@ func defgotype(ctxt *Link, gotype *sym.Symbol) *sym.Symbol {
|
|||
|
||||
func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
|
||||
name := gotype.Name[5:] // could also decode from Type.string
|
||||
kind := decodetypeKind(ctxt.Arch, gotype)
|
||||
bytesize := decodetypeSize(ctxt.Arch, gotype)
|
||||
kind := decodetypeKind(ctxt.Arch, gotype.P)
|
||||
bytesize := decodetypeSize(ctxt.Arch, gotype.P)
|
||||
|
||||
var die, typedefdie *dwarf.DWDie
|
||||
switch kind {
|
||||
|
|
@ -488,17 +488,17 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
|
|||
die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
|
||||
newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
|
||||
typedefdie = dotypedef(ctxt, &dwtypes, name, die)
|
||||
nfields := decodetypeFuncInCount(ctxt.Arch, gotype)
|
||||
nfields := decodetypeFuncInCount(ctxt.Arch, gotype.P)
|
||||
for i := 0; i < nfields; i++ {
|
||||
s := decodetypeFuncInType(ctxt.Arch, gotype, i)
|
||||
fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
|
||||
newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
|
||||
}
|
||||
|
||||
if decodetypeFuncDotdotdot(ctxt.Arch, gotype) {
|
||||
if decodetypeFuncDotdotdot(ctxt.Arch, gotype.P) {
|
||||
newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
|
||||
}
|
||||
nfields = decodetypeFuncOutCount(ctxt.Arch, gotype)
|
||||
nfields = decodetypeFuncOutCount(ctxt.Arch, gotype.P)
|
||||
for i := 0; i < nfields; i++ {
|
||||
s := decodetypeFuncOutType(ctxt.Arch, gotype, i)
|
||||
fld := newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
|
||||
|
|
@ -508,7 +508,7 @@ func newtype(ctxt *Link, gotype *sym.Symbol) *dwarf.DWDie {
|
|||
case objabi.KindInterface:
|
||||
die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
|
||||
typedefdie = dotypedef(ctxt, &dwtypes, name, die)
|
||||
nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype))
|
||||
nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype.P))
|
||||
var s *sym.Symbol
|
||||
if nfields == 0 {
|
||||
s = lookupOrDiag(ctxt, "type.runtime.eface")
|
||||
|
|
@ -733,7 +733,7 @@ func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
|
|||
gotype := getattr(die, dwarf.DW_AT_type).Data.(*sym.Symbol)
|
||||
keytype := decodetypeMapKey(ctxt.Arch, gotype)
|
||||
valtype := decodetypeMapValue(ctxt.Arch, gotype)
|
||||
keysize, valsize := decodetypeSize(ctxt.Arch, keytype), decodetypeSize(ctxt.Arch, valtype)
|
||||
keysize, valsize := decodetypeSize(ctxt.Arch, keytype.P), decodetypeSize(ctxt.Arch, valtype.P)
|
||||
keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype))
|
||||
|
||||
// compute size info like hashmap.c does.
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@ func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename s
|
|||
return
|
||||
}
|
||||
p1 += p0
|
||||
|
||||
loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
|
||||
}
|
||||
}
|
||||
|
|
@ -123,6 +122,39 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
|
|||
return
|
||||
}
|
||||
|
||||
// Find cgo_export symbols. They are roots in the deadcode pass.
|
||||
for _, f := range directives {
|
||||
switch f[0] {
|
||||
case "cgo_export_static", "cgo_export_dynamic":
|
||||
if len(f) < 2 || len(f) > 3 {
|
||||
continue
|
||||
}
|
||||
local := f[1]
|
||||
switch ctxt.BuildMode {
|
||||
case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
|
||||
if local == "main" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
local = expandpkg(local, pkg)
|
||||
if f[0] == "cgo_export_static" {
|
||||
ctxt.cgo_export_static[local] = true
|
||||
} else {
|
||||
ctxt.cgo_export_dynamic[local] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *flagNewobj {
|
||||
// Record the directives. We'll process them later after Symbols are created.
|
||||
ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
|
||||
} else {
|
||||
setCgoAttr(ctxt, ctxt.Syms.Lookup, file, pkg, directives)
|
||||
}
|
||||
}
|
||||
|
||||
// Set symbol attributes or flags based on cgo directives.
|
||||
func setCgoAttr(ctxt *Link, lookup func(string, int) *sym.Symbol, file string, pkg string, directives [][]string) {
|
||||
for _, f := range directives {
|
||||
switch f[0] {
|
||||
case "cgo_import_dynamic":
|
||||
|
|
@ -164,8 +196,8 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
|
|||
if i := strings.Index(remote, "#"); i >= 0 {
|
||||
remote, q = remote[:i], remote[i+1:]
|
||||
}
|
||||
s := ctxt.Syms.Lookup(local, 0)
|
||||
if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SHOSTOBJ {
|
||||
s := lookup(local, 0)
|
||||
if s.Type == 0 || s.Type == sym.SXREF || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS || s.Type == sym.SHOSTOBJ {
|
||||
s.SetDynimplib(lib)
|
||||
s.SetExtname(remote)
|
||||
s.SetDynimpvers(q)
|
||||
|
|
@ -183,7 +215,7 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
|
|||
}
|
||||
local := f[1]
|
||||
|
||||
s := ctxt.Syms.Lookup(local, 0)
|
||||
s := lookup(local, 0)
|
||||
s.Type = sym.SHOSTOBJ
|
||||
s.Size = 0
|
||||
continue
|
||||
|
|
@ -204,11 +236,11 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
|
|||
// functions. Link.loadlib will resolve any
|
||||
// ABI aliases we find here (since we may not
|
||||
// yet know it's an alias).
|
||||
s := ctxt.Syms.Lookup(local, 0)
|
||||
s := lookup(local, 0)
|
||||
|
||||
switch ctxt.BuildMode {
|
||||
case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
|
||||
if s == ctxt.Syms.Lookup("main", 0) {
|
||||
if s == lookup("main", 0) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -223,7 +255,6 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
|
|||
|
||||
if !s.Attr.CgoExport() {
|
||||
s.SetExtname(remote)
|
||||
dynexp = append(dynexp, s)
|
||||
} else if s.Extname() != remote {
|
||||
fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname(), remote)
|
||||
nerrors++
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import (
|
|||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"cmd/link/internal/loadelf"
|
||||
"cmd/link/internal/loader"
|
||||
"cmd/link/internal/loadmacho"
|
||||
"cmd/link/internal/loadpe"
|
||||
"cmd/link/internal/loadxcoff"
|
||||
|
|
@ -57,6 +58,7 @@ import (
|
|||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
|
@ -378,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import (
|
|||
"cmd/internal/obj"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"cmd/link/internal/loader"
|
||||
"cmd/link/internal/sym"
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
|
|
@ -96,6 +97,18 @@ type Link struct {
|
|||
runtimeCU *sym.CompilationUnit // One of the runtime CUs, the last one seen.
|
||||
|
||||
relocbuf []byte // temporary buffer for applying relocations
|
||||
|
||||
loader *loader.Loader
|
||||
cgodata []cgodata // cgo directives to load, three strings are args for loadcgo
|
||||
|
||||
cgo_export_static map[string]bool
|
||||
cgo_export_dynamic map[string]bool
|
||||
}
|
||||
|
||||
type cgodata struct {
|
||||
file string
|
||||
pkg string
|
||||
directives [][]string
|
||||
}
|
||||
|
||||
type unresolvedSymKey struct {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ var (
|
|||
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
|
||||
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
|
||||
FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
|
||||
flagNewobj = flag.Bool("newobj", false, "use new object file format")
|
||||
|
||||
FlagRound = flag.Int("R", -1, "set address rounding `quantum`")
|
||||
FlagTextAddr = flag.Int64("T", -1, "set text segment `address`")
|
||||
|
|
@ -208,8 +209,13 @@ func Main(arch *sys.Arch, theArch Arch) {
|
|||
}
|
||||
ctxt.loadlib()
|
||||
|
||||
ctxt.dostrdata()
|
||||
deadcode(ctxt)
|
||||
if *flagNewobj {
|
||||
ctxt.loadlibfull() // XXX do it here for now
|
||||
}
|
||||
ctxt.linksetup()
|
||||
ctxt.dostrdata()
|
||||
|
||||
dwarfGenerateDebugInfo(ctxt)
|
||||
if objabi.Fieldtrack_enabled != 0 {
|
||||
fieldtrack(ctxt)
|
||||
|
|
|
|||
|
|
@ -305,10 +305,10 @@ func (ctxt *Link) pclntab() {
|
|||
// appears in the Pcfile table. In that case, this assigns
|
||||
// the outer file a number.
|
||||
numberfile(ctxt, call.File)
|
||||
nameoff := nameToOffset(call.Func.Name)
|
||||
nameoff := nameToOffset(call.Func)
|
||||
|
||||
inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent))
|
||||
inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func.Name, call.Func.File)))
|
||||
inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func, "")))
|
||||
// byte 3 is unused
|
||||
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value))
|
||||
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line))
|
||||
|
|
|
|||
|
|
@ -694,7 +694,7 @@ func (ctxt *Link) symtab() {
|
|||
// creating the moduledata from scratch and it does not have a
|
||||
// compiler-provided size, so read it from the type data.
|
||||
moduledatatype := ctxt.Syms.ROLookup("type.runtime.moduledata", 0)
|
||||
moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype)
|
||||
moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype.P)
|
||||
moduledata.Grow(moduledata.Size)
|
||||
|
||||
lastmoduledatap := ctxt.Syms.Lookup("runtime.lastmoduledatap", 0)
|
||||
|
|
|
|||
|
|
@ -88,3 +88,10 @@ func contains(s []string, v string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// implements sort.Interface, for sorting symbols by name.
|
||||
type byName []*sym.Symbol
|
||||
|
||||
func (s byName) Len() int { return len(s) }
|
||||
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s byName) Less(i, j int) bool { return s[i].Name < s[j].Name }
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"cmd/internal/bio"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"cmd/link/internal/loader"
|
||||
"cmd/link/internal/sym"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
|
|
@ -451,7 +452,20 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags
|
|||
return found, ehdrFlags, nil
|
||||
}
|
||||
|
||||
// Load loads the ELF file pn from f.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) {
|
||||
newSym := func(name string, version int) *sym.Symbol {
|
||||
return l.LookupOrCreate(name, version, syms)
|
||||
}
|
||||
return load(arch, syms.IncVersion(), newSym, newSym, f, pkg, length, pn, flags)
|
||||
}
|
||||
|
||||
func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, flags uint32) ([]*sym.Symbol, uint32, error) {
|
||||
return load(arch, syms.IncVersion(), syms.Newsym, syms.Lookup, f, pkg, length, pn, flags)
|
||||
}
|
||||
|
||||
type lookupFunc func(string, int) *sym.Symbol
|
||||
|
||||
// load loads the ELF file pn from f.
|
||||
// Symbols are written into syms, and a slice of the text symbols is returned.
|
||||
//
|
||||
// On ARM systems, Load will attempt to determine what ELF header flags to
|
||||
|
|
@ -459,12 +473,11 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags
|
|||
// parameter initEhdrFlags contains the current header flags for the output
|
||||
// object, and the returned ehdrFlags contains what this Load function computes.
|
||||
// TODO: find a better place for this logic.
|
||||
func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []*sym.Symbol, ehdrFlags uint32, err error) {
|
||||
func load(arch *sys.Arch, localSymVersion int, newSym, lookup lookupFunc, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []*sym.Symbol, ehdrFlags uint32, err error) {
|
||||
errorf := func(str string, args ...interface{}) ([]*sym.Symbol, uint32, error) {
|
||||
return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
|
||||
}
|
||||
|
||||
localSymVersion := syms.IncVersion()
|
||||
base := f.Offset()
|
||||
|
||||
var hdrbuf [64]uint8
|
||||
|
|
@ -715,7 +728,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
|
|||
}
|
||||
sectsymNames[name] = true
|
||||
|
||||
s := syms.Lookup(name, localSymVersion)
|
||||
s := lookup(name, localSymVersion)
|
||||
|
||||
switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) {
|
||||
default:
|
||||
|
|
@ -754,7 +767,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
|
|||
|
||||
for i := 1; i < elfobj.nsymtab; i++ {
|
||||
var elfsym ElfSym
|
||||
if err := readelfsym(arch, syms, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
|
||||
if err := readelfsym(newSym, lookup, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
|
||||
return errorf("%s: malformed elf file: %v", pn, err)
|
||||
}
|
||||
symbols[i] = elfsym.sym
|
||||
|
|
@ -925,7 +938,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
|
|||
rp.Sym = nil
|
||||
} else {
|
||||
var elfsym ElfSym
|
||||
if err := readelfsym(arch, syms, elfobj, int(info>>32), &elfsym, 0, 0); err != nil {
|
||||
if err := readelfsym(newSym, lookup, arch, elfobj, int(info>>32), &elfsym, 0, 0); err != nil {
|
||||
return errorf("malformed elf file: %v", err)
|
||||
}
|
||||
elfsym.sym = symbols[info>>32]
|
||||
|
|
@ -1002,7 +1015,7 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func readelfsym(arch *sys.Arch, syms *sym.Symbols, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
|
||||
func readelfsym(newSym, lookup lookupFunc, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
|
||||
if i >= elfobj.nsymtab || i < 0 {
|
||||
err = fmt.Errorf("invalid elf symbol index")
|
||||
return err
|
||||
|
|
@ -1052,7 +1065,7 @@ func readelfsym(arch *sys.Arch, syms *sym.Symbols, elfobj *ElfObj, i int, elfsym
|
|||
switch elfsym.bind {
|
||||
case ElfSymBindGlobal:
|
||||
if needSym != 0 {
|
||||
s = syms.Lookup(elfsym.name, 0)
|
||||
s = lookup(elfsym.name, 0)
|
||||
|
||||
// for global scoped hidden symbols we should insert it into
|
||||
// symbol hash table, but mark them as hidden.
|
||||
|
|
@ -1077,7 +1090,7 @@ func readelfsym(arch *sys.Arch, syms *sym.Symbols, elfobj *ElfObj, i int, elfsym
|
|||
// We need to be able to look this up,
|
||||
// so put it in the hash table.
|
||||
if needSym != 0 {
|
||||
s = syms.Lookup(elfsym.name, localSymVersion)
|
||||
s = lookup(elfsym.name, localSymVersion)
|
||||
s.Attr |= sym.AttrVisibilityHidden
|
||||
}
|
||||
|
||||
|
|
@ -1088,14 +1101,14 @@ func readelfsym(arch *sys.Arch, syms *sym.Symbols, elfobj *ElfObj, i int, elfsym
|
|||
// local names and hidden global names are unique
|
||||
// and should only be referenced by their index, not name, so we
|
||||
// don't bother to add them into the hash table
|
||||
s = syms.Newsym(elfsym.name, localSymVersion)
|
||||
s = newSym(elfsym.name, localSymVersion)
|
||||
|
||||
s.Attr |= sym.AttrVisibilityHidden
|
||||
}
|
||||
|
||||
case ElfSymBindWeak:
|
||||
if needSym != 0 {
|
||||
s = syms.Lookup(elfsym.name, 0)
|
||||
s = lookup(elfsym.name, 0)
|
||||
if elfsym.other == 2 {
|
||||
s.Attr |= sym.AttrVisibilityHidden
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -10,6 +10,7 @@ import (
|
|||
"cmd/internal/bio"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"cmd/link/internal/loader"
|
||||
"cmd/link/internal/sym"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
|
@ -423,14 +424,24 @@ func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
// Load loads the Mach-O file pn from f.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) ([]*sym.Symbol, error) {
|
||||
newSym := func(name string, version int) *sym.Symbol {
|
||||
return l.LookupOrCreate(name, version, syms)
|
||||
}
|
||||
return load(arch, syms.IncVersion(), newSym, f, pkg, length, pn)
|
||||
}
|
||||
|
||||
func LoadOld(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
|
||||
return load(arch, syms.IncVersion(), syms.Lookup, f, pkg, length, pn)
|
||||
}
|
||||
|
||||
// load the Mach-O file pn from f.
|
||||
// Symbols are written into syms, and a slice of the text symbols is returned.
|
||||
func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
|
||||
func load(arch *sys.Arch, localSymVersion int, lookup func(string, int) *sym.Symbol, f *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
|
||||
errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
|
||||
return nil, fmt.Errorf("loadmacho: %v: %v", pn, fmt.Sprintf(str, args...))
|
||||
}
|
||||
|
||||
localSymVersion := syms.IncVersion()
|
||||
base := f.Offset()
|
||||
|
||||
var hdr [7 * 4]uint8
|
||||
|
|
@ -562,7 +573,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
|
|||
continue
|
||||
}
|
||||
name := fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
|
||||
s := syms.Lookup(name, localSymVersion)
|
||||
s := lookup(name, localSymVersion)
|
||||
if s.Type != 0 {
|
||||
return errorf("duplicate %s/%s", sect.segname, sect.name)
|
||||
}
|
||||
|
|
@ -610,7 +621,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, pkg string, length i
|
|||
if machsym.type_&N_EXT == 0 {
|
||||
v = localSymVersion
|
||||
}
|
||||
s := syms.Lookup(name, v)
|
||||
s := lookup(name, v)
|
||||
if machsym.type_&N_EXT == 0 {
|
||||
s.Attr |= sym.AttrDuplicateOK
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"cmd/internal/bio"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"cmd/link/internal/loader"
|
||||
"cmd/link/internal/sym"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
|
|
@ -144,12 +145,21 @@ func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
// Load loads the PE file pn from input.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
|
||||
lookup := func(name string, version int) *sym.Symbol {
|
||||
return l.LookupOrCreate(name, version, syms)
|
||||
}
|
||||
return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
|
||||
}
|
||||
|
||||
func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
|
||||
return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn)
|
||||
}
|
||||
|
||||
// load loads the PE file pn from input.
|
||||
// Symbols are written into syms, and a slice of the text symbols is returned.
|
||||
// If an .rsrc section is found, its symbol is returned as rsrc.
|
||||
func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
|
||||
localSymVersion := syms.IncVersion()
|
||||
|
||||
func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, rsrc *sym.Symbol, err error) {
|
||||
sectsyms := make(map[*pe.Section]*sym.Symbol)
|
||||
sectdata := make(map[*pe.Section][]byte)
|
||||
|
||||
|
|
@ -181,7 +191,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
|
|||
}
|
||||
|
||||
name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
|
||||
s := syms.Lookup(name, localSymVersion)
|
||||
s := lookup(name, localSymVersion)
|
||||
|
||||
switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
|
||||
case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
|
||||
|
|
@ -239,7 +249,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
|
|||
return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
|
||||
}
|
||||
pesym := &f.COFFSymbols[r.SymbolTableIndex]
|
||||
gosym, err := readpesym(arch, syms, f, pesym, sectsyms, localSymVersion)
|
||||
gosym, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -351,7 +361,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
|
|||
}
|
||||
}
|
||||
|
||||
s, err := readpesym(arch, syms, f, pesym, sectsyms, localSymVersion)
|
||||
s, err := readpesym(arch, lookup, f, pesym, sectsyms, localSymVersion)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -435,7 +445,7 @@ func issect(s *pe.COFFSymbol) bool {
|
|||
return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
|
||||
}
|
||||
|
||||
func readpesym(arch *sys.Arch, syms *sym.Symbols, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]*sym.Symbol, localSymVersion int) (*sym.Symbol, error) {
|
||||
func readpesym(arch *sys.Arch, lookup func(string, int) *sym.Symbol, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]*sym.Symbol, localSymVersion int) (*sym.Symbol, error) {
|
||||
symname, err := pesym.FullName(f.StringTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -481,10 +491,10 @@ func readpesym(arch *sys.Arch, syms *sym.Symbols, f *pe.File, pesym *pe.COFFSymb
|
|||
case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
|
||||
switch pesym.StorageClass {
|
||||
case IMAGE_SYM_CLASS_EXTERNAL: //global
|
||||
s = syms.Lookup(name, 0)
|
||||
s = lookup(name, 0)
|
||||
|
||||
case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
|
||||
s = syms.Lookup(name, localSymVersion)
|
||||
s = lookup(name, localSymVersion)
|
||||
s.Attr |= sym.AttrDuplicateOK
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"cmd/internal/bio"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"cmd/link/internal/loader"
|
||||
"cmd/link/internal/sym"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -38,13 +39,25 @@ func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
// Load loads the Xcoff file pn from f.
|
||||
// Load loads xcoff files with the indexed object files.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
|
||||
lookup := func(name string, version int) *sym.Symbol {
|
||||
return l.LookupOrCreate(name, version, syms)
|
||||
}
|
||||
return load(arch, lookup, syms.IncVersion(), input, pkg, length, pn)
|
||||
}
|
||||
|
||||
// LoadOld uses the old version of object loading.
|
||||
func LoadOld(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
|
||||
return load(arch, syms.Lookup, syms.IncVersion(), input, pkg, length, pn)
|
||||
}
|
||||
|
||||
// loads the Xcoff file pn from f.
|
||||
// Symbols are written into syms, and a slice of the text symbols is returned.
|
||||
func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
|
||||
func load(arch *sys.Arch, lookup func(string, int) *sym.Symbol, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
|
||||
errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
|
||||
return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
|
||||
}
|
||||
localSymVersion := syms.IncVersion()
|
||||
|
||||
var ldSections []*ldSection
|
||||
|
||||
|
|
@ -62,7 +75,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
|
|||
lds := new(ldSection)
|
||||
lds.Section = *sect
|
||||
name := fmt.Sprintf("%s(%s)", pkg, lds.Name)
|
||||
s := syms.Lookup(name, localSymVersion)
|
||||
s := lookup(name, localSymVersion)
|
||||
|
||||
switch lds.Type {
|
||||
default:
|
||||
|
|
@ -100,7 +113,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
|
|||
continue
|
||||
}
|
||||
|
||||
s := syms.Lookup(sx.Name, 0)
|
||||
s := lookup(sx.Name, 0)
|
||||
|
||||
// Text symbol
|
||||
if s.Type == sym.STEXT {
|
||||
|
|
@ -122,7 +135,7 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
|
|||
for i, rx := range sect.Relocs {
|
||||
r := &rs[i]
|
||||
|
||||
r.Sym = syms.Lookup(rx.Symbol.Name, 0)
|
||||
r.Sym = lookup(rx.Symbol.Name, 0)
|
||||
if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
|
||||
return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ overwrite:
|
|||
pc.InlTree[i].Parent = r.readInt32()
|
||||
pc.InlTree[i].File = r.readSymIndex()
|
||||
pc.InlTree[i].Line = r.readInt32()
|
||||
pc.InlTree[i].Func = r.readSymIndex()
|
||||
pc.InlTree[i].Func = r.readSymIndex().Name
|
||||
pc.InlTree[i].ParentPC = r.readInt32()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ func archreloctoc(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol, val int64) int64 {
|
|||
const prefix = "TOC."
|
||||
var tarSym *sym.Symbol
|
||||
if strings.HasPrefix(r.Sym.Name, prefix) {
|
||||
tarSym = ctxt.Syms.ROLookup(strings.TrimPrefix(r.Sym.Name, prefix), 0)
|
||||
tarSym = r.Sym.R[0].Sym
|
||||
} else {
|
||||
ld.Errorf(s, "archreloctoc called for a symbol without TOC anchor")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ type InlinedCall struct {
|
|||
Parent int32 // index of parent in InlTree
|
||||
File *Symbol // file of the inlined call
|
||||
Line int32 // line number of the inlined call
|
||||
Func *Symbol // function that was inlined
|
||||
Func string // name of the function that was inlined
|
||||
ParentPC int32 // PC of the instruction just before the inlined body (offset from function start)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,17 @@ func (syms *Symbols) ROLookup(name string, v int) *Symbol {
|
|||
return syms.hash[v][name]
|
||||
}
|
||||
|
||||
// Add an existing symbol to the symbol table.
|
||||
func (syms *Symbols) Add(s *Symbol) {
|
||||
name := s.Name
|
||||
v := int(s.Version)
|
||||
m := syms.hash[v]
|
||||
if _, ok := m[name]; ok {
|
||||
panic(name + " already added")
|
||||
}
|
||||
m[name] = s
|
||||
}
|
||||
|
||||
// Allocate a new version (i.e. symbol namespace).
|
||||
func (syms *Symbols) IncVersion() int {
|
||||
syms.hash = append(syms.hash, make(map[string]*Symbol))
|
||||
|
|
|
|||
|
|
@ -161,3 +161,8 @@ var RelROMap = map[SymKind]SymKind{
|
|||
SRODATA: SRODATARELRO,
|
||||
SFUNCTAB: SFUNCTABRELRO,
|
||||
}
|
||||
|
||||
// IsData returns true if the type is a data type.
|
||||
func (t SymKind) IsData() bool {
|
||||
return t == SDATA || t == SNOPTRDATA || t == SBSS || t == SNOPTRBSS
|
||||
}
|
||||
|
|
|
|||
|
|
@ -376,3 +376,68 @@ func TestIssue34788Android386TLSSequence(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const testStrictDupGoSrc = `
|
||||
package main
|
||||
func f()
|
||||
func main() { f() }
|
||||
`
|
||||
|
||||
const testStrictDupAsmSrc1 = `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
|
||||
RET
|
||||
`
|
||||
|
||||
const testStrictDupAsmSrc2 = `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
|
||||
JMP 0(PC)
|
||||
`
|
||||
|
||||
func TestStrictDup(t *testing.T) {
|
||||
// Check that -strictdups flag works.
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "TestStrictDup")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
src := filepath.Join(tmpdir, "x.go")
|
||||
err = ioutil.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
src = filepath.Join(tmpdir, "a.s")
|
||||
err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc1), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
src = filepath.Join(tmpdir, "b.s")
|
||||
err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc2), 0666)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
|
||||
cmd.Dir = tmpdir
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("linking with -strictdups=1 failed: %v", err)
|
||||
}
|
||||
if !bytes.Contains(out, []byte("mismatched payload")) {
|
||||
t.Errorf("unexpected output:\n%s", out)
|
||||
}
|
||||
|
||||
cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
|
||||
cmd.Dir = tmpdir
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Errorf("linking with -strictdups=2 did not fail")
|
||||
}
|
||||
if !bytes.Contains(out, []byte("mismatched payload")) {
|
||||
t.Errorf("unexpected output:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ TEXT runtime∕internal∕atomic·Cas(SB),NOSPLIT|NOFRAME,$0
|
|||
CMP $7, R11
|
||||
BLT 2(PC)
|
||||
JMP ·armcas(SB)
|
||||
JMP ·kernelcas<>(SB)
|
||||
JMP kernelcas<>(SB)
|
||||
|
||||
TEXT runtime∕internal∕atomic·kernelcas<>(SB),NOSPLIT,$0
|
||||
TEXT kernelcas<>(SB),NOSPLIT,$0
|
||||
MOVW ptr+0(FP), R2
|
||||
// trigger potential paging fault here,
|
||||
// because we don't know how to traceback through __kuser_cmpxchg
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue