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 testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
203 t.Parallel()
204 testenv.MustHaveGoBuild(t)
205
206 dir := t.TempDir()
207
208 srcfile := filepath.Join(dir, "test.go")
209 objfile := filepath.Join(dir, "test.dll")
210 if err := os.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil {
211 t.Fatal(err)
212 }
213 argv := []string{"build", "-buildmode=c-shared"}
214 if !useASLR {
215 argv = append(argv, "-ldflags", "-aslr=false")
216 }
217 argv = append(argv, "-o", objfile, srcfile)
218 out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
219 if err != nil {
220 t.Fatalf("build failure: %s\n%s\n", err, string(out))
221 }
222
223 f, err := pe.Open(objfile)
224 if err != nil {
225 t.Fatal(err)
226 }
227 defer f.Close()
228 var dc uint16
229 switch oh := f.OptionalHeader.(type) {
230 case *pe.OptionalHeader32:
231 dc = oh.DllCharacteristics
232 case *pe.OptionalHeader64:
233 dc = oh.DllCharacteristics
234 hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0
235 if useASLR && !hasHEVA {
236 t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
237 } else if !useASLR && hasHEVA {
238 t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set")
239 }
240 default:
241 t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
242 }
243 hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0
244 if useASLR && !hasASLR {
245 t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
246 } else if !useASLR && hasASLR {
247 t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
248 }
249 }
250
251
252
253
254
255
256 func TestMemProfileCheck(t *testing.T) {
257 testenv.MustHaveGoBuild(t)
258 t.Parallel()
259
260 tests := []struct {
261 name string
262 prog string
263 wantOut string
264 }{
265 {
266 "no_memprofile",
267 `
268 package main
269 import "runtime"
270 func main() {
271 println(runtime.MemProfileRate)
272 }
273 `,
274 "0",
275 },
276 {
277 "with_memprofile",
278 `
279 package main
280 import "runtime"
281 func main() {
282 runtime.MemProfile(nil, false)
283 println(runtime.MemProfileRate)
284 }
285 `,
286 "524288",
287 },
288 {
289 "with_memprofile_indirect",
290 `
291 package main
292 import "runtime"
293 var f = runtime.MemProfile
294 func main() {
295 if f == nil {
296 panic("no f")
297 }
298 println(runtime.MemProfileRate)
299 }
300 `,
301 "524288",
302 },
303 {
304 "with_memprofile_runtime_pprof",
305 `
306 package main
307 import "runtime"
308 import "runtime/pprof"
309 func main() {
310 _ = pprof.Profiles()
311 println(runtime.MemProfileRate)
312 }
313 `,
314 "524288",
315 },
316 {
317 "with_memprofile_runtime_pprof_writeheap",
318 `
319 package main
320 import "io"
321 import "runtime"
322 import "runtime/pprof"
323 func main() {
324 _ = pprof.WriteHeapProfile(io.Discard)
325 println(runtime.MemProfileRate)
326 }
327 `,
328 "524288",
329 },
330 {
331 "with_memprofile_runtime_pprof_lookupheap",
332 `
333 package main
334 import "runtime"
335 import "runtime/pprof"
336 func main() {
337 _ = pprof.Lookup("heap")
338 println(runtime.MemProfileRate)
339 }
340 `,
341 "524288",
342 },
343 {
344 "with_memprofile_http_pprof",
345 `
346 package main
347 import "runtime"
348 import _ "net/http/pprof"
349 func main() {
350 println(runtime.MemProfileRate)
351 }
352 `,
353 "524288",
354 },
355 }
356 for _, tt := range tests {
357 tt := tt
358 t.Run(tt.name, func(t *testing.T) {
359 t.Parallel()
360 tempDir := t.TempDir()
361 src := filepath.Join(tempDir, "x.go")
362 if err := os.WriteFile(src, []byte(tt.prog), 0644); err != nil {
363 t.Fatal(err)
364 }
365 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
366 out, err := cmd.CombinedOutput()
367 if err != nil {
368 t.Fatal(err)
369 }
370 got := strings.TrimSpace(string(out))
371 if got != tt.wantOut {
372 t.Errorf("got %q; want %q", got, tt.wantOut)
373 }
374 })
375 }
376 }
377
378 func TestRISCVTrampolines(t *testing.T) {
379 testenv.MustHaveGoBuild(t)
380 t.Parallel()
381
382 tmpDir := t.TempDir()
383 tmpFile := filepath.Join(tmpDir, "x.s")
384
385
386
387 buf := new(bytes.Buffer)
388 fmt.Fprintf(buf, "TEXT a(SB),$0-0\n")
389 for i := 0; i < 1<<17; i++ {
390 fmt.Fprintf(buf, "\tADD $0, X0, X0\n")
391 }
392 fmt.Fprintf(buf, "\tCALL b(SB)\n")
393 fmt.Fprintf(buf, "\tRET\n")
394 fmt.Fprintf(buf, "TEXT b(SB),$0-0\n")
395 fmt.Fprintf(buf, "\tRET\n")
396 fmt.Fprintf(buf, "TEXT c(SB),$0-0\n")
397 fmt.Fprintf(buf, "\tCALL b(SB)\n")
398 fmt.Fprintf(buf, "\tRET\n")
399 fmt.Fprintf(buf, "TEXT ·d(SB),0,$0-0\n")
400 for i := 0; i < 1<<17; i++ {
401 fmt.Fprintf(buf, "\tADD $0, X0, X0\n")
402 }
403 fmt.Fprintf(buf, "\tCALL a(SB)\n")
404 fmt.Fprintf(buf, "\tCALL c(SB)\n")
405 fmt.Fprintf(buf, "\tRET\n")
406 if err := os.WriteFile(tmpFile, buf.Bytes(), 0644); err != nil {
407 t.Fatalf("Failed to write assembly file: %v", err)
408 }
409
410 if err := os.WriteFile(filepath.Join(tmpDir, "go.mod"), []byte("module riscvtramp"), 0644); err != nil {
411 t.Fatalf("Failed to write file: %v\n", err)
412 }
413 main := `package main
414 func main() {
415 d()
416 }
417
418 func d()
419 `
420 if err := os.WriteFile(filepath.Join(tmpDir, "x.go"), []byte(main), 0644); err != nil {
421 t.Fatalf("failed to write main: %v\n", err)
422 }
423 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal")
424 cmd.Dir = tmpDir
425 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
426 out, err := cmd.CombinedOutput()
427 if err != nil {
428 t.Fatalf("Build failed: %v, output: %s", err, out)
429 }
430
431
432 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", filepath.Join(tmpDir, "riscvtramp"))
433 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
434 out, err = cmd.CombinedOutput()
435 if err != nil {
436 t.Fatalf("nm failure: %s\n%s\n", err, string(out))
437 }
438 if !bytes.Contains(out, []byte(" T a-tramp0")) {
439 t.Errorf("Trampoline a-tramp0 is missing")
440 }
441 if bytes.Contains(out, []byte(" T b-tramp0")) {
442 t.Errorf("Trampoline b-tramp0 exists unnecessarily")
443 }
444 }
445
View as plain text