Newer
Older
panic("need to call Preload first")
}
return (*goobj.FuncInfo)(nil).ReadFile(fi.data, fi.lengths.FileOff, uint32(k))
}
// TopFrame returns true if the function associated with this FuncInfo
// is an entry point, meaning that unwinders should stop when they hit
// this function.
func (fi *FuncInfo) TopFrame() bool {
return (fi.FuncFlag() & objabi.FuncFlag_TOPFRAME) != 0
}
type InlTreeNode struct {
Parent int32
Line int32
Func Sym
ParentPC int32
}
func (fi *FuncInfo) NumInlTree() uint32 {
if !fi.lengths.Initialized {
panic("need to call Preload first")
}
return fi.lengths.NumInlTree
}
func (fi *FuncInfo) InlTree(k int) InlTreeNode {
if !fi.lengths.Initialized {
panic("need to call Preload first")
}
node := (*goobj.FuncInfo)(nil).ReadInlTree(fi.data, fi.lengths.InlTreeOff, uint32(k))
return InlTreeNode{
Parent: node.Parent,
Line: node.Line,
Func: fi.l.resolve(fi.r, node.Func),
ParentPC: node.ParentPC,
}
}
Cherry Zhang
committed
func (l *Loader) FuncInfo(i Sym) FuncInfo {
Cherry Zhang
committed
for j := range auxs {
a := &auxs[j]
if a.Type() == goobj.AuxFuncInfo {
Cherry Zhang
committed
b := r.Data(a.Sym().SymIdx)
return FuncInfo{l, r, b, goobj.FuncInfoLengths{}}
Cherry Zhang
committed
}
}
return FuncInfo{}
}
// Preload a package: adds autolib.
// Does not add defined package or non-packaged symbols to the symbol table.
// These are done in LoadSyms.
Cherry Zhang
committed
// Does not read symbol data.
// Returns the fingerprint of the object.
func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj.FingerprintType {
roObject, readonly, err := f.Slice(uint64(length)) // TODO: no need to map blocks that are for tools only (e.g. RefName)
if err != nil {
log.Fatal("cannot read object file:", err)
}
r := goobj.NewReaderFromBytes(roObject, readonly)
if len(roObject) >= 8 && bytes.Equal(roObject[:8], []byte("\x00go114ld")) {
log.Fatalf("found object file %s in old format", f.File().Name())
panic("cannot read object file")
}
pkgprefix := objabi.PathToPrefix(lib.Pkg) + "."
Cherry Zhang
committed
nhashed64def := r.NHashed64def()
nhasheddef := r.NHasheddef()
or := &oReader{
Cherry Zhang
committed
Reader: r,
unit: unit,
version: localSymVersion,
flags: r.Flags(),
pkgprefix: pkgprefix,
syms: make([]Sym, ndef+nhashed64def+nhasheddef+r.NNonpkgdef()+r.NNonpkgref()),
ndef: ndef,
nhasheddef: nhasheddef,
nhashed64def: nhashed64def,
objidx: uint32(len(l.objs)),
}
lib.Autolib = append(lib.Autolib, r.Autolib()...)
Cherry Zhang
committed
// DWARF file table
nfile := r.NFile()
unit.FileTable = make([]string, nfile)
for i := range unit.FileTable {
unit.FileTable[i] = r.File(i)
Cherry Zhang
committed
}
Cherry Zhang
committed
l.addObj(lib.Pkg, or)
// The caller expects us consuming all the data
f.MustSeek(length, os.SEEK_CUR)
return r.Fingerprint()
Cherry Zhang
committed
}
// Holds the loader along with temporary states for loading symbols.
type loadState struct {
l *Loader
hashed64Syms map[uint64]symAndSize // short hashed (content-addressable) symbols, keyed by content hash
hashedSyms map[goobj.HashType]symAndSize // hashed (content-addressable) symbols, keyed by content hash
Cherry Zhang
committed
// Preload symbols of given kind from an object.
func (st *loadState) preloadSyms(r *oReader, kind int) {
l := st.l
Cherry Zhang
committed
var start, end uint32
Cherry Zhang
committed
switch kind {
case pkgDef:
start = 0
end = uint32(r.ndef)
Cherry Zhang
committed
case hashed64Def:
start = uint32(r.ndef)
Cherry Zhang
committed
end = uint32(r.ndef + r.nhashed64def)
case hashedDef:
start = uint32(r.ndef + r.nhashed64def)
end = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
if l.hasUnknownPkgPath {
// The content hash depends on symbol name expansion. If any package is
// built without fully expanded names, the content hash is unreliable.
// Treat them as named symbols.
// This is rare.
// (We don't need to do this for hashed64Def case, as there the hash
// function is simply the identity function, which doesn't depend on
// name expansion.)
kind = nonPkgDef
}
Cherry Zhang
committed
case nonPkgDef:
Cherry Zhang
committed
start = uint32(r.ndef + r.nhashed64def + r.nhasheddef)
end = uint32(r.ndef + r.nhashed64def + r.nhasheddef + r.NNonpkgdef())
Cherry Zhang
committed
default:
panic("preloadSyms: bad kind")
}
Cherry Zhang
committed
l.growAttrBitmaps(len(l.objSyms) + int(end-start))
needNameExpansion := r.NeedNameExpansion()
loadingRuntimePkg := r.unit.Lib.Pkg == "runtime"
Cherry Zhang
committed
for i := start; i < end; i++ {
var name string
var v int
Cherry Zhang
committed
if kind != hashed64Def && kind != hashedDef { // we don't need the name, etc. for hashed symbols
name = osym.Name(r.Reader)
if needNameExpansion {
name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
}
v = abiToVer(osym.ABI(), r.version)
gi := st.addSym(name, v, r, i, kind, osym)
Cherry Zhang
committed
r.syms[i] = gi
if osym.Local() {
l.SetAttrLocal(gi, true)
}
Cherry Zhang
committed
if osym.UsedInIface() {
l.SetAttrUsedInIface(gi, true)
}
if strings.HasPrefix(name, "runtime.") ||
(loadingRuntimePkg && strings.HasPrefix(name, "type.")) {
if bi := goobj.BuiltinIdx(name, int(osym.ABI())); bi != -1 {
// This is a definition of a builtin symbol. Record where it is.
l.builtinSyms[bi] = gi
}
}
if a := int32(osym.Align()); a != 0 && a > l.SymAlign(gi) {
l.SetSymAlign(gi, a)
// Add syms, hashed (content-addressable) symbols, non-package symbols, and
// references to external symbols (which are always named).
func (l *Loader) LoadSyms(arch *sys.Arch) {
// Allocate space for symbols, making a guess as to how much space we need.
// This function was determined empirically by looking at the cmd/compile on
// Darwin, and picking factors for hashed and hashed64 syms.
var symSize, hashedSize, hashed64Size int
for _, o := range l.objs[goObjStart:] {
symSize += o.r.ndef + o.r.nhasheddef/2 + o.r.nhashed64def/2 + o.r.NNonpkgdef()
hashedSize += o.r.nhasheddef / 2
hashed64Size += o.r.nhashed64def / 2
}
// Index 0 is invalid for symbols.
l.objSyms = make([]objSym, 1, symSize)
st := loadState{
l: l,
hashed64Syms: make(map[uint64]symAndSize, hashed64Size),
hashedSyms: make(map[goobj.HashType]symAndSize, hashedSize),
}
for _, o := range l.objs[goObjStart:] {
st.preloadSyms(o.r, pkgDef)
Cherry Zhang
committed
for _, o := range l.objs[goObjStart:] {
st.preloadSyms(o.r, hashed64Def)
st.preloadSyms(o.r, hashedDef)
st.preloadSyms(o.r, nonPkgDef)
Cherry Zhang
committed
}
l.nhashedsyms = len(st.hashed64Syms) + len(st.hashedSyms)
Cherry Zhang
committed
for _, o := range l.objs[goObjStart:] {
loadObjRefs(l, o.r, arch)
Cherry Zhang
committed
}
l.values = make([]int64, l.NSym(), l.NSym()+1000) // +1000 make some room for external symbols
Cherry Zhang
committed
}
func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) {
Cherry Zhang
committed
// load non-package refs
ndef := uint32(r.NAlldef())
needNameExpansion := r.NeedNameExpansion()
Cherry Zhang
committed
for i, n := uint32(0), uint32(r.NNonpkgref()); i < n; i++ {
name := osym.Name(r.Reader)
if needNameExpansion {
name = strings.Replace(name, "\"\".", r.pkgprefix, -1)
}
v := abiToVer(osym.ABI(), r.version)
r.syms[ndef+i] = l.LookupOrCreateSym(name, v)
gi := r.syms[ndef+i]
if osym.Local() {
l.SetAttrLocal(gi, true)
}
Cherry Zhang
committed
if osym.UsedInIface() {
l.SetAttrUsedInIface(gi, true)
}
Cherry Zhang
committed
// referenced packages
npkg := r.NPkg()
r.pkg = make([]uint32, npkg)
for i := 1; i < npkg; i++ { // PkgIdx 0 is a dummy invalid package
pkg := r.Pkg(i)
objidx, ok := l.objByPkg[pkg]
if !ok {
log.Fatalf("%v: reference to nonexistent package %s", r.unit.Lib, pkg)
}
r.pkg[i] = objidx
}
Cherry Zhang
committed
// load flags of package refs
for i, n := 0, r.NRefFlags(); i < n; i++ {
rf := r.RefFlags(i)
gi := l.resolve(r, rf.Sym())
if rf.Flag2()&goobj.SymFlagUsedInIface != 0 {
Cherry Zhang
committed
l.SetAttrUsedInIface(gi, true)
}
}
}
func abiToVer(abi uint16, localSymVersion int) int {
var v int
if abi == goobj.SymABIstatic {
// Static
v = localSymVersion
} else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
// Note that data symbols are "ABI0", which maps to version 0.
v = abiver
} else {
log.Fatalf("invalid symbol ABI: %d", abi)
}
return v
}
// TopLevelSym tests a symbol (by name and kind) to determine whether
// the symbol first class sym (participating in the link) or is an
// anonymous aux or sub-symbol containing some sub-part or payload of
// another symbol.
func (l *Loader) TopLevelSym(s Sym) bool {
return topLevelSym(l.RawSymName(s), l.SymType(s))
}
// topLevelSym tests a symbol name and kind to determine whether
// the symbol first class sym (participating in the link) or is an
// anonymous aux or sub-symbol containing some sub-part or payload of
// another symbol.
func topLevelSym(sname string, skind sym.SymKind) bool {
if sname != "" {
return true
}
switch skind {
case sym.SDWARFFCN, sym.SDWARFABSFCN, sym.SDWARFTYPE, sym.SDWARFCONST, sym.SDWARFCUINFO, sym.SDWARFRANGE, sym.SDWARFLOC, sym.SDWARFLINES, sym.SGOFUNC:
return true
default:
return false
}
}
// cloneToExternal takes the existing object file symbol (symIdx)
// and creates a new external symbol payload that is a clone with
// respect to name, version, type, relocations, etc. The idea here
// is that if the linker decides it wants to update the contents of
// a symbol originally discovered as part of an object file, it's
// easier to do this if we make the updates to an external symbol
// payload.
func (l *Loader) cloneToExternal(symIdx Sym) {
if l.IsExternal(symIdx) {
panic("sym is already external, no need for clone")
}
// Read the particulars from object.
r, li := l.toLocal(symIdx)
sname := osym.Name(r.Reader)
if r.NeedNameExpansion() {
sname = strings.Replace(sname, "\"\".", r.pkgprefix, -1)
}
sver := abiToVer(osym.ABI(), r.version)
skind := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
// Create new symbol, update version and kind.
pi := l.newPayload(sname, sver)
pp := l.payloads[pi]
pp.kind = skind
pp.ver = sver
pp.size = int64(osym.Siz())
pp.objidx = r.objidx
// If this is a def, then copy the guts. We expect this case
// to be very rare (one case it may come up is with -X).
if li < uint32(r.NAlldef()) {
// Copy relocations
relocs := l.Relocs(symIdx)
pp.relocs = make([]goobj.Reloc, relocs.Count())
for i := range pp.relocs {
// Copy the relocs slice.
// Convert local reference to global reference.
pp.relocs[i].Set(rel.Off(), rel.Siz(), uint16(rel.Type()), rel.Add(), goobj.SymRef{PkgIdx: 0, SymIdx: uint32(rel.Sym())})
// Copy data
pp.data = r.Data(li)
}
// If we're overriding a data symbol, collect the associated
// Gotype, so as to propagate it to the new symbol.
pp.auxs = auxs
// Install new payload to global index space.
// (This needs to happen at the end, as the accessors above
// need to access the old symbol content.)
Cherry Zhang
committed
l.objSyms[symIdx] = objSym{l.extReader.objidx, uint32(pi)}
l.extReader.syms = append(l.extReader.syms, symIdx)
}
// Copy the payload of symbol src to dst. Both src and dst must be external
// symbols.
// The intended use case is that when building/linking against a shared library,
// where we do symbol name mangling, the Go object file may have reference to
// the original symbol name whereas the shared library provides a symbol with
// the mangled name. When we do mangling, we copy payload of mangled to original.
func (l *Loader) CopySym(src, dst Sym) {
if !l.IsExternal(dst) {
panic("dst is not external") //l.newExtSym(l.SymName(dst), l.SymVersion(dst))
}
if !l.IsExternal(src) {
panic("src is not external") //l.cloneToExternal(src)
}
l.payloads[l.extIndex(dst)] = l.payloads[l.extIndex(src)]
l.SetSymPkg(dst, l.SymPkg(src))
// TODO: other attributes?
}
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
// CopyAttributes copies over all of the attributes of symbol 'src' to
// symbol 'dst'.
func (l *Loader) CopyAttributes(src Sym, dst Sym) {
l.SetAttrReachable(dst, l.AttrReachable(src))
l.SetAttrOnList(dst, l.AttrOnList(src))
l.SetAttrLocal(dst, l.AttrLocal(src))
l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src))
if l.IsExternal(dst) {
l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src))
l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src))
l.SetAttrShared(dst, l.AttrShared(src))
l.SetAttrExternal(dst, l.AttrExternal(src))
} else {
// Some attributes are modifiable only for external symbols.
// In such cases, don't try to transfer over the attribute
// from the source even if there is a clash. This comes up
// when copying attributes from a dupOK ABI wrapper symbol to
// the real target symbol (which may not be marked dupOK).
}
l.SetAttrSpecial(dst, l.AttrSpecial(src))
l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src))
l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src))
l.SetAttrReadOnly(dst, l.AttrReadOnly(src))
}
Than McIntosh
committed
// CreateExtSym creates a new external symbol with the specified name
// without adding it to any lookup tables, returning a Sym index for it.
func (l *Loader) CreateExtSym(name string, ver int) Sym {
return l.newExtSym(name, ver)
}
// CreateStaticSym creates a new static symbol with the specified name
// without adding it to any lookup tables, returning a Sym index for it.
func (l *Loader) CreateStaticSym(name string) Sym {
// Assign a new unique negative version -- this is to mark the
// symbol so that it is not included in the name lookup table.
l.anonVersion--
return l.newExtSym(name, l.anonVersion)
Than McIntosh
committed
}
func (l *Loader) FreeSym(i Sym) {
if l.IsExternal(i) {
pp := l.getPayload(i)
*pp = extSymPayload{}
}
}
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
// relocId is essentially a <S,R> tuple identifying the Rth
// relocation of symbol S.
type relocId struct {
sym Sym
ridx int
}
// SetRelocVariant sets the 'variant' property of a relocation on
// some specific symbol.
func (l *Loader) SetRelocVariant(s Sym, ri int, v sym.RelocVariant) {
// sanity check
if relocs := l.Relocs(s); ri >= relocs.Count() {
panic("invalid relocation ID")
}
if l.relocVariant == nil {
l.relocVariant = make(map[relocId]sym.RelocVariant)
}
if v != 0 {
l.relocVariant[relocId{s, ri}] = v
} else {
delete(l.relocVariant, relocId{s, ri})
}
}
// RelocVariant returns the 'variant' property of a relocation on
// some specific symbol.
func (l *Loader) RelocVariant(s Sym, ri int) sym.RelocVariant {
return l.relocVariant[relocId{s, ri}]
}
// UndefinedRelocTargets iterates through the global symbol index
// space, looking for symbols with relocations targeting undefined
// references. The linker's loadlib method uses this to determine if
// there are unresolved references to functions in system libraries
// (for example, libgcc.a), presumably due to CGO code. Return
// value is a list of loader.Sym's corresponding to the undefined
// cross-refs. The "limit" param controls the maximum number of
// results returned; if "limit" is -1, then all undefs are returned.
func (l *Loader) UndefinedRelocTargets(limit int) []Sym {
result := []Sym{}
for si := Sym(1); si < Sym(len(l.objSyms)); si++ {
relocs := l.Relocs(si)
for ri := 0; ri < relocs.Count(); ri++ {
rs := r.Sym()
if rs != 0 && l.SymType(rs) == sym.SXREF && l.RawSymName(rs) != ".got" {
result = append(result, rs)
if limit != -1 && len(result) >= limit {
break
}
}
}
}
return result
}
// AssignTextSymbolOrder populates the Textp slices within each
// library and compilation unit, insuring that packages are laid down
// in dependency order (internal first, then everything else). Return value
// is a slice of all text syms.
func (l *Loader) AssignTextSymbolOrder(libs []*sym.Library, intlibs []bool, extsyms []Sym) []Sym {
// Library Textp lists should be empty at this point.
for _, lib := range libs {
if len(lib.Textp) != 0 {
panic("expected empty Textp slice for library")
if len(lib.DupTextSyms) != 0 {
panic("expected empty DupTextSyms slice for library")
}
}
// Used to record which dupok symbol we've assigned to a unit.
// Can't use the onlist attribute here because it will need to
// clear for the later assignment of the sym.Symbol to a unit.
// NB: we can convert to using onList once we no longer have to
// call the regular addToTextp.
assignedToUnit := MakeBitmap(l.NSym() + 1)
// Start off textp with reachable external syms.
textp := []Sym{}
for _, sym := range extsyms {
if !l.attrReachable.Has(sym) {
continue
}
}
// Walk through all text symbols from Go object files and append
// them to their corresponding library's textp list.
Cherry Zhang
committed
for _, o := range l.objs[goObjStart:] {
r := o.r
lib := r.unit.Lib
for i, n := uint32(0), uint32(r.NAlldef()); i < n; i++ {
gi := l.toGlobal(r, i)
if !l.attrReachable.Has(gi) {
continue
}
st := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
if st != sym.STEXT {
continue
}
dupok := osym.Dupok()
if r2, i2 := l.toLocal(gi); r2 != r || i2 != i {
// A dupok text symbol is resolved to another package.
// We still need to record its presence in the current
// package, as the trampoline pass expects packages
// are laid out in dependency order.
lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
continue // symbol in different object
}
if dupok {
lib.DupTextSyms = append(lib.DupTextSyms, sym.LoaderSym(gi))
continue
}
lib.Textp = append(lib.Textp, sym.LoaderSym(gi))
}
}
// Now assemble global textp, and assign text symbols to units.
for _, doInternal := range [2]bool{true, false} {
for idx, lib := range libs {
if intlibs[idx] != doInternal {
continue
}
lists := [2][]sym.LoaderSym{lib.Textp, lib.DupTextSyms}
for i, list := range lists {
for _, s := range list {
sym := Sym(s)
if !assignedToUnit.Has(sym) {
unit := l.SymUnit(sym)
if unit != nil {
unit.Textp = append(unit.Textp, s)
assignedToUnit.Set(sym)
// Dupok symbols may be defined in multiple packages; the
// associated package for a dupok sym is chosen sort of
// arbitrarily (the first containing package that the linker
// loads). Canonicalizes its Pkg to the package with which
// it will be laid down in text.
if i == 1 /* DupTextSyms2 */ && l.SymPkg(sym) != lib.Pkg {
l.SetSymPkg(sym, lib.Pkg)
}
}
}
}
lib.Textp = nil
lib.DupTextSyms = nil
}
}
}
// ErrorReporter is a helper class for reporting errors.
type ErrorReporter struct {
ldr *Loader
AfterErrorAction func()
}
// Errorf method logs an error message.
//
// After each error, the error actions function will be invoked; this
// will either terminate the link immediately (if -h option given)
// or it will keep a count and exit if more than 20 errors have been printed.
//
// Logging an error means that on exit cmd/link will delete any
// output file and return a non-zero error code.
//
func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) {
if s != 0 && reporter.ldr.SymName(s) != "" {
// Note: Replace is needed here because symbol names might have % in them,
// due to the use of LinkString for names of instantiating types.
format = strings.Replace(reporter.ldr.SymName(s), "%", "%%", -1) + ": " + format
} else {
format = fmt.Sprintf("sym %d: %s", s, format)
}
format += "\n"
fmt.Fprintf(os.Stderr, format, args...)
reporter.AfterErrorAction()
}
// GetErrorReporter returns the loader's associated error reporter.
func (l *Loader) GetErrorReporter() *ErrorReporter {
return l.errorReporter
}
// Errorf method logs an error message. See ErrorReporter.Errorf for details.
func (l *Loader) Errorf(s Sym, format string, args ...interface{}) {
l.errorReporter.Errorf(s, format, args...)
// Symbol statistics.
func (l *Loader) Stat() string {
s := fmt.Sprintf("%d symbols, %d reachable\n", l.NSym(), l.NReachableSym())
s += fmt.Sprintf("\t%d package symbols, %d hashed symbols, %d non-package symbols, %d external symbols\n",
l.npkgsyms, l.nhashedsyms, int(l.extStart)-l.npkgsyms-l.nhashedsyms, l.NSym()-int(l.extStart))
return s
}
// For debugging.
func (l *Loader) Dump() {
fmt.Println("objs")
Cherry Zhang
committed
for _, obj := range l.objs[goObjStart:] {
if obj.r != nil {
fmt.Println(obj.i, obj.r.unit.Lib)
}
}
Than McIntosh
committed
fmt.Println("extStart:", l.extStart)
fmt.Println("Nsyms:", len(l.objSyms))
fmt.Println("syms")
for i := Sym(1); i < Sym(len(l.objSyms)); i++ {
if l.IsExternal(i) {
pi = fmt.Sprintf("<ext %d>", l.extIndex(i))
}
sect := ""
if l.SymSect(i) != nil {
sect = l.SymSect(i).Name
}
fmt.Printf("%v %v %v %v %x %v\n", i, l.SymName(i), l.SymType(i), pi, l.SymValue(i), sect)
}
fmt.Println("symsByName")
for name, i := range l.symsByName[0] {
fmt.Println(i, name, 0)
}
for name, i := range l.symsByName[1] {
fmt.Println(i, name, 1)
fmt.Println("payloads:")
for i := range l.payloads {
pp := l.payloads[i]
fmt.Println(i, pp.name, pp.ver, pp.kind)
}