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 7package obj 8 9import ( 10 "bytes" 11 "cmd/internal/bio" 12 "cmd/internal/goobj" 13 "cmd/internal/notsha256" 14 "cmd/internal/objabi" 15 "cmd/internal/sys" 16 "encoding/binary" 17 "fmt" 18 "internal/abi" 19 "io" 20 "log" 21 "os" 22 "path/filepath" 23 "sort" 24 "strings" 25) 26 27const UnlinkablePkg = "<unlinkable>" // invalid package path, used when compiled without -p flag 28 29// Entry point of writing new object file. 30func 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 if ctxt.Std { 61 flags |= goobj.ObjFlagStd 62 } 63 h := goobj.Header{ 64 Magic: goobj.Magic, 65 Fingerprint: ctxt.Fingerprint, 66 Flags: flags, 67 } 68 h.Write(w.Writer) 69 70 // String table 71 w.StringTable() 72 73 // Autolib 74 h.Offsets[goobj.BlkAutolib] = w.Offset() 75 for i := range ctxt.Imports { 76 ctxt.Imports[i].Write(w.Writer) 77 } 78 79 // Package references 80 h.Offsets[goobj.BlkPkgIdx] = w.Offset() 81 for _, pkg := range w.pkglist { 82 w.StringRef(pkg) 83 } 84 85 // File table (for DWARF and pcln generation). 86 h.Offsets[goobj.BlkFile] = w.Offset() 87 for _, f := range ctxt.PosTable.FileTable() { 88 w.StringRef(filepath.ToSlash(f)) 89 } 90 91 // Symbol definitions 92 h.Offsets[goobj.BlkSymdef] = w.Offset() 93 for _, s := range ctxt.defs { 94 w.Sym(s) 95 } 96 97 // Short hashed symbol definitions 98 h.Offsets[goobj.BlkHashed64def] = w.Offset() 99 for _, s := range ctxt.hashed64defs { 100 w.Sym(s) 101 } 102 103 // Hashed symbol definitions 104 h.Offsets[goobj.BlkHasheddef] = w.Offset() 105 for _, s := range ctxt.hasheddefs { 106 w.Sym(s) 107 } 108 109 // Non-pkg symbol definitions 110 h.Offsets[goobj.BlkNonpkgdef] = w.Offset() 111 for _, s := range ctxt.nonpkgdefs { 112 w.Sym(s) 113 } 114 115 // Non-pkg symbol references 116 h.Offsets[goobj.BlkNonpkgref] = w.Offset() 117 for _, s := range ctxt.nonpkgrefs { 118 w.Sym(s) 119 } 120 121 // Referenced package symbol flags 122 h.Offsets[goobj.BlkRefFlags] = w.Offset() 123 w.refFlags() 124 125 // Hashes 126 h.Offsets[goobj.BlkHash64] = w.Offset() 127 for _, s := range ctxt.hashed64defs { 128 w.Hash64(s) 129 } 130 h.Offsets[goobj.BlkHash] = w.Offset() 131 for _, s := range ctxt.hasheddefs { 132 w.Hash(s) 133 } 134 // TODO: hashedrefs unused/unsupported for now 135 136 // Reloc indexes 137 h.Offsets[goobj.BlkRelocIdx] = w.Offset() 138 nreloc := uint32(0) 139 lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs} 140 for _, list := range lists { 141 for _, s := range list { 142 w.Uint32(nreloc) 143 nreloc += uint32(len(s.R)) 144 } 145 } 146 w.Uint32(nreloc) 147 148 // Symbol Info indexes 149 h.Offsets[goobj.BlkAuxIdx] = w.Offset() 150 naux := uint32(0) 151 for _, list := range lists { 152 for _, s := range list { 153 w.Uint32(naux) 154 naux += uint32(nAuxSym(s)) 155 } 156 } 157 w.Uint32(naux) 158 159 // Data indexes 160 h.Offsets[goobj.BlkDataIdx] = w.Offset() 161 dataOff := int64(0) 162 for _, list := range lists { 163 for _, s := range list { 164 w.Uint32(uint32(dataOff)) 165 dataOff += int64(len(s.P)) 166 if file := s.File(); file != nil { 167 dataOff += int64(file.Size) 168 } 169 } 170 } 171 if int64(uint32(dataOff)) != dataOff { 172 log.Fatalf("data too large") 173 } 174 w.Uint32(uint32(dataOff)) 175 176 // Relocs 177 h.Offsets[goobj.BlkReloc] = w.Offset() 178 for _, list := range lists { 179 for _, s := range list { 180 sort.Sort(relocByOff(s.R)) // some platforms (e.g. PE) requires relocations in address order 181 for i := range s.R { 182 w.Reloc(&s.R[i]) 183 } 184 } 185 } 186 187 // Aux symbol info 188 h.Offsets[goobj.BlkAux] = w.Offset() 189 for _, list := range lists { 190 for _, s := range list { 191 w.Aux(s) 192 } 193 } 194 195 // Data 196 h.Offsets[goobj.BlkData] = w.Offset() 197 for _, list := range lists { 198 for _, s := range list { 199 w.Bytes(s.P) 200 if file := s.File(); file != nil { 201 w.writeFile(ctxt, file) 202 } 203 } 204 } 205 206 // Blocks used only by tools (objdump, nm). 207 208 // Referenced symbol names from other packages 209 h.Offsets[goobj.BlkRefName] = w.Offset() 210 w.refNames() 211 212 h.Offsets[goobj.BlkEnd] = w.Offset() 213 214 // Fix up block offsets in the header 215 end := start + int64(w.Offset()) 216 b.MustSeek(start, 0) 217 h.Write(w.Writer) 218 b.MustSeek(end, 0) 219} 220 221type writer struct { 222 *goobj.Writer 223 filebuf []byte 224 ctxt *Link 225 pkgpath string // the package import path (escaped), "" if unknown 226 pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx 227 228 // scratch space for writing (the Write methods escape 229 // as they are interface calls) 230 tmpSym goobj.Sym 231 tmpReloc goobj.Reloc 232 tmpAux goobj.Aux 233 tmpHash64 goobj.Hash64Type 234 tmpHash goobj.HashType 235 tmpRefFlags goobj.RefFlags 236 tmpRefName goobj.RefName 237} 238 239// prepare package index list 240func (w *writer) init() { 241 w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1) 242 w.pkglist[0] = "" // dummy invalid package for index 0 243 for pkg, i := range w.ctxt.pkgIdx { 244 w.pkglist[i] = pkg 245 } 246} 247 248func (w *writer) writeFile(ctxt *Link, file *FileInfo) { 249 f, err := os.Open(file.Name) 250 if err != nil { 251 ctxt.Diag("%v", err) 252 return 253 } 254 defer f.Close() 255 if w.filebuf == nil { 256 w.filebuf = make([]byte, 1024) 257 } 258 buf := w.filebuf 259 written := int64(0) 260 for { 261 n, err := f.Read(buf) 262 w.Bytes(buf[:n]) 263 written += int64(n) 264 if err == io.EOF { 265 break 266 } 267 if err != nil { 268 ctxt.Diag("%v", err) 269 return 270 } 271 } 272 if written != file.Size { 273 ctxt.Diag("copy %s: unexpected length %d != %d", file.Name, written, file.Size) 274 } 275} 276 277func (w *writer) StringTable() { 278 w.AddString("") 279 for _, p := range w.ctxt.Imports { 280 w.AddString(p.Pkg) 281 } 282 for _, pkg := range w.pkglist { 283 w.AddString(pkg) 284 } 285 w.ctxt.traverseSyms(traverseAll, func(s *LSym) { 286 // Don't put names of builtins into the string table (to save 287 // space). 288 if s.PkgIdx == goobj.PkgIdxBuiltin { 289 return 290 } 291 // TODO: this includes references of indexed symbols from other packages, 292 // for which the linker doesn't need the name. Consider moving them to 293 // a separate block (for tools only). 294 if w.ctxt.Flag_noRefName && s.PkgIdx < goobj.PkgIdxSpecial { 295 // Don't include them if Flag_noRefName 296 return 297 } 298 if strings.HasPrefix(s.Name, `"".`) { 299 w.ctxt.Diag("unqualified symbol name: %v", s.Name) 300 } 301 w.AddString(s.Name) 302 }) 303 304 // All filenames are in the postable. 305 for _, f := range w.ctxt.PosTable.FileTable() { 306 w.AddString(filepath.ToSlash(f)) 307 } 308} 309 310// cutoff is the maximum data section size permitted by the linker 311// (see issue #9862). 312const cutoff = int64(2e9) // 2 GB (or so; looks better in errors than 2^31) 313 314func (w *writer) Sym(s *LSym) { 315 name := s.Name 316 abi := uint16(s.ABI()) 317 if s.Static() { 318 abi = goobj.SymABIstatic 319 } 320 flag := uint8(0) 321 if s.DuplicateOK() { 322 flag |= goobj.SymFlagDupok 323 } 324 if s.Local() { 325 flag |= goobj.SymFlagLocal 326 } 327 if s.MakeTypelink() { 328 flag |= goobj.SymFlagTypelink 329 } 330 if s.Leaf() { 331 flag |= goobj.SymFlagLeaf 332 } 333 if s.NoSplit() { 334 flag |= goobj.SymFlagNoSplit 335 } 336 if s.ReflectMethod() { 337 flag |= goobj.SymFlagReflectMethod 338 } 339 if strings.HasPrefix(s.Name, "type:") && s.Name[5] != '.' && s.Type == objabi.SRODATA { 340 flag |= goobj.SymFlagGoType 341 } 342 flag2 := uint8(0) 343 if s.UsedInIface() { 344 flag2 |= goobj.SymFlagUsedInIface 345 } 346 if strings.HasPrefix(s.Name, "go:itab.") && s.Type == objabi.SRODATA { 347 flag2 |= goobj.SymFlagItab 348 } 349 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) { 350 flag2 |= goobj.SymFlagDict 351 } 352 if s.IsPkgInit() { 353 flag2 |= goobj.SymFlagPkgInit 354 } 355 if s.IsLinkname() || (w.ctxt.IsAsm && name != "") || name == "main.main" { 356 // Assembly reference is treated the same as linkname, 357 // but not for unnamed (aux) symbols. 358 // The runtime linknames main.main. 359 flag2 |= goobj.SymFlagLinkname 360 } 361 if s.ABIWrapper() { 362 flag2 |= goobj.SymFlagABIWrapper 363 } 364 if strings.HasPrefix(name, "gofile..") { 365 name = filepath.ToSlash(name) 366 } 367 var align uint32 368 if fn := s.Func(); fn != nil { 369 align = uint32(fn.Align) 370 } 371 if s.ContentAddressable() && s.Size != 0 { 372 // We generally assume data symbols are naturally aligned 373 // (e.g. integer constants), except for strings and a few 374 // compiler-emitted funcdata. If we dedup a string symbol and 375 // a non-string symbol with the same content, we should keep 376 // the largest alignment. 377 // TODO: maybe the compiler could set the alignment for all 378 // data symbols more carefully. 379 switch { 380 case strings.HasPrefix(s.Name, "go:string."), 381 strings.HasPrefix(name, "type:.namedata."), 382 strings.HasPrefix(name, "type:.importpath."), 383 strings.HasSuffix(name, ".opendefer"), 384 strings.HasSuffix(name, ".arginfo0"), 385 strings.HasSuffix(name, ".arginfo1"), 386 strings.HasSuffix(name, ".argliveinfo"): 387 // These are just bytes, or varints. 388 align = 1 389 case strings.HasPrefix(name, "gclocals·"): 390 // It has 32-bit fields. 391 align = 4 392 default: 393 switch { 394 case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0: 395 align = 8 396 case s.Size%4 == 0: 397 align = 4 398 case s.Size%2 == 0: 399 align = 2 400 default: 401 align = 1 402 } 403 } 404 } 405 if s.Size > cutoff { 406 w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, cutoff) 407 } 408 o := &w.tmpSym 409 o.SetName(name, w.Writer) 410 o.SetABI(abi) 411 o.SetType(uint8(s.Type)) 412 o.SetFlag(flag) 413 o.SetFlag2(flag2) 414 o.SetSiz(uint32(s.Size)) 415 o.SetAlign(align) 416 o.Write(w.Writer) 417} 418 419func (w *writer) Hash64(s *LSym) { 420 if !s.ContentAddressable() || len(s.R) != 0 { 421 panic("Hash of non-content-addressable symbol") 422 } 423 w.tmpHash64 = contentHash64(s) 424 w.Bytes(w.tmpHash64[:]) 425} 426 427func (w *writer) Hash(s *LSym) { 428 if !s.ContentAddressable() { 429 panic("Hash of non-content-addressable symbol") 430 } 431 w.tmpHash = w.contentHash(s) 432 w.Bytes(w.tmpHash[:]) 433} 434 435// contentHashSection returns a mnemonic for s's section. 436// The goal is to prevent content-addressability from moving symbols between sections. 437// contentHashSection only distinguishes between sets of sections for which this matters. 438// Allowing flexibility increases the effectiveness of content-addressability. 439// But in some cases, such as doing addressing based on a base symbol, 440// we need to ensure that a symbol is always in a particular section. 441// Some of these conditions are duplicated in cmd/link/internal/ld.(*Link).symtab. 442// TODO: instead of duplicating them, have the compiler decide where symbols go. 443func contentHashSection(s *LSym) byte { 444 name := s.Name 445 if s.IsPcdata() { 446 return 'P' 447 } 448 if strings.HasPrefix(name, "gcargs.") || 449 strings.HasPrefix(name, "gclocals.") || 450 strings.HasPrefix(name, "gclocals·") || 451 strings.HasSuffix(name, ".opendefer") || 452 strings.HasSuffix(name, ".arginfo0") || 453 strings.HasSuffix(name, ".arginfo1") || 454 strings.HasSuffix(name, ".argliveinfo") || 455 strings.HasSuffix(name, ".wrapinfo") || 456 strings.HasSuffix(name, ".args_stackmap") || 457 strings.HasSuffix(name, ".stkobj") { 458 return 'F' // go:func.* or go:funcrel.* 459 } 460 if strings.HasPrefix(name, "type:") { 461 return 'T' 462 } 463 return 0 464} 465 466func contentHash64(s *LSym) goobj.Hash64Type { 467 if contentHashSection(s) != 0 { 468 panic("short hash of non-default-section sym " + s.Name) 469 } 470 var b goobj.Hash64Type 471 copy(b[:], s.P) 472 return b 473} 474 475// Compute the content hash for a content-addressable symbol. 476// We build a content hash based on its content and relocations. 477// Depending on the category of the referenced symbol, we choose 478// different hash algorithms such that the hash is globally 479// consistent. 480// - For referenced content-addressable symbol, its content hash 481// is globally consistent. 482// - For package symbol and builtin symbol, its local index is 483// globally consistent. 484// - For non-package symbol, its fully-expanded name is globally 485// consistent. For now, we require we know the current package 486// path so we can always expand symbol names. (Otherwise, 487// symbols with relocations are not considered hashable.) 488// 489// For now, we assume there is no circular dependencies among 490// hashed symbols. 491func (w *writer) contentHash(s *LSym) goobj.HashType { 492 h := notsha256.New() 493 var tmp [14]byte 494 495 // Include the size of the symbol in the hash. 496 // This preserves the length of symbols, preventing the following two symbols 497 // from hashing the same: 498 // 499 // [2]int{1,2} ≠ [10]int{1,2,0,0,0...} 500 // 501 // In this case, if the smaller symbol is alive, the larger is not kept unless 502 // needed. 503 binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size)) 504 // Some symbols require being in separate sections. 505 tmp[8] = contentHashSection(s) 506 h.Write(tmp[:9]) 507 508 // The compiler trims trailing zeros _sometimes_. We just do 509 // it always. 510 h.Write(bytes.TrimRight(s.P, "\x00")) 511 for i := range s.R { 512 r := &s.R[i] 513 binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off)) 514 tmp[4] = r.Siz 515 tmp[5] = uint8(r.Type) 516 binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add)) 517 h.Write(tmp[:]) 518 rs := r.Sym 519 if rs == nil { 520 fmt.Printf("symbol: %s\n", s) 521 fmt.Printf("relocation: %#v\n", r) 522 panic("nil symbol target in relocation") 523 } 524 switch rs.PkgIdx { 525 case goobj.PkgIdxHashed64: 526 h.Write([]byte{0}) 527 t := contentHash64(rs) 528 h.Write(t[:]) 529 case goobj.PkgIdxHashed: 530 h.Write([]byte{1}) 531 t := w.contentHash(rs) 532 h.Write(t[:]) 533 case goobj.PkgIdxNone: 534 h.Write([]byte{2}) 535 io.WriteString(h, rs.Name) // name is already expanded at this point 536 case goobj.PkgIdxBuiltin: 537 h.Write([]byte{3}) 538 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) 539 h.Write(tmp[:4]) 540 case goobj.PkgIdxSelf: 541 io.WriteString(h, w.pkgpath) 542 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) 543 h.Write(tmp[:4]) 544 default: 545 io.WriteString(h, rs.Pkg) 546 binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx)) 547 h.Write(tmp[:4]) 548 } 549 } 550 var b goobj.HashType 551 copy(b[:], h.Sum(nil)) 552 return b 553} 554 555func makeSymRef(s *LSym) goobj.SymRef { 556 if s == nil { 557 return goobj.SymRef{} 558 } 559 if s.PkgIdx == 0 || !s.Indexed() { 560 fmt.Printf("unindexed symbol reference: %v\n", s) 561 panic("unindexed symbol reference") 562 } 563 return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)} 564} 565 566func (w *writer) Reloc(r *Reloc) { 567 o := &w.tmpReloc 568 o.SetOff(r.Off) 569 o.SetSiz(r.Siz) 570 o.SetType(uint16(r.Type)) 571 o.SetAdd(r.Add) 572 o.SetSym(makeSymRef(r.Sym)) 573 o.Write(w.Writer) 574} 575 576func (w *writer) aux1(typ uint8, rs *LSym) { 577 o := &w.tmpAux 578 o.SetType(typ) 579 o.SetSym(makeSymRef(rs)) 580 o.Write(w.Writer) 581} 582 583func (w *writer) Aux(s *LSym) { 584 if s.Gotype != nil { 585 w.aux1(goobj.AuxGotype, s.Gotype) 586 } 587 if fn := s.Func(); fn != nil { 588 w.aux1(goobj.AuxFuncInfo, fn.FuncInfoSym) 589 590 for _, d := range fn.Pcln.Funcdata { 591 w.aux1(goobj.AuxFuncdata, d) 592 } 593 594 if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { 595 w.aux1(goobj.AuxDwarfInfo, fn.dwarfInfoSym) 596 } 597 if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { 598 w.aux1(goobj.AuxDwarfLoc, fn.dwarfLocSym) 599 } 600 if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { 601 w.aux1(goobj.AuxDwarfRanges, fn.dwarfRangesSym) 602 } 603 if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { 604 w.aux1(goobj.AuxDwarfLines, fn.dwarfDebugLinesSym) 605 } 606 if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { 607 w.aux1(goobj.AuxPcsp, fn.Pcln.Pcsp) 608 } 609 if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { 610 w.aux1(goobj.AuxPcfile, fn.Pcln.Pcfile) 611 } 612 if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { 613 w.aux1(goobj.AuxPcline, fn.Pcln.Pcline) 614 } 615 if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { 616 w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline) 617 } 618 if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 { 619 w.aux1(goobj.AuxSehUnwindInfo, fn.sehUnwindInfoSym) 620 } 621 for _, pcSym := range fn.Pcln.Pcdata { 622 w.aux1(goobj.AuxPcdata, pcSym) 623 } 624 if fn.WasmImportSym != nil { 625 if fn.WasmImportSym.Size == 0 { 626 panic("wasmimport aux sym must have non-zero size") 627 } 628 w.aux1(goobj.AuxWasmImport, fn.WasmImportSym) 629 } 630 } else if v := s.VarInfo(); v != nil { 631 if v.dwarfInfoSym != nil && v.dwarfInfoSym.Size != 0 { 632 w.aux1(goobj.AuxDwarfInfo, v.dwarfInfoSym) 633 } 634 } 635} 636 637// Emits flags of referenced indexed symbols. 638func (w *writer) refFlags() { 639 seen := make(map[*LSym]bool) 640 w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs 641 switch rs.PkgIdx { 642 case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference 643 return 644 case goobj.PkgIdxInvalid: 645 panic("unindexed symbol reference") 646 } 647 if seen[rs] { 648 return 649 } 650 seen[rs] = true 651 symref := makeSymRef(rs) 652 flag2 := uint8(0) 653 if rs.UsedInIface() { 654 flag2 |= goobj.SymFlagUsedInIface 655 } 656 if flag2 == 0 { 657 return // no need to write zero flags 658 } 659 o := &w.tmpRefFlags 660 o.SetSym(symref) 661 o.SetFlag2(flag2) 662 o.Write(w.Writer) 663 }) 664} 665 666// Emits names of referenced indexed symbols, used by tools (objdump, nm) 667// only. 668func (w *writer) refNames() { 669 if w.ctxt.Flag_noRefName { 670 return 671 } 672 seen := make(map[*LSym]bool) 673 w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs 674 switch rs.PkgIdx { 675 case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference 676 return 677 case goobj.PkgIdxInvalid: 678 panic("unindexed symbol reference") 679 } 680 if seen[rs] { 681 return 682 } 683 seen[rs] = true 684 symref := makeSymRef(rs) 685 o := &w.tmpRefName 686 o.SetSym(symref) 687 o.SetName(rs.Name, w.Writer) 688 o.Write(w.Writer) 689 }) 690 // TODO: output in sorted order? 691 // Currently tools (cmd/internal/goobj package) doesn't use mmap, 692 // and it just read it into a map in memory upfront. If it uses 693 // mmap, if the output is sorted, it probably could avoid reading 694 // into memory and just do lookups in the mmap'd object file. 695} 696 697// return the number of aux symbols s have. 698func nAuxSym(s *LSym) int { 699 n := 0 700 if s.Gotype != nil { 701 n++ 702 } 703 if fn := s.Func(); fn != nil { 704 // FuncInfo is an aux symbol, each Funcdata is an aux symbol 705 n += 1 + len(fn.Pcln.Funcdata) 706 if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { 707 n++ 708 } 709 if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { 710 n++ 711 } 712 if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { 713 n++ 714 } 715 if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { 716 n++ 717 } 718 if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { 719 n++ 720 } 721 if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { 722 n++ 723 } 724 if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { 725 n++ 726 } 727 if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { 728 n++ 729 } 730 if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 { 731 n++ 732 } 733 n += len(fn.Pcln.Pcdata) 734 if fn.WasmImport != nil { 735 if fn.WasmImportSym == nil || fn.WasmImportSym.Size == 0 { 736 panic("wasmimport aux sym must exist and have non-zero size") 737 } 738 n++ 739 } 740 } else if v := s.VarInfo(); v != nil { 741 if v.dwarfInfoSym != nil && v.dwarfInfoSym.Size != 0 { 742 n++ 743 } 744 } 745 return n 746} 747 748// generate symbols for FuncInfo. 749func genFuncInfoSyms(ctxt *Link) { 750 infosyms := make([]*LSym, 0, len(ctxt.Text)) 751 var b bytes.Buffer 752 symidx := int32(len(ctxt.defs)) 753 for _, s := range ctxt.Text { 754 fn := s.Func() 755 if fn == nil { 756 continue 757 } 758 o := goobj.FuncInfo{ 759 Args: uint32(fn.Args), 760 Locals: uint32(fn.Locals), 761 FuncID: fn.FuncID, 762 FuncFlag: fn.FuncFlag, 763 StartLine: fn.StartLine, 764 } 765 pc := &fn.Pcln 766 i := 0 767 o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles)) 768 for f := range pc.UsedFiles { 769 o.File[i] = f 770 i++ 771 } 772 sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] }) 773 o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes)) 774 for i, inl := range pc.InlTree.nodes { 775 f, l := ctxt.getFileIndexAndLine(inl.Pos) 776 o.InlTree[i] = goobj.InlTreeNode{ 777 Parent: int32(inl.Parent), 778 File: goobj.CUFileIndex(f), 779 Line: l, 780 Func: makeSymRef(inl.Func), 781 ParentPC: inl.ParentPC, 782 } 783 } 784 785 o.Write(&b) 786 p := b.Bytes() 787 isym := &LSym{ 788 Type: objabi.SDATA, // for now, I don't think it matters 789 PkgIdx: goobj.PkgIdxSelf, 790 SymIdx: symidx, 791 P: append([]byte(nil), p...), 792 Size: int64(len(p)), 793 } 794 isym.Set(AttrIndexed, true) 795 symidx++ 796 infosyms = append(infosyms, isym) 797 fn.FuncInfoSym = isym 798 b.Reset() 799 800 auxsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym, fn.WasmImportSym} 801 for _, s := range auxsyms { 802 if s == nil || s.Size == 0 { 803 continue 804 } 805 if s.OnList() { 806 panic("a symbol is added to defs multiple times") 807 } 808 s.PkgIdx = goobj.PkgIdxSelf 809 s.SymIdx = symidx 810 s.Set(AttrIndexed, true) 811 s.Set(AttrOnList, true) 812 symidx++ 813 infosyms = append(infosyms, s) 814 } 815 } 816 ctxt.defs = append(ctxt.defs, infosyms...) 817} 818 819func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) { 820 // Most aux symbols (ex: funcdata) are not interesting-- 821 // pick out just the DWARF ones for now. 822 switch aux.Type { 823 case objabi.SDWARFLOC, 824 objabi.SDWARFFCN, 825 objabi.SDWARFABSFCN, 826 objabi.SDWARFLINES, 827 objabi.SDWARFRANGE, 828 objabi.SDWARFVAR: 829 default: 830 return 831 } 832 ctxt.writeSymDebugNamed(aux, "aux for "+par.Name) 833} 834 835func debugAsmEmit(ctxt *Link) { 836 if ctxt.Debugasm > 0 { 837 ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug) 838 if ctxt.Debugasm > 1 { 839 fn := func(par *LSym, aux *LSym) { 840 writeAuxSymDebug(ctxt, par, aux) 841 } 842 ctxt.traverseAuxSyms(traverseAux, fn) 843 } 844 } 845} 846 847func (ctxt *Link) writeSymDebug(s *LSym) { 848 ctxt.writeSymDebugNamed(s, s.Name) 849} 850 851func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { 852 ver := "" 853 if ctxt.Debugasm > 1 { 854 ver = fmt.Sprintf("<%d>", s.ABI()) 855 if ctxt.Debugasm > 2 { 856 ver += fmt.Sprintf("<idx %d %d>", s.PkgIdx, s.SymIdx) 857 } 858 } 859 fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver) 860 if s.Type != 0 { 861 fmt.Fprintf(ctxt.Bso, "%v ", s.Type) 862 } 863 if s.Static() { 864 fmt.Fprint(ctxt.Bso, "static ") 865 } 866 if s.DuplicateOK() { 867 fmt.Fprintf(ctxt.Bso, "dupok ") 868 } 869 if s.CFunc() { 870 fmt.Fprintf(ctxt.Bso, "cfunc ") 871 } 872 if s.NoSplit() { 873 fmt.Fprintf(ctxt.Bso, "nosplit ") 874 } 875 if s.Func() != nil && s.Func().FuncFlag&abi.FuncFlagTopFrame != 0 { 876 fmt.Fprintf(ctxt.Bso, "topframe ") 877 } 878 if s.Func() != nil && s.Func().FuncFlag&abi.FuncFlagAsm != 0 { 879 fmt.Fprintf(ctxt.Bso, "asm ") 880 } 881 fmt.Fprintf(ctxt.Bso, "size=%d", s.Size) 882 if s.Type == objabi.STEXT { 883 fn := s.Func() 884 fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x align=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID), uint64(fn.Align)) 885 if s.Leaf() { 886 fmt.Fprintf(ctxt.Bso, " leaf") 887 } 888 } 889 fmt.Fprintf(ctxt.Bso, "\n") 890 if s.Type == objabi.STEXT { 891 for p := s.Func().Text; p != nil; p = p.Link { 892 fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc))) 893 if ctxt.Debugasm > 1 { 894 io.WriteString(ctxt.Bso, p.String()) 895 } else { 896 p.InnermostString(ctxt.Bso) 897 } 898 fmt.Fprintln(ctxt.Bso) 899 } 900 } 901 for i := 0; i < len(s.P); i += 16 { 902 fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) 903 j := i 904 for ; j < i+16 && j < len(s.P); j++ { 905 fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) 906 } 907 for ; j < i+16; j++ { 908 fmt.Fprintf(ctxt.Bso, " ") 909 } 910 fmt.Fprintf(ctxt.Bso, " ") 911 for j = i; j < i+16 && j < len(s.P); j++ { 912 c := int(s.P[j]) 913 b := byte('.') 914 if ' ' <= c && c <= 0x7e { 915 b = byte(c) 916 } 917 ctxt.Bso.WriteByte(b) 918 } 919 920 fmt.Fprintf(ctxt.Bso, "\n") 921 } 922 923 sort.Sort(relocByOff(s.R)) // generate stable output 924 for _, r := range s.R { 925 name := "" 926 ver := "" 927 if r.Sym != nil { 928 name = r.Sym.Name 929 if ctxt.Debugasm > 1 { 930 ver = fmt.Sprintf("<%d>", r.Sym.ABI()) 931 } 932 } else if r.Type == objabi.R_TLS_LE { 933 name = "TLS" 934 } 935 if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) { 936 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)) 937 } else { 938 fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%v %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add) 939 } 940 } 941} 942 943// relocByOff sorts relocations by their offsets. 944type relocByOff []Reloc 945 946func (x relocByOff) Len() int { return len(x) } 947func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off } 948func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 949