gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

objfile.go (18832B)


      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] }