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 flagFipso = flag.String("fipso", "", "write fips module to `file`")
72
73 flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`")
74 flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph")
75 flagRace = flag.Bool("race", false, "enable race detector")
76 flagMsan = flag.Bool("msan", false, "enable MSan interface")
77 flagAsan = flag.Bool("asan", false, "enable ASan interface")
78 flagAslr = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows")
79
80 flagFieldTrack = flag.String("k", "", "set field tracking `symbol`")
81 flagLibGCC = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable")
82 flagTmpdir = flag.String("tmpdir", "", "use `directory` for temporary files")
83
84 flagExtld quoted.Flag
85 flagExtldflags quoted.Flag
86 flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive")
87
88 flagCaptureHostObjs = flag.String("capturehostobjs", "", "capture host object files loaded during internal linking to specified dir")
89
90 flagA = flag.Bool("a", false, "no-op (deprecated)")
91 FlagC = flag.Bool("c", false, "dump call graph")
92 FlagD = flag.Bool("d", false, "disable dynamic executable")
93 flagF = flag.Bool("f", false, "ignore version mismatch")
94 flagG = flag.Bool("g", false, "disable go package data checks")
95 flagH = flag.Bool("h", false, "halt on error")
96 flagN = flag.Bool("n", false, "no-op (deprecated)")
97 FlagS = flag.Bool("s", false, "disable symbol table")
98 flag8 bool
99 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; \"none\" to disable")
100 flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
101 flagCheckLinkname = flag.Bool("checklinkname", true, "check linkname symbol references")
102 FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
103 FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size")
104 flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph")
105 FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
106 FlagRound = flag.Int64("R", -1, "set address rounding `quantum`")
107 FlagTextAddr = flag.Int64("T", -1, "set the start address of text symbols")
108 FlagDataAddr = flag.Int64("D", -1, "set the start address of data symbols")
109 FlagFuncAlign = flag.Int("funcalign", 0, "set function align to `N` bytes")
110 flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
111 flagPruneWeakMap = flag.Bool("pruneweakmap", true, "prune weak mapinit refs")
112 flagRandLayout = flag.Int64("randlayout", 0, "randomize function layout")
113 flagAllErrors = flag.Bool("e", false, "no limit on number of errors reported")
114 cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
115 memprofile = flag.String("memprofile", "", "write memory profile to `file`")
116 memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
117 benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
118 benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")
119
120 flagW ternaryFlag
121 FlagW = new(bool)
122 )
123
124
125
126
127
128 type ternaryFlag int
129
130 const (
131 ternaryFlagUnset ternaryFlag = iota
132 ternaryFlagFalse
133 ternaryFlagTrue
134 )
135
136 func (t *ternaryFlag) Set(s string) error {
137 v, err := strconv.ParseBool(s)
138 if err != nil {
139 return err
140 }
141 if v {
142 *t = ternaryFlagTrue
143 } else {
144 *t = ternaryFlagFalse
145 }
146 return nil
147 }
148
149 func (t *ternaryFlag) String() string {
150 switch *t {
151 case ternaryFlagFalse:
152 return "false"
153 case ternaryFlagTrue:
154 return "true"
155 }
156 return "unset"
157 }
158
159 func (t *ternaryFlag) IsBoolFlag() bool { return true }
160
161
162 func Main(arch *sys.Arch, theArch Arch) {
163 log.SetPrefix("link: ")
164 log.SetFlags(0)
165 counter.Open()
166 counter.Inc("link/invocations")
167
168 thearch = theArch
169 ctxt := linknew(arch)
170 ctxt.Bso = bufio.NewWriter(os.Stdout)
171
172
173
174
175 for _, arg := range os.Args {
176 if arg == "-crash_for_testing" {
177 os.Exit(2)
178 }
179 }
180
181 if buildcfg.GOROOT == "" {
182
183
184
185 } else {
186 addstrdata1(ctxt, "runtime.defaultGOROOT="+buildcfg.GOROOT)
187 }
188
189 buildVersion := buildcfg.Version
190 if goexperiment := buildcfg.Experiment.String(); goexperiment != "" {
191 sep := " "
192 if !strings.Contains(buildVersion, "-") {
193 sep = "-"
194 }
195 buildVersion += sep + "X:" + goexperiment
196 }
197 addstrdata1(ctxt, "runtime.buildVersion="+buildVersion)
198
199
200 if ctxt.Arch.Family == sys.AMD64 && buildcfg.GOOS == "plan9" {
201 flag.BoolVar(&flag8, "8", false, "use 64-bit addresses in symbol table")
202 }
203 flagHeadType := flag.String("H", "", "set header `type`")
204 flag.BoolVar(&ctxt.linkShared, "linkshared", false, "link against installed Go shared libraries")
205 flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`")
206 flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`")
207 flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible")
208 objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
209 objabi.AddVersionFlag()
210 objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
211 objabi.Flagcount("v", "print link trace", &ctxt.Debugvlog)
212 objabi.Flagfn1("importcfg", "read import configuration from `file`", ctxt.readImportCfg)
213
214 objabi.Flagparse(usage)
215 counter.CountFlags("link/flag:", *flag.CommandLine)
216
217 if ctxt.Debugvlog > 0 {
218
219 defer func() { ctxt.loader.Dump() }()
220 }
221 if ctxt.Debugvlog > 1 {
222
223 AtExit(func() {
224 if nerrors > 0 {
225 ctxt.loader.Dump()
226 }
227 })
228 }
229
230 switch *flagHeadType {
231 case "":
232 case "windowsgui":
233 ctxt.HeadType = objabi.Hwindows
234 windowsgui = true
235 default:
236 if err := ctxt.HeadType.Set(*flagHeadType); err != nil {
237 Errorf("%v", err)
238 usage()
239 }
240 }
241 if ctxt.HeadType == objabi.Hunknown {
242 ctxt.HeadType.Set(buildcfg.GOOS)
243 }
244
245 if !*flagAslr && ctxt.BuildMode != BuildModeCShared {
246 Errorf("-aslr=false is only allowed for -buildmode=c-shared")
247 usage()
248 }
249
250 if *FlagD && ctxt.UsesLibc() {
251 Exitf("dynamic linking required on %s; -d flag cannot be used", buildcfg.GOOS)
252 }
253
254 isPowerOfTwo := func(n int64) bool {
255 return n > 0 && n&(n-1) == 0
256 }
257 if *FlagRound != -1 && (*FlagRound < 4096 || !isPowerOfTwo(*FlagRound)) {
258 Exitf("invalid -R value 0x%x", *FlagRound)
259 }
260 if *FlagFuncAlign != 0 && !isPowerOfTwo(int64(*FlagFuncAlign)) {
261 Exitf("invalid -funcalign value %d", *FlagFuncAlign)
262 }
263
264 checkStrictDups = *FlagStrictDups
265
266 switch flagW {
267 case ternaryFlagFalse:
268 *FlagW = false
269 case ternaryFlagTrue:
270 *FlagW = true
271 case ternaryFlagUnset:
272 *FlagW = *FlagS
273 if ctxt.IsDarwin() && ctxt.BuildMode == BuildModeCShared {
274 *FlagW = true
275 }
276 }
277
278 if !buildcfg.Experiment.RegabiWrappers {
279 abiInternalVer = 0
280 }
281
282 startProfile()
283 if ctxt.BuildMode == BuildModeUnset {
284 ctxt.BuildMode.Set("exe")
285 }
286
287 if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 {
288 usage()
289 }
290
291 if *flagOutfile == "" {
292 *flagOutfile = "a.out"
293 if ctxt.HeadType == objabi.Hwindows {
294 *flagOutfile += ".exe"
295 }
296 }
297
298 interpreter = *flagInterpreter
299
300 if *flagHostBuildid == "" && *flagBuildid != "" {
301 *flagHostBuildid = "gobuildid"
302 }
303 addbuildinfo(ctxt)
304
305
306 var bench *benchmark.Metrics
307 if len(*benchmarkFlag) != 0 {
308 if *benchmarkFlag == "mem" {
309 bench = benchmark.New(benchmark.GC, *benchmarkFileFlag)
310 } else if *benchmarkFlag == "cpu" {
311 bench = benchmark.New(benchmark.NoGC, *benchmarkFileFlag)
312 } else {
313 Errorf("unknown benchmark flag: %q", *benchmarkFlag)
314 usage()
315 }
316 }
317
318 bench.Start("libinit")
319 libinit(ctxt)
320 bench.Start("computeTLSOffset")
321 ctxt.computeTLSOffset()
322 bench.Start("Archinit")
323 thearch.Archinit(ctxt)
324
325 if *FlagDataAddr != -1 && *FlagDataAddr%*FlagRound != 0 {
326 Exitf("invalid -D value 0x%x: not aligned to rounding quantum 0x%x", *FlagDataAddr, *FlagRound)
327 }
328
329 if ctxt.linkShared && !ctxt.IsELF {
330 Exitf("-linkshared can only be used on elf systems")
331 }
332
333 if ctxt.Debugvlog != 0 {
334 onOff := func(b bool) string {
335 if b {
336 return "on"
337 }
338 return "off"
339 }
340 ctxt.Logf("build mode: %s, symbol table: %s, DWARF: %s\n", ctxt.BuildMode, onOff(!*FlagS), onOff(dwarfEnabled(ctxt)))
341 ctxt.Logf("HEADER = -H%d -T0x%x -R0x%x\n", ctxt.HeadType, uint64(*FlagTextAddr), uint32(*FlagRound))
342 }
343
344 zerofp := goobj.FingerprintType{}
345 switch ctxt.BuildMode {
346 case BuildModeShared:
347 for i := 0; i < flag.NArg(); i++ {
348 arg := flag.Arg(i)
349 parts := strings.SplitN(arg, "=", 2)
350 var pkgpath, file string
351 if len(parts) == 1 {
352 pkgpath, file = "main", arg
353 } else {
354 pkgpath, file = parts[0], parts[1]
355 }
356 pkglistfornote = append(pkglistfornote, pkgpath...)
357 pkglistfornote = append(pkglistfornote, '\n')
358 addlibpath(ctxt, "command line", "command line", file, pkgpath, "", zerofp)
359 }
360 case BuildModePlugin:
361 addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "", zerofp)
362 default:
363 addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "", zerofp)
364 }
365 bench.Start("loadlib")
366 ctxt.loadlib()
367
368 bench.Start("inittasks")
369 ctxt.inittasks()
370
371 bench.Start("deadcode")
372 deadcode(ctxt)
373
374 bench.Start("linksetup")
375 ctxt.linksetup()
376
377 bench.Start("dostrdata")
378 ctxt.dostrdata()
379 if buildcfg.Experiment.FieldTrack {
380 bench.Start("fieldtrack")
381 fieldtrack(ctxt.Arch, ctxt.loader)
382 }
383
384 bench.Start("dwarfGenerateDebugInfo")
385 dwarfGenerateDebugInfo(ctxt)
386
387 bench.Start("callgraph")
388 ctxt.callgraph()
389
390 bench.Start("doStackCheck")
391 ctxt.doStackCheck()
392
393 bench.Start("mangleTypeSym")
394 ctxt.mangleTypeSym()
395
396 if ctxt.IsELF {
397 bench.Start("doelf")
398 ctxt.doelf()
399 }
400 if ctxt.IsDarwin() {
401 bench.Start("domacho")
402 ctxt.domacho()
403 }
404 if ctxt.IsWindows() {
405 bench.Start("dope")
406 ctxt.dope()
407 bench.Start("windynrelocsyms")
408 ctxt.windynrelocsyms()
409 }
410 if ctxt.IsAIX() {
411 bench.Start("doxcoff")
412 ctxt.doxcoff()
413 }
414
415 bench.Start("textbuildid")
416 ctxt.textbuildid()
417 bench.Start("addexport")
418 ctxt.setArchSyms()
419 ctxt.addexport()
420 bench.Start("Gentext")
421 thearch.Gentext(ctxt, ctxt.loader)
422
423 bench.Start("textaddress")
424 ctxt.textaddress()
425 bench.Start("typelink")
426 ctxt.typelink()
427 bench.Start("buildinfo")
428 ctxt.buildinfo()
429 bench.Start("pclntab")
430 containers := ctxt.findContainerSyms()
431 pclnState := ctxt.pclntab(containers)
432 bench.Start("findfunctab")
433 ctxt.findfunctab(pclnState, containers)
434 bench.Start("dwarfGenerateDebugSyms")
435 dwarfGenerateDebugSyms(ctxt)
436 bench.Start("symtab")
437 symGroupType := ctxt.symtab(pclnState)
438 bench.Start("dodata")
439 ctxt.dodata(symGroupType)
440 bench.Start("address")
441 order := ctxt.address()
442 bench.Start("dwarfcompress")
443 dwarfcompress(ctxt)
444 bench.Start("layout")
445 filesize := ctxt.layout(order)
446
447
448
449
450
451
452
453 if ctxt.Arch.Family != sys.Wasm {
454
455
456 if err := ctxt.Out.Mmap(filesize); err != nil {
457 Exitf("mapping output file failed: %v", err)
458 }
459 }
460
461
462 bench.Start("Asmb")
463 asmb(ctxt)
464 exitIfErrors()
465
466
467
468 bench.Start("GenSymsLate")
469 if thearch.GenSymsLate != nil {
470 thearch.GenSymsLate(ctxt, ctxt.loader)
471 }
472
473 asmbfips(ctxt, *flagFipso)
474
475 bench.Start("Asmb2")
476 asmb2(ctxt)
477
478 bench.Start("Munmap")
479 ctxt.Out.Close()
480
481 bench.Start("hostlink")
482 ctxt.hostlink()
483 if ctxt.Debugvlog != 0 {
484 ctxt.Logf("%s", ctxt.loader.Stat())
485 ctxt.Logf("%d liveness data\n", liveness)
486 }
487 bench.Start("Flush")
488 ctxt.Bso.Flush()
489 bench.Start("archive")
490 ctxt.archive()
491 bench.Report(os.Stdout)
492
493 errorexit()
494 }
495
496 type Rpath struct {
497 set bool
498 val string
499 }
500
501 func (r *Rpath) Set(val string) error {
502 r.set = true
503 r.val = val
504 return nil
505 }
506
507 func (r *Rpath) String() string {
508 return r.val
509 }
510
511 func startProfile() {
512 if *cpuprofile != "" {
513 f, err := os.Create(*cpuprofile)
514 if err != nil {
515 log.Fatalf("%v", err)
516 }
517 if err := pprof.StartCPUProfile(f); err != nil {
518 log.Fatalf("%v", err)
519 }
520 AtExit(func() {
521 pprof.StopCPUProfile()
522 if err = f.Close(); err != nil {
523 log.Fatalf("error closing cpu profile: %v", err)
524 }
525 })
526 }
527 if *memprofile != "" {
528 if *memprofilerate != 0 {
529 runtime.MemProfileRate = int(*memprofilerate)
530 }
531 f, err := os.Create(*memprofile)
532 if err != nil {
533 log.Fatalf("%v", err)
534 }
535 AtExit(func() {
536
537 runtime.GC()
538
539
540
541 const writeLegacyFormat = 1
542 if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
543 log.Fatalf("%v", err)
544 }
545
546 if err := f.Close(); err != nil {
547 log.Fatalf("could not close %v: %v", *memprofile, err)
548 }
549 })
550 }
551 }
552
View as plain text