diff options
Diffstat (limited to '')
-rw-r--r-- | src/cmd/internal/obj/objfile.go | 897 |
1 files changed, 897 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..ff0968e --- /dev/null +++ b/src/cmd/internal/obj/objfile.go @@ -0,0 +1,897 @@ +// 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/notsha256" + "cmd/internal/objabi" + "cmd/internal/sys" + "encoding/binary" + "fmt" + "io" + "log" + "os" + "path/filepath" + "sort" + "strings" +) + +const UnlinkablePkg = "<unlinkable>" // invalid package path, used when compiled without -p flag + +// 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 == UnlinkablePkg { + flags |= goobj.ObjFlagUnlinkable + } + if w.pkgpath == "" { + log.Fatal("empty package path") + } + 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 { + sort.Sort(relocByOff(s.R)) // some platforms (e.g. PE) requires relocations in address order + 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) + } + } + } + + // 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 + + // scratch space for writing (the Write methods escape + // as they are interface calls) + tmpSym goobj.Sym + tmpReloc goobj.Reloc + tmpAux goobj.Aux + tmpHash64 goobj.Hash64Type + tmpHash goobj.HashType + tmpRefFlags goobj.RefFlags + tmpRefName goobj.RefName +} + +// 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) { + // Don't put names of builtins into the string table (to save + // space). + if s.PkgIdx == goobj.PkgIdxBuiltin { + return + } + // 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.ctxt.Flag_noRefName && s.PkgIdx < goobj.PkgIdxSpecial { + // Don't include them if Flag_noRefName + return + } + if w.pkgpath != "" { + s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1) + } + 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 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 + } + if strings.HasPrefix(s.Name, w.ctxt.Pkgpath) && strings.HasPrefix(s.Name[len(w.ctxt.Pkgpath):], ".") && strings.HasPrefix(s.Name[len(w.ctxt.Pkgpath)+1:], objabi.GlobalDictPrefix) { + flag2 |= goobj.SymFlagDict + } + 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() && s.Size != 0 { + // We generally assume data symbols are natually aligned + // (e.g. integer constants), except for strings and a few + // compiler-emitted funcdata. 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. + switch { + case strings.HasPrefix(s.Name, "go:string."), + strings.HasPrefix(name, "type:.namedata."), + strings.HasPrefix(name, "type:.importpath."), + strings.HasSuffix(name, ".opendefer"), + strings.HasSuffix(name, ".arginfo0"), + strings.HasSuffix(name, ".arginfo1"), + strings.HasSuffix(name, ".argliveinfo"): + // These are just bytes, or varints. + align = 1 + case strings.HasPrefix(name, "gclocals·"): + // It has 32-bit fields. + align = 4 + default: + 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 + default: + align = 1 + } + } + } + if s.Size > cutoff { + w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, cutoff) + } + o := &w.tmpSym + 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-addressable symbol") + } + w.tmpHash64 = contentHash64(s) + w.Bytes(w.tmpHash64[:]) +} + +func (w *writer) Hash(s *LSym) { + if !s.ContentAddressable() { + panic("Hash of non-content-addressable symbol") + } + w.tmpHash = w.contentHash(s) + w.Bytes(w.tmpHash[:]) +} + +// contentHashSection returns a mnemonic for s's section. +// The goal is to prevent content-addressability from moving symbols between sections. +// contentHashSection only distinguishes between sets of sections for which this matters. +// Allowing flexibility increases the effectiveness of content-addressibility. +// But in some cases, such as doing addressing based on a base symbol, +// we need to ensure that a symbol is always in a prticular section. +// Some of these conditions are duplicated in cmd/link/internal/ld.(*Link).symtab. +// TODO: instead of duplicating them, have the compiler decide where symbols go. +func contentHashSection(s *LSym) byte { + name := s.Name + if s.IsPcdata() { + return 'P' + } + if strings.HasPrefix(name, "gcargs.") || + strings.HasPrefix(name, "gclocals.") || + strings.HasPrefix(name, "gclocals·") || + strings.HasSuffix(name, ".opendefer") || + strings.HasSuffix(name, ".arginfo0") || + strings.HasSuffix(name, ".arginfo1") || + strings.HasSuffix(name, ".argliveinfo") || + strings.HasSuffix(name, ".wrapinfo") || + strings.HasSuffix(name, ".args_stackmap") || + strings.HasSuffix(name, ".stkobj") { + return 'F' // go:func.* or go:funcrel.* + } + if strings.HasPrefix(name, "type:") { + return 'T' + } + return 0 +} + +func contentHash64(s *LSym) goobj.Hash64Type { + if contentHashSection(s) != 0 { + panic("short hash of non-default-section sym " + s.Name) + } + 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 := notsha256.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)) + // Some symbols require being in separate sections. + tmp[8] = contentHashSection(s) + h.Write(tmp[:9]) + + // 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 + if rs == nil { + fmt.Printf("symbol: %s\n", s) + fmt.Printf("relocation: %#v\n", r) + panic("nil symbol target in relocation") + } + 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) { + o := &w.tmpReloc + o.SetOff(r.Off) + o.SetSiz(r.Siz) + o.SetType(uint16(r.Type)) + o.SetAdd(r.Add) + o.SetSym(makeSymRef(r.Sym)) + o.Write(w.Writer) +} + +func (w *writer) aux1(typ uint8, rs *LSym) { + o := &w.tmpAux + 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 + } + o := &w.tmpRefFlags + 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() { + if w.ctxt.Flag_noRefName { + return + } + 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) + o := &w.tmpRefName + 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)) + 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: fn.FuncID, + FuncFlag: fn.FuncFlag, + StartLine: fn.StartLine, + } + pc := &fn.Pcln + 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 := ctxt.getFileIndexAndLine(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) + p := b.Bytes() + isym := &LSym{ + Type: objabi.SDATA, // for now, I don't think it matters + PkgIdx: goobj.PkgIdxSelf, + SymIdx: symidx, + P: append([]byte(nil), p...), + Size: int64(len(p)), + } + 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...) +} + +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.Func() != nil && s.Func().FuncFlag&objabi.FuncFlag_TOPFRAME != 0 { + fmt.Fprintf(ctxt.Bso, "topframe ") + } + if s.Func() != nil && s.Func().FuncFlag&objabi.FuncFlag_ASM != 0 { + fmt.Fprintf(ctxt.Bso, "asm ") + } + 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 align=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID), uint64(fn.Align)) + 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] } |