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
455 t.Parallel()
456
457 tmpdir := t.TempDir()
458
459 src := filepath.Join(tmpdir, "main.go")
460 err := os.WriteFile(src, []byte(trivialSrc), 0666)
461 if err != nil {
462 t.Fatal(err)
463 }
464
465 exe := filepath.Join(tmpdir, "main")
466 cmd := goCmd(t, "build", "-ldflags=-linkmode=internal", "-o", exe, src)
467 cmd.Env = append(cmd.Env,
468 "CGO_ENABLED=0",
469 "GOOS=darwin",
470 "GOARCH=amd64",
471 )
472 if out, err := cmd.CombinedOutput(); err != nil {
473 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
474 }
475 exef, err := os.Open(exe)
476 if err != nil {
477 t.Fatal(err)
478 }
479 defer exef.Close()
480 exem, err := macho.NewFile(exef)
481 if err != nil {
482 t.Fatal(err)
483 }
484 found := false
485 checkMin := func(ver uint32) {
486 major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
487 if major < 12 {
488 t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 12.0.0", major, minor, patch)
489 }
490 }
491 for _, cmd := range exem.Loads {
492 raw := cmd.Raw()
493 type_ := exem.ByteOrder.Uint32(raw)
494 if type_ != imacho.LC_BUILD_VERSION {
495 continue
496 }
497 osVer := exem.ByteOrder.Uint32(raw[12:])
498 checkMin(osVer)
499 sdkVer := exem.ByteOrder.Uint32(raw[16:])
500 checkMin(sdkVer)
501 found = true
502 break
503 }
504 if !found {
505 t.Errorf("no LC_BUILD_VERSION load command found")
506 }
507 }
508
509 func TestMachOUUID(t *testing.T) {
510 testenv.MustHaveGoBuild(t)
511 if runtime.GOOS != "darwin" {
512 t.Skip("this is only for darwin")
513 }
514
515 t.Parallel()
516
517 tmpdir := t.TempDir()
518
519 src := filepath.Join(tmpdir, "main.go")
520 err := os.WriteFile(src, []byte(trivialSrc), 0666)
521 if err != nil {
522 t.Fatal(err)
523 }
524
525 extractUUID := func(exe string) string {
526 exem, err := macho.Open(exe)
527 if err != nil {
528 t.Fatal(err)
529 }
530 defer exem.Close()
531 for _, cmd := range exem.Loads {
532 raw := cmd.Raw()
533 type_ := exem.ByteOrder.Uint32(raw)
534 if type_ != imacho.LC_UUID {
535 continue
536 }
537 return string(raw[8:24])
538 }
539 return ""
540 }
541
542 tests := []struct{ name, ldflags, expect string }{
543 {"default", "", "gobuildid"},
544 {"gobuildid", "-B=gobuildid", "gobuildid"},
545 {"specific", "-B=0x0123456789ABCDEF0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
546 {"none", "-B=none", ""},
547 }
548 if testenv.HasCGO() {
549 for _, test := range tests {
550 t1 := test
551 t1.name += "_external"
552 t1.ldflags += " -linkmode=external"
553 tests = append(tests, t1)
554 }
555 }
556 for _, test := range tests {
557 t.Run(test.name, func(t *testing.T) {
558 exe := filepath.Join(tmpdir, test.name)
559 cmd := goCmd(t, "build", "-ldflags="+test.ldflags, "-o", exe, src)
560 if out, err := cmd.CombinedOutput(); err != nil {
561 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
562 }
563 uuid := extractUUID(exe)
564 if test.expect == "gobuildid" {
565
566
567 if uuid == "" {
568 t.Fatal("expect nonempty UUID, got empty")
569 }
570
571 if uuid[6]>>4 != 3 {
572 t.Errorf("expect v3 UUID, got %X (version %d)", uuid, uuid[6]>>4)
573 }
574 } else if uuid != test.expect {
575 t.Errorf("UUID mismatch: got %X, want %X", uuid, test.expect)
576 }
577 })
578 }
579 }
580
581 const Issue34788src = `
582
583 package blah
584
585 func Blah(i int) int {
586 a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
587 return a[i&7]
588 }
589 `
590
591 func TestIssue34788Android386TLSSequence(t *testing.T) {
592 testenv.MustHaveGoBuild(t)
593
594
595
596
597 if runtime.GOARCH != "amd64" ||
598 (runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
599 t.Skip("skipping on non-{linux,darwin}/amd64 platform")
600 }
601
602 t.Parallel()
603
604 tmpdir := t.TempDir()
605
606 src := filepath.Join(tmpdir, "blah.go")
607 err := os.WriteFile(src, []byte(Issue34788src), 0666)
608 if err != nil {
609 t.Fatal(err)
610 }
611
612 obj := filepath.Join(tmpdir, "blah.o")
613 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src)
614 cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
615 if out, err := cmd.CombinedOutput(); err != nil {
616 t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
617 }
618
619
620 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "objdump", obj)
621 out, oerr := cmd.CombinedOutput()
622 if oerr != nil {
623 t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
624 }
625
626
627 scanner := bufio.NewScanner(bytes.NewReader(out))
628 for scanner.Scan() {
629 line := scanner.Text()
630 if strings.Contains(line, "R_TLS_LE") {
631 t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
632 }
633 }
634 }
635
636 const testStrictDupGoSrc = `
637 package main
638 func f()
639 func main() { f() }
640 `
641
642 const testStrictDupAsmSrc1 = `
643 #include "textflag.h"
644 TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
645 RET
646 `
647
648 const testStrictDupAsmSrc2 = `
649 #include "textflag.h"
650 TEXT ·f(SB), NOSPLIT|DUPOK, $0-0
651 JMP 0(PC)
652 `
653
654 const testStrictDupAsmSrc3 = `
655 #include "textflag.h"
656 GLOBL ·rcon(SB), RODATA|DUPOK, $64
657 `
658
659 const testStrictDupAsmSrc4 = `
660 #include "textflag.h"
661 GLOBL ·rcon(SB), RODATA|DUPOK, $32
662 `
663
664 func TestStrictDup(t *testing.T) {
665
666 testenv.MustHaveGoBuild(t)
667
668 asmfiles := []struct {
669 fname string
670 payload string
671 }{
672 {"a", testStrictDupAsmSrc1},
673 {"b", testStrictDupAsmSrc2},
674 {"c", testStrictDupAsmSrc3},
675 {"d", testStrictDupAsmSrc4},
676 }
677
678 t.Parallel()
679
680 tmpdir := t.TempDir()
681
682 src := filepath.Join(tmpdir, "x.go")
683 err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
684 if err != nil {
685 t.Fatal(err)
686 }
687 for _, af := range asmfiles {
688 src = filepath.Join(tmpdir, af.fname+".s")
689 err = os.WriteFile(src, []byte(af.payload), 0666)
690 if err != nil {
691 t.Fatal(err)
692 }
693 }
694 src = filepath.Join(tmpdir, "go.mod")
695 err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666)
696 if err != nil {
697 t.Fatal(err)
698 }
699
700 cmd := goCmd(t, "build", "-ldflags=-strictdups=1")
701 cmd.Dir = tmpdir
702 out, err := cmd.CombinedOutput()
703 if err != nil {
704 t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
705 }
706 if !bytes.Contains(out, []byte("mismatched payload")) {
707 t.Errorf("unexpected output:\n%s", out)
708 }
709
710 cmd = goCmd(t, "build", "-ldflags=-strictdups=2")
711 cmd.Dir = tmpdir
712 out, err = cmd.CombinedOutput()
713 if err == nil {
714 t.Errorf("linking with -strictdups=2 did not fail")
715 }
716
717
718 if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
719 bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
720 !bytes.Contains(out, []byte("mismatched payload: different sizes")) {
721 t.Errorf("unexpected output:\n%s", out)
722 }
723 }
724
725 const testFuncAlignSrc = `
726 package main
727 import (
728 "fmt"
729 )
730 func alignPc()
731 var alignPcFnAddr uintptr
732
733 func main() {
734 if alignPcFnAddr % 512 != 0 {
735 fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)
736 } else {
737 fmt.Printf("PASS")
738 }
739 }
740 `
741
742 var testFuncAlignAsmSources = map[string]string{
743 "arm64": `
744 #include "textflag.h"
745
746 TEXT ·alignPc(SB),NOSPLIT, $0-0
747 MOVD $2, R0
748 PCALIGN $512
749 MOVD $3, R1
750 RET
751
752 GLOBL ·alignPcFnAddr(SB),RODATA,$8
753 DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)
754 `,
755 "loong64": `
756 #include "textflag.h"
757
758 TEXT ·alignPc(SB),NOSPLIT, $0-0
759 MOVV $2, R4
760 PCALIGN $512
761 MOVV $3, R5
762 RET
763
764 GLOBL ·alignPcFnAddr(SB),RODATA,$8
765 DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)
766 `,
767 }
768
769
770
771 func TestFuncAlign(t *testing.T) {
772 testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH]
773 if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" {
774 t.Skip("skipping on non-linux/{arm64,loong64} platform")
775 }
776 testenv.MustHaveGoBuild(t)
777
778 t.Parallel()
779
780 tmpdir := t.TempDir()
781
782 src := filepath.Join(tmpdir, "go.mod")
783 err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
784 if err != nil {
785 t.Fatal(err)
786 }
787 src = filepath.Join(tmpdir, "falign.go")
788 err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666)
789 if err != nil {
790 t.Fatal(err)
791 }
792 src = filepath.Join(tmpdir, "falign.s")
793 err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
794 if err != nil {
795 t.Fatal(err)
796 }
797
798 cmd := goCmd(t, "build", "-o", "falign")
799 cmd.Dir = tmpdir
800 out, err := cmd.CombinedOutput()
801 if err != nil {
802 t.Errorf("build failed: %v", err)
803 }
804 cmd = testenv.Command(t, tmpdir+"/falign")
805 out, err = cmd.CombinedOutput()
806 if err != nil {
807 t.Errorf("failed to run with err %v, output: %s", err, out)
808 }
809 if string(out) != "PASS" {
810 t.Errorf("unexpected output: %s\n", out)
811 }
812 }
813
814 const testFuncAlignOptionSrc = `
815 package main
816 //go:noinline
817 func foo() {
818 }
819 //go:noinline
820 func bar() {
821 }
822 //go:noinline
823 func baz() {
824 }
825 func main() {
826 foo()
827 bar()
828 baz()
829 }
830 `
831
832
833 func TestFuncAlignOption(t *testing.T) {
834 testenv.MustHaveGoBuild(t)
835
836 t.Parallel()
837
838 tmpdir := t.TempDir()
839
840 src := filepath.Join(tmpdir, "falign.go")
841 err := os.WriteFile(src, []byte(testFuncAlignOptionSrc), 0666)
842 if err != nil {
843 t.Fatal(err)
844 }
845
846 alignTest := func(align uint64) {
847 exeName := "falign.exe"
848 cmd := goCmd(t, "build", "-ldflags=-funcalign="+strconv.FormatUint(align, 10), "-o", exeName, "falign.go")
849 cmd.Dir = tmpdir
850 out, err := cmd.CombinedOutput()
851 if err != nil {
852 t.Errorf("build failed: %v \n%s", err, out)
853 }
854 exe := filepath.Join(tmpdir, exeName)
855 cmd = testenv.Command(t, exe)
856 out, err = cmd.CombinedOutput()
857 if err != nil {
858 t.Errorf("failed to run with err %v, output: %s", err, out)
859 }
860
861
862 f, err := objfile.Open(exe)
863 if err != nil {
864 t.Fatalf("failed to open file:%v\n", err)
865 }
866 defer f.Close()
867
868 fname := map[string]bool{"_main.foo": false,
869 "_main.bar": false,
870 "_main.baz": false}
871 syms, err := f.Symbols()
872 for _, s := range syms {
873 fn := s.Name
874 if _, ok := fname[fn]; !ok {
875 fn = "_" + s.Name
876 if _, ok := fname[fn]; !ok {
877 continue
878 }
879 }
880 if s.Addr%align != 0 {
881 t.Fatalf("unaligned function: %s %x. Expected alignment: %d\n", fn, s.Addr, align)
882 }
883 fname[fn] = true
884 }
885 for k, v := range fname {
886 if !v {
887 t.Fatalf("function %s not found\n", k)
888 }
889 }
890 }
891 alignTest(16)
892 alignTest(32)
893 }
894
895 const testTrampSrc = `
896 package main
897 import "fmt"
898 func main() {
899 fmt.Println("hello")
900
901 defer func(){
902 if e := recover(); e == nil {
903 panic("did not panic")
904 }
905 }()
906 f1()
907 }
908
909 // Test deferreturn trampolines. See issue #39049.
910 func f1() { defer f2() }
911 func f2() { panic("XXX") }
912 `
913
914 func TestTrampoline(t *testing.T) {
915
916
917
918
919 buildmodes := []string{"default"}
920 switch runtime.GOARCH {
921 case "arm", "arm64", "ppc64", "loong64":
922 case "ppc64le":
923
924 buildmodes = append(buildmodes, "pie")
925 default:
926 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
927 }
928
929 testenv.MustHaveGoBuild(t)
930
931 t.Parallel()
932
933 tmpdir := t.TempDir()
934
935 src := filepath.Join(tmpdir, "hello.go")
936 err := os.WriteFile(src, []byte(testTrampSrc), 0666)
937 if err != nil {
938 t.Fatal(err)
939 }
940 exe := filepath.Join(tmpdir, "hello.exe")
941
942 for _, mode := range buildmodes {
943 cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
944 out, err := cmd.CombinedOutput()
945 if err != nil {
946 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
947 }
948 cmd = testenv.Command(t, exe)
949 out, err = cmd.CombinedOutput()
950 if err != nil {
951 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
952 }
953 if string(out) != "hello\n" {
954 t.Errorf("unexpected output (%s):\n%s", mode, out)
955 }
956
957 out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
958 if err != nil {
959 t.Errorf("nm failure: %s\n%s\n", err, string(out))
960 }
961 if ok, _ := regexp.Match("T runtime.deferreturn(\\+0)?-tramp0", out); !ok {
962 t.Errorf("Trampoline T runtime.deferreturn(+0)?-tramp0 is missing")
963 }
964 }
965 }
966
967 const testTrampCgoSrc = `
968 package main
969
970 // #include <stdio.h>
971 // void CHello() { printf("hello\n"); fflush(stdout); }
972 import "C"
973
974 func main() {
975 C.CHello()
976 }
977 `
978
979 func TestTrampolineCgo(t *testing.T) {
980
981
982
983
984 buildmodes := []string{"default"}
985 switch runtime.GOARCH {
986 case "arm", "arm64", "ppc64", "loong64":
987 case "ppc64le":
988
989 buildmodes = append(buildmodes, "pie")
990 default:
991 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
992 }
993
994 testenv.MustHaveGoBuild(t)
995 testenv.MustHaveCGO(t)
996
997 t.Parallel()
998
999 tmpdir := t.TempDir()
1000
1001 src := filepath.Join(tmpdir, "hello.go")
1002 err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
1003 if err != nil {
1004 t.Fatal(err)
1005 }
1006 exe := filepath.Join(tmpdir, "hello.exe")
1007
1008 for _, mode := range buildmodes {
1009 cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
1010 out, err := cmd.CombinedOutput()
1011 if err != nil {
1012 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
1013 }
1014 cmd = testenv.Command(t, exe)
1015 out, err = cmd.CombinedOutput()
1016 if err != nil {
1017 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1018 }
1019 if string(out) != "hello\n" && string(out) != "hello\r\n" {
1020 t.Errorf("unexpected output (%s):\n%s", mode, out)
1021 }
1022
1023
1024
1025 if !testenv.CanInternalLink(true) {
1026 continue
1027 }
1028 cmd = goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
1029 out, err = cmd.CombinedOutput()
1030 if err != nil {
1031 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
1032 }
1033 cmd = testenv.Command(t, exe)
1034 out, err = cmd.CombinedOutput()
1035 if err != nil {
1036 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1037 }
1038 if string(out) != "hello\n" && string(out) != "hello\r\n" {
1039 t.Errorf("unexpected output (%s):\n%s", mode, out)
1040 }
1041 }
1042 }
1043
1044 func TestIndexMismatch(t *testing.T) {
1045
1046
1047
1048 testenv.MustHaveGoBuild(t)
1049
1050
1051 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
1052
1053 t.Parallel()
1054
1055 tmpdir := t.TempDir()
1056
1057 aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
1058 bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
1059 mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
1060 aObj := filepath.Join(tmpdir, "a.o")
1061 mObj := filepath.Join(tmpdir, "main.o")
1062 exe := filepath.Join(tmpdir, "main.exe")
1063
1064 importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")
1065 testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")
1066 importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
1067 testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")
1068
1069
1070 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
1071 t.Log(cmd)
1072 out, err := cmd.CombinedOutput()
1073 if err != nil {
1074 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
1075 }
1076 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
1077 t.Log(cmd)
1078 out, err = cmd.CombinedOutput()
1079 if err != nil {
1080 t.Fatalf("compiling main.go failed: %v\n%s", err, out)
1081 }
1082 cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
1083 t.Log(cmd)
1084 out, err = cmd.CombinedOutput()
1085 if err != nil {
1086 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
1087 testenv.SkipFlaky(t, 58806)
1088 }
1089 t.Errorf("linking failed: %v\n%s", err, out)
1090 }
1091
1092
1093
1094 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
1095 t.Log(cmd)
1096 out, err = cmd.CombinedOutput()
1097 if err != nil {
1098 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
1099 }
1100 cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
1101 t.Log(cmd)
1102 out, err = cmd.CombinedOutput()
1103 if err == nil {
1104 t.Fatalf("linking didn't fail")
1105 }
1106 if !bytes.Contains(out, []byte("fingerprint mismatch")) {
1107 t.Errorf("did not see expected error message. out:\n%s", out)
1108 }
1109 }
1110
1111 func TestPErsrcBinutils(t *testing.T) {
1112
1113 testenv.MustHaveGoBuild(t)
1114
1115 if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
1116
1117 t.Skipf("this is only for windows/amd64 and windows/386")
1118 }
1119
1120 t.Parallel()
1121
1122 tmpdir := t.TempDir()
1123
1124 pkgdir := filepath.Join("testdata", "pe-binutils")
1125 exe := filepath.Join(tmpdir, "a.exe")
1126 cmd := goCmd(t, "build", "-o", exe)
1127 cmd.Dir = pkgdir
1128
1129 out, err := cmd.CombinedOutput()
1130 if err != nil {
1131 t.Fatalf("building failed: %v, output:\n%s", err, out)
1132 }
1133
1134
1135 b, err := os.ReadFile(exe)
1136 if err != nil {
1137 t.Fatalf("reading output failed: %v", err)
1138 }
1139 if !bytes.Contains(b, []byte("Hello Gophers!")) {
1140 t.Fatalf("binary does not contain expected content")
1141 }
1142 }
1143
1144 func TestPErsrcLLVM(t *testing.T) {
1145
1146 testenv.MustHaveGoBuild(t)
1147
1148 if runtime.GOOS != "windows" {
1149 t.Skipf("this is a windows-only test")
1150 }
1151
1152 t.Parallel()
1153
1154 tmpdir := t.TempDir()
1155
1156 pkgdir := filepath.Join("testdata", "pe-llvm")
1157 exe := filepath.Join(tmpdir, "a.exe")
1158 cmd := goCmd(t, "build", "-o", exe)
1159 cmd.Dir = pkgdir
1160
1161 out, err := cmd.CombinedOutput()
1162 if err != nil {
1163 t.Fatalf("building failed: %v, output:\n%s", err, out)
1164 }
1165
1166
1167 b, err := os.ReadFile(exe)
1168 if err != nil {
1169 t.Fatalf("reading output failed: %v", err)
1170 }
1171 if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
1172 t.Fatalf("binary does not contain expected content")
1173 }
1174 }
1175
1176 func TestContentAddressableSymbols(t *testing.T) {
1177
1178 testenv.MustHaveGoBuild(t)
1179
1180 t.Parallel()
1181
1182 src := filepath.Join("testdata", "testHashedSyms", "p.go")
1183 cmd := goCmd(t, "run", src)
1184 out, err := cmd.CombinedOutput()
1185 if err != nil {
1186 t.Errorf("command %s failed: %v\n%s", cmd, err, out)
1187 }
1188 }
1189
1190 func TestReadOnly(t *testing.T) {
1191
1192 testenv.MustHaveGoBuild(t)
1193
1194 t.Parallel()
1195
1196 src := filepath.Join("testdata", "testRO", "x.go")
1197 cmd := goCmd(t, "run", src)
1198 out, err := cmd.CombinedOutput()
1199 if err == nil {
1200 t.Errorf("running test program did not fail. output:\n%s", out)
1201 }
1202 }
1203
1204 const testIssue38554Src = `
1205 package main
1206
1207 type T [10<<20]byte
1208
1209 //go:noinline
1210 func f() T {
1211 return T{} // compiler will make a large stmp symbol, but not used.
1212 }
1213
1214 func main() {
1215 x := f()
1216 println(x[1])
1217 }
1218 `
1219
1220 func TestIssue38554(t *testing.T) {
1221 testenv.MustHaveGoBuild(t)
1222
1223 t.Parallel()
1224
1225 tmpdir := t.TempDir()
1226
1227 src := filepath.Join(tmpdir, "x.go")
1228 err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
1229 if err != nil {
1230 t.Fatalf("failed to write source file: %v", err)
1231 }
1232 exe := filepath.Join(tmpdir, "x.exe")
1233 cmd := goCmd(t, "build", "-o", exe, src)
1234 out, err := cmd.CombinedOutput()
1235 if err != nil {
1236 t.Fatalf("build failed: %v\n%s", err, out)
1237 }
1238
1239 fi, err := os.Stat(exe)
1240 if err != nil {
1241 t.Fatalf("failed to stat output file: %v", err)
1242 }
1243
1244
1245
1246
1247 const want = 5 << 20
1248 if got := fi.Size(); got > want {
1249 t.Errorf("binary too big: got %d, want < %d", got, want)
1250 }
1251 }
1252
1253 const testIssue42396src = `
1254 package main
1255
1256 //go:noinline
1257 //go:nosplit
1258 func callee(x int) {
1259 }
1260
1261 func main() {
1262 callee(9)
1263 }
1264 `
1265
1266 func TestIssue42396(t *testing.T) {
1267 testenv.MustHaveGoBuild(t)
1268
1269 if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
1270 t.Skip("no race detector support")
1271 }
1272
1273 t.Parallel()
1274
1275 tmpdir := t.TempDir()
1276
1277 src := filepath.Join(tmpdir, "main.go")
1278 err := os.WriteFile(src, []byte(testIssue42396src), 0666)
1279 if err != nil {
1280 t.Fatalf("failed to write source file: %v", err)
1281 }
1282 exe := filepath.Join(tmpdir, "main.exe")
1283 cmd := goCmd(t, "build", "-gcflags=-race", "-o", exe, src)
1284 out, err := cmd.CombinedOutput()
1285 if err == nil {
1286 t.Fatalf("build unexpectedly succeeded")
1287 }
1288
1289
1290
1291 if strings.Contains(string(out), "panic:") {
1292 t.Fatalf("build should not fail with panic:\n%s", out)
1293 }
1294 const want = "reference to undefined builtin"
1295 if !strings.Contains(string(out), want) {
1296 t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
1297 }
1298 }
1299
1300 const testLargeRelocSrc = `
1301 package main
1302
1303 var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
1304
1305 var addr = [...]*byte{
1306 &x[1<<23-1],
1307 &x[1<<23],
1308 &x[1<<23+1],
1309 &x[1<<24-1],
1310 &x[1<<24],
1311 &x[1<<24+1],
1312 }
1313
1314 func main() {
1315 // check relocations in instructions
1316 check(x[1<<23-1], 0)
1317 check(x[1<<23], 23)
1318 check(x[1<<23+1], 0)
1319 check(x[1<<24-1], 0)
1320 check(x[1<<24], 24)
1321 check(x[1<<24+1], 0)
1322
1323 // check absolute address relocations in data
1324 check(*addr[0], 0)
1325 check(*addr[1], 23)
1326 check(*addr[2], 0)
1327 check(*addr[3], 0)
1328 check(*addr[4], 24)
1329 check(*addr[5], 0)
1330 }
1331
1332 func check(x, y byte) {
1333 if x != y {
1334 panic("FAIL")
1335 }
1336 }
1337 `
1338
1339 func TestLargeReloc(t *testing.T) {
1340
1341
1342
1343 testenv.MustHaveGoBuild(t)
1344 t.Parallel()
1345
1346 tmpdir := t.TempDir()
1347
1348 src := filepath.Join(tmpdir, "x.go")
1349 err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
1350 if err != nil {
1351 t.Fatalf("failed to write source file: %v", err)
1352 }
1353 cmd := goCmd(t, "run", src)
1354 out, err := cmd.CombinedOutput()
1355 if err != nil {
1356 t.Errorf("build failed: %v. output:\n%s", err, out)
1357 }
1358
1359 if testenv.HasCGO() {
1360 cmd = goCmd(t, "run", "-ldflags=-linkmode=external", src)
1361 out, err = cmd.CombinedOutput()
1362 if err != nil {
1363 t.Fatalf("build failed: %v. output:\n%s", err, out)
1364 }
1365 }
1366 }
1367
1368 func TestUnlinkableObj(t *testing.T) {
1369
1370 testenv.MustHaveGoBuild(t)
1371 t.Parallel()
1372
1373 if true {
1374 t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
1375 }
1376
1377 tmpdir := t.TempDir()
1378
1379 xSrc := filepath.Join(tmpdir, "x.go")
1380 pSrc := filepath.Join(tmpdir, "p.go")
1381 xObj := filepath.Join(tmpdir, "x.o")
1382 pObj := filepath.Join(tmpdir, "p.o")
1383 exe := filepath.Join(tmpdir, "x.exe")
1384 importcfgfile := filepath.Join(tmpdir, "importcfg")
1385 testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
1386 err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
1387 if err != nil {
1388 t.Fatalf("failed to write source file: %v", err)
1389 }
1390 err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
1391 if err != nil {
1392 t.Fatalf("failed to write source file: %v", err)
1393 }
1394 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc)
1395 out, err := cmd.CombinedOutput()
1396 if err != nil {
1397 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1398 }
1399 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
1400 out, err = cmd.CombinedOutput()
1401 if err != nil {
1402 t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
1403 }
1404 cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
1405 out, err = cmd.CombinedOutput()
1406 if err == nil {
1407 t.Fatalf("link did not fail")
1408 }
1409 if !bytes.Contains(out, []byte("unlinkable object")) {
1410 t.Errorf("did not see expected error message. out:\n%s", out)
1411 }
1412
1413
1414 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
1415 out, err = cmd.CombinedOutput()
1416 if err != nil {
1417 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1418 }
1419 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc)
1420 out, err = cmd.CombinedOutput()
1421 if err != nil {
1422 t.Fatalf("compile failed: %v. output:\n%s", err, out)
1423 }
1424
1425 cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
1426 out, err = cmd.CombinedOutput()
1427 if err != nil {
1428 t.Errorf("link failed: %v. output:\n%s", err, out)
1429 }
1430 }
1431
1432 func TestExtLinkCmdlineDeterminism(t *testing.T) {
1433
1434 testenv.MustHaveGoBuild(t)
1435 testenv.MustHaveCGO(t)
1436 t.Parallel()
1437
1438
1439 testSrc := `
1440 package main
1441 import "C"
1442 //export F1
1443 func F1() {}
1444 //export F2
1445 func F2() {}
1446 //export F3
1447 func F3() {}
1448 func main() {}
1449 `
1450
1451 tmpdir := t.TempDir()
1452 src := filepath.Join(tmpdir, "x.go")
1453 if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {
1454 t.Fatal(err)
1455 }
1456 exe := filepath.Join(tmpdir, "x.exe")
1457
1458
1459
1460 linktmp := filepath.Join(tmpdir, "linktmp")
1461 if err := os.Mkdir(linktmp, 0777); err != nil {
1462 t.Fatal(err)
1463 }
1464
1465
1466
1467 ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp
1468 var out0 []byte
1469 for i := 0; i < 5; i++ {
1470 cmd := goCmd(t, "build", ldflags, "-o", exe, src)
1471 out, err := cmd.CombinedOutput()
1472 if err != nil {
1473 t.Fatalf("build failed: %v, output:\n%s", err, out)
1474 }
1475 if err := os.Remove(exe); err != nil {
1476 t.Fatal(err)
1477 }
1478
1479
1480 j := bytes.Index(out, []byte("\nhost link:"))
1481 if j == -1 {
1482 t.Fatalf("host link step not found, output:\n%s", out)
1483 }
1484 out = out[j+1:]
1485 k := bytes.Index(out, []byte("\n"))
1486 if k == -1 {
1487 t.Fatalf("no newline after host link, output:\n%s", out)
1488 }
1489 out = out[:k]
1490
1491
1492
1493 fs := bytes.Fields(out)
1494 for i, f := range fs {
1495 if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {
1496 fs[i+1] = []byte("a.out")
1497 break
1498 }
1499 }
1500 out = bytes.Join(fs, []byte{' '})
1501
1502 if i == 0 {
1503 out0 = out
1504 continue
1505 }
1506 if !bytes.Equal(out0, out) {
1507 t.Fatalf("output differ:\n%s\n==========\n%s", out0, out)
1508 }
1509 }
1510 }
1511
1512
1513
1514 func TestResponseFile(t *testing.T) {
1515 t.Parallel()
1516
1517 testenv.MustHaveGoBuild(t)
1518
1519
1520
1521 testenv.MustHaveCGO(t)
1522
1523 tmpdir := t.TempDir()
1524
1525 src := filepath.Join(tmpdir, "x.go")
1526 if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {
1527 t.Fatal(err)
1528 }
1529
1530
1531
1532
1533 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")
1534 cmd.Dir = tmpdir
1535
1536
1537 var sb strings.Builder
1538 sb.WriteString(`'-ldflags=all="-extldflags=`)
1539 for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {
1540 if i > 0 {
1541 sb.WriteString(" ")
1542 }
1543 sb.WriteString("-g")
1544 }
1545 sb.WriteString(`"'`)
1546 cmd = testenv.CleanCmdEnv(cmd)
1547 cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())
1548
1549 out, err := cmd.CombinedOutput()
1550 if len(out) > 0 {
1551 t.Logf("%s", out)
1552 }
1553 if err != nil {
1554 t.Error(err)
1555 }
1556 }
1557
1558 func TestDynimportVar(t *testing.T) {
1559
1560
1561 if runtime.GOOS != "darwin" {
1562 t.Skip("skip on non-darwin platform")
1563 }
1564
1565 testenv.MustHaveGoBuild(t)
1566 testenv.MustHaveCGO(t)
1567
1568 t.Parallel()
1569
1570 tmpdir := t.TempDir()
1571 exe := filepath.Join(tmpdir, "a.exe")
1572 src := filepath.Join("testdata", "dynimportvar", "main.go")
1573
1574 for _, mode := range []string{"internal", "external"} {
1575 cmd := goCmd(t, "build", "-ldflags=-linkmode="+mode, "-o", exe, src)
1576 out, err := cmd.CombinedOutput()
1577 if err != nil {
1578 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1579 }
1580 cmd = testenv.Command(t, exe)
1581 out, err = cmd.CombinedOutput()
1582 if err != nil {
1583 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1584 }
1585 }
1586 }
1587
1588 const helloSrc = `
1589 package main
1590 var X = 42
1591 var Y int
1592 func main() { println("hello", X, Y) }
1593 `
1594
1595 func TestFlagS(t *testing.T) {
1596
1597 testenv.MustHaveGoBuild(t)
1598
1599 t.Parallel()
1600
1601 tmpdir := t.TempDir()
1602 exe := filepath.Join(tmpdir, "a.exe")
1603 src := filepath.Join(tmpdir, "a.go")
1604 err := os.WriteFile(src, []byte(helloSrc), 0666)
1605 if err != nil {
1606 t.Fatal(err)
1607 }
1608
1609 modes := []string{"auto"}
1610 if testenv.HasCGO() {
1611 modes = append(modes, "external")
1612 }
1613
1614
1615 syms := []string{"main.main", "main.X", "main.Y"}
1616
1617 for _, mode := range modes {
1618 cmd := goCmd(t, "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)
1619 out, err := cmd.CombinedOutput()
1620 if err != nil {
1621 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1622 }
1623 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1624 out, err = cmd.CombinedOutput()
1625 if err != nil {
1626 if _, ok := errors.AsType[*exec.ExitError](err); !ok {
1627
1628
1629
1630 t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)
1631 }
1632 }
1633 for _, s := range syms {
1634 if bytes.Contains(out, []byte(s)) {
1635 t.Errorf("(mode=%s): unexpected symbol %s", mode, s)
1636 }
1637 }
1638 }
1639 }
1640
1641 func TestRandLayout(t *testing.T) {
1642
1643
1644 testenv.MustHaveGoBuild(t)
1645
1646 t.Parallel()
1647
1648 tmpdir := t.TempDir()
1649
1650 src := filepath.Join(tmpdir, "hello.go")
1651 err := os.WriteFile(src, []byte(trivialSrc), 0666)
1652 if err != nil {
1653 t.Fatal(err)
1654 }
1655
1656 var syms [2]string
1657 for i, seed := range []string{"123", "456"} {
1658 exe := filepath.Join(tmpdir, "hello"+seed+".exe")
1659 cmd := goCmd(t, "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
1660 out, err := cmd.CombinedOutput()
1661 if err != nil {
1662 t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)
1663 }
1664 cmd = testenv.Command(t, exe)
1665 err = cmd.Run()
1666 if err != nil {
1667 t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)
1668 }
1669 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1670 out, err = cmd.CombinedOutput()
1671 if err != nil {
1672 t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)
1673 }
1674 syms[i] = string(out)
1675 }
1676 if syms[0] == syms[1] {
1677 t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
1678 }
1679 }
1680
1681 func TestCheckLinkname(t *testing.T) {
1682
1683 testenv.MustHaveGoBuild(t)
1684 t.Parallel()
1685
1686 tmpdir := t.TempDir()
1687
1688 tests := []struct {
1689 src string
1690 ok bool
1691 }{
1692
1693 {"ok.go", true},
1694
1695 {"push.go", true},
1696
1697
1698 {"textvar", true},
1699
1700 {"coro.go", false},
1701 {"coro_var.go", false},
1702
1703 {"coro_asm", false},
1704
1705 {"coro2.go", false},
1706
1707 {"builtin.go", false},
1708 {"addmoduledata.go", false},
1709 {"freegc.go", false},
1710
1711 {"fastrand.go", true},
1712 {"badlinkname.go", true},
1713 }
1714 for _, test := range tests {
1715 test := test
1716 t.Run(test.src, func(t *testing.T) {
1717 t.Parallel()
1718 src := "./testdata/linkname/" + test.src
1719 exe := filepath.Join(tmpdir, test.src+".exe")
1720 cmd := goCmd(t, "build", "-o", exe, src)
1721 out, err := cmd.CombinedOutput()
1722 if test.ok && err != nil {
1723 t.Errorf("build failed unexpectedly: %v:\n%s", err, out)
1724 }
1725 if !test.ok && err == nil {
1726 t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out)
1727 }
1728 })
1729 }
1730 }
1731
1732 func TestLinknameBSS(t *testing.T) {
1733
1734
1735 testenv.MustHaveGoBuild(t)
1736 t.Parallel()
1737
1738 tmpdir := t.TempDir()
1739
1740 src := filepath.Join("testdata", "linkname", "sched.go")
1741 exe := filepath.Join(tmpdir, "sched.exe")
1742 cmd := goCmd(t, "build", "-o", exe, src)
1743 out, err := cmd.CombinedOutput()
1744 if err != nil {
1745 t.Fatalf("build failed unexpectedly: %v:\n%s", err, out)
1746 }
1747
1748
1749 f, err := objfile.Open(exe)
1750 if err != nil {
1751 t.Fatalf("fail to open executable: %v", err)
1752 }
1753 defer f.Close()
1754 syms, err := f.Symbols()
1755 if err != nil {
1756 t.Fatalf("fail to get symbols: %v", err)
1757 }
1758 found := false
1759 for _, s := range syms {
1760 if s.Name == "runtime.sched" || s.Name == "_runtime.sched" {
1761 found = true
1762 if s.Size < 100 {
1763
1764
1765
1766 t.Errorf("runtime.sched symbol size too small: want > 100, got %d", s.Size)
1767 }
1768 }
1769 }
1770 if !found {
1771 t.Errorf("runtime.sched symbol not found")
1772 }
1773
1774
1775 cmd = testenv.Command(t, exe)
1776 out, err = cmd.CombinedOutput()
1777 if err != nil {
1778 t.Errorf("executable failed to run: %v\n%s", err, out)
1779 }
1780 }
1781
1782
1783
1784 func setValueFromBytes[T any](p *T, s []byte) {
1785 copy(unsafe.Slice((*byte)(unsafe.Pointer(p)), unsafe.Sizeof(*p)), s)
1786 }
1787
1788
1789
1790
1791 func TestFuncdataPlacement(t *testing.T) {
1792 testenv.MustHaveGoBuild(t)
1793 t.Parallel()
1794
1795 tmpdir := t.TempDir()
1796 src := filepath.Join(tmpdir, "x.go")
1797 if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
1798 t.Fatal(err)
1799 }
1800
1801 exe := filepath.Join(tmpdir, "x.exe")
1802 cmd := goCmd(t, "build", "-o", exe, src)
1803 if out, err := cmd.CombinedOutput(); err != nil {
1804 t.Fatalf("build failed; %v, output:\n%s", err, out)
1805 }
1806
1807
1808
1809
1810
1811
1812
1813 ef, _ := elf.Open(exe)
1814 mf, _ := macho.Open(exe)
1815 pf, _ := pe.Open(exe)
1816 xf, _ := xcoff.Open(exe)
1817
1818 if ef == nil && mf == nil && pf == nil && xf == nil {
1819 t.Skip("unrecognized executable file format")
1820 }
1821
1822 const moddataSymName = "runtime.firstmoduledata"
1823 const gofuncSymName = "go:func.*"
1824 var (
1825 pclntab []byte
1826 pclntabAddr uint64
1827 pclntabEnd uint64
1828 moddataAddr uint64
1829 moddataBytes []byte
1830 gofuncAddr uint64
1831 imageBase uint64
1832 )
1833 switch {
1834 case ef != nil:
1835 defer ef.Close()
1836
1837 syms, err := ef.Symbols()
1838 if err != nil {
1839 t.Fatal(err)
1840 }
1841 for _, sym := range syms {
1842 switch sym.Name {
1843 case moddataSymName:
1844 moddataAddr = sym.Value
1845 case gofuncSymName:
1846 gofuncAddr = sym.Value
1847 }
1848 }
1849
1850 for _, sec := range ef.Sections {
1851 if sec.Name == ".gopclntab" {
1852 data, err := sec.Data()
1853 if err != nil {
1854 t.Fatal(err)
1855 }
1856 pclntab = data
1857 pclntabAddr = sec.Addr
1858 pclntabEnd = sec.Addr + sec.Size
1859 }
1860 if sec.Flags&elf.SHF_ALLOC != 0 && moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
1861 data, err := sec.Data()
1862 if err != nil {
1863 t.Fatal(err)
1864 }
1865 moddataBytes = data[moddataAddr-sec.Addr:]
1866 }
1867 }
1868
1869 case mf != nil:
1870 defer mf.Close()
1871
1872 for _, sym := range mf.Symtab.Syms {
1873 switch sym.Name {
1874 case moddataSymName:
1875 moddataAddr = sym.Value
1876 case gofuncSymName:
1877 gofuncAddr = sym.Value
1878 }
1879 }
1880
1881 for _, sec := range mf.Sections {
1882 if sec.Name == "__gopclntab" {
1883 data, err := sec.Data()
1884 if err != nil {
1885 t.Fatal(err)
1886 }
1887 pclntab = data
1888 pclntabAddr = sec.Addr
1889 pclntabEnd = sec.Addr + sec.Size
1890 }
1891 if moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
1892 data, err := sec.Data()
1893 if err != nil {
1894 t.Fatal(err)
1895 }
1896 moddataBytes = data[moddataAddr-sec.Addr:]
1897 }
1898 }
1899
1900 case pf != nil:
1901 defer pf.Close()
1902
1903 switch ohdr := pf.OptionalHeader.(type) {
1904 case *pe.OptionalHeader32:
1905 imageBase = uint64(ohdr.ImageBase)
1906 case *pe.OptionalHeader64:
1907 imageBase = ohdr.ImageBase
1908 }
1909
1910 var moddataSym, gofuncSym, pclntabSym, epclntabSym *pe.Symbol
1911 for _, sym := range pf.Symbols {
1912 switch sym.Name {
1913 case moddataSymName:
1914 moddataSym = sym
1915 case gofuncSymName:
1916 gofuncSym = sym
1917 case "runtime.pclntab":
1918 pclntabSym = sym
1919 case "runtime.epclntab":
1920 epclntabSym = sym
1921 }
1922 }
1923
1924 if moddataSym == nil {
1925 t.Fatalf("could not find symbol %s", moddataSymName)
1926 }
1927 if gofuncSym == nil {
1928 t.Fatalf("could not find symbol %s", gofuncSymName)
1929 }
1930 if pclntabSym == nil {
1931 t.Fatal("could not find symbol runtime.pclntab")
1932 }
1933 if epclntabSym == nil {
1934 t.Fatal("could not find symbol runtime.epclntab")
1935 }
1936
1937 sec := pf.Sections[moddataSym.SectionNumber-1]
1938 data, err := sec.Data()
1939 if err != nil {
1940 t.Fatal(err)
1941 }
1942 moddataBytes = data[moddataSym.Value:]
1943 moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
1944
1945 sec = pf.Sections[gofuncSym.SectionNumber-1]
1946 gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
1947
1948 if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
1949 t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
1950 }
1951 sec = pf.Sections[pclntabSym.SectionNumber-1]
1952 data, err = sec.Data()
1953 if err != nil {
1954 t.Fatal(err)
1955 }
1956 pclntab = data[pclntabSym.Value:epclntabSym.Value]
1957 pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
1958 pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
1959
1960 case xf != nil:
1961 defer xf.Close()
1962
1963 var moddataSym, gofuncSym, pclntabSym, epclntabSym *xcoff.Symbol
1964 for _, sym := range xf.Symbols {
1965 switch sym.Name {
1966 case moddataSymName:
1967 moddataSym = sym
1968 case gofuncSymName:
1969 gofuncSym = sym
1970 case "runtime.pclntab":
1971 pclntabSym = sym
1972 case "runtime.epclntab":
1973 epclntabSym = sym
1974 }
1975 }
1976
1977 if moddataSym == nil {
1978 t.Fatalf("could not find symbol %s", moddataSymName)
1979 }
1980 if gofuncSym == nil {
1981 t.Fatalf("could not find symbol %s", gofuncSymName)
1982 }
1983 if pclntabSym == nil {
1984 t.Fatal("could not find symbol runtime.pclntab")
1985 }
1986 if epclntabSym == nil {
1987 t.Fatal("could not find symbol runtime.epclntab")
1988 }
1989
1990 sec := xf.Sections[moddataSym.SectionNumber-1]
1991 data, err := sec.Data()
1992 if err != nil {
1993 t.Fatal(err)
1994 }
1995 moddataBytes = data[moddataSym.Value:]
1996 moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
1997
1998 sec = xf.Sections[gofuncSym.SectionNumber-1]
1999 gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
2000
2001 if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
2002 t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
2003 }
2004 sec = xf.Sections[pclntabSym.SectionNumber-1]
2005 data, err = sec.Data()
2006 if err != nil {
2007 t.Fatal(err)
2008 }
2009 pclntab = data[pclntabSym.Value:epclntabSym.Value]
2010 pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
2011 pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
2012
2013 default:
2014 panic("can't happen")
2015 }
2016
2017 if len(pclntab) == 0 {
2018 t.Fatal("could not find pclntab section")
2019 }
2020 if moddataAddr == 0 {
2021 t.Fatalf("could not find %s symbol", moddataSymName)
2022 }
2023 if gofuncAddr == 0 {
2024 t.Fatalf("could not find %s symbol", gofuncSymName)
2025 }
2026 if gofuncAddr < pclntabAddr || gofuncAddr >= pclntabEnd {
2027 t.Fatalf("%s out of range: value %#x not between %#x and %#x", gofuncSymName, gofuncAddr, pclntabAddr, pclntabEnd)
2028 }
2029 if len(moddataBytes) == 0 {
2030 t.Fatal("could not find module data")
2031 }
2032
2033
2034 type moddataSlice struct {
2035 addr uintptr
2036 len int
2037 cap int
2038 }
2039
2040
2041
2042
2043 type moddataType struct {
2044 pcHeader uintptr
2045 funcnametab moddataSlice
2046 cutab moddataSlice
2047 filetab moddataSlice
2048 pctab moddataSlice
2049 pclntable moddataSlice
2050 ftab moddataSlice
2051 findfunctab uintptr
2052 minpc, maxpc uintptr
2053
2054 text, etext uintptr
2055 noptrdata, enoptrdata uintptr
2056 data, edata uintptr
2057 bss, ebss uintptr
2058 noptrbss, enoptrbss uintptr
2059 covctrs, ecovctrs uintptr
2060 end, gcdata, gcbss uintptr
2061 types, etypes uintptr
2062 rodata uintptr
2063 gofunc uintptr
2064 }
2065
2066
2067
2068
2069
2070 var moddata moddataType
2071 setValueFromBytes(&moddata, moddataBytes)
2072
2073 ftabAddr := uint64(moddata.ftab.addr) - imageBase
2074 if ftabAddr < pclntabAddr || ftabAddr >= pclntabEnd {
2075 t.Fatalf("ftab address %#x not between %#x and %#x", ftabAddr, pclntabAddr, pclntabEnd)
2076 }
2077
2078
2079 type functab struct {
2080 entryoff uint32
2081 funcoff uint32
2082 }
2083
2084
2085 ftabLen := moddata.ftab.len - 1
2086 ftab := make([]functab, ftabLen)
2087 copy(ftab, unsafe.Slice((*functab)(unsafe.Pointer(&pclntab[ftabAddr-pclntabAddr])), ftabLen))
2088
2089 ftabBase := uint64(moddata.pclntable.addr) - imageBase
2090
2091
2092 type funcEntry struct {
2093 entryOff uint32
2094 nameOff int32
2095
2096 args int32
2097 deferreturn uint32
2098
2099 pcsp uint32
2100 pcfile uint32
2101 pcln uint32
2102 npcdata uint32
2103 cuOffset uint32
2104 startLine int32
2105 funcID abi.FuncID
2106 flag abi.FuncFlag
2107 _ [1]byte
2108 nfuncdata uint8
2109 }
2110
2111 for i, ftabEntry := range ftab {
2112 funcAddr := ftabBase + uint64(ftabEntry.funcoff)
2113 if funcAddr < pclntabAddr || funcAddr >= pclntabEnd {
2114 t.Errorf("ftab entry %d address %#x not between %#x and %#x", i, funcAddr, pclntabAddr, pclntabEnd)
2115 continue
2116 }
2117
2118 var fe funcEntry
2119 setValueFromBytes(&fe, pclntab[funcAddr-pclntabAddr:])
2120
2121 funcdataVals := funcAddr + uint64(unsafe.Sizeof(fe)) + uint64(fe.npcdata*4)
2122 for j := range fe.nfuncdata {
2123 var funcdataVal uint32
2124 setValueFromBytes(&funcdataVal, pclntab[funcdataVals+uint64(j)*4-pclntabAddr:])
2125 if funcdataVal == ^uint32(0) {
2126 continue
2127 }
2128 funcdataAddr := gofuncAddr + uint64(funcdataVal)
2129 if funcdataAddr < pclntabAddr || funcdataAddr >= pclntabEnd {
2130 t.Errorf("ftab entry %d funcdata %d address %#x not between %#x and %#x", i, j, funcdataAddr, pclntabAddr, pclntabEnd)
2131 }
2132 }
2133 }
2134
2135 if uint64(moddata.findfunctab)-imageBase < pclntabAddr || uint64(moddata.findfunctab)-imageBase >= pclntabEnd {
2136 t.Errorf("findfunctab address %#x not between %#x and %#x", moddata.findfunctab, pclntabAddr, pclntabEnd)
2137 }
2138 }
2139
2140
2141 func TestModuledataPlacement(t *testing.T) {
2142 testenv.MustHaveGoBuild(t)
2143 t.Parallel()
2144
2145 tmpdir := t.TempDir()
2146 src := filepath.Join(tmpdir, "x.go")
2147 if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
2148 t.Fatal(err)
2149 }
2150
2151 exe := filepath.Join(tmpdir, "x.exe")
2152 cmd := goCmd(t, "build", "-o", exe, src)
2153 if out, err := cmd.CombinedOutput(); err != nil {
2154 t.Fatalf("build failed; %v, output:\n%s", err, out)
2155 }
2156
2157 ef, _ := elf.Open(exe)
2158 mf, _ := macho.Open(exe)
2159 pf, _ := pe.Open(exe)
2160 xf, _ := xcoff.Open(exe)
2161
2162 if ef == nil && mf == nil && pf == nil && xf == nil {
2163 t.Skip("unrecognized executable file format")
2164 }
2165
2166 const moddataSymName = "runtime.firstmoduledata"
2167 switch {
2168 case ef != nil:
2169 defer ef.Close()
2170
2171 syms, err := ef.Symbols()
2172 if err != nil {
2173 t.Fatal(err)
2174 }
2175 for _, sym := range syms {
2176 if sym.Name == moddataSymName {
2177 sec := ef.Sections[sym.Section]
2178 if sec.Name != ".go.module" {
2179 t.Errorf("moduledata in section %s, not .go.module", sec.Name)
2180 }
2181 if sym.Value != sec.Addr {
2182 t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.Addr)
2183 }
2184 break
2185 }
2186 }
2187
2188 case mf != nil:
2189 defer mf.Close()
2190
2191 for _, sym := range mf.Symtab.Syms {
2192 if sym.Name == moddataSymName {
2193 if sym.Sect == 0 {
2194 t.Error("moduledata not in a section")
2195 } else {
2196 sec := mf.Sections[sym.Sect-1]
2197 if sec.Name != "__go_module" {
2198 t.Errorf("moduledata in section %s, not __go.module", sec.Name)
2199 }
2200 if sym.Value != sec.Addr {
2201 t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.Addr)
2202 }
2203 }
2204 break
2205 }
2206 }
2207
2208 case pf != nil, xf != nil:
2209 if pf != nil {
2210 defer pf.Close()
2211 }
2212 if xf != nil {
2213 defer xf.Close()
2214 }
2215
2216
2217
2218
2219 }
2220 }
2221
View as plain text