go/src/cmd/internal/obj/objfile.go
Cherry Mui 6e03de7b83 cmd/asm: require -p flag
CL 391014 requires the compiler to be invoked with the -p flag, to
specify the package path. Later, CL 394217 makes the compiler to
produce an unlinkable object file, so "go tool compile x.go" can
still be used on the command line. This CL does the same for the
assembler, requiring -p, otherwise generating an unlinkable object.

No special case for the main package, as the main package cannot
be only assembly code, and there is no way to tell if it is the
main package from an assembly file.

Now we guarantee that we always have an expanded package path in
the object file. A later CL will delete the name expansion code
in the linker.

Change-Id: I8c10661aaea2ff794614924ead958d80e7e2487d
Reviewed-on: https://go-review.googlesource.com/c/go/+/404298
Run-TryBot: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2022-05-11 22:59:46 +00:00

889 lines
22 KiB
Go

// 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
}
// 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.HasPrefix(name, "runtime.gcbits."),
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)
}
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-addressable symbol")
}
b := contentHash64(s)
w.Bytes(b[:])
}
func (w *writer) Hash(s *LSym) {
if !s.ContentAddressable() {
panic("Hash of non-content-addressable symbol")
}
b := w.contentHash(s)
w.Bytes(b[:])
}
// 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) {
var o goobj.Reloc
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) {
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() {
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)
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))
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,
}
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 := 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)
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...)
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.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] }