"bytes"
"cmd/internal/objabi"
"cmd/internal/sys"
- "cmd/link/internal/objfile"
+ "cmd/link/internal/loader"
"cmd/link/internal/sym"
"fmt"
"strings"
// - Debug output:
// Emit messages about which symbols are kept or deleted.
-type workQueue []objfile.Sym
+type workQueue []loader.Sym
-func (q *workQueue) push(i objfile.Sym) { *q = append(*q, i) }
-func (q *workQueue) pop() objfile.Sym { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
-func (q *workQueue) empty() bool { return len(*q) == 0 }
+func (q *workQueue) push(i loader.Sym) { *q = append(*q, i) }
+func (q *workQueue) pop() loader.Sym { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i }
+func (q *workQueue) empty() bool { return len(*q) == 0 }
type deadcodePass2 struct {
- ctxt *Link
- loader *objfile.Loader
- wq workQueue
+ ctxt *Link
+ ldr *loader.Loader
+ wq workQueue
ifaceMethod map[methodsig]bool // methods declared in reached interfaces
markableMethods []methodref2 // methods of reached types
}
func (d *deadcodePass2) init() {
- d.loader.InitReachable()
+ d.ldr.InitReachable()
d.ifaceMethod = make(map[methodsig]bool)
var names []string
// We don't keep the go.plugin.exports symbol,
// but we do keep the symbols it refers to.
- exportsIdx := d.loader.Lookup("go.plugin.exports", 0)
+ exportsIdx := d.ldr.Lookup("go.plugin.exports", 0)
if exportsIdx != 0 {
- relocs := d.loader.Relocs(exportsIdx)
+ relocs := d.ldr.Relocs(exportsIdx)
for i := 0; i < relocs.Count; i++ {
d.mark(relocs.At(i).Sym)
}
}
}
}
+
dynexpMap := d.ctxt.cgo_export_dynamic
if d.ctxt.LinkMode == LinkExternal {
dynexpMap = d.ctxt.cgo_export_static
for _, name := range names {
// Mark symbol as an data/ABI0 symbol.
- d.mark(d.loader.Lookup(name, 0))
+ d.mark(d.ldr.Lookup(name, 0))
// Also mark any Go functions (internal ABI).
- d.mark(d.loader.Lookup(name, sym.SymVerABIInternal))
+ d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal))
}
}
for !d.wq.empty() {
symIdx := d.wq.pop()
- d.reflectSeen = d.reflectSeen || d.loader.IsReflectMethod(symIdx)
+ d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
- name := d.loader.RawSymName(symIdx)
+ name := d.ldr.RawSymName(symIdx)
if strings.HasPrefix(name, "type.") && name[5] != '.' { // TODO: use an attribute instead of checking name
- p := d.loader.Data(symIdx)
+ p := d.ldr.Data(symIdx)
if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface {
- for _, sig := range decodeIfaceMethods2(d.loader, d.ctxt.Arch, symIdx) {
+ for _, sig := range decodeIfaceMethods2(d.ldr, d.ctxt.Arch, symIdx) {
if d.ctxt.Debugvlog > 1 {
d.ctxt.Logf("reached iface method: %s\n", sig)
}
}
var methods []methodref2
- relocs := d.loader.Relocs(symIdx)
+ relocs := d.ldr.Relocs(symIdx)
for i := 0; i < relocs.Count; i++ {
r := relocs.At(i)
if r.Type == objabi.R_WEAKADDROFF {
}
d.mark(r.Sym)
}
- naux := d.loader.NAux(symIdx)
+ naux := d.ldr.NAux(symIdx)
for i := 0; i < naux; i++ {
- d.mark(d.loader.AuxSym(symIdx, i))
+ d.mark(d.ldr.AuxSym(symIdx, i))
}
if len(methods) != 0 {
// Decode runtime type information for type methods
// to help work out which methods can be called
// dynamically via interfaces.
- methodsigs := decodetypeMethods2(d.loader, d.ctxt.Arch, symIdx)
+ methodsigs := decodetypeMethods2(d.ldr, d.ctxt.Arch, symIdx)
if len(methods) != len(methodsigs) {
- panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.loader.SymName(symIdx), 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
}
}
-func (d *deadcodePass2) mark(symIdx objfile.Sym) {
- if symIdx != 0 && !d.loader.Reachable.Has(symIdx) {
+func (d *deadcodePass2) mark(symIdx loader.Sym) {
+ if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) {
d.wq.push(symIdx)
- d.loader.Reachable.Set(symIdx)
+ d.ldr.Reachable.Set(symIdx)
}
}
func (d *deadcodePass2) markMethod(m methodref2) {
- relocs := d.loader.Relocs(m.src)
+ relocs := d.ldr.Relocs(m.src)
d.mark(relocs.At(m.r).Sym)
d.mark(relocs.At(m.r + 1).Sym)
d.mark(relocs.At(m.r + 2).Sym)
}
func deadcode2(ctxt *Link) {
- loader := ctxt.loader
- d := deadcodePass2{ctxt: ctxt, loader: loader}
+ ldr := ctxt.loader
+ d := deadcodePass2{ctxt: ctxt, ldr: ldr}
d.init()
d.flood()
- callSym := loader.Lookup("reflect.Value.Call", sym.SymVerABIInternal)
- methSym := loader.Lookup("reflect.Value.Method", sym.SymVerABIInternal)
-
+ 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.
// 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 && loader.Reachable.Has(callSym)) || (methSym != 0 && loader.Reachable.Has(methSym))
+ 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
d.flood()
}
- n := loader.NSym()
+ 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 := objfile.Sym(i)
- if strings.HasPrefix(loader.RawSymName(s), "go.itablink.") { // TODO: use an attribute instread of checking name
- relocs := loader.Relocs(s)
- if relocs.Count > 0 && loader.Reachable.Has(relocs.At(0).Sym) {
- loader.Reachable.Set(s)
+ s := loader.Sym(i)
+ if strings.HasPrefix(ldr.RawSymName(s), "go.itablink.") { // TODO: use an attribute instread of checking name
+ relocs := ldr.Relocs(s)
+ if relocs.Count > 0 && ldr.Reachable.Has(relocs.At(0).Sym) {
+ ldr.Reachable.Set(s)
}
}
}
// the reflect.method struct: mtyp, ifn, and tfn.
type methodref2 struct {
m methodsig
- src objfile.Sym // receiver type symbol
- r int // the index of R_METHODOFF relocations
+ src loader.Sym // receiver type symbol
+ r int // the index of R_METHODOFF relocations
}
func (m methodref2) isExported() bool {
// the function type.
//
// Conveniently this is the layout of both runtime.method and runtime.imethod.
-func decodeMethodSig2(loader *objfile.Loader, arch *sys.Arch, symIdx objfile.Sym, off, size, count int) []methodsig {
+func decodeMethodSig2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off, size, count int) []methodsig {
var buf bytes.Buffer
var methods []methodsig
for i := 0; i < count; i++ {
- buf.WriteString(decodetypeName2(loader, symIdx, off))
- mtypSym := decodeRelocSym2(loader, symIdx, int32(off+4))
- mp := loader.Data(mtypSym)
+ buf.WriteString(decodetypeName2(ldr, symIdx, off))
+ mtypSym := decodeRelocSym2(ldr, symIdx, int32(off+4))
+ mp := ldr.Data(mtypSym)
buf.WriteRune('(')
inCount := decodetypeFuncInCount(arch, mp)
if i > 0 {
buf.WriteString(", ")
}
- a := decodetypeFuncInType2(loader, arch, mtypSym, i)
- buf.WriteString(loader.SymName(a))
+ a := decodetypeFuncInType2(ldr, arch, mtypSym, i)
+ buf.WriteString(ldr.SymName(a))
}
buf.WriteString(") (")
outCount := decodetypeFuncOutCount(arch, mp)
if i > 0 {
buf.WriteString(", ")
}
- a := decodetypeFuncOutType2(loader, arch, mtypSym, i)
- buf.WriteString(loader.SymName(a))
+ a := decodetypeFuncOutType2(ldr, arch, mtypSym, i)
+ buf.WriteString(ldr.SymName(a))
}
buf.WriteRune(')')
return methods
}
-func decodeIfaceMethods2(loader *objfile.Loader, arch *sys.Arch, symIdx objfile.Sym) []methodsig {
- p := loader.Data(symIdx)
+func decodeIfaceMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) []methodsig {
+ p := ldr.Data(symIdx)
if decodetypeKind(arch, p)&kindMask != kindInterface {
- panic(fmt.Sprintf("symbol %q is not an interface", loader.SymName(symIdx)))
+ panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
}
- rel := decodeReloc2(loader, symIdx, int32(commonsize(arch)+arch.PtrSize))
+ rel := decodeReloc2(ldr, symIdx, 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", loader.SymName(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 decodeMethodSig2(loader, arch, symIdx, off, sizeofIMethod, numMethods)
+ return decodeMethodSig2(ldr, arch, symIdx, off, sizeofIMethod, numMethods)
}
-func decodetypeMethods2(loader *objfile.Loader, arch *sys.Arch, symIdx objfile.Sym) []methodsig {
- p := loader.Data(symIdx)
+func decodetypeMethods2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) []methodsig {
+ p := ldr.Data(symIdx)
if !decodetypeHasUncommon(arch, p) {
- panic(fmt.Sprintf("no methods on %q", loader.SymName(symIdx)))
+ panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
}
off := commonsize(arch) // reflect.rtype
switch decodetypeKind(arch, p) & kindMask {
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 decodeMethodSig2(loader, arch, symIdx, off, sizeofMethod, mcount)
+ return decodeMethodSig2(ldr, arch, symIdx, off, sizeofMethod, mcount)
}
-func decodeReloc2(loader *objfile.Loader, symIdx objfile.Sym, off int32) objfile.Reloc {
- relocs := loader.Relocs(symIdx)
+func decodeReloc2(ldr *loader.Loader, symIdx loader.Sym, off int32) loader.Reloc {
+ relocs := ldr.Relocs(symIdx)
for j := 0; j < relocs.Count; j++ {
rel := relocs.At(j)
if rel.Off == off {
return rel
}
}
- return objfile.Reloc{}
+ return loader.Reloc{}
}
-func decodeRelocSym2(loader *objfile.Loader, symIdx objfile.Sym, off int32) objfile.Sym {
- return decodeReloc2(loader, symIdx, off).Sym
+func decodeRelocSym2(ldr *loader.Loader, symIdx loader.Sym, off int32) loader.Sym {
+ return decodeReloc2(ldr, symIdx, off).Sym
}
// decodetypeName2 decodes the name from a reflect.name.
-func decodetypeName2(loader *objfile.Loader, symIdx objfile.Sym, off int) string {
- r := decodeRelocSym2(loader, symIdx, int32(off))
+func decodetypeName2(ldr *loader.Loader, symIdx loader.Sym, off int) string {
+ r := decodeRelocSym2(ldr, symIdx, int32(off))
if r == 0 {
return ""
}
- data := loader.Data(r)
+ data := ldr.Data(r)
namelen := int(uint16(data[1])<<8 | uint16(data[2]))
return string(data[3 : 3+namelen])
}
-func decodetypeFuncInType2(loader *objfile.Loader, arch *sys.Arch, symIdx objfile.Sym, i int) objfile.Sym {
+func decodetypeFuncInType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym {
uadd := commonsize(arch) + 4
if arch.PtrSize == 8 {
uadd += 4
}
- if decodetypeHasUncommon(arch, loader.Data(symIdx)) {
+ if decodetypeHasUncommon(arch, ldr.Data(symIdx)) {
uadd += uncommonSize()
}
- return decodeRelocSym2(loader, symIdx, int32(uadd+i*arch.PtrSize))
+ return decodeRelocSym2(ldr, symIdx, int32(uadd+i*arch.PtrSize))
}
-func decodetypeFuncOutType2(loader *objfile.Loader, arch *sys.Arch, symIdx objfile.Sym, i int) objfile.Sym {
- return decodetypeFuncInType2(loader, arch, symIdx, i+decodetypeFuncInCount(arch, loader.Data(symIdx)))
+func decodetypeFuncOutType2(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) loader.Sym {
+ return decodetypeFuncInType2(ldr, arch, symIdx, i+decodetypeFuncInCount(arch, ldr.Data(symIdx)))
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package objfile
+package loader
import (
"bytes"
}
// A Loader loads new object files and resolves indexed symbol references.
-//
-// TODO: describe local-global index mapping.
type Loader struct {
start map[*oReader]Sym // map from object file to its start index
objs []objIdx // sorted by start index (i.e. objIdx.i)
}
// Return the start index in the global index space for a given object file.
-func (l *Loader) StartIndex(r *oReader) Sym {
+func (l *Loader) startIndex(r *oReader) Sym {
return l.start[r]
}
// Add object file r, return the start index.
-func (l *Loader) AddObj(pkg string, r *oReader) Sym {
+func (l *Loader) addObj(pkg string, r *oReader) Sym {
if _, ok := l.start[r]; ok {
panic("already added")
}
if dupok {
return false
}
- overwrite := r.DataSize(int(i-l.StartIndex(r))) != 0
+ overwrite := r.DataSize(int(i-l.startIndex(r))) != 0
if overwrite {
// new symbol overwrites old symbol.
- oldr, li := l.ToLocal(oldi)
+ oldr, li := l.toLocal(oldi)
oldsym := goobj2.Sym{}
oldsym.Read(oldr.Reader, oldr.SymOff(li))
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type)]
}
// Convert a local index to a global index.
-func (l *Loader) ToGlobal(r *oReader, i int) Sym {
- g := l.StartIndex(r) + Sym(i)
+func (l *Loader) toGlobal(r *oReader, i int) Sym {
+ g := l.startIndex(r) + Sym(i)
if ov, ok := l.overwrite[g]; ok {
return ov
}
}
// Convert a global index to a local index.
-func (l *Loader) ToLocal(i Sym) (*oReader, int) {
+func (l *Loader) toLocal(i Sym) (*oReader, int) {
if ov, ok := l.overwrite[i]; ok {
i = ov
}
}
// Resolve a local symbol reference. Return global index.
-func (l *Loader) Resolve(r *oReader, s goobj2.SymRef) Sym {
+func (l *Loader) resolve(r *oReader, s goobj2.SymRef) Sym {
var rr *oReader
switch p := s.PkgIdx; p {
case goobj2.PkgIdxInvalid:
log.Fatalf("reference of nonexisted package %s, from %v", pkg, r.unit.Lib)
}
}
- return l.ToGlobal(rr, int(s.SymIdx))
+ return l.toGlobal(rr, int(s.SymIdx))
}
// Look up a symbol by name, return global index, or 0 if not found.
if l.extStart != 0 && i >= l.extStart {
return ""
}
- r, li := l.ToLocal(i)
+ r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return osym.Name
if l.extStart != 0 && i >= l.extStart {
return ""
}
- r, li := l.ToLocal(i)
+ r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1)
if l.extStart != 0 && i >= l.extStart {
return 0
}
- r, li := l.ToLocal(i)
+ r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)]
if l.extStart != 0 && i >= l.extStart {
return 0
}
- r, li := l.ToLocal(i)
+ r, li := l.toLocal(i)
osym := goobj2.Sym{}
osym.Read(r.Reader, r.SymOff(li))
return osym.Flag
if l.extStart != 0 && i >= l.extStart {
return nil
}
- r, li := l.ToLocal(i)
+ r, li := l.toLocal(i)
return r.Data(li)
}
if l.extStart != 0 && i >= l.extStart {
return 0
}
- r, li := l.ToLocal(i)
+ r, li := l.toLocal(i)
return r.NAux(li)
}
if l.extStart != 0 && i >= l.extStart {
return 0
}
- r, li := l.ToLocal(i)
+ r, li := l.toLocal(i)
a := goobj2.Aux{}
a.Read(r.Reader, r.AuxOff(li, j))
- return l.Resolve(r, a.Sym)
+ return l.resolve(r, a.Sym)
}
// Initialize Reachable bitmap for running deadcode pass.
func (relocs *Relocs) At(j int) Reloc {
rel := goobj2.Reloc{}
rel.Read(relocs.r.Reader, relocs.r.RelocOff(relocs.li, j))
- target := relocs.l.Resolve(relocs.r, rel.Sym)
+ target := relocs.l.resolve(relocs.r, rel.Sym)
return Reloc{
Off: rel.Off,
Size: rel.Siz,
if l.extStart != 0 && i >= l.extStart {
return Relocs{}
}
- r, li := l.ToLocal(i)
+ r, li := l.toLocal(i)
return l.relocs(r, li)
}
// Preload a package: add autolibs, add symbols to the symbol table.
// Does not read symbol data yet.
-func LoadNew(l *Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
+func (l *Loader) Preload(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
roObject, readonly, err := f.Slice(uint64(length))
if err != nil {
log.Fatal("cannot read object file:", err)
unit.DWARFFileTable[i] = r.DwarfFile(i)
}
- istart := l.AddObj(lib.Pkg, or)
+ istart := l.addObj(lib.Pkg, or)
ndef := r.NSym()
nnonpkgdef := r.NNonpkgdef()
// Make sure referenced symbols are added. Most of them should already be added.
// This should only be needed for referenced external symbols.
-func LoadRefs(l *Loader, arch *sys.Arch, syms *sym.Symbols) {
+func (l *Loader) LoadRefs(arch *sys.Arch, syms *sym.Symbols) {
for _, o := range l.objs[1:] {
loadObjRefs(l, o.r, arch, syms)
}
}
// Load full contents.
-func LoadFull(l *Loader, arch *sys.Arch, syms *sym.Symbols) {
+func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
// create all Symbols first.
l.Syms = make([]*sym.Symbol, l.NSym())
for _, o := range l.objs[1:] {
func loadObjSyms(l *Loader, syms *sym.Symbols, r *oReader) {
lib := r.unit.Lib
- istart := l.StartIndex(r)
+ istart := l.startIndex(r)
for i, n := 0, r.NSym()+r.NNonpkgdef(); i < n; i++ {
osym := goobj2.Sym{}
func loadObjFull(l *Loader, r *oReader) {
lib := r.unit.Lib
- istart := l.StartIndex(r)
+ istart := l.startIndex(r)
resolveSymRef := func(s goobj2.SymRef) *sym.Symbol {
- i := l.Resolve(r, s)
+ i := l.resolve(r, s)
return l.Syms[i]
}
Parent: inl.Parent,
File: resolveSymRef(inl.File),
Line: inl.Line,
- Func: l.SymName(l.Resolve(r, inl.Func)),
+ Func: l.SymName(l.resolve(r, inl.Func)),
ParentPC: inl.ParentPC,
}
}
}
}
+var emptyPkg = []byte(`"".`)
+
func patchDWARFName(s *sym.Symbol, r *oReader) {
// This is kind of ugly. Really the package name should not
// even be included here.