Source file
src/cmd/link/link_test.go
1
2
3
4
5 package main
6
7 import (
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 "strconv"
20 "strings"
21 "testing"
22
23 imacho "cmd/internal/macho"
24 "cmd/internal/objfile"
25 "cmd/internal/sys"
26 )
27
28 var AuthorPaidByTheColumnInch struct {
29 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."`
30
31 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."`
32
33 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."`
34
35 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."`
36 }
37
38 func TestLargeSymName(t *testing.T) {
39
40
41
42 _ = AuthorPaidByTheColumnInch
43 }
44
45 func TestIssue21703(t *testing.T) {
46 t.Parallel()
47
48 testenv.MustHaveGoBuild(t)
49
50
51 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
52
53 const source = `
54 package main
55 const X = "\n!\n"
56 func main() {}
57 `
58
59 tmpdir := t.TempDir()
60 main := filepath.Join(tmpdir, "main.go")
61
62 err := os.WriteFile(main, []byte(source), 0666)
63 if err != nil {
64 t.Fatalf("failed to write main.go: %v\n", err)
65 }
66
67 importcfgfile := filepath.Join(tmpdir, "importcfg")
68 testenv.WriteImportcfg(t, importcfgfile, nil, main)
69
70 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
71 cmd.Dir = tmpdir
72 out, err := cmd.CombinedOutput()
73 if err != nil {
74 t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
75 }
76
77 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "main.o")
78 cmd.Dir = tmpdir
79 out, err = cmd.CombinedOutput()
80 if err != nil {
81 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
82 testenv.SkipFlaky(t, 58806)
83 }
84 t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
85 }
86 }
87
88
89
90
91
92 func TestIssue28429(t *testing.T) {
93 t.Parallel()
94
95 testenv.MustHaveGoBuild(t)
96
97
98 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
99
100 tmpdir := t.TempDir()
101
102 write := func(name, content string) {
103 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
104 if err != nil {
105 t.Fatal(err)
106 }
107 }
108
109 runGo := func(args ...string) {
110 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
111 cmd.Dir = tmpdir
112 out, err := cmd.CombinedOutput()
113 if err != nil {
114 if len(args) >= 2 && args[1] == "link" && runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
115 testenv.SkipFlaky(t, 58806)
116 }
117 t.Fatalf("'go %s' failed: %v, output: %s",
118 strings.Join(args, " "), err, out)
119 }
120 }
121
122
123 write("main.go", "package main; func main() {}")
124 importcfgfile := filepath.Join(tmpdir, "importcfg")
125 testenv.WriteImportcfg(t, importcfgfile, nil, filepath.Join(tmpdir, "main.go"))
126 runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
127 runGo("tool", "pack", "c", "main.a", "main.o")
128
129
130
131 write(".facts", "this is not an object file")
132 runGo("tool", "pack", "r", "main.a", ".facts")
133
134
135
136 runGo("tool", "link", "-importcfg="+importcfgfile, "main.a")
137 }
138
139 func TestUnresolved(t *testing.T) {
140 testenv.MustHaveGoBuild(t)
141
142 t.Parallel()
143
144 tmpdir := t.TempDir()
145
146 write := func(name, content string) {
147 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
148 if err != nil {
149 t.Fatal(err)
150 }
151 }
152
153
154
155
156
157
158 write("go.mod", "module testunresolved\n")
159 write("main.go", `package main
160
161 func main() {
162 x()
163 }
164
165 func x()
166 `)
167 write("main.s", `
168 TEXT ·x(SB),0,$0
169 MOVD zero<>(SB), AX
170 MOVD zero(SB), AX
171 MOVD ·zero(SB), AX
172 RET
173 `)
174 cmd := testenv.Command(t, testenv.GoToolPath(t), "build")
175 cmd.Dir = tmpdir
176 cmd.Env = append(os.Environ(),
177 "GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
178 out, err := cmd.CombinedOutput()
179 if err == nil {
180 t.Fatalf("expected build to fail, but it succeeded")
181 }
182 out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
183 got := string(out)
184 want := `main.x: relocation target zero not defined
185 main.x: relocation target zero not defined
186 main.x: relocation target main.zero not defined
187 `
188 if want != got {
189 t.Fatalf("want:\n%sgot:\n%s", want, got)
190 }
191 }
192
193 func TestIssue33979(t *testing.T) {
194 testenv.MustHaveGoBuild(t)
195 testenv.MustHaveCGO(t)
196
197
198 testenv.MustInternalLink(t, testenv.SpecialBuildTypes{Cgo: true})
199
200 t.Parallel()
201
202 tmpdir := t.TempDir()
203
204 write := func(name, content string) {
205 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
206 if err != nil {
207 t.Fatal(err)
208 }
209 }
210
211 run := func(name string, args ...string) string {
212 cmd := testenv.Command(t, name, args...)
213 cmd.Dir = tmpdir
214 out, err := cmd.CombinedOutput()
215 if err != nil {
216 t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)
217 }
218 return string(out)
219 }
220 runGo := func(args ...string) string {
221 return run(testenv.GoToolPath(t), args...)
222 }
223
224
225
226
227
228
229 write("main.go", `package main
230 func main() {
231 x()
232 }
233 func x()
234 `)
235
236 write("x.s", `
237 TEXT ·x(SB),0,$0
238 CALL foo(SB)
239 RET
240 `)
241 write("x.c", `
242 void undefined();
243
244 void foo() {
245 undefined();
246 }
247 `)
248
249 cc := strings.TrimSpace(runGo("env", "CC"))
250 cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))
251
252 importcfgfile := filepath.Join(tmpdir, "importcfg")
253 testenv.WriteImportcfg(t, importcfgfile, nil, "runtime")
254
255
256 runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s")
257 runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go")
258 runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s")
259 run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)
260 runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")
261
262
263 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-linkmode=internal", "x.a")
264 cmd.Dir = tmpdir
265 out, err := cmd.CombinedOutput()
266 if err == nil {
267 t.Fatalf("expected link to fail, but it succeeded")
268 }
269 re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)
270 if !re.Match(out) {
271 t.Fatalf("got:\n%q\nwant:\n%s", out, re)
272 }
273 }
274
275 func TestBuildForTvOS(t *testing.T) {
276 testenv.MustHaveCGO(t)
277 testenv.MustHaveGoBuild(t)
278
279
280 if runtime.GOOS != "darwin" {
281 t.Skip("skipping on non-darwin platform")
282 }
283 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
284 t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
285 }
286 if err := testenv.Command(t, "xcrun", "--help").Run(); err != nil {
287 t.Skipf("error running xcrun, required for iOS cross build: %v", err)
288 }
289
290 t.Parallel()
291
292 sdkPath, err := testenv.Command(t, "xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
293 if err != nil {
294 t.Skip("failed to locate appletvos SDK, skipping")
295 }
296 CC := []string{
297 "clang",
298 "-arch",
299 "arm64",
300 "-isysroot", strings.TrimSpace(string(sdkPath)),
301 "-mtvos-version-min=12.0",
302 "-fembed-bitcode",
303 }
304 CGO_LDFLAGS := []string{"-framework", "CoreFoundation"}
305 lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
306 tmpDir := t.TempDir()
307
308 ar := filepath.Join(tmpDir, "lib.a")
309 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
310 env := []string{
311 "CGO_ENABLED=1",
312 "GOOS=ios",
313 "GOARCH=arm64",
314 "CC=" + strings.Join(CC, " "),
315 "CGO_CFLAGS=",
316 "CGO_LDFLAGS=" + strings.Join(CGO_LDFLAGS, " "),
317 }
318 cmd.Env = append(os.Environ(), env...)
319 t.Logf("%q %v", env, cmd)
320 if out, err := cmd.CombinedOutput(); err != nil {
321 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
322 }
323
324 link := testenv.Command(t, CC[0], CC[1:]...)
325 link.Args = append(link.Args, CGO_LDFLAGS...)
326 link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out"))
327 link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
328 t.Log(link)
329 if out, err := link.CombinedOutput(); err != nil {
330 t.Fatalf("%v: %v:\n%s", link.Args, err, out)
331 }
332 }
333
334 var testXFlagSrc = `
335 package main
336 var X = "hello"
337 var Z = [99999]int{99998:12345} // make it large enough to be mmaped
338 func main() { println(X) }
339 `
340
341 func TestXFlag(t *testing.T) {
342 testenv.MustHaveGoBuild(t)
343
344 t.Parallel()
345
346 tmpdir := t.TempDir()
347
348 src := filepath.Join(tmpdir, "main.go")
349 err := os.WriteFile(src, []byte(testXFlagSrc), 0666)
350 if err != nil {
351 t.Fatal(err)
352 }
353
354 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
355 if out, err := cmd.CombinedOutput(); err != nil {
356 t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
357 }
358 }
359
360 var trivialSrc = `
361 package main
362 func main() { }
363 `
364
365 func TestMachOBuildVersion(t *testing.T) {
366 testenv.MustHaveGoBuild(t)
367
368 t.Parallel()
369
370 tmpdir := t.TempDir()
371
372 src := filepath.Join(tmpdir, "main.go")
373 err := os.WriteFile(src, []byte(trivialSrc), 0666)
374 if err != nil {
375 t.Fatal(err)
376 }
377
378 exe := filepath.Join(tmpdir, "main")
379 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
380 cmd.Env = append(os.Environ(),
381 "CGO_ENABLED=0",
382 "GOOS=darwin",
383 "GOARCH=amd64",
384 )
385 if out, err := cmd.CombinedOutput(); err != nil {
386 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
387 }
388 exef, err := os.Open(exe)
389 if err != nil {
390 t.Fatal(err)
391 }
392 defer exef.Close()
393 exem, err := macho.NewFile(exef)
394 if err != nil {
395 t.Fatal(err)
396 }
397 found := false
398 checkMin := func(ver uint32) {
399 major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
400 if major < 12 {
401 t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 12.0.0", major, minor, patch)
402 }
403 }
404 for _, cmd := range exem.Loads {
405 raw := cmd.Raw()
406 type_ := exem.ByteOrder.Uint32(raw)
407 if type_ != imacho.LC_BUILD_VERSION {
408 continue
409 }
410 osVer := exem.ByteOrder.Uint32(raw[12:])
411 checkMin(osVer)
412 sdkVer := exem.ByteOrder.Uint32(raw[16:])
413 checkMin(sdkVer)
414 found = true
415 break
416 }
417 if !found {
418 t.Errorf("no LC_BUILD_VERSION load command found")
419 }
420 }
421
422 func TestMachOUUID(t *testing.T) {
423 testenv.MustHaveGoBuild(t)
424 if runtime.GOOS != "darwin" {
425 t.Skip("this is only for darwin")
426 }
427
428 t.Parallel()
429
430 tmpdir := t.TempDir()
431
432 src := filepath.Join(tmpdir, "main.go")
433 err := os.WriteFile(src, []byte(trivialSrc), 0666)
434 if err != nil {
435 t.Fatal(err)
436 }
437
438 extractUUID := func(exe string) string {
439 exem, err := macho.Open(exe)
440 if err != nil {
441 t.Fatal(err)
442 }
443 defer exem.Close()
444 for _, cmd := range exem.Loads {
445 raw := cmd.Raw()
446 type_ := exem.ByteOrder.Uint32(raw)
447 if type_ != imacho.LC_UUID {
448 continue
449 }
450 return string(raw[8:24])
451 }
452 return ""
453 }
454
455 tests := []struct{ name, ldflags, expect string }{
456 {"default", "", "gobuildid"},
457 {"gobuildid", "-B=gobuildid", "gobuildid"},
458 {"specific", "-B=0x0123456789ABCDEF0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
459 {"none", "-B=none", ""},
460 }
461 if testenv.HasCGO() {
462 for _, test := range tests {
463 t1 := test
464 t1.name += "_external"
465 t1.ldflags += " -linkmode=external"
466 tests = append(tests, t1)
467 }
468 }
469 for _, test := range tests {
470 t.Run(test.name, func(t *testing.T) {
471 exe := filepath.Join(tmpdir, test.name)
472 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags="+test.ldflags, "-o", exe, src)
473 if out, err := cmd.CombinedOutput(); err != nil {
474 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
475 }
476 uuid := extractUUID(exe)
477 if test.expect == "gobuildid" {
478
479
480 if uuid == "" {
481 t.Fatal("expect nonempty UUID, got empty")
482 }
483
484 if uuid[6]>>4 != 3 {
485 t.Errorf("expect v3 UUID, got %X (version %d)", uuid, uuid[6]>>4)
486 }
487 } else if uuid != test.expect {
488 t.Errorf("UUID mismatch: got %X, want %X", uuid, test.expect)
489 }
490 })
491 }
492 }
493
494 const Issue34788src = `
495
496 package blah
497
498 func Blah(i int) int {
499 a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
500 return a[i&7]
501 }
502 `
503
504 func TestIssue34788Android386TLSSequence(t *testing.T) {
505 testenv.MustHaveGoBuild(t)
506
507
508
509
510 if runtime.GOARCH != "amd64" ||
511 (runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
512 t.Skip("skipping on non-{linux,darwin}/amd64 platform")
513 }
514
515 t.Parallel()
516
517 tmpdir := t.TempDir()
518
519 src := filepath.Join(tmpdir, "blah.go")
520 err := os.WriteFile(src, []byte(Issue34788src), 0666)
521 if err != nil {
522 t.Fatal(err)
523 }
524
525 obj := filepath.Join(tmpdir, "blah.o")
526 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src)
527 cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
528 if out, err := cmd.CombinedOutput(); err != nil {
529 t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
530 }
531
532
533 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "objdump", obj)
534 out, oerr := cmd.CombinedOutput()
535 if oerr != nil {
536 t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
537 }
538
539
540 scanner := bufio.NewScanner(bytes.NewReader(out))
541 for scanner.Scan() {
542 line := scanner.Text()
543 if strings.Contains(line, "R_TLS_LE") {
544 t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
545 }
546 }
547 }
548
549 const testStrictDupGoSrc = `
550 package main
551 func f()
552 func main() { f() }
553 `
554
555 const testStrictDupAsmSrc1 = `
556 #include "textflag.h"
557 TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
558 RET
559 `
560
561 const testStrictDupAsmSrc2 = `
562 #include "textflag.h"
563 TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
564 JMP 0(PC)
565 `
566
567 const testStrictDupAsmSrc3 = `
568 #include "textflag.h"
569 GLOBL ·rcon(SB), RODATA|DUPOK, $64
570 `
571
572 const testStrictDupAsmSrc4 = `
573 #include "textflag.h"
574 GLOBL ·rcon(SB), RODATA|DUPOK, $32
575 `
576
577 func TestStrictDup(t *testing.T) {
578
579 testenv.MustHaveGoBuild(t)
580
581 asmfiles := []struct {
582 fname string
583 payload string
584 }{
585 {"a", testStrictDupAsmSrc1},
586 {"b", testStrictDupAsmSrc2},
587 {"c", testStrictDupAsmSrc3},
588 {"d", testStrictDupAsmSrc4},
589 }
590
591 t.Parallel()
592
593 tmpdir := t.TempDir()
594
595 src := filepath.Join(tmpdir, "x.go")
596 err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
597 if err != nil {
598 t.Fatal(err)
599 }
600 for _, af := range asmfiles {
601 src = filepath.Join(tmpdir, af.fname+".s")
602 err = os.WriteFile(src, []byte(af.payload), 0666)
603 if err != nil {
604 t.Fatal(err)
605 }
606 }
607 src = filepath.Join(tmpdir, "go.mod")
608 err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666)
609 if err != nil {
610 t.Fatal(err)
611 }
612
613 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
614 cmd.Dir = tmpdir
615 out, err := cmd.CombinedOutput()
616 if err != nil {
617 t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
618 }
619 if !bytes.Contains(out, []byte("mismatched payload")) {
620 t.Errorf("unexpected output:\n%s", out)
621 }
622
623 cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
624 cmd.Dir = tmpdir
625 out, err = cmd.CombinedOutput()
626 if err == nil {
627 t.Errorf("linking with -strictdups=2 did not fail")
628 }
629
630
631 if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
632 bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
633 !bytes.Contains(out, []byte("mismatched payload: different sizes")) {
634 t.Errorf("unexpected output:\n%s", out)
635 }
636 }
637
638 const testFuncAlignSrc = `
639 package main
640 import (
641 "fmt"
642 )
643 func alignPc()
644 var alignPcFnAddr uintptr
645
646 func main() {
647 if alignPcFnAddr % 512 != 0 {
648 fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)
649 } else {
650 fmt.Printf("PASS")
651 }
652 }
653 `
654
655 var testFuncAlignAsmSources = map[string]string{
656 "arm64": `
657 #include "textflag.h"
658
659 TEXT ·alignPc(SB),NOSPLIT, $0-0
660 MOVD $2, R0
661 PCALIGN $512
662 MOVD $3, R1
663 RET
664
665 GLOBL ·alignPcFnAddr(SB),RODATA,$8
666 DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)
667 `,
668 "loong64": `
669 #include "textflag.h"
670
671 TEXT ·alignPc(SB),NOSPLIT, $0-0
672 MOVV $2, R4
673 PCALIGN $512
674 MOVV $3, R5
675 RET
676
677 GLOBL ·alignPcFnAddr(SB),RODATA,$8
678 DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)
679 `,
680 }
681
682
683
684 func TestFuncAlign(t *testing.T) {
685 testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH]
686 if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" {
687 t.Skip("skipping on non-linux/{arm64,loong64} platform")
688 }
689 testenv.MustHaveGoBuild(t)
690
691 t.Parallel()
692
693 tmpdir := t.TempDir()
694
695 src := filepath.Join(tmpdir, "go.mod")
696 err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
697 if err != nil {
698 t.Fatal(err)
699 }
700 src = filepath.Join(tmpdir, "falign.go")
701 err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666)
702 if err != nil {
703 t.Fatal(err)
704 }
705 src = filepath.Join(tmpdir, "falign.s")
706 err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
707 if err != nil {
708 t.Fatal(err)
709 }
710
711 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "falign")
712 cmd.Dir = tmpdir
713 out, err := cmd.CombinedOutput()
714 if err != nil {
715 t.Errorf("build failed: %v", err)
716 }
717 cmd = testenv.Command(t, tmpdir+"/falign")
718 out, err = cmd.CombinedOutput()
719 if err != nil {
720 t.Errorf("failed to run with err %v, output: %s", err, out)
721 }
722 if string(out) != "PASS" {
723 t.Errorf("unexpected output: %s\n", out)
724 }
725 }
726
727 const testFuncAlignOptionSrc = `
728 package main
729 //go:noinline
730 func foo() {
731 }
732 //go:noinline
733 func bar() {
734 }
735 //go:noinline
736 func baz() {
737 }
738 func main() {
739 foo()
740 bar()
741 baz()
742 }
743 `
744
745
746 func TestFuncAlignOption(t *testing.T) {
747 testenv.MustHaveGoBuild(t)
748
749 t.Parallel()
750
751 tmpdir := t.TempDir()
752
753 src := filepath.Join(tmpdir, "falign.go")
754 err := os.WriteFile(src, []byte(testFuncAlignOptionSrc), 0666)
755 if err != nil {
756 t.Fatal(err)
757 }
758
759 alignTest := func(align uint64) {
760 exeName := "falign.exe"
761 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-funcalign="+strconv.FormatUint(align, 10), "-o", exeName, "falign.go")
762 cmd.Dir = tmpdir
763 out, err := cmd.CombinedOutput()
764 if err != nil {
765 t.Errorf("build failed: %v \n%s", err, out)
766 }
767 exe := filepath.Join(tmpdir, exeName)
768 cmd = testenv.Command(t, exe)
769 out, err = cmd.CombinedOutput()
770 if err != nil {
771 t.Errorf("failed to run with err %v, output: %s", err, out)
772 }
773
774
775 f, err := objfile.Open(exe)
776 if err != nil {
777 t.Fatalf("failed to open file:%v\n", err)
778 }
779 defer f.Close()
780
781 fname := map[string]bool{"_main.foo": false,
782 "_main.bar": false,
783 "_main.baz": false}
784 syms, err := f.Symbols()
785 for _, s := range syms {
786 fn := s.Name
787 if _, ok := fname[fn]; !ok {
788 fn = "_" + s.Name
789 if _, ok := fname[fn]; !ok {
790 continue
791 }
792 }
793 if s.Addr%align != 0 {
794 t.Fatalf("unaligned function: %s %x. Expected alignment: %d\n", fn, s.Addr, align)
795 }
796 fname[fn] = true
797 }
798 for k, v := range fname {
799 if !v {
800 t.Fatalf("function %s not found\n", k)
801 }
802 }
803 }
804 alignTest(16)
805 alignTest(32)
806 }
807
808 const testTrampSrc = `
809 package main
810 import "fmt"
811 func main() {
812 fmt.Println("hello")
813
814 defer func(){
815 if e := recover(); e == nil {
816 panic("did not panic")
817 }
818 }()
819 f1()
820 }
821
822 // Test deferreturn trampolines. See issue #39049.
823 func f1() { defer f2() }
824 func f2() { panic("XXX") }
825 `
826
827 func TestTrampoline(t *testing.T) {
828
829
830
831
832 buildmodes := []string{"default"}
833 switch runtime.GOARCH {
834 case "arm", "arm64", "ppc64", "loong64":
835 case "ppc64le":
836
837 buildmodes = append(buildmodes, "pie")
838 default:
839 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
840 }
841
842 testenv.MustHaveGoBuild(t)
843
844 t.Parallel()
845
846 tmpdir := t.TempDir()
847
848 src := filepath.Join(tmpdir, "hello.go")
849 err := os.WriteFile(src, []byte(testTrampSrc), 0666)
850 if err != nil {
851 t.Fatal(err)
852 }
853 exe := filepath.Join(tmpdir, "hello.exe")
854
855 for _, mode := range buildmodes {
856 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
857 out, err := cmd.CombinedOutput()
858 if err != nil {
859 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
860 }
861 cmd = testenv.Command(t, exe)
862 out, err = cmd.CombinedOutput()
863 if err != nil {
864 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
865 }
866 if string(out) != "hello\n" {
867 t.Errorf("unexpected output (%s):\n%s", mode, out)
868 }
869
870 out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
871 if err != nil {
872 t.Errorf("nm failure: %s\n%s\n", err, string(out))
873 }
874 if ok, _ := regexp.Match("T runtime.deferreturn(\\+0)?-tramp0", out); !ok {
875 t.Errorf("Trampoline T runtime.deferreturn(+0)?-tramp0 is missing")
876 }
877 }
878 }
879
880 const testTrampCgoSrc = `
881 package main
882
883 // #include <stdio.h>
884 // void CHello() { printf("hello\n"); fflush(stdout); }
885 import "C"
886
887 func main() {
888 C.CHello()
889 }
890 `
891
892 func TestTrampolineCgo(t *testing.T) {
893
894
895
896
897 buildmodes := []string{"default"}
898 switch runtime.GOARCH {
899 case "arm", "arm64", "ppc64", "loong64":
900 case "ppc64le":
901
902 buildmodes = append(buildmodes, "pie")
903 default:
904 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
905 }
906
907 testenv.MustHaveGoBuild(t)
908 testenv.MustHaveCGO(t)
909
910 t.Parallel()
911
912 tmpdir := t.TempDir()
913
914 src := filepath.Join(tmpdir, "hello.go")
915 err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
916 if err != nil {
917 t.Fatal(err)
918 }
919 exe := filepath.Join(tmpdir, "hello.exe")
920
921 for _, mode := range buildmodes {
922 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
923 out, err := cmd.CombinedOutput()
924 if err != nil {
925 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
926 }
927 cmd = testenv.Command(t, exe)
928 out, err = cmd.CombinedOutput()
929 if err != nil {
930 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
931 }
932 if string(out) != "hello\n" && string(out) != "hello\r\n" {
933 t.Errorf("unexpected output (%s):\n%s", mode, out)
934 }
935
936
937
938 if !testenv.CanInternalLink(true) {
939 continue
940 }
941 cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
942 out, err = cmd.CombinedOutput()
943 if err != nil {
944 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
945 }
946 cmd = testenv.Command(t, exe)
947 out, err = cmd.CombinedOutput()
948 if err != nil {
949 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
950 }
951 if string(out) != "hello\n" && string(out) != "hello\r\n" {
952 t.Errorf("unexpected output (%s):\n%s", mode, out)
953 }
954 }
955 }
956
957 func TestIndexMismatch(t *testing.T) {
958
959
960
961 testenv.MustHaveGoBuild(t)
962
963
964 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
965
966 t.Parallel()
967
968 tmpdir := t.TempDir()
969
970 aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
971 bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
972 mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
973 aObj := filepath.Join(tmpdir, "a.o")
974 mObj := filepath.Join(tmpdir, "main.o")
975 exe := filepath.Join(tmpdir, "main.exe")
976
977 importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")
978 testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")
979 importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
980 testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")
981
982
983 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
984 t.Log(cmd)
985 out, err := cmd.CombinedOutput()
986 if err != nil {
987 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
988 }
989 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
990 t.Log(cmd)
991 out, err = cmd.CombinedOutput()
992 if err != nil {
993 t.Fatalf("compiling main.go failed: %v\n%s", err, out)
994 }
995 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
996 t.Log(cmd)
997 out, err = cmd.CombinedOutput()
998 if err != nil {
999 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
1000 testenv.SkipFlaky(t, 58806)
1001 }
1002 t.Errorf("linking failed: %v\n%s", err, out)
1003 }
1004
1005
1006
1007 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
1008 t.Log(cmd)
1009 out, err = cmd.CombinedOutput()
1010 if err != nil {
1011 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
1012 }
1013 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
1014 t.Log(cmd)
1015 out, err = cmd.CombinedOutput()
1016 if err == nil {
1017 t.Fatalf("linking didn't fail")
1018 }
1019 if !bytes.Contains(out, []byte("fingerprint mismatch")) {
1020 t.Errorf("did not see expected error message. out:\n%s", out)
1021 }
1022 }
1023
1024 func TestPErsrcBinutils(t *testing.T) {
1025
1026 testenv.MustHaveGoBuild(t)
1027
1028 if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
1029
1030 t.Skipf("this is only for windows/amd64 and windows/386")
1031 }
1032
1033 t.Parallel()
1034
1035 tmpdir := t.TempDir()
1036
1037 pkgdir := filepath.Join("testdata", "pe-binutils")
1038 exe := filepath.Join(tmpdir, "a.exe")
1039 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe)
1040 cmd.Dir = pkgdir
1041
1042 out, err := cmd.CombinedOutput()
1043 if err != nil {
1044 t.Fatalf("building failed: %v, output:\n%s", err, out)
1045 }
1046
1047
1048 b, err := os.ReadFile(exe)
1049 if err != nil {
1050 t.Fatalf("reading output failed: %v", err)
1051 }
1052 if !bytes.Contains(b, []byte("Hello Gophers!")) {
1053 t.Fatalf("binary does not contain expected content")
1054 }
1055 }
1056
1057 func TestPErsrcLLVM(t *testing.T) {
1058
1059 testenv.MustHaveGoBuild(t)
1060
1061 if runtime.GOOS != "windows" {
1062 t.Skipf("this is a windows-only test")
1063 }
1064
1065 t.Parallel()
1066
1067 tmpdir := t.TempDir()
1068
1069 pkgdir := filepath.Join("testdata", "pe-llvm")
1070 exe := filepath.Join(tmpdir, "a.exe")
1071 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe)
1072 cmd.Dir = pkgdir
1073
1074 out, err := cmd.CombinedOutput()
1075 if err != nil {
1076 t.Fatalf("building failed: %v, output:\n%s", err, out)
1077 }
1078
1079
1080 b, err := os.ReadFile(exe)
1081 if err != nil {
1082 t.Fatalf("reading output failed: %v", err)
1083 }
1084 if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
1085 t.Fatalf("binary does not contain expected content")
1086 }
1087 }
1088
1089 func TestContentAddressableSymbols(t *testing.T) {
1090
1091 testenv.MustHaveGoBuild(t)
1092
1093 t.Parallel()
1094
1095 src := filepath.Join("testdata", "testHashedSyms", "p.go")
1096 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
1097 out, err := cmd.CombinedOutput()
1098 if err != nil {
1099 t.Errorf("command %s failed: %v\n%s", cmd, err, out)
1100 }
1101 }
1102
1103 func TestReadOnly(t *testing.T) {
1104
1105 testenv.MustHaveGoBuild(t)
1106
1107 t.Parallel()
1108
1109 src := filepath.Join("testdata", "testRO", "x.go")
1110 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
1111 out, err := cmd.CombinedOutput()
1112 if err == nil {
1113 t.Errorf("running test program did not fail. output:\n%s", out)
1114 }
1115 }
1116
1117 const testIssue38554Src = `
1118 package main
1119
1120 type T [10<<20]byte
1121
1122 //go:noinline
1123 func f() T {
1124 return T{} // compiler will make a large stmp symbol, but not used.
1125 }
1126
1127 func main() {
1128 x := f()
1129 println(x[1])
1130 }
1131 `
1132
1133 func TestIssue38554(t *testing.T) {
1134 testenv.MustHaveGoBuild(t)
1135
1136 t.Parallel()
1137
1138 tmpdir := t.TempDir()
1139
1140 src := filepath.Join(tmpdir, "x.go")
1141 err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
1142 if err != nil {
1143 t.Fatalf("failed to write source file: %v", err)
1144 }
1145 exe := filepath.Join(tmpdir, "x.exe")
1146 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
1147 out, err := cmd.CombinedOutput()
1148 if err != nil {
1149 t.Fatalf("build failed: %v\n%s", err, out)
1150 }
1151
1152 fi, err := os.Stat(exe)
1153 if err != nil {
1154 t.Fatalf("failed to stat output file: %v", err)
1155 }
1156
1157
1158
1159
1160 const want = 5 << 20
1161 if got := fi.Size(); got > want {
1162 t.Errorf("binary too big: got %d, want < %d", got, want)
1163 }
1164 }
1165
1166 const testIssue42396src = `
1167 package main
1168
1169 //go:noinline
1170 //go:nosplit
1171 func callee(x int) {
1172 }
1173
1174 func main() {
1175 callee(9)
1176 }
1177 `
1178
1179 func TestIssue42396(t *testing.T) {
1180 testenv.MustHaveGoBuild(t)
1181
1182 if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
1183 t.Skip("no race detector support")
1184 }
1185
1186 t.Parallel()
1187
1188 tmpdir := t.TempDir()
1189
1190 src := filepath.Join(tmpdir, "main.go")
1191 err := os.WriteFile(src, []byte(testIssue42396src), 0666)
1192 if err != nil {
1193 t.Fatalf("failed to write source file: %v", err)
1194 }
1195 exe := filepath.Join(tmpdir, "main.exe")
1196 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
1197 out, err := cmd.CombinedOutput()
1198 if err == nil {
1199 t.Fatalf("build unexpectedly succeeded")
1200 }
1201
1202
1203
1204 if strings.Contains(string(out), "panic:") {
1205 t.Fatalf("build should not fail with panic:\n%s", out)
1206 }
1207 const want = "reference to undefined builtin"
1208 if !strings.Contains(string(out), want) {
1209 t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
1210 }
1211 }
1212
1213 const testLargeRelocSrc = `
1214 package main
1215
1216 var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
1217
1218 var addr = [...]*byte{
1219 &x[1<<23-1],
1220 &x[1<<23],
1221 &x[1<<23+1],
1222 &x[1<<24-1],
1223 &x[1<<24],
1224 &x[1<<24+1],
1225 }
1226
1227 func main() {
1228 // check relocations in instructions
1229 check(x[1<<23-1], 0)
1230 check(x[1<<23], 23)
1231 check(x[1<<23+1], 0)
1232 check(x[1<<24-1], 0)
1233 check(x[1<<24], 24)
1234 check(x[1<<24+1], 0)
1235
1236 // check absolute address relocations in data
1237 check(*addr[0], 0)
1238 check(*addr[1], 23)
1239 check(*addr[2], 0)
1240 check(*addr[3], 0)
1241 check(*addr[4], 24)
1242 check(*addr[5], 0)
1243 }
1244
1245 func check(x, y byte) {
1246 if x != y {
1247 panic("FAIL")
1248 }
1249 }
1250 `
1251
1252 func TestLargeReloc(t *testing.T) {
1253
1254
1255
1256 testenv.MustHaveGoBuild(t)
1257 t.Parallel()
1258
1259 tmpdir := t.TempDir()
1260
1261 src := filepath.Join(tmpdir, "x.go")
1262 err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
1263 if err != nil {
1264 t.Fatalf("failed to write source file: %v", err)
1265 }
1266 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
1267 out, err := cmd.CombinedOutput()
1268 if err != nil {
1269 t.Errorf("build failed: %v. output:\n%s", err, out)
1270 }
1271
1272 if testenv.HasCGO() {
1273 cmd = testenv.Command(t, testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src)
1274 out, err = cmd.CombinedOutput()
1275 if err != nil {
1276 t.Fatalf("build failed: %v. output:\n%s", err, out)
1277 }
1278 }
1279 }
1280
1281 func TestUnlinkableObj(t *testing.T) {
1282
1283 testenv.MustHaveGoBuild(t)
1284 t.Parallel()
1285
1286 if true {
1287 t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
1288 }
1289
1290 tmpdir := t.TempDir()
1291
1292 xSrc := filepath.Join(tmpdir, "x.go")
1293 pSrc := filepath.Join(tmpdir, "p.go")
1294 xObj := filepath.Join(tmpdir, "x.o")
1295 pObj := filepath.Join(tmpdir, "p.o")
1296 exe := filepath.Join(tmpdir, "x.exe")
1297 importcfgfile := filepath.Join(tmpdir, "importcfg")
1298 testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
1299 err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
1300 if err != nil {
1301 t.Fatalf("failed to write source file: %v", err)
1302 }
1303 err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
1304 if err != nil {
1305 t.Fatalf("failed to write source file: %v", err)
1306 }
1307 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc)
1308 out, err := cmd.CombinedOutput()
1309 if err != nil {
1310 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1311 }
1312 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
1313 out, err = cmd.CombinedOutput()
1314 if err != nil {
1315 t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
1316 }
1317 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj)
1318 out, err = cmd.CombinedOutput()
1319 if err == nil {
1320 t.Fatalf("link did not fail")
1321 }
1322 if !bytes.Contains(out, []byte("unlinkable object")) {
1323 t.Errorf("did not see expected error message. out:\n%s", out)
1324 }
1325
1326
1327 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
1328 out, err = cmd.CombinedOutput()
1329 if err != nil {
1330 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1331 }
1332 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc)
1333 out, err = cmd.CombinedOutput()
1334 if err != nil {
1335 t.Fatalf("compile failed: %v. output:\n%s", err, out)
1336 }
1337
1338 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj)
1339 out, err = cmd.CombinedOutput()
1340 if err != nil {
1341 t.Errorf("link failed: %v. output:\n%s", err, out)
1342 }
1343 }
1344
1345 func TestExtLinkCmdlineDeterminism(t *testing.T) {
1346
1347 testenv.MustHaveGoBuild(t)
1348 testenv.MustHaveCGO(t)
1349 t.Parallel()
1350
1351
1352 testSrc := `
1353 package main
1354 import "C"
1355 //export F1
1356 func F1() {}
1357 //export F2
1358 func F2() {}
1359 //export F3
1360 func F3() {}
1361 func main() {}
1362 `
1363
1364 tmpdir := t.TempDir()
1365 src := filepath.Join(tmpdir, "x.go")
1366 if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {
1367 t.Fatal(err)
1368 }
1369 exe := filepath.Join(tmpdir, "x.exe")
1370
1371
1372
1373 linktmp := filepath.Join(tmpdir, "linktmp")
1374 if err := os.Mkdir(linktmp, 0777); err != nil {
1375 t.Fatal(err)
1376 }
1377
1378
1379
1380 ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp
1381 var out0 []byte
1382 for i := 0; i < 5; i++ {
1383 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", ldflags, "-o", exe, src)
1384 out, err := cmd.CombinedOutput()
1385 if err != nil {
1386 t.Fatalf("build failed: %v, output:\n%s", err, out)
1387 }
1388 if err := os.Remove(exe); err != nil {
1389 t.Fatal(err)
1390 }
1391
1392
1393 j := bytes.Index(out, []byte("\nhost link:"))
1394 if j == -1 {
1395 t.Fatalf("host link step not found, output:\n%s", out)
1396 }
1397 out = out[j+1:]
1398 k := bytes.Index(out, []byte("\n"))
1399 if k == -1 {
1400 t.Fatalf("no newline after host link, output:\n%s", out)
1401 }
1402 out = out[:k]
1403
1404
1405
1406 fs := bytes.Fields(out)
1407 for i, f := range fs {
1408 if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {
1409 fs[i+1] = []byte("a.out")
1410 break
1411 }
1412 }
1413 out = bytes.Join(fs, []byte{' '})
1414
1415 if i == 0 {
1416 out0 = out
1417 continue
1418 }
1419 if !bytes.Equal(out0, out) {
1420 t.Fatalf("output differ:\n%s\n==========\n%s", out0, out)
1421 }
1422 }
1423 }
1424
1425
1426
1427 func TestResponseFile(t *testing.T) {
1428 t.Parallel()
1429
1430 testenv.MustHaveGoBuild(t)
1431
1432
1433
1434 testenv.MustHaveCGO(t)
1435
1436 tmpdir := t.TempDir()
1437
1438 src := filepath.Join(tmpdir, "x.go")
1439 if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {
1440 t.Fatal(err)
1441 }
1442
1443 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")
1444 cmd.Dir = tmpdir
1445
1446
1447 var sb strings.Builder
1448 sb.WriteString(`'-ldflags=all="-extldflags=`)
1449 for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {
1450 if i > 0 {
1451 sb.WriteString(" ")
1452 }
1453 sb.WriteString("-g")
1454 }
1455 sb.WriteString(`"'`)
1456 cmd = testenv.CleanCmdEnv(cmd)
1457 cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())
1458
1459 out, err := cmd.CombinedOutput()
1460 if len(out) > 0 {
1461 t.Logf("%s", out)
1462 }
1463 if err != nil {
1464 t.Error(err)
1465 }
1466 }
1467
1468 func TestDynimportVar(t *testing.T) {
1469
1470
1471 if runtime.GOOS != "darwin" {
1472 t.Skip("skip on non-darwin platform")
1473 }
1474
1475 testenv.MustHaveGoBuild(t)
1476 testenv.MustHaveCGO(t)
1477
1478 t.Parallel()
1479
1480 tmpdir := t.TempDir()
1481 exe := filepath.Join(tmpdir, "a.exe")
1482 src := filepath.Join("testdata", "dynimportvar", "main.go")
1483
1484 for _, mode := range []string{"internal", "external"} {
1485 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode="+mode, "-o", exe, src)
1486 out, err := cmd.CombinedOutput()
1487 if err != nil {
1488 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1489 }
1490 cmd = testenv.Command(t, exe)
1491 out, err = cmd.CombinedOutput()
1492 if err != nil {
1493 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1494 }
1495 }
1496 }
1497
1498 const helloSrc = `
1499 package main
1500 var X = 42
1501 var Y int
1502 func main() { println("hello", X, Y) }
1503 `
1504
1505 func TestFlagS(t *testing.T) {
1506
1507 testenv.MustHaveGoBuild(t)
1508
1509 t.Parallel()
1510
1511 tmpdir := t.TempDir()
1512 exe := filepath.Join(tmpdir, "a.exe")
1513 src := filepath.Join(tmpdir, "a.go")
1514 err := os.WriteFile(src, []byte(helloSrc), 0666)
1515 if err != nil {
1516 t.Fatal(err)
1517 }
1518
1519 modes := []string{"auto"}
1520 if testenv.HasCGO() {
1521 modes = append(modes, "external")
1522 }
1523
1524
1525 syms := []string{"main.main", "main.X", "main.Y"}
1526
1527 for _, mode := range modes {
1528 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)
1529 out, err := cmd.CombinedOutput()
1530 if err != nil {
1531 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1532 }
1533 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1534 out, err = cmd.CombinedOutput()
1535 if err != nil && !errors.As(err, new(*exec.ExitError)) {
1536
1537
1538
1539 t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)
1540 }
1541 for _, s := range syms {
1542 if bytes.Contains(out, []byte(s)) {
1543 t.Errorf("(mode=%s): unexpected symbol %s", mode, s)
1544 }
1545 }
1546 }
1547 }
1548
1549 func TestRandLayout(t *testing.T) {
1550
1551
1552 testenv.MustHaveGoBuild(t)
1553
1554 t.Parallel()
1555
1556 tmpdir := t.TempDir()
1557
1558 src := filepath.Join(tmpdir, "hello.go")
1559 err := os.WriteFile(src, []byte(trivialSrc), 0666)
1560 if err != nil {
1561 t.Fatal(err)
1562 }
1563
1564 var syms [2]string
1565 for i, seed := range []string{"123", "456"} {
1566 exe := filepath.Join(tmpdir, "hello"+seed+".exe")
1567 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
1568 out, err := cmd.CombinedOutput()
1569 if err != nil {
1570 t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)
1571 }
1572 cmd = testenv.Command(t, exe)
1573 err = cmd.Run()
1574 if err != nil {
1575 t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)
1576 }
1577 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1578 out, err = cmd.CombinedOutput()
1579 if err != nil {
1580 t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)
1581 }
1582 syms[i] = string(out)
1583 }
1584 if syms[0] == syms[1] {
1585 t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
1586 }
1587 }
1588
1589 func TestCheckLinkname(t *testing.T) {
1590
1591 testenv.MustHaveGoBuild(t)
1592 t.Parallel()
1593
1594 tmpdir := t.TempDir()
1595
1596 tests := []struct {
1597 src string
1598 ok bool
1599 }{
1600
1601 {"ok.go", true},
1602
1603 {"push.go", true},
1604
1605
1606 {"textvar", true},
1607
1608 {"coro.go", false},
1609 {"coro_var.go", false},
1610
1611 {"coro_asm", false},
1612
1613 {"coro2.go", false},
1614
1615 {"builtin.go", false},
1616
1617 {"fastrand.go", true},
1618 {"badlinkname.go", true},
1619 }
1620 for _, test := range tests {
1621 test := test
1622 t.Run(test.src, func(t *testing.T) {
1623 t.Parallel()
1624 src := "./testdata/linkname/" + test.src
1625 exe := filepath.Join(tmpdir, test.src+".exe")
1626 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
1627 out, err := cmd.CombinedOutput()
1628 if test.ok && err != nil {
1629 t.Errorf("build failed unexpectedly: %v:\n%s", err, out)
1630 }
1631 if !test.ok && err == nil {
1632 t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out)
1633 }
1634 })
1635 }
1636 }
1637
1638 func TestLinknameBSS(t *testing.T) {
1639
1640
1641 testenv.MustHaveGoBuild(t)
1642 t.Parallel()
1643
1644 tmpdir := t.TempDir()
1645
1646 src := filepath.Join("testdata", "linkname", "sched.go")
1647 exe := filepath.Join(tmpdir, "sched.exe")
1648 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
1649 out, err := cmd.CombinedOutput()
1650 if err != nil {
1651 t.Fatalf("build failed unexpectedly: %v:\n%s", err, out)
1652 }
1653
1654
1655 f, err := objfile.Open(exe)
1656 if err != nil {
1657 t.Fatalf("fail to open executable: %v", err)
1658 }
1659 defer f.Close()
1660 syms, err := f.Symbols()
1661 if err != nil {
1662 t.Fatalf("fail to get symbols: %v", err)
1663 }
1664 found := false
1665 for _, s := range syms {
1666 if s.Name == "runtime.sched" || s.Name == "_runtime.sched" {
1667 found = true
1668 if s.Size < 100 {
1669
1670
1671
1672 t.Errorf("runtime.sched symbol size too small: want > 100, got %d", s.Size)
1673 }
1674 }
1675 }
1676 if !found {
1677 t.Errorf("runtime.sched symbol not found")
1678 }
1679
1680
1681 cmd = testenv.Command(t, exe)
1682 out, err = cmd.CombinedOutput()
1683 if err != nil {
1684 t.Errorf("executable failed to run: %v\n%s", err, out)
1685 }
1686 }
1687
View as plain text