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_http_pprof",
315 `
316 package main
317 import "runtime"
318 import _ "net/http/pprof"
319 func main() {
320 println(runtime.MemProfileRate)
321 }
322 `,
323 "524288",
324 },
325 }
326 for _, tt := range tests {
327 tt := tt
328 t.Run(tt.name, func(t *testing.T) {
329 t.Parallel()
330 tempDir := t.TempDir()
331 src := filepath.Join(tempDir, "x.go")
332 if err := os.WriteFile(src, []byte(tt.prog), 0644); err != nil {
333 t.Fatal(err)
334 }
335 cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
336 out, err := cmd.CombinedOutput()
337 if err != nil {
338 t.Fatal(err)
339 }
340 got := strings.TrimSpace(string(out))
341 if got != tt.wantOut {
342 t.Errorf("got %q; want %q", got, tt.wantOut)
343 }
344 })
345 }
346 }
347
348 func TestRISCVTrampolines(t *testing.T) {
349 testenv.MustHaveGoBuild(t)
350 t.Parallel()
351
352 tmpDir := t.TempDir()
353 tmpFile := filepath.Join(tmpDir, "x.s")
354
355
356
357 buf := new(bytes.Buffer)
358 fmt.Fprintf(buf, "TEXT a(SB),$0-0\n")
359 for i := 0; i < 1<<17; i++ {
360 fmt.Fprintf(buf, "\tADD $0, X0, X0\n")
361 }
362 fmt.Fprintf(buf, "\tCALL b(SB)\n")
363 fmt.Fprintf(buf, "\tRET\n")
364 fmt.Fprintf(buf, "TEXT b(SB),$0-0\n")
365 fmt.Fprintf(buf, "\tRET\n")
366 fmt.Fprintf(buf, "TEXT c(SB),$0-0\n")
367 fmt.Fprintf(buf, "\tCALL b(SB)\n")
368 fmt.Fprintf(buf, "\tRET\n")
369 fmt.Fprintf(buf, "TEXT ·d(SB),0,$0-0\n")
370 for i := 0; i < 1<<17; i++ {
371 fmt.Fprintf(buf, "\tADD $0, X0, X0\n")
372 }
373 fmt.Fprintf(buf, "\tCALL a(SB)\n")
374 fmt.Fprintf(buf, "\tCALL c(SB)\n")
375 fmt.Fprintf(buf, "\tRET\n")
376 if err := os.WriteFile(tmpFile, buf.Bytes(), 0644); err != nil {
377 t.Fatalf("Failed to write assembly file: %v", err)
378 }
379
380 if err := os.WriteFile(filepath.Join(tmpDir, "go.mod"), []byte("module riscvtramp"), 0644); err != nil {
381 t.Fatalf("Failed to write file: %v\n", err)
382 }
383 main := `package main
384 func main() {
385 d()
386 }
387
388 func d()
389 `
390 if err := os.WriteFile(filepath.Join(tmpDir, "x.go"), []byte(main), 0644); err != nil {
391 t.Fatalf("failed to write main: %v\n", err)
392 }
393 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal")
394 cmd.Dir = tmpDir
395 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
396 out, err := cmd.CombinedOutput()
397 if err != nil {
398 t.Fatalf("Build failed: %v, output: %s", err, out)
399 }
400
401
402 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", filepath.Join(tmpDir, "riscvtramp"))
403 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
404 out, err = cmd.CombinedOutput()
405 if err != nil {
406 t.Fatalf("nm failure: %s\n%s\n", err, string(out))
407 }
408 if !bytes.Contains(out, []byte(" T a-tramp0")) {
409 t.Errorf("Trampoline a-tramp0 is missing")
410 }
411 if bytes.Contains(out, []byte(" T b-tramp0")) {
412 t.Errorf("Trampoline b-tramp0 exists unnecessarily")
413 }
414 }
415
View as plain text