github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/ld/go.go (about) 1 // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/ld/go.go 2 3 // Copyright 2009 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 // go-specific code shared across loaders (5l, 6l, 8l). 8 9 package ld 10 11 import ( 12 "bytes" 13 "rsc.io/tmp/bootstrap/internal/obj" 14 "fmt" 15 "os" 16 "strconv" 17 "strings" 18 ) 19 20 // go-specific code shared across loaders (5l, 6l, 8l). 21 22 // replace all "". with pkg. 23 func expandpkg(t0 string, pkg string) string { 24 return strings.Replace(t0, `"".`, pkg+".", -1) 25 } 26 27 // accumulate all type information from .6 files. 28 // check for inconsistencies. 29 30 // TODO: 31 // generate debugging section in binary. 32 // once the dust settles, try to move some code to 33 // libmach, so that other linkers and ar can share. 34 35 /* 36 * package import data 37 */ 38 type Import struct { 39 prefix string // "type", "var", "func", "const" 40 name string 41 def string 42 file string 43 } 44 45 // importmap records type information about imported symbols to detect inconsistencies. 46 // Entries are keyed by qualified symbol name (e.g., "runtime.Callers" or "net/url.Error"). 47 var importmap = map[string]*Import{} 48 49 func lookupImport(name string) *Import { 50 if x, ok := importmap[name]; ok { 51 return x 52 } 53 x := &Import{name: name} 54 importmap[name] = x 55 return x 56 } 57 58 func ldpkg(f *Biobuf, pkg string, length int64, filename string, whence int) { 59 var p0, p1 int 60 61 if Debug['g'] != 0 { 62 return 63 } 64 65 if int64(int(length)) != length { 66 fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) 67 if Debug['u'] != 0 { 68 errorexit() 69 } 70 return 71 } 72 73 bdata := make([]byte, length) 74 if int64(Bread(f, bdata)) != length { 75 fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) 76 if Debug['u'] != 0 { 77 errorexit() 78 } 79 return 80 } 81 data := string(bdata) 82 83 // first \n$$ marks beginning of exports - skip rest of line 84 p0 = strings.Index(data, "\n$$") 85 if p0 < 0 { 86 if Debug['u'] != 0 && whence != ArchiveObj { 87 Exitf("cannot find export data in %s", filename) 88 } 89 return 90 } 91 92 p0 += 3 93 for p0 < len(data) && data[p0] != '\n' { 94 p0++ 95 } 96 97 // second marks end of exports / beginning of local data 98 p1 = strings.Index(data[p0:], "\n$$") 99 if p1 < 0 { 100 fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename) 101 if Debug['u'] != 0 { 102 errorexit() 103 } 104 return 105 } 106 p1 += p0 107 108 for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { 109 p0++ 110 } 111 if p0 < p1 { 112 if !strings.HasPrefix(data[p0:], "package ") { 113 fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:]) 114 if Debug['u'] != 0 { 115 errorexit() 116 } 117 return 118 } 119 120 p0 += 8 121 for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { 122 p0++ 123 } 124 pname := p0 125 for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' { 126 p0++ 127 } 128 if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) { 129 Exitf("load of unsafe package %s", filename) 130 } 131 132 name := data[pname:p0] 133 for p0 < p1 && data[p0] != '\n' { 134 p0++ 135 } 136 if p0 < p1 { 137 p0++ 138 } 139 140 if pkg == "main" && name != "main" { 141 Exitf("%s: not package main (package %s)", filename, name) 142 } 143 144 loadpkgdata(filename, pkg, data[p0:p1]) 145 } 146 147 // __.PKGDEF has no cgo section - those are in the C compiler-generated object files. 148 if whence == Pkgdef { 149 return 150 } 151 152 // look for cgo section 153 p0 = strings.Index(data[p1:], "\n$$ // cgo") 154 if p0 >= 0 { 155 p0 += p1 156 i := strings.IndexByte(data[p0+1:], '\n') 157 if i < 0 { 158 fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) 159 if Debug['u'] != 0 { 160 errorexit() 161 } 162 return 163 } 164 p0 += 1 + i 165 166 p1 = strings.Index(data[p0:], "\n$$") 167 if p1 < 0 { 168 p1 = strings.Index(data[p0:], "\n!\n") 169 } 170 if p1 < 0 { 171 fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) 172 if Debug['u'] != 0 { 173 errorexit() 174 } 175 return 176 } 177 p1 += p0 178 179 loadcgo(filename, pkg, data[p0:p1]) 180 } 181 } 182 183 func loadpkgdata(file string, pkg string, data string) { 184 var prefix string 185 var name string 186 var def string 187 188 p := data 189 for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 { 190 x := lookupImport(name) 191 if x.prefix == "" { 192 x.prefix = prefix 193 x.def = def 194 x.file = file 195 } else if x.prefix != prefix { 196 fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name) 197 fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", x.file, x.prefix, name) 198 fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", file, prefix, name) 199 nerrors++ 200 } else if x.def != def { 201 fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name) 202 fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", x.file, x.prefix, name, x.def) 203 fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", file, prefix, name, def) 204 nerrors++ 205 } 206 } 207 } 208 209 func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int { 210 // skip white space 211 p := *pp 212 213 loop: 214 for len(p) > 0 && (p[0] == ' ' || p[0] == '\t' || p[0] == '\n') { 215 p = p[1:] 216 } 217 if len(p) == 0 || strings.HasPrefix(p, "$$\n") { 218 return 0 219 } 220 221 // prefix: (var|type|func|const) 222 prefix := p 223 224 if len(p) < 7 { 225 return -1 226 } 227 if strings.HasPrefix(p, "var ") { 228 p = p[4:] 229 } else if strings.HasPrefix(p, "type ") { 230 p = p[5:] 231 } else if strings.HasPrefix(p, "func ") { 232 p = p[5:] 233 } else if strings.HasPrefix(p, "const ") { 234 p = p[6:] 235 } else if strings.HasPrefix(p, "import ") { 236 p = p[7:] 237 for len(p) > 0 && p[0] != ' ' { 238 p = p[1:] 239 } 240 p = p[1:] 241 line := p 242 for len(p) > 0 && p[0] != '\n' { 243 p = p[1:] 244 } 245 if len(p) == 0 { 246 fmt.Fprintf(os.Stderr, "%s: %s: confused in import line\n", os.Args[0], file) 247 nerrors++ 248 return -1 249 } 250 line = line[:len(line)-len(p)] 251 line = strings.TrimSuffix(line, " // indirect") 252 path, err := strconv.Unquote(line) 253 if err != nil { 254 fmt.Fprintf(os.Stderr, "%s: %s: confused in import path: %q\n", os.Args[0], file, line) 255 nerrors++ 256 return -1 257 } 258 p = p[1:] 259 imported(pkg, path) 260 goto loop 261 } else { 262 fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix) 263 nerrors++ 264 return -1 265 } 266 267 prefix = prefix[:len(prefix)-len(p)-1] 268 269 // name: a.b followed by space 270 name := p 271 272 inquote := false 273 for len(p) > 0 { 274 if p[0] == ' ' && !inquote { 275 break 276 } 277 278 if p[0] == '\\' { 279 p = p[1:] 280 } else if p[0] == '"' { 281 inquote = !inquote 282 } 283 284 p = p[1:] 285 } 286 287 if len(p) == 0 { 288 return -1 289 } 290 name = name[:len(name)-len(p)] 291 p = p[1:] 292 293 // def: free form to new line 294 def := p 295 296 for len(p) > 0 && p[0] != '\n' { 297 p = p[1:] 298 } 299 if len(p) == 0 { 300 return -1 301 } 302 def = def[:len(def)-len(p)] 303 var defbuf *bytes.Buffer 304 p = p[1:] 305 306 // include methods on successive lines in def of named type 307 var meth string 308 for parsemethod(&p, &meth) > 0 { 309 if defbuf == nil { 310 defbuf = new(bytes.Buffer) 311 defbuf.WriteString(def) 312 } 313 defbuf.WriteString("\n\t") 314 defbuf.WriteString(meth) 315 } 316 if defbuf != nil { 317 def = defbuf.String() 318 } 319 320 name = expandpkg(name, pkg) 321 def = expandpkg(def, pkg) 322 323 // done 324 *pp = p 325 326 *prefixp = prefix 327 *namep = name 328 *defp = def 329 return 1 330 } 331 332 func parsemethod(pp *string, methp *string) int { 333 // skip white space 334 p := *pp 335 336 for len(p) > 0 && (p[0] == ' ' || p[0] == '\t') { 337 p = p[1:] 338 } 339 if len(p) == 0 { 340 return 0 341 } 342 343 // might be a comment about the method 344 if strings.HasPrefix(p, "//") { 345 goto useline 346 } 347 348 // if it says "func (", it's a method 349 if strings.HasPrefix(p, "func (") { 350 goto useline 351 } 352 return 0 353 354 // definition to end of line 355 useline: 356 *methp = p 357 358 for len(p) > 0 && p[0] != '\n' { 359 p = p[1:] 360 } 361 if len(p) == 0 { 362 fmt.Fprintf(os.Stderr, "%s: lost end of line in method definition\n", os.Args[0]) 363 *pp = "" 364 return -1 365 } 366 367 *methp = (*methp)[:len(*methp)-len(p)] 368 *pp = p[1:] 369 return 1 370 } 371 372 func loadcgo(file string, pkg string, p string) { 373 var next string 374 var q string 375 var f []string 376 var local string 377 var remote string 378 var lib string 379 var s *LSym 380 381 p0 := "" 382 for ; p != ""; p = next { 383 if i := strings.Index(p, "\n"); i >= 0 { 384 p, next = p[:i], p[i+1:] 385 } else { 386 next = "" 387 } 388 389 p0 = p // save for error message 390 f = tokenize(p) 391 if len(f) == 0 { 392 continue 393 } 394 395 if f[0] == "cgo_import_dynamic" { 396 if len(f) < 2 || len(f) > 4 { 397 goto err 398 } 399 400 local = f[1] 401 remote = local 402 if len(f) > 2 { 403 remote = f[2] 404 } 405 lib = "" 406 if len(f) > 3 { 407 lib = f[3] 408 } 409 410 if Debug['d'] != 0 { 411 fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file) 412 nerrors++ 413 return 414 } 415 416 if local == "_" && remote == "_" { 417 // allow #pragma dynimport _ _ "foo.so" 418 // to force a link of foo.so. 419 havedynamic = 1 420 421 Thearch.Adddynlib(lib) 422 continue 423 } 424 425 local = expandpkg(local, pkg) 426 q = "" 427 if i := strings.Index(remote, "#"); i >= 0 { 428 remote, q = remote[:i], remote[i+1:] 429 } 430 s = Linklookup(Ctxt, local, 0) 431 if local != f[1] { 432 } 433 if s.Type == 0 || s.Type == obj.SXREF || s.Type == obj.SHOSTOBJ { 434 s.Dynimplib = lib 435 s.Extname = remote 436 s.Dynimpvers = q 437 if s.Type != obj.SHOSTOBJ { 438 s.Type = obj.SDYNIMPORT 439 } 440 havedynamic = 1 441 } 442 443 continue 444 } 445 446 if f[0] == "cgo_import_static" { 447 if len(f) != 2 { 448 goto err 449 } 450 local = f[1] 451 s = Linklookup(Ctxt, local, 0) 452 s.Type = obj.SHOSTOBJ 453 s.Size = 0 454 continue 455 } 456 457 if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" { 458 if len(f) < 2 || len(f) > 3 { 459 goto err 460 } 461 local = f[1] 462 if len(f) > 2 { 463 remote = f[2] 464 } else { 465 remote = local 466 } 467 local = expandpkg(local, pkg) 468 s = Linklookup(Ctxt, local, 0) 469 470 switch Buildmode { 471 case BuildmodeCShared, BuildmodeCArchive: 472 if s == Linklookup(Ctxt, "main", 0) { 473 continue 474 } 475 } 476 477 // export overrides import, for openbsd/cgo. 478 // see issue 4878. 479 if s.Dynimplib != "" { 480 s.Dynimplib = "" 481 s.Extname = "" 482 s.Dynimpvers = "" 483 s.Type = 0 484 } 485 486 if s.Cgoexport == 0 { 487 s.Extname = remote 488 dynexp = append(dynexp, s) 489 } else if s.Extname != remote { 490 fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname, remote) 491 nerrors++ 492 return 493 } 494 495 if f[0] == "cgo_export_static" { 496 s.Cgoexport |= CgoExportStatic 497 } else { 498 s.Cgoexport |= CgoExportDynamic 499 } 500 if local != f[1] { 501 } 502 continue 503 } 504 505 if f[0] == "cgo_dynamic_linker" { 506 if len(f) != 2 { 507 goto err 508 } 509 510 if Debug['I'] == 0 { 511 if interpreter != "" && interpreter != f[1] { 512 fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1]) 513 nerrors++ 514 return 515 } 516 517 interpreter = f[1] 518 } 519 520 continue 521 } 522 523 if f[0] == "cgo_ldflag" { 524 if len(f) != 2 { 525 goto err 526 } 527 ldflag = append(ldflag, f[1]) 528 continue 529 } 530 } 531 532 return 533 534 err: 535 fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0) 536 nerrors++ 537 } 538 539 var markq *LSym 540 541 var emarkq *LSym 542 543 func mark1(s *LSym, parent *LSym) { 544 if s == nil || s.Reachable { 545 return 546 } 547 if strings.HasPrefix(s.Name, "go.weak.") { 548 return 549 } 550 s.Reachable = true 551 s.Reachparent = parent 552 if markq == nil { 553 markq = s 554 } else { 555 emarkq.Queue = s 556 } 557 emarkq = s 558 } 559 560 func mark(s *LSym) { 561 mark1(s, nil) 562 } 563 564 func markflood() { 565 var a *Auto 566 var i int 567 568 for s := markq; s != nil; s = s.Queue { 569 if s.Type == obj.STEXT { 570 if Debug['v'] > 1 { 571 fmt.Fprintf(&Bso, "marktext %s\n", s.Name) 572 } 573 for a = s.Autom; a != nil; a = a.Link { 574 mark1(a.Gotype, s) 575 } 576 } 577 578 for i = 0; i < len(s.R); i++ { 579 mark1(s.R[i].Sym, s) 580 } 581 if s.Pcln != nil { 582 for i = 0; i < s.Pcln.Nfuncdata; i++ { 583 mark1(s.Pcln.Funcdata[i], s) 584 } 585 } 586 587 mark1(s.Gotype, s) 588 mark1(s.Sub, s) 589 mark1(s.Outer, s) 590 } 591 } 592 593 var markextra = []string{ 594 "runtime.morestack", 595 "runtime.morestackx", 596 "runtime.morestack00", 597 "runtime.morestack10", 598 "runtime.morestack01", 599 "runtime.morestack11", 600 "runtime.morestack8", 601 "runtime.morestack16", 602 "runtime.morestack24", 603 "runtime.morestack32", 604 "runtime.morestack40", 605 "runtime.morestack48", 606 // on arm, lock in the div/mod helpers too 607 "_div", 608 "_divu", 609 "_mod", 610 "_modu", 611 } 612 613 func deadcode() { 614 if Debug['v'] != 0 { 615 fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime()) 616 } 617 618 if Buildmode == BuildmodeShared { 619 // Mark all symbols as reachable when building a 620 // shared library. 621 for s := Ctxt.Allsym; s != nil; s = s.Allsym { 622 if s.Type != 0 { 623 mark(s) 624 } 625 } 626 mark(Linkrlookup(Ctxt, "main.main", 0)) 627 mark(Linkrlookup(Ctxt, "main.init", 0)) 628 } else { 629 mark(Linklookup(Ctxt, INITENTRY, 0)) 630 if Linkshared && Buildmode == BuildmodeExe { 631 mark(Linkrlookup(Ctxt, "main.main", 0)) 632 mark(Linkrlookup(Ctxt, "main.init", 0)) 633 } 634 for i := 0; i < len(markextra); i++ { 635 mark(Linklookup(Ctxt, markextra[i], 0)) 636 } 637 638 for i := 0; i < len(dynexp); i++ { 639 mark(dynexp[i]) 640 } 641 markflood() 642 643 // keep each beginning with 'typelink.' if the symbol it points at is being kept. 644 for s := Ctxt.Allsym; s != nil; s = s.Allsym { 645 if strings.HasPrefix(s.Name, "go.typelink.") { 646 s.Reachable = len(s.R) == 1 && s.R[0].Sym.Reachable 647 } 648 } 649 650 // remove dead text but keep file information (z symbols). 651 var last *LSym 652 653 for s := Ctxt.Textp; s != nil; s = s.Next { 654 if !s.Reachable { 655 continue 656 } 657 658 // NOTE: Removing s from old textp and adding to new, shorter textp. 659 if last == nil { 660 Ctxt.Textp = s 661 } else { 662 last.Next = s 663 } 664 last = s 665 } 666 667 if last == nil { 668 Ctxt.Textp = nil 669 Ctxt.Etextp = nil 670 } else { 671 last.Next = nil 672 Ctxt.Etextp = last 673 } 674 } 675 676 for s := Ctxt.Allsym; s != nil; s = s.Allsym { 677 if strings.HasPrefix(s.Name, "go.weak.") { 678 s.Special = 1 // do not lay out in data segment 679 s.Reachable = true 680 s.Hide = 1 681 } 682 } 683 684 // record field tracking references 685 var buf bytes.Buffer 686 var p *LSym 687 for s := Ctxt.Allsym; s != nil; s = s.Allsym { 688 if strings.HasPrefix(s.Name, "go.track.") { 689 s.Special = 1 // do not lay out in data segment 690 s.Hide = 1 691 if s.Reachable { 692 buf.WriteString(s.Name[9:]) 693 for p = s.Reachparent; p != nil; p = p.Reachparent { 694 buf.WriteString("\t") 695 buf.WriteString(p.Name) 696 } 697 buf.WriteString("\n") 698 } 699 700 s.Type = obj.SCONST 701 s.Value = 0 702 } 703 } 704 705 if tracksym == "" { 706 return 707 } 708 s := Linklookup(Ctxt, tracksym, 0) 709 if !s.Reachable { 710 return 711 } 712 addstrdata(tracksym, buf.String()) 713 } 714 715 func doweak() { 716 var t *LSym 717 718 // resolve weak references only if 719 // target symbol will be in binary anyway. 720 for s := Ctxt.Allsym; s != nil; s = s.Allsym { 721 if strings.HasPrefix(s.Name, "go.weak.") { 722 t = Linkrlookup(Ctxt, s.Name[8:], int(s.Version)) 723 if t != nil && t.Type != 0 && t.Reachable { 724 s.Value = t.Value 725 s.Type = t.Type 726 s.Outer = t 727 } else { 728 s.Type = obj.SCONST 729 s.Value = 0 730 } 731 732 continue 733 } 734 } 735 } 736 737 func addexport() { 738 if HEADTYPE == obj.Hdarwin { 739 return 740 } 741 742 for i := 0; i < len(dynexp); i++ { 743 Thearch.Adddynsym(Ctxt, dynexp[i]) 744 } 745 } 746 747 type Pkg struct { 748 mark bool 749 checked bool 750 path string 751 impby []*Pkg 752 } 753 754 var ( 755 // pkgmap records the imported-by relationship between packages. 756 // Entries are keyed by package path (e.g., "runtime" or "net/url"). 757 pkgmap = map[string]*Pkg{} 758 759 pkgall []*Pkg 760 ) 761 762 func lookupPkg(path string) *Pkg { 763 if p, ok := pkgmap[path]; ok { 764 return p 765 } 766 p := &Pkg{path: path} 767 pkgmap[path] = p 768 pkgall = append(pkgall, p) 769 return p 770 } 771 772 // imported records that package pkg imports package imp. 773 func imported(pkg, imp string) { 774 // everyone imports runtime, even runtime. 775 if imp == "runtime" { 776 return 777 } 778 779 p := lookupPkg(pkg) 780 i := lookupPkg(imp) 781 i.impby = append(i.impby, p) 782 } 783 784 func (p *Pkg) cycle() *Pkg { 785 if p.checked { 786 return nil 787 } 788 789 if p.mark { 790 nerrors++ 791 fmt.Printf("import cycle:\n") 792 fmt.Printf("\t%s\n", p.path) 793 return p 794 } 795 796 p.mark = true 797 for _, q := range p.impby { 798 if bad := q.cycle(); bad != nil { 799 p.mark = false 800 p.checked = true 801 fmt.Printf("\timports %s\n", p.path) 802 if bad == p { 803 return nil 804 } 805 return bad 806 } 807 } 808 809 p.checked = true 810 p.mark = false 811 return nil 812 } 813 814 func importcycles() { 815 for _, p := range pkgall { 816 p.cycle() 817 } 818 } 819 820 func setlinkmode(arg string) { 821 if arg == "internal" { 822 Linkmode = LinkInternal 823 } else if arg == "external" { 824 Linkmode = LinkExternal 825 } else if arg == "auto" { 826 Linkmode = LinkAuto 827 } else { 828 Exitf("unknown link mode -linkmode %s", arg) 829 } 830 }