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