src/cmd/link/link_test.go GO 2,491 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,491.
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.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.