1// Copyright 2016 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package main67import (8 "bufio"9 "bytes"10 "debug/elf"11 "debug/macho"12 "debug/pe"13 "errors"14 "internal/abi"15 "internal/buildcfg"16 "internal/platform"17 "internal/testenv"18 "internal/xcoff"19 "os"20 "os/exec"21 "path/filepath"22 "regexp"23 "runtime"24 "strconv"25 "strings"26 "testing"27 "unsafe"2829 imacho "cmd/internal/macho"30 "cmd/internal/objfile"31 "cmd/internal/sys"32)3334// TestMain allows this test binary to run as a -toolexec wrapper for35// the 'go' command. If LINK_TEST_TOOLEXEC is set, TestMain runs the36// binary as if it were cmd/link, and otherwise runs the requested37// tool as a subprocess.38//39// This allows the test to verify the behavior of the current contents of the40// cmd/link package even if the installed cmd/link binary is stale.41func TestMain(m *testing.M) {42 // Are we running as a toolexec wrapper? If so then run either43 // the correct tool or this executable itself (for the linker).44 // Running as toolexec wrapper.45 if os.Getenv("LINK_TEST_TOOLEXEC") != "" {46 if strings.TrimSuffix(filepath.Base(os.Args[1]), ".exe") == "link" {47 // Running as a -toolexec linker, and the tool is cmd/link.48 // Substitute this test binary for the linker.49 os.Args = os.Args[1:]50 main()51 os.Exit(0)52 }53 // Running some other tool.54 cmd := exec.Command(os.Args[1], os.Args[2:]...)55 cmd.Stdin = os.Stdin56 cmd.Stdout = os.Stdout57 cmd.Stderr = os.Stderr58 if err := cmd.Run(); err != nil {59 os.Exit(1)60 }61 os.Exit(0)62 }6364 // Are we being asked to run as the linker (without toolexec)?65 // If so then kick off main.66 if os.Getenv("LINK_TEST_EXEC_LINKER") != "" {67 main()68 os.Exit(0)69 }7071 if testExe, err := os.Executable(); err == nil {72 // on wasm, some phones, we expect an error from os.Executable()73 testLinker = testExe74 }7576 // Not running as a -toolexec wrapper or as a linker executable.77 // Just run the tests.78 os.Exit(m.Run())79}8081// testLinker is the path of the test executable being run.82// This is used by [TestScript].83var testLinker string8485// goCmd returns a [*exec.Cmd] that runs the go tool using86// the current linker sources rather than the installed linker.87// The first element of the args parameter should be the go subcommand88// to run, such as "build" or "run". It must be a subcommand that89// takes the go command's build flags.90func goCmd(t *testing.T, args ...string) *exec.Cmd {91 goArgs := []string{args[0], "-toolexec", testenv.Executable(t)}92 args = append(goArgs, args[1:]...)93 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)94 cmd = testenv.CleanCmdEnv(cmd)95 cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")96 return cmd97}9899// linkCmd returns a [*exec.Cmd] that runs the linker built from100// the current sources. This is like "go tool link", but runs the101// current linker rather than the installed one.102func linkCmd(t *testing.T, args ...string) *exec.Cmd {103 // Set up the arguments that TestMain looks for.104 args = append([]string{"link"}, args...)105 cmd := testenv.Command(t, testenv.Executable(t), args...)106 cmd = testenv.CleanCmdEnv(cmd)107 cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")108 return cmd109}110111var AuthorPaidByTheColumnInch struct {112 fog int `text:"London. Michaelmas term lately over, and the Lord Chancellor sitting in Lincoln’s Inn Hall. Implacable November weather. As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill. Smoke lowering down from chimney-pots, making a soft black drizzle, with flakes of soot in it as big as full-grown snowflakes—gone into mourning, one might imagine, for the death of the sun. Dogs, undistinguishable in mire. Horses, scarcely better; splashed to their very blinkers. Foot passengers, jostling one another’s umbrellas in a general infection of ill temper, and losing their foot-hold at street-corners, where tens of thousands of other foot passengers have been slipping and sliding since the day broke (if this day ever broke), adding new deposits to the crust upon crust of mud, sticking at those points tenaciously to the pavement, and accumulating at compound interest. Fog everywhere. Fog up the river, where it flows among green aits and meadows; fog down the river, where it rolls defiled among the tiers of shipping and the waterside pollutions of a great (and dirty) city. Fog on the Essex marshes, fog on the Kentish heights. Fog creeping into the cabooses of collier-brigs; fog lying out on the yards and hovering in the rigging of great ships; fog drooping on the gunwales of barges and small boats. Fog in the eyes and throats of ancient Greenwich pensioners, wheezing by the firesides of their wards; fog in the stem and bowl of the afternoon pipe of the wrathful skipper, down in his close cabin; fog cruelly pinching the toes and fingers of his shivering little ‘prentice boy on deck. Chance people on the bridges peeping over the parapets into a nether sky of fog, with fog all round them, as if they were up in a balloon and hanging in the misty clouds. Gas looming through the fog in divers places in the streets, much as the sun may, from the spongey fields, be seen to loom by husbandman and ploughboy. Most of the shops lighted two hours before their time—as the gas seems to know, for it has a haggard and unwilling look. The raw afternoon is rawest, and the dense fog is densest, and the muddy streets are muddiest near that leaden-headed old obstruction, appropriate ornament for the threshold of a leaden-headed old corporation, Temple Bar. And hard by Temple Bar, in Lincoln’s Inn Hall, at the very heart of the fog, sits the Lord High Chancellor in his High Court of Chancery."`113114 wind int `text:"It was grand to see how the wind awoke, and bent the trees, and drove the rain before it like a cloud of smoke; and to hear the solemn thunder, and to see the lightning; and while thinking with awe of the tremendous powers by which our little lives are encompassed, to consider how beneficent they are, and how upon the smallest flower and leaf there was already a freshness poured from all this seeming rage, which seemed to make creation new again."`115116 jarndyce int `text:"Jarndyce and Jarndyce drones on. This scarecrow of a suit has, over the course of time, become so complicated, that no man alive knows what it means. The parties to it understand it least; but it has been observed that no two Chancery lawyers can talk about it for five minutes, without coming to a total disagreement as to all the premises. Innumerable children have been born into the cause; innumerable young people have married into it; innumerable old people have died out of it. Scores of persons have deliriously found themselves made parties in Jarndyce and Jarndyce, without knowing how or why; whole families have inherited legendary hatreds with the suit. The little plaintiff or defendant, who was promised a new rocking-horse when Jarndyce and Jarndyce should be settled, has grown up, possessed himself of a real horse, and trotted away into the other world. Fair wards of court have faded into mothers and grandmothers; a long procession of Chancellors has come in and gone out; the legion of bills in the suit have been transformed into mere bills of mortality; there are not three Jarndyces left upon the earth perhaps, since old Tom Jarndyce in despair blew his brains out at a coffee-house in Chancery Lane; but Jarndyce and Jarndyce still drags its dreary length before the Court, perennially hopeless."`117118 principle int `text:"The one great principle of the English law is, to make business for itself. There is no other principle distinctly, certainly, and consistently maintained through all its narrow turnings. Viewed by this light it becomes a coherent scheme, and not the monstrous maze the laity are apt to think it. Let them but once clearly perceive that its grand principle is to make business for itself at their expense, and surely they will cease to grumble."`119}120121func TestLargeSymName(t *testing.T) {122 // The compiler generates a symbol name using the string form of the123 // type. This tests that the linker can read symbol names larger than124 // the bufio buffer. Issue #15104.125 _ = AuthorPaidByTheColumnInch126}127128func TestIssue21703(t *testing.T) {129 t.Parallel()130131 testenv.MustHaveGoBuild(t)132 // N.B. the build below explicitly doesn't pass through133 // -asan/-msan/-race, so we don't care about those.134 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)135136 const source = `137package main138const X = "\n!\n"139func main() {}140`141142 tmpdir := t.TempDir()143 main := filepath.Join(tmpdir, "main.go")144145 err := os.WriteFile(main, []byte(source), 0666)146 if err != nil {147 t.Fatalf("failed to write main.go: %v\n", err)148 }149150 importcfgfile := filepath.Join(tmpdir, "importcfg")151 testenv.WriteImportcfg(t, importcfgfile, nil, main)152153 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")154 cmd.Dir = tmpdir155 out, err := cmd.CombinedOutput()156 if err != nil {157 t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)158 }159160 cmd = linkCmd(t, "-importcfg="+importcfgfile, "main.o")161 cmd.Dir = tmpdir162 out, err = cmd.CombinedOutput()163 if err != nil {164 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {165 testenv.SkipFlaky(t, 58806)166 }167 t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)168 }169}170171// TestIssue28429 ensures that the linker does not attempt to link172// sections not named *.o. Such sections may be used by a build system173// to, for example, save facts produced by a modular static analysis174// such as golang.org/x/tools/go/analysis.175func TestIssue28429(t *testing.T) {176 t.Parallel()177178 testenv.MustHaveGoBuild(t)179 // N.B. go build below explicitly doesn't pass through180 // -asan/-msan/-race, so we don't care about those.181 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)182183 tmpdir := t.TempDir()184185 write := func(name, content string) {186 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)187 if err != nil {188 t.Fatal(err)189 }190 }191192 runGo := func(args ...string) {193 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)194 cmd.Dir = tmpdir195 out, err := cmd.CombinedOutput()196 if err != nil {197 t.Fatalf("'go %s' failed: %v, output: %s",198 strings.Join(args, " "), err, out)199 }200 }201202 // Compile a main package.203 write("main.go", "package main; func main() {}")204 importcfgfile := filepath.Join(tmpdir, "importcfg")205 testenv.WriteImportcfg(t, importcfgfile, nil, filepath.Join(tmpdir, "main.go"))206 runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")207 runGo("tool", "pack", "c", "main.a", "main.o")208209 // Add an extra section with a short, non-.o name.210 // This simulates an alternative build system.211 write(".facts", "this is not an object file")212 runGo("tool", "pack", "r", "main.a", ".facts")213214 // Verify that the linker does not attempt215 // to compile the extra section.216 cmd := linkCmd(t, "-importcfg="+importcfgfile, "main.a")217 cmd.Dir = tmpdir218 out, err := cmd.CombinedOutput()219 if err != nil {220 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {221 testenv.SkipFlaky(t, 58806)222 }223 t.Fatalf("linker failed: %v, output %s", err, out)224 }225}226227func TestUnresolved(t *testing.T) {228 // Don't build library for non-standard target in short mode.229 if testing.Short() && (runtime.GOOS != "linux" || runtime.GOARCH != "amd64") {230 t.Skipf("skipping on %s/%s in short mode", runtime.GOOS, runtime.GOARCH)231 }232233 testenv.MustHaveGoBuild(t)234235 t.Parallel()236237 tmpdir := t.TempDir()238239 write := func(name, content string) {240 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)241 if err != nil {242 t.Fatal(err)243 }244 }245246 // Test various undefined references. Because of issue #29852,247 // this used to give confusing error messages because the248 // linker would find an undefined reference to "zero" created249 // by the runtime package.250251 write("go.mod", "module testunresolved\n")252 write("main.go", `package main253254func main() {255 x()256}257258func x()259`)260 write("main.s", `261TEXT ·x(SB),0,$0262 MOVD zero<>(SB), AX263 MOVD zero(SB), AX264 MOVD ·zero(SB), AX265 RET266`)267 cmd := goCmd(t, "build")268 cmd.Dir = tmpdir269 cmd.Env = append(cmd.Env,270 "GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))271 out, err := cmd.CombinedOutput()272 if err == nil {273 t.Fatalf("expected build to fail, but it succeeded")274 }275 out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)276 got := string(out)277 want := `main.x: relocation target zero not defined278main.x: relocation target zero not defined279main.x: relocation target main.zero not defined280`281 if want != got {282 t.Fatalf("want:\n%sgot:\n%s", want, got)283 }284}285286func TestIssue33979(t *testing.T) {287 testenv.MustHaveGoBuild(t)288 testenv.MustHaveCGO(t)289 // N.B. go build below explicitly doesn't pass through290 // -asan/-msan/-race, so we don't care about those.291 testenv.MustInternalLink(t, testenv.SpecialBuildTypes{Cgo: true})292293 t.Parallel()294295 tmpdir := t.TempDir()296297 write := func(name, content string) {298 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)299 if err != nil {300 t.Fatal(err)301 }302 }303304 run := func(name string, args ...string) string {305 cmd := testenv.Command(t, name, args...)306 cmd.Dir = tmpdir307 out, err := cmd.CombinedOutput()308 if err != nil {309 t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)310 }311 return string(out)312 }313 runGo := func(args ...string) string {314 return run(testenv.GoToolPath(t), args...)315 }316317 // Test object with undefined reference that was not generated318 // by Go, resulting in an SXREF symbol being loaded during linking.319 // Because of issue #33979, the SXREF symbol would be found during320 // error reporting, resulting in confusing error messages.321322 write("main.go", `package main323func main() {324 x()325}326func x()327`)328 // The following assembly must work on all architectures.329 write("x.s", `330TEXT ·x(SB),0,$0331 CALL foo(SB)332 RET333`)334 write("x.c", `335void undefined();336337void foo() {338 undefined();339}340`)341342 cc := strings.TrimSpace(runGo("env", "CC"))343 cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))344345 importcfgfile := filepath.Join(tmpdir, "importcfg")346 testenv.WriteImportcfg(t, importcfgfile, nil, "runtime")347348 // Compile, assemble and pack the Go and C code.349 runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s")350 runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go")351 runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s")352 run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)353 runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")354355 // Now attempt to link using the internal linker.356 cmd := linkCmd(t, "-importcfg="+importcfgfile, "-linkmode=internal", "x.a")357 cmd.Dir = tmpdir358 out, err := cmd.CombinedOutput()359 if err == nil {360 t.Fatalf("expected link to fail, but it succeeded")361 }362 re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)363 if !re.Match(out) {364 t.Fatalf("got:\n%q\nwant:\n%s", out, re)365 }366}367368func TestBuildForTvOS(t *testing.T) {369 testenv.MustHaveCGO(t)370 testenv.MustHaveGoBuild(t)371372 // Only run this on darwin, where we can cross build for tvOS.373 if runtime.GOOS != "darwin" {374 t.Skip("skipping on non-darwin platform")375 }376 if testing.Short() && testenv.Builder() == "" {377 t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")378 }379 if err := testenv.Command(t, "xcrun", "--help").Run(); err != nil {380 t.Skipf("error running xcrun, required for iOS cross build: %v", err)381 }382383 t.Parallel()384385 sdkPath, err := testenv.Command(t, "xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()386 if err != nil {387 t.Skip("failed to locate appletvos SDK, skipping")388 }389 CC := []string{390 "clang",391 "-arch",392 "arm64",393 "-isysroot", strings.TrimSpace(string(sdkPath)),394 "-mtvos-version-min=12.0",395 "-fembed-bitcode",396 }397 CGO_LDFLAGS := []string{"-framework", "CoreFoundation"}398 lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")399 tmpDir := t.TempDir()400401 ar := filepath.Join(tmpDir, "lib.a")402 cmd := goCmd(t, "build", "-buildmode=c-archive", "-o", ar, lib)403 env := []string{404 "CGO_ENABLED=1",405 "GOOS=ios",406 "GOARCH=arm64",407 "CC=" + strings.Join(CC, " "),408 "CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459409 "CGO_LDFLAGS=" + strings.Join(CGO_LDFLAGS, " "),410 }411 cmd.Env = append(cmd.Env, env...)412 t.Logf("%q %v", env, cmd)413 if out, err := cmd.CombinedOutput(); err != nil {414 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)415 }416417 link := testenv.Command(t, CC[0], CC[1:]...)418 link.Args = append(link.Args, CGO_LDFLAGS...)419 link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out")) // Avoid writing to package directory.420 link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))421 t.Log(link)422 if out, err := link.CombinedOutput(); err != nil {423 t.Fatalf("%v: %v:\n%s", link.Args, err, out)424 }425}426427var testXFlagSrc = `428package main429var X = "hello"430var Z = [99999]int{99998:12345} // make it large enough to be mmaped431func main() { println(X) }432`433434func TestXFlag(t *testing.T) {435 testenv.MustHaveGoBuild(t)436437 t.Parallel()438439 tmpdir := t.TempDir()440441 src := filepath.Join(tmpdir, "main.go")442 err := os.WriteFile(src, []byte(testXFlagSrc), 0666)443 if err != nil {444 t.Fatal(err)445 }446447 cmd := goCmd(t, "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)448 if out, err := cmd.CombinedOutput(); err != nil {449 t.Errorf("%v: %v:\n%s", cmd.Args, err, out)450 }451}452453var trivialSrc = `454package main455func main() { }456`457458func TestMachOBuildVersion(t *testing.T) {459 testenv.MustHaveGoBuild(t)460 if runtime.GOOS != "darwin" {461 t.Skip("skip on non-Mach-O platforms")462 }463 t.Parallel()464465 tmpdir := t.TempDir()466467 src := filepath.Join(tmpdir, "main.go")468 err := os.WriteFile(src, []byte(trivialSrc), 0666)469 if err != nil {470 t.Fatal(err)471 }472473 exe := filepath.Join(tmpdir, "main")474 cmd := goCmd(t, "build", "-ldflags=-linkmode=internal", "-o", exe, src)475 cmd.Env = append(cmd.Env, "CGO_ENABLED=0")476 if out, err := cmd.CombinedOutput(); err != nil {477 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)478 }479 exef, err := os.Open(exe)480 if err != nil {481 t.Fatal(err)482 }483 defer exef.Close()484 exem, err := macho.NewFile(exef)485 if err != nil {486 t.Fatal(err)487 }488 found := false489 checkMin := func(ver uint32) {490 major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff491 if major < 13 {492 t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 13.0.0", major, minor, patch)493 }494 }495 for _, cmd := range exem.Loads {496 raw := cmd.Raw()497 type_ := exem.ByteOrder.Uint32(raw)498 if type_ != imacho.LC_BUILD_VERSION {499 continue500 }501 osVer := exem.ByteOrder.Uint32(raw[12:])502 checkMin(osVer)503 sdkVer := exem.ByteOrder.Uint32(raw[16:])504 checkMin(sdkVer)505 found = true506 break507 }508 if !found {509 t.Errorf("no LC_BUILD_VERSION load command found")510 }511}512513func TestMachOUUID(t *testing.T) {514 testenv.MustHaveGoBuild(t)515 if runtime.GOOS != "darwin" {516 t.Skip("this is only for darwin")517 }518519 t.Parallel()520521 tmpdir := t.TempDir()522523 src := filepath.Join(tmpdir, "main.go")524 err := os.WriteFile(src, []byte(trivialSrc), 0666)525 if err != nil {526 t.Fatal(err)527 }528529 extractUUID := func(exe string) string {530 exem, err := macho.Open(exe)531 if err != nil {532 t.Fatal(err)533 }534 defer exem.Close()535 for _, cmd := range exem.Loads {536 raw := cmd.Raw()537 type_ := exem.ByteOrder.Uint32(raw)538 if type_ != imacho.LC_UUID {539 continue540 }541 return string(raw[8:24])542 }543 return ""544 }545546 tests := []struct{ name, ldflags, expect string }{547 {"default", "", "gobuildid"},548 {"gobuildid", "-B=gobuildid", "gobuildid"},549 {"specific", "-B=0x0123456789ABCDEF0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45\x67\x89\xAB\xCD\xEF"},550 {"none", "-B=none", ""},551 }552 if testenv.HasCGO() {553 for _, test := range tests {554 t1 := test555 t1.name += "_external"556 t1.ldflags += " -linkmode=external"557 tests = append(tests, t1)558 }559 }560 for _, test := range tests {561 t.Run(test.name, func(t *testing.T) {562 exe := filepath.Join(tmpdir, test.name)563 cmd := goCmd(t, "build", "-ldflags="+test.ldflags, "-o", exe, src)564 if out, err := cmd.CombinedOutput(); err != nil {565 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)566 }567 uuid := extractUUID(exe)568 if test.expect == "gobuildid" {569 // Go buildid is not known in source code. Check UUID is present,570 // and satisfies UUIDv3.571 if uuid == "" {572 t.Fatal("expect nonempty UUID, got empty")573 }574 // The version number is the high 4 bits of byte 6.575 if uuid[6]>>4 != 3 {576 t.Errorf("expect v3 UUID, got %X (version %d)", uuid, uuid[6]>>4)577 }578 } else if uuid != test.expect {579 t.Errorf("UUID mismatch: got %X, want %X", uuid, test.expect)580 }581 })582 }583}584585const Issue34788src = `586587package blah588589func Blah(i int) int {590 a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}591 return a[i&7]592}593`594595func TestIssue34788Android386TLSSequence(t *testing.T) {596 testenv.MustHaveGoBuild(t)597598 // This is a cross-compilation test, so it doesn't make599 // sense to run it on every GOOS/GOARCH combination. Limit600 // the test to amd64 + darwin/linux.601 if runtime.GOARCH != "amd64" ||602 (runtime.GOOS != "darwin" && runtime.GOOS != "linux") {603 t.Skip("skipping on non-{linux,darwin}/amd64 platform")604 }605606 t.Parallel()607608 tmpdir := t.TempDir()609610 src := filepath.Join(tmpdir, "blah.go")611 err := os.WriteFile(src, []byte(Issue34788src), 0666)612 if err != nil {613 t.Fatal(err)614 }615616 obj := filepath.Join(tmpdir, "blah.o")617 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src)618 cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")619 if out, err := cmd.CombinedOutput(); err != nil {620 t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)621 }622623 // Run objdump on the resulting object.624 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "objdump", obj)625 out, oerr := cmd.CombinedOutput()626 if oerr != nil {627 t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)628 }629630 // Sift through the output; we should not be seeing any R_TLS_LE relocs.631 scanner := bufio.NewScanner(bytes.NewReader(out))632 for scanner.Scan() {633 line := scanner.Text()634 if strings.Contains(line, "R_TLS_LE") {635 t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)636 }637 }638}639640const testStrictDupGoSrc = `641package main642func f()643func main() { f() }644`645646const testStrictDupAsmSrc1 = `647#include "textflag.h"648TEXT ·f(SB), NOSPLIT|DUPOK, $0-0649 RET650`651652const testStrictDupAsmSrc2 = `653#include "textflag.h"654TEXT ·f(SB), NOSPLIT|DUPOK, $0-0655 JMP 0(PC)656`657658const testStrictDupAsmSrc3 = `659#include "textflag.h"660GLOBL ·rcon(SB), RODATA|DUPOK, $64661`662663const testStrictDupAsmSrc4 = `664#include "textflag.h"665GLOBL ·rcon(SB), RODATA|DUPOK, $32666`667668func TestStrictDup(t *testing.T) {669 // Check that -strictdups flag works.670 testenv.MustHaveGoBuild(t)671672 asmfiles := []struct {673 fname string674 payload string675 }{676 {"a", testStrictDupAsmSrc1},677 {"b", testStrictDupAsmSrc2},678 {"c", testStrictDupAsmSrc3},679 {"d", testStrictDupAsmSrc4},680 }681682 t.Parallel()683684 tmpdir := t.TempDir()685686 src := filepath.Join(tmpdir, "x.go")687 err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666)688 if err != nil {689 t.Fatal(err)690 }691 for _, af := range asmfiles {692 src = filepath.Join(tmpdir, af.fname+".s")693 err = os.WriteFile(src, []byte(af.payload), 0666)694 if err != nil {695 t.Fatal(err)696 }697 }698 src = filepath.Join(tmpdir, "go.mod")699 err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666)700 if err != nil {701 t.Fatal(err)702 }703704 cmd := goCmd(t, "build", "-ldflags=-strictdups=1")705 cmd.Dir = tmpdir706 out, err := cmd.CombinedOutput()707 if err != nil {708 t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))709 }710 if !bytes.Contains(out, []byte("mismatched payload")) {711 t.Errorf("unexpected output:\n%s", out)712 }713714 cmd = goCmd(t, "build", "-ldflags=-strictdups=2")715 cmd.Dir = tmpdir716 out, err = cmd.CombinedOutput()717 if err == nil {718 t.Errorf("linking with -strictdups=2 did not fail")719 }720 // NB: on amd64 we get the 'new length' error, on arm64 the 'different721 // contents' error.722 if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||723 bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||724 !bytes.Contains(out, []byte("mismatched payload: different sizes")) {725 t.Errorf("unexpected output:\n%s", out)726 }727}728729const testFuncAlignSrc = `730package main731import (732 "fmt"733)734func alignPc()735var alignPcFnAddr uintptr736737func main() {738 if alignPcFnAddr % 512 != 0 {739 fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)740 } else {741 fmt.Printf("PASS")742 }743}744`745746var testFuncAlignAsmSources = map[string]string{747 "arm64": `748#include "textflag.h"749750TEXT ·alignPc(SB),NOSPLIT, $0-0751 MOVD $2, R0752 PCALIGN $512753 MOVD $3, R1754 RET755756GLOBL ·alignPcFnAddr(SB),RODATA,$8757DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)758`,759 "loong64": `760#include "textflag.h"761762TEXT ·alignPc(SB),NOSPLIT, $0-0763 MOVV $2, R4764 PCALIGN $512765 MOVV $3, R5766 RET767768GLOBL ·alignPcFnAddr(SB),RODATA,$8769DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)770`,771}772773// TestFuncAlign verifies that the address of a function can be aligned774// with a specific value on arm64 and loong64.775func TestFuncAlign(t *testing.T) {776 testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH]777 if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" {778 t.Skip("skipping on non-linux/{arm64,loong64} platform")779 }780 testenv.MustHaveGoBuild(t)781782 t.Parallel()783784 tmpdir := t.TempDir()785786 src := filepath.Join(tmpdir, "go.mod")787 err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)788 if err != nil {789 t.Fatal(err)790 }791 src = filepath.Join(tmpdir, "falign.go")792 err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666)793 if err != nil {794 t.Fatal(err)795 }796 src = filepath.Join(tmpdir, "falign.s")797 err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)798 if err != nil {799 t.Fatal(err)800 }801802 cmd := goCmd(t, "build", "-o", "falign")803 cmd.Dir = tmpdir804 out, err := cmd.CombinedOutput()805 if err != nil {806 t.Errorf("build failed: %v", err)807 }808 cmd = testenv.Command(t, tmpdir+"/falign")809 out, err = cmd.CombinedOutput()810 if err != nil {811 t.Errorf("failed to run with err %v, output: %s", err, out)812 }813 if string(out) != "PASS" {814 t.Errorf("unexpected output: %s\n", out)815 }816}817818const testFuncAlignOptionSrc = `819package main820//go:noinline821func foo() {822}823//go:noinline824func bar() {825}826//go:noinline827func baz() {828}829func main() {830 foo()831 bar()832 baz()833}834`835836// TestFuncAlignOption verifies that the -funcalign option changes the function alignment837func TestFuncAlignOption(t *testing.T) {838 testenv.MustHaveGoBuild(t)839840 t.Parallel()841842 tmpdir := t.TempDir()843844 src := filepath.Join(tmpdir, "falign.go")845 err := os.WriteFile(src, []byte(testFuncAlignOptionSrc), 0666)846 if err != nil {847 t.Fatal(err)848 }849850 alignTest := func(align uint64) {851 exeName := "falign.exe"852 cmd := goCmd(t, "build", "-ldflags=-funcalign="+strconv.FormatUint(align, 10), "-o", exeName, "falign.go")853 cmd.Dir = tmpdir854 out, err := cmd.CombinedOutput()855 if err != nil {856 t.Errorf("build failed: %v \n%s", err, out)857 }858 exe := filepath.Join(tmpdir, exeName)859 cmd = testenv.Command(t, exe)860 out, err = cmd.CombinedOutput()861 if err != nil {862 t.Errorf("failed to run with err %v, output: %s", err, out)863 }864865 // Check function alignment866 f, err := objfile.Open(exe)867 if err != nil {868 t.Fatalf("failed to open file:%v\n", err)869 }870 defer f.Close()871872 fname := map[string]bool{"_main.foo": false,873 "_main.bar": false,874 "_main.baz": false}875 syms, err := f.Symbols()876 if err != nil {877 t.Errorf("failed to get symbols with err %v", err)878 }879 for _, s := range syms {880 fn := s.Name881 if _, ok := fname[fn]; !ok {882 fn = "_" + s.Name883 if _, ok := fname[fn]; !ok {884 continue885 }886 }887 if s.Addr%align != 0 {888 t.Fatalf("unaligned function: %s %x. Expected alignment: %d\n", fn, s.Addr, align)889 }890 fname[fn] = true891 }892 for k, v := range fname {893 if !v {894 t.Fatalf("function %s not found\n", k)895 }896 }897 }898 alignTest(16)899 alignTest(32)900}901902const testTrampSrc = `903package main904import "fmt"905func main() {906 fmt.Println("hello")907908 defer func(){909 if e := recover(); e == nil {910 panic("did not panic")911 }912 }()913 f1()914}915916// Test deferreturn trampolines. See issue #39049.917func f1() { defer f2() }918func f2() { panic("XXX") }919`920921func TestTrampoline(t *testing.T) {922 // Test that trampoline insertion works as expected.923 // For stress test, we set -debugtramp=2 flag, which sets a very low924 // threshold for trampoline generation, and essentially all cross-package925 // calls will use trampolines.926 buildmodes := []string{"default"}927 switch runtime.GOARCH {928 case "arm", "arm64", "loong64":929 case "ppc64le", "ppc64":930 switch runtime.GOOS {931 case "aix":932 case "linux":933 // Trampolines are generated differently when internal linking PIE, test them too.934 buildmodes = append(buildmodes, "pie")935 default:936 t.Skipf("trampoline insertion is not implemented on %s-%s", runtime.GOARCH, runtime.GOOS)937 }938 default:939 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)940 }941942 testenv.MustHaveGoBuild(t)943944 t.Parallel()945946 tmpdir := t.TempDir()947948 src := filepath.Join(tmpdir, "hello.go")949 err := os.WriteFile(src, []byte(testTrampSrc), 0666)950 if err != nil {951 t.Fatal(err)952 }953 exe := filepath.Join(tmpdir, "hello.exe")954955 for _, mode := range buildmodes {956 cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)957 out, err := cmd.CombinedOutput()958 if err != nil {959 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)960 }961 cmd = testenv.Command(t, exe)962 out, err = cmd.CombinedOutput()963 if err != nil {964 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)965 }966 if string(out) != "hello\n" {967 t.Errorf("unexpected output (%s):\n%s", mode, out)968 }969970 out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()971 if err != nil {972 t.Errorf("nm failure: %s\n%s\n", err, string(out))973 }974 if ok, _ := regexp.Match("T runtime.deferreturn(\\+0)?-tramp0", out); !ok {975 t.Errorf("Trampoline T runtime.deferreturn(+0)?-tramp0 is missing")976 }977 }978}979980const testTrampCgoSrc = `981package main982983// #include <stdio.h>984// void CHello() { printf("hello\n"); fflush(stdout); }985import "C"986987func main() {988 C.CHello()989}990`991992func TestTrampolineCgo(t *testing.T) {993 // Test that trampoline insertion works for cgo code.994 // For stress test, we set -debugtramp=2 flag, which sets a very low995 // threshold for trampoline generation, and essentially all cross-package996 // calls will use trampolines.997 buildmodes := []string{"default"}998 switch runtime.GOARCH {999 case "arm", "arm64", "loong64":1000 case "ppc64le", "ppc64":1001 // Trampolines are generated differently when internal linking PIE, test them too.1002 buildmodes = append(buildmodes, "pie")1003 default:1004 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)1005 }10061007 testenv.MustHaveGoBuild(t)1008 testenv.MustHaveCGO(t)10091010 t.Parallel()10111012 tmpdir := t.TempDir()10131014 src := filepath.Join(tmpdir, "hello.go")1015 err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)1016 if err != nil {1017 t.Fatal(err)1018 }1019 exe := filepath.Join(tmpdir, "hello.exe")10201021 for _, mode := range buildmodes {1022 cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)1023 out, err := cmd.CombinedOutput()1024 if err != nil {1025 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)1026 }1027 cmd = testenv.Command(t, exe)1028 out, err = cmd.CombinedOutput()1029 if err != nil {1030 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)1031 }1032 if string(out) != "hello\n" && string(out) != "hello\r\n" {1033 t.Errorf("unexpected output (%s):\n%s", mode, out)1034 }10351036 // Test internal linking mode.10371038 if !testenv.CanInternalLink(true) {1039 continue1040 }1041 cmd = goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)1042 out, err = cmd.CombinedOutput()1043 if err != nil {1044 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)1045 }1046 cmd = testenv.Command(t, exe)1047 out, err = cmd.CombinedOutput()1048 if err != nil {1049 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)1050 }1051 if string(out) != "hello\n" && string(out) != "hello\r\n" {1052 t.Errorf("unexpected output (%s):\n%s", mode, out)1053 }1054 }1055}10561057func TestIndexMismatch(t *testing.T) {1058 // Test that index mismatch will cause a link-time error (not run-time error).1059 // This shouldn't happen with "go build". We invoke the compiler and the linker1060 // manually, and try to "trick" the linker with an inconsistent object file.1061 testenv.MustHaveGoBuild(t)1062 // N.B. the build below explicitly doesn't pass through1063 // -asan/-msan/-race, so we don't care about those.1064 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)10651066 t.Parallel()10671068 tmpdir := t.TempDir()10691070 aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")1071 bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")1072 mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")1073 aObj := filepath.Join(tmpdir, "a.o")1074 mObj := filepath.Join(tmpdir, "main.o")1075 exe := filepath.Join(tmpdir, "main.exe")10761077 importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")1078 testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")1079 importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")1080 testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")10811082 // Build a program with main package importing package a.1083 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)1084 t.Log(cmd)1085 out, err := cmd.CombinedOutput()1086 if err != nil {1087 t.Fatalf("compiling a.go failed: %v\n%s", err, out)1088 }1089 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-o", mObj, mSrc)1090 t.Log(cmd)1091 out, err = cmd.CombinedOutput()1092 if err != nil {1093 t.Fatalf("compiling main.go failed: %v\n%s", err, out)1094 }1095 cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)1096 t.Log(cmd)1097 out, err = cmd.CombinedOutput()1098 if err != nil {1099 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {1100 testenv.SkipFlaky(t, 58806)1101 }1102 t.Errorf("linking failed: %v\n%s", err, out)1103 }11041105 // Now, overwrite a.o with the object of b.go. This should1106 // result in an index mismatch.1107 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)1108 t.Log(cmd)1109 out, err = cmd.CombinedOutput()1110 if err != nil {1111 t.Fatalf("compiling a.go failed: %v\n%s", err, out)1112 }1113 cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)1114 t.Log(cmd)1115 out, err = cmd.CombinedOutput()1116 if err == nil {1117 t.Fatalf("linking didn't fail")1118 }1119 if !bytes.Contains(out, []byte("fingerprint mismatch")) {1120 t.Errorf("did not see expected error message. out:\n%s", out)1121 }1122}11231124func TestPErsrcBinutils(t *testing.T) {1125 // Test that PE rsrc section is handled correctly (issue 39658).1126 testenv.MustHaveGoBuild(t)11271128 if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {1129 // This test is limited to amd64 and 386, because binutils is limited as such1130 t.Skipf("this is only for windows/amd64 and windows/386")1131 }11321133 t.Parallel()11341135 tmpdir := t.TempDir()11361137 pkgdir := filepath.Join("testdata", "pe-binutils")1138 exe := filepath.Join(tmpdir, "a.exe")1139 cmd := goCmd(t, "build", "-o", exe)1140 cmd.Dir = pkgdir1141 // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment1142 out, err := cmd.CombinedOutput()1143 if err != nil {1144 t.Fatalf("building failed: %v, output:\n%s", err, out)1145 }11461147 // Check that the binary contains the rsrc data1148 b, err := os.ReadFile(exe)1149 if err != nil {1150 t.Fatalf("reading output failed: %v", err)1151 }1152 if !bytes.Contains(b, []byte("Hello Gophers!")) {1153 t.Fatalf("binary does not contain expected content")1154 }1155}11561157func TestPErsrcLLVM(t *testing.T) {1158 // Test that PE rsrc section is handled correctly (issue 39658).1159 testenv.MustHaveGoBuild(t)11601161 if runtime.GOOS != "windows" {1162 t.Skipf("this is a windows-only test")1163 }11641165 t.Parallel()11661167 tmpdir := t.TempDir()11681169 pkgdir := filepath.Join("testdata", "pe-llvm")1170 exe := filepath.Join(tmpdir, "a.exe")1171 cmd := goCmd(t, "build", "-o", exe)1172 cmd.Dir = pkgdir1173 // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment1174 out, err := cmd.CombinedOutput()1175 if err != nil {1176 t.Fatalf("building failed: %v, output:\n%s", err, out)1177 }11781179 // Check that the binary contains the rsrc data1180 b, err := os.ReadFile(exe)1181 if err != nil {1182 t.Fatalf("reading output failed: %v", err)1183 }1184 if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {1185 t.Fatalf("binary does not contain expected content")1186 }1187}11881189func TestContentAddressableSymbols(t *testing.T) {1190 // Test that the linker handles content-addressable symbols correctly.1191 testenv.MustHaveGoBuild(t)11921193 t.Parallel()11941195 src := filepath.Join("testdata", "testHashedSyms", "p.go")1196 cmd := goCmd(t, "run", src)1197 out, err := cmd.CombinedOutput()1198 if err != nil {1199 t.Errorf("command %s failed: %v\n%s", cmd, err, out)1200 }1201}12021203func TestReadOnly(t *testing.T) {1204 // Test that read-only data is indeed read-only.1205 testenv.MustHaveGoBuild(t)12061207 t.Parallel()12081209 src := filepath.Join("testdata", "testRO", "x.go")1210 cmd := goCmd(t, "run", src)1211 out, err := cmd.CombinedOutput()1212 if err == nil {1213 t.Errorf("running test program did not fail. output:\n%s", out)1214 }1215}12161217const testIssue38554Src = `1218package main12191220type T [10<<20]byte12211222//go:noinline1223func f() T {1224 return T{} // compiler will make a large stmp symbol, but not used.1225}12261227func main() {1228 x := f()1229 println(x[1])1230}1231`12321233func TestIssue38554(t *testing.T) {1234 testenv.MustHaveGoBuild(t)12351236 t.Parallel()12371238 tmpdir := t.TempDir()12391240 src := filepath.Join(tmpdir, "x.go")1241 err := os.WriteFile(src, []byte(testIssue38554Src), 0666)1242 if err != nil {1243 t.Fatalf("failed to write source file: %v", err)1244 }1245 exe := filepath.Join(tmpdir, "x.exe")1246 cmd := goCmd(t, "build", "-o", exe, src)1247 out, err := cmd.CombinedOutput()1248 if err != nil {1249 t.Fatalf("build failed: %v\n%s", err, out)1250 }12511252 fi, err := os.Stat(exe)1253 if err != nil {1254 t.Fatalf("failed to stat output file: %v", err)1255 }12561257 // The test program is not much different from a helloworld, which is1258 // typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,1259 // it will be over 10 MB.1260 const want = 5 << 201261 if got := fi.Size(); got > want {1262 t.Errorf("binary too big: got %d, want < %d", got, want)1263 }1264}12651266const testIssue42396src = `1267package main12681269//go:noinline1270//go:nosplit1271func callee(x int) {1272}12731274func main() {1275 callee(9)1276}1277`12781279func TestIssue42396(t *testing.T) {1280 testenv.MustHaveGoBuild(t)12811282 if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {1283 t.Skip("no race detector support")1284 }12851286 t.Parallel()12871288 tmpdir := t.TempDir()12891290 src := filepath.Join(tmpdir, "main.go")1291 err := os.WriteFile(src, []byte(testIssue42396src), 0666)1292 if err != nil {1293 t.Fatalf("failed to write source file: %v", err)1294 }1295 exe := filepath.Join(tmpdir, "main.exe")1296 cmd := goCmd(t, "build", "-gcflags=-race", "-o", exe, src)1297 out, err := cmd.CombinedOutput()1298 if err == nil {1299 t.Fatalf("build unexpectedly succeeded")1300 }13011302 // Check to make sure that we see a reasonable error message1303 // and not a panic.1304 if strings.Contains(string(out), "panic:") {1305 t.Fatalf("build should not fail with panic:\n%s", out)1306 }1307 const want = "reference to undefined builtin"1308 if !strings.Contains(string(out), want) {1309 t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)1310 }1311}13121313const testLargeRelocSrc = `1314package main13151316var x = [1<<25]byte{1<<23: 23, 1<<24: 24}13171318var addr = [...]*byte{1319 &x[1<<23-1],1320 &x[1<<23],1321 &x[1<<23+1],1322 &x[1<<24-1],1323 &x[1<<24],1324 &x[1<<24+1],1325}13261327func main() {1328 // check relocations in instructions1329 check(x[1<<23-1], 0)1330 check(x[1<<23], 23)1331 check(x[1<<23+1], 0)1332 check(x[1<<24-1], 0)1333 check(x[1<<24], 24)1334 check(x[1<<24+1], 0)13351336 // check absolute address relocations in data1337 check(*addr[0], 0)1338 check(*addr[1], 23)1339 check(*addr[2], 0)1340 check(*addr[3], 0)1341 check(*addr[4], 24)1342 check(*addr[5], 0)1343}13441345func check(x, y byte) {1346 if x != y {1347 panic("FAIL")1348 }1349}1350`13511352func TestLargeReloc(t *testing.T) {1353 // Test that large relocation addend is handled correctly.1354 // In particular, on darwin/arm64 when external linking,1355 // Mach-O relocation has only 24-bit addend. See issue #42738.1356 testenv.MustHaveGoBuild(t)1357 t.Parallel()13581359 tmpdir := t.TempDir()13601361 src := filepath.Join(tmpdir, "x.go")1362 err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)1363 if err != nil {1364 t.Fatalf("failed to write source file: %v", err)1365 }1366 cmd := goCmd(t, "run", src)1367 out, err := cmd.CombinedOutput()1368 if err != nil {1369 t.Errorf("build failed: %v. output:\n%s", err, out)1370 }13711372 if testenv.HasCGO() { // currently all targets that support cgo can external link1373 cmd = goCmd(t, "run", "-ldflags=-linkmode=external", src)1374 out, err = cmd.CombinedOutput()1375 if err != nil {1376 t.Fatalf("build failed: %v. output:\n%s", err, out)1377 }1378 }1379}13801381func TestUnlinkableObj(t *testing.T) {1382 // Test that the linker emits an error with unlinkable object.1383 testenv.MustHaveGoBuild(t)1384 t.Parallel()13851386 if true /* was buildcfg.Experiment.Unified */ {1387 t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")1388 }13891390 tmpdir := t.TempDir()13911392 xSrc := filepath.Join(tmpdir, "x.go")1393 pSrc := filepath.Join(tmpdir, "p.go")1394 xObj := filepath.Join(tmpdir, "x.o")1395 pObj := filepath.Join(tmpdir, "p.o")1396 exe := filepath.Join(tmpdir, "x.exe")1397 importcfgfile := filepath.Join(tmpdir, "importcfg")1398 testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})1399 err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)1400 if err != nil {1401 t.Fatalf("failed to write source file: %v", err)1402 }1403 err = os.WriteFile(pSrc, []byte("package p\n"), 0666)1404 if err != nil {1405 t.Fatalf("failed to write source file: %v", err)1406 }1407 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc) // without -p1408 out, err := cmd.CombinedOutput()1409 if err != nil {1410 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)1411 }1412 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)1413 out, err = cmd.CombinedOutput()1414 if err != nil {1415 t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)1416 }1417 cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)1418 out, err = cmd.CombinedOutput()1419 if err == nil {1420 t.Fatalf("link did not fail")1421 }1422 if !bytes.Contains(out, []byte("unlinkable object")) {1423 t.Errorf("did not see expected error message. out:\n%s", out)1424 }14251426 // It is okay to omit -p for (only) main package.1427 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)1428 out, err = cmd.CombinedOutput()1429 if err != nil {1430 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)1431 }1432 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc) // without -p1433 out, err = cmd.CombinedOutput()1434 if err != nil {1435 t.Fatalf("compile failed: %v. output:\n%s", err, out)1436 }14371438 cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)1439 out, err = cmd.CombinedOutput()1440 if err != nil {1441 t.Errorf("link failed: %v. output:\n%s", err, out)1442 }1443}14441445func TestExtLinkCmdlineDeterminism(t *testing.T) {1446 // Test that we pass flags in deterministic order to the external linker1447 testenv.MustHaveGoBuild(t)1448 testenv.MustHaveCGO(t) // this test requires -linkmode=external1449 t.Parallel()14501451 // test source code, with some cgo exports1452 testSrc := `1453package main1454import "C"1455//export F11456func F1() {}1457//export F21458func F2() {}1459//export F31460func F3() {}1461func main() {}1462`14631464 tmpdir := t.TempDir()1465 src := filepath.Join(tmpdir, "x.go")1466 if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {1467 t.Fatal(err)1468 }1469 exe := filepath.Join(tmpdir, "x.exe")14701471 // Use a deterministic tmp directory so the temporary file paths are1472 // deterministic.1473 linktmp := filepath.Join(tmpdir, "linktmp")1474 if err := os.Mkdir(linktmp, 0777); err != nil {1475 t.Fatal(err)1476 }14771478 // Link with -v -linkmode=external to see the flags we pass to the1479 // external linker.1480 ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp1481 var out0, fullOut0 []byte1482 for i := 0; i < 5; i++ {1483 cmd := goCmd(t, "build", ldflags, "-x", "-o", exe, src)1484 fullOut, err := cmd.CombinedOutput()1485 out := fullOut1486 if err != nil {1487 t.Fatalf("build failed: %v, output:\n%s", err, out)1488 }1489 if err := os.Remove(exe); err != nil {1490 t.Fatal(err)1491 }14921493 // extract the "host link" invocation1494 j := bytes.Index(out, []byte("\nhost link:"))1495 if j == -1 {1496 t.Fatalf("host link step not found, output:\n%s", out)1497 }1498 out = out[j+1:]1499 k := bytes.Index(out, []byte("\n"))1500 if k == -1 {1501 t.Fatalf("no newline after host link, output:\n%s", out)1502 }1503 out = out[:k]15041505 // filter out output file name, which is passed by the go1506 // command and is nondeterministic.1507 fs := bytes.Fields(out)1508 for i, f := range fs {1509 if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {1510 fs[i+1] = []byte("a.out")1511 break1512 }1513 }1514 out = bytes.Join(fs, []byte{' '})15151516 if i == 0 {1517 out0 = out1518 fullOut0 = fullOut1519 continue1520 }1521 if !bytes.Equal(out0, out) {1522 t.Fatalf("output differ:\n%s\n==========\n%s\n\nfull output:\n%s\n==========\n%s",1523 out0, out, fullOut0, fullOut)1524 }1525 }1526}15271528// TestResponseFile tests that creating a response file to pass to the1529// external linker works correctly.1530func TestResponseFile(t *testing.T) {1531 t.Parallel()15321533 testenv.MustHaveGoBuild(t)15341535 // This test requires -linkmode=external. Currently all1536 // systems that support cgo support -linkmode=external.1537 testenv.MustHaveCGO(t)15381539 tmpdir := t.TempDir()15401541 src := filepath.Join(tmpdir, "x.go")1542 if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {1543 t.Fatal(err)1544 }15451546 // We don't use goCmd here, as -toolexec doesn't use response files.1547 // This test is more for the go command than the linker anyhow.15481549 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")1550 cmd.Dir = tmpdir15511552 // Add enough arguments to push cmd/link into creating a response file.1553 var sb strings.Builder1554 sb.WriteString(`'-ldflags=all="-extldflags=`)1555 for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {1556 if i > 0 {1557 sb.WriteString(" ")1558 }1559 sb.WriteString("-g")1560 }1561 sb.WriteString(`"'`)1562 cmd = testenv.CleanCmdEnv(cmd)1563 cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())15641565 out, err := cmd.CombinedOutput()1566 if len(out) > 0 {1567 t.Logf("%s", out)1568 }1569 if err != nil {1570 t.Error(err)1571 }1572}15731574func TestDynimportVar(t *testing.T) {1575 // Test that we can access dynamically imported variables.1576 // Currently darwin only.1577 if runtime.GOOS != "darwin" {1578 t.Skip("skip on non-darwin platform")1579 }15801581 testenv.MustHaveGoBuild(t)1582 testenv.MustHaveCGO(t)15831584 t.Parallel()15851586 tmpdir := t.TempDir()1587 exe := filepath.Join(tmpdir, "a.exe")1588 src := filepath.Join("testdata", "dynimportvar", "main.go")15891590 for _, mode := range []string{"internal", "external"} {1591 cmd := goCmd(t, "build", "-ldflags=-linkmode="+mode, "-o", exe, src)1592 out, err := cmd.CombinedOutput()1593 if err != nil {1594 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)1595 }1596 cmd = testenv.Command(t, exe)1597 out, err = cmd.CombinedOutput()1598 if err != nil {1599 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)1600 }1601 }1602}16031604const helloSrc = `1605package main1606var X = 421607var Y int1608func main() { println("hello", X, Y) }1609`16101611func TestFlagS(t *testing.T) {1612 // Test that the -s flag strips the symbol table.1613 testenv.MustHaveGoBuild(t)16141615 t.Parallel()16161617 tmpdir := t.TempDir()1618 exe := filepath.Join(tmpdir, "a.exe")1619 src := filepath.Join(tmpdir, "a.go")1620 err := os.WriteFile(src, []byte(helloSrc), 0666)1621 if err != nil {1622 t.Fatal(err)1623 }16241625 modes := []string{"auto"}1626 if testenv.HasCGO() {1627 modes = append(modes, "external")1628 }16291630 // check a text symbol, a data symbol, and a BSS symbol1631 syms := []string{"main.main", "main.X", "main.Y"}16321633 for _, mode := range modes {1634 cmd := goCmd(t, "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)1635 out, err := cmd.CombinedOutput()1636 if err != nil {1637 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)1638 }1639 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)1640 out, err = cmd.CombinedOutput()1641 if err != nil {1642 if _, ok := errors.AsType[*exec.ExitError](err); !ok {1643 // Error exit is fine as it may have no symbols.1644 // On darwin we need to emit dynamic symbol references so it1645 // actually has some symbols, and nm succeeds.1646 t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)1647 }1648 }1649 for _, s := range syms {1650 if bytes.Contains(out, []byte(s)) {1651 t.Errorf("(mode=%s): unexpected symbol %s", mode, s)1652 }1653 }1654 }1655}16561657func TestRandLayout(t *testing.T) {1658 // Test that the -randlayout flag randomizes function order and1659 // generates a working binary.1660 testenv.MustHaveGoBuild(t)16611662 t.Parallel()16631664 tmpdir := t.TempDir()16651666 src := filepath.Join(tmpdir, "hello.go")1667 err := os.WriteFile(src, []byte(trivialSrc), 0666)1668 if err != nil {1669 t.Fatal(err)1670 }16711672 var syms [2]string1673 for i, seed := range []string{"123", "456"} {1674 exe := filepath.Join(tmpdir, "hello"+seed+".exe")1675 cmd := goCmd(t, "build", "-ldflags=-randlayout="+seed, "-o", exe, src)1676 out, err := cmd.CombinedOutput()1677 if err != nil {1678 t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)1679 }1680 cmd = testenv.Command(t, exe)1681 err = cmd.Run()1682 if err != nil {1683 t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)1684 }1685 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)1686 out, err = cmd.CombinedOutput()1687 if err != nil {1688 t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)1689 }1690 syms[i] = string(out)1691 }1692 if syms[0] == syms[1] {1693 t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])1694 }1695}16961697func TestCheckLinkname(t *testing.T) {1698 // Test that code containing blocked linknames does not build.1699 testenv.MustHaveGoBuild(t)1700 t.Parallel()17011702 tmpdir := t.TempDir()17031704 tests := []struct {1705 src string1706 ok bool1707 }{1708 // use (instantiation) of public API is ok1709 {"ok.go", true},1710 // push linkname is ok1711 {"push.go", true},1712 // using a linknamed variable to reference an assembly1713 // function in the same package is ok1714 {"textvar", true},1715 // pull linkname of blocked symbol is not ok1716 {"coro.go", false},1717 {"coro_var.go", false},1718 // assembly reference is not ok1719 {"coro_asm", false},1720 // pull-only linkname is not ok1721 {"coro2.go", false},1722 // pull linkname of a builtin symbol is not ok1723 {"builtin.go", false},1724 // using a linkname to reference a runtime assembly1725 // function is not ok (except on non-regabi platforms)1726 {"systemstack.go", !buildcfg.Experiment.RegabiWrappers},1727 // misc1728 {"addmoduledata.go", false},1729 {"freegc.go", false},1730 // legacy bad linkname is ok, for now1731 {"fastrand.go", true},1732 {"badlinkname.go", true},1733 }1734 for _, test := range tests {1735 t.Run(test.src, func(t *testing.T) {1736 t.Parallel()1737 src := "./testdata/linkname/" + test.src1738 exe := filepath.Join(tmpdir, test.src+".exe")1739 cmd := goCmd(t, "build", "-o", exe, src)1740 out, err := cmd.CombinedOutput()1741 if test.ok && err != nil {1742 t.Errorf("build failed unexpectedly: %v:\n%s", err, out)1743 }1744 if !test.ok && err == nil {1745 t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out)1746 }1747 })1748 }1749}17501751func TestLinknameBSS(t *testing.T) {1752 // Test that the linker chooses the right one as the definition1753 // for linknamed variables. See issue #72032.1754 testenv.MustHaveGoBuild(t)1755 t.Parallel()17561757 tmpdir := t.TempDir()17581759 src := filepath.Join("testdata", "linkname", "sched.go")1760 exe := filepath.Join(tmpdir, "sched.exe")1761 cmd := goCmd(t, "build", "-o", exe, src)1762 out, err := cmd.CombinedOutput()1763 if err != nil {1764 t.Fatalf("build failed unexpectedly: %v:\n%s", err, out)1765 }17661767 // Check the symbol size.1768 f, err := objfile.Open(exe)1769 if err != nil {1770 t.Fatalf("fail to open executable: %v", err)1771 }1772 defer f.Close()1773 syms, err := f.Symbols()1774 if err != nil {1775 t.Fatalf("fail to get symbols: %v", err)1776 }1777 found := false1778 for _, s := range syms {1779 if s.Name == "runtime.sched" || s.Name == "_runtime.sched" {1780 found = true1781 if s.Size < 100 {1782 // As of Go 1.25 (Mar 2025), runtime.sched has 6848 bytes on1783 // darwin/arm64. It should always be larger than 100 bytes on1784 // all platforms.1785 t.Errorf("runtime.sched symbol size too small: want > 100, got %d", s.Size)1786 }1787 }1788 }1789 if !found {1790 t.Errorf("runtime.sched symbol not found")1791 }17921793 // Executable should run.1794 cmd = testenv.Command(t, exe)1795 out, err = cmd.CombinedOutput()1796 if err != nil {1797 t.Errorf("executable failed to run: %v\n%s", err, out)1798 }1799}18001801// setValueFromBytes copies from a []byte to a variable.1802// This is used to get correctly aligned values in TestFuncdataPlacement.1803func setValueFromBytes[T any](p *T, s []byte) {1804 copy(unsafe.Slice((*byte)(unsafe.Pointer(p)), unsafe.Sizeof(*p)), s)1805}18061807// Test that all funcdata values are stored in the .gopclntab section.1808// This is pretty ugly as there is no API for accessing this data.1809// This test will have to be updated when the data formats change.1810func TestFuncdataPlacement(t *testing.T) {1811 testenv.MustHaveGoBuild(t)1812 t.Parallel()18131814 tmpdir := t.TempDir()1815 src := filepath.Join(tmpdir, "x.go")1816 if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {1817 t.Fatal(err)1818 }18191820 exe := filepath.Join(tmpdir, "x.exe")1821 cmd := goCmd(t, "build", "-o", exe, src)1822 if out, err := cmd.CombinedOutput(); err != nil {1823 t.Fatalf("build failed; %v, output:\n%s", err, out)1824 }18251826 // We want to find the funcdata in the executable.1827 // We look at the section table to find the .gopclntab section,1828 // which starts with the pcHeader.1829 // That will give us the table of functions,1830 // which we can use to find the funcdata.18311832 ef, _ := elf.Open(exe)1833 mf, _ := macho.Open(exe)1834 pf, _ := pe.Open(exe)1835 xf, _ := xcoff.Open(exe)1836 // TODO: plan91837 if ef == nil && mf == nil && pf == nil && xf == nil {1838 t.Skip("unrecognized executable file format")1839 }18401841 const moddataSymName = "runtime.firstmoduledata"1842 const gofuncSymName = "go:func.*"1843 var (1844 pclntab []byte1845 pclntabAddr uint641846 pclntabEnd uint641847 moddataAddr uint641848 moddataBytes []byte1849 gofuncAddr uint641850 imageBase uint641851 )1852 switch {1853 case ef != nil:1854 defer ef.Close()18551856 syms, err := ef.Symbols()1857 if err != nil {1858 t.Fatal(err)1859 }1860 for _, sym := range syms {1861 switch sym.Name {1862 case moddataSymName:1863 moddataAddr = sym.Value1864 case gofuncSymName:1865 gofuncAddr = sym.Value1866 }1867 }18681869 for _, sec := range ef.Sections {1870 if sec.Name == ".gopclntab" {1871 data, err := sec.Data()1872 if err != nil {1873 t.Fatal(err)1874 }1875 pclntab = data1876 pclntabAddr = sec.Addr1877 pclntabEnd = sec.Addr + sec.Size1878 }1879 if sec.Flags&elf.SHF_ALLOC != 0 && moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {1880 data, err := sec.Data()1881 if err != nil {1882 t.Fatal(err)1883 }1884 moddataBytes = data[moddataAddr-sec.Addr:]1885 }1886 }18871888 case mf != nil:1889 defer mf.Close()18901891 for _, sym := range mf.Symtab.Syms {1892 switch sym.Name {1893 case moddataSymName:1894 moddataAddr = sym.Value1895 case gofuncSymName:1896 gofuncAddr = sym.Value1897 }1898 }18991900 for _, sec := range mf.Sections {1901 if sec.Name == "__gopclntab" {1902 data, err := sec.Data()1903 if err != nil {1904 t.Fatal(err)1905 }1906 pclntab = data1907 pclntabAddr = sec.Addr1908 pclntabEnd = sec.Addr + sec.Size1909 }1910 if moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {1911 data, err := sec.Data()1912 if err != nil {1913 t.Fatal(err)1914 }1915 moddataBytes = data[moddataAddr-sec.Addr:]1916 }1917 }19181919 case pf != nil:1920 defer pf.Close()19211922 switch ohdr := pf.OptionalHeader.(type) {1923 case *pe.OptionalHeader32:1924 imageBase = uint64(ohdr.ImageBase)1925 case *pe.OptionalHeader64:1926 imageBase = ohdr.ImageBase1927 }19281929 var moddataSym, gofuncSym, pclntabSym, epclntabSym *pe.Symbol1930 for _, sym := range pf.Symbols {1931 switch sym.Name {1932 case moddataSymName:1933 moddataSym = sym1934 case gofuncSymName:1935 gofuncSym = sym1936 case "runtime.pclntab":1937 pclntabSym = sym1938 case "runtime.epclntab":1939 epclntabSym = sym1940 }1941 }19421943 if moddataSym == nil {1944 t.Fatalf("could not find symbol %s", moddataSymName)1945 }1946 if gofuncSym == nil {1947 t.Fatalf("could not find symbol %s", gofuncSymName)1948 }1949 if pclntabSym == nil {1950 t.Fatal("could not find symbol runtime.pclntab")1951 }1952 if epclntabSym == nil {1953 t.Fatal("could not find symbol runtime.epclntab")1954 }19551956 sec := pf.Sections[moddataSym.SectionNumber-1]1957 data, err := sec.Data()1958 if err != nil {1959 t.Fatal(err)1960 }1961 moddataBytes = data[moddataSym.Value:]1962 moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)19631964 sec = pf.Sections[gofuncSym.SectionNumber-1]1965 gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)19661967 if pclntabSym.SectionNumber != epclntabSym.SectionNumber {1968 t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)1969 }1970 sec = pf.Sections[pclntabSym.SectionNumber-1]1971 data, err = sec.Data()1972 if err != nil {1973 t.Fatal(err)1974 }1975 pclntab = data[pclntabSym.Value:epclntabSym.Value]1976 pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)1977 pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)19781979 case xf != nil:1980 defer xf.Close()19811982 var moddataSym, gofuncSym, pclntabSym, epclntabSym *xcoff.Symbol1983 for _, sym := range xf.Symbols {1984 switch sym.Name {1985 case moddataSymName:1986 moddataSym = sym1987 case gofuncSymName:1988 gofuncSym = sym1989 case "runtime.pclntab":1990 pclntabSym = sym1991 case "runtime.epclntab":1992 epclntabSym = sym1993 }1994 }19951996 if moddataSym == nil {1997 t.Fatalf("could not find symbol %s", moddataSymName)1998 }1999 if gofuncSym == nil {2000 t.Fatalf("could not find symbol %s", gofuncSymName)
Findings
✓ No findings reported for this file.