1
2
3
4
5 package ld
6
7 import (
8 "bytes"
9 "debug/pe"
10 "fmt"
11 "internal/testenv"
12 "os"
13 "path/filepath"
14 "runtime"
15 "strings"
16 "testing"
17 )
18
19 func TestUndefinedRelocErrors(t *testing.T) {
20 testenv.MustHaveGoBuild(t)
21
22
23
24
25
26
27 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
28
29 t.Parallel()
30
31 out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "./testdata/issue10978").CombinedOutput()
32 if err == nil {
33 t.Fatal("expected build to fail")
34 }
35
36 wantErrors := map[string]int{
37
38 "function main is undeclared in the main package": 1,
39
40
41
42
43 "main.defined1: relocation target main.undefined not defined": 1,
44 "main.defined2: relocation target main.undefined not defined": 1,
45 }
46 unexpectedErrors := map[string]int{}
47
48 for _, l := range strings.Split(string(out), "\n") {
49 if strings.HasPrefix(l, "#") || l == "" {
50 continue
51 }
52 matched := ""
53 for want := range wantErrors {
54 if strings.Contains(l, want) {
55 matched = want
56 break
57 }
58 }
59 if matched != "" {
60 wantErrors[matched]--
61 } else {
62 unexpectedErrors[l]++
63 }
64 }
65
66 for want, n := range wantErrors {
67 switch {
68 case n > 0:
69 t.Errorf("unmatched error: %s (x%d)", want, n)
70 case n < 0:
71 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
72 testenv.SkipFlaky(t, 58807)
73 }
74 t.Errorf("extra errors: %s (x%d)", want, -n)
75 }
76 }
77 for unexpected, n := range unexpectedErrors {
78 t.Errorf("unexpected error: %s (x%d)", unexpected, n)
79 }
80 }
81
82 const carchiveSrcText = `
83 package main
84
85 //export GoFunc
86 func GoFunc() {
87 println(42)
88 }
89
90 func main() {
91 }
92 `
93
94 func TestArchiveBuildInvokeWithExec(t *testing.T) {
95 t.Parallel()
96 testenv.MustHaveGoBuild(t)
97 testenv.MustHaveCGO(t)
98
99
100
101 pair := runtime.GOOS + "-" + runtime.GOARCH
102 switch pair {
103 case "darwin-amd64", "darwin-arm64", "linux-amd64", "freebsd-amd64":
104 default:
105 t.Skip("no need for test on " + pair)
106 }
107 switch runtime.GOOS {
108 case "openbsd", "windows":
109 t.Skip("c-archive unsupported")
110 }
111 dir := t.TempDir()
112
113 srcfile := filepath.Join(dir, "test.go")
114 arfile := filepath.Join(dir, "test.a")
115 if err := os.WriteFile(srcfile, []byte(carchiveSrcText), 0666); err != nil {
116 t.Fatal(err)
117 }
118
119 ldf := fmt.Sprintf("-ldflags=-v -tmpdir=%s", dir)
120 argv := []string{"build", "-buildmode=c-archive", "-o", arfile, ldf, srcfile}
121 out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
122 if err != nil {
123 t.Fatalf("build failure: %s\n%s\n", err, string(out))
124 }
125
126 found := false
127 const want = "invoking archiver with syscall.Exec"
128 for _, l := range strings.Split(string(out), "\n") {
129 if strings.HasPrefix(l, want) {
130 found = true
131 break
132 }
133 }
134
135 if !found {
136 t.Errorf("expected '%s' in -v output, got:\n%s\n", want, string(out))
137 }
138 }
139
140 func TestLargeTextSectionSplitting(t *testing.T) {
141 switch runtime.GOARCH {
142 case "ppc64", "ppc64le", "arm":
143 case "arm64":
144 if runtime.GOOS == "darwin" {
145 break
146 }
147 fallthrough
148 default:
149 t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH)
150 }
151
152 testenv.MustHaveGoBuild(t)
153 testenv.MustHaveCGO(t)
154 t.Parallel()
155 dir := t.TempDir()
156
157
158
159
160
161
162 exe := filepath.Join(dir, "go.exe")
163 out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, "-ldflags=-linkmode=external -debugtextsize=1048576", "cmd/go").CombinedOutput()
164 if err != nil {
165 t.Fatalf("build failure: %s\n%s\n", err, string(out))
166 }
167
168
169 out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
170 if err != nil {
171 t.Fatalf("nm failure: %s\n%s\n", err, string(out))
172 }
173 if !bytes.Contains(out, []byte("runtime.text.1")) {
174 t.Errorf("runtime.text.1 not found, text section not split?")
175 }
176
177
178 _, err = testenv.Command(t, exe, "version").CombinedOutput()
179 if err != nil {
180 t.Fatal(err)
181 }
182 }
183
184 func TestWindowsBuildmodeCSharedASLR(t *testing.T) {
185 platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
186 switch platform {
187 case "windows/amd64", "windows/386":
188 default:
189 t.Skip("skipping windows amd64/386 only test")
190 }
191
192 testenv.MustHaveCGO(t)
193
194 t.Run("aslr", func(t *testing.T) {
195 testWindowsBuildmodeCSharedASLR(t, true)
196 })
197 t.Run("no-aslr", func(t *testing.T) {
198 testWindowsBuildmodeCSharedASLR(t, false)
199 })
200 }
201
202 func TestWindowsBuildmodeCSharedTrailingDotOutput(t *testing.T) {
203 if runtime.GOOS != "windows" {
204 t.Skip("skipping windows only test")
205 }
206
207 t.Parallel()
208 testenv.MustHaveGoBuild(t)
209 testenv.MustHaveCGO(t)
210 testenv.MustHaveBuildMode(t, "c-shared")
211
212 dir := t.TempDir()
213 srcfile := filepath.Join(dir, "test.go")
214 objfile := filepath.Join(dir, "mypackage.")
215 linktmp := filepath.Join(dir, "linktmp")
216 if err := os.Mkdir(linktmp, 0777); err != nil {
217 t.Fatal(err)
218 }
219 if err := os.WriteFile(srcfile, []byte(`package main
220 import "C"
221
222 //export Hello
223 func Hello() {}
224
225 func main() {}
226 `), 0666); err != nil {
227 t.Fatal(err)
228 }
229
230 argv := []string{"build", "-buildmode=c-shared", "-o", objfile, "-ldflags", "-tmpdir=" + linktmp, srcfile}
231 out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
232 if err != nil {
233 t.Fatalf("build failure: %s\n%s\n", err, string(out))
234 }
235
236 def, err := os.ReadFile(filepath.Join(linktmp, "export_file.def"))
237 if err != nil {
238 t.Fatal(err)
239 }
240 if want := []byte("LIBRARY \"mypackage.\"\n"); !bytes.HasPrefix(def, want) {
241 t.Fatalf("export_file.def begins with %q, want %q", def, want)
242 }
243 }
244
245 func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
246 t.Parallel()
247 testenv.MustHaveGoBuild(t)
248
249 dir := t.TempDir()
250
251 srcfile := filepath.Join(dir, "test.go")
252 objfile := filepath.Join(dir, "test.dll")
253 if err := os.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil {
254 t.Fatal(err)
255 }
256 argv := []string{"build", "-buildmode=c-shared"}
257 if !useASLR {
258 argv = append(argv, "-ldflags", "-aslr=false")
259 }
260 argv = append(argv, "-o", objfile, srcfile)
261 out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
262 if err != nil {
263 t.Fatalf("build failure: %s\n%s\n", err, string(out))
264 }
265
266 f, err := pe.Open(objfile)
267 if err != nil {
268 t.Fatal(err)
269 }
270 defer f.Close()
271 var dc uint16
272 switch oh := f.OptionalHeader.(type) {
273 case *pe.OptionalHeader32:
274 dc = oh.DllCharacteristics
275 case *pe.OptionalHeader64:
276 dc = oh.DllCharacteristics
277 hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0
278 if useASLR && !hasHEVA {
279 t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
280 } else if !useASLR && hasHEVA {
281 t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set")
282 }
283 default:
284 t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
285 }
286 hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0
287 if useASLR && !hasASLR {
288 t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
289 } else if !useASLR && hasASLR {
290 t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
291 }
292 }
293
294
295
296
297
298
299 func TestMemProfileCheck(t *testing.T) {
300 testenv.MustHaveGoBuild(t)
301 t.Parallel()
302
303 tests := []struct {
304 name string
305 prog string
306 wantOut string
307 }{
308 {
309 "no_memprofile",
310 `
311 package main
312 import "runtime"
313 func main() {
314 println(runtime.MemProfileRate)
315 }
316 `,
317 "0",
318 },
319 {
320 "with_memprofile",
321 `
322 package main
323 import "runtime"
324 func main() {
325 runtime.MemProfile(nil, false)
326 println(runtime.MemProfileRate)
327 }
328 `,
329 "524288",
330 },
331 {
332 "with_memprofile_indirect",
333 `
334 package main
335 import "runtime"
336 var f = runtime.MemProfile
337 func main() {
338 if f == nil {
339 panic("no f")
340 }
341 println(runtime.MemProfileRate)
342 }
343 `,
344 "524288",
345 },
346 {
347 "with_memprofile_runtime_pprof",
348 `
349 package main
350 import "runtime"
351 import "runtime/pprof"
352 func main() {
353 _ = pprof.Profiles()
354 println(runtime.MemProfileRate)
355 }
356 `,
357 "524288",
358 },
359 {
360 "with_memprofile_runtime_pprof_writeheap",
361 `
362 package main
363 import "io"
364 import "runtime"
365 import "runtime/pprof"
366 func main() {
367 _ = pprof.WriteHeapProfile(io.Discard)
368 println(runtime.MemProfileRate)
369 }
370 `,
371 "524288",
372 },
373 {
374 "with_memprofile_runtime_pprof_lookupheap",
375 `
376 package main
377 import "runtime"
378 import "runtime/pprof"
379 func main() {
380 _ = pprof.Lookup("heap")
381 println(runtime.MemProfileRate)
382 }
383 `,
384 "524288",
385 },
386 {
387 "with_memprofile_http_pprof",
388 `
389 package main
390 import "runtime"
391 import _ "net/http/pprof"
392 func main() {
393 println(runtime.MemProfileRate)
394 }
395 `,
396 "524288",
397 },
398 }
399 for _, tt := range tests {
400 tt := tt
401 t.Run(tt.name, func(t *testing.T) {
402 t.Parallel()
403 tempDir := t.TempDir()
404 src := filepath.Join(tempDir, "x.go")
405 if err := os.WriteFile(src, []byte(tt.prog), 0644); err != nil {
406 t.Fatal(err)
407 }
408 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
409 out, err := cmd.CombinedOutput()
410 if err != nil {
411 t.Fatal(err)
412 }
413 got := strings.TrimSpace(string(out))
414 if got != tt.wantOut {
415 t.Errorf("got %q; want %q", got, tt.wantOut)
416 }
417 })
418 }
419 }
420
421 func TestRISCVTrampolines(t *testing.T) {
422 testenv.MustHaveGoBuild(t)
423 t.Parallel()
424
425 tmpDir := t.TempDir()
426 tmpFile := filepath.Join(tmpDir, "x.s")
427
428
429
430 buf := new(bytes.Buffer)
431 fmt.Fprintf(buf, "TEXT a(SB),$0-0\n")
432 for i := 0; i < 1<<17; i++ {
433 fmt.Fprintf(buf, "\tADD $0, X5, X0\n")
434 }
435 fmt.Fprintf(buf, "\tCALL b(SB)\n")
436 fmt.Fprintf(buf, "\tRET\n")
437 fmt.Fprintf(buf, "TEXT b(SB),$0-0\n")
438 fmt.Fprintf(buf, "\tRET\n")
439 fmt.Fprintf(buf, "TEXT c(SB),$0-0\n")
440 fmt.Fprintf(buf, "\tCALL b(SB)\n")
441 fmt.Fprintf(buf, "\tRET\n")
442 fmt.Fprintf(buf, "TEXT ·d(SB),0,$0-0\n")
443 for i := 0; i < 1<<17; i++ {
444 fmt.Fprintf(buf, "\tADD $0, X5, X0\n")
445 }
446 fmt.Fprintf(buf, "\tCALL a(SB)\n")
447 fmt.Fprintf(buf, "\tCALL c(SB)\n")
448 fmt.Fprintf(buf, "\tRET\n")
449 if err := os.WriteFile(tmpFile, buf.Bytes(), 0644); err != nil {
450 t.Fatalf("Failed to write assembly file: %v", err)
451 }
452
453 if err := os.WriteFile(filepath.Join(tmpDir, "go.mod"), []byte("module riscvtramp"), 0644); err != nil {
454 t.Fatalf("Failed to write file: %v\n", err)
455 }
456 main := `package main
457 func main() {
458 d()
459 }
460
461 func d()
462 `
463 if err := os.WriteFile(filepath.Join(tmpDir, "x.go"), []byte(main), 0644); err != nil {
464 t.Fatalf("failed to write main: %v\n", err)
465 }
466 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal")
467 cmd.Dir = tmpDir
468 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
469 out, err := cmd.CombinedOutput()
470 if err != nil {
471 t.Fatalf("Build failed: %v, output: %s", err, out)
472 }
473
474
475 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", filepath.Join(tmpDir, "riscvtramp"))
476 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
477 out, err = cmd.CombinedOutput()
478 if err != nil {
479 t.Fatalf("nm failure: %s\n%s\n", err, string(out))
480 }
481 if !bytes.Contains(out, []byte(" T a-tramp0")) {
482 t.Errorf("Trampoline a-tramp0 is missing")
483 }
484 if bytes.Contains(out, []byte(" T b-tramp0")) {
485 t.Errorf("Trampoline b-tramp0 exists unnecessarily")
486 }
487 }
488
489 func TestRounding(t *testing.T) {
490 testCases := []struct {
491 input int64
492 quantum int64
493 expected int64
494 }{
495 {0x30000000, 0x2000, 0x30000000},
496 {0x30002000, 0x2000, 0x30002000},
497 {0x30001234, 0x2000, 0x30002000},
498 {0x30001000, 0x2000, 0x30002000},
499 {0x30001fff, 0x2000, 0x30002000},
500 }
501
502 for _, tc := range testCases {
503 result := Rnd(tc.input, tc.quantum)
504 if result != tc.expected {
505 t.Errorf("Rnd(0x%x, 0x%x) = 0x%x, expected 0x%x",
506 tc.input, tc.quantum, result, tc.expected)
507 }
508 }
509 }
510
View as plain text