objfile.go raw

   1  // Copyright 2013 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  // Writing Go object files.
   6  
   7  package obj
   8  
   9  import (
  10  	"bytes"
  11  	"github.com/twitchyliquid64/golang-asm/bio"
  12  	"github.com/twitchyliquid64/golang-asm/goobj"
  13  	"github.com/twitchyliquid64/golang-asm/objabi"
  14  	"github.com/twitchyliquid64/golang-asm/sys"
  15  	"crypto/sha1"
  16  	"encoding/binary"
  17  	"fmt"
  18  	"io"
  19  	"path/filepath"
  20  	"sort"
  21  	"strings"
  22  )
  23  
  24  // Entry point of writing new object file.
  25  func WriteObjFile(ctxt *Link, b *bio.Writer) {
  26  
  27  	debugAsmEmit(ctxt)
  28  
  29  	genFuncInfoSyms(ctxt)
  30  
  31  	w := writer{
  32  		Writer:  goobj.NewWriter(b),
  33  		ctxt:    ctxt,
  34  		pkgpath: objabi.PathToPrefix(ctxt.Pkgpath),
  35  	}
  36  
  37  	start := b.Offset()
  38  	w.init()
  39  
  40  	// Header
  41  	// We just reserve the space. We'll fill in the offsets later.
  42  	flags := uint32(0)
  43  	if ctxt.Flag_shared {
  44  		flags |= goobj.ObjFlagShared
  45  	}
  46  	if w.pkgpath == "" {
  47  		flags |= goobj.ObjFlagNeedNameExpansion
  48  	}
  49  	if ctxt.IsAsm {
  50  		flags |= goobj.ObjFlagFromAssembly
  51  	}
  52  	h := goobj.Header{
  53  		Magic:       goobj.Magic,
  54  		Fingerprint: ctxt.Fingerprint,
  55  		Flags:       flags,
  56  	}
  57  	h.Write(w.Writer)
  58  
  59  	// String table
  60  	w.StringTable()
  61  
  62  	// Autolib
  63  	h.Offsets[goobj.BlkAutolib] = w.Offset()
  64  	for i := range ctxt.Imports {
  65  		ctxt.Imports[i].Write(w.Writer)
  66  	}
  67  
  68  	// Package references
  69  	h.Offsets[goobj.BlkPkgIdx] = w.Offset()
  70  	for _, pkg := range w.pkglist {
  71  		w.StringRef(pkg)
  72  	}
  73  
  74  	// File table (for DWARF and pcln generation).
  75  	h.Offsets[goobj.BlkFile] = w.Offset()
  76  	for _, f := range ctxt.PosTable.FileTable() {
  77  		w.StringRef(filepath.ToSlash(f))
  78  	}
  79  
  80  	// Symbol definitions
  81  	h.Offsets[goobj.BlkSymdef] = w.Offset()
  82  	for _, s := range ctxt.defs {
  83  		w.Sym(s)
  84  	}
  85  
  86  	// Short hashed symbol definitions
  87  	h.Offsets[goobj.BlkHashed64def] = w.Offset()
  88  	for _, s := range ctxt.hashed64defs {
  89  		w.Sym(s)
  90  	}
  91  
  92  	// Hashed symbol definitions
  93  	h.Offsets[goobj.BlkHasheddef] = w.Offset()
  94  	for _, s := range ctxt.hasheddefs {
  95  		w.Sym(s)
  96  	}
  97  
  98  	// Non-pkg symbol definitions
  99  	h.Offsets[goobj.BlkNonpkgdef] = w.Offset()
 100  	for _, s := range ctxt.nonpkgdefs {
 101  		w.Sym(s)
 102  	}
 103  
 104  	// Non-pkg symbol references
 105  	h.Offsets[goobj.BlkNonpkgref] = w.Offset()
 106  	for _, s := range ctxt.nonpkgrefs {
 107  		w.Sym(s)
 108  	}
 109  
 110  	// Referenced package symbol flags
 111  	h.Offsets[goobj.BlkRefFlags] = w.Offset()
 112  	w.refFlags()
 113  
 114  	// Hashes
 115  	h.Offsets[goobj.BlkHash64] = w.Offset()
 116  	for _, s := range ctxt.hashed64defs {
 117  		w.Hash64(s)
 118  	}
 119  	h.Offsets[goobj.BlkHash] = w.Offset()
 120  	for _, s := range ctxt.hasheddefs {
 121  		w.Hash(s)
 122  	}
 123  	// TODO: hashedrefs unused/unsupported for now
 124  
 125  	// Reloc indexes
 126  	h.Offsets[goobj.BlkRelocIdx] = w.Offset()
 127  	nreloc := uint32(0)
 128  	lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs}
 129  	for _, list := range lists {
 130  		for _, s := range list {
 131  			w.Uint32(nreloc)
 132  			nreloc += uint32(len(s.R))
 133  		}
 134  	}
 135  	w.Uint32(nreloc)
 136  
 137  	// Symbol Info indexes
 138  	h.Offsets[goobj.BlkAuxIdx] = w.Offset()
 139  	naux := uint32(0)
 140  	for _, list := range lists {
 141  		for _, s := range list {
 142  			w.Uint32(naux)
 143  			naux += uint32(nAuxSym(s))
 144  		}
 145  	}
 146  	w.Uint32(naux)
 147  
 148  	// Data indexes
 149  	h.Offsets[goobj.BlkDataIdx] = w.Offset()
 150  	dataOff := uint32(0)
 151  	for _, list := range lists {
 152  		for _, s := range list {
 153  			w.Uint32(dataOff)
 154  			dataOff += uint32(len(s.P))
 155  		}
 156  	}
 157  	w.Uint32(dataOff)
 158  
 159  	// Relocs
 160  	h.Offsets[goobj.BlkReloc] = w.Offset()
 161  	for _, list := range lists {
 162  		for _, s := range list {
 163  			for i := range s.R {
 164  				w.Reloc(&s.R[i])
 165  			}
 166  		}
 167  	}
 168  
 169  	// Aux symbol info
 170  	h.Offsets[goobj.BlkAux] = w.Offset()
 171  	for _, list := range lists {
 172  		for _, s := range list {
 173  			w.Aux(s)
 174  		}
 175  	}
 176  
 177  	// Data
 178  	h.Offsets[goobj.BlkData] = w.Offset()
 179  	for _, list := range lists {
 180  		for _, s := range list {
 181  			w.Bytes(s.P)
 182  		}
 183  	}
 184  
 185  	// Pcdata
 186  	h.Offsets[goobj.BlkPcdata] = w.Offset()
 187  	for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms
 188  		if s.Func != nil {
 189  			pc := &s.Func.Pcln
 190  			w.Bytes(pc.Pcsp.P)
 191  			w.Bytes(pc.Pcfile.P)
 192  			w.Bytes(pc.Pcline.P)
 193  			w.Bytes(pc.Pcinline.P)
 194  			for i := range pc.Pcdata {
 195  				w.Bytes(pc.Pcdata[i].P)
 196  			}
 197  		}
 198  	}
 199  
 200  	// Blocks used only by tools (objdump, nm).
 201  
 202  	// Referenced symbol names from other packages
 203  	h.Offsets[goobj.BlkRefName] = w.Offset()
 204  	w.refNames()
 205  
 206  	h.Offsets[goobj.BlkEnd] = w.Offset()
 207  
 208  	// Fix up block offsets in the header
 209  	end := start + int64(w.Offset())
 210  	b.MustSeek(start, 0)
 211  	h.Write(w.Writer)
 212  	b.MustSeek(end, 0)
 213  }
 214  
 215  type writer struct {
 216  	*goobj.Writer
 217  	ctxt    *Link
 218  	pkgpath string   // the package import path (escaped), "" if unknown
 219  	pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
 220  }
 221  
 222  // prepare package index list
 223  func (w *writer) init() {
 224  	w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1)
 225  	w.pkglist[0] = "" // dummy invalid package for index 0
 226  	for pkg, i := range w.ctxt.pkgIdx {
 227  		w.pkglist[i] = pkg
 228  	}
 229  }
 230  
 231  func (w *writer) StringTable() {
 232  	w.AddString("")
 233  	for _, p := range w.ctxt.Imports {
 234  		w.AddString(p.Pkg)
 235  	}
 236  	for _, pkg := range w.pkglist {
 237  		w.AddString(pkg)
 238  	}
 239  	w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
 240  		// TODO: this includes references of indexed symbols from other packages,
 241  		// for which the linker doesn't need the name. Consider moving them to
 242  		// a separate block (for tools only).
 243  		if w.pkgpath != "" {
 244  			s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
 245  		}
 246  		// Don't put names of builtins into the string table (to save
 247  		// space).
 248  		if s.PkgIdx == goobj.PkgIdxBuiltin {
 249  			return
 250  		}
 251  		w.AddString(s.Name)
 252  	})
 253  
 254  	// All filenames are in the postable.
 255  	for _, f := range w.ctxt.PosTable.FileTable() {
 256  		w.AddString(filepath.ToSlash(f))
 257  	}
 258  }
 259  
 260  func (w *writer) Sym(s *LSym) {
 261  	abi := uint16(s.ABI())
 262  	if s.Static() {
 263  		abi = goobj.SymABIstatic
 264  	}
 265  	flag := uint8(0)
 266  	if s.DuplicateOK() {
 267  		flag |= goobj.SymFlagDupok
 268  	}
 269  	if s.Local() {
 270  		flag |= goobj.SymFlagLocal
 271  	}
 272  	if s.MakeTypelink() {
 273  		flag |= goobj.SymFlagTypelink
 274  	}
 275  	if s.Leaf() {
 276  		flag |= goobj.SymFlagLeaf
 277  	}
 278  	if s.NoSplit() {
 279  		flag |= goobj.SymFlagNoSplit
 280  	}
 281  	if s.ReflectMethod() {
 282  		flag |= goobj.SymFlagReflectMethod
 283  	}
 284  	if s.TopFrame() {
 285  		flag |= goobj.SymFlagTopFrame
 286  	}
 287  	if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
 288  		flag |= goobj.SymFlagGoType
 289  	}
 290  	flag2 := uint8(0)
 291  	if s.UsedInIface() {
 292  		flag2 |= goobj.SymFlagUsedInIface
 293  	}
 294  	if strings.HasPrefix(s.Name, "go.itab.") && s.Type == objabi.SRODATA {
 295  		flag2 |= goobj.SymFlagItab
 296  	}
 297  	name := s.Name
 298  	if strings.HasPrefix(name, "gofile..") {
 299  		name = filepath.ToSlash(name)
 300  	}
 301  	var align uint32
 302  	if s.Func != nil {
 303  		align = uint32(s.Func.Align)
 304  	}
 305  	if s.ContentAddressable() {
 306  		// We generally assume data symbols are natually aligned,
 307  		// except for strings. If we dedup a string symbol and a
 308  		// non-string symbol with the same content, we should keep
 309  		// the largest alignment.
 310  		// TODO: maybe the compiler could set the alignment for all
 311  		// data symbols more carefully.
 312  		if s.Size != 0 && !strings.HasPrefix(s.Name, "go.string.") {
 313  			switch {
 314  			case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0:
 315  				align = 8
 316  			case s.Size%4 == 0:
 317  				align = 4
 318  			case s.Size%2 == 0:
 319  				align = 2
 320  			}
 321  			// don't bother setting align to 1.
 322  		}
 323  	}
 324  	var o goobj.Sym
 325  	o.SetName(name, w.Writer)
 326  	o.SetABI(abi)
 327  	o.SetType(uint8(s.Type))
 328  	o.SetFlag(flag)
 329  	o.SetFlag2(flag2)
 330  	o.SetSiz(uint32(s.Size))
 331  	o.SetAlign(align)
 332  	o.Write(w.Writer)
 333  }
 334  
 335  func (w *writer) Hash64(s *LSym) {
 336  	if !s.ContentAddressable() || len(s.R) != 0 {
 337  		panic("Hash of non-content-addresable symbol")
 338  	}
 339  	b := contentHash64(s)
 340  	w.Bytes(b[:])
 341  }
 342  
 343  func (w *writer) Hash(s *LSym) {
 344  	if !s.ContentAddressable() {
 345  		panic("Hash of non-content-addresable symbol")
 346  	}
 347  	b := w.contentHash(s)
 348  	w.Bytes(b[:])
 349  }
 350  
 351  func contentHash64(s *LSym) goobj.Hash64Type {
 352  	var b goobj.Hash64Type
 353  	copy(b[:], s.P)
 354  	return b
 355  }
 356  
 357  // Compute the content hash for a content-addressable symbol.
 358  // We build a content hash based on its content and relocations.
 359  // Depending on the category of the referenced symbol, we choose
 360  // different hash algorithms such that the hash is globally
 361  // consistent.
 362  // - For referenced content-addressable symbol, its content hash
 363  //   is globally consistent.
 364  // - For package symbol and builtin symbol, its local index is
 365  //   globally consistent.
 366  // - For non-package symbol, its fully-expanded name is globally
 367  //   consistent. For now, we require we know the current package
 368  //   path so we can always expand symbol names. (Otherwise,
 369  //   symbols with relocations are not considered hashable.)
 370  //
 371  // For now, we assume there is no circular dependencies among
 372  // hashed symbols.
 373  func (w *writer) contentHash(s *LSym) goobj.HashType {
 374  	h := sha1.New()
 375  	// The compiler trims trailing zeros _sometimes_. We just do
 376  	// it always.
 377  	h.Write(bytes.TrimRight(s.P, "\x00"))
 378  	var tmp [14]byte
 379  	for i := range s.R {
 380  		r := &s.R[i]
 381  		binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off))
 382  		tmp[4] = r.Siz
 383  		tmp[5] = uint8(r.Type)
 384  		binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add))
 385  		h.Write(tmp[:])
 386  		rs := r.Sym
 387  		switch rs.PkgIdx {
 388  		case goobj.PkgIdxHashed64:
 389  			h.Write([]byte{0})
 390  			t := contentHash64(rs)
 391  			h.Write(t[:])
 392  		case goobj.PkgIdxHashed:
 393  			h.Write([]byte{1})
 394  			t := w.contentHash(rs)
 395  			h.Write(t[:])
 396  		case goobj.PkgIdxNone:
 397  			h.Write([]byte{2})
 398  			io.WriteString(h, rs.Name) // name is already expanded at this point
 399  		case goobj.PkgIdxBuiltin:
 400  			h.Write([]byte{3})
 401  			binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
 402  			h.Write(tmp[:4])
 403  		case goobj.PkgIdxSelf:
 404  			io.WriteString(h, w.pkgpath)
 405  			binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
 406  			h.Write(tmp[:4])
 407  		default:
 408  			io.WriteString(h, rs.Pkg)
 409  			binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
 410  			h.Write(tmp[:4])
 411  		}
 412  	}
 413  	var b goobj.HashType
 414  	copy(b[:], h.Sum(nil))
 415  	return b
 416  }
 417  
 418  func makeSymRef(s *LSym) goobj.SymRef {
 419  	if s == nil {
 420  		return goobj.SymRef{}
 421  	}
 422  	if s.PkgIdx == 0 || !s.Indexed() {
 423  		fmt.Printf("unindexed symbol reference: %v\n", s)
 424  		panic("unindexed symbol reference")
 425  	}
 426  	return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
 427  }
 428  
 429  func (w *writer) Reloc(r *Reloc) {
 430  	var o goobj.Reloc
 431  	o.SetOff(r.Off)
 432  	o.SetSiz(r.Siz)
 433  	o.SetType(uint8(r.Type))
 434  	o.SetAdd(r.Add)
 435  	o.SetSym(makeSymRef(r.Sym))
 436  	o.Write(w.Writer)
 437  }
 438  
 439  func (w *writer) aux1(typ uint8, rs *LSym) {
 440  	var o goobj.Aux
 441  	o.SetType(typ)
 442  	o.SetSym(makeSymRef(rs))
 443  	o.Write(w.Writer)
 444  }
 445  
 446  func (w *writer) Aux(s *LSym) {
 447  	if s.Gotype != nil {
 448  		w.aux1(goobj.AuxGotype, s.Gotype)
 449  	}
 450  	if s.Func != nil {
 451  		w.aux1(goobj.AuxFuncInfo, s.Func.FuncInfoSym)
 452  
 453  		for _, d := range s.Func.Pcln.Funcdata {
 454  			w.aux1(goobj.AuxFuncdata, d)
 455  		}
 456  
 457  		if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 {
 458  			w.aux1(goobj.AuxDwarfInfo, s.Func.dwarfInfoSym)
 459  		}
 460  		if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
 461  			w.aux1(goobj.AuxDwarfLoc, s.Func.dwarfLocSym)
 462  		}
 463  		if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
 464  			w.aux1(goobj.AuxDwarfRanges, s.Func.dwarfRangesSym)
 465  		}
 466  		if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
 467  			w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym)
 468  		}
 469  	}
 470  }
 471  
 472  // Emits flags of referenced indexed symbols.
 473  func (w *writer) refFlags() {
 474  	seen := make(map[*LSym]bool)
 475  	w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
 476  		switch rs.PkgIdx {
 477  		case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
 478  			return
 479  		case goobj.PkgIdxInvalid:
 480  			panic("unindexed symbol reference")
 481  		}
 482  		if seen[rs] {
 483  			return
 484  		}
 485  		seen[rs] = true
 486  		symref := makeSymRef(rs)
 487  		flag2 := uint8(0)
 488  		if rs.UsedInIface() {
 489  			flag2 |= goobj.SymFlagUsedInIface
 490  		}
 491  		if flag2 == 0 {
 492  			return // no need to write zero flags
 493  		}
 494  		var o goobj.RefFlags
 495  		o.SetSym(symref)
 496  		o.SetFlag2(flag2)
 497  		o.Write(w.Writer)
 498  	})
 499  }
 500  
 501  // Emits names of referenced indexed symbols, used by tools (objdump, nm)
 502  // only.
 503  func (w *writer) refNames() {
 504  	seen := make(map[*LSym]bool)
 505  	w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
 506  		switch rs.PkgIdx {
 507  		case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
 508  			return
 509  		case goobj.PkgIdxInvalid:
 510  			panic("unindexed symbol reference")
 511  		}
 512  		if seen[rs] {
 513  			return
 514  		}
 515  		seen[rs] = true
 516  		symref := makeSymRef(rs)
 517  		var o goobj.RefName
 518  		o.SetSym(symref)
 519  		o.SetName(rs.Name, w.Writer)
 520  		o.Write(w.Writer)
 521  	})
 522  	// TODO: output in sorted order?
 523  	// Currently tools (cmd/internal/goobj package) doesn't use mmap,
 524  	// and it just read it into a map in memory upfront. If it uses
 525  	// mmap, if the output is sorted, it probably could avoid reading
 526  	// into memory and just do lookups in the mmap'd object file.
 527  }
 528  
 529  // return the number of aux symbols s have.
 530  func nAuxSym(s *LSym) int {
 531  	n := 0
 532  	if s.Gotype != nil {
 533  		n++
 534  	}
 535  	if s.Func != nil {
 536  		// FuncInfo is an aux symbol, each Funcdata is an aux symbol
 537  		n += 1 + len(s.Func.Pcln.Funcdata)
 538  		if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 {
 539  			n++
 540  		}
 541  		if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
 542  			n++
 543  		}
 544  		if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
 545  			n++
 546  		}
 547  		if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
 548  			n++
 549  		}
 550  	}
 551  	return n
 552  }
 553  
 554  // generate symbols for FuncInfo.
 555  func genFuncInfoSyms(ctxt *Link) {
 556  	infosyms := make([]*LSym, 0, len(ctxt.Text))
 557  	var pcdataoff uint32
 558  	var b bytes.Buffer
 559  	symidx := int32(len(ctxt.defs))
 560  	for _, s := range ctxt.Text {
 561  		if s.Func == nil {
 562  			continue
 563  		}
 564  		o := goobj.FuncInfo{
 565  			Args:   uint32(s.Func.Args),
 566  			Locals: uint32(s.Func.Locals),
 567  			FuncID: objabi.FuncID(s.Func.FuncID),
 568  		}
 569  		pc := &s.Func.Pcln
 570  		o.Pcsp = pcdataoff
 571  		pcdataoff += uint32(len(pc.Pcsp.P))
 572  		o.Pcfile = pcdataoff
 573  		pcdataoff += uint32(len(pc.Pcfile.P))
 574  		o.Pcline = pcdataoff
 575  		pcdataoff += uint32(len(pc.Pcline.P))
 576  		o.Pcinline = pcdataoff
 577  		pcdataoff += uint32(len(pc.Pcinline.P))
 578  		o.Pcdata = make([]uint32, len(pc.Pcdata))
 579  		for i, pcd := range pc.Pcdata {
 580  			o.Pcdata[i] = pcdataoff
 581  			pcdataoff += uint32(len(pcd.P))
 582  		}
 583  		o.PcdataEnd = pcdataoff
 584  		o.Funcdataoff = make([]uint32, len(pc.Funcdataoff))
 585  		for i, x := range pc.Funcdataoff {
 586  			o.Funcdataoff[i] = uint32(x)
 587  		}
 588  		i := 0
 589  		o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles))
 590  		for f := range pc.UsedFiles {
 591  			o.File[i] = f
 592  			i++
 593  		}
 594  		sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] })
 595  		o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes))
 596  		for i, inl := range pc.InlTree.nodes {
 597  			f, l := getFileIndexAndLine(ctxt, inl.Pos)
 598  			o.InlTree[i] = goobj.InlTreeNode{
 599  				Parent:   int32(inl.Parent),
 600  				File:     goobj.CUFileIndex(f),
 601  				Line:     l,
 602  				Func:     makeSymRef(inl.Func),
 603  				ParentPC: inl.ParentPC,
 604  			}
 605  		}
 606  
 607  		o.Write(&b)
 608  		isym := &LSym{
 609  			Type:   objabi.SDATA, // for now, I don't think it matters
 610  			PkgIdx: goobj.PkgIdxSelf,
 611  			SymIdx: symidx,
 612  			P:      append([]byte(nil), b.Bytes()...),
 613  		}
 614  		isym.Set(AttrIndexed, true)
 615  		symidx++
 616  		infosyms = append(infosyms, isym)
 617  		s.Func.FuncInfoSym = isym
 618  		b.Reset()
 619  
 620  		dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym, s.Func.dwarfInfoSym}
 621  		for _, s := range dwsyms {
 622  			if s == nil || s.Size == 0 {
 623  				continue
 624  			}
 625  			s.PkgIdx = goobj.PkgIdxSelf
 626  			s.SymIdx = symidx
 627  			s.Set(AttrIndexed, true)
 628  			symidx++
 629  			infosyms = append(infosyms, s)
 630  		}
 631  	}
 632  	ctxt.defs = append(ctxt.defs, infosyms...)
 633  }
 634  
 635  // debugDumpAux is a dumper for selected aux symbols.
 636  func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) {
 637  	// Most aux symbols (ex: funcdata) are not interesting--
 638  	// pick out just the DWARF ones for now.
 639  	if aux.Type != objabi.SDWARFLOC &&
 640  		aux.Type != objabi.SDWARFFCN &&
 641  		aux.Type != objabi.SDWARFABSFCN &&
 642  		aux.Type != objabi.SDWARFLINES &&
 643  		aux.Type != objabi.SDWARFRANGE {
 644  		return
 645  	}
 646  	ctxt.writeSymDebugNamed(aux, "aux for "+par.Name)
 647  }
 648  
 649  func debugAsmEmit(ctxt *Link) {
 650  	if ctxt.Debugasm > 0 {
 651  		ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
 652  		if ctxt.Debugasm > 1 {
 653  			fn := func(par *LSym, aux *LSym) {
 654  				writeAuxSymDebug(ctxt, par, aux)
 655  			}
 656  			ctxt.traverseAuxSyms(traverseAux, fn)
 657  		}
 658  	}
 659  }
 660  
 661  func (ctxt *Link) writeSymDebug(s *LSym) {
 662  	ctxt.writeSymDebugNamed(s, s.Name)
 663  }
 664  
 665  func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
 666  	ver := ""
 667  	if ctxt.Debugasm > 1 {
 668  		ver = fmt.Sprintf("<%d>", s.ABI())
 669  	}
 670  	fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver)
 671  	if s.Type != 0 {
 672  		fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
 673  	}
 674  	if s.Static() {
 675  		fmt.Fprint(ctxt.Bso, "static ")
 676  	}
 677  	if s.DuplicateOK() {
 678  		fmt.Fprintf(ctxt.Bso, "dupok ")
 679  	}
 680  	if s.CFunc() {
 681  		fmt.Fprintf(ctxt.Bso, "cfunc ")
 682  	}
 683  	if s.NoSplit() {
 684  		fmt.Fprintf(ctxt.Bso, "nosplit ")
 685  	}
 686  	if s.TopFrame() {
 687  		fmt.Fprintf(ctxt.Bso, "topframe ")
 688  	}
 689  	fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
 690  	if s.Type == objabi.STEXT {
 691  		fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(s.Func.Args), uint64(s.Func.Locals), uint64(s.Func.FuncID))
 692  		if s.Leaf() {
 693  			fmt.Fprintf(ctxt.Bso, " leaf")
 694  		}
 695  	}
 696  	fmt.Fprintf(ctxt.Bso, "\n")
 697  	if s.Type == objabi.STEXT {
 698  		for p := s.Func.Text; p != nil; p = p.Link {
 699  			fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc)))
 700  			if ctxt.Debugasm > 1 {
 701  				io.WriteString(ctxt.Bso, p.String())
 702  			} else {
 703  				p.InnermostString(ctxt.Bso)
 704  			}
 705  			fmt.Fprintln(ctxt.Bso)
 706  		}
 707  	}
 708  	for i := 0; i < len(s.P); i += 16 {
 709  		fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
 710  		j := i
 711  		for ; j < i+16 && j < len(s.P); j++ {
 712  			fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
 713  		}
 714  		for ; j < i+16; j++ {
 715  			fmt.Fprintf(ctxt.Bso, "   ")
 716  		}
 717  		fmt.Fprintf(ctxt.Bso, "  ")
 718  		for j = i; j < i+16 && j < len(s.P); j++ {
 719  			c := int(s.P[j])
 720  			b := byte('.')
 721  			if ' ' <= c && c <= 0x7e {
 722  				b = byte(c)
 723  			}
 724  			ctxt.Bso.WriteByte(b)
 725  		}
 726  
 727  		fmt.Fprintf(ctxt.Bso, "\n")
 728  	}
 729  
 730  	sort.Sort(relocByOff(s.R)) // generate stable output
 731  	for _, r := range s.R {
 732  		name := ""
 733  		ver := ""
 734  		if r.Sym != nil {
 735  			name = r.Sym.Name
 736  			if ctxt.Debugasm > 1 {
 737  				ver = fmt.Sprintf("<%d>", s.ABI())
 738  			}
 739  		} else if r.Type == objabi.R_TLS_LE {
 740  			name = "TLS"
 741  		}
 742  		if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
 743  			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))
 744  		} else {
 745  			fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add)
 746  		}
 747  	}
 748  }
 749  
 750  // relocByOff sorts relocations by their offsets.
 751  type relocByOff []Reloc
 752  
 753  func (x relocByOff) Len() int           { return len(x) }
 754  func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
 755  func (x relocByOff) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
 756