1// Inferno utils/6l/obj.c 2// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c 3// 4// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6// Portions Copyright © 1997-1999 Vita Nuova Limited 7// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8// Portions Copyright © 2004,2006 Bruce Ellis 9// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11// Portions Copyright © 2009 The Go Authors. All rights reserved. 12// 13// Permission is hereby granted, free of charge, to any person obtaining a copy 14// of this software and associated documentation files (the "Software"), to deal 15// in the Software without restriction, including without limitation the rights 16// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17// copies of the Software, and to permit persons to whom the Software is 18// furnished to do so, subject to the following conditions: 19// 20// The above copyright notice and this permission notice shall be included in 21// all copies or substantial portions of the Software. 22// 23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29// THE SOFTWARE. 30 31package ld 32 33import ( 34 "bufio" 35 "cmd/internal/goobj" 36 "cmd/internal/objabi" 37 "cmd/internal/quoted" 38 "cmd/internal/sys" 39 "cmd/internal/telemetry/counter" 40 "cmd/link/internal/benchmark" 41 "flag" 42 "internal/buildcfg" 43 "log" 44 "os" 45 "runtime" 46 "runtime/pprof" 47 "strconv" 48 "strings" 49) 50 51var ( 52 pkglistfornote []byte 53 windowsgui bool // writes a "GUI binary" instead of a "console binary" 54 ownTmpDir bool // set to true if tmp dir created by linker (e.g. no -tmpdir) 55) 56 57func init() { 58 flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...") 59 flag.Var(&flagExtld, "extld", "use `linker` when linking in external mode") 60 flag.Var(&flagExtldflags, "extldflags", "pass `flags` to external linker") 61 flag.Var(&flagW, "w", "disable DWARF generation") 62} 63 64// Flags used by the linker. The exported flags are used by the architecture-specific packages. 65var ( 66 flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id") 67 flagBindNow = flag.Bool("bindnow", false, "mark a dynamically linked ELF object for immediate function binding") 68 69 flagOutfile = flag.String("o", "", "write output to `file`") 70 flagPluginPath = flag.String("pluginpath", "", "full path name for plugin") 71 72 flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`") 73 flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph") 74 flagRace = flag.Bool("race", false, "enable race detector") 75 flagMsan = flag.Bool("msan", false, "enable MSan interface") 76 flagAsan = flag.Bool("asan", false, "enable ASan interface") 77 flagAslr = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows") 78 79 flagFieldTrack = flag.String("k", "", "set field tracking `symbol`") 80 flagLibGCC = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable") 81 flagTmpdir = flag.String("tmpdir", "", "use `directory` for temporary files") 82 83 flagExtld quoted.Flag 84 flagExtldflags quoted.Flag 85 flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive") 86 87 flagCaptureHostObjs = flag.String("capturehostobjs", "", "capture host object files loaded during internal linking to specified dir") 88 89 flagA = flag.Bool("a", false, "no-op (deprecated)") 90 FlagC = flag.Bool("c", false, "dump call graph") 91 FlagD = flag.Bool("d", false, "disable dynamic executable") 92 flagF = flag.Bool("f", false, "ignore version mismatch") 93 flagG = flag.Bool("g", false, "disable go package data checks") 94 flagH = flag.Bool("h", false, "halt on error") 95 flagN = flag.Bool("n", false, "no-op (deprecated)") 96 FlagS = flag.Bool("s", false, "disable symbol table") 97 flag8 bool // use 64-bit addresses in symbol table 98 flagHostBuildid = flag.String("B", "", "set ELF NT_GNU_BUILD_ID `note` or Mach-O UUID; use \"gobuildid\" to generate it from the Go build ID") 99 flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") 100 flagCheckLinkname = flag.Bool("checklinkname", true, "check linkname symbol references") 101 FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines") 102 FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size") 103 flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph") 104 FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).") 105 FlagRound = flag.Int64("R", -1, "set address rounding `quantum`") 106 FlagTextAddr = flag.Int64("T", -1, "set the start address of text symbols") 107 flagEntrySymbol = flag.String("E", "", "set `entry` symbol name") 108 flagPruneWeakMap = flag.Bool("pruneweakmap", true, "prune weak mapinit refs") 109 flagRandLayout = flag.Int64("randlayout", 0, "randomize function layout") 110 cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") 111 memprofile = flag.String("memprofile", "", "write memory profile to `file`") 112 memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`") 113 benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking") 114 benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof") 115 116 flagW ternaryFlag 117 FlagW = new(bool) // the -w flag, computed in main from flagW 118) 119 120// ternaryFlag is like a boolean flag, but has a default value that is 121// neither true nor false, allowing it to be set from context (e.g. from another 122// flag). 123// *ternaryFlag implements flag.Value. 124type ternaryFlag int 125 126const ( 127 ternaryFlagUnset ternaryFlag = iota 128 ternaryFlagFalse 129 ternaryFlagTrue 130) 131 132func (t *ternaryFlag) Set(s string) error { 133 v, err := strconv.ParseBool(s) 134 if err != nil { 135 return err 136 } 137 if v { 138 *t = ternaryFlagTrue 139 } else { 140 *t = ternaryFlagFalse 141 } 142 return nil 143} 144 145func (t *ternaryFlag) String() string { 146 switch *t { 147 case ternaryFlagFalse: 148 return "false" 149 case ternaryFlagTrue: 150 return "true" 151 } 152 return "unset" 153} 154 155func (t *ternaryFlag) IsBoolFlag() bool { return true } // parse like a boolean flag 156 157// Main is the main entry point for the linker code. 158func Main(arch *sys.Arch, theArch Arch) { 159 log.SetPrefix("link: ") 160 log.SetFlags(0) 161 counter.Open() 162 counter.Inc("link/invocations") 163 164 thearch = theArch 165 ctxt := linknew(arch) 166 ctxt.Bso = bufio.NewWriter(os.Stdout) 167 168 // For testing behavior of go command when tools crash silently. 169 // Undocumented, not in standard flag parser to avoid 170 // exposing in usage message. 171 for _, arg := range os.Args { 172 if arg == "-crash_for_testing" { 173 os.Exit(2) 174 } 175 } 176 177 if buildcfg.GOROOT == "" { 178 // cmd/go clears the GOROOT variable when -trimpath is set, 179 // so omit it from the binary even if cmd/link itself has an 180 // embedded GOROOT value reported by runtime.GOROOT. 181 } else { 182 addstrdata1(ctxt, "runtime.defaultGOROOT="+buildcfg.GOROOT) 183 } 184 185 buildVersion := buildcfg.Version 186 if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { 187 buildVersion += " X:" + goexperiment 188 } 189 addstrdata1(ctxt, "runtime.buildVersion="+buildVersion) 190 191 // TODO(matloob): define these above and then check flag values here 192 if ctxt.Arch.Family == sys.AMD64 && buildcfg.GOOS == "plan9" { 193 flag.BoolVar(&flag8, "8", false, "use 64-bit addresses in symbol table") 194 } 195 flagHeadType := flag.String("H", "", "set header `type`") 196 flag.BoolVar(&ctxt.linkShared, "linkshared", false, "link against installed Go shared libraries") 197 flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`") 198 flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`") 199 flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible") 200 objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) }) 201 objabi.AddVersionFlag() // -V 202 objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) }) 203 objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog) 204 objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg) 205 206 objabi.Flagparse(usage) 207 counter.CountFlags("link/flag:", *flag.CommandLine) 208 209 if ctxt.Debugvlog > 0 { 210 // dump symbol info on crash 211 defer func() { ctxt.loader.Dump() }() 212 } 213 if ctxt.Debugvlog > 1 { 214 // dump symbol info on error 215 AtExit(func() { 216 if nerrors > 0 { 217 ctxt.loader.Dump() 218 } 219 }) 220 } 221 222 switch *flagHeadType { 223 case "": 224 case "windowsgui": 225 ctxt.HeadType = objabi.Hwindows 226 windowsgui = true 227 default: 228 if err := ctxt.HeadType.Set(*flagHeadType); err != nil { 229 Errorf(nil, "%v", err) 230 usage() 231 } 232 } 233 if ctxt.HeadType == objabi.Hunknown { 234 ctxt.HeadType.Set(buildcfg.GOOS) 235 } 236 237 if !*flagAslr && ctxt.BuildMode != BuildModeCShared { 238 Errorf(nil, "-aslr=false is only allowed for -buildmode=c-shared") 239 usage() 240 } 241 242 if *FlagD && ctxt.UsesLibc() { 243 Exitf("dynamic linking required on %s; -d flag cannot be used", buildcfg.GOOS) 244 } 245 246 isPowerOfTwo := func(n int64) bool { 247 return n > 0 && n&(n-1) == 0 248 } 249 if *FlagRound != -1 && (*FlagRound < 4096 || !isPowerOfTwo(*FlagRound)) { 250 Exitf("invalid -R value 0x%x", *FlagRound) 251 } 252 253 checkStrictDups = *FlagStrictDups 254 255 switch flagW { 256 case ternaryFlagFalse: 257 *FlagW = false 258 case ternaryFlagTrue: 259 *FlagW = true 260 case ternaryFlagUnset: 261 *FlagW = *FlagS // -s implies -w if not explicitly set 262 if ctxt.IsDarwin() && ctxt.BuildMode == BuildModeCShared { 263 *FlagW = true // default to -w in c-shared mode on darwin, see #61229 264 } 265 } 266 267 if !buildcfg.Experiment.RegabiWrappers { 268 abiInternalVer = 0 269 } 270 271 startProfile() 272 if ctxt.BuildMode == BuildModeUnset { 273 ctxt.BuildMode.Set("exe") 274 } 275 276 if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 { 277 usage() 278 } 279 280 if *flagOutfile == "" { 281 *flagOutfile = "a.out" 282 if ctxt.HeadType == objabi.Hwindows { 283 *flagOutfile += ".exe" 284 } 285 } 286 287 interpreter = *flagInterpreter 288 289 if *flagBuildid == "" && ctxt.Target.IsOpenbsd() { 290 // TODO(jsing): Remove once direct syscalls are no longer in use. 291 // OpenBSD 6.7 onwards will not permit direct syscalls from a 292 // dynamically linked binary unless it identifies the binary 293 // contains a .note.go.buildid ELF note. See issue #36435. 294 *flagBuildid = "go-openbsd" 295 } 296 297 if *flagHostBuildid != "" { 298 addbuildinfo(ctxt) 299 } 300 301 // enable benchmarking 302 var bench *benchmark.Metrics 303 if len(*benchmarkFlag) != 0 { 304 if *benchmarkFlag == "mem" { 305 bench = benchmark.New(benchmark.GC, *benchmarkFileFlag) 306 } else if *benchmarkFlag == "cpu" { 307 bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag) 308 } else { 309 Errorf(nil, "unknown benchmark flag: %q", *benchmarkFlag) 310 usage() 311 } 312 } 313 314 bench.Start("libinit") 315 libinit(ctxt) // creates outfile 316 bench.Start("computeTLSOffset") 317 ctxt.computeTLSOffset() 318 bench.Start("Archinit") 319 thearch.Archinit(ctxt) 320 321 if ctxt.linkShared && !ctxt.IsELF { 322 Exitf("-linkshared can only be used on elf systems") 323 } 324 325 if ctxt.Debugvlog != 0 { 326 onOff := func(b bool) string { 327 if b { 328 return "on" 329 } 330 return "off" 331 } 332 ctxt.Logf("build mode: %s, symbol table: %s, DWARF: %s\n", ctxt.BuildMode, onOff(!*FlagS), onOff(dwarfEnabled(ctxt))) 333 ctxt.Logf("HEADER = -H%d -T0x%x -R0x%x\n", ctxt.HeadType, uint64(*FlagTextAddr), uint32(*FlagRound)) 334 } 335 336 zerofp := goobj.FingerprintType{} 337 switch ctxt.BuildMode { 338 case BuildModeShared: 339 for i := 0; i < flag.NArg(); i++ { 340 arg := flag.Arg(i) 341 parts := strings.SplitN(arg, "=", 2) 342 var pkgpath, file string 343 if len(parts) == 1 { 344 pkgpath, file = "main", arg 345 } else { 346 pkgpath, file = parts[0], parts[1] 347 } 348 pkglistfornote = append(pkglistfornote, pkgpath...) 349 pkglistfornote = append(pkglistfornote, '\n') 350 addlibpath(ctxt, "command line", "command line", file, pkgpath, "", zerofp) 351 } 352 case BuildModePlugin: 353 addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "", zerofp) 354 default: 355 addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "", zerofp) 356 } 357 bench.Start("loadlib") 358 ctxt.loadlib() 359 360 bench.Start("inittasks") 361 ctxt.inittasks() 362 363 bench.Start("deadcode") 364 deadcode(ctxt) 365 366 bench.Start("linksetup") 367 ctxt.linksetup() 368 369 bench.Start("dostrdata") 370 ctxt.dostrdata() 371 if buildcfg.Experiment.FieldTrack { 372 bench.Start("fieldtrack") 373 fieldtrack(ctxt.Arch, ctxt.loader) 374 } 375 376 bench.Start("dwarfGenerateDebugInfo") 377 dwarfGenerateDebugInfo(ctxt) 378 379 bench.Start("callgraph") 380 ctxt.callgraph() 381 382 bench.Start("doStackCheck") 383 ctxt.doStackCheck() 384 385 bench.Start("mangleTypeSym") 386 ctxt.mangleTypeSym() 387 388 if ctxt.IsELF { 389 bench.Start("doelf") 390 ctxt.doelf() 391 } 392 if ctxt.IsDarwin() { 393 bench.Start("domacho") 394 ctxt.domacho() 395 } 396 if ctxt.IsWindows() { 397 bench.Start("dope") 398 ctxt.dope() 399 bench.Start("windynrelocsyms") 400 ctxt.windynrelocsyms() 401 } 402 if ctxt.IsAIX() { 403 bench.Start("doxcoff") 404 ctxt.doxcoff() 405 } 406 407 bench.Start("textbuildid") 408 ctxt.textbuildid() 409 bench.Start("addexport") 410 ctxt.setArchSyms() 411 ctxt.addexport() 412 bench.Start("Gentext") 413 thearch.Gentext(ctxt, ctxt.loader) // trampolines, call stubs, etc. 414 415 bench.Start("textaddress") 416 ctxt.textaddress() 417 bench.Start("typelink") 418 ctxt.typelink() 419 bench.Start("buildinfo") 420 ctxt.buildinfo() 421 bench.Start("pclntab") 422 containers := ctxt.findContainerSyms() 423 pclnState := ctxt.pclntab(containers) 424 bench.Start("findfunctab") 425 ctxt.findfunctab(pclnState, containers) 426 bench.Start("dwarfGenerateDebugSyms") 427 dwarfGenerateDebugSyms(ctxt) 428 bench.Start("symtab") 429 symGroupType := ctxt.symtab(pclnState) 430 bench.Start("dodata") 431 ctxt.dodata(symGroupType) 432 bench.Start("address") 433 order := ctxt.address() 434 bench.Start("dwarfcompress") 435 dwarfcompress(ctxt) 436 bench.Start("layout") 437 filesize := ctxt.layout(order) 438 439 // Write out the output file. 440 // It is split into two parts (Asmb and Asmb2). The first 441 // part writes most of the content (sections and segments), 442 // for which we have computed the size and offset, in a 443 // mmap'd region. The second part writes more content, for 444 // which we don't know the size. 445 if ctxt.Arch.Family != sys.Wasm { 446 // Don't mmap if we're building for Wasm. Wasm file 447 // layout is very different so filesize is meaningless. 448 if err := ctxt.Out.Mmap(filesize); err != nil { 449 Exitf("mapping output file failed: %v", err) 450 } 451 } 452 // asmb will redirect symbols to the output file mmap, and relocations 453 // will be applied directly there. 454 bench.Start("Asmb") 455 asmb(ctxt) 456 457 exitIfErrors() 458 459 // Generate additional symbols for the native symbol table just prior 460 // to code generation. 461 bench.Start("GenSymsLate") 462 if thearch.GenSymsLate != nil { 463 thearch.GenSymsLate(ctxt, ctxt.loader) 464 } 465 466 bench.Start("Asmb2") 467 asmb2(ctxt) 468 469 bench.Start("Munmap") 470 ctxt.Out.Close() // Close handles Munmapping if necessary. 471 472 bench.Start("hostlink") 473 ctxt.hostlink() 474 if ctxt.Debugvlog != 0 { 475 ctxt.Logf("%s", ctxt.loader.Stat()) 476 ctxt.Logf("%d liveness data\n", liveness) 477 } 478 bench.Start("Flush") 479 ctxt.Bso.Flush() 480 bench.Start("archive") 481 ctxt.archive() 482 bench.Report(os.Stdout) 483 484 errorexit() 485} 486 487type Rpath struct { 488 set bool 489 val string 490} 491 492func (r *Rpath) Set(val string) error { 493 r.set = true 494 r.val = val 495 return nil 496} 497 498func (r *Rpath) String() string { 499 return r.val 500} 501 502func startProfile() { 503 if *cpuprofile != "" { 504 f, err := os.Create(*cpuprofile) 505 if err != nil { 506 log.Fatalf("%v", err) 507 } 508 if err := pprof.StartCPUProfile(f); err != nil { 509 log.Fatalf("%v", err) 510 } 511 AtExit(func() { 512 pprof.StopCPUProfile() 513 if err = f.Close(); err != nil { 514 log.Fatalf("error closing cpu profile: %v", err) 515 } 516 }) 517 } 518 if *memprofile != "" { 519 if *memprofilerate != 0 { 520 runtime.MemProfileRate = int(*memprofilerate) 521 } 522 f, err := os.Create(*memprofile) 523 if err != nil { 524 log.Fatalf("%v", err) 525 } 526 AtExit(func() { 527 // Profile all outstanding allocations. 528 runtime.GC() 529 // compilebench parses the memory profile to extract memstats, 530 // which are only written in the legacy pprof format. 531 // See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap. 532 const writeLegacyFormat = 1 533 if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil { 534 log.Fatalf("%v", err) 535 } 536 // Close the file after writing the profile. 537 if err := f.Close(); err != nil { 538 log.Fatalf("could not close %v: %v", *memprofile, err) 539 } 540 }) 541 } 542} 543