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