Source file
src/cmd/link/link_test.go
1
2
3
4
5 package main
6
7 import (
8 "bufio"
9 "bytes"
10 "debug/elf"
11 "debug/macho"
12 "debug/pe"
13 "errors"
14 "internal/abi"
15 "internal/buildcfg"
16 "internal/platform"
17 "internal/testenv"
18 "internal/xcoff"
19 "os"
20 "os/exec"
21 "path/filepath"
22 "regexp"
23 "runtime"
24 "strconv"
25 "strings"
26 "testing"
27 "unsafe"
28
29 imacho "cmd/internal/macho"
30 "cmd/internal/objfile"
31 "cmd/internal/sys"
32 )
33
34
35
36
37
38
39
40
41 func TestMain(m *testing.M) {
42
43
44
45 if os.Getenv("LINK_TEST_TOOLEXEC") != "" {
46 if strings.TrimSuffix(filepath.Base(os.Args[1]), ".exe") == "link" {
47
48
49 os.Args = os.Args[1:]
50 main()
51 os.Exit(0)
52 }
53
54 cmd := exec.Command(os.Args[1], os.Args[2:]...)
55 cmd.Stdin = os.Stdin
56 cmd.Stdout = os.Stdout
57 cmd.Stderr = os.Stderr
58 if err := cmd.Run(); err != nil {
59 os.Exit(1)
60 }
61 os.Exit(0)
62 }
63
64
65
66 if os.Getenv("LINK_TEST_EXEC_LINKER") != "" {
67 main()
68 os.Exit(0)
69 }
70
71 if testExe, err := os.Executable(); err == nil {
72
73 testLinker = testExe
74 }
75
76
77
78 os.Exit(m.Run())
79 }
80
81
82
83 var testLinker string
84
85
86
87
88
89
90 func goCmd(t *testing.T, args ...string) *exec.Cmd {
91 goArgs := []string{args[0], "-toolexec", testenv.Executable(t)}
92 args = append(goArgs, args[1:]...)
93 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
94 cmd = testenv.CleanCmdEnv(cmd)
95 cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")
96 return cmd
97 }
98
99
100
101
102 func linkCmd(t *testing.T, args ...string) *exec.Cmd {
103
104 args = append([]string{"link"}, args...)
105 cmd := testenv.Command(t, testenv.Executable(t), args...)
106 cmd = testenv.CleanCmdEnv(cmd)
107 cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")
108 return cmd
109 }
110
111 var AuthorPaidByTheColumnInch struct {
112 fog int `text:"London. Michaelmas term lately over, and the Lord Chancellor sitting in Lincoln’s Inn Hall. Implacable November weather. As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill. Smoke lowering down from chimney-pots, making a soft black drizzle, with flakes of soot in it as big as full-grown snowflakes—gone into mourning, one might imagine, for the death of the sun. Dogs, undistinguishable in mire. Horses, scarcely better; splashed to their very blinkers. Foot passengers, jostling one another’s umbrellas in a general infection of ill temper, and losing their foot-hold at street-corners, where tens of thousands of other foot passengers have been slipping and sliding since the day broke (if this day ever broke), adding new deposits to the crust upon crust of mud, sticking at those points tenaciously to the pavement, and accumulating at compound interest. Fog everywhere. Fog up the river, where it flows among green aits and meadows; fog down the river, where it rolls defiled among the tiers of shipping and the waterside pollutions of a great (and dirty) city. Fog on the Essex marshes, fog on the Kentish heights. Fog creeping into the cabooses of collier-brigs; fog lying out on the yards and hovering in the rigging of great ships; fog drooping on the gunwales of barges and small boats. Fog in the eyes and throats of ancient Greenwich pensioners, wheezing by the firesides of their wards; fog in the stem and bowl of the afternoon pipe of the wrathful skipper, down in his close cabin; fog cruelly pinching the toes and fingers of his shivering little ‘prentice boy on deck. Chance people on the bridges peeping over the parapets into a nether sky of fog, with fog all round them, as if they were up in a balloon and hanging in the misty clouds. Gas looming through the fog in divers places in the streets, much as the sun may, from the spongey fields, be seen to loom by husbandman and ploughboy. Most of the shops lighted two hours before their time—as the gas seems to know, for it has a haggard and unwilling look. The raw afternoon is rawest, and the dense fog is densest, and the muddy streets are muddiest near that leaden-headed old obstruction, appropriate ornament for the threshold of a leaden-headed old corporation, Temple Bar. And hard by Temple Bar, in Lincoln’s Inn Hall, at the very heart of the fog, sits the Lord High Chancellor in his High Court of Chancery."`
113
114 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."`
115
116 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."`
117
118 principle int `text:"The one great principle of the English law is, to make business for itself. There is no other principle distinctly, certainly, and consistently maintained through all its narrow turnings. Viewed by this light it becomes a coherent scheme, and not the monstrous maze the laity are apt to think it. Let them but once clearly perceive that its grand principle is to make business for itself at their expense, and surely they will cease to grumble."`
119 }
120
121 func TestLargeSymName(t *testing.T) {
122
123
124
125 _ = AuthorPaidByTheColumnInch
126 }
127
128 func TestIssue21703(t *testing.T) {
129 t.Parallel()
130
131 testenv.MustHaveGoBuild(t)
132
133
134 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
135
136 const source = `
137 package main
138 const X = "\n!\n"
139 func main() {}
140 `
141
142 tmpdir := t.TempDir()
143 main := filepath.Join(tmpdir, "main.go")
144
145 err := os.WriteFile(main, []byte(source), 0666)
146 if err != nil {
147 t.Fatalf("failed to write main.go: %v\n", err)
148 }
149
150 importcfgfile := filepath.Join(tmpdir, "importcfg")
151 testenv.WriteImportcfg(t, importcfgfile, nil, main)
152
153 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
154 cmd.Dir = tmpdir
155 out, err := cmd.CombinedOutput()
156 if err != nil {
157 t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
158 }
159
160 cmd = linkCmd(t, "-importcfg="+importcfgfile, "main.o")
161 cmd.Dir = tmpdir
162 out, err = cmd.CombinedOutput()
163 if err != nil {
164 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
165 testenv.SkipFlaky(t, 58806)
166 }
167 t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
168 }
169 }
170
171
172
173
174
175 func TestIssue28429(t *testing.T) {
176 t.Parallel()
177
178 testenv.MustHaveGoBuild(t)
179
180
181 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
182
183 tmpdir := t.TempDir()
184
185 write := func(name, content string) {
186 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
187 if err != nil {
188 t.Fatal(err)
189 }
190 }
191
192 runGo := func(args ...string) {
193 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
194 cmd.Dir = tmpdir
195 out, err := cmd.CombinedOutput()
196 if err != nil {
197 t.Fatalf("'go %s' failed: %v, output: %s",
198 strings.Join(args, " "), err, out)
199 }
200 }
201
202
203 write("main.go", "package main; func main() {}")
204 importcfgfile := filepath.Join(tmpdir, "importcfg")
205 testenv.WriteImportcfg(t, importcfgfile, nil, filepath.Join(tmpdir, "main.go"))
206 runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
207 runGo("tool", "pack", "c", "main.a", "main.o")
208
209
210
211 write(".facts", "this is not an object file")
212 runGo("tool", "pack", "r", "main.a", ".facts")
213
214
215
216 cmd := linkCmd(t, "-importcfg="+importcfgfile, "main.a")
217 cmd.Dir = tmpdir
218 out, err := cmd.CombinedOutput()
219 if err != nil {
220 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
221 testenv.SkipFlaky(t, 58806)
222 }
223 t.Fatalf("linker failed: %v, output %s", err, out)
224 }
225 }
226
227 func TestUnresolved(t *testing.T) {
228 testenv.MustHaveGoBuild(t)
229
230 t.Parallel()
231
232 tmpdir := t.TempDir()
233
234 write := func(name, content string) {
235 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
236 if err != nil {
237 t.Fatal(err)
238 }
239 }
240
241
242
243
244
245
246 write("go.mod", "module testunresolved\n")
247 write("main.go", `package main
248
249 func main() {
250 x()
251 }
252
253 func x()
254 `)
255 write("main.s", `
256 TEXT ·x(SB),0,$0
257 MOVD zero<>(SB), AX
258 MOVD zero(SB), AX
259 MOVD ·zero(SB), AX
260 RET
261 `)
262 cmd := goCmd(t, "build")
263 cmd.Dir = tmpdir
264 cmd.Env = append(cmd.Env,
265 "GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
266 out, err := cmd.CombinedOutput()
267 if err == nil {
268 t.Fatalf("expected build to fail, but it succeeded")
269 }
270 out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
271 got := string(out)
272 want := `main.x: relocation target zero not defined
273 main.x: relocation target zero not defined
274 main.x: relocation target main.zero not defined
275 `
276 if want != got {
277 t.Fatalf("want:\n%sgot:\n%s", want, got)
278 }
279 }
280
281 func TestIssue33979(t *testing.T) {
282 testenv.MustHaveGoBuild(t)
283 testenv.MustHaveCGO(t)
284
285
286 testenv.MustInternalLink(t, testenv.SpecialBuildTypes{Cgo: true})
287
288 t.Parallel()
289
290 tmpdir := t.TempDir()
291
292 write := func(name, content string) {
293 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
294 if err != nil {
295 t.Fatal(err)
296 }
297 }
298
299 run := func(name string, args ...string) string {
300 cmd := testenv.Command(t, name, args...)
301 cmd.Dir = tmpdir
302 out, err := cmd.CombinedOutput()
303 if err != nil {
304 t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)
305 }
306 return string(out)
307 }
308 runGo := func(args ...string) string {
309 return run(testenv.GoToolPath(t), args...)
310 }
311
312
313
314
315
316
317 write("main.go", `package main
318 func main() {
319 x()
320 }
321 func x()
322 `)
323
324 write("x.s", `
325 TEXT ·x(SB),0,$0
326 CALL foo(SB)
327 RET
328 `)
329 write("x.c", `
330 void undefined();
331
332 void foo() {
333 undefined();
334 }
335 `)
336
337 cc := strings.TrimSpace(runGo("env", "CC"))
338 cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))
339
340 importcfgfile := filepath.Join(tmpdir, "importcfg")
341 testenv.WriteImportcfg(t, importcfgfile, nil, "runtime")
342
343
344 runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s")
345 runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go")
346 runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s")
347 run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)
348 runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")
349
350
351 cmd := linkCmd(t, "-importcfg="+importcfgfile, "-linkmode=internal", "x.a")
352 cmd.Dir = tmpdir
353 out, err := cmd.CombinedOutput()
354 if err == nil {
355 t.Fatalf("expected link to fail, but it succeeded")
356 }
357 re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)
358 if !re.Match(out) {
359 t.Fatalf("got:\n%q\nwant:\n%s", out, re)
360 }
361 }
362
363 func TestBuildForTvOS(t *testing.T) {
364 testenv.MustHaveCGO(t)
365 testenv.MustHaveGoBuild(t)
366
367
368 if runtime.GOOS != "darwin" {
369 t.Skip("skipping on non-darwin platform")
370 }
371 if testing.Short() && testenv.Builder() == "" {
372 t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
373 }
374 if err := testenv.Command(t, "xcrun", "--help").Run(); err != nil {
375 t.Skipf("error running xcrun, required for iOS cross build: %v", err)
376 }
377
378 t.Parallel()
379
380 sdkPath, err := testenv.Command(t, "xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
381 if err != nil {
382 t.Skip("failed to locate appletvos SDK, skipping")
383 }
384 CC := []string{
385 "clang",
386 "-arch",
387 "arm64",
388 "-isysroot", strings.TrimSpace(string(sdkPath)),
389 "-mtvos-version-min=12.0",
390 "-fembed-bitcode",
391 }
392 CGO_LDFLAGS := []string{"-framework", "CoreFoundation"}
393 lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
394 tmpDir := t.TempDir()
395
396 ar := filepath.Join(tmpDir, "lib.a")
397 cmd := goCmd(t, "build", "-buildmode=c-archive", "-o", ar, lib)
398 env := []string{
399 "CGO_ENABLED=1",
400 "GOOS=ios",
401 "GOARCH=arm64",
402 "CC=" + strings.Join(CC, " "),
403 "CGO_CFLAGS=",
404 "CGO_LDFLAGS=" + strings.Join(CGO_LDFLAGS, " "),
405 }
406 cmd.Env = append(cmd.Env, env...)
407 t.Logf("%q %v", env, cmd)
408 if out, err := cmd.CombinedOutput(); err != nil {
409 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
410 }
411
412 link := testenv.Command(t, CC[0], CC[1:]...)
413 link.Args = append(link.Args, CGO_LDFLAGS...)
414 link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out"))
415 link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
416 t.Log(link)
417 if out, err := link.CombinedOutput(); err != nil {
418 t.Fatalf("%v: %v:\n%s", link.Args, err, out)
419 }
420 }
421
422 var testXFlagSrc = `
423 package main
424 var X = "hello"
425 var Z = [99999]int{99998:12345} // make it large enough to be mmaped
426 func main() { println(X) }
427 `
428
429 func TestXFlag(t *testing.T) {
430 testenv.MustHaveGoBuild(t)
431
432 t.Parallel()
433
434 tmpdir := t.TempDir()
435
436 src := filepath.Join(tmpdir, "main.go")
437 err := os.WriteFile(src, []byte(testXFlagSrc), 0666)
438 if err != nil {
439 t.Fatal(err)
440 }
441
442 cmd := goCmd(t, "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
443 if out, err := cmd.CombinedOutput(); err != nil {
444 t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
445 }
446 }
447
448 var trivialSrc = `
449 package main
450 func main() { }
451 `
452
453 func TestMachOBuildVersion(t *testing.T) {
454 testenv.MustHaveGoBuild(t)
455 if runtime.GOOS != "darwin" {
456 t.Skip("skip on non-Mach-O platforms")
457 }
458 t.Parallel()
459
460 tmpdir := t.TempDir()
461
462 src := filepath.Join(tmpdir, "main.go")
463 err := os.WriteFile(src, []byte(trivialSrc), 0666)
464 if err != nil {
465 t.Fatal(err)
466 }
467
468 exe := filepath.Join(tmpdir, "main")
469 cmd := goCmd(t, "build", "-ldflags=-linkmode=internal", "-o", exe, src)
470 cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
471 if out, err := cmd.CombinedOutput(); err != nil {
472 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
473 }
474 exef, err := os.Open(exe)
475 if err != nil {
476 t.Fatal(err)
477 }
478 defer exef.Close()
479 exem, err := macho.NewFile(exef)
480 if err != nil {
481 t.Fatal(err)
482 }
483 found := false
484 checkMin := func(ver uint32) {
485 major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
486 if major < 13 {
487 t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 13.0.0", major, minor, patch)
488 }
489 }
490 for _, cmd := range exem.Loads {
491 raw := cmd.Raw()
492 type_ := exem.ByteOrder.Uint32(raw)
493 if type_ != imacho.LC_BUILD_VERSION {
494 continue
495 }
496 osVer := exem.ByteOrder.Uint32(raw[12:])
497 checkMin(osVer)
498 sdkVer := exem.ByteOrder.Uint32(raw[16:])
499 checkMin(sdkVer)
500 found = true
501 break
502 }
503 if !found {
504 t.Errorf("no LC_BUILD_VERSION load command found")
505 }
506 }
507
508 func TestMachOUUID(t *testing.T) {
509 testenv.MustHaveGoBuild(t)
510 if runtime.GOOS != "darwin" {
511 t.Skip("this is only for darwin")
512 }
513
514 t.Parallel()
515
516 tmpdir := t.TempDir()
517
518 src := filepath.Join(tmpdir, "main.go")
519 err := os.WriteFile(src, []byte(trivialSrc), 0666)
520 if err != nil {
521 t.Fatal(err)
522 }
523
524 extractUUID := func(exe string) string {
525 exem, err := macho.Open(exe)
526 if err != nil {
527 t.Fatal(err)
528 }
529 defer exem.Close()
530 for _, cmd := range exem.Loads {
531 raw := cmd.Raw()
532 type_ := exem.ByteOrder.Uint32(raw)
533 if type_ != imacho.LC_UUID {
534 continue
535 }
536 return string(raw[8:24])
537 }
538 return ""
539 }
540
541 tests := []struct{ name, ldflags, expect string }{
542 {"default", "", "gobuildid"},
543 {"gobuildid", "-B=gobuildid", "gobuildid"},
544 {"specific", "-B=0x0123456789ABCDEF0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
545 {"none", "-B=none", ""},
546 }
547 if testenv.HasCGO() {
548 for _, test := range tests {
549 t1 := test
550 t1.name += "_external"
551 t1.ldflags += " -linkmode=external"
552 tests = append(tests, t1)
553 }
554 }
555 for _, test := range tests {
556 t.Run(test.name, func(t *testing.T) {
557 exe := filepath.Join(tmpdir, test.name)
558 cmd := goCmd(t, "build", "-ldflags="+test.ldflags, "-o", exe, src)
559 if out, err := cmd.CombinedOutput(); err != nil {
560 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
561 }
562 uuid := extractUUID(exe)
563 if test.expect == "gobuildid" {
564
565
566 if uuid == "" {
567 t.Fatal("expect nonempty UUID, got empty")
568 }
569
570 if uuid[6]>>4 != 3 {
571 t.Errorf("expect v3 UUID, got %X (version %d)", uuid, uuid[6]>>4)
572 }
573 } else if uuid != test.expect {
574 t.Errorf("UUID mismatch: got %X, want %X", uuid, test.expect)
575 }
576 })
577 }
578 }
579
580 const Issue34788src = `
581
582 package blah
583
584 func Blah(i int) int {
585 a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
586 return a[i&7]
587 }
588 `
589
590 func TestIssue34788Android386TLSSequence(t *testing.T) {
591 testenv.MustHaveGoBuild(t)
592
593
594
595
596 if runtime.GOARCH != "amd64" ||
597 (runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
598 t.Skip("skipping on non-{linux,darwin}/amd64 platform")
599 }
600
601 t.Parallel()
602
603 tmpdir := t.TempDir()
604
605 src := filepath.Join(tmpdir, "blah.go")
606 err := os.WriteFile(src, []byte(Issue34788src), 0666)
607 if err != nil {
608 t.Fatal(err)
609 }
610
611 obj := filepath.Join(tmpdir, "blah.o")
612 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src)
613 cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
614 if out, err := cmd.CombinedOutput(); err != nil {
615 t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
616 }
617
618
619 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "objdump", obj)
620 out, oerr := cmd.CombinedOutput()
621 if oerr != nil {
622 t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
623 }
624
625
626 scanner := bufio.NewScanner(bytes.NewReader(out))
627 for scanner.Scan() {
628 line := scanner.Text()
629 if strings.Contains(line, "R_TLS_LE") {
630 t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
631 }
632 }
633 }
634
635 const testStrictDupGoSrc = `
636 package main
637 func f()
638 func main() { f() }
639 `
640
641 const testStrictDupAsmSrc1 = `
642 #include "textflag.h"
643 TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
644 RET
645 `
646
647 const testStrictDupAsmSrc2 = `
648 #include "textflag.h"
649 TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
650 JMP 0(PC)
651 `
652
653 const testStrictDupAsmSrc3 = `
654 #include "textflag.h"
655 GLOBL ·rcon(SB), RODATA|DUPOK, $64
656 `
657
658 const testStrictDupAsmSrc4 = `
659 #include "textflag.h"
660 GLOBL ·rcon(SB), RODATA|DUPOK, $32
661 `
662
663 func TestStrictDup(t *testing.T) {
664
665 testenv.MustHaveGoBuild(t)
666
667 asmfiles := []struct {
668 fname string
669 payload string
670 }{
671 {"a", testStrictDupAsmSrc1},
672 {"b", testStrictDupAsmSrc2},
673 {"c", testStrictDupAsmSrc3},
674 {"d", testStrictDupAsmSrc4},
675 }
676
677 t.Parallel()
678
679 tmpdir := t.TempDir()
680
681 src := filepath.Join(tmpdir, "x.go")
682 err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
683 if err != nil {
684 t.Fatal(err)
685 }
686 for _, af := range asmfiles {
687 src = filepath.Join(tmpdir, af.fname+".s")
688 err = os.WriteFile(src, []byte(af.payload), 0666)
689 if err != nil {
690 t.Fatal(err)
691 }
692 }
693 src = filepath.Join(tmpdir, "go.mod")
694 err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666)
695 if err != nil {
696 t.Fatal(err)
697 }
698
699 cmd := goCmd(t, "build", "-ldflags=-strictdups=1")
700 cmd.Dir = tmpdir
701 out, err := cmd.CombinedOutput()
702 if err != nil {
703 t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
704 }
705 if !bytes.Contains(out, []byte("mismatched payload")) {
706 t.Errorf("unexpected output:\n%s", out)
707 }
708
709 cmd = goCmd(t, "build", "-ldflags=-strictdups=2")
710 cmd.Dir = tmpdir
711 out, err = cmd.CombinedOutput()
712 if err == nil {
713 t.Errorf("linking with -strictdups=2 did not fail")
714 }
715
716
717 if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
718 bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
719 !bytes.Contains(out, []byte("mismatched payload: different sizes")) {
720 t.Errorf("unexpected output:\n%s", out)
721 }
722 }
723
724 const testFuncAlignSrc = `
725 package main
726 import (
727 "fmt"
728 )
729 func alignPc()
730 var alignPcFnAddr uintptr
731
732 func main() {
733 if alignPcFnAddr % 512 != 0 {
734 fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)
735 } else {
736 fmt.Printf("PASS")
737 }
738 }
739 `
740
741 var testFuncAlignAsmSources = map[string]string{
742 "arm64": `
743 #include "textflag.h"
744
745 TEXT ·alignPc(SB),NOSPLIT, $0-0
746 MOVD $2, R0
747 PCALIGN $512
748 MOVD $3, R1
749 RET
750
751 GLOBL ·alignPcFnAddr(SB),RODATA,$8
752 DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)
753 `,
754 "loong64": `
755 #include "textflag.h"
756
757 TEXT ·alignPc(SB),NOSPLIT, $0-0
758 MOVV $2, R4
759 PCALIGN $512
760 MOVV $3, R5
761 RET
762
763 GLOBL ·alignPcFnAddr(SB),RODATA,$8
764 DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)
765 `,
766 }
767
768
769
770 func TestFuncAlign(t *testing.T) {
771 testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH]
772 if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" {
773 t.Skip("skipping on non-linux/{arm64,loong64} platform")
774 }
775 testenv.MustHaveGoBuild(t)
776
777 t.Parallel()
778
779 tmpdir := t.TempDir()
780
781 src := filepath.Join(tmpdir, "go.mod")
782 err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
783 if err != nil {
784 t.Fatal(err)
785 }
786 src = filepath.Join(tmpdir, "falign.go")
787 err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666)
788 if err != nil {
789 t.Fatal(err)
790 }
791 src = filepath.Join(tmpdir, "falign.s")
792 err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
793 if err != nil {
794 t.Fatal(err)
795 }
796
797 cmd := goCmd(t, "build", "-o", "falign")
798 cmd.Dir = tmpdir
799 out, err := cmd.CombinedOutput()
800 if err != nil {
801 t.Errorf("build failed: %v", err)
802 }
803 cmd = testenv.Command(t, tmpdir+"/falign")
804 out, err = cmd.CombinedOutput()
805 if err != nil {
806 t.Errorf("failed to run with err %v, output: %s", err, out)
807 }
808 if string(out) != "PASS" {
809 t.Errorf("unexpected output: %s\n", out)
810 }
811 }
812
813 const testFuncAlignOptionSrc = `
814 package main
815 //go:noinline
816 func foo() {
817 }
818 //go:noinline
819 func bar() {
820 }
821 //go:noinline
822 func baz() {
823 }
824 func main() {
825 foo()
826 bar()
827 baz()
828 }
829 `
830
831
832 func TestFuncAlignOption(t *testing.T) {
833 testenv.MustHaveGoBuild(t)
834
835 t.Parallel()
836
837 tmpdir := t.TempDir()
838
839 src := filepath.Join(tmpdir, "falign.go")
840 err := os.WriteFile(src, []byte(testFuncAlignOptionSrc), 0666)
841 if err != nil {
842 t.Fatal(err)
843 }
844
845 alignTest := func(align uint64) {
846 exeName := "falign.exe"
847 cmd := goCmd(t, "build", "-ldflags=-funcalign="+strconv.FormatUint(align, 10), "-o", exeName, "falign.go")
848 cmd.Dir = tmpdir
849 out, err := cmd.CombinedOutput()
850 if err != nil {
851 t.Errorf("build failed: %v \n%s", err, out)
852 }
853 exe := filepath.Join(tmpdir, exeName)
854 cmd = testenv.Command(t, exe)
855 out, err = cmd.CombinedOutput()
856 if err != nil {
857 t.Errorf("failed to run with err %v, output: %s", err, out)
858 }
859
860
861 f, err := objfile.Open(exe)
862 if err != nil {
863 t.Fatalf("failed to open file:%v\n", err)
864 }
865 defer f.Close()
866
867 fname := map[string]bool{"_main.foo": false,
868 "_main.bar": false,
869 "_main.baz": false}
870 syms, err := f.Symbols()
871 if err != nil {
872 t.Errorf("failed to get symbols with err %v", err)
873 }
874 for _, s := range syms {
875 fn := s.Name
876 if _, ok := fname[fn]; !ok {
877 fn = "_" + s.Name
878 if _, ok := fname[fn]; !ok {
879 continue
880 }
881 }
882 if s.Addr%align != 0 {
883 t.Fatalf("unaligned function: %s %x. Expected alignment: %d\n", fn, s.Addr, align)
884 }
885 fname[fn] = true
886 }
887 for k, v := range fname {
888 if !v {
889 t.Fatalf("function %s not found\n", k)
890 }
891 }
892 }
893 alignTest(16)
894 alignTest(32)
895 }
896
897 const testTrampSrc = `
898 package main
899 import "fmt"
900 func main() {
901 fmt.Println("hello")
902
903 defer func(){
904 if e := recover(); e == nil {
905 panic("did not panic")
906 }
907 }()
908 f1()
909 }
910
911 // Test deferreturn trampolines. See issue #39049.
912 func f1() { defer f2() }
913 func f2() { panic("XXX") }
914 `
915
916 func TestTrampoline(t *testing.T) {
917
918
919
920
921 buildmodes := []string{"default"}
922 switch runtime.GOARCH {
923 case "arm", "arm64", "loong64":
924 case "ppc64le", "ppc64":
925 switch runtime.GOOS {
926 case "aix":
927 case "linux":
928
929 buildmodes = append(buildmodes, "pie")
930 default:
931 t.Skipf("trampoline insertion is not implemented on %s-%s", runtime.GOARCH, runtime.GOOS)
932 }
933 default:
934 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
935 }
936
937 testenv.MustHaveGoBuild(t)
938
939 t.Parallel()
940
941 tmpdir := t.TempDir()
942
943 src := filepath.Join(tmpdir, "hello.go")
944 err := os.WriteFile(src, []byte(testTrampSrc), 0666)
945 if err != nil {
946 t.Fatal(err)
947 }
948 exe := filepath.Join(tmpdir, "hello.exe")
949
950 for _, mode := range buildmodes {
951 cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
952 out, err := cmd.CombinedOutput()
953 if err != nil {
954 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
955 }
956 cmd = testenv.Command(t, exe)
957 out, err = cmd.CombinedOutput()
958 if err != nil {
959 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
960 }
961 if string(out) != "hello\n" {
962 t.Errorf("unexpected output (%s):\n%s", mode, out)
963 }
964
965 out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
966 if err != nil {
967 t.Errorf("nm failure: %s\n%s\n", err, string(out))
968 }
969 if ok, _ := regexp.Match("T runtime.deferreturn(\\+0)?-tramp0", out); !ok {
970 t.Errorf("Trampoline T runtime.deferreturn(+0)?-tramp0 is missing")
971 }
972 }
973 }
974
975 const testTrampCgoSrc = `
976 package main
977
978 // #include <stdio.h>
979 // void CHello() { printf("hello\n"); fflush(stdout); }
980 import "C"
981
982 func main() {
983 C.CHello()
984 }
985 `
986
987 func TestTrampolineCgo(t *testing.T) {
988
989
990
991
992 buildmodes := []string{"default"}
993 switch runtime.GOARCH {
994 case "arm", "arm64", "loong64":
995 case "ppc64le", "ppc64":
996
997 buildmodes = append(buildmodes, "pie")
998 default:
999 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
1000 }
1001
1002 testenv.MustHaveGoBuild(t)
1003 testenv.MustHaveCGO(t)
1004
1005 t.Parallel()
1006
1007 tmpdir := t.TempDir()
1008
1009 src := filepath.Join(tmpdir, "hello.go")
1010 err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
1011 if err != nil {
1012 t.Fatal(err)
1013 }
1014 exe := filepath.Join(tmpdir, "hello.exe")
1015
1016 for _, mode := range buildmodes {
1017 cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
1018 out, err := cmd.CombinedOutput()
1019 if err != nil {
1020 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
1021 }
1022 cmd = testenv.Command(t, exe)
1023 out, err = cmd.CombinedOutput()
1024 if err != nil {
1025 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1026 }
1027 if string(out) != "hello\n" && string(out) != "hello\r\n" {
1028 t.Errorf("unexpected output (%s):\n%s", mode, out)
1029 }
1030
1031
1032
1033 if !testenv.CanInternalLink(true) {
1034 continue
1035 }
1036 cmd = goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
1037 out, err = cmd.CombinedOutput()
1038 if err != nil {
1039 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
1040 }
1041 cmd = testenv.Command(t, exe)
1042 out, err = cmd.CombinedOutput()
1043 if err != nil {
1044 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1045 }
1046 if string(out) != "hello\n" && string(out) != "hello\r\n" {
1047 t.Errorf("unexpected output (%s):\n%s", mode, out)
1048 }
1049 }
1050 }
1051
1052 func TestIndexMismatch(t *testing.T) {
1053
1054
1055
1056 testenv.MustHaveGoBuild(t)
1057
1058
1059 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
1060
1061 t.Parallel()
1062
1063 tmpdir := t.TempDir()
1064
1065 aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
1066 bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
1067 mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
1068 aObj := filepath.Join(tmpdir, "a.o")
1069 mObj := filepath.Join(tmpdir, "main.o")
1070 exe := filepath.Join(tmpdir, "main.exe")
1071
1072 importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")
1073 testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")
1074 importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
1075 testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")
1076
1077
1078 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
1079 t.Log(cmd)
1080 out, err := cmd.CombinedOutput()
1081 if err != nil {
1082 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
1083 }
1084 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
1085 t.Log(cmd)
1086 out, err = cmd.CombinedOutput()
1087 if err != nil {
1088 t.Fatalf("compiling main.go failed: %v\n%s", err, out)
1089 }
1090 cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
1091 t.Log(cmd)
1092 out, err = cmd.CombinedOutput()
1093 if err != nil {
1094 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
1095 testenv.SkipFlaky(t, 58806)
1096 }
1097 t.Errorf("linking failed: %v\n%s", err, out)
1098 }
1099
1100
1101
1102 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
1103 t.Log(cmd)
1104 out, err = cmd.CombinedOutput()
1105 if err != nil {
1106 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
1107 }
1108 cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
1109 t.Log(cmd)
1110 out, err = cmd.CombinedOutput()
1111 if err == nil {
1112 t.Fatalf("linking didn't fail")
1113 }
1114 if !bytes.Contains(out, []byte("fingerprint mismatch")) {
1115 t.Errorf("did not see expected error message. out:\n%s", out)
1116 }
1117 }
1118
1119 func TestPErsrcBinutils(t *testing.T) {
1120
1121 testenv.MustHaveGoBuild(t)
1122
1123 if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
1124
1125 t.Skipf("this is only for windows/amd64 and windows/386")
1126 }
1127
1128 t.Parallel()
1129
1130 tmpdir := t.TempDir()
1131
1132 pkgdir := filepath.Join("testdata", "pe-binutils")
1133 exe := filepath.Join(tmpdir, "a.exe")
1134 cmd := goCmd(t, "build", "-o", exe)
1135 cmd.Dir = pkgdir
1136
1137 out, err := cmd.CombinedOutput()
1138 if err != nil {
1139 t.Fatalf("building failed: %v, output:\n%s", err, out)
1140 }
1141
1142
1143 b, err := os.ReadFile(exe)
1144 if err != nil {
1145 t.Fatalf("reading output failed: %v", err)
1146 }
1147 if !bytes.Contains(b, []byte("Hello Gophers!")) {
1148 t.Fatalf("binary does not contain expected content")
1149 }
1150 }
1151
1152 func TestPErsrcLLVM(t *testing.T) {
1153
1154 testenv.MustHaveGoBuild(t)
1155
1156 if runtime.GOOS != "windows" {
1157 t.Skipf("this is a windows-only test")
1158 }
1159
1160 t.Parallel()
1161
1162 tmpdir := t.TempDir()
1163
1164 pkgdir := filepath.Join("testdata", "pe-llvm")
1165 exe := filepath.Join(tmpdir, "a.exe")
1166 cmd := goCmd(t, "build", "-o", exe)
1167 cmd.Dir = pkgdir
1168
1169 out, err := cmd.CombinedOutput()
1170 if err != nil {
1171 t.Fatalf("building failed: %v, output:\n%s", err, out)
1172 }
1173
1174
1175 b, err := os.ReadFile(exe)
1176 if err != nil {
1177 t.Fatalf("reading output failed: %v", err)
1178 }
1179 if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
1180 t.Fatalf("binary does not contain expected content")
1181 }
1182 }
1183
1184 func TestContentAddressableSymbols(t *testing.T) {
1185
1186 testenv.MustHaveGoBuild(t)
1187
1188 t.Parallel()
1189
1190 src := filepath.Join("testdata", "testHashedSyms", "p.go")
1191 cmd := goCmd(t, "run", src)
1192 out, err := cmd.CombinedOutput()
1193 if err != nil {
1194 t.Errorf("command %s failed: %v\n%s", cmd, err, out)
1195 }
1196 }
1197
1198 func TestReadOnly(t *testing.T) {
1199
1200 testenv.MustHaveGoBuild(t)
1201
1202 t.Parallel()
1203
1204 src := filepath.Join("testdata", "testRO", "x.go")
1205 cmd := goCmd(t, "run", src)
1206 out, err := cmd.CombinedOutput()
1207 if err == nil {
1208 t.Errorf("running test program did not fail. output:\n%s", out)
1209 }
1210 }
1211
1212 const testIssue38554Src = `
1213 package main
1214
1215 type T [10<<20]byte
1216
1217 //go:noinline
1218 func f() T {
1219 return T{} // compiler will make a large stmp symbol, but not used.
1220 }
1221
1222 func main() {
1223 x := f()
1224 println(x[1])
1225 }
1226 `
1227
1228 func TestIssue38554(t *testing.T) {
1229 testenv.MustHaveGoBuild(t)
1230
1231 t.Parallel()
1232
1233 tmpdir := t.TempDir()
1234
1235 src := filepath.Join(tmpdir, "x.go")
1236 err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
1237 if err != nil {
1238 t.Fatalf("failed to write source file: %v", err)
1239 }
1240 exe := filepath.Join(tmpdir, "x.exe")
1241 cmd := goCmd(t, "build", "-o", exe, src)
1242 out, err := cmd.CombinedOutput()
1243 if err != nil {
1244 t.Fatalf("build failed: %v\n%s", err, out)
1245 }
1246
1247 fi, err := os.Stat(exe)
1248 if err != nil {
1249 t.Fatalf("failed to stat output file: %v", err)
1250 }
1251
1252
1253
1254
1255 const want = 5 << 20
1256 if got := fi.Size(); got > want {
1257 t.Errorf("binary too big: got %d, want < %d", got, want)
1258 }
1259 }
1260
1261 const testIssue42396src = `
1262 package main
1263
1264 //go:noinline
1265 //go:nosplit
1266 func callee(x int) {
1267 }
1268
1269 func main() {
1270 callee(9)
1271 }
1272 `
1273
1274 func TestIssue42396(t *testing.T) {
1275 testenv.MustHaveGoBuild(t)
1276
1277 if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
1278 t.Skip("no race detector support")
1279 }
1280
1281 t.Parallel()
1282
1283 tmpdir := t.TempDir()
1284
1285 src := filepath.Join(tmpdir, "main.go")
1286 err := os.WriteFile(src, []byte(testIssue42396src), 0666)
1287 if err != nil {
1288 t.Fatalf("failed to write source file: %v", err)
1289 }
1290 exe := filepath.Join(tmpdir, "main.exe")
1291 cmd := goCmd(t, "build", "-gcflags=-race", "-o", exe, src)
1292 out, err := cmd.CombinedOutput()
1293 if err == nil {
1294 t.Fatalf("build unexpectedly succeeded")
1295 }
1296
1297
1298
1299 if strings.Contains(string(out), "panic:") {
1300 t.Fatalf("build should not fail with panic:\n%s", out)
1301 }
1302 const want = "reference to undefined builtin"
1303 if !strings.Contains(string(out), want) {
1304 t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
1305 }
1306 }
1307
1308 const testLargeRelocSrc = `
1309 package main
1310
1311 var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
1312
1313 var addr = [...]*byte{
1314 &x[1<<23-1],
1315 &x[1<<23],
1316 &x[1<<23+1],
1317 &x[1<<24-1],
1318 &x[1<<24],
1319 &x[1<<24+1],
1320 }
1321
1322 func main() {
1323 // check relocations in instructions
1324 check(x[1<<23-1], 0)
1325 check(x[1<<23], 23)
1326 check(x[1<<23+1], 0)
1327 check(x[1<<24-1], 0)
1328 check(x[1<<24], 24)
1329 check(x[1<<24+1], 0)
1330
1331 // check absolute address relocations in data
1332 check(*addr[0], 0)
1333 check(*addr[1], 23)
1334 check(*addr[2], 0)
1335 check(*addr[3], 0)
1336 check(*addr[4], 24)
1337 check(*addr[5], 0)
1338 }
1339
1340 func check(x, y byte) {
1341 if x != y {
1342 panic("FAIL")
1343 }
1344 }
1345 `
1346
1347 func TestLargeReloc(t *testing.T) {
1348
1349
1350
1351 testenv.MustHaveGoBuild(t)
1352 t.Parallel()
1353
1354 tmpdir := t.TempDir()
1355
1356 src := filepath.Join(tmpdir, "x.go")
1357 err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
1358 if err != nil {
1359 t.Fatalf("failed to write source file: %v", err)
1360 }
1361 cmd := goCmd(t, "run", src)
1362 out, err := cmd.CombinedOutput()
1363 if err != nil {
1364 t.Errorf("build failed: %v. output:\n%s", err, out)
1365 }
1366
1367 if testenv.HasCGO() {
1368 cmd = goCmd(t, "run", "-ldflags=-linkmode=external", src)
1369 out, err = cmd.CombinedOutput()
1370 if err != nil {
1371 t.Fatalf("build failed: %v. output:\n%s", err, out)
1372 }
1373 }
1374 }
1375
1376 func TestUnlinkableObj(t *testing.T) {
1377
1378 testenv.MustHaveGoBuild(t)
1379 t.Parallel()
1380
1381 if true {
1382 t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
1383 }
1384
1385 tmpdir := t.TempDir()
1386
1387 xSrc := filepath.Join(tmpdir, "x.go")
1388 pSrc := filepath.Join(tmpdir, "p.go")
1389 xObj := filepath.Join(tmpdir, "x.o")
1390 pObj := filepath.Join(tmpdir, "p.o")
1391 exe := filepath.Join(tmpdir, "x.exe")
1392 importcfgfile := filepath.Join(tmpdir, "importcfg")
1393 testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
1394 err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
1395 if err != nil {
1396 t.Fatalf("failed to write source file: %v", err)
1397 }
1398 err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
1399 if err != nil {
1400 t.Fatalf("failed to write source file: %v", err)
1401 }
1402 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc)
1403 out, err := cmd.CombinedOutput()
1404 if err != nil {
1405 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1406 }
1407 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
1408 out, err = cmd.CombinedOutput()
1409 if err != nil {
1410 t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
1411 }
1412 cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
1413 out, err = cmd.CombinedOutput()
1414 if err == nil {
1415 t.Fatalf("link did not fail")
1416 }
1417 if !bytes.Contains(out, []byte("unlinkable object")) {
1418 t.Errorf("did not see expected error message. out:\n%s", out)
1419 }
1420
1421
1422 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
1423 out, err = cmd.CombinedOutput()
1424 if err != nil {
1425 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1426 }
1427 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc)
1428 out, err = cmd.CombinedOutput()
1429 if err != nil {
1430 t.Fatalf("compile failed: %v. output:\n%s", err, out)
1431 }
1432
1433 cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
1434 out, err = cmd.CombinedOutput()
1435 if err != nil {
1436 t.Errorf("link failed: %v. output:\n%s", err, out)
1437 }
1438 }
1439
1440 func TestExtLinkCmdlineDeterminism(t *testing.T) {
1441
1442 testenv.MustHaveGoBuild(t)
1443 testenv.MustHaveCGO(t)
1444 t.Parallel()
1445
1446
1447 testSrc := `
1448 package main
1449 import "C"
1450 //export F1
1451 func F1() {}
1452 //export F2
1453 func F2() {}
1454 //export F3
1455 func F3() {}
1456 func main() {}
1457 `
1458
1459 tmpdir := t.TempDir()
1460 src := filepath.Join(tmpdir, "x.go")
1461 if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {
1462 t.Fatal(err)
1463 }
1464 exe := filepath.Join(tmpdir, "x.exe")
1465
1466
1467
1468 linktmp := filepath.Join(tmpdir, "linktmp")
1469 if err := os.Mkdir(linktmp, 0777); err != nil {
1470 t.Fatal(err)
1471 }
1472
1473
1474
1475 ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp
1476 var out0, fullOut0 []byte
1477 for i := 0; i < 5; i++ {
1478 cmd := goCmd(t, "build", ldflags, "-x", "-o", exe, src)
1479 fullOut, err := cmd.CombinedOutput()
1480 out := fullOut
1481 if err != nil {
1482 t.Fatalf("build failed: %v, output:\n%s", err, out)
1483 }
1484 if err := os.Remove(exe); err != nil {
1485 t.Fatal(err)
1486 }
1487
1488
1489 j := bytes.Index(out, []byte("\nhost link:"))
1490 if j == -1 {
1491 t.Fatalf("host link step not found, output:\n%s", out)
1492 }
1493 out = out[j+1:]
1494 k := bytes.Index(out, []byte("\n"))
1495 if k == -1 {
1496 t.Fatalf("no newline after host link, output:\n%s", out)
1497 }
1498 out = out[:k]
1499
1500
1501
1502 fs := bytes.Fields(out)
1503 for i, f := range fs {
1504 if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {
1505 fs[i+1] = []byte("a.out")
1506 break
1507 }
1508 }
1509 out = bytes.Join(fs, []byte{' '})
1510
1511 if i == 0 {
1512 out0 = out
1513 fullOut0 = fullOut
1514 continue
1515 }
1516 if !bytes.Equal(out0, out) {
1517 t.Fatalf("output differ:\n%s\n==========\n%s\n\nfull output:\n%s\n==========\n%s",
1518 out0, out, fullOut0, fullOut)
1519 }
1520 }
1521 }
1522
1523
1524
1525 func TestResponseFile(t *testing.T) {
1526 t.Parallel()
1527
1528 testenv.MustHaveGoBuild(t)
1529
1530
1531
1532 testenv.MustHaveCGO(t)
1533
1534 tmpdir := t.TempDir()
1535
1536 src := filepath.Join(tmpdir, "x.go")
1537 if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {
1538 t.Fatal(err)
1539 }
1540
1541
1542
1543
1544 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")
1545 cmd.Dir = tmpdir
1546
1547
1548 var sb strings.Builder
1549 sb.WriteString(`'-ldflags=all="-extldflags=`)
1550 for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {
1551 if i > 0 {
1552 sb.WriteString(" ")
1553 }
1554 sb.WriteString("-g")
1555 }
1556 sb.WriteString(`"'`)
1557 cmd = testenv.CleanCmdEnv(cmd)
1558 cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())
1559
1560 out, err := cmd.CombinedOutput()
1561 if len(out) > 0 {
1562 t.Logf("%s", out)
1563 }
1564 if err != nil {
1565 t.Error(err)
1566 }
1567 }
1568
1569 func TestDynimportVar(t *testing.T) {
1570
1571
1572 if runtime.GOOS != "darwin" {
1573 t.Skip("skip on non-darwin platform")
1574 }
1575
1576 testenv.MustHaveGoBuild(t)
1577 testenv.MustHaveCGO(t)
1578
1579 t.Parallel()
1580
1581 tmpdir := t.TempDir()
1582 exe := filepath.Join(tmpdir, "a.exe")
1583 src := filepath.Join("testdata", "dynimportvar", "main.go")
1584
1585 for _, mode := range []string{"internal", "external"} {
1586 cmd := goCmd(t, "build", "-ldflags=-linkmode="+mode, "-o", exe, src)
1587 out, err := cmd.CombinedOutput()
1588 if err != nil {
1589 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1590 }
1591 cmd = testenv.Command(t, exe)
1592 out, err = cmd.CombinedOutput()
1593 if err != nil {
1594 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1595 }
1596 }
1597 }
1598
1599 const helloSrc = `
1600 package main
1601 var X = 42
1602 var Y int
1603 func main() { println("hello", X, Y) }
1604 `
1605
1606 func TestFlagS(t *testing.T) {
1607
1608 testenv.MustHaveGoBuild(t)
1609
1610 t.Parallel()
1611
1612 tmpdir := t.TempDir()
1613 exe := filepath.Join(tmpdir, "a.exe")
1614 src := filepath.Join(tmpdir, "a.go")
1615 err := os.WriteFile(src, []byte(helloSrc), 0666)
1616 if err != nil {
1617 t.Fatal(err)
1618 }
1619
1620 modes := []string{"auto"}
1621 if testenv.HasCGO() {
1622 modes = append(modes, "external")
1623 }
1624
1625
1626 syms := []string{"main.main", "main.X", "main.Y"}
1627
1628 for _, mode := range modes {
1629 cmd := goCmd(t, "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)
1630 out, err := cmd.CombinedOutput()
1631 if err != nil {
1632 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1633 }
1634 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1635 out, err = cmd.CombinedOutput()
1636 if err != nil {
1637 if _, ok := errors.AsType[*exec.ExitError](err); !ok {
1638
1639
1640
1641 t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)
1642 }
1643 }
1644 for _, s := range syms {
1645 if bytes.Contains(out, []byte(s)) {
1646 t.Errorf("(mode=%s): unexpected symbol %s", mode, s)
1647 }
1648 }
1649 }
1650 }
1651
1652 func TestRandLayout(t *testing.T) {
1653
1654
1655 testenv.MustHaveGoBuild(t)
1656
1657 t.Parallel()
1658
1659 tmpdir := t.TempDir()
1660
1661 src := filepath.Join(tmpdir, "hello.go")
1662 err := os.WriteFile(src, []byte(trivialSrc), 0666)
1663 if err != nil {
1664 t.Fatal(err)
1665 }
1666
1667 var syms [2]string
1668 for i, seed := range []string{"123", "456"} {
1669 exe := filepath.Join(tmpdir, "hello"+seed+".exe")
1670 cmd := goCmd(t, "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
1671 out, err := cmd.CombinedOutput()
1672 if err != nil {
1673 t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)
1674 }
1675 cmd = testenv.Command(t, exe)
1676 err = cmd.Run()
1677 if err != nil {
1678 t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)
1679 }
1680 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1681 out, err = cmd.CombinedOutput()
1682 if err != nil {
1683 t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)
1684 }
1685 syms[i] = string(out)
1686 }
1687 if syms[0] == syms[1] {
1688 t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
1689 }
1690 }
1691
1692 func TestCheckLinkname(t *testing.T) {
1693
1694 testenv.MustHaveGoBuild(t)
1695 t.Parallel()
1696
1697 tmpdir := t.TempDir()
1698
1699 tests := []struct {
1700 src string
1701 ok bool
1702 }{
1703
1704 {"ok.go", true},
1705
1706 {"push.go", true},
1707
1708
1709 {"textvar", true},
1710
1711 {"coro.go", false},
1712 {"coro_var.go", false},
1713
1714 {"coro_asm", false},
1715
1716 {"coro2.go", false},
1717
1718 {"builtin.go", false},
1719
1720
1721 {"systemstack.go", !buildcfg.Experiment.RegabiWrappers},
1722
1723 {"addmoduledata.go", false},
1724 {"freegc.go", false},
1725
1726 {"fastrand.go", true},
1727 {"badlinkname.go", true},
1728 }
1729 for _, test := range tests {
1730 t.Run(test.src, func(t *testing.T) {
1731 t.Parallel()
1732 src := "./testdata/linkname/" + test.src
1733 exe := filepath.Join(tmpdir, test.src+".exe")
1734 cmd := goCmd(t, "build", "-o", exe, src)
1735 out, err := cmd.CombinedOutput()
1736 if test.ok && err != nil {
1737 t.Errorf("build failed unexpectedly: %v:\n%s", err, out)
1738 }
1739 if !test.ok && err == nil {
1740 t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out)
1741 }
1742 })
1743 }
1744 }
1745
1746 func TestLinknameBSS(t *testing.T) {
1747
1748
1749 testenv.MustHaveGoBuild(t)
1750 t.Parallel()
1751
1752 tmpdir := t.TempDir()
1753
1754 src := filepath.Join("testdata", "linkname", "sched.go")
1755 exe := filepath.Join(tmpdir, "sched.exe")
1756 cmd := goCmd(t, "build", "-o", exe, src)
1757 out, err := cmd.CombinedOutput()
1758 if err != nil {
1759 t.Fatalf("build failed unexpectedly: %v:\n%s", err, out)
1760 }
1761
1762
1763 f, err := objfile.Open(exe)
1764 if err != nil {
1765 t.Fatalf("fail to open executable: %v", err)
1766 }
1767 defer f.Close()
1768 syms, err := f.Symbols()
1769 if err != nil {
1770 t.Fatalf("fail to get symbols: %v", err)
1771 }
1772 found := false
1773 for _, s := range syms {
1774 if s.Name == "runtime.sched" || s.Name == "_runtime.sched" {
1775 found = true
1776 if s.Size < 100 {
1777
1778
1779
1780 t.Errorf("runtime.sched symbol size too small: want > 100, got %d", s.Size)
1781 }
1782 }
1783 }
1784 if !found {
1785 t.Errorf("runtime.sched symbol not found")
1786 }
1787
1788
1789 cmd = testenv.Command(t, exe)
1790 out, err = cmd.CombinedOutput()
1791 if err != nil {
1792 t.Errorf("executable failed to run: %v\n%s", err, out)
1793 }
1794 }
1795
1796
1797
1798 func setValueFromBytes[T any](p *T, s []byte) {
1799 copy(unsafe.Slice((*byte)(unsafe.Pointer(p)), unsafe.Sizeof(*p)), s)
1800 }
1801
1802
1803
1804
1805 func TestFuncdataPlacement(t *testing.T) {
1806 testenv.MustHaveGoBuild(t)
1807 t.Parallel()
1808
1809 tmpdir := t.TempDir()
1810 src := filepath.Join(tmpdir, "x.go")
1811 if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
1812 t.Fatal(err)
1813 }
1814
1815 exe := filepath.Join(tmpdir, "x.exe")
1816 cmd := goCmd(t, "build", "-o", exe, src)
1817 if out, err := cmd.CombinedOutput(); err != nil {
1818 t.Fatalf("build failed; %v, output:\n%s", err, out)
1819 }
1820
1821
1822
1823
1824
1825
1826
1827 ef, _ := elf.Open(exe)
1828 mf, _ := macho.Open(exe)
1829 pf, _ := pe.Open(exe)
1830 xf, _ := xcoff.Open(exe)
1831
1832 if ef == nil && mf == nil && pf == nil && xf == nil {
1833 t.Skip("unrecognized executable file format")
1834 }
1835
1836 const moddataSymName = "runtime.firstmoduledata"
1837 const gofuncSymName = "go:func.*"
1838 var (
1839 pclntab []byte
1840 pclntabAddr uint64
1841 pclntabEnd uint64
1842 moddataAddr uint64
1843 moddataBytes []byte
1844 gofuncAddr uint64
1845 imageBase uint64
1846 )
1847 switch {
1848 case ef != nil:
1849 defer ef.Close()
1850
1851 syms, err := ef.Symbols()
1852 if err != nil {
1853 t.Fatal(err)
1854 }
1855 for _, sym := range syms {
1856 switch sym.Name {
1857 case moddataSymName:
1858 moddataAddr = sym.Value
1859 case gofuncSymName:
1860 gofuncAddr = sym.Value
1861 }
1862 }
1863
1864 for _, sec := range ef.Sections {
1865 if sec.Name == ".gopclntab" {
1866 data, err := sec.Data()
1867 if err != nil {
1868 t.Fatal(err)
1869 }
1870 pclntab = data
1871 pclntabAddr = sec.Addr
1872 pclntabEnd = sec.Addr + sec.Size
1873 }
1874 if sec.Flags&elf.SHF_ALLOC != 0 && moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
1875 data, err := sec.Data()
1876 if err != nil {
1877 t.Fatal(err)
1878 }
1879 moddataBytes = data[moddataAddr-sec.Addr:]
1880 }
1881 }
1882
1883 case mf != nil:
1884 defer mf.Close()
1885
1886 for _, sym := range mf.Symtab.Syms {
1887 switch sym.Name {
1888 case moddataSymName:
1889 moddataAddr = sym.Value
1890 case gofuncSymName:
1891 gofuncAddr = sym.Value
1892 }
1893 }
1894
1895 for _, sec := range mf.Sections {
1896 if sec.Name == "__gopclntab" {
1897 data, err := sec.Data()
1898 if err != nil {
1899 t.Fatal(err)
1900 }
1901 pclntab = data
1902 pclntabAddr = sec.Addr
1903 pclntabEnd = sec.Addr + sec.Size
1904 }
1905 if moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
1906 data, err := sec.Data()
1907 if err != nil {
1908 t.Fatal(err)
1909 }
1910 moddataBytes = data[moddataAddr-sec.Addr:]
1911 }
1912 }
1913
1914 case pf != nil:
1915 defer pf.Close()
1916
1917 switch ohdr := pf.OptionalHeader.(type) {
1918 case *pe.OptionalHeader32:
1919 imageBase = uint64(ohdr.ImageBase)
1920 case *pe.OptionalHeader64:
1921 imageBase = ohdr.ImageBase
1922 }
1923
1924 var moddataSym, gofuncSym, pclntabSym, epclntabSym *pe.Symbol
1925 for _, sym := range pf.Symbols {
1926 switch sym.Name {
1927 case moddataSymName:
1928 moddataSym = sym
1929 case gofuncSymName:
1930 gofuncSym = sym
1931 case "runtime.pclntab":
1932 pclntabSym = sym
1933 case "runtime.epclntab":
1934 epclntabSym = sym
1935 }
1936 }
1937
1938 if moddataSym == nil {
1939 t.Fatalf("could not find symbol %s", moddataSymName)
1940 }
1941 if gofuncSym == nil {
1942 t.Fatalf("could not find symbol %s", gofuncSymName)
1943 }
1944 if pclntabSym == nil {
1945 t.Fatal("could not find symbol runtime.pclntab")
1946 }
1947 if epclntabSym == nil {
1948 t.Fatal("could not find symbol runtime.epclntab")
1949 }
1950
1951 sec := pf.Sections[moddataSym.SectionNumber-1]
1952 data, err := sec.Data()
1953 if err != nil {
1954 t.Fatal(err)
1955 }
1956 moddataBytes = data[moddataSym.Value:]
1957 moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
1958
1959 sec = pf.Sections[gofuncSym.SectionNumber-1]
1960 gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
1961
1962 if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
1963 t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
1964 }
1965 sec = pf.Sections[pclntabSym.SectionNumber-1]
1966 data, err = sec.Data()
1967 if err != nil {
1968 t.Fatal(err)
1969 }
1970 pclntab = data[pclntabSym.Value:epclntabSym.Value]
1971 pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
1972 pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
1973
1974 case xf != nil:
1975 defer xf.Close()
1976
1977 var moddataSym, gofuncSym, pclntabSym, epclntabSym *xcoff.Symbol
1978 for _, sym := range xf.Symbols {
1979 switch sym.Name {
1980 case moddataSymName:
1981 moddataSym = sym
1982 case gofuncSymName:
1983 gofuncSym = sym
1984 case "runtime.pclntab":
1985 pclntabSym = sym
1986 case "runtime.epclntab":
1987 epclntabSym = sym
1988 }
1989 }
1990
1991 if moddataSym == nil {
1992 t.Fatalf("could not find symbol %s", moddataSymName)
1993 }
1994 if gofuncSym == nil {
1995 t.Fatalf("could not find symbol %s", gofuncSymName)
1996 }
1997 if pclntabSym == nil {
1998 t.Fatal("could not find symbol runtime.pclntab")
1999 }
2000 if epclntabSym == nil {
2001 t.Fatal("could not find symbol runtime.epclntab")
2002 }
2003
2004 sec := xf.Sections[moddataSym.SectionNumber-1]
2005 data, err := sec.Data()
2006 if err != nil {
2007 t.Fatal(err)
2008 }
2009 moddataBytes = data[moddataSym.Value:]
2010 moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
2011
2012 sec = xf.Sections[gofuncSym.SectionNumber-1]
2013 gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
2014
2015 if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
2016 t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
2017 }
2018 sec = xf.Sections[pclntabSym.SectionNumber-1]
2019 data, err = sec.Data()
2020 if err != nil {
2021 t.Fatal(err)
2022 }
2023 pclntab = data[pclntabSym.Value:epclntabSym.Value]
2024 pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
2025 pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
2026
2027 default:
2028 panic("can't happen")
2029 }
2030
2031 if len(pclntab) == 0 {
2032 t.Fatal("could not find pclntab section")
2033 }
2034 if moddataAddr == 0 {
2035 t.Fatalf("could not find %s symbol", moddataSymName)
2036 }
2037 if gofuncAddr == 0 {
2038 t.Fatalf("could not find %s symbol", gofuncSymName)
2039 }
2040 if gofuncAddr < pclntabAddr || gofuncAddr >= pclntabEnd {
2041 t.Fatalf("%s out of range: value %#x not between %#x and %#x", gofuncSymName, gofuncAddr, pclntabAddr, pclntabEnd)
2042 }
2043 if len(moddataBytes) == 0 {
2044 t.Fatal("could not find module data")
2045 }
2046
2047
2048 type moddataSlice struct {
2049 addr uintptr
2050 len int
2051 cap int
2052 }
2053
2054
2055
2056
2057 type moddataType struct {
2058 pcHeader uintptr
2059 funcnametab moddataSlice
2060 cutab moddataSlice
2061 filetab moddataSlice
2062 pctab moddataSlice
2063 pclntable moddataSlice
2064 ftab moddataSlice
2065 findfunctab uintptr
2066 minpc, maxpc uintptr
2067
2068 text, etext uintptr
2069 noptrdata, enoptrdata uintptr
2070 data, edata uintptr
2071 bss, ebss uintptr
2072 noptrbss, enoptrbss uintptr
2073 covctrs, ecovctrs uintptr
2074 end, gcdata, gcbss uintptr
2075 types, etypes uintptr
2076 rodata uintptr
2077 gofunc uintptr
2078 }
2079
2080
2081
2082
2083
2084 var moddata moddataType
2085 setValueFromBytes(&moddata, moddataBytes)
2086
2087 ftabAddr := uint64(moddata.ftab.addr) - imageBase
2088 if ftabAddr < pclntabAddr || ftabAddr >= pclntabEnd {
2089 t.Fatalf("ftab address %#x not between %#x and %#x", ftabAddr, pclntabAddr, pclntabEnd)
2090 }
2091
2092
2093 type functab struct {
2094 entryoff uint32
2095 funcoff uint32
2096 }
2097
2098
2099 ftabLen := moddata.ftab.len - 1
2100 ftab := make([]functab, ftabLen)
2101 copy(ftab, unsafe.Slice((*functab)(unsafe.Pointer(&pclntab[ftabAddr-pclntabAddr])), ftabLen))
2102
2103 ftabBase := uint64(moddata.pclntable.addr) - imageBase
2104
2105
2106 type funcEntry struct {
2107 entryOff uint32
2108 nameOff int32
2109
2110 args int32
2111 deferreturn uint32
2112
2113 pcsp uint32
2114 pcfile uint32
2115 pcln uint32
2116 npcdata uint32
2117 cuOffset uint32
2118 startLine int32
2119 funcID abi.FuncID
2120 flag abi.FuncFlag
2121 _ [1]byte
2122 nfuncdata uint8
2123 }
2124
2125 for i, ftabEntry := range ftab {
2126 funcAddr := ftabBase + uint64(ftabEntry.funcoff)
2127 if funcAddr < pclntabAddr || funcAddr >= pclntabEnd {
2128 t.Errorf("ftab entry %d address %#x not between %#x and %#x", i, funcAddr, pclntabAddr, pclntabEnd)
2129 continue
2130 }
2131
2132 var fe funcEntry
2133 setValueFromBytes(&fe, pclntab[funcAddr-pclntabAddr:])
2134
2135 funcdataVals := funcAddr + uint64(unsafe.Sizeof(fe)) + uint64(fe.npcdata*4)
2136 for j := range fe.nfuncdata {
2137 var funcdataVal uint32
2138 setValueFromBytes(&funcdataVal, pclntab[funcdataVals+uint64(j)*4-pclntabAddr:])
2139 if funcdataVal == ^uint32(0) {
2140 continue
2141 }
2142 funcdataAddr := gofuncAddr + uint64(funcdataVal)
2143 if funcdataAddr < pclntabAddr || funcdataAddr >= pclntabEnd {
2144 t.Errorf("ftab entry %d funcdata %d address %#x not between %#x and %#x", i, j, funcdataAddr, pclntabAddr, pclntabEnd)
2145 }
2146 }
2147 }
2148
2149 if uint64(moddata.findfunctab)-imageBase < pclntabAddr || uint64(moddata.findfunctab)-imageBase >= pclntabEnd {
2150 t.Errorf("findfunctab address %#x not between %#x and %#x", moddata.findfunctab, pclntabAddr, pclntabEnd)
2151 }
2152 }
2153
2154
2155 func TestModuledataPlacement(t *testing.T) {
2156 testenv.MustHaveGoBuild(t)
2157 t.Parallel()
2158
2159 tmpdir := t.TempDir()
2160 src := filepath.Join(tmpdir, "x.go")
2161 if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
2162 t.Fatal(err)
2163 }
2164
2165 exe := filepath.Join(tmpdir, "x.exe")
2166 cmd := goCmd(t, "build", "-o", exe, src)
2167 if out, err := cmd.CombinedOutput(); err != nil {
2168 t.Fatalf("build failed; %v, output:\n%s", err, out)
2169 }
2170
2171 ef, _ := elf.Open(exe)
2172 mf, _ := macho.Open(exe)
2173 pf, _ := pe.Open(exe)
2174 xf, _ := xcoff.Open(exe)
2175
2176 if ef == nil && mf == nil && pf == nil && xf == nil {
2177 t.Skip("unrecognized executable file format")
2178 }
2179
2180 const moddataSymName = "runtime.firstmoduledata"
2181 switch {
2182 case ef != nil:
2183 defer ef.Close()
2184
2185 syms, err := ef.Symbols()
2186 if err != nil {
2187 t.Fatal(err)
2188 }
2189 for _, sym := range syms {
2190 if sym.Name == moddataSymName {
2191 sec := ef.Sections[sym.Section]
2192 if sec.Name != ".go.module" {
2193 t.Errorf("moduledata in section %s, not .go.module", sec.Name)
2194 }
2195 if sym.Value != sec.Addr {
2196 t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.Addr)
2197 }
2198 break
2199 }
2200 }
2201
2202 case mf != nil:
2203 defer mf.Close()
2204
2205 for _, sym := range mf.Symtab.Syms {
2206 if sym.Name == moddataSymName {
2207 if sym.Sect == 0 {
2208 t.Error("moduledata not in a section")
2209 } else {
2210 sec := mf.Sections[sym.Sect-1]
2211 if sec.Name != "__go_module" {
2212 t.Errorf("moduledata in section %s, not __go.module", sec.Name)
2213 }
2214 if sym.Value != sec.Addr {
2215 t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.Addr)
2216 }
2217 }
2218 break
2219 }
2220 }
2221
2222 case pf != nil, xf != nil:
2223 if pf != nil {
2224 defer pf.Close()
2225 }
2226 if xf != nil {
2227 defer xf.Close()
2228 }
2229
2230
2231
2232
2233 }
2234 }
2235
2236 const typeSrc = `
2237 package main
2238
2239 import (
2240 "fmt"
2241 "unsafe"
2242 )
2243
2244 type MyInt int
2245
2246 var vals = []any{
2247 0,
2248 0.1,
2249 "",
2250 MyInt(0),
2251 struct{ f int }{0},
2252 func() {},
2253 }
2254
2255 var global int
2256
2257 func main() {
2258 fmt.Printf("global %#x\n", &global)
2259 for _, v := range vals {
2260 // Unsafe assumption: the first word of a value
2261 // of type any is the type descriptor.
2262 td := *(*uintptr)(unsafe.Pointer(&v))
2263 fmt.Printf("%#x\n", td)
2264 }
2265 }
2266 `
2267
2268
2269 func TestTypePlacement(t *testing.T) {
2270 testenv.MustHaveGoRun(t)
2271 t.Parallel()
2272
2273 tmpdir := t.TempDir()
2274 src := filepath.Join(tmpdir, "x.go")
2275 if err := os.WriteFile(src, []byte(typeSrc), 0o444); err != nil {
2276 t.Fatal(err)
2277 }
2278
2279 exe := filepath.Join(tmpdir, "x.exe")
2280 cmd := goCmd(t, "build", "-o", exe, src)
2281 if out, err := cmd.CombinedOutput(); err != nil {
2282 t.Fatalf("build failed; %v, output:\n%s", err, out)
2283 }
2284
2285 cmd = testenv.Command(t, exe)
2286 var stdout, stderr strings.Builder
2287 cmd.Stdout = &stdout
2288 cmd.Stderr = &stderr
2289 if err := cmd.Run(); err != nil {
2290 t.Fatalf("running test program failed: %v, stdout:\n%s\nstderr:\n%s", err, &stdout, &stderr)
2291 }
2292 stderrString := stderr.String()
2293 if stderrString != "" {
2294 t.Fatalf("running test program printed to stderr:\n%s", stderrString)
2295 }
2296
2297 t.Logf("\n%s", &stdout)
2298
2299 var globalExeAddr uint64
2300 var addrs []uint64
2301 globalNext := false
2302 for s := range strings.FieldsSeq(stdout.String()) {
2303 if globalNext {
2304 v, err := strconv.ParseUint(s, 0, 64)
2305 if err != nil {
2306 t.Errorf("failed to parse test program output %s: %v", s, err)
2307 }
2308 globalExeAddr = v
2309 globalNext = false
2310 } else if s == "global" {
2311 globalNext = true
2312 } else {
2313 addr, err := strconv.ParseUint(s, 0, 64)
2314 if err != nil {
2315 t.Errorf("failed to parse test program output %q: %v", s, err)
2316 }
2317 addrs = append(addrs, addr)
2318 }
2319 }
2320
2321 ef, _ := elf.Open(exe)
2322 mf, _ := macho.Open(exe)
2323 pf, _ := pe.Open(exe)
2324 xf, _ := xcoff.Open(exe)
2325
2326 if ef == nil && mf == nil && pf == nil && xf == nil {
2327 t.Skip("unrecognized executable file format")
2328 }
2329
2330 const globalName = "main.global"
2331 var typeStart, typeEnd uint64
2332 var globalObjAddr uint64
2333 switch {
2334 case ef != nil:
2335 defer ef.Close()
2336
2337 for _, sec := range ef.Sections {
2338 if sec.Name == ".go.type" {
2339 typeStart = sec.Addr
2340 typeEnd = sec.Addr + sec.Size
2341 break
2342 }
2343 }
2344
2345 syms, err := ef.Symbols()
2346 if err != nil {
2347 t.Fatal(err)
2348 }
2349
2350 if typeStart == 0 && typeEnd == 0 {
2351
2352
2353 for _, sym := range syms {
2354 switch sym.Name {
2355 case "runtime.types":
2356 typeStart = sym.Value
2357 case "runtime.etypes":
2358 typeEnd = sym.Value
2359 }
2360 }
2361 }
2362
2363 for _, sym := range syms {
2364 if sym.Name == globalName {
2365 globalObjAddr = sym.Value
2366 break
2367 }
2368 }
2369
2370 case mf != nil:
2371 defer mf.Close()
2372
2373 for _, sec := range mf.Sections {
2374 if sec.Name == "__go_type" {
2375 typeStart = sec.Addr
2376 typeEnd = sec.Addr + sec.Size
2377 break
2378 }
2379 }
2380
2381 for _, sym := range mf.Symtab.Syms {
2382 if sym.Name == globalName {
2383 globalObjAddr = sym.Value
2384 break
2385 }
2386 }
2387
2388 case pf != nil:
2389 defer pf.Close()
2390
2391 var imageBase uint64
2392 switch ohdr := pf.OptionalHeader.(type) {
2393 case *pe.OptionalHeader32:
2394 imageBase = uint64(ohdr.ImageBase)
2395 case *pe.OptionalHeader64:
2396 imageBase = ohdr.ImageBase
2397 }
2398
2399 var typeSym, eTypeSym *pe.Symbol
2400 for _, sym := range pf.Symbols {
2401 switch sym.Name {
2402 case "runtime.types":
2403 typeSym = sym
2404 case "runtime.etypes":
2405 eTypeSym = sym
2406 case globalName:
2407 globalSec := pf.Sections[sym.SectionNumber-1]
2408 globalObjAddr = imageBase + uint64(globalSec.VirtualAddress+sym.Value)
2409 }
2410 }
2411
2412 if typeSym == nil {
2413 t.Fatal("could not find symbol runtime.types")
2414 }
2415 if eTypeSym == nil {
2416 t.Fatal("could not find symbol runtime.etypes")
2417 }
2418 if typeSym.SectionNumber != eTypeSym.SectionNumber {
2419 t.Fatalf("runtime.types section %d != runtime.etypes section %d", typeSym.SectionNumber, eTypeSym.SectionNumber)
2420 }
2421
2422 sec := pf.Sections[typeSym.SectionNumber-1]
2423
2424 typeStart = imageBase + uint64(sec.VirtualAddress+typeSym.Value)
2425 typeEnd = imageBase + uint64(sec.VirtualAddress+eTypeSym.Value)
2426
2427 case xf != nil:
2428 defer xf.Close()
2429
2430
2431
2432
2433 var typeSym, eTypeSym *xcoff.Symbol
2434 for _, sym := range xf.Symbols {
2435 switch sym.Name {
2436 case "runtime.types":
2437 typeSym = sym
2438 case "runtime.etypes":
2439 eTypeSym = sym
2440 case globalName:
2441 globalSec := xf.Sections[sym.SectionNumber-1]
2442 globalObjAddr = uint64(globalSec.VirtualAddress + sym.Value)
2443 }
2444 }
2445
2446 if typeSym == nil {
2447 t.Fatal("could not find symbol runtime.types")
2448 }
2449 if eTypeSym == nil {
2450 t.Fatal("could not find symbol runtime.etypes")
2451 }
2452 if typeSym.SectionNumber != eTypeSym.SectionNumber {
2453 t.Fatalf("runtime.types section %d != runtime.etypes section %d", typeSym.SectionNumber, eTypeSym.SectionNumber)
2454 }
2455
2456 sec := xf.Sections[typeSym.SectionNumber-1]
2457
2458 typeStart = uint64(sec.VirtualAddress + typeSym.Value)
2459
2460 typeEnd = uint64(sec.VirtualAddress + eTypeSym.Value)
2461 }
2462
2463 if typeStart == 0 || typeEnd == 0 {
2464 t.Fatalf("failed to find type descriptor addresses; found %#x to %#x", typeStart, typeEnd)
2465 }
2466 t.Logf("type start: %#x type end: %#x", typeStart, typeEnd)
2467
2468 offset := globalExeAddr - globalObjAddr
2469 t.Logf("execution offset: %#x", offset)
2470
2471
2472
2473
2474
2475 if runtime.GOOS == "aix" {
2476 offset = 0
2477 }
2478
2479 for _, addr := range addrs {
2480 addr -= offset
2481 if addr < typeStart || addr >= typeEnd {
2482 t.Errorf("type descriptor address %#x out of range: not between %#x and %#x", addr, typeStart, typeEnd)
2483 }
2484 }
2485 }
2486
View as plain text