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 if err != nil {
873 t.Errorf("failed to get symbols with err %v", err)
874 }
875 for _, s := range syms {
876 fn := s.Name
877 if _, ok := fname[fn]; !ok {
878 fn = "_" + s.Name
879 if _, ok := fname[fn]; !ok {
880 continue
881 }
882 }
883 if s.Addr%align != 0 {
884 t.Fatalf("unaligned function: %s %x. Expected alignment: %d\n", fn, s.Addr, align)
885 }
886 fname[fn] = true
887 }
888 for k, v := range fname {
889 if !v {
890 t.Fatalf("function %s not found\n", k)
891 }
892 }
893 }
894 alignTest(16)
895 alignTest(32)
896 }
897
898 const testTrampSrc = `
899 package main
900 import "fmt"
901 func main() {
902 fmt.Println("hello")
903
904 defer func(){
905 if e := recover(); e == nil {
906 panic("did not panic")
907 }
908 }()
909 f1()
910 }
911
912 // Test deferreturn trampolines. See issue #39049.
913 func f1() { defer f2() }
914 func f2() { panic("XXX") }
915 `
916
917 func TestTrampoline(t *testing.T) {
918
919
920
921
922 buildmodes := []string{"default"}
923 switch runtime.GOARCH {
924 case "arm", "arm64", "ppc64", "loong64":
925 case "ppc64le":
926
927 buildmodes = append(buildmodes, "pie")
928 default:
929 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
930 }
931
932 testenv.MustHaveGoBuild(t)
933
934 t.Parallel()
935
936 tmpdir := t.TempDir()
937
938 src := filepath.Join(tmpdir, "hello.go")
939 err := os.WriteFile(src, []byte(testTrampSrc), 0666)
940 if err != nil {
941 t.Fatal(err)
942 }
943 exe := filepath.Join(tmpdir, "hello.exe")
944
945 for _, mode := range buildmodes {
946 cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
947 out, err := cmd.CombinedOutput()
948 if err != nil {
949 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
950 }
951 cmd = testenv.Command(t, exe)
952 out, err = cmd.CombinedOutput()
953 if err != nil {
954 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
955 }
956 if string(out) != "hello\n" {
957 t.Errorf("unexpected output (%s):\n%s", mode, out)
958 }
959
960 out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
961 if err != nil {
962 t.Errorf("nm failure: %s\n%s\n", err, string(out))
963 }
964 if ok, _ := regexp.Match("T runtime.deferreturn(\\+0)?-tramp0", out); !ok {
965 t.Errorf("Trampoline T runtime.deferreturn(+0)?-tramp0 is missing")
966 }
967 }
968 }
969
970 const testTrampCgoSrc = `
971 package main
972
973 // #include <stdio.h>
974 // void CHello() { printf("hello\n"); fflush(stdout); }
975 import "C"
976
977 func main() {
978 C.CHello()
979 }
980 `
981
982 func TestTrampolineCgo(t *testing.T) {
983
984
985
986
987 buildmodes := []string{"default"}
988 switch runtime.GOARCH {
989 case "arm", "arm64", "ppc64", "loong64":
990 case "ppc64le":
991
992 buildmodes = append(buildmodes, "pie")
993 default:
994 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
995 }
996
997 testenv.MustHaveGoBuild(t)
998 testenv.MustHaveCGO(t)
999
1000 t.Parallel()
1001
1002 tmpdir := t.TempDir()
1003
1004 src := filepath.Join(tmpdir, "hello.go")
1005 err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
1006 if err != nil {
1007 t.Fatal(err)
1008 }
1009 exe := filepath.Join(tmpdir, "hello.exe")
1010
1011 for _, mode := range buildmodes {
1012 cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
1013 out, err := cmd.CombinedOutput()
1014 if err != nil {
1015 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
1016 }
1017 cmd = testenv.Command(t, exe)
1018 out, err = cmd.CombinedOutput()
1019 if err != nil {
1020 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1021 }
1022 if string(out) != "hello\n" && string(out) != "hello\r\n" {
1023 t.Errorf("unexpected output (%s):\n%s", mode, out)
1024 }
1025
1026
1027
1028 if !testenv.CanInternalLink(true) {
1029 continue
1030 }
1031 cmd = goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
1032 out, err = cmd.CombinedOutput()
1033 if err != nil {
1034 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
1035 }
1036 cmd = testenv.Command(t, exe)
1037 out, err = cmd.CombinedOutput()
1038 if err != nil {
1039 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1040 }
1041 if string(out) != "hello\n" && string(out) != "hello\r\n" {
1042 t.Errorf("unexpected output (%s):\n%s", mode, out)
1043 }
1044 }
1045 }
1046
1047 func TestIndexMismatch(t *testing.T) {
1048
1049
1050
1051 testenv.MustHaveGoBuild(t)
1052
1053
1054 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
1055
1056 t.Parallel()
1057
1058 tmpdir := t.TempDir()
1059
1060 aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
1061 bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
1062 mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
1063 aObj := filepath.Join(tmpdir, "a.o")
1064 mObj := filepath.Join(tmpdir, "main.o")
1065 exe := filepath.Join(tmpdir, "main.exe")
1066
1067 importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")
1068 testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")
1069 importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
1070 testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")
1071
1072
1073 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
1074 t.Log(cmd)
1075 out, err := cmd.CombinedOutput()
1076 if err != nil {
1077 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
1078 }
1079 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
1080 t.Log(cmd)
1081 out, err = cmd.CombinedOutput()
1082 if err != nil {
1083 t.Fatalf("compiling main.go failed: %v\n%s", err, out)
1084 }
1085 cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
1086 t.Log(cmd)
1087 out, err = cmd.CombinedOutput()
1088 if err != nil {
1089 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
1090 testenv.SkipFlaky(t, 58806)
1091 }
1092 t.Errorf("linking failed: %v\n%s", err, out)
1093 }
1094
1095
1096
1097 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
1098 t.Log(cmd)
1099 out, err = cmd.CombinedOutput()
1100 if err != nil {
1101 t.Fatalf("compiling a.go failed: %v\n%s", err, out)
1102 }
1103 cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
1104 t.Log(cmd)
1105 out, err = cmd.CombinedOutput()
1106 if err == nil {
1107 t.Fatalf("linking didn't fail")
1108 }
1109 if !bytes.Contains(out, []byte("fingerprint mismatch")) {
1110 t.Errorf("did not see expected error message. out:\n%s", out)
1111 }
1112 }
1113
1114 func TestPErsrcBinutils(t *testing.T) {
1115
1116 testenv.MustHaveGoBuild(t)
1117
1118 if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
1119
1120 t.Skipf("this is only for windows/amd64 and windows/386")
1121 }
1122
1123 t.Parallel()
1124
1125 tmpdir := t.TempDir()
1126
1127 pkgdir := filepath.Join("testdata", "pe-binutils")
1128 exe := filepath.Join(tmpdir, "a.exe")
1129 cmd := goCmd(t, "build", "-o", exe)
1130 cmd.Dir = pkgdir
1131
1132 out, err := cmd.CombinedOutput()
1133 if err != nil {
1134 t.Fatalf("building failed: %v, output:\n%s", err, out)
1135 }
1136
1137
1138 b, err := os.ReadFile(exe)
1139 if err != nil {
1140 t.Fatalf("reading output failed: %v", err)
1141 }
1142 if !bytes.Contains(b, []byte("Hello Gophers!")) {
1143 t.Fatalf("binary does not contain expected content")
1144 }
1145 }
1146
1147 func TestPErsrcLLVM(t *testing.T) {
1148
1149 testenv.MustHaveGoBuild(t)
1150
1151 if runtime.GOOS != "windows" {
1152 t.Skipf("this is a windows-only test")
1153 }
1154
1155 t.Parallel()
1156
1157 tmpdir := t.TempDir()
1158
1159 pkgdir := filepath.Join("testdata", "pe-llvm")
1160 exe := filepath.Join(tmpdir, "a.exe")
1161 cmd := goCmd(t, "build", "-o", exe)
1162 cmd.Dir = pkgdir
1163
1164 out, err := cmd.CombinedOutput()
1165 if err != nil {
1166 t.Fatalf("building failed: %v, output:\n%s", err, out)
1167 }
1168
1169
1170 b, err := os.ReadFile(exe)
1171 if err != nil {
1172 t.Fatalf("reading output failed: %v", err)
1173 }
1174 if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
1175 t.Fatalf("binary does not contain expected content")
1176 }
1177 }
1178
1179 func TestContentAddressableSymbols(t *testing.T) {
1180
1181 testenv.MustHaveGoBuild(t)
1182
1183 t.Parallel()
1184
1185 src := filepath.Join("testdata", "testHashedSyms", "p.go")
1186 cmd := goCmd(t, "run", src)
1187 out, err := cmd.CombinedOutput()
1188 if err != nil {
1189 t.Errorf("command %s failed: %v\n%s", cmd, err, out)
1190 }
1191 }
1192
1193 func TestReadOnly(t *testing.T) {
1194
1195 testenv.MustHaveGoBuild(t)
1196
1197 t.Parallel()
1198
1199 src := filepath.Join("testdata", "testRO", "x.go")
1200 cmd := goCmd(t, "run", src)
1201 out, err := cmd.CombinedOutput()
1202 if err == nil {
1203 t.Errorf("running test program did not fail. output:\n%s", out)
1204 }
1205 }
1206
1207 const testIssue38554Src = `
1208 package main
1209
1210 type T [10<<20]byte
1211
1212 //go:noinline
1213 func f() T {
1214 return T{} // compiler will make a large stmp symbol, but not used.
1215 }
1216
1217 func main() {
1218 x := f()
1219 println(x[1])
1220 }
1221 `
1222
1223 func TestIssue38554(t *testing.T) {
1224 testenv.MustHaveGoBuild(t)
1225
1226 t.Parallel()
1227
1228 tmpdir := t.TempDir()
1229
1230 src := filepath.Join(tmpdir, "x.go")
1231 err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
1232 if err != nil {
1233 t.Fatalf("failed to write source file: %v", err)
1234 }
1235 exe := filepath.Join(tmpdir, "x.exe")
1236 cmd := goCmd(t, "build", "-o", exe, src)
1237 out, err := cmd.CombinedOutput()
1238 if err != nil {
1239 t.Fatalf("build failed: %v\n%s", err, out)
1240 }
1241
1242 fi, err := os.Stat(exe)
1243 if err != nil {
1244 t.Fatalf("failed to stat output file: %v", err)
1245 }
1246
1247
1248
1249
1250 const want = 5 << 20
1251 if got := fi.Size(); got > want {
1252 t.Errorf("binary too big: got %d, want < %d", got, want)
1253 }
1254 }
1255
1256 const testIssue42396src = `
1257 package main
1258
1259 //go:noinline
1260 //go:nosplit
1261 func callee(x int) {
1262 }
1263
1264 func main() {
1265 callee(9)
1266 }
1267 `
1268
1269 func TestIssue42396(t *testing.T) {
1270 testenv.MustHaveGoBuild(t)
1271
1272 if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
1273 t.Skip("no race detector support")
1274 }
1275
1276 t.Parallel()
1277
1278 tmpdir := t.TempDir()
1279
1280 src := filepath.Join(tmpdir, "main.go")
1281 err := os.WriteFile(src, []byte(testIssue42396src), 0666)
1282 if err != nil {
1283 t.Fatalf("failed to write source file: %v", err)
1284 }
1285 exe := filepath.Join(tmpdir, "main.exe")
1286 cmd := goCmd(t, "build", "-gcflags=-race", "-o", exe, src)
1287 out, err := cmd.CombinedOutput()
1288 if err == nil {
1289 t.Fatalf("build unexpectedly succeeded")
1290 }
1291
1292
1293
1294 if strings.Contains(string(out), "panic:") {
1295 t.Fatalf("build should not fail with panic:\n%s", out)
1296 }
1297 const want = "reference to undefined builtin"
1298 if !strings.Contains(string(out), want) {
1299 t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
1300 }
1301 }
1302
1303 const testLargeRelocSrc = `
1304 package main
1305
1306 var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
1307
1308 var addr = [...]*byte{
1309 &x[1<<23-1],
1310 &x[1<<23],
1311 &x[1<<23+1],
1312 &x[1<<24-1],
1313 &x[1<<24],
1314 &x[1<<24+1],
1315 }
1316
1317 func main() {
1318 // check relocations in instructions
1319 check(x[1<<23-1], 0)
1320 check(x[1<<23], 23)
1321 check(x[1<<23+1], 0)
1322 check(x[1<<24-1], 0)
1323 check(x[1<<24], 24)
1324 check(x[1<<24+1], 0)
1325
1326 // check absolute address relocations in data
1327 check(*addr[0], 0)
1328 check(*addr[1], 23)
1329 check(*addr[2], 0)
1330 check(*addr[3], 0)
1331 check(*addr[4], 24)
1332 check(*addr[5], 0)
1333 }
1334
1335 func check(x, y byte) {
1336 if x != y {
1337 panic("FAIL")
1338 }
1339 }
1340 `
1341
1342 func TestLargeReloc(t *testing.T) {
1343
1344
1345
1346 testenv.MustHaveGoBuild(t)
1347 t.Parallel()
1348
1349 tmpdir := t.TempDir()
1350
1351 src := filepath.Join(tmpdir, "x.go")
1352 err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
1353 if err != nil {
1354 t.Fatalf("failed to write source file: %v", err)
1355 }
1356 cmd := goCmd(t, "run", src)
1357 out, err := cmd.CombinedOutput()
1358 if err != nil {
1359 t.Errorf("build failed: %v. output:\n%s", err, out)
1360 }
1361
1362 if testenv.HasCGO() {
1363 cmd = goCmd(t, "run", "-ldflags=-linkmode=external", src)
1364 out, err = cmd.CombinedOutput()
1365 if err != nil {
1366 t.Fatalf("build failed: %v. output:\n%s", err, out)
1367 }
1368 }
1369 }
1370
1371 func TestUnlinkableObj(t *testing.T) {
1372
1373 testenv.MustHaveGoBuild(t)
1374 t.Parallel()
1375
1376 if true {
1377 t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
1378 }
1379
1380 tmpdir := t.TempDir()
1381
1382 xSrc := filepath.Join(tmpdir, "x.go")
1383 pSrc := filepath.Join(tmpdir, "p.go")
1384 xObj := filepath.Join(tmpdir, "x.o")
1385 pObj := filepath.Join(tmpdir, "p.o")
1386 exe := filepath.Join(tmpdir, "x.exe")
1387 importcfgfile := filepath.Join(tmpdir, "importcfg")
1388 testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
1389 err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
1390 if err != nil {
1391 t.Fatalf("failed to write source file: %v", err)
1392 }
1393 err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
1394 if err != nil {
1395 t.Fatalf("failed to write source file: %v", err)
1396 }
1397 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc)
1398 out, err := cmd.CombinedOutput()
1399 if err != nil {
1400 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1401 }
1402 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
1403 out, err = cmd.CombinedOutput()
1404 if err != nil {
1405 t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
1406 }
1407 cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
1408 out, err = cmd.CombinedOutput()
1409 if err == nil {
1410 t.Fatalf("link did not fail")
1411 }
1412 if !bytes.Contains(out, []byte("unlinkable object")) {
1413 t.Errorf("did not see expected error message. out:\n%s", out)
1414 }
1415
1416
1417 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
1418 out, err = cmd.CombinedOutput()
1419 if err != nil {
1420 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
1421 }
1422 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc)
1423 out, err = cmd.CombinedOutput()
1424 if err != nil {
1425 t.Fatalf("compile failed: %v. output:\n%s", err, out)
1426 }
1427
1428 cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
1429 out, err = cmd.CombinedOutput()
1430 if err != nil {
1431 t.Errorf("link failed: %v. output:\n%s", err, out)
1432 }
1433 }
1434
1435 func TestExtLinkCmdlineDeterminism(t *testing.T) {
1436
1437 testenv.MustHaveGoBuild(t)
1438 testenv.MustHaveCGO(t)
1439 t.Parallel()
1440
1441
1442 testSrc := `
1443 package main
1444 import "C"
1445 //export F1
1446 func F1() {}
1447 //export F2
1448 func F2() {}
1449 //export F3
1450 func F3() {}
1451 func main() {}
1452 `
1453
1454 tmpdir := t.TempDir()
1455 src := filepath.Join(tmpdir, "x.go")
1456 if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {
1457 t.Fatal(err)
1458 }
1459 exe := filepath.Join(tmpdir, "x.exe")
1460
1461
1462
1463 linktmp := filepath.Join(tmpdir, "linktmp")
1464 if err := os.Mkdir(linktmp, 0777); err != nil {
1465 t.Fatal(err)
1466 }
1467
1468
1469
1470 ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp
1471 var out0 []byte
1472 for i := 0; i < 5; i++ {
1473 cmd := goCmd(t, "build", ldflags, "-o", exe, src)
1474 out, err := cmd.CombinedOutput()
1475 if err != nil {
1476 t.Fatalf("build failed: %v, output:\n%s", err, out)
1477 }
1478 if err := os.Remove(exe); err != nil {
1479 t.Fatal(err)
1480 }
1481
1482
1483 j := bytes.Index(out, []byte("\nhost link:"))
1484 if j == -1 {
1485 t.Fatalf("host link step not found, output:\n%s", out)
1486 }
1487 out = out[j+1:]
1488 k := bytes.Index(out, []byte("\n"))
1489 if k == -1 {
1490 t.Fatalf("no newline after host link, output:\n%s", out)
1491 }
1492 out = out[:k]
1493
1494
1495
1496 fs := bytes.Fields(out)
1497 for i, f := range fs {
1498 if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {
1499 fs[i+1] = []byte("a.out")
1500 break
1501 }
1502 }
1503 out = bytes.Join(fs, []byte{' '})
1504
1505 if i == 0 {
1506 out0 = out
1507 continue
1508 }
1509 if !bytes.Equal(out0, out) {
1510 t.Fatalf("output differ:\n%s\n==========\n%s", out0, out)
1511 }
1512 }
1513 }
1514
1515
1516
1517 func TestResponseFile(t *testing.T) {
1518 t.Parallel()
1519
1520 testenv.MustHaveGoBuild(t)
1521
1522
1523
1524 testenv.MustHaveCGO(t)
1525
1526 tmpdir := t.TempDir()
1527
1528 src := filepath.Join(tmpdir, "x.go")
1529 if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {
1530 t.Fatal(err)
1531 }
1532
1533
1534
1535
1536 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")
1537 cmd.Dir = tmpdir
1538
1539
1540 var sb strings.Builder
1541 sb.WriteString(`'-ldflags=all="-extldflags=`)
1542 for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {
1543 if i > 0 {
1544 sb.WriteString(" ")
1545 }
1546 sb.WriteString("-g")
1547 }
1548 sb.WriteString(`"'`)
1549 cmd = testenv.CleanCmdEnv(cmd)
1550 cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())
1551
1552 out, err := cmd.CombinedOutput()
1553 if len(out) > 0 {
1554 t.Logf("%s", out)
1555 }
1556 if err != nil {
1557 t.Error(err)
1558 }
1559 }
1560
1561 func TestDynimportVar(t *testing.T) {
1562
1563
1564 if runtime.GOOS != "darwin" {
1565 t.Skip("skip on non-darwin platform")
1566 }
1567
1568 testenv.MustHaveGoBuild(t)
1569 testenv.MustHaveCGO(t)
1570
1571 t.Parallel()
1572
1573 tmpdir := t.TempDir()
1574 exe := filepath.Join(tmpdir, "a.exe")
1575 src := filepath.Join("testdata", "dynimportvar", "main.go")
1576
1577 for _, mode := range []string{"internal", "external"} {
1578 cmd := goCmd(t, "build", "-ldflags=-linkmode="+mode, "-o", exe, src)
1579 out, err := cmd.CombinedOutput()
1580 if err != nil {
1581 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1582 }
1583 cmd = testenv.Command(t, exe)
1584 out, err = cmd.CombinedOutput()
1585 if err != nil {
1586 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
1587 }
1588 }
1589 }
1590
1591 const helloSrc = `
1592 package main
1593 var X = 42
1594 var Y int
1595 func main() { println("hello", X, Y) }
1596 `
1597
1598 func TestFlagS(t *testing.T) {
1599
1600 testenv.MustHaveGoBuild(t)
1601
1602 t.Parallel()
1603
1604 tmpdir := t.TempDir()
1605 exe := filepath.Join(tmpdir, "a.exe")
1606 src := filepath.Join(tmpdir, "a.go")
1607 err := os.WriteFile(src, []byte(helloSrc), 0666)
1608 if err != nil {
1609 t.Fatal(err)
1610 }
1611
1612 modes := []string{"auto"}
1613 if testenv.HasCGO() {
1614 modes = append(modes, "external")
1615 }
1616
1617
1618 syms := []string{"main.main", "main.X", "main.Y"}
1619
1620 for _, mode := range modes {
1621 cmd := goCmd(t, "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)
1622 out, err := cmd.CombinedOutput()
1623 if err != nil {
1624 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
1625 }
1626 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1627 out, err = cmd.CombinedOutput()
1628 if err != nil {
1629 if _, ok := errors.AsType[*exec.ExitError](err); !ok {
1630
1631
1632
1633 t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)
1634 }
1635 }
1636 for _, s := range syms {
1637 if bytes.Contains(out, []byte(s)) {
1638 t.Errorf("(mode=%s): unexpected symbol %s", mode, s)
1639 }
1640 }
1641 }
1642 }
1643
1644 func TestRandLayout(t *testing.T) {
1645
1646
1647 testenv.MustHaveGoBuild(t)
1648
1649 t.Parallel()
1650
1651 tmpdir := t.TempDir()
1652
1653 src := filepath.Join(tmpdir, "hello.go")
1654 err := os.WriteFile(src, []byte(trivialSrc), 0666)
1655 if err != nil {
1656 t.Fatal(err)
1657 }
1658
1659 var syms [2]string
1660 for i, seed := range []string{"123", "456"} {
1661 exe := filepath.Join(tmpdir, "hello"+seed+".exe")
1662 cmd := goCmd(t, "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
1663 out, err := cmd.CombinedOutput()
1664 if err != nil {
1665 t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)
1666 }
1667 cmd = testenv.Command(t, exe)
1668 err = cmd.Run()
1669 if err != nil {
1670 t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)
1671 }
1672 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
1673 out, err = cmd.CombinedOutput()
1674 if err != nil {
1675 t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)
1676 }
1677 syms[i] = string(out)
1678 }
1679 if syms[0] == syms[1] {
1680 t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
1681 }
1682 }
1683
1684 func TestCheckLinkname(t *testing.T) {
1685
1686 testenv.MustHaveGoBuild(t)
1687 t.Parallel()
1688
1689 tmpdir := t.TempDir()
1690
1691 tests := []struct {
1692 src string
1693 ok bool
1694 }{
1695
1696 {"ok.go", true},
1697
1698 {"push.go", true},
1699
1700
1701 {"textvar", true},
1702
1703 {"coro.go", false},
1704 {"coro_var.go", false},
1705
1706 {"coro_asm", false},
1707
1708 {"coro2.go", false},
1709
1710 {"builtin.go", false},
1711 {"addmoduledata.go", false},
1712 {"freegc.go", false},
1713
1714 {"fastrand.go", true},
1715 {"badlinkname.go", true},
1716 }
1717 for _, test := range tests {
1718 test := test
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
View as plain text