1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package work
6
7import (
8	"fmt"
9	"io/ioutil"
10	"os"
11	"os/exec"
12	"path/filepath"
13	"strings"
14
15	"cmd/go/internal/base"
16	"cmd/go/internal/cfg"
17	"cmd/go/internal/load"
18	"cmd/go/internal/str"
19)
20
21// The Gccgo toolchain.
22
23type gccgoToolchain struct{}
24
25var GccgoName, GccgoBin string
26var gccgoErr error
27
28func init() {
29	GccgoName = cfg.Getenv("GCCGO")
30	if GccgoName == "" {
31		GccgoName = "gccgo"
32	}
33	GccgoBin, gccgoErr = exec.LookPath(GccgoName)
34}
35
36func (gccgoToolchain) compiler() string {
37	checkGccgoBin()
38	return GccgoBin
39}
40
41func (gccgoToolchain) linker() string {
42	checkGccgoBin()
43	return GccgoBin
44}
45
46func (gccgoToolchain) ar() string {
47	ar := cfg.Getenv("AR")
48	if ar == "" {
49		ar = "ar"
50	}
51	return ar
52}
53
54func checkGccgoBin() {
55	if gccgoErr == nil {
56		return
57	}
58	fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
59	base.SetExitStatus(2)
60	base.Exit()
61}
62
63func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
64	p := a.Package
65	objdir := a.Objdir
66	out := "_go_.o"
67	ofile = objdir + out
68	gcargs := []string{"-g"}
69	gcargs = append(gcargs, b.gccArchArgs()...)
70	gcargs = append(gcargs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
71	gcargs = append(gcargs, "-gno-record-gcc-switches")
72	if pkgpath := gccgoPkgpath(p); pkgpath != "" {
73		gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
74	}
75	if p.Internal.LocalPrefix != "" {
76		gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
77	}
78
79	args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile, forcedGccgoflags)
80	if importcfg != nil {
81		if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
82			if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
83				return "", nil, err
84			}
85			args = append(args, "-fgo-importcfg="+objdir+"importcfg")
86		} else {
87			root := objdir + "_importcfgroot_"
88			if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
89				return "", nil, err
90			}
91			args = append(args, "-I", root)
92		}
93	}
94	if cfg.BuildTrimpath && b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
95		args = append(args, "-ffile-prefix-map="+base.Cwd+"=.")
96		args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
97	}
98	args = append(args, a.Package.Internal.Gccgoflags...)
99	for _, f := range gofiles {
100		args = append(args, mkAbs(p.Dir, f))
101	}
102
103	output, err = b.runOut(a, p.Dir, nil, args)
104	return ofile, output, err
105}
106
107// buildImportcfgSymlinks builds in root a tree of symlinks
108// implementing the directives from importcfg.
109// This serves as a temporary transition mechanism until
110// we can depend on gccgo reading an importcfg directly.
111// (The Go 1.9 and later gc compilers already do.)
112func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
113	for lineNum, line := range strings.Split(string(importcfg), "\n") {
114		lineNum++ // 1-based
115		line = strings.TrimSpace(line)
116		if line == "" {
117			continue
118		}
119		if line == "" || strings.HasPrefix(line, "#") {
120			continue
121		}
122		var verb, args string
123		if i := strings.Index(line, " "); i < 0 {
124			verb = line
125		} else {
126			verb, args = line[:i], strings.TrimSpace(line[i+1:])
127		}
128		var before, after string
129		if i := strings.Index(args, "="); i >= 0 {
130			before, after = args[:i], args[i+1:]
131		}
132		switch verb {
133		default:
134			base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
135		case "packagefile":
136			if before == "" || after == "" {
137				return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
138			}
139			archive := gccgoArchive(root, before)
140			if err := b.Mkdir(filepath.Dir(archive)); err != nil {
141				return err
142			}
143			if err := b.Symlink(after, archive); err != nil {
144				return err
145			}
146		case "importmap":
147			if before == "" || after == "" {
148				return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
149			}
150			beforeA := gccgoArchive(root, before)
151			afterA := gccgoArchive(root, after)
152			if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
153				return err
154			}
155			if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
156				return err
157			}
158			if err := b.Symlink(afterA, beforeA); err != nil {
159				return err
160			}
161		case "packageshlib":
162			return fmt.Errorf("gccgo -importcfg does not support shared libraries")
163		}
164	}
165	return nil
166}
167
168func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
169	p := a.Package
170	var ofiles []string
171	for _, sfile := range sfiles {
172		base := filepath.Base(sfile)
173		ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
174		ofiles = append(ofiles, ofile)
175		sfile = mkAbs(p.Dir, sfile)
176		defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
177		if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
178			defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
179		}
180		defs = tools.maybePIC(defs)
181		defs = append(defs, b.gccArchArgs()...)
182		err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
183		if err != nil {
184			return nil, err
185		}
186	}
187	return ofiles, nil
188}
189
190func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
191	return "", nil
192}
193
194func gccgoArchive(basedir, imp string) string {
195	end := filepath.FromSlash(imp + ".a")
196	afile := filepath.Join(basedir, end)
197	// add "lib" to the final element
198	return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
199}
200
201func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
202	p := a.Package
203	objdir := a.Objdir
204	var absOfiles []string
205	for _, f := range ofiles {
206		absOfiles = append(absOfiles, mkAbs(objdir, f))
207	}
208	var arArgs []string
209	if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
210		// AIX puts both 32-bit and 64-bit objects in the same archive.
211		// Tell the AIX "ar" command to only care about 64-bit objects.
212		arArgs = []string{"-X64"}
213	}
214	absAfile := mkAbs(objdir, afile)
215	// Try with D modifier first, then without if that fails.
216	output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
217	if err != nil {
218		return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
219	}
220
221	if len(output) > 0 {
222		// Show the output if there is any even without errors.
223		b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(output))
224	}
225
226	return nil
227}
228
229func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
230	// gccgo needs explicit linking with all package dependencies,
231	// and all LDFLAGS from cgo dependencies.
232	afiles := []string{}
233	shlibs := []string{}
234	ldflags := b.gccArchArgs()
235	cgoldflags := []string{}
236	usesCgo := false
237	cxx := false
238	objc := false
239	fortran := false
240	if root.Package != nil {
241		cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
242		objc = len(root.Package.MFiles) > 0
243		fortran = len(root.Package.FFiles) > 0
244	}
245
246	readCgoFlags := func(flagsFile string) error {
247		flags, err := ioutil.ReadFile(flagsFile)
248		if err != nil {
249			return err
250		}
251		const ldflagsPrefix = "_CGO_LDFLAGS="
252		for _, line := range strings.Split(string(flags), "\n") {
253			if strings.HasPrefix(line, ldflagsPrefix) {
254				newFlags := strings.Fields(line[len(ldflagsPrefix):])
255				for _, flag := range newFlags {
256					// Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
257					// but they don't mean anything to the linker so filter
258					// them out.
259					if flag != "-g" && !strings.HasPrefix(flag, "-O") {
260						cgoldflags = append(cgoldflags, flag)
261					}
262				}
263			}
264		}
265		return nil
266	}
267
268	var arArgs []string
269	if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
270		// AIX puts both 32-bit and 64-bit objects in the same archive.
271		// Tell the AIX "ar" command to only care about 64-bit objects.
272		arArgs = []string{"-X64"}
273	}
274
275	newID := 0
276	readAndRemoveCgoFlags := func(archive string) (string, error) {
277		newID++
278		newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
279		if err := b.copyFile(newArchive, archive, 0666, false); err != nil {
280			return "", err
281		}
282		if cfg.BuildN || cfg.BuildX {
283			b.Showcmd("", "ar d %s _cgo_flags", newArchive)
284			if cfg.BuildN {
285				// TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
286				// Either the archive is already built and we can read them out,
287				// or we're printing commands to build the archive and can
288				// forward the _cgo_flags directly to this step.
289				return "", nil
290			}
291		}
292		err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
293		if err != nil {
294			return "", err
295		}
296		err = b.run(root, ".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
297		if err != nil {
298			return "", err
299		}
300		err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
301		if err != nil {
302			return "", err
303		}
304		return newArchive, nil
305	}
306
307	// If using -linkshared, find the shared library deps.
308	haveShlib := make(map[string]bool)
309	targetBase := filepath.Base(root.Target)
310	if cfg.BuildLinkshared {
311		for _, a := range root.Deps {
312			p := a.Package
313			if p == nil || p.Shlib == "" {
314				continue
315			}
316
317			// The .a we are linking into this .so
318			// will have its Shlib set to this .so.
319			// Don't start thinking we want to link
320			// this .so into itself.
321			base := filepath.Base(p.Shlib)
322			if base != targetBase {
323				haveShlib[base] = true
324			}
325		}
326	}
327
328	// Arrange the deps into afiles and shlibs.
329	addedShlib := make(map[string]bool)
330	for _, a := range root.Deps {
331		p := a.Package
332		if p != nil && p.Shlib != "" && haveShlib[filepath.Base(p.Shlib)] {
333			// This is a package linked into a shared
334			// library that we will put into shlibs.
335			continue
336		}
337
338		if haveShlib[filepath.Base(a.Target)] {
339			// This is a shared library we want to link against.
340			if !addedShlib[a.Target] {
341				shlibs = append(shlibs, a.Target)
342				addedShlib[a.Target] = true
343			}
344			continue
345		}
346
347		if p != nil {
348			target := a.built
349			if p.UsesCgo() || p.UsesSwig() {
350				var err error
351				target, err = readAndRemoveCgoFlags(target)
352				if err != nil {
353					continue
354				}
355			}
356
357			afiles = append(afiles, target)
358		}
359	}
360
361	for _, a := range allactions {
362		// Gather CgoLDFLAGS, but not from standard packages.
363		// The go tool can dig up runtime/cgo from GOROOT and
364		// think that it should use its CgoLDFLAGS, but gccgo
365		// doesn't use runtime/cgo.
366		if a.Package == nil {
367			continue
368		}
369		if !a.Package.Standard {
370			cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...)
371		}
372		if len(a.Package.CgoFiles) > 0 {
373			usesCgo = true
374		}
375		if a.Package.UsesSwig() {
376			usesCgo = true
377		}
378		if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
379			cxx = true
380		}
381		if len(a.Package.MFiles) > 0 {
382			objc = true
383		}
384		if len(a.Package.FFiles) > 0 {
385			fortran = true
386		}
387	}
388
389	wholeArchive := []string{"-Wl,--whole-archive"}
390	noWholeArchive := []string{"-Wl,--no-whole-archive"}
391	if cfg.Goos == "aix" {
392		wholeArchive = nil
393		noWholeArchive = nil
394	}
395	ldflags = append(ldflags, wholeArchive...)
396	ldflags = append(ldflags, afiles...)
397	ldflags = append(ldflags, noWholeArchive...)
398
399	ldflags = append(ldflags, cgoldflags...)
400	ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
401	if root.Package != nil {
402		ldflags = append(ldflags, root.Package.CgoLDFLAGS...)
403	}
404	if cfg.Goos != "aix" {
405		ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
406	}
407
408	if root.buildID != "" {
409		// On systems that normally use gold or the GNU linker,
410		// use the --build-id option to write a GNU build ID note.
411		switch cfg.Goos {
412		case "android", "dragonfly", "linux", "netbsd":
413			ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID))
414		}
415	}
416
417	var rLibPath string
418	if cfg.Goos == "aix" {
419		rLibPath = "-Wl,-blibpath="
420	} else {
421		rLibPath = "-Wl,-rpath="
422	}
423	for _, shlib := range shlibs {
424		ldflags = append(
425			ldflags,
426			"-L"+filepath.Dir(shlib),
427			rLibPath+filepath.Dir(shlib),
428			"-l"+strings.TrimSuffix(
429				strings.TrimPrefix(filepath.Base(shlib), "lib"),
430				".so"))
431	}
432
433	var realOut string
434	goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive)
435	switch buildmode {
436	case "exe":
437		if usesCgo && cfg.Goos == "linux" {
438			ldflags = append(ldflags, "-Wl,-E")
439		}
440
441	case "c-archive":
442		// Link the Go files into a single .o, and also link
443		// in -lgolibbegin.
444		//
445		// We need to use --whole-archive with -lgolibbegin
446		// because it doesn't define any symbols that will
447		// cause the contents to be pulled in; it's just
448		// initialization code.
449		//
450		// The user remains responsible for linking against
451		// -lgo -lpthread -lm in the final link. We can't use
452		// -r to pick them up because we can't combine
453		// split-stack and non-split-stack code in a single -r
454		// link, and libgo picks up non-split-stack code from
455		// libffi.
456		ldflags = append(ldflags, "-Wl,-r", "-nostdlib")
457		ldflags = append(ldflags, goLibBegin...)
458
459		if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
460			ldflags = append(ldflags, nopie)
461		}
462
463		// We are creating an object file, so we don't want a build ID.
464		if root.buildID == "" {
465			ldflags = b.disableBuildID(ldflags)
466		}
467
468		realOut = out
469		out = out + ".o"
470
471	case "c-shared":
472		ldflags = append(ldflags, "-shared", "-nostdlib")
473		ldflags = append(ldflags, goLibBegin...)
474		ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
475
476	case "shared":
477		if cfg.Goos != "aix" {
478			ldflags = append(ldflags, "-zdefs")
479		}
480		ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
481
482	default:
483		base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
484	}
485
486	switch buildmode {
487	case "exe", "c-shared":
488		if cxx {
489			ldflags = append(ldflags, "-lstdc++")
490		}
491		if objc {
492			ldflags = append(ldflags, "-lobjc")
493		}
494		if fortran {
495			fc := cfg.Getenv("FC")
496			if fc == "" {
497				fc = "gfortran"
498			}
499			// support gfortran out of the box and let others pass the correct link options
500			// via CGO_LDFLAGS
501			if strings.Contains(fc, "gfortran") {
502				ldflags = append(ldflags, "-lgfortran")
503			}
504		}
505	}
506
507	if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
508		return err
509	}
510
511	switch buildmode {
512	case "c-archive":
513		if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
514			return err
515		}
516	}
517	return nil
518}
519
520func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
521	return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
522}
523
524func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
525	return tools.link(b, root, out, importcfg, allactions, "shared", out)
526}
527
528func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
529	p := a.Package
530	inc := filepath.Join(cfg.GOROOT, "pkg", "include")
531	cfile = mkAbs(p.Dir, cfile)
532	defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
533	defs = append(defs, b.gccArchArgs()...)
534	if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
535		defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
536	}
537	compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
538	if b.gccSupportsFlag(compiler, "-fsplit-stack") {
539		defs = append(defs, "-fsplit-stack")
540	}
541	defs = tools.maybePIC(defs)
542	if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
543		defs = append(defs, "-ffile-prefix-map="+base.Cwd+"=.")
544		defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
545	} else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
546		defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
547	}
548	if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
549		defs = append(defs, "-gno-record-gcc-switches")
550	}
551	return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
552		"-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
553}
554
555// maybePIC adds -fPIC to the list of arguments if needed.
556func (tools gccgoToolchain) maybePIC(args []string) []string {
557	switch cfg.BuildBuildmode {
558	case "c-shared", "shared", "plugin":
559		args = append(args, "-fPIC")
560	}
561	return args
562}
563
564func gccgoPkgpath(p *load.Package) string {
565	if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
566		return ""
567	}
568	return p.ImportPath
569}
570
571func gccgoCleanPkgpath(p *load.Package) string {
572	clean := func(r rune) rune {
573		switch {
574		case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
575			'0' <= r && r <= '9':
576			return r
577		}
578		return '_'
579	}
580	return strings.Map(clean, gccgoPkgpath(p))
581}
582