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