1// Copyright 2016 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package main 6 7import ( 8 "bufio" 9 "bytes" 10 "debug/macho" 11 "errors" 12 "internal/platform" 13 "internal/testenv" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "regexp" 18 "runtime" 19 "strings" 20 "testing" 21 22 "cmd/internal/sys" 23) 24 25var AuthorPaidByTheColumnInch struct { 26 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."` 27 28 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."` 29 30 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."` 31 32 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."` 33} 34 35func TestLargeSymName(t *testing.T) { 36 // The compiler generates a symbol name using the string form of the 37 // type. This tests that the linker can read symbol names larger than 38 // the bufio buffer. Issue #15104. 39 _ = AuthorPaidByTheColumnInch 40} 41 42func TestIssue21703(t *testing.T) { 43 t.Parallel() 44 45 testenv.MustHaveGoBuild(t) 46 testenv.MustInternalLink(t, false) 47 48 const source = ` 49package main 50const X = "\n!\n" 51func main() {} 52` 53 54 tmpdir := t.TempDir() 55 main := filepath.Join(tmpdir, "main.go") 56 57 err := os.WriteFile(main, []byte(source), 0666) 58 if err != nil { 59 t.Fatalf("failed to write main.go: %v\n", err) 60 } 61 62 importcfgfile := filepath.Join(tmpdir, "importcfg") 63 testenv.WriteImportcfg(t, importcfgfile, nil, main) 64 65 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go") 66 cmd.Dir = tmpdir 67 out, err := cmd.CombinedOutput() 68 if err != nil { 69 t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out) 70 } 71 72 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "main.o") 73 cmd.Dir = tmpdir 74 out, err = cmd.CombinedOutput() 75 if err != nil { 76 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" { 77 testenv.SkipFlaky(t, 58806) 78 } 79 t.Fatalf("failed to link main.o: %v, output: %s\n", err, out) 80 } 81} 82 83// TestIssue28429 ensures that the linker does not attempt to link 84// sections not named *.o. Such sections may be used by a build system 85// to, for example, save facts produced by a modular static analysis 86// such as golang.org/x/tools/go/analysis. 87func TestIssue28429(t *testing.T) { 88 t.Parallel() 89 90 testenv.MustHaveGoBuild(t) 91 testenv.MustInternalLink(t, false) 92 93 tmpdir := t.TempDir() 94 95 write := func(name, content string) { 96 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666) 97 if err != nil { 98 t.Fatal(err) 99 } 100 } 101 102 runGo := func(args ...string) { 103 cmd := testenv.Command(t, testenv.GoToolPath(t), args...) 104 cmd.Dir = tmpdir 105 out, err := cmd.CombinedOutput() 106 if err != nil { 107 if len(args) >= 2 && args[1] == "link" && runtime.GOOS == "android" && runtime.GOARCH == "arm64" { 108 testenv.SkipFlaky(t, 58806) 109 } 110 t.Fatalf("'go %s' failed: %v, output: %s", 111 strings.Join(args, " "), err, out) 112 } 113 } 114 115 // Compile a main package. 116 write("main.go", "package main; func main() {}") 117 importcfgfile := filepath.Join(tmpdir, "importcfg") 118 testenv.WriteImportcfg(t, importcfgfile, nil, filepath.Join(tmpdir, "main.go")) 119 runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go") 120 runGo("tool", "pack", "c", "main.a", "main.o") 121 122 // Add an extra section with a short, non-.o name. 123 // This simulates an alternative build system. 124 write(".facts", "this is not an object file") 125 runGo("tool", "pack", "r", "main.a", ".facts") 126 127 // Verify that the linker does not attempt 128 // to compile the extra section. 129 runGo("tool", "link", "-importcfg="+importcfgfile, "main.a") 130} 131 132func TestUnresolved(t *testing.T) { 133 testenv.MustHaveGoBuild(t) 134 135 t.Parallel() 136 137 tmpdir := t.TempDir() 138 139 write := func(name, content string) { 140 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666) 141 if err != nil { 142 t.Fatal(err) 143 } 144 } 145 146 // Test various undefined references. Because of issue #29852, 147 // this used to give confusing error messages because the 148 // linker would find an undefined reference to "zero" created 149 // by the runtime package. 150 151 write("go.mod", "module testunresolved\n") 152 write("main.go", `package main 153 154func main() { 155 x() 156} 157 158func x() 159`) 160 write("main.s", ` 161TEXT ·x(SB),0,$0 162 MOVD zero<>(SB), AX 163 MOVD zero(SB), AX 164 MOVD ·zero(SB), AX 165 RET 166`) 167 cmd := testenv.Command(t, testenv.GoToolPath(t), "build") 168 cmd.Dir = tmpdir 169 cmd.Env = append(os.Environ(), 170 "GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath")) 171 out, err := cmd.CombinedOutput() 172 if err == nil { 173 t.Fatalf("expected build to fail, but it succeeded") 174 } 175 out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil) 176 got := string(out) 177 want := `main.x: relocation target zero not defined 178main.x: relocation target zero not defined 179main.x: relocation target main.zero not defined 180` 181 if want != got { 182 t.Fatalf("want:\n%sgot:\n%s", want, got) 183 } 184} 185 186func TestIssue33979(t *testing.T) { 187 testenv.MustHaveGoBuild(t) 188 testenv.MustHaveCGO(t) 189 testenv.MustInternalLink(t, true) 190 191 t.Parallel() 192 193 tmpdir := t.TempDir() 194 195 write := func(name, content string) { 196 err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666) 197 if err != nil { 198 t.Fatal(err) 199 } 200 } 201 202 run := func(name string, args ...string) string { 203 cmd := testenv.Command(t, name, args...) 204 cmd.Dir = tmpdir 205 out, err := cmd.CombinedOutput() 206 if err != nil { 207 t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out) 208 } 209 return string(out) 210 } 211 runGo := func(args ...string) string { 212 return run(testenv.GoToolPath(t), args...) 213 } 214 215 // Test object with undefined reference that was not generated 216 // by Go, resulting in an SXREF symbol being loaded during linking. 217 // Because of issue #33979, the SXREF symbol would be found during 218 // error reporting, resulting in confusing error messages. 219 220 write("main.go", `package main 221func main() { 222 x() 223} 224func x() 225`) 226 // The following assembly must work on all architectures. 227 write("x.s", ` 228TEXT ·x(SB),0,$0 229 CALL foo(SB) 230 RET 231`) 232 write("x.c", ` 233void undefined(); 234 235void foo() { 236 undefined(); 237} 238`) 239 240 cc := strings.TrimSpace(runGo("env", "CC")) 241 cflags := strings.Fields(runGo("env", "GOGCCFLAGS")) 242 243 importcfgfile := filepath.Join(tmpdir, "importcfg") 244 testenv.WriteImportcfg(t, importcfgfile, nil, "runtime") 245 246 // Compile, assemble and pack the Go and C code. 247 runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s") 248 runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go") 249 runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s") 250 run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...) 251 runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o") 252 253 // Now attempt to link using the internal linker. 254 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-linkmode=internal", "x.a") 255 cmd.Dir = tmpdir 256 out, err := cmd.CombinedOutput() 257 if err == nil { 258 t.Fatalf("expected link to fail, but it succeeded") 259 } 260 re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`) 261 if !re.Match(out) { 262 t.Fatalf("got:\n%q\nwant:\n%s", out, re) 263 } 264} 265 266func TestBuildForTvOS(t *testing.T) { 267 testenv.MustHaveCGO(t) 268 testenv.MustHaveGoBuild(t) 269 270 // Only run this on darwin, where we can cross build for tvOS. 271 if runtime.GOOS != "darwin" { 272 t.Skip("skipping on non-darwin platform") 273 } 274 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { 275 t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty") 276 } 277 if err := testenv.Command(t, "xcrun", "--help").Run(); err != nil { 278 t.Skipf("error running xcrun, required for iOS cross build: %v", err) 279 } 280 281 t.Parallel() 282 283 sdkPath, err := testenv.Command(t, "xcrun", "--sdk", "appletvos", "--show-sdk-path").Output() 284 if err != nil { 285 t.Skip("failed to locate appletvos SDK, skipping") 286 } 287 CC := []string{ 288 "clang", 289 "-arch", 290 "arm64", 291 "-isysroot", strings.TrimSpace(string(sdkPath)), 292 "-mtvos-version-min=12.0", 293 "-fembed-bitcode", 294 } 295 CGO_LDFLAGS := []string{"-framework", "CoreFoundation"} 296 lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go") 297 tmpDir := t.TempDir() 298 299 ar := filepath.Join(tmpDir, "lib.a") 300 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib) 301 env := []string{ 302 "CGO_ENABLED=1", 303 "GOOS=ios", 304 "GOARCH=arm64", 305 "CC=" + strings.Join(CC, " "), 306 "CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459 307 "CGO_LDFLAGS=" + strings.Join(CGO_LDFLAGS, " "), 308 } 309 cmd.Env = append(os.Environ(), env...) 310 t.Logf("%q %v", env, cmd) 311 if out, err := cmd.CombinedOutput(); err != nil { 312 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out) 313 } 314 315 link := testenv.Command(t, CC[0], CC[1:]...) 316 link.Args = append(link.Args, CGO_LDFLAGS...) 317 link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out")) // Avoid writing to package directory. 318 link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m")) 319 t.Log(link) 320 if out, err := link.CombinedOutput(); err != nil { 321 t.Fatalf("%v: %v:\n%s", link.Args, err, out) 322 } 323} 324 325var testXFlagSrc = ` 326package main 327var X = "hello" 328var Z = [99999]int{99998:12345} // make it large enough to be mmaped 329func main() { println(X) } 330` 331 332func TestXFlag(t *testing.T) { 333 testenv.MustHaveGoBuild(t) 334 335 t.Parallel() 336 337 tmpdir := t.TempDir() 338 339 src := filepath.Join(tmpdir, "main.go") 340 err := os.WriteFile(src, []byte(testXFlagSrc), 0666) 341 if err != nil { 342 t.Fatal(err) 343 } 344 345 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src) 346 if out, err := cmd.CombinedOutput(); err != nil { 347 t.Errorf("%v: %v:\n%s", cmd.Args, err, out) 348 } 349} 350 351var trivialSrc = ` 352package main 353func main() { } 354` 355 356func TestMachOBuildVersion(t *testing.T) { 357 testenv.MustHaveGoBuild(t) 358 359 t.Parallel() 360 361 tmpdir := t.TempDir() 362 363 src := filepath.Join(tmpdir, "main.go") 364 err := os.WriteFile(src, []byte(trivialSrc), 0666) 365 if err != nil { 366 t.Fatal(err) 367 } 368 369 exe := filepath.Join(tmpdir, "main") 370 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src) 371 cmd.Env = append(os.Environ(), 372 "CGO_ENABLED=0", 373 "GOOS=darwin", 374 "GOARCH=amd64", 375 ) 376 if out, err := cmd.CombinedOutput(); err != nil { 377 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out) 378 } 379 exef, err := os.Open(exe) 380 if err != nil { 381 t.Fatal(err) 382 } 383 defer exef.Close() 384 exem, err := macho.NewFile(exef) 385 if err != nil { 386 t.Fatal(err) 387 } 388 found := false 389 const LC_BUILD_VERSION = 0x32 390 checkMin := func(ver uint32) { 391 major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff 392 if major < 11 { 393 t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 11.0.0", major, minor, patch) 394 } 395 } 396 for _, cmd := range exem.Loads { 397 raw := cmd.Raw() 398 type_ := exem.ByteOrder.Uint32(raw) 399 if type_ != LC_BUILD_VERSION { 400 continue 401 } 402 osVer := exem.ByteOrder.Uint32(raw[12:]) 403 checkMin(osVer) 404 sdkVer := exem.ByteOrder.Uint32(raw[16:]) 405 checkMin(sdkVer) 406 found = true 407 break 408 } 409 if !found { 410 t.Errorf("no LC_BUILD_VERSION load command found") 411 } 412} 413 414const Issue34788src = ` 415 416package blah 417 418func Blah(i int) int { 419 a := [...]int{1, 2, 3, 4, 5, 6, 7, 8} 420 return a[i&7] 421} 422` 423 424func TestIssue34788Android386TLSSequence(t *testing.T) { 425 testenv.MustHaveGoBuild(t) 426 427 // This is a cross-compilation test, so it doesn't make 428 // sense to run it on every GOOS/GOARCH combination. Limit 429 // the test to amd64 + darwin/linux. 430 if runtime.GOARCH != "amd64" || 431 (runtime.GOOS != "darwin" && runtime.GOOS != "linux") { 432 t.Skip("skipping on non-{linux,darwin}/amd64 platform") 433 } 434 435 t.Parallel() 436 437 tmpdir := t.TempDir() 438 439 src := filepath.Join(tmpdir, "blah.go") 440 err := os.WriteFile(src, []byte(Issue34788src), 0666) 441 if err != nil { 442 t.Fatal(err) 443 } 444 445 obj := filepath.Join(tmpdir, "blah.o") 446 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src) 447 cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android") 448 if out, err := cmd.CombinedOutput(); err != nil { 449 t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out) 450 } 451 452 // Run objdump on the resulting object. 453 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "objdump", obj) 454 out, oerr := cmd.CombinedOutput() 455 if oerr != nil { 456 t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out) 457 } 458 459 // Sift through the output; we should not be seeing any R_TLS_LE relocs. 460 scanner := bufio.NewScanner(bytes.NewReader(out)) 461 for scanner.Scan() { 462 line := scanner.Text() 463 if strings.Contains(line, "R_TLS_LE") { 464 t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line) 465 } 466 } 467} 468 469const testStrictDupGoSrc = ` 470package main 471func f() 472func main() { f() } 473` 474 475const testStrictDupAsmSrc1 = ` 476#include "textflag.h" 477TEXT ·f(SB), NOSPLIT|DUPOK, $0-0 478 RET 479` 480 481const testStrictDupAsmSrc2 = ` 482#include "textflag.h" 483TEXT ·f(SB), NOSPLIT|DUPOK, $0-0 484 JMP 0(PC) 485` 486 487const testStrictDupAsmSrc3 = ` 488#include "textflag.h" 489GLOBL ·rcon(SB), RODATA|DUPOK, $64 490` 491 492const testStrictDupAsmSrc4 = ` 493#include "textflag.h" 494GLOBL ·rcon(SB), RODATA|DUPOK, $32 495` 496 497func TestStrictDup(t *testing.T) { 498 // Check that -strictdups flag works. 499 testenv.MustHaveGoBuild(t) 500 501 asmfiles := []struct { 502 fname string 503 payload string 504 }{ 505 {"a", testStrictDupAsmSrc1}, 506 {"b", testStrictDupAsmSrc2}, 507 {"c", testStrictDupAsmSrc3}, 508 {"d", testStrictDupAsmSrc4}, 509 } 510 511 t.Parallel() 512 513 tmpdir := t.TempDir() 514 515 src := filepath.Join(tmpdir, "x.go") 516 err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666) 517 if err != nil { 518 t.Fatal(err) 519 } 520 for _, af := range asmfiles { 521 src = filepath.Join(tmpdir, af.fname+".s") 522 err = os.WriteFile(src, []byte(af.payload), 0666) 523 if err != nil { 524 t.Fatal(err) 525 } 526 } 527 src = filepath.Join(tmpdir, "go.mod") 528 err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666) 529 if err != nil { 530 t.Fatal(err) 531 } 532 533 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1") 534 cmd.Dir = tmpdir 535 out, err := cmd.CombinedOutput() 536 if err != nil { 537 t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out)) 538 } 539 if !bytes.Contains(out, []byte("mismatched payload")) { 540 t.Errorf("unexpected output:\n%s", out) 541 } 542 543 cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2") 544 cmd.Dir = tmpdir 545 out, err = cmd.CombinedOutput() 546 if err == nil { 547 t.Errorf("linking with -strictdups=2 did not fail") 548 } 549 // NB: on amd64 we get the 'new length' error, on arm64 the 'different 550 // contents' error. 551 if !(bytes.Contains(out, []byte("mismatched payload: new length")) || 552 bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) || 553 !bytes.Contains(out, []byte("mismatched payload: different sizes")) { 554 t.Errorf("unexpected output:\n%s", out) 555 } 556} 557 558const testFuncAlignSrc = ` 559package main 560import ( 561 "fmt" 562) 563func alignPc() 564var alignPcFnAddr uintptr 565 566func main() { 567 if alignPcFnAddr % 512 != 0 { 568 fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr) 569 } else { 570 fmt.Printf("PASS") 571 } 572} 573` 574 575var testFuncAlignAsmSources = map[string]string{ 576 "arm64": ` 577#include "textflag.h" 578 579TEXT ·alignPc(SB),NOSPLIT, $0-0 580 MOVD $2, R0 581 PCALIGN $512 582 MOVD $3, R1 583 RET 584 585GLOBL ·alignPcFnAddr(SB),RODATA,$8 586DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB) 587`, 588 "loong64": ` 589#include "textflag.h" 590 591TEXT ·alignPc(SB),NOSPLIT, $0-0 592 MOVV $2, R4 593 PCALIGN $512 594 MOVV $3, R5 595 RET 596 597GLOBL ·alignPcFnAddr(SB),RODATA,$8 598DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB) 599`, 600} 601 602// TestFuncAlign verifies that the address of a function can be aligned 603// with a specific value on arm64 and loong64. 604func TestFuncAlign(t *testing.T) { 605 testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH] 606 if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" { 607 t.Skip("skipping on non-linux/{arm64,loong64} platform") 608 } 609 testenv.MustHaveGoBuild(t) 610 611 t.Parallel() 612 613 tmpdir := t.TempDir() 614 615 src := filepath.Join(tmpdir, "go.mod") 616 err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666) 617 if err != nil { 618 t.Fatal(err) 619 } 620 src = filepath.Join(tmpdir, "falign.go") 621 err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666) 622 if err != nil { 623 t.Fatal(err) 624 } 625 src = filepath.Join(tmpdir, "falign.s") 626 err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666) 627 if err != nil { 628 t.Fatal(err) 629 } 630 631 // Build and run with old object file format. 632 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "falign") 633 cmd.Dir = tmpdir 634 out, err := cmd.CombinedOutput() 635 if err != nil { 636 t.Errorf("build failed: %v", err) 637 } 638 cmd = testenv.Command(t, tmpdir+"/falign") 639 out, err = cmd.CombinedOutput() 640 if err != nil { 641 t.Errorf("failed to run with err %v, output: %s", err, out) 642 } 643 if string(out) != "PASS" { 644 t.Errorf("unexpected output: %s\n", out) 645 } 646} 647 648const testTrampSrc = ` 649package main 650import "fmt" 651func main() { 652 fmt.Println("hello") 653 654 defer func(){ 655 if e := recover(); e == nil { 656 panic("did not panic") 657 } 658 }() 659 f1() 660} 661 662// Test deferreturn trampolines. See issue #39049. 663func f1() { defer f2() } 664func f2() { panic("XXX") } 665` 666 667func TestTrampoline(t *testing.T) { 668 // Test that trampoline insertion works as expected. 669 // For stress test, we set -debugtramp=2 flag, which sets a very low 670 // threshold for trampoline generation, and essentially all cross-package 671 // calls will use trampolines. 672 buildmodes := []string{"default"} 673 switch runtime.GOARCH { 674 case "arm", "arm64", "ppc64": 675 case "ppc64le": 676 // Trampolines are generated differently when internal linking PIE, test them too. 677 buildmodes = append(buildmodes, "pie") 678 default: 679 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH) 680 } 681 682 testenv.MustHaveGoBuild(t) 683 684 t.Parallel() 685 686 tmpdir := t.TempDir() 687 688 src := filepath.Join(tmpdir, "hello.go") 689 err := os.WriteFile(src, []byte(testTrampSrc), 0666) 690 if err != nil { 691 t.Fatal(err) 692 } 693 exe := filepath.Join(tmpdir, "hello.exe") 694 695 for _, mode := range buildmodes { 696 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src) 697 out, err := cmd.CombinedOutput() 698 if err != nil { 699 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) 700 } 701 cmd = testenv.Command(t, exe) 702 out, err = cmd.CombinedOutput() 703 if err != nil { 704 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) 705 } 706 if string(out) != "hello\n" { 707 t.Errorf("unexpected output (%s):\n%s", mode, out) 708 } 709 } 710} 711 712const testTrampCgoSrc = ` 713package main 714 715// #include <stdio.h> 716// void CHello() { printf("hello\n"); fflush(stdout); } 717import "C" 718 719func main() { 720 C.CHello() 721} 722` 723 724func TestTrampolineCgo(t *testing.T) { 725 // Test that trampoline insertion works for cgo code. 726 // For stress test, we set -debugtramp=2 flag, which sets a very low 727 // threshold for trampoline generation, and essentially all cross-package 728 // calls will use trampolines. 729 buildmodes := []string{"default"} 730 switch runtime.GOARCH { 731 case "arm", "arm64", "ppc64": 732 case "ppc64le": 733 // Trampolines are generated differently when internal linking PIE, test them too. 734 buildmodes = append(buildmodes, "pie") 735 default: 736 t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH) 737 } 738 739 testenv.MustHaveGoBuild(t) 740 testenv.MustHaveCGO(t) 741 742 t.Parallel() 743 744 tmpdir := t.TempDir() 745 746 src := filepath.Join(tmpdir, "hello.go") 747 err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666) 748 if err != nil { 749 t.Fatal(err) 750 } 751 exe := filepath.Join(tmpdir, "hello.exe") 752 753 for _, mode := range buildmodes { 754 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src) 755 out, err := cmd.CombinedOutput() 756 if err != nil { 757 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) 758 } 759 cmd = testenv.Command(t, exe) 760 out, err = cmd.CombinedOutput() 761 if err != nil { 762 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) 763 } 764 if string(out) != "hello\n" && string(out) != "hello\r\n" { 765 t.Errorf("unexpected output (%s):\n%s", mode, out) 766 } 767 768 // Test internal linking mode. 769 770 if !testenv.CanInternalLink(true) { 771 continue 772 } 773 cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src) 774 out, err = cmd.CombinedOutput() 775 if err != nil { 776 t.Fatalf("build (%s) failed: %v\n%s", mode, err, out) 777 } 778 cmd = testenv.Command(t, exe) 779 out, err = cmd.CombinedOutput() 780 if err != nil { 781 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) 782 } 783 if string(out) != "hello\n" && string(out) != "hello\r\n" { 784 t.Errorf("unexpected output (%s):\n%s", mode, out) 785 } 786 } 787} 788 789func TestIndexMismatch(t *testing.T) { 790 // Test that index mismatch will cause a link-time error (not run-time error). 791 // This shouldn't happen with "go build". We invoke the compiler and the linker 792 // manually, and try to "trick" the linker with an inconsistent object file. 793 testenv.MustHaveGoBuild(t) 794 testenv.MustInternalLink(t, false) 795 796 t.Parallel() 797 798 tmpdir := t.TempDir() 799 800 aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go") 801 bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go") 802 mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go") 803 aObj := filepath.Join(tmpdir, "a.o") 804 mObj := filepath.Join(tmpdir, "main.o") 805 exe := filepath.Join(tmpdir, "main.exe") 806 807 importcfgFile := filepath.Join(tmpdir, "runtime.importcfg") 808 testenv.WriteImportcfg(t, importcfgFile, nil, "runtime") 809 importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg") 810 testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime") 811 812 // Build a program with main package importing package a. 813 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc) 814 t.Log(cmd) 815 out, err := cmd.CombinedOutput() 816 if err != nil { 817 t.Fatalf("compiling a.go failed: %v\n%s", err, out) 818 } 819 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc) 820 t.Log(cmd) 821 out, err = cmd.CombinedOutput() 822 if err != nil { 823 t.Fatalf("compiling main.go failed: %v\n%s", err, out) 824 } 825 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj) 826 t.Log(cmd) 827 out, err = cmd.CombinedOutput() 828 if err != nil { 829 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" { 830 testenv.SkipFlaky(t, 58806) 831 } 832 t.Errorf("linking failed: %v\n%s", err, out) 833 } 834 835 // Now, overwrite a.o with the object of b.go. This should 836 // result in an index mismatch. 837 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc) 838 t.Log(cmd) 839 out, err = cmd.CombinedOutput() 840 if err != nil { 841 t.Fatalf("compiling a.go failed: %v\n%s", err, out) 842 } 843 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj) 844 t.Log(cmd) 845 out, err = cmd.CombinedOutput() 846 if err == nil { 847 t.Fatalf("linking didn't fail") 848 } 849 if !bytes.Contains(out, []byte("fingerprint mismatch")) { 850 t.Errorf("did not see expected error message. out:\n%s", out) 851 } 852} 853 854func TestPErsrcBinutils(t *testing.T) { 855 // Test that PE rsrc section is handled correctly (issue 39658). 856 testenv.MustHaveGoBuild(t) 857 858 if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" { 859 // This test is limited to amd64 and 386, because binutils is limited as such 860 t.Skipf("this is only for windows/amd64 and windows/386") 861 } 862 863 t.Parallel() 864 865 tmpdir := t.TempDir() 866 867 pkgdir := filepath.Join("testdata", "pe-binutils") 868 exe := filepath.Join(tmpdir, "a.exe") 869 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe) 870 cmd.Dir = pkgdir 871 // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment 872 out, err := cmd.CombinedOutput() 873 if err != nil { 874 t.Fatalf("building failed: %v, output:\n%s", err, out) 875 } 876 877 // Check that the binary contains the rsrc data 878 b, err := os.ReadFile(exe) 879 if err != nil { 880 t.Fatalf("reading output failed: %v", err) 881 } 882 if !bytes.Contains(b, []byte("Hello Gophers!")) { 883 t.Fatalf("binary does not contain expected content") 884 } 885} 886 887func TestPErsrcLLVM(t *testing.T) { 888 // Test that PE rsrc section is handled correctly (issue 39658). 889 testenv.MustHaveGoBuild(t) 890 891 if runtime.GOOS != "windows" { 892 t.Skipf("this is a windows-only test") 893 } 894 895 t.Parallel() 896 897 tmpdir := t.TempDir() 898 899 pkgdir := filepath.Join("testdata", "pe-llvm") 900 exe := filepath.Join(tmpdir, "a.exe") 901 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe) 902 cmd.Dir = pkgdir 903 // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment 904 out, err := cmd.CombinedOutput() 905 if err != nil { 906 t.Fatalf("building failed: %v, output:\n%s", err, out) 907 } 908 909 // Check that the binary contains the rsrc data 910 b, err := os.ReadFile(exe) 911 if err != nil { 912 t.Fatalf("reading output failed: %v", err) 913 } 914 if !bytes.Contains(b, []byte("resname RCDATA a.rc")) { 915 t.Fatalf("binary does not contain expected content") 916 } 917} 918 919func TestContentAddressableSymbols(t *testing.T) { 920 // Test that the linker handles content-addressable symbols correctly. 921 testenv.MustHaveGoBuild(t) 922 923 t.Parallel() 924 925 src := filepath.Join("testdata", "testHashedSyms", "p.go") 926 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src) 927 out, err := cmd.CombinedOutput() 928 if err != nil { 929 t.Errorf("command %s failed: %v\n%s", cmd, err, out) 930 } 931} 932 933func TestReadOnly(t *testing.T) { 934 // Test that read-only data is indeed read-only. 935 testenv.MustHaveGoBuild(t) 936 937 t.Parallel() 938 939 src := filepath.Join("testdata", "testRO", "x.go") 940 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src) 941 out, err := cmd.CombinedOutput() 942 if err == nil { 943 t.Errorf("running test program did not fail. output:\n%s", out) 944 } 945} 946 947const testIssue38554Src = ` 948package main 949 950type T [10<<20]byte 951 952//go:noinline 953func f() T { 954 return T{} // compiler will make a large stmp symbol, but not used. 955} 956 957func main() { 958 x := f() 959 println(x[1]) 960} 961` 962 963func TestIssue38554(t *testing.T) { 964 testenv.MustHaveGoBuild(t) 965 966 t.Parallel() 967 968 tmpdir := t.TempDir() 969 970 src := filepath.Join(tmpdir, "x.go") 971 err := os.WriteFile(src, []byte(testIssue38554Src), 0666) 972 if err != nil { 973 t.Fatalf("failed to write source file: %v", err) 974 } 975 exe := filepath.Join(tmpdir, "x.exe") 976 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src) 977 out, err := cmd.CombinedOutput() 978 if err != nil { 979 t.Fatalf("build failed: %v\n%s", err, out) 980 } 981 982 fi, err := os.Stat(exe) 983 if err != nil { 984 t.Fatalf("failed to stat output file: %v", err) 985 } 986 987 // The test program is not much different from a helloworld, which is 988 // typically a little over 1 MB. We allow 5 MB. If the bad stmp is live, 989 // it will be over 10 MB. 990 const want = 5 << 20 991 if got := fi.Size(); got > want { 992 t.Errorf("binary too big: got %d, want < %d", got, want) 993 } 994} 995 996const testIssue42396src = ` 997package main 998 999//go:noinline 1000//go:nosplit 1001func callee(x int) { 1002} 1003 1004func main() { 1005 callee(9) 1006} 1007` 1008 1009func TestIssue42396(t *testing.T) { 1010 testenv.MustHaveGoBuild(t) 1011 1012 if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) { 1013 t.Skip("no race detector support") 1014 } 1015 1016 t.Parallel() 1017 1018 tmpdir := t.TempDir() 1019 1020 src := filepath.Join(tmpdir, "main.go") 1021 err := os.WriteFile(src, []byte(testIssue42396src), 0666) 1022 if err != nil { 1023 t.Fatalf("failed to write source file: %v", err) 1024 } 1025 exe := filepath.Join(tmpdir, "main.exe") 1026 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src) 1027 out, err := cmd.CombinedOutput() 1028 if err == nil { 1029 t.Fatalf("build unexpectedly succeeded") 1030 } 1031 1032 // Check to make sure that we see a reasonable error message 1033 // and not a panic. 1034 if strings.Contains(string(out), "panic:") { 1035 t.Fatalf("build should not fail with panic:\n%s", out) 1036 } 1037 const want = "reference to undefined builtin" 1038 if !strings.Contains(string(out), want) { 1039 t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out) 1040 } 1041} 1042 1043const testLargeRelocSrc = ` 1044package main 1045 1046var x = [1<<25]byte{1<<23: 23, 1<<24: 24} 1047 1048var addr = [...]*byte{ 1049 &x[1<<23-1], 1050 &x[1<<23], 1051 &x[1<<23+1], 1052 &x[1<<24-1], 1053 &x[1<<24], 1054 &x[1<<24+1], 1055} 1056 1057func main() { 1058 // check relocations in instructions 1059 check(x[1<<23-1], 0) 1060 check(x[1<<23], 23) 1061 check(x[1<<23+1], 0) 1062 check(x[1<<24-1], 0) 1063 check(x[1<<24], 24) 1064 check(x[1<<24+1], 0) 1065 1066 // check absolute address relocations in data 1067 check(*addr[0], 0) 1068 check(*addr[1], 23) 1069 check(*addr[2], 0) 1070 check(*addr[3], 0) 1071 check(*addr[4], 24) 1072 check(*addr[5], 0) 1073} 1074 1075func check(x, y byte) { 1076 if x != y { 1077 panic("FAIL") 1078 } 1079} 1080` 1081 1082func TestLargeReloc(t *testing.T) { 1083 // Test that large relocation addend is handled correctly. 1084 // In particular, on darwin/arm64 when external linking, 1085 // Mach-O relocation has only 24-bit addend. See issue #42738. 1086 testenv.MustHaveGoBuild(t) 1087 t.Parallel() 1088 1089 tmpdir := t.TempDir() 1090 1091 src := filepath.Join(tmpdir, "x.go") 1092 err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666) 1093 if err != nil { 1094 t.Fatalf("failed to write source file: %v", err) 1095 } 1096 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src) 1097 out, err := cmd.CombinedOutput() 1098 if err != nil { 1099 t.Errorf("build failed: %v. output:\n%s", err, out) 1100 } 1101 1102 if testenv.HasCGO() { // currently all targets that support cgo can external link 1103 cmd = testenv.Command(t, testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src) 1104 out, err = cmd.CombinedOutput() 1105 if err != nil { 1106 t.Fatalf("build failed: %v. output:\n%s", err, out) 1107 } 1108 } 1109} 1110 1111func TestUnlinkableObj(t *testing.T) { 1112 // Test that the linker emits an error with unlinkable object. 1113 testenv.MustHaveGoBuild(t) 1114 t.Parallel() 1115 1116 if true /* was buildcfg.Experiment.Unified */ { 1117 t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified") 1118 } 1119 1120 tmpdir := t.TempDir() 1121 1122 xSrc := filepath.Join(tmpdir, "x.go") 1123 pSrc := filepath.Join(tmpdir, "p.go") 1124 xObj := filepath.Join(tmpdir, "x.o") 1125 pObj := filepath.Join(tmpdir, "p.o") 1126 exe := filepath.Join(tmpdir, "x.exe") 1127 importcfgfile := filepath.Join(tmpdir, "importcfg") 1128 testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj}) 1129 err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666) 1130 if err != nil { 1131 t.Fatalf("failed to write source file: %v", err) 1132 } 1133 err = os.WriteFile(pSrc, []byte("package p\n"), 0666) 1134 if err != nil { 1135 t.Fatalf("failed to write source file: %v", err) 1136 } 1137 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc) // without -p 1138 out, err := cmd.CombinedOutput() 1139 if err != nil { 1140 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out) 1141 } 1142 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc) 1143 out, err = cmd.CombinedOutput() 1144 if err != nil { 1145 t.Fatalf("compile x.go failed: %v. output:\n%s", err, out) 1146 } 1147 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj) 1148 out, err = cmd.CombinedOutput() 1149 if err == nil { 1150 t.Fatalf("link did not fail") 1151 } 1152 if !bytes.Contains(out, []byte("unlinkable object")) { 1153 t.Errorf("did not see expected error message. out:\n%s", out) 1154 } 1155 1156 // It is okay to omit -p for (only) main package. 1157 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc) 1158 out, err = cmd.CombinedOutput() 1159 if err != nil { 1160 t.Fatalf("compile p.go failed: %v. output:\n%s", err, out) 1161 } 1162 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc) // without -p 1163 out, err = cmd.CombinedOutput() 1164 if err != nil { 1165 t.Fatalf("compile failed: %v. output:\n%s", err, out) 1166 } 1167 1168 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj) 1169 out, err = cmd.CombinedOutput() 1170 if err != nil { 1171 t.Errorf("link failed: %v. output:\n%s", err, out) 1172 } 1173} 1174 1175func TestExtLinkCmdlineDeterminism(t *testing.T) { 1176 // Test that we pass flags in deterministic order to the external linker 1177 testenv.MustHaveGoBuild(t) 1178 testenv.MustHaveCGO(t) // this test requires -linkmode=external 1179 t.Parallel() 1180 1181 // test source code, with some cgo exports 1182 testSrc := ` 1183package main 1184import "C" 1185//export F1 1186func F1() {} 1187//export F2 1188func F2() {} 1189//export F3 1190func F3() {} 1191func main() {} 1192` 1193 1194 tmpdir := t.TempDir() 1195 src := filepath.Join(tmpdir, "x.go") 1196 if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil { 1197 t.Fatal(err) 1198 } 1199 exe := filepath.Join(tmpdir, "x.exe") 1200 1201 // Use a deterministic tmp directory so the temporary file paths are 1202 // deterministic. 1203 linktmp := filepath.Join(tmpdir, "linktmp") 1204 if err := os.Mkdir(linktmp, 0777); err != nil { 1205 t.Fatal(err) 1206 } 1207 1208 // Link with -v -linkmode=external to see the flags we pass to the 1209 // external linker. 1210 ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp 1211 var out0 []byte 1212 for i := 0; i < 5; i++ { 1213 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", ldflags, "-o", exe, src) 1214 out, err := cmd.CombinedOutput() 1215 if err != nil { 1216 t.Fatalf("build failed: %v, output:\n%s", err, out) 1217 } 1218 if err := os.Remove(exe); err != nil { 1219 t.Fatal(err) 1220 } 1221 1222 // extract the "host link" invocation 1223 j := bytes.Index(out, []byte("\nhost link:")) 1224 if j == -1 { 1225 t.Fatalf("host link step not found, output:\n%s", out) 1226 } 1227 out = out[j+1:] 1228 k := bytes.Index(out, []byte("\n")) 1229 if k == -1 { 1230 t.Fatalf("no newline after host link, output:\n%s", out) 1231 } 1232 out = out[:k] 1233 1234 // filter out output file name, which is passed by the go 1235 // command and is nondeterministic. 1236 fs := bytes.Fields(out) 1237 for i, f := range fs { 1238 if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) { 1239 fs[i+1] = []byte("a.out") 1240 break 1241 } 1242 } 1243 out = bytes.Join(fs, []byte{' '}) 1244 1245 if i == 0 { 1246 out0 = out 1247 continue 1248 } 1249 if !bytes.Equal(out0, out) { 1250 t.Fatalf("output differ:\n%s\n==========\n%s", out0, out) 1251 } 1252 } 1253} 1254 1255// TestResponseFile tests that creating a response file to pass to the 1256// external linker works correctly. 1257func TestResponseFile(t *testing.T) { 1258 t.Parallel() 1259 1260 testenv.MustHaveGoBuild(t) 1261 1262 // This test requires -linkmode=external. Currently all 1263 // systems that support cgo support -linkmode=external. 1264 testenv.MustHaveCGO(t) 1265 1266 tmpdir := t.TempDir() 1267 1268 src := filepath.Join(tmpdir, "x.go") 1269 if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil { 1270 t.Fatal(err) 1271 } 1272 1273 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go") 1274 cmd.Dir = tmpdir 1275 1276 // Add enough arguments to push cmd/link into creating a response file. 1277 var sb strings.Builder 1278 sb.WriteString(`'-ldflags=all="-extldflags=`) 1279 for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ { 1280 if i > 0 { 1281 sb.WriteString(" ") 1282 } 1283 sb.WriteString("-g") 1284 } 1285 sb.WriteString(`"'`) 1286 cmd = testenv.CleanCmdEnv(cmd) 1287 cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String()) 1288 1289 out, err := cmd.CombinedOutput() 1290 if len(out) > 0 { 1291 t.Logf("%s", out) 1292 } 1293 if err != nil { 1294 t.Error(err) 1295 } 1296} 1297 1298func TestDynimportVar(t *testing.T) { 1299 // Test that we can access dynamically imported variables. 1300 // Currently darwin only. 1301 if runtime.GOOS != "darwin" { 1302 t.Skip("skip on non-darwin platform") 1303 } 1304 1305 testenv.MustHaveGoBuild(t) 1306 testenv.MustHaveCGO(t) 1307 1308 t.Parallel() 1309 1310 tmpdir := t.TempDir() 1311 exe := filepath.Join(tmpdir, "a.exe") 1312 src := filepath.Join("testdata", "dynimportvar", "main.go") 1313 1314 for _, mode := range []string{"internal", "external"} { 1315 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode="+mode, "-o", exe, src) 1316 out, err := cmd.CombinedOutput() 1317 if err != nil { 1318 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out) 1319 } 1320 cmd = testenv.Command(t, exe) 1321 out, err = cmd.CombinedOutput() 1322 if err != nil { 1323 t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out) 1324 } 1325 } 1326} 1327 1328const helloSrc = ` 1329package main 1330var X = 42 1331var Y int 1332func main() { println("hello", X, Y) } 1333` 1334 1335func TestFlagS(t *testing.T) { 1336 // Test that the -s flag strips the symbol table. 1337 testenv.MustHaveGoBuild(t) 1338 1339 t.Parallel() 1340 1341 tmpdir := t.TempDir() 1342 exe := filepath.Join(tmpdir, "a.exe") 1343 src := filepath.Join(tmpdir, "a.go") 1344 err := os.WriteFile(src, []byte(helloSrc), 0666) 1345 if err != nil { 1346 t.Fatal(err) 1347 } 1348 1349 modes := []string{"auto"} 1350 if testenv.HasCGO() { 1351 modes = append(modes, "external") 1352 } 1353 1354 // check a text symbol, a data symbol, and a BSS symbol 1355 syms := []string{"main.main", "main.X", "main.Y"} 1356 1357 for _, mode := range modes { 1358 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src) 1359 out, err := cmd.CombinedOutput() 1360 if err != nil { 1361 t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out) 1362 } 1363 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe) 1364 out, err = cmd.CombinedOutput() 1365 if err != nil && !errors.As(err, new(*exec.ExitError)) { 1366 // Error exit is fine as it may have no symbols. 1367 // On darwin we need to emit dynamic symbol references so it 1368 // actually has some symbols, and nm succeeds. 1369 t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out) 1370 } 1371 for _, s := range syms { 1372 if bytes.Contains(out, []byte(s)) { 1373 t.Errorf("(mode=%s): unexpected symbol %s", mode, s) 1374 } 1375 } 1376 } 1377} 1378 1379func TestRandLayout(t *testing.T) { 1380 // Test that the -randlayout flag randomizes function order and 1381 // generates a working binary. 1382 testenv.MustHaveGoBuild(t) 1383 1384 t.Parallel() 1385 1386 tmpdir := t.TempDir() 1387 1388 src := filepath.Join(tmpdir, "hello.go") 1389 err := os.WriteFile(src, []byte(trivialSrc), 0666) 1390 if err != nil { 1391 t.Fatal(err) 1392 } 1393 1394 var syms [2]string 1395 for i, seed := range []string{"123", "456"} { 1396 exe := filepath.Join(tmpdir, "hello"+seed+".exe") 1397 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-randlayout="+seed, "-o", exe, src) 1398 out, err := cmd.CombinedOutput() 1399 if err != nil { 1400 t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out) 1401 } 1402 cmd = testenv.Command(t, exe) 1403 err = cmd.Run() 1404 if err != nil { 1405 t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out) 1406 } 1407 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe) 1408 out, err = cmd.CombinedOutput() 1409 if err != nil { 1410 t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out) 1411 } 1412 syms[i] = string(out) 1413 } 1414 if syms[0] == syms[1] { 1415 t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1]) 1416 } 1417} 1418 1419func TestCheckLinkname(t *testing.T) { 1420 // Test that code containing blocked linknames does not build. 1421 testenv.MustHaveGoBuild(t) 1422 t.Parallel() 1423 1424 tmpdir := t.TempDir() 1425 1426 tests := []struct { 1427 src string 1428 ok bool 1429 }{ 1430 // use (instantiation) of public API is ok 1431 {"ok.go", true}, 1432 // push linkname is ok 1433 {"push.go", true}, 1434 // pull linkname of blocked symbol is not ok 1435 {"coro.go", false}, 1436 {"coro_var.go", false}, 1437 // assembly reference is not ok 1438 {"coro_asm", false}, 1439 // pull-only linkname is not ok 1440 {"coro2.go", false}, 1441 // legacy bad linkname is ok, for now 1442 {"fastrand.go", true}, 1443 {"badlinkname.go", true}, 1444 } 1445 for _, test := range tests { 1446 test := test 1447 t.Run(test.src, func(t *testing.T) { 1448 t.Parallel() 1449 src := filepath.Join("testdata", "linkname", test.src) 1450 exe := filepath.Join(tmpdir, test.src+".exe") 1451 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src) 1452 out, err := cmd.CombinedOutput() 1453 if test.ok && err != nil { 1454 t.Errorf("build failed unexpectedly: %v:\n%s", err, out) 1455 } 1456 if !test.ok && err == nil { 1457 t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out) 1458 } 1459 }) 1460 } 1461} 1462