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