]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/link/link_test.go
ce065721649d30baa25bc8bbbb30064f54ce98c0
[gostls13.git] / src / cmd / link / link_test.go
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
5 package main
6
7 import (
8         "bufio"
9         "bytes"
10         "debug/macho"
11         "internal/buildcfg"
12         "internal/platform"
13         "internal/testenv"
14         "os"
15         "os/exec"
16         "path/filepath"
17         "regexp"
18         "runtime"
19         "strings"
20         "testing"
21 )
22
23 var AuthorPaidByTheColumnInch struct {
24         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."`
25
26         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."`
27
28         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."`
29
30         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."`
31 }
32
33 func TestLargeSymName(t *testing.T) {
34         // The compiler generates a symbol name using the string form of the
35         // type. This tests that the linker can read symbol names larger than
36         // the bufio buffer. Issue #15104.
37         _ = AuthorPaidByTheColumnInch
38 }
39
40 func TestIssue21703(t *testing.T) {
41         t.Parallel()
42
43         testenv.MustHaveGoBuild(t)
44
45         const source = `
46 package main
47 const X = "\n!\n"
48 func main() {}
49 `
50
51         tmpdir := t.TempDir()
52
53         importcfgfile := filepath.Join(tmpdir, "importcfg")
54         testenv.WriteImportcfg(t, importcfgfile, nil)
55
56         err := os.WriteFile(filepath.Join(tmpdir, "main.go"), []byte(source), 0666)
57         if err != nil {
58                 t.Fatalf("failed to write main.go: %v\n", err)
59         }
60
61         cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
62         cmd.Dir = tmpdir
63         out, err := cmd.CombinedOutput()
64         if err != nil {
65                 t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
66         }
67
68         cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "main.o")
69         cmd.Dir = tmpdir
70         out, err = cmd.CombinedOutput()
71         if err != nil {
72                 t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
73         }
74 }
75
76 // TestIssue28429 ensures that the linker does not attempt to link
77 // sections not named *.o. Such sections may be used by a build system
78 // to, for example, save facts produced by a modular static analysis
79 // such as golang.org/x/tools/go/analysis.
80 func TestIssue28429(t *testing.T) {
81         t.Parallel()
82
83         testenv.MustHaveGoBuild(t)
84
85         tmpdir := t.TempDir()
86
87         write := func(name, content string) {
88                 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
89                 if err != nil {
90                         t.Fatal(err)
91                 }
92         }
93
94         runGo := func(args ...string) {
95                 cmd := exec.Command(testenv.GoToolPath(t), args...)
96                 cmd.Dir = tmpdir
97                 out, err := cmd.CombinedOutput()
98                 if err != nil {
99                         t.Fatalf("'go %s' failed: %v, output: %s",
100                                 strings.Join(args, " "), err, out)
101                 }
102         }
103
104         importcfgfile := filepath.Join(tmpdir, "importcfg")
105         testenv.WriteImportcfg(t, importcfgfile, nil)
106
107         // Compile a main package.
108         write("main.go", "package main; func main() {}")
109         runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
110         runGo("tool", "pack", "c", "main.a", "main.o")
111
112         // Add an extra section with a short, non-.o name.
113         // This simulates an alternative build system.
114         write(".facts", "this is not an object file")
115         runGo("tool", "pack", "r", "main.a", ".facts")
116
117         // Verify that the linker does not attempt
118         // to compile the extra section.
119         runGo("tool", "link", "-importcfg="+importcfgfile, "main.a")
120 }
121
122 func TestUnresolved(t *testing.T) {
123         testenv.MustHaveGoBuild(t)
124
125         t.Parallel()
126
127         tmpdir := t.TempDir()
128
129         write := func(name, content string) {
130                 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
131                 if err != nil {
132                         t.Fatal(err)
133                 }
134         }
135
136         // Test various undefined references. Because of issue #29852,
137         // this used to give confusing error messages because the
138         // linker would find an undefined reference to "zero" created
139         // by the runtime package.
140
141         write("go.mod", "module testunresolved\n")
142         write("main.go", `package main
143
144 func main() {
145         x()
146 }
147
148 func x()
149 `)
150         write("main.s", `
151 TEXT ·x(SB),0,$0
152         MOVD zero<>(SB), AX
153         MOVD zero(SB), AX
154         MOVD ·zero(SB), AX
155         RET
156 `)
157         cmd := exec.Command(testenv.GoToolPath(t), "build")
158         cmd.Dir = tmpdir
159         cmd.Env = append(os.Environ(),
160                 "GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
161         out, err := cmd.CombinedOutput()
162         if err == nil {
163                 t.Fatalf("expected build to fail, but it succeeded")
164         }
165         out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
166         got := string(out)
167         want := `main.x: relocation target zero not defined
168 main.x: relocation target zero not defined
169 main.x: relocation target main.zero not defined
170 `
171         if want != got {
172                 t.Fatalf("want:\n%sgot:\n%s", want, got)
173         }
174 }
175
176 func TestIssue33979(t *testing.T) {
177         testenv.MustHaveGoBuild(t)
178         testenv.MustHaveCGO(t)
179         testenv.MustInternalLink(t)
180
181         // Skip test on platforms that do not support cgo internal linking.
182         switch runtime.GOARCH {
183         case "loong64":
184                 t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
185         case "mips", "mipsle", "mips64", "mips64le":
186                 t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
187         }
188         if runtime.GOOS == "aix" ||
189                 runtime.GOOS == "windows" && runtime.GOARCH == "arm64" {
190                 t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
191         }
192
193         t.Parallel()
194
195         tmpdir := t.TempDir()
196
197         write := func(name, content string) {
198                 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
199                 if err != nil {
200                         t.Fatal(err)
201                 }
202         }
203
204         run := func(name string, args ...string) string {
205                 cmd := exec.Command(name, args...)
206                 cmd.Dir = tmpdir
207                 out, err := cmd.CombinedOutput()
208                 if err != nil {
209                         t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)
210                 }
211                 return string(out)
212         }
213         runGo := func(args ...string) string {
214                 return run(testenv.GoToolPath(t), args...)
215         }
216
217         // Test object with undefined reference that was not generated
218         // by Go, resulting in an SXREF symbol being loaded during linking.
219         // Because of issue #33979, the SXREF symbol would be found during
220         // error reporting, resulting in confusing error messages.
221
222         write("main.go", `package main
223 func main() {
224         x()
225 }
226 func x()
227 `)
228         // The following assembly must work on all architectures.
229         write("x.s", `
230 TEXT ·x(SB),0,$0
231         CALL foo(SB)
232         RET
233 `)
234         write("x.c", `
235 void undefined();
236
237 void foo() {
238         undefined();
239 }
240 `)
241
242         cc := strings.TrimSpace(runGo("env", "CC"))
243         cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))
244
245         importcfgfile := filepath.Join(tmpdir, "importcfg")
246         testenv.WriteImportcfg(t, importcfgfile, nil)
247
248         // Compile, assemble and pack the Go and C code.
249         runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s")
250         runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go")
251         runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s")
252         run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)
253         runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")
254
255         // Now attempt to link using the internal linker.
256         cmd := exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-linkmode=internal", "x.a")
257         cmd.Dir = tmpdir
258         out, err := cmd.CombinedOutput()
259         if err == nil {
260                 t.Fatalf("expected link to fail, but it succeeded")
261         }
262         re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)
263         if !re.Match(out) {
264                 t.Fatalf("got:\n%q\nwant:\n%s", out, re)
265         }
266 }
267
268 func TestBuildForTvOS(t *testing.T) {
269         testenv.MustHaveCGO(t)
270         testenv.MustHaveGoBuild(t)
271
272         // Only run this on darwin/amd64, where we can cross build for tvOS.
273         if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
274                 t.Skip("skipping on non-darwin/amd64 platform")
275         }
276         if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
277                 t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
278         }
279         if err := exec.Command("xcrun", "--help").Run(); err != nil {
280                 t.Skipf("error running xcrun, required for iOS cross build: %v", err)
281         }
282
283         t.Parallel()
284
285         sdkPath, err := exec.Command("xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
286         if err != nil {
287                 t.Skip("failed to locate appletvos SDK, skipping")
288         }
289         CC := []string{
290                 "clang",
291                 "-arch",
292                 "arm64",
293                 "-isysroot", strings.TrimSpace(string(sdkPath)),
294                 "-mtvos-version-min=12.0",
295                 "-fembed-bitcode",
296         }
297         CGO_LDFLAGS := []string{"-framework", "CoreFoundation"}
298         lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
299         tmpDir := t.TempDir()
300
301         ar := filepath.Join(tmpDir, "lib.a")
302         cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
303         cmd.Env = append(os.Environ(),
304                 "CGO_ENABLED=1",
305                 "GOOS=ios",
306                 "GOARCH=arm64",
307                 "CC="+strings.Join(CC, " "),
308                 "CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
309                 "CGO_LDFLAGS="+strings.Join(CGO_LDFLAGS, " "),
310         )
311         if out, err := cmd.CombinedOutput(); err != nil {
312                 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
313         }
314
315         link := exec.Command(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         if out, err := link.CombinedOutput(); err != nil {
320                 t.Fatalf("%v: %v:\n%s", link.Args, err, out)
321         }
322 }
323
324 var testXFlagSrc = `
325 package main
326 var X = "hello"
327 var Z = [99999]int{99998:12345} // make it large enough to be mmaped
328 func main() { println(X) }
329 `
330
331 func TestXFlag(t *testing.T) {
332         testenv.MustHaveGoBuild(t)
333
334         t.Parallel()
335
336         tmpdir := t.TempDir()
337
338         src := filepath.Join(tmpdir, "main.go")
339         err := os.WriteFile(src, []byte(testXFlagSrc), 0666)
340         if err != nil {
341                 t.Fatal(err)
342         }
343
344         cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
345         if out, err := cmd.CombinedOutput(); err != nil {
346                 t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
347         }
348 }
349
350 var testMachOBuildVersionSrc = `
351 package main
352 func main() { }
353 `
354
355 func TestMachOBuildVersion(t *testing.T) {
356         testenv.MustHaveGoBuild(t)
357
358         t.Parallel()
359
360         tmpdir := t.TempDir()
361
362         src := filepath.Join(tmpdir, "main.go")
363         err := os.WriteFile(src, []byte(testMachOBuildVersionSrc), 0666)
364         if err != nil {
365                 t.Fatal(err)
366         }
367
368         exe := filepath.Join(tmpdir, "main")
369         cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
370         cmd.Env = append(os.Environ(),
371                 "CGO_ENABLED=0",
372                 "GOOS=darwin",
373                 "GOARCH=amd64",
374         )
375         if out, err := cmd.CombinedOutput(); err != nil {
376                 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
377         }
378         exef, err := os.Open(exe)
379         if err != nil {
380                 t.Fatal(err)
381         }
382         defer exef.Close()
383         exem, err := macho.NewFile(exef)
384         if err != nil {
385                 t.Fatal(err)
386         }
387         found := false
388         const LC_BUILD_VERSION = 0x32
389         checkMin := func(ver uint32) {
390                 major, minor := (ver>>16)&0xff, (ver>>8)&0xff
391                 if major != 10 || minor < 9 {
392                         t.Errorf("LC_BUILD_VERSION version %d.%d < 10.9", major, minor)
393                 }
394         }
395         for _, cmd := range exem.Loads {
396                 raw := cmd.Raw()
397                 type_ := exem.ByteOrder.Uint32(raw)
398                 if type_ != LC_BUILD_VERSION {
399                         continue
400                 }
401                 osVer := exem.ByteOrder.Uint32(raw[12:])
402                 checkMin(osVer)
403                 sdkVer := exem.ByteOrder.Uint32(raw[16:])
404                 checkMin(sdkVer)
405                 found = true
406                 break
407         }
408         if !found {
409                 t.Errorf("no LC_BUILD_VERSION load command found")
410         }
411 }
412
413 const Issue34788src = `
414
415 package blah
416
417 func Blah(i int) int {
418         a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
419         return a[i&7]
420 }
421 `
422
423 func TestIssue34788Android386TLSSequence(t *testing.T) {
424         testenv.MustHaveGoBuild(t)
425
426         // This is a cross-compilation test, so it doesn't make
427         // sense to run it on every GOOS/GOARCH combination. Limit
428         // the test to amd64 + darwin/linux.
429         if runtime.GOARCH != "amd64" ||
430                 (runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
431                 t.Skip("skipping on non-{linux,darwin}/amd64 platform")
432         }
433
434         t.Parallel()
435
436         tmpdir := t.TempDir()
437
438         src := filepath.Join(tmpdir, "blah.go")
439         err := os.WriteFile(src, []byte(Issue34788src), 0666)
440         if err != nil {
441                 t.Fatal(err)
442         }
443
444         obj := filepath.Join(tmpdir, "blah.o")
445         cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src)
446         cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
447         if out, err := cmd.CombinedOutput(); err != nil {
448                 t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
449         }
450
451         // Run objdump on the resulting object.
452         cmd = exec.Command(testenv.GoToolPath(t), "tool", "objdump", obj)
453         out, oerr := cmd.CombinedOutput()
454         if oerr != nil {
455                 t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
456         }
457
458         // Sift through the output; we should not be seeing any R_TLS_LE relocs.
459         scanner := bufio.NewScanner(bytes.NewReader(out))
460         for scanner.Scan() {
461                 line := scanner.Text()
462                 if strings.Contains(line, "R_TLS_LE") {
463                         t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
464                 }
465         }
466 }
467
468 const testStrictDupGoSrc = `
469 package main
470 func f()
471 func main() { f() }
472 `
473
474 const testStrictDupAsmSrc1 = `
475 #include "textflag.h"
476 TEXT    ·f(SB), NOSPLIT|DUPOK, $0-0
477         RET
478 `
479
480 const testStrictDupAsmSrc2 = `
481 #include "textflag.h"
482 TEXT    ·f(SB), NOSPLIT|DUPOK, $0-0
483         JMP     0(PC)
484 `
485
486 const testStrictDupAsmSrc3 = `
487 #include "textflag.h"
488 GLOBL ·rcon(SB), RODATA|DUPOK, $64
489 `
490
491 const testStrictDupAsmSrc4 = `
492 #include "textflag.h"
493 GLOBL ·rcon(SB), RODATA|DUPOK, $32
494 `
495
496 func TestStrictDup(t *testing.T) {
497         // Check that -strictdups flag works.
498         testenv.MustHaveGoBuild(t)
499
500         asmfiles := []struct {
501                 fname   string
502                 payload string
503         }{
504                 {"a", testStrictDupAsmSrc1},
505                 {"b", testStrictDupAsmSrc2},
506                 {"c", testStrictDupAsmSrc3},
507                 {"d", testStrictDupAsmSrc4},
508         }
509
510         t.Parallel()
511
512         tmpdir := t.TempDir()
513
514         src := filepath.Join(tmpdir, "x.go")
515         err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
516         if err != nil {
517                 t.Fatal(err)
518         }
519         for _, af := range asmfiles {
520                 src = filepath.Join(tmpdir, af.fname+".s")
521                 err = os.WriteFile(src, []byte(af.payload), 0666)
522                 if err != nil {
523                         t.Fatal(err)
524                 }
525         }
526         src = filepath.Join(tmpdir, "go.mod")
527         err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666)
528         if err != nil {
529                 t.Fatal(err)
530         }
531
532         cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
533         cmd.Dir = tmpdir
534         out, err := cmd.CombinedOutput()
535         if err != nil {
536                 t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
537         }
538         if !bytes.Contains(out, []byte("mismatched payload")) {
539                 t.Errorf("unexpected output:\n%s", out)
540         }
541
542         cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
543         cmd.Dir = tmpdir
544         out, err = cmd.CombinedOutput()
545         if err == nil {
546                 t.Errorf("linking with -strictdups=2 did not fail")
547         }
548         // NB: on amd64 we get the 'new length' error, on arm64 the 'different
549         // contents' error.
550         if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
551                 bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
552                 !bytes.Contains(out, []byte("mismatched payload: different sizes")) {
553                 t.Errorf("unexpected output:\n%s", out)
554         }
555 }
556
557 const testFuncAlignSrc = `
558 package main
559 import (
560         "fmt"
561 )
562 func alignPc()
563 var alignPcFnAddr uintptr
564
565 func main() {
566         if alignPcFnAddr % 512 != 0 {
567                 fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)
568         } else {
569                 fmt.Printf("PASS")
570         }
571 }
572 `
573
574 const testFuncAlignAsmSrc = `
575 #include "textflag.h"
576
577 TEXT    ·alignPc(SB),NOSPLIT, $0-0
578         MOVD    $2, R0
579         PCALIGN $512
580         MOVD    $3, R1
581         RET
582
583 GLOBL   ·alignPcFnAddr(SB),RODATA,$8
584 DATA    ·alignPcFnAddr(SB)/8,$·alignPc(SB)
585 `
586
587 // TestFuncAlign verifies that the address of a function can be aligned
588 // with a specific value on arm64.
589 func TestFuncAlign(t *testing.T) {
590         if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
591                 t.Skip("skipping on non-linux/arm64 platform")
592         }
593         testenv.MustHaveGoBuild(t)
594
595         t.Parallel()
596
597         tmpdir := t.TempDir()
598
599         src := filepath.Join(tmpdir, "go.mod")
600         err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
601         if err != nil {
602                 t.Fatal(err)
603         }
604         src = filepath.Join(tmpdir, "falign.go")
605         err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666)
606         if err != nil {
607                 t.Fatal(err)
608         }
609         src = filepath.Join(tmpdir, "falign.s")
610         err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
611         if err != nil {
612                 t.Fatal(err)
613         }
614
615         // Build and run with old object file format.
616         cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "falign")
617         cmd.Dir = tmpdir
618         out, err := cmd.CombinedOutput()
619         if err != nil {
620                 t.Errorf("build failed: %v", err)
621         }
622         cmd = exec.Command(tmpdir + "/falign")
623         out, err = cmd.CombinedOutput()
624         if err != nil {
625                 t.Errorf("failed to run with err %v, output: %s", err, out)
626         }
627         if string(out) != "PASS" {
628                 t.Errorf("unexpected output: %s\n", out)
629         }
630 }
631
632 const testTrampSrc = `
633 package main
634 import "fmt"
635 func main() {
636         fmt.Println("hello")
637
638         defer func(){
639                 if e := recover(); e == nil {
640                         panic("did not panic")
641                 }
642         }()
643         f1()
644 }
645
646 // Test deferreturn trampolines. See issue #39049.
647 func f1() { defer f2() }
648 func f2() { panic("XXX") }
649 `
650
651 func TestTrampoline(t *testing.T) {
652         // Test that trampoline insertion works as expected.
653         // For stress test, we set -debugtramp=2 flag, which sets a very low
654         // threshold for trampoline generation, and essentially all cross-package
655         // calls will use trampolines.
656         buildmodes := []string{"default"}
657         switch runtime.GOARCH {
658         case "arm", "arm64", "ppc64":
659         case "ppc64le":
660                 // Trampolines are generated differently when internal linking PIE, test them too.
661                 buildmodes = append(buildmodes, "pie")
662         default:
663                 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
664         }
665
666         testenv.MustHaveGoBuild(t)
667
668         t.Parallel()
669
670         tmpdir := t.TempDir()
671
672         src := filepath.Join(tmpdir, "hello.go")
673         err := os.WriteFile(src, []byte(testTrampSrc), 0666)
674         if err != nil {
675                 t.Fatal(err)
676         }
677         exe := filepath.Join(tmpdir, "hello.exe")
678
679         for _, mode := range buildmodes {
680                 cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
681                 out, err := cmd.CombinedOutput()
682                 if err != nil {
683                         t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
684                 }
685                 cmd = exec.Command(exe)
686                 out, err = cmd.CombinedOutput()
687                 if err != nil {
688                         t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
689                 }
690                 if string(out) != "hello\n" {
691                         t.Errorf("unexpected output (%s):\n%s", mode, out)
692                 }
693         }
694 }
695
696 const testTrampCgoSrc = `
697 package main
698
699 // #include <stdio.h>
700 // void CHello() { printf("hello\n"); fflush(stdout); }
701 import "C"
702
703 func main() {
704         C.CHello()
705 }
706 `
707
708 func TestTrampolineCgo(t *testing.T) {
709         // Test that trampoline insertion works for cgo code.
710         // For stress test, we set -debugtramp=2 flag, which sets a very low
711         // threshold for trampoline generation, and essentially all cross-package
712         // calls will use trampolines.
713         buildmodes := []string{"default"}
714         switch runtime.GOARCH {
715         case "arm", "arm64", "ppc64":
716         case "ppc64le":
717                 // Trampolines are generated differently when internal linking PIE, test them too.
718                 buildmodes = append(buildmodes, "pie")
719         default:
720                 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
721         }
722
723         testenv.MustHaveGoBuild(t)
724         testenv.MustHaveCGO(t)
725
726         t.Parallel()
727
728         tmpdir := t.TempDir()
729
730         src := filepath.Join(tmpdir, "hello.go")
731         err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
732         if err != nil {
733                 t.Fatal(err)
734         }
735         exe := filepath.Join(tmpdir, "hello.exe")
736
737         for _, mode := range buildmodes {
738                 cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
739                 out, err := cmd.CombinedOutput()
740                 if err != nil {
741                         t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
742                 }
743                 cmd = exec.Command(exe)
744                 out, err = cmd.CombinedOutput()
745                 if err != nil {
746                         t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
747                 }
748                 if string(out) != "hello\n" && string(out) != "hello\r\n" {
749                         t.Errorf("unexpected output (%s):\n%s", mode, out)
750                 }
751
752                 // Test internal linking mode.
753
754                 if runtime.GOARCH == "ppc64" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() {
755                         return // internal linking cgo is not supported
756                 }
757                 cmd = exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
758                 out, err = cmd.CombinedOutput()
759                 if err != nil {
760                         t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
761                 }
762                 cmd = exec.Command(exe)
763                 out, err = cmd.CombinedOutput()
764                 if err != nil {
765                         t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
766                 }
767                 if string(out) != "hello\n" && string(out) != "hello\r\n" {
768                         t.Errorf("unexpected output (%s):\n%s", mode, out)
769                 }
770         }
771 }
772
773 func TestIndexMismatch(t *testing.T) {
774         // Test that index mismatch will cause a link-time error (not run-time error).
775         // This shouldn't happen with "go build". We invoke the compiler and the linker
776         // manually, and try to "trick" the linker with an inconsistent object file.
777         testenv.MustHaveGoBuild(t)
778
779         t.Parallel()
780
781         tmpdir := t.TempDir()
782
783         aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
784         bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
785         mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
786         aObj := filepath.Join(tmpdir, "a.o")
787         mObj := filepath.Join(tmpdir, "main.o")
788         exe := filepath.Join(tmpdir, "main.exe")
789
790         importcfgFile := filepath.Join(tmpdir, "stdlib.importcfg")
791         testenv.WriteImportcfg(t, importcfgFile, nil)
792         importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
793         testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj})
794
795         // Build a program with main package importing package a.
796         cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
797         t.Log(cmd)
798         out, err := cmd.CombinedOutput()
799         if err != nil {
800                 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
801         }
802         cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
803         t.Log(cmd)
804         out, err = cmd.CombinedOutput()
805         if err != nil {
806                 t.Fatalf("compiling main.go failed: %v\n%s", err, out)
807         }
808         cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
809         t.Log(cmd)
810         out, err = cmd.CombinedOutput()
811         if err != nil {
812                 t.Errorf("linking failed: %v\n%s", err, out)
813         }
814
815         // Now, overwrite a.o with the object of b.go. This should
816         // result in an index mismatch.
817         cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
818         t.Log(cmd)
819         out, err = cmd.CombinedOutput()
820         if err != nil {
821                 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
822         }
823         cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
824         t.Log(cmd)
825         out, err = cmd.CombinedOutput()
826         if err == nil {
827                 t.Fatalf("linking didn't fail")
828         }
829         if !bytes.Contains(out, []byte("fingerprint mismatch")) {
830                 t.Errorf("did not see expected error message. out:\n%s", out)
831         }
832 }
833
834 func TestPErsrcBinutils(t *testing.T) {
835         // Test that PE rsrc section is handled correctly (issue 39658).
836         testenv.MustHaveGoBuild(t)
837
838         if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
839                 // This test is limited to amd64 and 386, because binutils is limited as such
840                 t.Skipf("this is only for windows/amd64 and windows/386")
841         }
842
843         t.Parallel()
844
845         tmpdir := t.TempDir()
846
847         pkgdir := filepath.Join("testdata", "pe-binutils")
848         exe := filepath.Join(tmpdir, "a.exe")
849         cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
850         cmd.Dir = pkgdir
851         // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
852         out, err := cmd.CombinedOutput()
853         if err != nil {
854                 t.Fatalf("building failed: %v, output:\n%s", err, out)
855         }
856
857         // Check that the binary contains the rsrc data
858         b, err := os.ReadFile(exe)
859         if err != nil {
860                 t.Fatalf("reading output failed: %v", err)
861         }
862         if !bytes.Contains(b, []byte("Hello Gophers!")) {
863                 t.Fatalf("binary does not contain expected content")
864         }
865 }
866
867 func TestPErsrcLLVM(t *testing.T) {
868         // Test that PE rsrc section is handled correctly (issue 39658).
869         testenv.MustHaveGoBuild(t)
870
871         if runtime.GOOS != "windows" {
872                 t.Skipf("this is a windows-only test")
873         }
874
875         t.Parallel()
876
877         tmpdir := t.TempDir()
878
879         pkgdir := filepath.Join("testdata", "pe-llvm")
880         exe := filepath.Join(tmpdir, "a.exe")
881         cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
882         cmd.Dir = pkgdir
883         // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
884         out, err := cmd.CombinedOutput()
885         if err != nil {
886                 t.Fatalf("building failed: %v, output:\n%s", err, out)
887         }
888
889         // Check that the binary contains the rsrc data
890         b, err := os.ReadFile(exe)
891         if err != nil {
892                 t.Fatalf("reading output failed: %v", err)
893         }
894         if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
895                 t.Fatalf("binary does not contain expected content")
896         }
897 }
898
899 func TestContentAddressableSymbols(t *testing.T) {
900         // Test that the linker handles content-addressable symbols correctly.
901         testenv.MustHaveGoBuild(t)
902
903         t.Parallel()
904
905         src := filepath.Join("testdata", "testHashedSyms", "p.go")
906         cmd := exec.Command(testenv.GoToolPath(t), "run", src)
907         out, err := cmd.CombinedOutput()
908         if err != nil {
909                 t.Errorf("command %s failed: %v\n%s", cmd, err, out)
910         }
911 }
912
913 func TestReadOnly(t *testing.T) {
914         // Test that read-only data is indeed read-only.
915         testenv.MustHaveGoBuild(t)
916
917         t.Parallel()
918
919         src := filepath.Join("testdata", "testRO", "x.go")
920         cmd := exec.Command(testenv.GoToolPath(t), "run", src)
921         out, err := cmd.CombinedOutput()
922         if err == nil {
923                 t.Errorf("running test program did not fail. output:\n%s", out)
924         }
925 }
926
927 const testIssue38554Src = `
928 package main
929
930 type T [10<<20]byte
931
932 //go:noinline
933 func f() T {
934         return T{} // compiler will make a large stmp symbol, but not used.
935 }
936
937 func main() {
938         x := f()
939         println(x[1])
940 }
941 `
942
943 func TestIssue38554(t *testing.T) {
944         testenv.MustHaveGoBuild(t)
945
946         t.Parallel()
947
948         tmpdir := t.TempDir()
949
950         src := filepath.Join(tmpdir, "x.go")
951         err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
952         if err != nil {
953                 t.Fatalf("failed to write source file: %v", err)
954         }
955         exe := filepath.Join(tmpdir, "x.exe")
956         cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src)
957         out, err := cmd.CombinedOutput()
958         if err != nil {
959                 t.Fatalf("build failed: %v\n%s", err, out)
960         }
961
962         fi, err := os.Stat(exe)
963         if err != nil {
964                 t.Fatalf("failed to stat output file: %v", err)
965         }
966
967         // The test program is not much different from a helloworld, which is
968         // typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
969         // it will be over 10 MB.
970         const want = 5 << 20
971         if got := fi.Size(); got > want {
972                 t.Errorf("binary too big: got %d, want < %d", got, want)
973         }
974 }
975
976 const testIssue42396src = `
977 package main
978
979 //go:noinline
980 //go:nosplit
981 func callee(x int) {
982 }
983
984 func main() {
985         callee(9)
986 }
987 `
988
989 func TestIssue42396(t *testing.T) {
990         testenv.MustHaveGoBuild(t)
991
992         if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
993                 t.Skip("no race detector support")
994         }
995
996         t.Parallel()
997
998         tmpdir := t.TempDir()
999
1000         src := filepath.Join(tmpdir, "main.go")
1001         err := os.WriteFile(src, []byte(testIssue42396src), 0666)
1002         if err != nil {
1003                 t.Fatalf("failed to write source file: %v", err)
1004         }
1005         exe := filepath.Join(tmpdir, "main.exe")
1006         cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
1007         out, err := cmd.CombinedOutput()
1008         if err == nil {
1009                 t.Fatalf("build unexpectedly succeeded")
1010         }
1011
1012         // Check to make sure that we see a reasonable error message
1013         // and not a panic.
1014         if strings.Contains(string(out), "panic:") {
1015                 t.Fatalf("build should not fail with panic:\n%s", out)
1016         }
1017         const want = "reference to undefined builtin"
1018         if !strings.Contains(string(out), want) {
1019                 t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
1020         }
1021 }
1022
1023 const testLargeRelocSrc = `
1024 package main
1025
1026 var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
1027
1028 var addr = [...]*byte{
1029         &x[1<<23-1],
1030         &x[1<<23],
1031         &x[1<<23+1],
1032         &x[1<<24-1],
1033         &x[1<<24],
1034         &x[1<<24+1],
1035 }
1036
1037 func main() {
1038         // check relocations in instructions
1039         check(x[1<<23-1], 0)
1040         check(x[1<<23], 23)
1041         check(x[1<<23+1], 0)
1042         check(x[1<<24-1], 0)
1043         check(x[1<<24], 24)
1044         check(x[1<<24+1], 0)
1045
1046         // check absolute address relocations in data
1047         check(*addr[0], 0)
1048         check(*addr[1], 23)
1049         check(*addr[2], 0)
1050         check(*addr[3], 0)
1051         check(*addr[4], 24)
1052         check(*addr[5], 0)
1053 }
1054
1055 func check(x, y byte) {
1056         if x != y {
1057                 panic("FAIL")
1058         }
1059 }
1060 `
1061
1062 func TestLargeReloc(t *testing.T) {
1063         // Test that large relocation addend is handled correctly.
1064         // In particular, on darwin/arm64 when external linking,
1065         // Mach-O relocation has only 24-bit addend. See issue #42738.
1066         testenv.MustHaveGoBuild(t)
1067         t.Parallel()
1068
1069         tmpdir := t.TempDir()
1070
1071         src := filepath.Join(tmpdir, "x.go")
1072         err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
1073         if err != nil {
1074                 t.Fatalf("failed to write source file: %v", err)
1075         }
1076         cmd := exec.Command(testenv.GoToolPath(t), "run", src)
1077         out, err := cmd.CombinedOutput()
1078         if err != nil {
1079                 t.Errorf("build failed: %v. output:\n%s", err, out)
1080         }
1081
1082         if testenv.HasCGO() { // currently all targets that support cgo can external link
1083                 cmd = exec.Command(testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src)
1084                 out, err = cmd.CombinedOutput()
1085                 if err != nil {
1086                         t.Fatalf("build failed: %v. output:\n%s", err, out)
1087                 }
1088         }
1089 }
1090
1091 func TestUnlinkableObj(t *testing.T) {
1092         // Test that the linker emits an error with unlinkable object.
1093         testenv.MustHaveGoBuild(t)
1094         t.Parallel()
1095
1096         if buildcfg.Experiment.Unified {
1097                 t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
1098         }
1099
1100         tmpdir := t.TempDir()
1101
1102         xSrc := filepath.Join(tmpdir, "x.go")
1103         pSrc := filepath.Join(tmpdir, "p.go")
1104         xObj := filepath.Join(tmpdir, "x.o")
1105         pObj := filepath.Join(tmpdir, "p.o")
1106         exe := filepath.Join(tmpdir, "x.exe")
1107         err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
1108         if err != nil {
1109                 t.Fatalf("failed to write source file: %v", err)
1110         }
1111         err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
1112         if err != nil {
1113                 t.Fatalf("failed to write source file: %v", err)
1114         }
1115         cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", pObj, pSrc) // without -p
1116         out, err := cmd.CombinedOutput()
1117         if err != nil {
1118                 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1119         }
1120         cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", tmpdir, "-p=main", "-o", xObj, xSrc)
1121         out, err = cmd.CombinedOutput()
1122         if err != nil {
1123                 t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
1124         }
1125         cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, xObj)
1126         out, err = cmd.CombinedOutput()
1127         if err == nil {
1128                 t.Fatalf("link did not fail")
1129         }
1130         if !bytes.Contains(out, []byte("unlinkable object")) {
1131                 t.Errorf("did not see expected error message. out:\n%s", out)
1132         }
1133
1134         // It is okay to omit -p for (only) main package.
1135         cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=p", "-o", pObj, pSrc)
1136         out, err = cmd.CombinedOutput()
1137         if err != nil {
1138                 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1139         }
1140         cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", tmpdir, "-o", xObj, xSrc) // without -p
1141         out, err = cmd.CombinedOutput()
1142         if err != nil {
1143                 t.Fatalf("compile failed: %v. output:\n%s", err, out)
1144         }
1145         cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, xObj)
1146         out, err = cmd.CombinedOutput()
1147         if err != nil {
1148                 t.Errorf("link failed: %v. output:\n%s", err, out)
1149         }
1150 }