github.com/bir3/gocompiler@v0.9.2202/src/cmd/internal/obj/objfile.go (about) 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/bir3/gocompiler/src/cmd/internal/bio" 12 "github.com/bir3/gocompiler/src/cmd/internal/goobj" 13 "github.com/bir3/gocompiler/src/cmd/internal/notsha256" 14 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 15 "github.com/bir3/gocompiler/src/cmd/internal/sys" 16 "encoding/binary" 17 "fmt" 18 "github.com/bir3/gocompiler/src/internal/abi" 19 "io" 20 "log" 21 "os" 22 "path/filepath" 23 "sort" 24 "strings" 25 ) 26 27 const UnlinkablePkg = "<unlinkable>" // invalid package path, used when compiled without -p flag 28 29 // Entry point of writing new object file. 30 func WriteObjFile(ctxt *Link, b *bio.Writer) { 31 32 debugAsmEmit(ctxt) 33 34 genFuncInfoSyms(ctxt) 35 36 w := writer{ 37 Writer: goobj.NewWriter(b), 38 ctxt: ctxt, 39 pkgpath: objabi.PathToPrefix(ctxt.Pkgpath), 40 } 41 42 start := b.Offset() 43 w.init() 44 45 // Header 46 // We just reserve the space. We'll fill in the offsets later. 47 flags := uint32(0) 48 if ctxt.Flag_shared { 49 flags |= goobj.ObjFlagShared 50 } 51 if w.pkgpath == UnlinkablePkg { 52 flags |= goobj.ObjFlagUnlinkable 53 } 54 if w.pkgpath == "" { 55 log.Fatal("empty package path") 56 } 57 if ctxt.IsAsm { 58 flags |= goobj.ObjFlagFromAssembly 59 } 60 h := goobj.Header{ 61 Magic: goobj.Magic, 62 Fingerprint: ctxt.Fingerprint, 63 Flags: flags, 64 } 65 h.Write(w.Writer) 66 67 // String table 68 w.StringTable() 69 70 // Autolib 71 h.Offsets[goobj.BlkAutolib] = w.Offset() 72 for i := range ctxt.Imports { 73 ctxt.Imports[i].Write(w.Writer) 74 } 75 76 // Package references 77 h.Offsets[goobj.BlkPkgIdx] = w.Offset() 78 for _, pkg := range w.pkglist { 79 w.StringRef(pkg) 80 } 81 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)) 86 } 87 88 // Symbol definitions 89 h.Offsets[goobj.BlkSymdef] = w.Offset() 90 for _, s := range ctxt.defs { 91 w.Sym(s) 92 } 93 94 // Short hashed symbol definitions 95 h.Offsets[goobj.BlkHashed64def] = w.Offset() 96 for _, s := range ctxt.hashed64defs { 97 w.Sym(s) 98 } 99 100 // Hashed symbol definitions 101 h.Offsets[goobj.BlkHasheddef] = w.Offset() 102 for _, s := range ctxt.hasheddefs { 103 w.Sym(s) 104 } 105 106 // Non-pkg symbol definitions 107 h.Offsets[goobj.BlkNonpkgdef] = w.Offset() 108 for _, s := range ctxt.nonpkgdefs { 109 w.Sym(s) 110 } 111 112 // Non-pkg symbol references 113 h.Offsets[goobj.BlkNonpkgref] = w.Offset() 114 for _, s := range ctxt.nonpkgrefs { 115 w.Sym(s) 116 } 117 118 // Referenced package symbol flags 119 h.Offsets[goobj.BlkRefFlags] = w.Offset() 120 w.refFlags() 121 122 // Hashes 123 h.Offsets[goobj.BlkHash64] = w.Offset() 124 for _, s := range ctxt.hashed64defs { 125 w.Hash64(s) 126 } 127 h.Offsets[goobj.BlkHash] = w.Offset() 128 for _, s := range ctxt.hasheddefs { 129 w.Hash(s) 130 } 131 // TODO: hashedrefs unused/unsupported for now 132 133 // Reloc indexes 134 h.Offsets[goobj.BlkRelocIdx] = w.Offset() 135 nreloc := uint32(0) 136 lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs} 137 for _, list := range lists { 138 for _, s := range list { 139 w.Uint32(nreloc) 140 nreloc += uint32(len(s.R)) 141 } 142 } 143 w.Uint32(nreloc) 144 145 // Symbol Info indexes 146 h.Offsets[goobj.BlkAuxIdx] = w.Offset() 147 naux := uint32(0) 148 for _, list := range lists { 149 for _, s := range list { 150 w.Uint32(naux) 151 naux += uint32(nAuxSym(s)) 152 } 153 } 154 w.Uint32(naux) 155 156 // Data indexes 157 h.Offsets[goobj.BlkDataIdx] = w.Offset() 158 dataOff := int64(0) 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) 165 } 166 } 167 } 168 if int64(uint32(dataOff)) != dataOff { 169 log.Fatalf("data too large") 170 } 171 w.Uint32(uint32(dataOff)) 172 173 // Relocs 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 178 for i := range s.R { 179 w.Reloc(&s.R[i]) 180 } 181 } 182 } 183 184 // Aux symbol info 185 h.Offsets[goobj.BlkAux] = w.Offset() 186 for _, list := range lists { 187 for _, s := range list { 188 w.Aux(s) 189 } 190 } 191 192 // Data 193 h.Offsets[goobj.BlkData] = w.Offset() 194 for _, list := range lists { 195 for _, s := range list { 196 w.Bytes(s.P) 197 if file := s.File(); file != nil { 198 w.writeFile(ctxt, file) 199 } 200 } 201 } 202 203 // Blocks used only by tools (objdump, nm). 204 205 // Referenced symbol names from other packages 206 h.Offsets[goobj.BlkRefName] = w.Offset() 207 w.refNames() 208 209 h.Offsets[goobj.BlkEnd] = w.Offset() 210 211 // Fix up block offsets in the header 212 end := start + int64(w.Offset()) 213 b.MustSeek(start, 0) 214 h.Write(w.Writer) 215 b.MustSeek(end, 0) 216 } 217 218 type writer struct { 219 *goobj.Writer 220 filebuf []byte 221 ctxt *Link 222 pkgpath string // the package import path (escaped), "" if unknown 223 pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx 224 225 // scratch space for writing (the Write methods escape 226 // as they are interface calls) 227 tmpSym goobj.Sym 228 tmpReloc goobj.Reloc 229 tmpAux goobj.Aux 230 tmpHash64 goobj.Hash64Type 231 tmpHash goobj.HashType 232 tmpRefFlags goobj.RefFlags 233 tmpRefName goobj.RefName 234 } 235 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 { 241 w.pkglist[i] = pkg 242 } 243 } 244 245 func (w *writer) writeFile(ctxt *Link, file *FileInfo) { 246 f, err := os.Open(file.Name) 247 if err != nil { 248 ctxt.Diag("%v", err) 249 return 250 } 251 defer f.Close() 252 if w.filebuf == nil { 253 w.filebuf = make([]byte, 1024) 254 } 255 buf := w.filebuf 256 written := int64(0) 257 for { 258 n, err := f.Read(buf) 259 w.Bytes(buf[:n]) 260 written += int64(n) 261 if err == io.EOF { 262 break 263 } 264 if err != nil { 265 ctxt.Diag("%v", err) 266 return 267 } 268 } 269 if written != file.Size { 270 ctxt.Diag("copy %s: unexpected length %d != %d", file.Name, written, file.Size) 271 } 272 } 273 274 func (w *writer) StringTable() { 275 w.AddString("") 276 for _, p := range w.ctxt.Imports { 277 w.AddString(p.Pkg) 278 } 279 for _, pkg := range w.pkglist { 280 w.AddString(pkg) 281 } 282 w.ctxt.traverseSyms(traverseAll, func(s *LSym) { 283 // Don't put names of builtins into the string table (to save 284 // space). 285 if s.PkgIdx == goobj.PkgIdxBuiltin { 286 return 287 } 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 293 return 294 } 295 if strings.HasPrefix(s.Name, `"".`) { 296 w.ctxt.Diag("unqualified symbol name: %v", s.Name) 297 } 298 w.AddString(s.Name) 299 }) 300 301 // All filenames are in the postable. 302 for _, f := range w.ctxt.PosTable.FileTable() { 303 w.AddString(filepath.ToSlash(f)) 304 } 305 } 306 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) 310 311 func (w *writer) Sym(s *LSym) { 312 abi := uint16(s.ABI()) 313 if s.Static() { 314 abi = goobj.SymABIstatic 315 } 316 flag := uint8(0) 317 if s.DuplicateOK() { 318 flag |= goobj.SymFlagDupok 319 } 320 if s.Local() { 321 flag |= goobj.SymFlagLocal 322 } 323 if s.MakeTypelink() { 324 flag |= goobj.SymFlagTypelink 325 } 326 if s.Leaf() { 327 flag |= goobj.SymFlagLeaf 328 } 329 if s.NoSplit() { 330 flag |= goobj.SymFlagNoSplit 331 } 332 if s.ReflectMethod() { 333 flag |= goobj.SymFlagReflectMethod 334 } 335 if strings.HasPrefix(s.Name, "type:") && s.Name[5] != '.' && s.Type == objabi.SRODATA { 336 flag |= goobj.SymFlagGoType 337 } 338 flag2 := uint8(0) 339 if s.UsedInIface() { 340 flag2 |= goobj.SymFlagUsedInIface 341 } 342 if strings.HasPrefix(s.Name, "go:itab.") && s.Type == objabi.SRODATA { 343 flag2 |= goobj.SymFlagItab 344 } 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 347 } 348 if s.IsPkgInit() { 349 flag2 |= goobj.SymFlagPkgInit 350 } 351 name := s.Name 352 if strings.HasPrefix(name, "gofile..") { 353 name = filepath.ToSlash(name) 354 } 355 var align uint32 356 if fn := s.Func(); fn != nil { 357 align = uint32(fn.Align) 358 } 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. 367 switch { 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. 376 align = 1 377 case strings.HasPrefix(name, "gclocals·"): 378 // It has 32-bit fields. 379 align = 4 380 default: 381 switch { 382 case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0: 383 align = 8 384 case s.Size%4 == 0: 385 align = 4 386 case s.Size%2 == 0: 387 align = 2 388 default: 389 align = 1 390 } 391 } 392 } 393 if s.Size > cutoff { 394 w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, cutoff) 395 } 396 o := &w.tmpSym 397 o.SetName(name, w.Writer) 398 o.SetABI(abi) 399 o.SetType(uint8(s.Type)) 400 o.SetFlag(flag) 401 o.SetFlag2(flag2) 402 o.SetSiz(uint32(s.Size)) 403 o.SetAlign(align) 404 o.Write(w.Writer) 405 } 406 407 func (w *writer) Hash64(s *LSym) { 408 if !s.ContentAddressable() || len(s.R) != 0 { 409 panic("Hash of non-content-addressable symbol") 410 } 411 w.tmpHash64 = contentHash64(s) 412 w.Bytes(w.tmpHash64[:]) 413 } 414 415 func (w *writer) Hash(s *LSym) { 416 if !s.ContentAddressable() { 417 panic("Hash of non-content-addressable symbol") 418 } 419 w.tmpHash = w.contentHash(s) 420 w.Bytes(w.tmpHash[:]) 421 } 422 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 { 432 name := s.Name 433 if s.IsPcdata() { 434 return 'P' 435 } 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.* 447 } 448 if strings.HasPrefix(name, "type:") { 449 return 'T' 450 } 451 return 0 452 } 453 454 func contentHash64(s *LSym) goobj.Hash64Type { 455 if contentHashSection(s) != 0 { 456 panic("short hash of non-default-section sym " + s.Name) 457 } 458 var b goobj.Hash64Type 459 copy(b[:], s.P) 460 return b 461 } 462 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 467 // consistent. 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.) 476 // 477 // For now, we assume there is no circular dependencies among 478 // hashed symbols. 479 func (w *writer) contentHash(s *LSym) goobj.HashType { 480 h := notsha256.New() 481 var tmp [14]byte 482 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: 486 // 487 // [2]int{1,2} ≠ [10]int{1,2,0,0,0...} 488 // 489 // In this case, if the smaller symbol is alive, the larger is not kept unless 490 // needed. 491 binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size)) 492 // Some symbols require being in separate sections. 493 tmp[8] = contentHashSection(s) 494 h.Write(tmp[:9]) 495 496 // The compiler trims trailing zeros _sometimes_. We just do 497 // it always. 498 h.Write(bytes.TrimRight(s.P, "\x00")) 499 for i := range s.R { 500 r := &s.R[i] 501 binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off)) 502 tmp[4] = r.Siz 503 tmp[5] = uint8(r.Type) 504 binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add)) 505 h.Write(tmp[:]) 506 rs := r.Sym 507 if rs == nil { 508 fmt.Printf("symbol: %s\n", s) 509 fmt.Printf("relocation: %#v\n", r) 510 panic("nil symbol target in relocation") 511 } 512 switch rs.PkgIdx { 513 case goobj.PkgIdxHashed64: 514 h.Write([]byte{0}) 515 t := contentHash64(rs) 516 h.Write(t[:]) 517 case goobj.PkgIdxHashed: 518 h.Write([]byte{1}) 519 t := w.contentHash(rs) 520 h.Write(t[:]) 521 case goobj.PkgIdxNone: 522 h.Write([]byte{2}) 523 io.WriteString(h, rs.Name) // name is already expanded at this point 524 case goobj.PkgIdxBuiltin: 525 h.Write([]byte{3}) 526 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) 527 h.Write(tmp[:4]) 528 case goobj.PkgIdxSelf: 529 io.WriteString(h, w.pkgpath) 530 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) 531 h.Write(tmp[:4]) 532 default: 533 io.WriteString(h, rs.Pkg) 534 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) 535 h.Write(tmp[:4]) 536 } 537 } 538 var b goobj.HashType 539 copy(b[:], h.Sum(nil)) 540 return b 541 } 542 543 func makeSymRef(s *LSym) goobj.SymRef { 544 if s == nil { 545 return goobj.SymRef{} 546 } 547 if s.PkgIdx == 0 || !s.Indexed() { 548 fmt.Printf("unindexed symbol reference: %v\n", s) 549 panic("unindexed symbol reference") 550 } 551 return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)} 552 } 553 554 func (w *writer) Reloc(r *Reloc) { 555 o := &w.tmpReloc 556 o.SetOff(r.Off) 557 o.SetSiz(r.Siz) 558 o.SetType(uint16(r.Type)) 559 o.SetAdd(r.Add) 560 o.SetSym(makeSymRef(r.Sym)) 561 o.Write(w.Writer) 562 } 563 564 func (w *writer) aux1(typ uint8, rs *LSym) { 565 o := &w.tmpAux 566 o.SetType(typ) 567 o.SetSym(makeSymRef(rs)) 568 o.Write(w.Writer) 569 } 570 571 func (w *writer) Aux(s *LSym) { 572 if s.Gotype != nil { 573 w.aux1(goobj.AuxGotype, s.Gotype) 574 } 575 if fn := s.Func(); fn != nil { 576 w.aux1(goobj.AuxFuncInfo, fn.FuncInfoSym) 577 578 for _, d := range fn.Pcln.Funcdata { 579 w.aux1(goobj.AuxFuncdata, d) 580 } 581 582 if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { 583 w.aux1(goobj.AuxDwarfInfo, fn.dwarfInfoSym) 584 } 585 if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { 586 w.aux1(goobj.AuxDwarfLoc, fn.dwarfLocSym) 587 } 588 if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { 589 w.aux1(goobj.AuxDwarfRanges, fn.dwarfRangesSym) 590 } 591 if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { 592 w.aux1(goobj.AuxDwarfLines, fn.dwarfDebugLinesSym) 593 } 594 if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { 595 w.aux1(goobj.AuxPcsp, fn.Pcln.Pcsp) 596 } 597 if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { 598 w.aux1(goobj.AuxPcfile, fn.Pcln.Pcfile) 599 } 600 if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { 601 w.aux1(goobj.AuxPcline, fn.Pcln.Pcline) 602 } 603 if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { 604 w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline) 605 } 606 if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 { 607 w.aux1(goobj.AuxSehUnwindInfo, fn.sehUnwindInfoSym) 608 } 609 for _, pcSym := range fn.Pcln.Pcdata { 610 w.aux1(goobj.AuxPcdata, pcSym) 611 } 612 if fn.WasmImportSym != nil { 613 if fn.WasmImportSym.Size == 0 { 614 panic("wasmimport aux sym must have non-zero size") 615 } 616 w.aux1(goobj.AuxWasmImport, fn.WasmImportSym) 617 } 618 } else if v := s.VarInfo(); v != nil { 619 if v.dwarfInfoSym != nil && v.dwarfInfoSym.Size != 0 { 620 w.aux1(goobj.AuxDwarfInfo, v.dwarfInfoSym) 621 } 622 } 623 } 624 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 629 switch rs.PkgIdx { 630 case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference 631 return 632 case goobj.PkgIdxInvalid: 633 panic("unindexed symbol reference") 634 } 635 if seen[rs] { 636 return 637 } 638 seen[rs] = true 639 symref := makeSymRef(rs) 640 flag2 := uint8(0) 641 if rs.UsedInIface() { 642 flag2 |= goobj.SymFlagUsedInIface 643 } 644 if flag2 == 0 { 645 return // no need to write zero flags 646 } 647 o := &w.tmpRefFlags 648 o.SetSym(symref) 649 o.SetFlag2(flag2) 650 o.Write(w.Writer) 651 }) 652 } 653 654 // Emits names of referenced indexed symbols, used by tools (objdump, nm) 655 // only. 656 func (w *writer) refNames() { 657 if w.ctxt.Flag_noRefName { 658 return 659 } 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 662 switch rs.PkgIdx { 663 case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference 664 return 665 case goobj.PkgIdxInvalid: 666 panic("unindexed symbol reference") 667 } 668 if seen[rs] { 669 return 670 } 671 seen[rs] = true 672 symref := makeSymRef(rs) 673 o := &w.tmpRefName 674 o.SetSym(symref) 675 o.SetName(rs.Name, w.Writer) 676 o.Write(w.Writer) 677 }) 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. 683 } 684 685 // return the number of aux symbols s have. 686 func nAuxSym(s *LSym) int { 687 n := 0 688 if s.Gotype != nil { 689 n++ 690 } 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 { 695 n++ 696 } 697 if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { 698 n++ 699 } 700 if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { 701 n++ 702 } 703 if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { 704 n++ 705 } 706 if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { 707 n++ 708 } 709 if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { 710 n++ 711 } 712 if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { 713 n++ 714 } 715 if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { 716 n++ 717 } 718 if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 { 719 n++ 720 } 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") 725 } 726 n++ 727 } 728 } else if v := s.VarInfo(); v != nil { 729 if v.dwarfInfoSym != nil && v.dwarfInfoSym.Size != 0 { 730 n++ 731 } 732 } 733 return n 734 } 735 736 // generate symbols for FuncInfo. 737 func genFuncInfoSyms(ctxt *Link) { 738 infosyms := make([]*LSym, 0, len(ctxt.Text)) 739 var b bytes.Buffer 740 symidx := int32(len(ctxt.defs)) 741 for _, s := range ctxt.Text { 742 fn := s.Func() 743 if fn == nil { 744 continue 745 } 746 o := goobj.FuncInfo{ 747 Args: uint32(fn.Args), 748 Locals: uint32(fn.Locals), 749 FuncID: fn.FuncID, 750 FuncFlag: fn.FuncFlag, 751 StartLine: fn.StartLine, 752 } 753 pc := &fn.Pcln 754 i := 0 755 o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles)) 756 for f := range pc.UsedFiles { 757 o.File[i] = f 758 i++ 759 } 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), 767 Line: l, 768 Func: makeSymRef(inl.Func), 769 ParentPC: inl.ParentPC, 770 } 771 } 772 773 o.Write(&b) 774 p := b.Bytes() 775 isym := &LSym{ 776 Type: objabi.SDATA, // for now, I don't think it matters 777 PkgIdx: goobj.PkgIdxSelf, 778 SymIdx: symidx, 779 P: append([]byte(nil), p...), 780 Size: int64(len(p)), 781 } 782 isym.Set(AttrIndexed, true) 783 symidx++ 784 infosyms = append(infosyms, isym) 785 fn.FuncInfoSym = isym 786 b.Reset() 787 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 { 791 continue 792 } 793 s.PkgIdx = goobj.PkgIdxSelf 794 s.SymIdx = symidx 795 s.Set(AttrIndexed, true) 796 symidx++ 797 infosyms = append(infosyms, s) 798 } 799 } 800 ctxt.defs = append(ctxt.defs, infosyms...) 801 } 802 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. 806 switch aux.Type { 807 case objabi.SDWARFLOC, 808 objabi.SDWARFFCN, 809 objabi.SDWARFABSFCN, 810 objabi.SDWARFLINES, 811 objabi.SDWARFRANGE, 812 objabi.SDWARFVAR: 813 default: 814 return 815 } 816 ctxt.writeSymDebugNamed(aux, "aux for "+par.Name) 817 } 818 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) 825 } 826 ctxt.traverseAuxSyms(traverseAux, fn) 827 } 828 } 829 } 830 831 func (ctxt *Link) writeSymDebug(s *LSym) { 832 ctxt.writeSymDebugNamed(s, s.Name) 833 } 834 835 func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { 836 ver := "" 837 if ctxt.Debugasm > 1 { 838 ver = fmt.Sprintf("<%d>", s.ABI()) 839 } 840 fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver) 841 if s.Type != 0 { 842 fmt.Fprintf(ctxt.Bso, "%v ", s.Type) 843 } 844 if s.Static() { 845 fmt.Fprint(ctxt.Bso, "static ") 846 } 847 if s.DuplicateOK() { 848 fmt.Fprintf(ctxt.Bso, "dupok ") 849 } 850 if s.CFunc() { 851 fmt.Fprintf(ctxt.Bso, "cfunc ") 852 } 853 if s.NoSplit() { 854 fmt.Fprintf(ctxt.Bso, "nosplit ") 855 } 856 if s.Func() != nil && s.Func().FuncFlag&abi.FuncFlagTopFrame != 0 { 857 fmt.Fprintf(ctxt.Bso, "topframe ") 858 } 859 if s.Func() != nil && s.Func().FuncFlag&abi.FuncFlagAsm != 0 { 860 fmt.Fprintf(ctxt.Bso, "asm ") 861 } 862 fmt.Fprintf(ctxt.Bso, "size=%d", s.Size) 863 if s.Type == objabi.STEXT { 864 fn := s.Func() 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)) 866 if s.Leaf() { 867 fmt.Fprintf(ctxt.Bso, " leaf") 868 } 869 } 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()) 876 } else { 877 p.InnermostString(ctxt.Bso) 878 } 879 fmt.Fprintln(ctxt.Bso) 880 } 881 } 882 for i := 0; i < len(s.P); i += 16 { 883 fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) 884 j := i 885 for ; j < i+16 && j < len(s.P); j++ { 886 fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) 887 } 888 for ; j < i+16; j++ { 889 fmt.Fprintf(ctxt.Bso, " ") 890 } 891 fmt.Fprintf(ctxt.Bso, " ") 892 for j = i; j < i+16 && j < len(s.P); j++ { 893 c := int(s.P[j]) 894 b := byte('.') 895 if ' ' <= c && c <= 0x7e { 896 b = byte(c) 897 } 898 ctxt.Bso.WriteByte(b) 899 } 900 901 fmt.Fprintf(ctxt.Bso, "\n") 902 } 903 904 sort.Sort(relocByOff(s.R)) // generate stable output 905 for _, r := range s.R { 906 name := "" 907 ver := "" 908 if r.Sym != nil { 909 name = r.Sym.Name 910 if ctxt.Debugasm > 1 { 911 ver = fmt.Sprintf("<%d>", r.Sym.ABI()) 912 } 913 } else if r.Type == objabi.R_TLS_LE { 914 name = "TLS" 915 } 916 if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) { 917 fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%v %s%s+%x\n", int(r.Off), r.Siz, r.Type, name, ver, uint64(r.Add)) 918 } else { 919 fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%v %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add) 920 } 921 } 922 } 923 924 // relocByOff sorts relocations by their offsets. 925 type relocByOff []Reloc 926 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] }