1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package ld
32
33 import (
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
51 var (
52 pkglistfornote []byte
53 windowsgui bool
54 ownTmpDir bool
55 )
56
57 func 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
65 var (
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
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)
118 )
119
120
121
122
123
124 type ternaryFlag int
125
126 const (
127 ternaryFlagUnset ternaryFlag = iota
128 ternaryFlagFalse
129 ternaryFlagTrue
130 )
131
132 func (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
145 func (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
155 func (t *ternaryFlag) IsBoolFlag() bool { return true }
156
157
158 func 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
169
170
171 for _, arg := range os.Args {
172 if arg == "-crash_for_testing" {
173 os.Exit(2)
174 }
175 }
176
177 if buildcfg.GOROOT == "" {
178
179
180
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
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()
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
211 defer func() { ctxt.loader.Dump() }()
212 }
213 if ctxt.Debugvlog > 1 {
214
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
262 if ctxt.IsDarwin() && ctxt.BuildMode == BuildModeCShared {
263 *FlagW = true
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
291
292
293
294 *flagBuildid = "go-openbsd"
295 }
296
297 if *flagHostBuildid != "" {
298 addbuildinfo(ctxt)
299 }
300
301
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)
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)
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
440
441
442
443
444
445 if ctxt.Arch.Family != sys.Wasm {
446
447
448 if err := ctxt.Out.Mmap(filesize); err != nil {
449 Exitf("mapping output file failed: %v", err)
450 }
451 }
452
453
454 bench.Start("Asmb")
455 asmb(ctxt)
456
457 exitIfErrors()
458
459
460
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()
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
487 type Rpath struct {
488 set bool
489 val string
490 }
491
492 func (r *Rpath) Set(val string) error {
493 r.set = true
494 r.val = val
495 return nil
496 }
497
498 func (r *Rpath) String() string {
499 return r.val
500 }
501
502 func 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
528 runtime.GC()
529
530
531
532 const writeLegacyFormat = 1
533 if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
534 log.Fatalf("%v", err)
535 }
536
537 if err := f.Close(); err != nil {
538 log.Fatalf("could not close %v: %v", *memprofile, err)
539 }
540 })
541 }
542 }
543
View as plain text