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.
5 // Writing Go object files.
13 "cmd/internal/notsha256"
27 const UnlinkablePkg = "<unlinkable>" // invalid package path, used when compiled without -p flag
29 // Entry point of writing new object file.
30 func WriteObjFile(ctxt *Link, b *bio.Writer) {
37 Writer: goobj.NewWriter(b),
39 pkgpath: objabi.PathToPrefix(ctxt.Pkgpath),
46 // We just reserve the space. We'll fill in the offsets later.
49 flags |= goobj.ObjFlagShared
51 if w.pkgpath == UnlinkablePkg {
52 flags |= goobj.ObjFlagUnlinkable
55 log.Fatal("empty package path")
58 flags |= goobj.ObjFlagFromAssembly
62 Fingerprint: ctxt.Fingerprint,
71 h.Offsets[goobj.BlkAutolib] = w.Offset()
72 for i := range ctxt.Imports {
73 ctxt.Imports[i].Write(w.Writer)
77 h.Offsets[goobj.BlkPkgIdx] = w.Offset()
78 for _, pkg := range w.pkglist {
82 // File table (for DWARF and pcln generation).
83 h.Offsets[goobj.BlkFile] = w.Offset()
84 for _, f := range ctxt.PosTable.FileTable() {
85 w.StringRef(filepath.ToSlash(f))
89 h.Offsets[goobj.BlkSymdef] = w.Offset()
90 for _, s := range ctxt.defs {
94 // Short hashed symbol definitions
95 h.Offsets[goobj.BlkHashed64def] = w.Offset()
96 for _, s := range ctxt.hashed64defs {
100 // Hashed symbol definitions
101 h.Offsets[goobj.BlkHasheddef] = w.Offset()
102 for _, s := range ctxt.hasheddefs {
106 // Non-pkg symbol definitions
107 h.Offsets[goobj.BlkNonpkgdef] = w.Offset()
108 for _, s := range ctxt.nonpkgdefs {
112 // Non-pkg symbol references
113 h.Offsets[goobj.BlkNonpkgref] = w.Offset()
114 for _, s := range ctxt.nonpkgrefs {
118 // Referenced package symbol flags
119 h.Offsets[goobj.BlkRefFlags] = w.Offset()
123 h.Offsets[goobj.BlkHash64] = w.Offset()
124 for _, s := range ctxt.hashed64defs {
127 h.Offsets[goobj.BlkHash] = w.Offset()
128 for _, s := range ctxt.hasheddefs {
131 // TODO: hashedrefs unused/unsupported for now
134 h.Offsets[goobj.BlkRelocIdx] = w.Offset()
136 lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs}
137 for _, list := range lists {
138 for _, s := range list {
140 nreloc += uint32(len(s.R))
145 // Symbol Info indexes
146 h.Offsets[goobj.BlkAuxIdx] = w.Offset()
148 for _, list := range lists {
149 for _, s := range list {
151 naux += uint32(nAuxSym(s))
157 h.Offsets[goobj.BlkDataIdx] = w.Offset()
159 for _, list := range lists {
160 for _, s := range list {
161 w.Uint32(uint32(dataOff))
162 dataOff += int64(len(s.P))
163 if file := s.File(); file != nil {
164 dataOff += int64(file.Size)
168 if int64(uint32(dataOff)) != dataOff {
169 log.Fatalf("data too large")
171 w.Uint32(uint32(dataOff))
174 h.Offsets[goobj.BlkReloc] = w.Offset()
175 for _, list := range lists {
176 for _, s := range list {
177 sort.Sort(relocByOff(s.R)) // some platforms (e.g. PE) requires relocations in address order
185 h.Offsets[goobj.BlkAux] = w.Offset()
186 for _, list := range lists {
187 for _, s := range list {
193 h.Offsets[goobj.BlkData] = w.Offset()
194 for _, list := range lists {
195 for _, s := range list {
197 if file := s.File(); file != nil {
198 w.writeFile(ctxt, file)
203 // Blocks used only by tools (objdump, nm).
205 // Referenced symbol names from other packages
206 h.Offsets[goobj.BlkRefName] = w.Offset()
209 h.Offsets[goobj.BlkEnd] = w.Offset()
211 // Fix up block offsets in the header
212 end := start + int64(w.Offset())
222 pkgpath string // the package import path (escaped), "" if unknown
223 pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
225 // scratch space for writing (the Write methods escape
226 // as they are interface calls)
230 tmpHash64 goobj.Hash64Type
231 tmpHash goobj.HashType
232 tmpRefFlags goobj.RefFlags
233 tmpRefName goobj.RefName
236 // prepare package index list
237 func (w *writer) init() {
238 w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1)
239 w.pkglist[0] = "" // dummy invalid package for index 0
240 for pkg, i := range w.ctxt.pkgIdx {
245 func (w *writer) writeFile(ctxt *Link, file *FileInfo) {
246 f, err := os.Open(file.Name)
252 if w.filebuf == nil {
253 w.filebuf = make([]byte, 1024)
258 n, err := f.Read(buf)
269 if written != file.Size {
270 ctxt.Diag("copy %s: unexpected length %d != %d", file.Name, written, file.Size)
274 func (w *writer) StringTable() {
276 for _, p := range w.ctxt.Imports {
279 for _, pkg := range w.pkglist {
282 w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
283 // Don't put names of builtins into the string table (to save
285 if s.PkgIdx == goobj.PkgIdxBuiltin {
288 // TODO: this includes references of indexed symbols from other packages,
289 // for which the linker doesn't need the name. Consider moving them to
290 // a separate block (for tools only).
291 if w.ctxt.Flag_noRefName && s.PkgIdx < goobj.PkgIdxSpecial {
292 // Don't include them if Flag_noRefName
296 s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
301 // All filenames are in the postable.
302 for _, f := range w.ctxt.PosTable.FileTable() {
303 w.AddString(filepath.ToSlash(f))
307 // cutoff is the maximum data section size permitted by the linker
308 // (see issue #9862).
309 const cutoff = int64(2e9) // 2 GB (or so; looks better in errors than 2^31)
311 func (w *writer) Sym(s *LSym) {
312 abi := uint16(s.ABI())
314 abi = goobj.SymABIstatic
318 flag |= goobj.SymFlagDupok
321 flag |= goobj.SymFlagLocal
323 if s.MakeTypelink() {
324 flag |= goobj.SymFlagTypelink
327 flag |= goobj.SymFlagLeaf
330 flag |= goobj.SymFlagNoSplit
332 if s.ReflectMethod() {
333 flag |= goobj.SymFlagReflectMethod
335 if strings.HasPrefix(s.Name, "type:") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
336 flag |= goobj.SymFlagGoType
340 flag2 |= goobj.SymFlagUsedInIface
342 if strings.HasPrefix(s.Name, "go:itab.") && s.Type == objabi.SRODATA {
343 flag2 |= goobj.SymFlagItab
345 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) {
346 flag2 |= goobj.SymFlagDict
349 flag2 |= goobj.SymFlagPkgInit
352 if strings.HasPrefix(name, "gofile..") {
353 name = filepath.ToSlash(name)
356 if fn := s.Func(); fn != nil {
357 align = uint32(fn.Align)
359 if s.ContentAddressable() && s.Size != 0 {
360 // We generally assume data symbols are naturally aligned
361 // (e.g. integer constants), except for strings and a few
362 // compiler-emitted funcdata. If we dedup a string symbol and
363 // a non-string symbol with the same content, we should keep
364 // the largest alignment.
365 // TODO: maybe the compiler could set the alignment for all
366 // data symbols more carefully.
368 case strings.HasPrefix(s.Name, "go:string."),
369 strings.HasPrefix(name, "type:.namedata."),
370 strings.HasPrefix(name, "type:.importpath."),
371 strings.HasSuffix(name, ".opendefer"),
372 strings.HasSuffix(name, ".arginfo0"),
373 strings.HasSuffix(name, ".arginfo1"),
374 strings.HasSuffix(name, ".argliveinfo"):
375 // These are just bytes, or varints.
377 case strings.HasPrefix(name, "gclocals·"):
378 // It has 32-bit fields.
382 case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0:
394 w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, cutoff)
397 o.SetName(name, w.Writer)
399 o.SetType(uint8(s.Type))
402 o.SetSiz(uint32(s.Size))
407 func (w *writer) Hash64(s *LSym) {
408 if !s.ContentAddressable() || len(s.R) != 0 {
409 panic("Hash of non-content-addressable symbol")
411 w.tmpHash64 = contentHash64(s)
412 w.Bytes(w.tmpHash64[:])
415 func (w *writer) Hash(s *LSym) {
416 if !s.ContentAddressable() {
417 panic("Hash of non-content-addressable symbol")
419 w.tmpHash = w.contentHash(s)
420 w.Bytes(w.tmpHash[:])
423 // contentHashSection returns a mnemonic for s's section.
424 // The goal is to prevent content-addressability from moving symbols between sections.
425 // contentHashSection only distinguishes between sets of sections for which this matters.
426 // Allowing flexibility increases the effectiveness of content-addressability.
427 // But in some cases, such as doing addressing based on a base symbol,
428 // we need to ensure that a symbol is always in a particular section.
429 // Some of these conditions are duplicated in cmd/link/internal/ld.(*Link).symtab.
430 // TODO: instead of duplicating them, have the compiler decide where symbols go.
431 func contentHashSection(s *LSym) byte {
436 if strings.HasPrefix(name, "gcargs.") ||
437 strings.HasPrefix(name, "gclocals.") ||
438 strings.HasPrefix(name, "gclocals·") ||
439 strings.HasSuffix(name, ".opendefer") ||
440 strings.HasSuffix(name, ".arginfo0") ||
441 strings.HasSuffix(name, ".arginfo1") ||
442 strings.HasSuffix(name, ".argliveinfo") ||
443 strings.HasSuffix(name, ".wrapinfo") ||
444 strings.HasSuffix(name, ".args_stackmap") ||
445 strings.HasSuffix(name, ".stkobj") {
446 return 'F' // go:func.* or go:funcrel.*
448 if strings.HasPrefix(name, "type:") {
454 func contentHash64(s *LSym) goobj.Hash64Type {
455 if contentHashSection(s) != 0 {
456 panic("short hash of non-default-section sym " + s.Name)
458 var b goobj.Hash64Type
463 // Compute the content hash for a content-addressable symbol.
464 // We build a content hash based on its content and relocations.
465 // Depending on the category of the referenced symbol, we choose
466 // different hash algorithms such that the hash is globally
468 // - For referenced content-addressable symbol, its content hash
469 // is globally consistent.
470 // - For package symbol and builtin symbol, its local index is
471 // globally consistent.
472 // - For non-package symbol, its fully-expanded name is globally
473 // consistent. For now, we require we know the current package
474 // path so we can always expand symbol names. (Otherwise,
475 // symbols with relocations are not considered hashable.)
477 // For now, we assume there is no circular dependencies among
479 func (w *writer) contentHash(s *LSym) goobj.HashType {
483 // Include the size of the symbol in the hash.
484 // This preserves the length of symbols, preventing the following two symbols
485 // from hashing the same:
487 // [2]int{1,2} ≠[10]int{1,2,0,0,0...}
489 // In this case, if the smaller symbol is alive, the larger is not kept unless
491 binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size))
492 // Some symbols require being in separate sections.
493 tmp[8] = contentHashSection(s)
496 // The compiler trims trailing zeros _sometimes_. We just do
498 h.Write(bytes.TrimRight(s.P, "\x00"))
501 binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off))
503 tmp[5] = uint8(r.Type)
504 binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add))
508 fmt.Printf("symbol: %s\n", s)
509 fmt.Printf("relocation: %#v\n", r)
510 panic("nil symbol target in relocation")
513 case goobj.PkgIdxHashed64:
515 t := contentHash64(rs)
517 case goobj.PkgIdxHashed:
519 t := w.contentHash(rs)
521 case goobj.PkgIdxNone:
523 io.WriteString(h, rs.Name) // name is already expanded at this point
524 case goobj.PkgIdxBuiltin:
526 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
528 case goobj.PkgIdxSelf:
529 io.WriteString(h, w.pkgpath)
530 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
533 io.WriteString(h, rs.Pkg)
534 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
539 copy(b[:], h.Sum(nil))
543 func makeSymRef(s *LSym) goobj.SymRef {
545 return goobj.SymRef{}
547 if s.PkgIdx == 0 || !s.Indexed() {
548 fmt.Printf("unindexed symbol reference: %v\n", s)
549 panic("unindexed symbol reference")
551 return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
554 func (w *writer) Reloc(r *Reloc) {
558 o.SetType(uint16(r.Type))
560 o.SetSym(makeSymRef(r.Sym))
564 func (w *writer) aux1(typ uint8, rs *LSym) {
567 o.SetSym(makeSymRef(rs))
571 func (w *writer) Aux(s *LSym) {
573 w.aux1(goobj.AuxGotype, s.Gotype)
575 if fn := s.Func(); fn != nil {
576 w.aux1(goobj.AuxFuncInfo, fn.FuncInfoSym)
578 for _, d := range fn.Pcln.Funcdata {
579 w.aux1(goobj.AuxFuncdata, d)
582 if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 {
583 w.aux1(goobj.AuxDwarfInfo, fn.dwarfInfoSym)
585 if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 {
586 w.aux1(goobj.AuxDwarfLoc, fn.dwarfLocSym)
588 if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 {
589 w.aux1(goobj.AuxDwarfRanges, fn.dwarfRangesSym)
591 if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 {
592 w.aux1(goobj.AuxDwarfLines, fn.dwarfDebugLinesSym)
594 if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 {
595 w.aux1(goobj.AuxPcsp, fn.Pcln.Pcsp)
597 if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 {
598 w.aux1(goobj.AuxPcfile, fn.Pcln.Pcfile)
600 if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 {
601 w.aux1(goobj.AuxPcline, fn.Pcln.Pcline)
603 if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 {
604 w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline)
606 if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 {
607 w.aux1(goobj.AuxSehUnwindInfo, fn.sehUnwindInfoSym)
609 for _, pcSym := range fn.Pcln.Pcdata {
610 w.aux1(goobj.AuxPcdata, pcSym)
612 if fn.WasmImportSym != nil {
613 if fn.WasmImportSym.Size == 0 {
614 panic("wasmimport aux sym must have non-zero size")
616 w.aux1(goobj.AuxWasmImport, fn.WasmImportSym)
618 } else if v := s.VarInfo(); v != nil {
619 if v.dwarfInfoSym != nil && v.dwarfInfoSym.Size != 0 {
620 w.aux1(goobj.AuxDwarfInfo, v.dwarfInfoSym)
625 // Emits flags of referenced indexed symbols.
626 func (w *writer) refFlags() {
627 seen := make(map[*LSym]bool)
628 w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
630 case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
632 case goobj.PkgIdxInvalid:
633 panic("unindexed symbol reference")
639 symref := makeSymRef(rs)
641 if rs.UsedInIface() {
642 flag2 |= goobj.SymFlagUsedInIface
645 return // no need to write zero flags
654 // Emits names of referenced indexed symbols, used by tools (objdump, nm)
656 func (w *writer) refNames() {
657 if w.ctxt.Flag_noRefName {
660 seen := make(map[*LSym]bool)
661 w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
663 case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
665 case goobj.PkgIdxInvalid:
666 panic("unindexed symbol reference")
672 symref := makeSymRef(rs)
675 o.SetName(rs.Name, w.Writer)
678 // TODO: output in sorted order?
679 // Currently tools (cmd/internal/goobj package) doesn't use mmap,
680 // and it just read it into a map in memory upfront. If it uses
681 // mmap, if the output is sorted, it probably could avoid reading
682 // into memory and just do lookups in the mmap'd object file.
685 // return the number of aux symbols s have.
686 func nAuxSym(s *LSym) int {
691 if fn := s.Func(); fn != nil {
692 // FuncInfo is an aux symbol, each Funcdata is an aux symbol
693 n += 1 + len(fn.Pcln.Funcdata)
694 if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 {
697 if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 {
700 if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 {
703 if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 {
706 if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 {
709 if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 {
712 if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 {
715 if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 {
718 if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 {
721 n += len(fn.Pcln.Pcdata)
722 if fn.WasmImport != nil {
723 if fn.WasmImportSym == nil || fn.WasmImportSym.Size == 0 {
724 panic("wasmimport aux sym must exist and have non-zero size")
728 } else if v := s.VarInfo(); v != nil {
729 if v.dwarfInfoSym != nil && v.dwarfInfoSym.Size != 0 {
736 // generate symbols for FuncInfo.
737 func genFuncInfoSyms(ctxt *Link) {
738 infosyms := make([]*LSym, 0, len(ctxt.Text))
740 symidx := int32(len(ctxt.defs))
741 for _, s := range ctxt.Text {
747 Args: uint32(fn.Args),
748 Locals: uint32(fn.Locals),
750 FuncFlag: fn.FuncFlag,
751 StartLine: fn.StartLine,
755 o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles))
756 for f := range pc.UsedFiles {
760 sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] })
761 o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes))
762 for i, inl := range pc.InlTree.nodes {
763 f, l := ctxt.getFileIndexAndLine(inl.Pos)
764 o.InlTree[i] = goobj.InlTreeNode{
765 Parent: int32(inl.Parent),
766 File: goobj.CUFileIndex(f),
768 Func: makeSymRef(inl.Func),
769 ParentPC: inl.ParentPC,
776 Type: objabi.SDATA, // for now, I don't think it matters
777 PkgIdx: goobj.PkgIdxSelf,
779 P: append([]byte(nil), p...),
782 isym.Set(AttrIndexed, true)
784 infosyms = append(infosyms, isym)
785 fn.FuncInfoSym = isym
788 auxsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym, fn.WasmImportSym, fn.sehUnwindInfoSym}
789 for _, s := range auxsyms {
790 if s == nil || s.Size == 0 {
793 s.PkgIdx = goobj.PkgIdxSelf
795 s.Set(AttrIndexed, true)
797 infosyms = append(infosyms, s)
800 ctxt.defs = append(ctxt.defs, infosyms...)
803 func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) {
804 // Most aux symbols (ex: funcdata) are not interesting--
805 // pick out just the DWARF ones for now.
807 case objabi.SDWARFLOC,
816 ctxt.writeSymDebugNamed(aux, "aux for "+par.Name)
819 func debugAsmEmit(ctxt *Link) {
820 if ctxt.Debugasm > 0 {
821 ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
822 if ctxt.Debugasm > 1 {
823 fn := func(par *LSym, aux *LSym) {
824 writeAuxSymDebug(ctxt, par, aux)
826 ctxt.traverseAuxSyms(traverseAux, fn)
831 func (ctxt *Link) writeSymDebug(s *LSym) {
832 ctxt.writeSymDebugNamed(s, s.Name)
835 func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
837 if ctxt.Debugasm > 1 {
838 ver = fmt.Sprintf("<%d>", s.ABI())
840 fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver)
842 fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
845 fmt.Fprint(ctxt.Bso, "static ")
848 fmt.Fprintf(ctxt.Bso, "dupok ")
851 fmt.Fprintf(ctxt.Bso, "cfunc ")
854 fmt.Fprintf(ctxt.Bso, "nosplit ")
856 if s.Func() != nil && s.Func().FuncFlag&abi.FuncFlagTopFrame != 0 {
857 fmt.Fprintf(ctxt.Bso, "topframe ")
859 if s.Func() != nil && s.Func().FuncFlag&abi.FuncFlagAsm != 0 {
860 fmt.Fprintf(ctxt.Bso, "asm ")
862 fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
863 if s.Type == objabi.STEXT {
865 fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x align=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID), uint64(fn.Align))
867 fmt.Fprintf(ctxt.Bso, " leaf")
870 fmt.Fprintf(ctxt.Bso, "\n")
871 if s.Type == objabi.STEXT {
872 for p := s.Func().Text; p != nil; p = p.Link {
873 fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc)))
874 if ctxt.Debugasm > 1 {
875 io.WriteString(ctxt.Bso, p.String())
877 p.InnermostString(ctxt.Bso)
879 fmt.Fprintln(ctxt.Bso)
882 for i := 0; i < len(s.P); i += 16 {
883 fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
885 for ; j < i+16 && j < len(s.P); j++ {
886 fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
888 for ; j < i+16; j++ {
889 fmt.Fprintf(ctxt.Bso, " ")
891 fmt.Fprintf(ctxt.Bso, " ")
892 for j = i; j < i+16 && j < len(s.P); j++ {
895 if ' ' <= c && c <= 0x7e {
898 ctxt.Bso.WriteByte(b)
901 fmt.Fprintf(ctxt.Bso, "\n")
904 sort.Sort(relocByOff(s.R)) // generate stable output
905 for _, r := range s.R {
910 if ctxt.Debugasm > 1 {
911 ver = fmt.Sprintf("<%d>", r.Sym.ABI())
913 } else if r.Type == objabi.R_TLS_LE {
916 if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
917 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))
919 fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add)
924 // relocByOff sorts relocations by their offsets.
925 type relocByOff []Reloc
927 func (x relocByOff) Len() int { return len(x) }
928 func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
929 func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] }