1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7import (
8	"bufio"
9	"bytes"
10	"debug/macho"
11	"errors"
12	"internal/platform"
13	"internal/testenv"
14	"os"
15	"os/exec"
16	"path/filepath"
17	"regexp"
18	"runtime"
19	"strings"
20	"testing"
21
22	"cmd/internal/sys"
23)
24
25var AuthorPaidByTheColumnInch struct {
26	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."`
27
28	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."`
29
30	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."`
31
32	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."`
33}
34
35func TestLargeSymName(t *testing.T) {
36	// The compiler generates a symbol name using the string form of the
37	// type. This tests that the linker can read symbol names larger than
38	// the bufio buffer. Issue #15104.
39	_ = AuthorPaidByTheColumnInch
40}
41
42func TestIssue21703(t *testing.T) {
43	t.Parallel()
44
45	testenv.MustHaveGoBuild(t)
46	testenv.MustInternalLink(t, false)
47
48	const source = `
49package main
50const X = "\n!\n"
51func main() {}
52`
53
54	tmpdir := t.TempDir()
55	main := filepath.Join(tmpdir, "main.go")
56
57	err := os.WriteFile(main, []byte(source), 0666)
58	if err != nil {
59		t.Fatalf("failed to write main.go: %v\n", err)
60	}
61
62	importcfgfile := filepath.Join(tmpdir, "importcfg")
63	testenv.WriteImportcfg(t, importcfgfile, nil, main)
64
65	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
66	cmd.Dir = tmpdir
67	out, err := cmd.CombinedOutput()
68	if err != nil {
69		t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
70	}
71
72	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "main.o")
73	cmd.Dir = tmpdir
74	out, err = cmd.CombinedOutput()
75	if err != nil {
76		if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
77			testenv.SkipFlaky(t, 58806)
78		}
79		t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
80	}
81}
82
83// TestIssue28429 ensures that the linker does not attempt to link
84// sections not named *.o. Such sections may be used by a build system
85// to, for example, save facts produced by a modular static analysis
86// such as golang.org/x/tools/go/analysis.
87func TestIssue28429(t *testing.T) {
88	t.Parallel()
89
90	testenv.MustHaveGoBuild(t)
91	testenv.MustInternalLink(t, false)
92
93	tmpdir := t.TempDir()
94
95	write := func(name, content string) {
96		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
97		if err != nil {
98			t.Fatal(err)
99		}
100	}
101
102	runGo := func(args ...string) {
103		cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
104		cmd.Dir = tmpdir
105		out, err := cmd.CombinedOutput()
106		if err != nil {
107			if len(args) >= 2 && args[1] == "link" && runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
108				testenv.SkipFlaky(t, 58806)
109			}
110			t.Fatalf("'go %s' failed: %v, output: %s",
111				strings.Join(args, " "), err, out)
112		}
113	}
114
115	// Compile a main package.
116	write("main.go", "package main; func main() {}")
117	importcfgfile := filepath.Join(tmpdir, "importcfg")
118	testenv.WriteImportcfg(t, importcfgfile, nil, filepath.Join(tmpdir, "main.go"))
119	runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
120	runGo("tool", "pack", "c", "main.a", "main.o")
121
122	// Add an extra section with a short, non-.o name.
123	// This simulates an alternative build system.
124	write(".facts", "this is not an object file")
125	runGo("tool", "pack", "r", "main.a", ".facts")
126
127	// Verify that the linker does not attempt
128	// to compile the extra section.
129	runGo("tool", "link", "-importcfg="+importcfgfile, "main.a")
130}
131
132func TestUnresolved(t *testing.T) {
133	testenv.MustHaveGoBuild(t)
134
135	t.Parallel()
136
137	tmpdir := t.TempDir()
138
139	write := func(name, content string) {
140		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
141		if err != nil {
142			t.Fatal(err)
143		}
144	}
145
146	// Test various undefined references. Because of issue #29852,
147	// this used to give confusing error messages because the
148	// linker would find an undefined reference to "zero" created
149	// by the runtime package.
150
151	write("go.mod", "module testunresolved\n")
152	write("main.go", `package main
153
154func main() {
155        x()
156}
157
158func x()
159`)
160	write("main.s", `
161TEXT ·x(SB),0,$0
162        MOVD zero<>(SB), AX
163        MOVD zero(SB), AX
164        MOVD ·zero(SB), AX
165        RET
166`)
167	cmd := testenv.Command(t, testenv.GoToolPath(t), "build")
168	cmd.Dir = tmpdir
169	cmd.Env = append(os.Environ(),
170		"GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
171	out, err := cmd.CombinedOutput()
172	if err == nil {
173		t.Fatalf("expected build to fail, but it succeeded")
174	}
175	out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
176	got := string(out)
177	want := `main.x: relocation target zero not defined
178main.x: relocation target zero not defined
179main.x: relocation target main.zero not defined
180`
181	if want != got {
182		t.Fatalf("want:\n%sgot:\n%s", want, got)
183	}
184}
185
186func TestIssue33979(t *testing.T) {
187	testenv.MustHaveGoBuild(t)
188	testenv.MustHaveCGO(t)
189	testenv.MustInternalLink(t, true)
190
191	t.Parallel()
192
193	tmpdir := t.TempDir()
194
195	write := func(name, content string) {
196		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
197		if err != nil {
198			t.Fatal(err)
199		}
200	}
201
202	run := func(name string, args ...string) string {
203		cmd := testenv.Command(t, name, args...)
204		cmd.Dir = tmpdir
205		out, err := cmd.CombinedOutput()
206		if err != nil {
207			t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)
208		}
209		return string(out)
210	}
211	runGo := func(args ...string) string {
212		return run(testenv.GoToolPath(t), args...)
213	}
214
215	// Test object with undefined reference that was not generated
216	// by Go, resulting in an SXREF symbol being loaded during linking.
217	// Because of issue #33979, the SXREF symbol would be found during
218	// error reporting, resulting in confusing error messages.
219
220	write("main.go", `package main
221func main() {
222        x()
223}
224func x()
225`)
226	// The following assembly must work on all architectures.
227	write("x.s", `
228TEXT ·x(SB),0,$0
229        CALL foo(SB)
230        RET
231`)
232	write("x.c", `
233void undefined();
234
235void foo() {
236        undefined();
237}
238`)
239
240	cc := strings.TrimSpace(runGo("env", "CC"))
241	cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))
242
243	importcfgfile := filepath.Join(tmpdir, "importcfg")
244	testenv.WriteImportcfg(t, importcfgfile, nil, "runtime")
245
246	// Compile, assemble and pack the Go and C code.
247	runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s")
248	runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go")
249	runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s")
250	run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)
251	runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")
252
253	// Now attempt to link using the internal linker.
254	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-linkmode=internal", "x.a")
255	cmd.Dir = tmpdir
256	out, err := cmd.CombinedOutput()
257	if err == nil {
258		t.Fatalf("expected link to fail, but it succeeded")
259	}
260	re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)
261	if !re.Match(out) {
262		t.Fatalf("got:\n%q\nwant:\n%s", out, re)
263	}
264}
265
266func TestBuildForTvOS(t *testing.T) {
267	testenv.MustHaveCGO(t)
268	testenv.MustHaveGoBuild(t)
269
270	// Only run this on darwin, where we can cross build for tvOS.
271	if runtime.GOOS != "darwin" {
272		t.Skip("skipping on non-darwin platform")
273	}
274	if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
275		t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
276	}
277	if err := testenv.Command(t, "xcrun", "--help").Run(); err != nil {
278		t.Skipf("error running xcrun, required for iOS cross build: %v", err)
279	}
280
281	t.Parallel()
282
283	sdkPath, err := testenv.Command(t, "xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
284	if err != nil {
285		t.Skip("failed to locate appletvos SDK, skipping")
286	}
287	CC := []string{
288		"clang",
289		"-arch",
290		"arm64",
291		"-isysroot", strings.TrimSpace(string(sdkPath)),
292		"-mtvos-version-min=12.0",
293		"-fembed-bitcode",
294	}
295	CGO_LDFLAGS := []string{"-framework", "CoreFoundation"}
296	lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
297	tmpDir := t.TempDir()
298
299	ar := filepath.Join(tmpDir, "lib.a")
300	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
301	env := []string{
302		"CGO_ENABLED=1",
303		"GOOS=ios",
304		"GOARCH=arm64",
305		"CC=" + strings.Join(CC, " "),
306		"CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
307		"CGO_LDFLAGS=" + strings.Join(CGO_LDFLAGS, " "),
308	}
309	cmd.Env = append(os.Environ(), env...)
310	t.Logf("%q %v", env, cmd)
311	if out, err := cmd.CombinedOutput(); err != nil {
312		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
313	}
314
315	link := testenv.Command(t, CC[0], CC[1:]...)
316	link.Args = append(link.Args, CGO_LDFLAGS...)
317	link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out")) // Avoid writing to package directory.
318	link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
319	t.Log(link)
320	if out, err := link.CombinedOutput(); err != nil {
321		t.Fatalf("%v: %v:\n%s", link.Args, err, out)
322	}
323}
324
325var testXFlagSrc = `
326package main
327var X = "hello"
328var Z = [99999]int{99998:12345} // make it large enough to be mmaped
329func main() { println(X) }
330`
331
332func TestXFlag(t *testing.T) {
333	testenv.MustHaveGoBuild(t)
334
335	t.Parallel()
336
337	tmpdir := t.TempDir()
338
339	src := filepath.Join(tmpdir, "main.go")
340	err := os.WriteFile(src, []byte(testXFlagSrc), 0666)
341	if err != nil {
342		t.Fatal(err)
343	}
344
345	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
346	if out, err := cmd.CombinedOutput(); err != nil {
347		t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
348	}
349}
350
351var trivialSrc = `
352package main
353func main() { }
354`
355
356func TestMachOBuildVersion(t *testing.T) {
357	testenv.MustHaveGoBuild(t)
358
359	t.Parallel()
360
361	tmpdir := t.TempDir()
362
363	src := filepath.Join(tmpdir, "main.go")
364	err := os.WriteFile(src, []byte(trivialSrc), 0666)
365	if err != nil {
366		t.Fatal(err)
367	}
368
369	exe := filepath.Join(tmpdir, "main")
370	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
371	cmd.Env = append(os.Environ(),
372		"CGO_ENABLED=0",
373		"GOOS=darwin",
374		"GOARCH=amd64",
375	)
376	if out, err := cmd.CombinedOutput(); err != nil {
377		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
378	}
379	exef, err := os.Open(exe)
380	if err != nil {
381		t.Fatal(err)
382	}
383	defer exef.Close()
384	exem, err := macho.NewFile(exef)
385	if err != nil {
386		t.Fatal(err)
387	}
388	found := false
389	const LC_BUILD_VERSION = 0x32
390	checkMin := func(ver uint32) {
391		major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
392		if major < 11 {
393			t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 11.0.0", major, minor, patch)
394		}
395	}
396	for _, cmd := range exem.Loads {
397		raw := cmd.Raw()
398		type_ := exem.ByteOrder.Uint32(raw)
399		if type_ != LC_BUILD_VERSION {
400			continue
401		}
402		osVer := exem.ByteOrder.Uint32(raw[12:])
403		checkMin(osVer)
404		sdkVer := exem.ByteOrder.Uint32(raw[16:])
405		checkMin(sdkVer)
406		found = true
407		break
408	}
409	if !found {
410		t.Errorf("no LC_BUILD_VERSION load command found")
411	}
412}
413
414const Issue34788src = `
415
416package blah
417
418func Blah(i int) int {
419	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
420	return a[i&7]
421}
422`
423
424func TestIssue34788Android386TLSSequence(t *testing.T) {
425	testenv.MustHaveGoBuild(t)
426
427	// This is a cross-compilation test, so it doesn't make
428	// sense to run it on every GOOS/GOARCH combination. Limit
429	// the test to amd64 + darwin/linux.
430	if runtime.GOARCH != "amd64" ||
431		(runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
432		t.Skip("skipping on non-{linux,darwin}/amd64 platform")
433	}
434
435	t.Parallel()
436
437	tmpdir := t.TempDir()
438
439	src := filepath.Join(tmpdir, "blah.go")
440	err := os.WriteFile(src, []byte(Issue34788src), 0666)
441	if err != nil {
442		t.Fatal(err)
443	}
444
445	obj := filepath.Join(tmpdir, "blah.o")
446	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src)
447	cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
448	if out, err := cmd.CombinedOutput(); err != nil {
449		t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
450	}
451
452	// Run objdump on the resulting object.
453	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "objdump", obj)
454	out, oerr := cmd.CombinedOutput()
455	if oerr != nil {
456		t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
457	}
458
459	// Sift through the output; we should not be seeing any R_TLS_LE relocs.
460	scanner := bufio.NewScanner(bytes.NewReader(out))
461	for scanner.Scan() {
462		line := scanner.Text()
463		if strings.Contains(line, "R_TLS_LE") {
464			t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
465		}
466	}
467}
468
469const testStrictDupGoSrc = `
470package main
471func f()
472func main() { f() }
473`
474
475const testStrictDupAsmSrc1 = `
476#include "textflag.h"
477TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
478	RET
479`
480
481const testStrictDupAsmSrc2 = `
482#include "textflag.h"
483TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
484	JMP	0(PC)
485`
486
487const testStrictDupAsmSrc3 = `
488#include "textflag.h"
489GLOBL ·rcon(SB), RODATA|DUPOK, $64
490`
491
492const testStrictDupAsmSrc4 = `
493#include "textflag.h"
494GLOBL ·rcon(SB), RODATA|DUPOK, $32
495`
496
497func TestStrictDup(t *testing.T) {
498	// Check that -strictdups flag works.
499	testenv.MustHaveGoBuild(t)
500
501	asmfiles := []struct {
502		fname   string
503		payload string
504	}{
505		{"a", testStrictDupAsmSrc1},
506		{"b", testStrictDupAsmSrc2},
507		{"c", testStrictDupAsmSrc3},
508		{"d", testStrictDupAsmSrc4},
509	}
510
511	t.Parallel()
512
513	tmpdir := t.TempDir()
514
515	src := filepath.Join(tmpdir, "x.go")
516	err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
517	if err != nil {
518		t.Fatal(err)
519	}
520	for _, af := range asmfiles {
521		src = filepath.Join(tmpdir, af.fname+".s")
522		err = os.WriteFile(src, []byte(af.payload), 0666)
523		if err != nil {
524			t.Fatal(err)
525		}
526	}
527	src = filepath.Join(tmpdir, "go.mod")
528	err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666)
529	if err != nil {
530		t.Fatal(err)
531	}
532
533	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
534	cmd.Dir = tmpdir
535	out, err := cmd.CombinedOutput()
536	if err != nil {
537		t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
538	}
539	if !bytes.Contains(out, []byte("mismatched payload")) {
540		t.Errorf("unexpected output:\n%s", out)
541	}
542
543	cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
544	cmd.Dir = tmpdir
545	out, err = cmd.CombinedOutput()
546	if err == nil {
547		t.Errorf("linking with -strictdups=2 did not fail")
548	}
549	// NB: on amd64 we get the 'new length' error, on arm64 the 'different
550	// contents' error.
551	if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
552		bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
553		!bytes.Contains(out, []byte("mismatched payload: different sizes")) {
554		t.Errorf("unexpected output:\n%s", out)
555	}
556}
557
558const testFuncAlignSrc = `
559package main
560import (
561	"fmt"
562)
563func alignPc()
564var alignPcFnAddr uintptr
565
566func main() {
567	if alignPcFnAddr % 512 != 0 {
568		fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)
569	} else {
570		fmt.Printf("PASS")
571	}
572}
573`
574
575var testFuncAlignAsmSources = map[string]string{
576	"arm64": `
577#include "textflag.h"
578
579TEXT	·alignPc(SB),NOSPLIT, $0-0
580	MOVD	$2, R0
581	PCALIGN	$512
582	MOVD	$3, R1
583	RET
584
585GLOBL	·alignPcFnAddr(SB),RODATA,$8
586DATA	·alignPcFnAddr(SB)/8,$·alignPc(SB)
587`,
588	"loong64": `
589#include "textflag.h"
590
591TEXT	·alignPc(SB),NOSPLIT, $0-0
592	MOVV	$2, R4
593	PCALIGN	$512
594	MOVV	$3, R5
595	RET
596
597GLOBL	·alignPcFnAddr(SB),RODATA,$8
598DATA	·alignPcFnAddr(SB)/8,$·alignPc(SB)
599`,
600}
601
602// TestFuncAlign verifies that the address of a function can be aligned
603// with a specific value on arm64 and loong64.
604func TestFuncAlign(t *testing.T) {
605	testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH]
606	if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" {
607		t.Skip("skipping on non-linux/{arm64,loong64} platform")
608	}
609	testenv.MustHaveGoBuild(t)
610
611	t.Parallel()
612
613	tmpdir := t.TempDir()
614
615	src := filepath.Join(tmpdir, "go.mod")
616	err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
617	if err != nil {
618		t.Fatal(err)
619	}
620	src = filepath.Join(tmpdir, "falign.go")
621	err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666)
622	if err != nil {
623		t.Fatal(err)
624	}
625	src = filepath.Join(tmpdir, "falign.s")
626	err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
627	if err != nil {
628		t.Fatal(err)
629	}
630
631	// Build and run with old object file format.
632	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "falign")
633	cmd.Dir = tmpdir
634	out, err := cmd.CombinedOutput()
635	if err != nil {
636		t.Errorf("build failed: %v", err)
637	}
638	cmd = testenv.Command(t, tmpdir+"/falign")
639	out, err = cmd.CombinedOutput()
640	if err != nil {
641		t.Errorf("failed to run with err %v, output: %s", err, out)
642	}
643	if string(out) != "PASS" {
644		t.Errorf("unexpected output: %s\n", out)
645	}
646}
647
648const testTrampSrc = `
649package main
650import "fmt"
651func main() {
652	fmt.Println("hello")
653
654	defer func(){
655		if e := recover(); e == nil {
656			panic("did not panic")
657		}
658	}()
659	f1()
660}
661
662// Test deferreturn trampolines. See issue #39049.
663func f1() { defer f2() }
664func f2() { panic("XXX") }
665`
666
667func TestTrampoline(t *testing.T) {
668	// Test that trampoline insertion works as expected.
669	// For stress test, we set -debugtramp=2 flag, which sets a very low
670	// threshold for trampoline generation, and essentially all cross-package
671	// calls will use trampolines.
672	buildmodes := []string{"default"}
673	switch runtime.GOARCH {
674	case "arm", "arm64", "ppc64":
675	case "ppc64le":
676		// Trampolines are generated differently when internal linking PIE, test them too.
677		buildmodes = append(buildmodes, "pie")
678	default:
679		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
680	}
681
682	testenv.MustHaveGoBuild(t)
683
684	t.Parallel()
685
686	tmpdir := t.TempDir()
687
688	src := filepath.Join(tmpdir, "hello.go")
689	err := os.WriteFile(src, []byte(testTrampSrc), 0666)
690	if err != nil {
691		t.Fatal(err)
692	}
693	exe := filepath.Join(tmpdir, "hello.exe")
694
695	for _, mode := range buildmodes {
696		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
697		out, err := cmd.CombinedOutput()
698		if err != nil {
699			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
700		}
701		cmd = testenv.Command(t, exe)
702		out, err = cmd.CombinedOutput()
703		if err != nil {
704			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
705		}
706		if string(out) != "hello\n" {
707			t.Errorf("unexpected output (%s):\n%s", mode, out)
708		}
709	}
710}
711
712const testTrampCgoSrc = `
713package main
714
715// #include <stdio.h>
716// void CHello() { printf("hello\n"); fflush(stdout); }
717import "C"
718
719func main() {
720	C.CHello()
721}
722`
723
724func TestTrampolineCgo(t *testing.T) {
725	// Test that trampoline insertion works for cgo code.
726	// For stress test, we set -debugtramp=2 flag, which sets a very low
727	// threshold for trampoline generation, and essentially all cross-package
728	// calls will use trampolines.
729	buildmodes := []string{"default"}
730	switch runtime.GOARCH {
731	case "arm", "arm64", "ppc64":
732	case "ppc64le":
733		// Trampolines are generated differently when internal linking PIE, test them too.
734		buildmodes = append(buildmodes, "pie")
735	default:
736		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
737	}
738
739	testenv.MustHaveGoBuild(t)
740	testenv.MustHaveCGO(t)
741
742	t.Parallel()
743
744	tmpdir := t.TempDir()
745
746	src := filepath.Join(tmpdir, "hello.go")
747	err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
748	if err != nil {
749		t.Fatal(err)
750	}
751	exe := filepath.Join(tmpdir, "hello.exe")
752
753	for _, mode := range buildmodes {
754		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
755		out, err := cmd.CombinedOutput()
756		if err != nil {
757			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
758		}
759		cmd = testenv.Command(t, exe)
760		out, err = cmd.CombinedOutput()
761		if err != nil {
762			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
763		}
764		if string(out) != "hello\n" && string(out) != "hello\r\n" {
765			t.Errorf("unexpected output (%s):\n%s", mode, out)
766		}
767
768		// Test internal linking mode.
769
770		if !testenv.CanInternalLink(true) {
771			continue
772		}
773		cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
774		out, err = cmd.CombinedOutput()
775		if err != nil {
776			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
777		}
778		cmd = testenv.Command(t, exe)
779		out, err = cmd.CombinedOutput()
780		if err != nil {
781			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
782		}
783		if string(out) != "hello\n" && string(out) != "hello\r\n" {
784			t.Errorf("unexpected output (%s):\n%s", mode, out)
785		}
786	}
787}
788
789func TestIndexMismatch(t *testing.T) {
790	// Test that index mismatch will cause a link-time error (not run-time error).
791	// This shouldn't happen with "go build". We invoke the compiler and the linker
792	// manually, and try to "trick" the linker with an inconsistent object file.
793	testenv.MustHaveGoBuild(t)
794	testenv.MustInternalLink(t, false)
795
796	t.Parallel()
797
798	tmpdir := t.TempDir()
799
800	aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
801	bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
802	mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
803	aObj := filepath.Join(tmpdir, "a.o")
804	mObj := filepath.Join(tmpdir, "main.o")
805	exe := filepath.Join(tmpdir, "main.exe")
806
807	importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")
808	testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")
809	importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
810	testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")
811
812	// Build a program with main package importing package a.
813	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
814	t.Log(cmd)
815	out, err := cmd.CombinedOutput()
816	if err != nil {
817		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
818	}
819	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
820	t.Log(cmd)
821	out, err = cmd.CombinedOutput()
822	if err != nil {
823		t.Fatalf("compiling main.go failed: %v\n%s", err, out)
824	}
825	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
826	t.Log(cmd)
827	out, err = cmd.CombinedOutput()
828	if err != nil {
829		if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
830			testenv.SkipFlaky(t, 58806)
831		}
832		t.Errorf("linking failed: %v\n%s", err, out)
833	}
834
835	// Now, overwrite a.o with the object of b.go. This should
836	// result in an index mismatch.
837	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
838	t.Log(cmd)
839	out, err = cmd.CombinedOutput()
840	if err != nil {
841		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
842	}
843	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
844	t.Log(cmd)
845	out, err = cmd.CombinedOutput()
846	if err == nil {
847		t.Fatalf("linking didn't fail")
848	}
849	if !bytes.Contains(out, []byte("fingerprint mismatch")) {
850		t.Errorf("did not see expected error message. out:\n%s", out)
851	}
852}
853
854func TestPErsrcBinutils(t *testing.T) {
855	// Test that PE rsrc section is handled correctly (issue 39658).
856	testenv.MustHaveGoBuild(t)
857
858	if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
859		// This test is limited to amd64 and 386, because binutils is limited as such
860		t.Skipf("this is only for windows/amd64 and windows/386")
861	}
862
863	t.Parallel()
864
865	tmpdir := t.TempDir()
866
867	pkgdir := filepath.Join("testdata", "pe-binutils")
868	exe := filepath.Join(tmpdir, "a.exe")
869	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe)
870	cmd.Dir = pkgdir
871	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
872	out, err := cmd.CombinedOutput()
873	if err != nil {
874		t.Fatalf("building failed: %v, output:\n%s", err, out)
875	}
876
877	// Check that the binary contains the rsrc data
878	b, err := os.ReadFile(exe)
879	if err != nil {
880		t.Fatalf("reading output failed: %v", err)
881	}
882	if !bytes.Contains(b, []byte("Hello Gophers!")) {
883		t.Fatalf("binary does not contain expected content")
884	}
885}
886
887func TestPErsrcLLVM(t *testing.T) {
888	// Test that PE rsrc section is handled correctly (issue 39658).
889	testenv.MustHaveGoBuild(t)
890
891	if runtime.GOOS != "windows" {
892		t.Skipf("this is a windows-only test")
893	}
894
895	t.Parallel()
896
897	tmpdir := t.TempDir()
898
899	pkgdir := filepath.Join("testdata", "pe-llvm")
900	exe := filepath.Join(tmpdir, "a.exe")
901	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe)
902	cmd.Dir = pkgdir
903	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
904	out, err := cmd.CombinedOutput()
905	if err != nil {
906		t.Fatalf("building failed: %v, output:\n%s", err, out)
907	}
908
909	// Check that the binary contains the rsrc data
910	b, err := os.ReadFile(exe)
911	if err != nil {
912		t.Fatalf("reading output failed: %v", err)
913	}
914	if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
915		t.Fatalf("binary does not contain expected content")
916	}
917}
918
919func TestContentAddressableSymbols(t *testing.T) {
920	// Test that the linker handles content-addressable symbols correctly.
921	testenv.MustHaveGoBuild(t)
922
923	t.Parallel()
924
925	src := filepath.Join("testdata", "testHashedSyms", "p.go")
926	cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
927	out, err := cmd.CombinedOutput()
928	if err != nil {
929		t.Errorf("command %s failed: %v\n%s", cmd, err, out)
930	}
931}
932
933func TestReadOnly(t *testing.T) {
934	// Test that read-only data is indeed read-only.
935	testenv.MustHaveGoBuild(t)
936
937	t.Parallel()
938
939	src := filepath.Join("testdata", "testRO", "x.go")
940	cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
941	out, err := cmd.CombinedOutput()
942	if err == nil {
943		t.Errorf("running test program did not fail. output:\n%s", out)
944	}
945}
946
947const testIssue38554Src = `
948package main
949
950type T [10<<20]byte
951
952//go:noinline
953func f() T {
954	return T{} // compiler will make a large stmp symbol, but not used.
955}
956
957func main() {
958	x := f()
959	println(x[1])
960}
961`
962
963func TestIssue38554(t *testing.T) {
964	testenv.MustHaveGoBuild(t)
965
966	t.Parallel()
967
968	tmpdir := t.TempDir()
969
970	src := filepath.Join(tmpdir, "x.go")
971	err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
972	if err != nil {
973		t.Fatalf("failed to write source file: %v", err)
974	}
975	exe := filepath.Join(tmpdir, "x.exe")
976	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
977	out, err := cmd.CombinedOutput()
978	if err != nil {
979		t.Fatalf("build failed: %v\n%s", err, out)
980	}
981
982	fi, err := os.Stat(exe)
983	if err != nil {
984		t.Fatalf("failed to stat output file: %v", err)
985	}
986
987	// The test program is not much different from a helloworld, which is
988	// typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
989	// it will be over 10 MB.
990	const want = 5 << 20
991	if got := fi.Size(); got > want {
992		t.Errorf("binary too big: got %d, want < %d", got, want)
993	}
994}
995
996const testIssue42396src = `
997package main
998
999//go:noinline
1000//go:nosplit
1001func callee(x int) {
1002}
1003
1004func main() {
1005	callee(9)
1006}
1007`
1008
1009func TestIssue42396(t *testing.T) {
1010	testenv.MustHaveGoBuild(t)
1011
1012	if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
1013		t.Skip("no race detector support")
1014	}
1015
1016	t.Parallel()
1017
1018	tmpdir := t.TempDir()
1019
1020	src := filepath.Join(tmpdir, "main.go")
1021	err := os.WriteFile(src, []byte(testIssue42396src), 0666)
1022	if err != nil {
1023		t.Fatalf("failed to write source file: %v", err)
1024	}
1025	exe := filepath.Join(tmpdir, "main.exe")
1026	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
1027	out, err := cmd.CombinedOutput()
1028	if err == nil {
1029		t.Fatalf("build unexpectedly succeeded")
1030	}
1031
1032	// Check to make sure that we see a reasonable error message
1033	// and not a panic.
1034	if strings.Contains(string(out), "panic:") {
1035		t.Fatalf("build should not fail with panic:\n%s", out)
1036	}
1037	const want = "reference to undefined builtin"
1038	if !strings.Contains(string(out), want) {
1039		t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
1040	}
1041}
1042
1043const testLargeRelocSrc = `
1044package main
1045
1046var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
1047
1048var addr = [...]*byte{
1049	&x[1<<23-1],
1050	&x[1<<23],
1051	&x[1<<23+1],
1052	&x[1<<24-1],
1053	&x[1<<24],
1054	&x[1<<24+1],
1055}
1056
1057func main() {
1058	// check relocations in instructions
1059	check(x[1<<23-1], 0)
1060	check(x[1<<23], 23)
1061	check(x[1<<23+1], 0)
1062	check(x[1<<24-1], 0)
1063	check(x[1<<24], 24)
1064	check(x[1<<24+1], 0)
1065
1066	// check absolute address relocations in data
1067	check(*addr[0], 0)
1068	check(*addr[1], 23)
1069	check(*addr[2], 0)
1070	check(*addr[3], 0)
1071	check(*addr[4], 24)
1072	check(*addr[5], 0)
1073}
1074
1075func check(x, y byte) {
1076	if x != y {
1077		panic("FAIL")
1078	}
1079}
1080`
1081
1082func TestLargeReloc(t *testing.T) {
1083	// Test that large relocation addend is handled correctly.
1084	// In particular, on darwin/arm64 when external linking,
1085	// Mach-O relocation has only 24-bit addend. See issue #42738.
1086	testenv.MustHaveGoBuild(t)
1087	t.Parallel()
1088
1089	tmpdir := t.TempDir()
1090
1091	src := filepath.Join(tmpdir, "x.go")
1092	err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
1093	if err != nil {
1094		t.Fatalf("failed to write source file: %v", err)
1095	}
1096	cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
1097	out, err := cmd.CombinedOutput()
1098	if err != nil {
1099		t.Errorf("build failed: %v. output:\n%s", err, out)
1100	}
1101
1102	if testenv.HasCGO() { // currently all targets that support cgo can external link
1103		cmd = testenv.Command(t, testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src)
1104		out, err = cmd.CombinedOutput()
1105		if err != nil {
1106			t.Fatalf("build failed: %v. output:\n%s", err, out)
1107		}
1108	}
1109}
1110
1111func TestUnlinkableObj(t *testing.T) {
1112	// Test that the linker emits an error with unlinkable object.
1113	testenv.MustHaveGoBuild(t)
1114	t.Parallel()
1115
1116	if true /* was buildcfg.Experiment.Unified */ {
1117		t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
1118	}
1119
1120	tmpdir := t.TempDir()
1121
1122	xSrc := filepath.Join(tmpdir, "x.go")
1123	pSrc := filepath.Join(tmpdir, "p.go")
1124	xObj := filepath.Join(tmpdir, "x.o")
1125	pObj := filepath.Join(tmpdir, "p.o")
1126	exe := filepath.Join(tmpdir, "x.exe")
1127	importcfgfile := filepath.Join(tmpdir, "importcfg")
1128	testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
1129	err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
1130	if err != nil {
1131		t.Fatalf("failed to write source file: %v", err)
1132	}
1133	err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
1134	if err != nil {
1135		t.Fatalf("failed to write source file: %v", err)
1136	}
1137	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc) // without -p
1138	out, err := cmd.CombinedOutput()
1139	if err != nil {
1140		t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1141	}
1142	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
1143	out, err = cmd.CombinedOutput()
1144	if err != nil {
1145		t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
1146	}
1147	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj)
1148	out, err = cmd.CombinedOutput()
1149	if err == nil {
1150		t.Fatalf("link did not fail")
1151	}
1152	if !bytes.Contains(out, []byte("unlinkable object")) {
1153		t.Errorf("did not see expected error message. out:\n%s", out)
1154	}
1155
1156	// It is okay to omit -p for (only) main package.
1157	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
1158	out, err = cmd.CombinedOutput()
1159	if err != nil {
1160		t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1161	}
1162	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc) // without -p
1163	out, err = cmd.CombinedOutput()
1164	if err != nil {
1165		t.Fatalf("compile failed: %v. output:\n%s", err, out)
1166	}
1167
1168	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj)
1169	out, err = cmd.CombinedOutput()
1170	if err != nil {
1171		t.Errorf("link failed: %v. output:\n%s", err, out)
1172	}
1173}
1174
1175func TestExtLinkCmdlineDeterminism(t *testing.T) {
1176	// Test that we pass flags in deterministic order to the external linker
1177	testenv.MustHaveGoBuild(t)
1178	testenv.MustHaveCGO(t) // this test requires -linkmode=external
1179	t.Parallel()
1180
1181	// test source code, with some cgo exports
1182	testSrc := `
1183package main
1184import "C"
1185//export F1
1186func F1() {}
1187//export F2
1188func F2() {}
1189//export F3
1190func F3() {}
1191func main() {}
1192`
1193
1194	tmpdir := t.TempDir()
1195	src := filepath.Join(tmpdir, "x.go")
1196	if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {
1197		t.Fatal(err)
1198	}
1199	exe := filepath.Join(tmpdir, "x.exe")
1200
1201	// Use a deterministic tmp directory so the temporary file paths are
1202	// deterministic.
1203	linktmp := filepath.Join(tmpdir, "linktmp")
1204	if err := os.Mkdir(linktmp, 0777); err != nil {
1205		t.Fatal(err)
1206	}
1207
1208	// Link with -v -linkmode=external to see the flags we pass to the
1209	// external linker.
1210	ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp
1211	var out0 []byte
1212	for i := 0; i < 5; i++ {
1213		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", ldflags, "-o", exe, src)
1214		out, err := cmd.CombinedOutput()
1215		if err != nil {
1216			t.Fatalf("build failed: %v, output:\n%s", err, out)
1217		}
1218		if err := os.Remove(exe); err != nil {
1219			t.Fatal(err)
1220		}
1221
1222		// extract the "host link" invocation
1223		j := bytes.Index(out, []byte("\nhost link:"))
1224		if j == -1 {
1225			t.Fatalf("host link step not found, output:\n%s", out)
1226		}
1227		out = out[j+1:]
1228		k := bytes.Index(out, []byte("\n"))
1229		if k == -1 {
1230			t.Fatalf("no newline after host link, output:\n%s", out)
1231		}
1232		out = out[:k]
1233
1234		// filter out output file name, which is passed by the go
1235		// command and is nondeterministic.
1236		fs := bytes.Fields(out)
1237		for i, f := range fs {
1238			if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {
1239				fs[i+1] = []byte("a.out")
1240				break
1241			}
1242		}
1243		out = bytes.Join(fs, []byte{' '})
1244
1245		if i == 0 {
1246			out0 = out
1247			continue
1248		}
1249		if !bytes.Equal(out0, out) {
1250			t.Fatalf("output differ:\n%s\n==========\n%s", out0, out)
1251		}
1252	}
1253}
1254
1255// TestResponseFile tests that creating a response file to pass to the
1256// external linker works correctly.
1257func TestResponseFile(t *testing.T) {
1258	t.Parallel()
1259
1260	testenv.MustHaveGoBuild(t)
1261
1262	// This test requires -linkmode=external. Currently all
1263	// systems that support cgo support -linkmode=external.
1264	testenv.MustHaveCGO(t)
1265
1266	tmpdir := t.TempDir()
1267
1268	src := filepath.Join(tmpdir, "x.go")
1269	if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {
1270		t.Fatal(err)
1271	}
1272
1273	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")
1274	cmd.Dir = tmpdir
1275
1276	// Add enough arguments to push cmd/link into creating a response file.
1277	var sb strings.Builder
1278	sb.WriteString(`'-ldflags=all="-extldflags=`)
1279	for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {
1280		if i > 0 {
1281			sb.WriteString(" ")
1282		}
1283		sb.WriteString("-g")
1284	}
1285	sb.WriteString(`"'`)
1286	cmd = testenv.CleanCmdEnv(cmd)
1287	cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())
1288
1289	out, err := cmd.CombinedOutput()
1290	if len(out) > 0 {
1291		t.Logf("%s", out)
1292	}
1293	if err != nil {
1294		t.Error(err)
1295	}
1296}
1297
1298func TestDynimportVar(t *testing.T) {
1299	// Test that we can access dynamically imported variables.
1300	// Currently darwin only.
1301	if runtime.GOOS != "darwin" {
1302		t.Skip("skip on non-darwin platform")
1303	}
1304
1305	testenv.MustHaveGoBuild(t)
1306	testenv.MustHaveCGO(t)
1307
1308	t.Parallel()
1309
1310	tmpdir := t.TempDir()
1311	exe := filepath.Join(tmpdir, "a.exe")
1312	src := filepath.Join("testdata", "dynimportvar", "main.go")
1313
1314	for _, mode := range []string{"internal", "external"} {
1315		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode="+mode, "-o", exe, src)
1316		out, err := cmd.CombinedOutput()
1317		if err != nil {
1318			t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1319		}
1320		cmd = testenv.Command(t, exe)
1321		out, err = cmd.CombinedOutput()
1322		if err != nil {
1323			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1324		}
1325	}
1326}
1327
1328const helloSrc = `
1329package main
1330var X = 42
1331var Y int
1332func main() { println("hello", X, Y) }
1333`
1334
1335func TestFlagS(t *testing.T) {
1336	// Test that the -s flag strips the symbol table.
1337	testenv.MustHaveGoBuild(t)
1338
1339	t.Parallel()
1340
1341	tmpdir := t.TempDir()
1342	exe := filepath.Join(tmpdir, "a.exe")
1343	src := filepath.Join(tmpdir, "a.go")
1344	err := os.WriteFile(src, []byte(helloSrc), 0666)
1345	if err != nil {
1346		t.Fatal(err)
1347	}
1348
1349	modes := []string{"auto"}
1350	if testenv.HasCGO() {
1351		modes = append(modes, "external")
1352	}
1353
1354	// check a text symbol, a data symbol, and a BSS symbol
1355	syms := []string{"main.main", "main.X", "main.Y"}
1356
1357	for _, mode := range modes {
1358		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)
1359		out, err := cmd.CombinedOutput()
1360		if err != nil {
1361			t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1362		}
1363		cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1364		out, err = cmd.CombinedOutput()
1365		if err != nil && !errors.As(err, new(*exec.ExitError)) {
1366			// Error exit is fine as it may have no symbols.
1367			// On darwin we need to emit dynamic symbol references so it
1368			// actually has some symbols, and nm succeeds.
1369			t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)
1370		}
1371		for _, s := range syms {
1372			if bytes.Contains(out, []byte(s)) {
1373				t.Errorf("(mode=%s): unexpected symbol %s", mode, s)
1374			}
1375		}
1376	}
1377}
1378
1379func TestRandLayout(t *testing.T) {
1380	// Test that the -randlayout flag randomizes function order and
1381	// generates a working binary.
1382	testenv.MustHaveGoBuild(t)
1383
1384	t.Parallel()
1385
1386	tmpdir := t.TempDir()
1387
1388	src := filepath.Join(tmpdir, "hello.go")
1389	err := os.WriteFile(src, []byte(trivialSrc), 0666)
1390	if err != nil {
1391		t.Fatal(err)
1392	}
1393
1394	var syms [2]string
1395	for i, seed := range []string{"123", "456"} {
1396		exe := filepath.Join(tmpdir, "hello"+seed+".exe")
1397		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
1398		out, err := cmd.CombinedOutput()
1399		if err != nil {
1400			t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)
1401		}
1402		cmd = testenv.Command(t, exe)
1403		err = cmd.Run()
1404		if err != nil {
1405			t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)
1406		}
1407		cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1408		out, err = cmd.CombinedOutput()
1409		if err != nil {
1410			t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)
1411		}
1412		syms[i] = string(out)
1413	}
1414	if syms[0] == syms[1] {
1415		t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
1416	}
1417}
1418
1419func TestCheckLinkname(t *testing.T) {
1420	// Test that code containing blocked linknames does not build.
1421	testenv.MustHaveGoBuild(t)
1422	t.Parallel()
1423
1424	tmpdir := t.TempDir()
1425
1426	tests := []struct {
1427		src string
1428		ok  bool
1429	}{
1430		// use (instantiation) of public API is ok
1431		{"ok.go", true},
1432		// push linkname is ok
1433		{"push.go", true},
1434		// pull linkname of blocked symbol is not ok
1435		{"coro.go", false},
1436		{"coro_var.go", false},
1437		// assembly reference is not ok
1438		{"coro_asm", false},
1439		// pull-only linkname is not ok
1440		{"coro2.go", false},
1441		// legacy bad linkname is ok, for now
1442		{"fastrand.go", true},
1443		{"badlinkname.go", true},
1444	}
1445	for _, test := range tests {
1446		test := test
1447		t.Run(test.src, func(t *testing.T) {
1448			t.Parallel()
1449			src := filepath.Join("testdata", "linkname", test.src)
1450			exe := filepath.Join(tmpdir, test.src+".exe")
1451			cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
1452			out, err := cmd.CombinedOutput()
1453			if test.ok && err != nil {
1454				t.Errorf("build failed unexpectedly: %v:\n%s", err, out)
1455			}
1456			if !test.ok && err == nil {
1457				t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out)
1458			}
1459		})
1460	}
1461}
1462