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