1
2
3
4
5 package ld
6
7 import (
8 "bytes"
9 "debug/pe"
10 "fmt"
11 "internal/testenv"
12 "io/ioutil"
13 "os/exec"
14 "path/filepath"
15 "runtime"
16 "strings"
17 "testing"
18 )
19
20 func TestUndefinedRelocErrors(t *testing.T) {
21 testenv.MustHaveGoBuild(t)
22
23
24
25 testenv.MustInternalLink(t)
26
27 t.Parallel()
28
29 out, err := exec.Command(testenv.GoToolPath(t), "build", "./testdata/issue10978").CombinedOutput()
30 if err == nil {
31 t.Fatal("expected build to fail")
32 }
33
34 wantErrors := map[string]int{
35
36 "function main is undeclared in the main package": 1,
37
38
39
40
41 "main.defined1: relocation target main.undefined not defined": 1,
42 "main.defined2: relocation target main.undefined not defined": 1,
43 }
44 unexpectedErrors := map[string]int{}
45
46 for _, l := range strings.Split(string(out), "\n") {
47 if strings.HasPrefix(l, "#") || l == "" {
48 continue
49 }
50 matched := ""
51 for want := range wantErrors {
52 if strings.Contains(l, want) {
53 matched = want
54 break
55 }
56 }
57 if matched != "" {
58 wantErrors[matched]--
59 } else {
60 unexpectedErrors[l]++
61 }
62 }
63
64 for want, n := range wantErrors {
65 switch {
66 case n > 0:
67 t.Errorf("unmatched error: %s (x%d)", want, n)
68 case n < 0:
69 t.Errorf("extra errors: %s (x%d)", want, -n)
70 }
71 }
72 for unexpected, n := range unexpectedErrors {
73 t.Errorf("unexpected error: %s (x%d)", unexpected, n)
74 }
75 }
76
77 const carchiveSrcText = `
78 package main
79
80 //export GoFunc
81 func GoFunc() {
82 println(42)
83 }
84
85 func main() {
86 }
87 `
88
89 func TestArchiveBuildInvokeWithExec(t *testing.T) {
90 t.Parallel()
91 testenv.MustHaveGoBuild(t)
92 testenv.MustHaveCGO(t)
93
94
95
96 pair := runtime.GOOS + "-" + runtime.GOARCH
97 switch pair {
98 case "darwin-amd64", "darwin-arm64", "linux-amd64", "freebsd-amd64":
99 default:
100 t.Skip("no need for test on " + pair)
101 }
102 switch runtime.GOOS {
103 case "openbsd", "windows":
104 t.Skip("c-archive unsupported")
105 }
106 dir := t.TempDir()
107
108 srcfile := filepath.Join(dir, "test.go")
109 arfile := filepath.Join(dir, "test.a")
110 if err := ioutil.WriteFile(srcfile, []byte(carchiveSrcText), 0666); err != nil {
111 t.Fatal(err)
112 }
113
114 ldf := fmt.Sprintf("-ldflags=-v -tmpdir=%s", dir)
115 argv := []string{"build", "-buildmode=c-archive", "-o", arfile, ldf, srcfile}
116 out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
117 if err != nil {
118 t.Fatalf("build failure: %s\n%s\n", err, string(out))
119 }
120
121 found := false
122 const want = "invoking archiver with syscall.Exec"
123 for _, l := range strings.Split(string(out), "\n") {
124 if strings.HasPrefix(l, want) {
125 found = true
126 break
127 }
128 }
129
130 if !found {
131 t.Errorf("expected '%s' in -v output, got:\n%s\n", want, string(out))
132 }
133 }
134
135 func TestLargeTextSectionSplitting(t *testing.T) {
136 switch runtime.GOARCH {
137 case "ppc64", "ppc64le":
138 case "arm64":
139 if runtime.GOOS == "darwin" {
140 break
141 }
142 fallthrough
143 default:
144 t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH)
145 }
146
147 testenv.MustHaveGoBuild(t)
148 testenv.MustHaveCGO(t)
149 t.Parallel()
150 dir := t.TempDir()
151
152
153
154
155
156
157 exe := filepath.Join(dir, "go.exe")
158 out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "-ldflags=-linkmode=external -debugtextsize=1048576", "cmd/go").CombinedOutput()
159 if err != nil {
160 t.Fatalf("build failure: %s\n%s\n", err, string(out))
161 }
162
163
164 out, err = exec.Command(testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
165 if err != nil {
166 t.Fatalf("nm failure: %s\n%s\n", err, string(out))
167 }
168 if !bytes.Contains(out, []byte("runtime.text.1")) {
169 t.Errorf("runtime.text.1 not found, text section not split?")
170 }
171
172
173 _, err = exec.Command(exe, "version").CombinedOutput()
174 if err != nil {
175 t.Fatal(err)
176 }
177 }
178
179 func TestWindowsBuildmodeCSharedASLR(t *testing.T) {
180 platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
181 switch platform {
182 case "windows/amd64", "windows/386":
183 default:
184 t.Skip("skipping windows amd64/386 only test")
185 }
186
187 testenv.MustHaveCGO(t)
188
189 t.Run("aslr", func(t *testing.T) {
190 testWindowsBuildmodeCSharedASLR(t, true)
191 })
192 t.Run("no-aslr", func(t *testing.T) {
193 testWindowsBuildmodeCSharedASLR(t, false)
194 })
195 }
196
197 func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
198 t.Parallel()
199 testenv.MustHaveGoBuild(t)
200
201 dir := t.TempDir()
202
203 srcfile := filepath.Join(dir, "test.go")
204 objfile := filepath.Join(dir, "test.dll")
205 if err := ioutil.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil {
206 t.Fatal(err)
207 }
208 argv := []string{"build", "-buildmode=c-shared"}
209 if !useASLR {
210 argv = append(argv, "-ldflags", "-aslr=false")
211 }
212 argv = append(argv, "-o", objfile, srcfile)
213 out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
214 if err != nil {
215 t.Fatalf("build failure: %s\n%s\n", err, string(out))
216 }
217
218 f, err := pe.Open(objfile)
219 if err != nil {
220 t.Fatal(err)
221 }
222 defer f.Close()
223 var dc uint16
224 switch oh := f.OptionalHeader.(type) {
225 case *pe.OptionalHeader32:
226 dc = oh.DllCharacteristics
227 case *pe.OptionalHeader64:
228 dc = oh.DllCharacteristics
229 hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0
230 if useASLR && !hasHEVA {
231 t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
232 } else if !useASLR && hasHEVA {
233 t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set")
234 }
235 default:
236 t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
237 }
238 hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0
239 if useASLR && !hasASLR {
240 t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
241 } else if !useASLR && hasASLR {
242 t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
243 }
244 }
245
246
247
248
249
250
251 func TestMemProfileCheck(t *testing.T) {
252 testenv.MustHaveGoBuild(t)
253 t.Parallel()
254
255 tests := []struct {
256 name string
257 prog string
258 wantOut string
259 }{
260 {
261 "no_memprofile",
262 `
263 package main
264 import "runtime"
265 func main() {
266 println(runtime.MemProfileRate)
267 }
268 `,
269 "0",
270 },
271 {
272 "with_memprofile",
273 `
274 package main
275 import "runtime"
276 func main() {
277 runtime.MemProfile(nil, false)
278 println(runtime.MemProfileRate)
279 }
280 `,
281 "524288",
282 },
283 {
284 "with_memprofile_indirect",
285 `
286 package main
287 import "runtime"
288 var f = runtime.MemProfile
289 func main() {
290 if f == nil {
291 panic("no f")
292 }
293 println(runtime.MemProfileRate)
294 }
295 `,
296 "524288",
297 },
298 {
299 "with_memprofile_runtime_pprof",
300 `
301 package main
302 import "runtime"
303 import "runtime/pprof"
304 func main() {
305 _ = pprof.Profiles()
306 println(runtime.MemProfileRate)
307 }
308 `,
309 "524288",
310 },
311 {
312 "with_memprofile_http_pprof",
313 `
314 package main
315 import "runtime"
316 import _ "net/http/pprof"
317 func main() {
318 println(runtime.MemProfileRate)
319 }
320 `,
321 "524288",
322 },
323 }
324 for _, tt := range tests {
325 tt := tt
326 t.Run(tt.name, func(t *testing.T) {
327 t.Parallel()
328 tempDir := t.TempDir()
329 src := filepath.Join(tempDir, "x.go")
330 if err := ioutil.WriteFile(src, []byte(tt.prog), 0644); err != nil {
331 t.Fatal(err)
332 }
333 cmd := exec.Command(testenv.GoToolPath(t), "run", src)
334 out, err := cmd.CombinedOutput()
335 if err != nil {
336 t.Fatal(err)
337 }
338 got := strings.TrimSpace(string(out))
339 if got != tt.wantOut {
340 t.Errorf("got %q; want %q", got, tt.wantOut)
341 }
342 })
343 }
344 }
345
View as plain text