blob: 112675a31bd7798f230dea9489a51b6725ef555b [file] [log] [blame]
Peter Collingbournead9841e2014-11-27 00:06:42 +00001//===- gllgo.go - gccgo-like driver for llgo ------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This is llgo's driver. It has a gccgo-like interface in order to easily
11// interoperate with the "go" command and the libgo build system.
12//
13//===----------------------------------------------------------------------===//
14
15package main
16
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +000017/*
18#include "config.h"
19*/
20import "C"
21
Peter Collingbournead9841e2014-11-27 00:06:42 +000022import (
23 "errors"
24 "fmt"
25 "go/scanner"
Peter Collingbourne93509422014-12-31 00:25:32 +000026 "go/token"
Peter Collingbournead9841e2014-11-27 00:06:42 +000027 "io/ioutil"
28 "log"
29 "os"
30 "os/exec"
31 "path/filepath"
32 "strings"
33
34 "llvm.org/llgo/debug"
Peter Collingbourne93509422014-12-31 00:25:32 +000035 "llvm.org/llgo/driver"
Peter Collingbournead9841e2014-11-27 00:06:42 +000036 "llvm.org/llgo/irgen"
37 "llvm.org/llvm/bindings/go/llvm"
38)
39
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +000040const LibDirSuffix = C.LLVM_LIBDIR_SUFFIX
41
Peter Collingbournead9841e2014-11-27 00:06:42 +000042func report(err error) {
43 if list, ok := err.(scanner.ErrorList); ok {
44 for _, e := range list {
45 fmt.Fprintf(os.Stderr, "%s\n", e)
46 }
47 } else if err != nil {
48 fmt.Fprintf(os.Stderr, "gllgo: error: %s\n", err)
49 }
50}
51
52func llvmVersion() string {
53 return strings.Replace(llvm.Version, "svn", "", 1)
54}
55
56func displayVersion() {
57 fmt.Printf("llgo version %s (%s)\n\n", llvmVersion(), irgen.GoVersion())
58 os.Exit(0)
59}
60
61func initCompiler(opts *driverOptions) (*irgen.Compiler, error) {
62 importPaths := make([]string, len(opts.importPaths)+len(opts.libPaths))
63 copy(importPaths, opts.importPaths)
64 copy(importPaths[len(opts.importPaths):], opts.libPaths)
65 if opts.prefix != "" {
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +000066 importPaths = append(importPaths, filepath.Join(opts.prefix, "lib"+LibDirSuffix, "go", "llgo-"+llvmVersion()))
Peter Collingbournead9841e2014-11-27 00:06:42 +000067 }
68 copts := irgen.CompilerOptions{
69 TargetTriple: opts.triple,
70 GenerateDebug: opts.generateDebug,
71 DebugPrefixMaps: opts.debugPrefixMaps,
72 DumpSSA: opts.dumpSSA,
73 GccgoPath: opts.gccgoPath,
Peter Collingbourned34d92f2014-12-31 00:25:39 +000074 GccgoABI: opts.gccgoPath != "",
Peter Collingbournead9841e2014-11-27 00:06:42 +000075 ImportPaths: importPaths,
76 SanitizerAttribute: opts.sanitizer.getAttribute(),
77 }
78 if opts.dumpTrace {
79 copts.Logger = log.New(os.Stderr, "", 0)
80 }
81 return irgen.NewCompiler(copts)
82}
83
84type actionKind int
85
86const (
87 actionAssemble = actionKind(iota)
88 actionCompile
89 actionLink
90 actionPrint
91)
92
93type action struct {
94 kind actionKind
95 inputs []string
96}
97
98type sanitizerOptions struct {
99 blacklist string
100 crtPrefix string
101
102 address, thread, memory, dataflow bool
103}
104
105func (san *sanitizerOptions) resourcePath() string {
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000106 return filepath.Join(san.crtPrefix, "lib"+LibDirSuffix, "clang", llvmVersion())
Peter Collingbournead9841e2014-11-27 00:06:42 +0000107}
108
109func (san *sanitizerOptions) isPIEDefault() bool {
110 return san.thread || san.memory || san.dataflow
111}
112
113func (san *sanitizerOptions) addPasses(mpm, fpm llvm.PassManager) {
114 switch {
115 case san.address:
116 mpm.AddAddressSanitizerModulePass()
117 fpm.AddAddressSanitizerFunctionPass()
118 case san.thread:
119 mpm.AddThreadSanitizerPass()
120 case san.memory:
121 mpm.AddMemorySanitizerPass()
122 case san.dataflow:
123 blacklist := san.blacklist
124 if blacklist == "" {
125 blacklist = filepath.Join(san.resourcePath(), "dfsan_abilist.txt")
126 }
Peter Collingbournec969b1b2015-02-13 22:58:36 +0000127 mpm.AddDataFlowSanitizerPass([]string{blacklist})
Peter Collingbournead9841e2014-11-27 00:06:42 +0000128 }
129}
130
131func (san *sanitizerOptions) libPath(triple, sanitizerName string) string {
132 s := strings.Split(triple, "-")
133 return filepath.Join(san.resourcePath(), "lib", s[2], "libclang_rt."+sanitizerName+"-"+s[0]+".a")
134}
135
136func (san *sanitizerOptions) addLibsForSanitizer(flags []string, triple, sanitizerName string) []string {
137 return append(flags, san.libPath(triple, sanitizerName),
138 "-Wl,--no-as-needed", "-lpthread", "-lrt", "-lm", "-ldl")
139}
140
141func (san *sanitizerOptions) addLibs(triple string, flags []string) []string {
142 switch {
143 case san.address:
144 flags = san.addLibsForSanitizer(flags, triple, "asan")
145 case san.thread:
146 flags = san.addLibsForSanitizer(flags, triple, "tsan")
147 case san.memory:
148 flags = san.addLibsForSanitizer(flags, triple, "msan")
149 case san.dataflow:
150 flags = san.addLibsForSanitizer(flags, triple, "dfsan")
151 }
152
153 return flags
154}
155
156func (san *sanitizerOptions) getAttribute() llvm.Attribute {
157 switch {
158 case san.address:
159 return llvm.SanitizeAddressAttribute
160 case san.thread:
161 return llvm.SanitizeThreadAttribute
162 case san.memory:
163 return llvm.SanitizeMemoryAttribute
164 default:
165 return 0
166 }
167}
168
169type driverOptions struct {
170 actions []action
171 output string
172
173 bprefix string
174 debugPrefixMaps []debug.PrefixMap
175 dumpSSA bool
176 dumpTrace bool
177 emitIR bool
178 gccgoPath string
179 generateDebug bool
180 importPaths []string
181 libPaths []string
182 llvmArgs []string
183 lto bool
184 optLevel int
185 pic bool
186 pieLink bool
187 pkgpath string
188 plugins []string
189 prefix string
190 sanitizer sanitizerOptions
191 sizeLevel int
192 staticLibgcc bool
193 staticLibgo bool
194 staticLink bool
195 triple string
196}
197
198func getInstPrefix() (string, error) {
199 path, err := exec.LookPath(os.Args[0])
200 if err != nil {
201 return "", err
202 }
203
204 path, err = filepath.EvalSymlinks(path)
205 if err != nil {
206 return "", err
207 }
208
209 prefix := filepath.Join(path, "..", "..")
210 return prefix, nil
211}
212
213func parseArguments(args []string) (opts driverOptions, err error) {
214 var goInputs, otherInputs []string
215 hasOtherNonFlagInputs := false
216 noPrefix := false
217 actionKind := actionLink
218 opts.triple = llvm.DefaultTargetTriple()
219
220 for len(args) > 0 {
221 consumedArgs := 1
222
223 switch {
224 case !strings.HasPrefix(args[0], "-"):
225 if strings.HasSuffix(args[0], ".go") {
226 goInputs = append(goInputs, args[0])
227 } else {
228 hasOtherNonFlagInputs = true
229 otherInputs = append(otherInputs, args[0])
230 }
231
232 case strings.HasPrefix(args[0], "-Wl,"), strings.HasPrefix(args[0], "-l"), strings.HasPrefix(args[0], "--sysroot="):
233 // TODO(pcc): Handle these correctly.
234 otherInputs = append(otherInputs, args[0])
235
236 case args[0] == "-B":
237 if len(args) == 1 {
238 return opts, errors.New("missing argument after '-B'")
239 }
240 opts.bprefix = args[1]
241 consumedArgs = 2
242
243 case args[0] == "-D":
244 if len(args) == 1 {
245 return opts, errors.New("missing argument after '-D'")
246 }
247 otherInputs = append(otherInputs, args[0], args[1])
248 consumedArgs = 2
249
250 case strings.HasPrefix(args[0], "-D"):
251 otherInputs = append(otherInputs, args[0])
252
253 case args[0] == "-I":
254 if len(args) == 1 {
255 return opts, errors.New("missing argument after '-I'")
256 }
257 opts.importPaths = append(opts.importPaths, args[1])
258 consumedArgs = 2
259
260 case strings.HasPrefix(args[0], "-I"):
261 opts.importPaths = append(opts.importPaths, args[0][2:])
262
263 case args[0] == "-isystem":
264 if len(args) == 1 {
265 return opts, errors.New("missing argument after '-isystem'")
266 }
267 otherInputs = append(otherInputs, args[0], args[1])
268 consumedArgs = 2
269
270 case args[0] == "-L":
271 if len(args) == 1 {
272 return opts, errors.New("missing argument after '-L'")
273 }
274 opts.libPaths = append(opts.libPaths, args[1])
275 consumedArgs = 2
276
277 case strings.HasPrefix(args[0], "-L"):
278 opts.libPaths = append(opts.libPaths, args[0][2:])
279
280 case args[0] == "-O0":
281 opts.optLevel = 0
282
283 case args[0] == "-O1", args[0] == "-O":
284 opts.optLevel = 1
285
286 case args[0] == "-O2":
287 opts.optLevel = 2
288
289 case args[0] == "-Os":
290 opts.optLevel = 2
291 opts.sizeLevel = 1
292
293 case args[0] == "-O3":
294 opts.optLevel = 3
295
296 case args[0] == "-S":
297 actionKind = actionAssemble
298
299 case args[0] == "-c":
300 actionKind = actionCompile
301
302 case strings.HasPrefix(args[0], "-fcompilerrt-prefix="):
303 opts.sanitizer.crtPrefix = args[0][20:]
304
305 case strings.HasPrefix(args[0], "-fdebug-prefix-map="):
306 split := strings.SplitN(args[0][19:], "=", 2)
307 if len(split) < 2 {
308 return opts, fmt.Errorf("argument '%s' must be of form '-fdebug-prefix-map=SOURCE=REPLACEMENT'", args[0])
309 }
310 opts.debugPrefixMaps = append(opts.debugPrefixMaps, debug.PrefixMap{split[0], split[1]})
311
312 case args[0] == "-fdump-ssa":
313 opts.dumpSSA = true
314
315 case args[0] == "-fdump-trace":
316 opts.dumpTrace = true
317
318 case strings.HasPrefix(args[0], "-fgccgo-path="):
319 opts.gccgoPath = args[0][13:]
320
321 case strings.HasPrefix(args[0], "-fgo-pkgpath="):
322 opts.pkgpath = args[0][13:]
323
324 case strings.HasPrefix(args[0], "-fgo-relative-import-path="):
325 // TODO(pcc): Handle this.
326
327 case args[0] == "-fload-plugin":
328 if len(args) == 1 {
329 return opts, errors.New("missing argument after '-fload-plugin'")
330 }
331 opts.plugins = append(opts.plugins, args[1])
332 consumedArgs = 2
333
334 case args[0] == "-fno-toplevel-reorder":
335 // This is a GCC-specific code generation option. Ignore.
336
337 case args[0] == "-emit-llvm":
338 opts.emitIR = true
339
340 case args[0] == "-flto":
341 opts.lto = true
342
343 case args[0] == "-fPIC":
344 opts.pic = true
345
346 case strings.HasPrefix(args[0], "-fsanitize-blacklist="):
347 opts.sanitizer.blacklist = args[0][21:]
348
349 // TODO(pcc): Enforce mutual exclusion between sanitizers.
350
351 case args[0] == "-fsanitize=address":
352 opts.sanitizer.address = true
353
354 case args[0] == "-fsanitize=thread":
355 opts.sanitizer.thread = true
356
357 case args[0] == "-fsanitize=memory":
358 opts.sanitizer.memory = true
359
360 case args[0] == "-fsanitize=dataflow":
361 opts.sanitizer.dataflow = true
362
363 case args[0] == "-g":
364 opts.generateDebug = true
365
366 case args[0] == "-mllvm":
367 if len(args) == 1 {
368 return opts, errors.New("missing argument after '-mllvm'")
369 }
370 opts.llvmArgs = append(opts.llvmArgs, args[1])
371 consumedArgs = 2
372
373 case strings.HasPrefix(args[0], "-m"), args[0] == "-funsafe-math-optimizations", args[0] == "-ffp-contract=off":
374 // TODO(pcc): Handle code generation options.
375
376 case args[0] == "-no-prefix":
377 noPrefix = true
378
379 case args[0] == "-o":
380 if len(args) == 1 {
381 return opts, errors.New("missing argument after '-o'")
382 }
383 opts.output = args[1]
384 consumedArgs = 2
385
386 case args[0] == "-pie":
387 opts.pieLink = true
388
389 case args[0] == "-dumpversion",
390 args[0] == "-print-libgcc-file-name",
391 args[0] == "-print-multi-os-directory",
392 args[0] == "--version":
393 actionKind = actionPrint
394 opts.output = args[0]
395
396 case args[0] == "-static":
397 opts.staticLink = true
398
399 case args[0] == "-static-libgcc":
400 opts.staticLibgcc = true
401
402 case args[0] == "-static-libgo":
403 opts.staticLibgo = true
404
405 default:
406 return opts, fmt.Errorf("unrecognized command line option '%s'", args[0])
407 }
408
409 args = args[consumedArgs:]
410 }
411
412 if actionKind != actionPrint && len(goInputs) == 0 && !hasOtherNonFlagInputs {
413 return opts, errors.New("no input files")
414 }
415
416 if !noPrefix {
417 opts.prefix, err = getInstPrefix()
418 if err != nil {
419 return opts, err
420 }
421 }
422
423 if opts.sanitizer.crtPrefix == "" {
424 opts.sanitizer.crtPrefix = opts.prefix
425 }
426
427 if opts.sanitizer.isPIEDefault() {
428 // This should really only be turning on -fPIE, but this isn't
429 // easy to do from Go, and -fPIC is a superset of it anyway.
430 opts.pic = true
431 opts.pieLink = true
432 }
433
434 switch actionKind {
435 case actionLink:
436 if len(goInputs) != 0 {
437 opts.actions = []action{action{actionCompile, goInputs}}
438 }
439 opts.actions = append(opts.actions, action{actionLink, otherInputs})
440
441 case actionCompile, actionAssemble:
442 if len(goInputs) != 0 {
443 opts.actions = []action{action{actionKind, goInputs}}
444 }
445
446 case actionPrint:
447 opts.actions = []action{action{actionKind, nil}}
448 }
449
450 if opts.output == "" && len(opts.actions) != 0 {
451 switch actionKind {
452 case actionCompile, actionAssemble:
453 base := filepath.Base(goInputs[0])
454 base = base[0 : len(base)-3]
455 if actionKind == actionCompile {
456 opts.output = base + ".o"
457 } else {
458 opts.output = base + ".s"
459 }
460
461 case actionLink:
462 opts.output = "a.out"
463 }
464 }
465
466 return opts, nil
467}
468
469func runPasses(opts *driverOptions, tm llvm.TargetMachine, m llvm.Module) {
470 fpm := llvm.NewFunctionPassManagerForModule(m)
471 defer fpm.Dispose()
472
473 mpm := llvm.NewPassManager()
474 defer mpm.Dispose()
475
476 pmb := llvm.NewPassManagerBuilder()
477 defer pmb.Dispose()
478
479 pmb.SetOptLevel(opts.optLevel)
480 pmb.SetSizeLevel(opts.sizeLevel)
481
482 target := tm.TargetData()
483 mpm.Add(target)
484 fpm.Add(target)
485 tm.AddAnalysisPasses(mpm)
486 tm.AddAnalysisPasses(fpm)
487
488 mpm.AddVerifierPass()
489 fpm.AddVerifierPass()
490
491 pmb.Populate(mpm)
492 pmb.PopulateFunc(fpm)
493
494 if opts.optLevel == 0 {
495 // Remove references (via the descriptor) to dead functions,
496 // for compatibility with other compilers.
497 mpm.AddGlobalDCEPass()
498 }
499
500 opts.sanitizer.addPasses(mpm, fpm)
501
502 fpm.InitializeFunc()
503 for fn := m.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
504 fpm.RunFunc(fn)
505 }
506 fpm.FinalizeFunc()
507
508 mpm.Run(m)
509}
510
511func getMetadataSectionInlineAsm(name string) string {
512 // ELF: creates a non-allocated excluded section.
513 return ".section \"" + name + "\", \"e\"\n"
514}
515
516func getDataInlineAsm(data []byte) string {
517 edata := make([]byte, 0, len(data)*4+10)
518
519 edata = append(edata, ".ascii \""...)
520 for i := range data {
521 switch data[i] {
522 case '\000':
523 edata = append(edata, "\\000"...)
524 continue
525 case '\n':
526 edata = append(edata, "\\n"...)
527 continue
528 case '"', '\\':
529 edata = append(edata, '\\')
530 }
531 edata = append(edata, data[i])
532 }
533 edata = append(edata, "\"\n"...)
534 return string(edata)
535}
536
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000537// Get the lib path to the standard libraries for the given driver options.
538// This is normally 'lib' but can vary for cross compilation, LTO, sanitizers
539// etc.
540func getLibDir(opts *driverOptions) string {
541 lib := "lib" + LibDirSuffix
Peter Collingbournead9841e2014-11-27 00:06:42 +0000542 switch {
543 case opts.lto:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000544 return filepath.Join(lib, "llvm-lto.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000545 case opts.sanitizer.address:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000546 return filepath.Join(lib, "llvm-asan.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000547 case opts.sanitizer.thread:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000548 return filepath.Join(lib, "llvm-tsan.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000549 case opts.sanitizer.memory:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000550 return filepath.Join(lib, "llvm-msan.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000551 case opts.sanitizer.dataflow:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000552 return filepath.Join(lib, "llvm-dfsan.0")
Peter Collingbournead9841e2014-11-27 00:06:42 +0000553 default:
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000554 return lib
Peter Collingbournead9841e2014-11-27 00:06:42 +0000555 }
556}
557
558func performAction(opts *driverOptions, kind actionKind, inputs []string, output string) error {
559 switch kind {
560 case actionPrint:
561 switch opts.output {
562 case "-dumpversion":
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000563 fmt.Println("llgo-" + llvmVersion())
Peter Collingbournead9841e2014-11-27 00:06:42 +0000564 return nil
565 case "-print-libgcc-file-name":
566 cmd := exec.Command(opts.bprefix+"gcc", "-print-libgcc-file-name")
567 out, err := cmd.CombinedOutput()
568 os.Stdout.Write(out)
569 return err
570 case "-print-multi-os-directory":
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000571 fmt.Println(filepath.Join("..", getLibDir(opts)))
Peter Collingbournead9841e2014-11-27 00:06:42 +0000572 return nil
573 case "--version":
574 displayVersion()
575 return nil
576 default:
577 panic("unexpected print command")
578 }
579
580 case actionCompile, actionAssemble:
581 compiler, err := initCompiler(opts)
582 if err != nil {
583 return err
584 }
585
Peter Collingbourne93509422014-12-31 00:25:32 +0000586 fset := token.NewFileSet()
587 files, err := driver.ParseFiles(fset, inputs)
588 if err != nil {
589 return err
590 }
591
592 module, err := compiler.Compile(fset, files, opts.pkgpath)
Peter Collingbournead9841e2014-11-27 00:06:42 +0000593 if err != nil {
594 return err
595 }
596
597 defer module.Dispose()
598
599 target, err := llvm.GetTargetFromTriple(opts.triple)
600 if err != nil {
601 return err
602 }
603
604 optLevel := [...]llvm.CodeGenOptLevel{
605 llvm.CodeGenLevelNone,
606 llvm.CodeGenLevelLess,
607 llvm.CodeGenLevelDefault,
608 llvm.CodeGenLevelAggressive,
609 }[opts.optLevel]
610
611 relocMode := llvm.RelocStatic
612 if opts.pic {
613 relocMode = llvm.RelocPIC
614 }
615
616 tm := target.CreateTargetMachine(opts.triple, "", "", optLevel,
617 relocMode, llvm.CodeModelDefault)
618 defer tm.Dispose()
619
620 runPasses(opts, tm, module.Module)
621
622 var file *os.File
623 if output == "-" {
624 file = os.Stdout
625 } else {
626 file, err = os.Create(output)
627 if err != nil {
628 return err
629 }
630 defer file.Close()
631 }
632
633 switch {
634 case !opts.lto && !opts.emitIR:
635 if module.ExportData != nil {
636 asm := getMetadataSectionInlineAsm(".go_export")
637 asm += getDataInlineAsm(module.ExportData)
638 module.Module.SetInlineAsm(asm)
639 }
640
641 fileType := llvm.AssemblyFile
642 if kind == actionCompile {
643 fileType = llvm.ObjectFile
644 }
645 mb, err := tm.EmitToMemoryBuffer(module.Module, fileType)
646 if err != nil {
647 return err
648 }
649 defer mb.Dispose()
650
651 bytes := mb.Bytes()
652 _, err = file.Write(bytes)
653 return err
654
655 case opts.lto:
656 bcmb := llvm.WriteBitcodeToMemoryBuffer(module.Module)
657 defer bcmb.Dispose()
658
659 // This is a bit of a hack. We just want an object file
660 // containing some metadata sections. This might be simpler
661 // if we had bindings for the MC library, but for now we create
662 // a fresh module containing only inline asm that creates the
663 // sections.
664 outmodule := llvm.NewModule("")
665 defer outmodule.Dispose()
666 asm := getMetadataSectionInlineAsm(".llvmbc")
667 asm += getDataInlineAsm(bcmb.Bytes())
668 if module.ExportData != nil {
669 asm += getMetadataSectionInlineAsm(".go_export")
670 asm += getDataInlineAsm(module.ExportData)
671 }
672 outmodule.SetInlineAsm(asm)
673
674 fileType := llvm.AssemblyFile
675 if kind == actionCompile {
676 fileType = llvm.ObjectFile
677 }
678 mb, err := tm.EmitToMemoryBuffer(outmodule, fileType)
679 if err != nil {
680 return err
681 }
682 defer mb.Dispose()
683
684 bytes := mb.Bytes()
685 _, err = file.Write(bytes)
686 return err
687
688 case kind == actionCompile:
689 err := llvm.WriteBitcodeToFile(module.Module, file)
690 return err
691
692 case kind == actionAssemble:
693 _, err := file.WriteString(module.Module.String())
694 return err
695
696 default:
697 panic("unexpected action kind")
698 }
699
700 case actionLink:
701 // TODO(pcc): Teach this to do LTO.
702 args := []string{"-o", output}
703 if opts.pic {
704 args = append(args, "-fPIC")
705 }
706 if opts.pieLink {
707 args = append(args, "-pie")
708 }
709 if opts.staticLink {
710 args = append(args, "-static")
711 }
712 if opts.staticLibgcc {
713 args = append(args, "-static-libgcc")
714 }
715 for _, p := range opts.libPaths {
716 args = append(args, "-L", p)
717 }
718 for _, p := range opts.importPaths {
719 args = append(args, "-I", p)
720 }
721 args = append(args, inputs...)
722 var linkerPath string
723 if opts.gccgoPath == "" {
724 // TODO(pcc): See if we can avoid calling gcc here.
725 // We currently rely on it to find crt*.o and compile
726 // any C source files passed as arguments.
727 linkerPath = opts.bprefix + "gcc"
728
729 if opts.prefix != "" {
Chandler Carrutha5ecd8a92014-12-29 22:57:21 +0000730 libdir := filepath.Join(opts.prefix, getLibDir(opts))
Peter Collingbournead9841e2014-11-27 00:06:42 +0000731 args = append(args, "-L", libdir)
732 if !opts.staticLibgo {
733 args = append(args, "-Wl,-rpath,"+libdir)
734 }
735 }
736
737 args = append(args, "-lgobegin-llgo")
738 if opts.staticLibgo {
739 args = append(args, "-Wl,-Bstatic", "-lgo-llgo", "-Wl,-Bdynamic", "-lpthread", "-lm")
740 } else {
741 args = append(args, "-lgo-llgo")
742 }
743 } else {
744 linkerPath = opts.gccgoPath
745 if opts.staticLibgo {
746 args = append(args, "-static-libgo")
747 }
748 }
749
750 args = opts.sanitizer.addLibs(opts.triple, args)
751
752 cmd := exec.Command(linkerPath, args...)
753 out, err := cmd.CombinedOutput()
754 if err != nil {
755 os.Stderr.Write(out)
756 }
757 return err
758
759 default:
760 panic("unexpected action kind")
761 }
762}
763
764func performActions(opts *driverOptions) error {
765 var extraInput string
766
767 for _, plugin := range opts.plugins {
768 err := llvm.LoadLibraryPermanently(plugin)
769 if err != nil {
770 return err
771 }
772 }
773
774 llvm.ParseCommandLineOptions(append([]string{"llgo"}, opts.llvmArgs...), "llgo (LLVM option parsing)\n")
775
776 for i, action := range opts.actions {
777 var output string
778 if i == len(opts.actions)-1 {
779 output = opts.output
780 } else {
781 tmpfile, err := ioutil.TempFile("", "llgo")
782 if err != nil {
783 return err
784 }
785 output = tmpfile.Name() + ".o"
786 tmpfile.Close()
787 err = os.Remove(tmpfile.Name())
788 if err != nil {
789 return err
790 }
791 defer os.Remove(output)
792 }
793
794 inputs := action.inputs
795 if extraInput != "" {
796 inputs = append([]string{extraInput}, inputs...)
797 }
798
799 err := performAction(opts, action.kind, inputs, output)
800 if err != nil {
801 return err
802 }
803
804 extraInput = output
805 }
806
807 return nil
808}
809
810func main() {
811 llvm.InitializeAllTargets()
812 llvm.InitializeAllTargetMCs()
813 llvm.InitializeAllTargetInfos()
814 llvm.InitializeAllAsmParsers()
815 llvm.InitializeAllAsmPrinters()
816
817 opts, err := parseArguments(os.Args[1:])
818 if err != nil {
819 report(err)
820 os.Exit(1)
821 }
822
823 err = performActions(&opts)
824 if err != nil {
825 report(err)
826 os.Exit(1)
827 }
828}