diff options
Diffstat (limited to '')
-rw-r--r-- | src/cmd/internal/obj/objfile.go | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go new file mode 100644 index 0000000..bb58b4f --- /dev/null +++ b/src/cmd/internal/obj/objfile.go @@ -0,0 +1,861 @@ +// Copyright 2013 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/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "crypto/sha1" + "encoding/binary" + "fmt" + "io" + "log" + "os" + "path/filepath" + "sort" + "strings" +) + +// Entry point of writing new object file. +func WriteObjFile(ctxt *Link, b *bio.Writer) { + + debugAsmEmit(ctxt) + + genFuncInfoSyms(ctxt) + + w := writer{ + Writer: goobj.NewWriter(b), + ctxt: ctxt, + pkgpath: objabi.PathToPrefix(ctxt.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 |= goobj.ObjFlagShared + } + if w.pkgpath == "" { + flags |= goobj.ObjFlagNeedNameExpansion + } + if ctxt.IsAsm { + flags |= goobj.ObjFlagFromAssembly + } + h := goobj.Header{ + Magic: goobj.Magic, + Fingerprint: ctxt.Fingerprint, + Flags: flags, + } + h.Write(w.Writer) + + // String table + w.StringTable() + + // Autolib + h.Offsets[goobj.BlkAutolib] = w.Offset() + for i := range ctxt.Imports { + ctxt.Imports[i].Write(w.Writer) + } + + // Package references + h.Offsets[goobj.BlkPkgIdx] = w.Offset() + for _, pkg := range w.pkglist { + w.StringRef(pkg) + } + + // File table (for DWARF and pcln generation). + h.Offsets[goobj.BlkFile] = w.Offset() + for _, f := range ctxt.PosTable.FileTable() { + w.StringRef(filepath.ToSlash(f)) + } + + // Symbol definitions + h.Offsets[goobj.BlkSymdef] = w.Offset() + for _, s := range ctxt.defs { + w.Sym(s) + } + + // Short hashed symbol definitions + h.Offsets[goobj.BlkHashed64def] = w.Offset() + for _, s := range ctxt.hashed64defs { + w.Sym(s) + } + + // Hashed symbol definitions + h.Offsets[goobj.BlkHasheddef] = w.Offset() + for _, s := range ctxt.hasheddefs { + w.Sym(s) + } + + // Non-pkg symbol definitions + h.Offsets[goobj.BlkNonpkgdef] = w.Offset() + for _, s := range ctxt.nonpkgdefs { + w.Sym(s) + } + + // Non-pkg symbol references + h.Offsets[goobj.BlkNonpkgref] = w.Offset() + for _, s := range ctxt.nonpkgrefs { + w.Sym(s) + } + + // Referenced package symbol flags + h.Offsets[goobj.BlkRefFlags] = w.Offset() + w.refFlags() + + // Hashes + h.Offsets[goobj.BlkHash64] = w.Offset() + for _, s := range ctxt.hashed64defs { + w.Hash64(s) + } + h.Offsets[goobj.BlkHash] = w.Offset() + for _, s := range ctxt.hasheddefs { + w.Hash(s) + } + // TODO: hashedrefs unused/unsupported for now + + // Reloc indexes + h.Offsets[goobj.BlkRelocIdx] = w.Offset() + nreloc := uint32(0) + lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, 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[goobj.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[goobj.BlkDataIdx] = w.Offset() + dataOff := int64(0) + for _, list := range lists { + for _, s := range list { + w.Uint32(uint32(dataOff)) + dataOff += int64(len(s.P)) + if file := s.File(); file != nil { + dataOff += int64(file.Size) + } + } + } + if int64(uint32(dataOff)) != dataOff { + log.Fatalf("data too large") + } + w.Uint32(uint32(dataOff)) + + // Relocs + h.Offsets[goobj.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[goobj.BlkAux] = w.Offset() + for _, list := range lists { + for _, s := range list { + w.Aux(s) + } + } + + // Data + h.Offsets[goobj.BlkData] = w.Offset() + for _, list := range lists { + for _, s := range list { + w.Bytes(s.P) + if file := s.File(); file != nil { + w.writeFile(ctxt, file) + } + } + } + + // Pcdata + h.Offsets[goobj.BlkPcdata] = w.Offset() + for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms + // Because of the phase order, it's possible that we try to write an invalid + // object file, and the Pcln variables haven't been filled in. As such, we + // need to check that Pcsp exists, and assume the other pcln variables exist + // as well. Tests like test/fixedbugs/issue22200.go demonstrate this issue. + if fn := s.Func(); fn != nil && fn.Pcln.Pcsp != nil { + pc := &fn.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) + } + } + } + + // Blocks used only by tools (objdump, nm). + + // Referenced symbol names from other packages + h.Offsets[goobj.BlkRefName] = w.Offset() + w.refNames() + + h.Offsets[goobj.BlkEnd] = w.Offset() + + // 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 { + *goobj.Writer + filebuf []byte + 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) writeFile(ctxt *Link, file *FileInfo) { + f, err := os.Open(file.Name) + if err != nil { + ctxt.Diag("%v", err) + return + } + defer f.Close() + if w.filebuf == nil { + w.filebuf = make([]byte, 1024) + } + buf := w.filebuf + written := int64(0) + for { + n, err := f.Read(buf) + w.Bytes(buf[:n]) + written += int64(n) + if err == io.EOF { + break + } + if err != nil { + ctxt.Diag("%v", err) + return + } + } + if written != file.Size { + ctxt.Diag("copy %s: unexpected length %d != %d", file.Name, written, file.Size) + } +} + +func (w *writer) StringTable() { + w.AddString("") + for _, p := range w.ctxt.Imports { + w.AddString(p.Pkg) + } + for _, pkg := range w.pkglist { + w.AddString(pkg) + } + w.ctxt.traverseSyms(traverseAll, func(s *LSym) { + // TODO: this includes references of indexed symbols from other packages, + // for which the linker doesn't need the name. Consider moving them to + // a separate block (for tools only). + if w.pkgpath != "" { + s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1) + } + // Don't put names of builtins into the string table (to save + // space). + if s.PkgIdx == goobj.PkgIdxBuiltin { + return + } + w.AddString(s.Name) + }) + + // All filenames are in the postable. + for _, f := range w.ctxt.PosTable.FileTable() { + w.AddString(filepath.ToSlash(f)) + } +} + +// cutoff is the maximum data section size permitted by the linker +// (see issue #9862). +const cutoff = int64(2e9) // 2 GB (or so; looks better in errors than 2^31) + +func (w *writer) Sym(s *LSym) { + abi := uint16(s.ABI()) + if s.Static() { + abi = goobj.SymABIstatic + } + flag := uint8(0) + if s.DuplicateOK() { + flag |= goobj.SymFlagDupok + } + if s.Local() { + flag |= goobj.SymFlagLocal + } + if s.MakeTypelink() { + flag |= goobj.SymFlagTypelink + } + if s.Leaf() { + flag |= goobj.SymFlagLeaf + } + if s.NoSplit() { + flag |= goobj.SymFlagNoSplit + } + if s.ReflectMethod() { + flag |= goobj.SymFlagReflectMethod + } + if s.TopFrame() { + flag |= goobj.SymFlagTopFrame + } + if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA { + flag |= goobj.SymFlagGoType + } + flag2 := uint8(0) + if s.UsedInIface() { + flag2 |= goobj.SymFlagUsedInIface + } + if strings.HasPrefix(s.Name, "go.itab.") && s.Type == objabi.SRODATA { + flag2 |= goobj.SymFlagItab + } + name := s.Name + if strings.HasPrefix(name, "gofile..") { + name = filepath.ToSlash(name) + } + var align uint32 + if fn := s.Func(); fn != nil { + align = uint32(fn.Align) + } + if s.ContentAddressable() { + // We generally assume data symbols are natually aligned, + // except for strings. If we dedup a string symbol and a + // non-string symbol with the same content, we should keep + // the largest alignment. + // TODO: maybe the compiler could set the alignment for all + // data symbols more carefully. + if s.Size != 0 && !strings.HasPrefix(s.Name, "go.string.") { + switch { + case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0: + align = 8 + case s.Size%4 == 0: + align = 4 + case s.Size%2 == 0: + align = 2 + } + // don't bother setting align to 1. + } + } + if s.Size > cutoff { + w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, cutoff) + } + var o goobj.Sym + o.SetName(name, w.Writer) + o.SetABI(abi) + o.SetType(uint8(s.Type)) + o.SetFlag(flag) + o.SetFlag2(flag2) + o.SetSiz(uint32(s.Size)) + o.SetAlign(align) + o.Write(w.Writer) +} + +func (w *writer) Hash64(s *LSym) { + if !s.ContentAddressable() || len(s.R) != 0 { + panic("Hash of non-content-addresable symbol") + } + b := contentHash64(s) + w.Bytes(b[:]) +} + +func (w *writer) Hash(s *LSym) { + if !s.ContentAddressable() { + panic("Hash of non-content-addresable symbol") + } + b := w.contentHash(s) + w.Bytes(b[:]) +} + +func contentHash64(s *LSym) goobj.Hash64Type { + var b goobj.Hash64Type + copy(b[:], s.P) + return b +} + +// Compute the content hash for a content-addressable symbol. +// We build a content hash based on its content and relocations. +// Depending on the category of the referenced symbol, we choose +// different hash algorithms such that the hash is globally +// consistent. +// - For referenced content-addressable symbol, its content hash +// is globally consistent. +// - For package symbol and builtin symbol, its local index is +// globally consistent. +// - For non-package symbol, its fully-expanded name is globally +// consistent. For now, we require we know the current package +// path so we can always expand symbol names. (Otherwise, +// symbols with relocations are not considered hashable.) +// +// For now, we assume there is no circular dependencies among +// hashed symbols. +func (w *writer) contentHash(s *LSym) goobj.HashType { + h := sha1.New() + var tmp [14]byte + + // Include the size of the symbol in the hash. + // This preserves the length of symbols, preventing the following two symbols + // from hashing the same: + // + // [2]int{1,2} ≠ [10]int{1,2,0,0,0...} + // + // In this case, if the smaller symbol is alive, the larger is not kept unless + // needed. + binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size)) + h.Write(tmp[:8]) + + // Don't dedup type symbols with others, as they are in a different + // section. + if strings.HasPrefix(s.Name, "type.") { + h.Write([]byte{'T'}) + } else { + h.Write([]byte{0}) + } + // The compiler trims trailing zeros _sometimes_. We just do + // it always. + h.Write(bytes.TrimRight(s.P, "\x00")) + for i := range s.R { + r := &s.R[i] + binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off)) + tmp[4] = r.Siz + tmp[5] = uint8(r.Type) + binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add)) + h.Write(tmp[:]) + rs := r.Sym + switch rs.PkgIdx { + case goobj.PkgIdxHashed64: + h.Write([]byte{0}) + t := contentHash64(rs) + h.Write(t[:]) + case goobj.PkgIdxHashed: + h.Write([]byte{1}) + t := w.contentHash(rs) + h.Write(t[:]) + case goobj.PkgIdxNone: + h.Write([]byte{2}) + io.WriteString(h, rs.Name) // name is already expanded at this point + case goobj.PkgIdxBuiltin: + h.Write([]byte{3}) + binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) + h.Write(tmp[:4]) + case goobj.PkgIdxSelf: + io.WriteString(h, w.pkgpath) + binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) + h.Write(tmp[:4]) + default: + io.WriteString(h, rs.Pkg) + binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) + h.Write(tmp[:4]) + } + } + var b goobj.HashType + copy(b[:], h.Sum(nil)) + return b +} + +func makeSymRef(s *LSym) goobj.SymRef { + if s == nil { + return goobj.SymRef{} + } + if s.PkgIdx == 0 || !s.Indexed() { + fmt.Printf("unindexed symbol reference: %v\n", s) + panic("unindexed symbol reference") + } + return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)} +} + +func (w *writer) Reloc(r *Reloc) { + var o goobj.Reloc + o.SetOff(r.Off) + o.SetSiz(r.Siz) + o.SetType(uint8(r.Type)) + o.SetAdd(r.Add) + o.SetSym(makeSymRef(r.Sym)) + o.Write(w.Writer) +} + +func (w *writer) aux1(typ uint8, rs *LSym) { + var o goobj.Aux + o.SetType(typ) + o.SetSym(makeSymRef(rs)) + o.Write(w.Writer) +} + +func (w *writer) Aux(s *LSym) { + if s.Gotype != nil { + w.aux1(goobj.AuxGotype, s.Gotype) + } + if fn := s.Func(); fn != nil { + w.aux1(goobj.AuxFuncInfo, fn.FuncInfoSym) + + for _, d := range fn.Pcln.Funcdata { + w.aux1(goobj.AuxFuncdata, d) + } + + if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { + w.aux1(goobj.AuxDwarfInfo, fn.dwarfInfoSym) + } + if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { + w.aux1(goobj.AuxDwarfLoc, fn.dwarfLocSym) + } + if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { + w.aux1(goobj.AuxDwarfRanges, fn.dwarfRangesSym) + } + if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { + w.aux1(goobj.AuxDwarfLines, fn.dwarfDebugLinesSym) + } + if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { + w.aux1(goobj.AuxPcsp, fn.Pcln.Pcsp) + } + if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { + w.aux1(goobj.AuxPcfile, fn.Pcln.Pcfile) + } + if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { + w.aux1(goobj.AuxPcline, fn.Pcln.Pcline) + } + if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { + w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline) + } + for _, pcSym := range fn.Pcln.Pcdata { + w.aux1(goobj.AuxPcdata, pcSym) + } + + } +} + +// Emits flags of referenced indexed symbols. +func (w *writer) refFlags() { + seen := make(map[*LSym]bool) + w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs + switch rs.PkgIdx { + case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference + return + case goobj.PkgIdxInvalid: + panic("unindexed symbol reference") + } + if seen[rs] { + return + } + seen[rs] = true + symref := makeSymRef(rs) + flag2 := uint8(0) + if rs.UsedInIface() { + flag2 |= goobj.SymFlagUsedInIface + } + if flag2 == 0 { + return // no need to write zero flags + } + var o goobj.RefFlags + o.SetSym(symref) + o.SetFlag2(flag2) + o.Write(w.Writer) + }) +} + +// Emits names of referenced indexed symbols, used by tools (objdump, nm) +// only. +func (w *writer) refNames() { + seen := make(map[*LSym]bool) + w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs + switch rs.PkgIdx { + case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference + return + case goobj.PkgIdxInvalid: + panic("unindexed symbol reference") + } + if seen[rs] { + return + } + seen[rs] = true + symref := makeSymRef(rs) + var o goobj.RefName + o.SetSym(symref) + o.SetName(rs.Name, w.Writer) + o.Write(w.Writer) + }) + // TODO: output in sorted order? + // Currently tools (cmd/internal/goobj package) doesn't use mmap, + // and it just read it into a map in memory upfront. If it uses + // mmap, if the output is sorted, it probably could avoid reading + // into memory and just do lookups in the mmap'd object file. +} + +// return the number of aux symbols s have. +func nAuxSym(s *LSym) int { + n := 0 + if s.Gotype != nil { + n++ + } + if fn := s.Func(); fn != nil { + // FuncInfo is an aux symbol, each Funcdata is an aux symbol + n += 1 + len(fn.Pcln.Funcdata) + if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { + n++ + } + if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { + n++ + } + if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { + n++ + } + if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { + n++ + } + if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { + n++ + } + if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { + n++ + } + if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { + n++ + } + if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { + n++ + } + n += len(fn.Pcln.Pcdata) + } + return n +} + +// generate symbols for FuncInfo. +func genFuncInfoSyms(ctxt *Link) { + infosyms := make([]*LSym, 0, len(ctxt.Text)) + hashedsyms := make([]*LSym, 0, 4*len(ctxt.Text)) + preparePcSym := func(s *LSym) *LSym { + if s == nil { + return s + } + s.PkgIdx = goobj.PkgIdxHashed + s.SymIdx = int32(len(hashedsyms) + len(ctxt.hasheddefs)) + s.Set(AttrIndexed, true) + hashedsyms = append(hashedsyms, s) + return s + } + var b bytes.Buffer + symidx := int32(len(ctxt.defs)) + for _, s := range ctxt.Text { + fn := s.Func() + if fn == nil { + continue + } + o := goobj.FuncInfo{ + Args: uint32(fn.Args), + Locals: uint32(fn.Locals), + FuncID: objabi.FuncID(fn.FuncID), + } + pc := &fn.Pcln + o.Pcsp = makeSymRef(preparePcSym(pc.Pcsp)) + o.Pcfile = makeSymRef(preparePcSym(pc.Pcfile)) + o.Pcline = makeSymRef(preparePcSym(pc.Pcline)) + o.Pcinline = makeSymRef(preparePcSym(pc.Pcinline)) + o.Pcdata = make([]goobj.SymRef, len(pc.Pcdata)) + for i, pcSym := range pc.Pcdata { + o.Pcdata[i] = makeSymRef(preparePcSym(pcSym)) + } + o.Funcdataoff = make([]uint32, len(pc.Funcdataoff)) + for i, x := range pc.Funcdataoff { + o.Funcdataoff[i] = uint32(x) + } + i := 0 + o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles)) + for f := range pc.UsedFiles { + o.File[i] = f + i++ + } + sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] }) + o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes)) + for i, inl := range pc.InlTree.nodes { + f, l := getFileIndexAndLine(ctxt, inl.Pos) + o.InlTree[i] = goobj.InlTreeNode{ + Parent: int32(inl.Parent), + File: goobj.CUFileIndex(f), + 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: goobj.PkgIdxSelf, + SymIdx: symidx, + P: append([]byte(nil), b.Bytes()...), + } + isym.Set(AttrIndexed, true) + symidx++ + infosyms = append(infosyms, isym) + fn.FuncInfoSym = isym + b.Reset() + + dwsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym} + for _, s := range dwsyms { + if s == nil || s.Size == 0 { + continue + } + s.PkgIdx = goobj.PkgIdxSelf + s.SymIdx = symidx + s.Set(AttrIndexed, true) + symidx++ + infosyms = append(infosyms, s) + } + } + ctxt.defs = append(ctxt.defs, infosyms...) + ctxt.hasheddefs = append(ctxt.hasheddefs, hashedsyms...) +} + +func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) { + // Most aux symbols (ex: funcdata) are not interesting-- + // pick out just the DWARF ones for now. + if aux.Type != objabi.SDWARFLOC && + aux.Type != objabi.SDWARFFCN && + aux.Type != objabi.SDWARFABSFCN && + aux.Type != objabi.SDWARFLINES && + aux.Type != objabi.SDWARFRANGE { + return + } + ctxt.writeSymDebugNamed(aux, "aux for "+par.Name) +} + +func debugAsmEmit(ctxt *Link) { + if ctxt.Debugasm > 0 { + ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug) + if ctxt.Debugasm > 1 { + fn := func(par *LSym, aux *LSym) { + writeAuxSymDebug(ctxt, par, aux) + } + ctxt.traverseAuxSyms(traverseAux, fn) + } + } +} + +func (ctxt *Link) writeSymDebug(s *LSym) { + ctxt.writeSymDebugNamed(s, s.Name) +} + +func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { + ver := "" + if ctxt.Debugasm > 1 { + ver = fmt.Sprintf("<%d>", s.ABI()) + } + fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver) + if s.Type != 0 { + fmt.Fprintf(ctxt.Bso, "%v ", s.Type) + } + if s.Static() { + fmt.Fprint(ctxt.Bso, "static ") + } + if s.DuplicateOK() { + fmt.Fprintf(ctxt.Bso, "dupok ") + } + if s.CFunc() { + fmt.Fprintf(ctxt.Bso, "cfunc ") + } + if s.NoSplit() { + fmt.Fprintf(ctxt.Bso, "nosplit ") + } + if s.TopFrame() { + fmt.Fprintf(ctxt.Bso, "topframe ") + } + fmt.Fprintf(ctxt.Bso, "size=%d", s.Size) + if s.Type == objabi.STEXT { + fn := s.Func() + fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID)) + if s.Leaf() { + fmt.Fprintf(ctxt.Bso, " leaf") + } + } + fmt.Fprintf(ctxt.Bso, "\n") + if s.Type == objabi.STEXT { + for p := s.Func().Text; p != nil; p = p.Link { + fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc))) + if ctxt.Debugasm > 1 { + io.WriteString(ctxt.Bso, p.String()) + } else { + p.InnermostString(ctxt.Bso) + } + fmt.Fprintln(ctxt.Bso) + } + } + for i := 0; i < len(s.P); i += 16 { + fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) + j := i + for ; j < i+16 && j < len(s.P); j++ { + fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) + } + for ; j < i+16; j++ { + fmt.Fprintf(ctxt.Bso, " ") + } + fmt.Fprintf(ctxt.Bso, " ") + for j = i; j < i+16 && j < len(s.P); j++ { + c := int(s.P[j]) + b := byte('.') + if ' ' <= c && c <= 0x7e { + b = byte(c) + } + ctxt.Bso.WriteByte(b) + } + + fmt.Fprintf(ctxt.Bso, "\n") + } + + sort.Sort(relocByOff(s.R)) // generate stable output + for _, r := range s.R { + name := "" + ver := "" + if r.Sym != nil { + name = r.Sym.Name + if ctxt.Debugasm > 1 { + ver = fmt.Sprintf("<%d>", r.Sym.ABI()) + } + } else if r.Type == objabi.R_TLS_LE { + name = "TLS" + } + if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) { + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%x\n", int(r.Off), r.Siz, r.Type, name, ver, uint64(r.Add)) + } else { + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add) + } + } +} + +// relocByOff sorts relocations by their offsets. +type relocByOff []Reloc + +func (x relocByOff) Len() int { return len(x) } +func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off } +func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] } |