Source file
src/cmd/link/elf_test.go
1
2
3
4
5
6
7
8 package main
9
10 import (
11 "cmd/internal/sys"
12 "debug/elf"
13 "fmt"
14 "internal/testenv"
15 "io/ioutil"
16 "os"
17 "os/exec"
18 "path/filepath"
19 "runtime"
20 "strings"
21 "sync"
22 "testing"
23 "text/template"
24 )
25
26 func getCCAndCCFLAGS(t *testing.T, env []string) (string, []string) {
27 goTool := testenv.GoToolPath(t)
28 cmd := exec.Command(goTool, "env", "CC")
29 cmd.Env = env
30 ccb, err := cmd.Output()
31 if err != nil {
32 t.Fatal(err)
33 }
34 cc := strings.TrimSpace(string(ccb))
35
36 cmd = exec.Command(goTool, "env", "GOGCCFLAGS")
37 cmd.Env = env
38 cflagsb, err := cmd.Output()
39 if err != nil {
40 t.Fatal(err)
41 }
42 cflags := strings.Fields(string(cflagsb))
43
44 return cc, cflags
45 }
46
47 var asmSource = `
48 .section .text1,"ax"
49 s1:
50 .byte 0
51 .section .text2,"ax"
52 s2:
53 .byte 0
54 `
55
56 var goSource = `
57 package main
58 func main() {}
59 `
60
61
62
63 func TestSectionsWithSameName(t *testing.T) {
64 testenv.MustHaveGoBuild(t)
65 testenv.MustHaveCGO(t)
66 t.Parallel()
67
68 objcopy, err := exec.LookPath("objcopy")
69 if err != nil {
70 t.Skipf("can't find objcopy: %v", err)
71 }
72
73 dir := t.TempDir()
74
75 gopath := filepath.Join(dir, "GOPATH")
76 env := append(os.Environ(), "GOPATH="+gopath)
77
78 if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
79 t.Fatal(err)
80 }
81
82 asmFile := filepath.Join(dir, "x.s")
83 if err := ioutil.WriteFile(asmFile, []byte(asmSource), 0444); err != nil {
84 t.Fatal(err)
85 }
86
87 goTool := testenv.GoToolPath(t)
88 cc, cflags := getCCAndCCFLAGS(t, env)
89
90 asmObj := filepath.Join(dir, "x.o")
91 t.Logf("%s %v -c -o %s %s", cc, cflags, asmObj, asmFile)
92 if out, err := exec.Command(cc, append(cflags, "-c", "-o", asmObj, asmFile)...).CombinedOutput(); err != nil {
93 t.Logf("%s", out)
94 t.Fatal(err)
95 }
96
97 asm2Obj := filepath.Join(dir, "x2.syso")
98 t.Logf("%s --rename-section .text2=.text1 %s %s", objcopy, asmObj, asm2Obj)
99 if out, err := exec.Command(objcopy, "--rename-section", ".text2=.text1", asmObj, asm2Obj).CombinedOutput(); err != nil {
100 t.Logf("%s", out)
101 t.Fatal(err)
102 }
103
104 for _, s := range []string{asmFile, asmObj} {
105 if err := os.Remove(s); err != nil {
106 t.Fatal(err)
107 }
108 }
109
110 goFile := filepath.Join(dir, "main.go")
111 if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
112 t.Fatal(err)
113 }
114
115 cmd := exec.Command(goTool, "build")
116 cmd.Dir = dir
117 cmd.Env = env
118 t.Logf("%s build", goTool)
119 if out, err := cmd.CombinedOutput(); err != nil {
120 t.Logf("%s", out)
121 t.Fatal(err)
122 }
123 }
124
125 var cSources35779 = []string{`
126 static int blah() { return 42; }
127 int Cfunc1() { return blah(); }
128 `, `
129 static int blah() { return 42; }
130 int Cfunc2() { return blah(); }
131 `,
132 }
133
134
135
136
137
138 func TestMinusRSymsWithSameName(t *testing.T) {
139 testenv.MustHaveGoBuild(t)
140 testenv.MustHaveCGO(t)
141 t.Parallel()
142
143 dir := t.TempDir()
144
145 gopath := filepath.Join(dir, "GOPATH")
146 env := append(os.Environ(), "GOPATH="+gopath)
147
148 if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
149 t.Fatal(err)
150 }
151
152 goTool := testenv.GoToolPath(t)
153 cc, cflags := getCCAndCCFLAGS(t, env)
154
155 objs := []string{}
156 csrcs := []string{}
157 for i, content := range cSources35779 {
158 csrcFile := filepath.Join(dir, fmt.Sprintf("x%d.c", i))
159 csrcs = append(csrcs, csrcFile)
160 if err := ioutil.WriteFile(csrcFile, []byte(content), 0444); err != nil {
161 t.Fatal(err)
162 }
163
164 obj := filepath.Join(dir, fmt.Sprintf("x%d.o", i))
165 objs = append(objs, obj)
166 t.Logf("%s %v -c -o %s %s", cc, cflags, obj, csrcFile)
167 if out, err := exec.Command(cc, append(cflags, "-c", "-o", obj, csrcFile)...).CombinedOutput(); err != nil {
168 t.Logf("%s", out)
169 t.Fatal(err)
170 }
171 }
172
173 sysoObj := filepath.Join(dir, "ldr.syso")
174 t.Logf("%s %v -nostdlib -r -o %s %v", cc, cflags, sysoObj, objs)
175 if out, err := exec.Command(cc, append(cflags, "-nostdlib", "-r", "-o", sysoObj, objs[0], objs[1])...).CombinedOutput(); err != nil {
176 t.Logf("%s", out)
177 t.Fatal(err)
178 }
179
180 cruft := [][]string{objs, csrcs}
181 for _, sl := range cruft {
182 for _, s := range sl {
183 if err := os.Remove(s); err != nil {
184 t.Fatal(err)
185 }
186 }
187 }
188
189 goFile := filepath.Join(dir, "main.go")
190 if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
191 t.Fatal(err)
192 }
193
194 t.Logf("%s build", goTool)
195 cmd := exec.Command(goTool, "build")
196 cmd.Dir = dir
197 cmd.Env = env
198 if out, err := cmd.CombinedOutput(); err != nil {
199 t.Logf("%s", out)
200 t.Fatal(err)
201 }
202 }
203
204 const pieSourceTemplate = `
205 package main
206
207 import "fmt"
208
209 // Force the creation of a lot of type descriptors that will go into
210 // the .data.rel.ro section.
211 {{range $index, $element := .}}var V{{$index}} interface{} = [{{$index}}]int{}
212 {{end}}
213
214 func main() {
215 {{range $index, $element := .}} fmt.Println(V{{$index}})
216 {{end}}
217 }
218 `
219
220 func TestPIESize(t *testing.T) {
221 testenv.MustHaveGoBuild(t)
222
223
224
225
226 testenv.MustHaveCGO(t)
227
228 if !sys.BuildModeSupported(runtime.Compiler, "pie", runtime.GOOS, runtime.GOARCH) {
229 t.Skip("-buildmode=pie not supported")
230 }
231
232 t.Parallel()
233
234 tmpl := template.Must(template.New("pie").Parse(pieSourceTemplate))
235
236 writeGo := func(t *testing.T, dir string) {
237 f, err := os.Create(filepath.Join(dir, "pie.go"))
238 if err != nil {
239 t.Fatal(err)
240 }
241
242
243
244
245 if err := tmpl.Execute(f, make([]byte, 100)); err != nil {
246 t.Fatal(err)
247 }
248
249 if err := f.Close(); err != nil {
250 t.Fatal(err)
251 }
252 }
253
254 for _, external := range []bool{false, true} {
255 external := external
256
257 name := "TestPieSize-"
258 if external {
259 name += "external"
260 } else {
261 name += "internal"
262 }
263 t.Run(name, func(t *testing.T) {
264 t.Parallel()
265
266 dir := t.TempDir()
267
268 writeGo(t, dir)
269
270 binexe := filepath.Join(dir, "exe")
271 binpie := filepath.Join(dir, "pie")
272 if external {
273 binexe += "external"
274 binpie += "external"
275 }
276
277 build := func(bin, mode string) error {
278 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", bin, "-buildmode="+mode)
279 if external {
280 cmd.Args = append(cmd.Args, "-ldflags=-linkmode=external")
281 }
282 cmd.Args = append(cmd.Args, "pie.go")
283 cmd.Dir = dir
284 t.Logf("%v", cmd.Args)
285 out, err := cmd.CombinedOutput()
286 if len(out) > 0 {
287 t.Logf("%s", out)
288 }
289 if err != nil {
290 t.Error(err)
291 }
292 return err
293 }
294
295 var errexe, errpie error
296 var wg sync.WaitGroup
297 wg.Add(2)
298 go func() {
299 defer wg.Done()
300 errexe = build(binexe, "exe")
301 }()
302 go func() {
303 defer wg.Done()
304 errpie = build(binpie, "pie")
305 }()
306 wg.Wait()
307 if errexe != nil || errpie != nil {
308 t.Fatal("link failed")
309 }
310
311 var sizeexe, sizepie uint64
312 if fi, err := os.Stat(binexe); err != nil {
313 t.Fatal(err)
314 } else {
315 sizeexe = uint64(fi.Size())
316 }
317 if fi, err := os.Stat(binpie); err != nil {
318 t.Fatal(err)
319 } else {
320 sizepie = uint64(fi.Size())
321 }
322
323 elfexe, err := elf.Open(binexe)
324 if err != nil {
325 t.Fatal(err)
326 }
327 defer elfexe.Close()
328
329 elfpie, err := elf.Open(binpie)
330 if err != nil {
331 t.Fatal(err)
332 }
333 defer elfpie.Close()
334
335
336
337
338
339
340
341
342
343
344
345
346 textsize := func(ef *elf.File, name string) uint64 {
347 for _, s := range ef.Sections {
348 if s.Name == ".text" {
349 return s.Size
350 }
351 }
352 t.Fatalf("%s: no .text section", name)
353 return 0
354 }
355 textexe := textsize(elfexe, binexe)
356 textpie := textsize(elfpie, binpie)
357
358 dynsize := func(ef *elf.File) uint64 {
359 var ret uint64
360 for _, s := range ef.Sections {
361 if s.Flags&elf.SHF_ALLOC == 0 {
362 continue
363 }
364 switch s.Type {
365 case elf.SHT_DYNSYM, elf.SHT_STRTAB, elf.SHT_REL, elf.SHT_RELA, elf.SHT_HASH, elf.SHT_GNU_HASH, elf.SHT_GNU_VERDEF, elf.SHT_GNU_VERNEED, elf.SHT_GNU_VERSYM:
366 ret += s.Size
367 }
368 if s.Flags&elf.SHF_WRITE != 0 && (strings.Contains(s.Name, ".got") || strings.Contains(s.Name, ".plt")) {
369 ret += s.Size
370 }
371 }
372 return ret
373 }
374
375 dynexe := dynsize(elfexe)
376 dynpie := dynsize(elfpie)
377
378 extrasize := func(ef *elf.File) uint64 {
379 var ret uint64
380
381 for _, s := range ef.Sections {
382 if s.Flags&elf.SHF_ALLOC == 0 {
383 ret += s.Size
384 }
385 }
386
387 var prev *elf.Prog
388 for _, seg := range ef.Progs {
389 if seg.Type != elf.PT_LOAD {
390 continue
391 }
392 if prev != nil {
393 ret += seg.Off - prev.Off - prev.Filesz
394 }
395 prev = seg
396 }
397 return ret
398 }
399
400 extraexe := extrasize(elfexe)
401 extrapie := extrasize(elfpie)
402
403 diffReal := (sizepie - extrapie) - (sizeexe - extraexe)
404 diffExpected := (textpie + dynpie) - (textexe + dynexe)
405
406 t.Logf("real size difference %#x, expected %#x", diffReal, diffExpected)
407
408 if diffReal > (diffExpected + diffExpected/10) {
409 t.Errorf("PIE unexpectedly large: got difference of %d (%d - %d), expected difference %d", diffReal, sizepie, sizeexe, diffExpected)
410 }
411 })
412 }
413 }
414
View as plain text