Source file
src/cmd/link/elf_test.go
1
2
3
4
5
6
7 package main
8
9 import (
10 "cmd/internal/buildid"
11 "cmd/internal/hash"
12 "cmd/link/internal/ld"
13 "debug/elf"
14 "encoding/binary"
15 "fmt"
16 "internal/platform"
17 "internal/testenv"
18 "os"
19 "os/exec"
20 "path/filepath"
21 "runtime"
22 "strings"
23 "sync"
24 "testing"
25 "text/template"
26 "unsafe"
27 )
28
29 func getCCAndCCFLAGS(t *testing.T, env []string) (string, []string) {
30 goTool := testenv.GoToolPath(t)
31 cmd := testenv.Command(t, goTool, "env", "CC")
32 cmd.Env = env
33 ccb, err := cmd.Output()
34 if err != nil {
35 t.Fatal(err)
36 }
37 cc := strings.TrimSpace(string(ccb))
38
39 cmd = testenv.Command(t, goTool, "env", "GOGCCFLAGS")
40 cmd.Env = env
41 cflagsb, err := cmd.Output()
42 if err != nil {
43 t.Fatal(err)
44 }
45 cflags := strings.Fields(string(cflagsb))
46
47 return cc, cflags
48 }
49
50 var asmSource = `
51 .section .text1,"ax"
52 s1:
53 .byte 0
54 .section .text2,"ax"
55 s2:
56 .byte 0
57 `
58
59 var goSource = `
60 package main
61 func main() {}
62 `
63
64 var goSourceWithData = `
65 package main
66 var globalVar = 42
67 func main() { println(&globalVar) }
68 `
69
70
71
72 func TestSectionsWithSameName(t *testing.T) {
73 testenv.MustHaveGoBuild(t)
74 testenv.MustHaveCGO(t)
75 t.Parallel()
76
77 objcopy, err := exec.LookPath("objcopy")
78 if err != nil {
79 t.Skipf("can't find objcopy: %v", err)
80 }
81
82 dir := t.TempDir()
83
84 gopath := filepath.Join(dir, "GOPATH")
85 gopathEnv := "GOPATH=" + gopath
86 env := append(os.Environ(), gopathEnv)
87
88 if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
89 t.Fatal(err)
90 }
91
92 asmFile := filepath.Join(dir, "x.s")
93 if err := os.WriteFile(asmFile, []byte(asmSource), 0444); err != nil {
94 t.Fatal(err)
95 }
96
97 cc, cflags := getCCAndCCFLAGS(t, env)
98
99 asmObj := filepath.Join(dir, "x.o")
100 t.Logf("%s %v -c -o %s %s", cc, cflags, asmObj, asmFile)
101 if out, err := testenv.Command(t, cc, append(cflags, "-c", "-o", asmObj, asmFile)...).CombinedOutput(); err != nil {
102 t.Logf("%s", out)
103 t.Fatal(err)
104 }
105
106 asm2Obj := filepath.Join(dir, "x2.syso")
107 t.Logf("%s --rename-section .text2=.text1 %s %s", objcopy, asmObj, asm2Obj)
108 if out, err := testenv.Command(t, objcopy, "--rename-section", ".text2=.text1", asmObj, asm2Obj).CombinedOutput(); err != nil {
109 t.Logf("%s", out)
110 t.Fatal(err)
111 }
112
113 for _, s := range []string{asmFile, asmObj} {
114 if err := os.Remove(s); err != nil {
115 t.Fatal(err)
116 }
117 }
118
119 goFile := filepath.Join(dir, "main.go")
120 if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil {
121 t.Fatal(err)
122 }
123
124 cmd := goCmd(t, "build")
125 cmd.Dir = dir
126 cmd.Env = append(cmd.Env, gopathEnv)
127 t.Logf("%s build", testenv.GoToolPath(t))
128 if out, err := cmd.CombinedOutput(); err != nil {
129 t.Logf("%s", out)
130 t.Fatal(err)
131 }
132 }
133
134 var cSources35779 = []string{`
135 static int blah() { return 42; }
136 int Cfunc1() { return blah(); }
137 `, `
138 static int blah() { return 42; }
139 int Cfunc2() { return blah(); }
140 `,
141 }
142
143
144
145
146
147 func TestMinusRSymsWithSameName(t *testing.T) {
148 testenv.MustHaveGoBuild(t)
149 testenv.MustHaveCGO(t)
150 t.Parallel()
151
152 dir := t.TempDir()
153
154 gopath := filepath.Join(dir, "GOPATH")
155 gopathEnv := "GOPATH=" + gopath
156 env := append(os.Environ(), gopathEnv)
157
158 if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module elf_test\n"), 0666); err != nil {
159 t.Fatal(err)
160 }
161
162 cc, cflags := getCCAndCCFLAGS(t, env)
163
164 objs := []string{}
165 csrcs := []string{}
166 for i, content := range cSources35779 {
167 csrcFile := filepath.Join(dir, fmt.Sprintf("x%d.c", i))
168 csrcs = append(csrcs, csrcFile)
169 if err := os.WriteFile(csrcFile, []byte(content), 0444); err != nil {
170 t.Fatal(err)
171 }
172
173 obj := filepath.Join(dir, fmt.Sprintf("x%d.o", i))
174 objs = append(objs, obj)
175 t.Logf("%s %v -c -o %s %s", cc, cflags, obj, csrcFile)
176 if out, err := testenv.Command(t, cc, append(cflags, "-c", "-o", obj, csrcFile)...).CombinedOutput(); err != nil {
177 t.Logf("%s", out)
178 t.Fatal(err)
179 }
180 }
181
182 sysoObj := filepath.Join(dir, "ldr.syso")
183 t.Logf("%s %v -nostdlib -r -o %s %v", cc, cflags, sysoObj, objs)
184 if out, err := testenv.Command(t, cc, append(cflags, "-nostdlib", "-r", "-o", sysoObj, objs[0], objs[1])...).CombinedOutput(); err != nil {
185 t.Logf("%s", out)
186 t.Fatal(err)
187 }
188
189 cruft := [][]string{objs, csrcs}
190 for _, sl := range cruft {
191 for _, s := range sl {
192 if err := os.Remove(s); err != nil {
193 t.Fatal(err)
194 }
195 }
196 }
197
198 goFile := filepath.Join(dir, "main.go")
199 if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil {
200 t.Fatal(err)
201 }
202
203 t.Logf("%s build", testenv.GoToolPath(t))
204 cmd := goCmd(t, "build")
205 cmd.Dir = dir
206 cmd.Env = append(cmd.Env, gopathEnv)
207 if out, err := cmd.CombinedOutput(); err != nil {
208 t.Logf("%s", out)
209 t.Fatal(err)
210 }
211 }
212
213 func TestGNUBuildID(t *testing.T) {
214 testenv.MustHaveGoBuild(t)
215
216 t.Parallel()
217
218 tmpdir := t.TempDir()
219 goFile := filepath.Join(tmpdir, "notes.go")
220 if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil {
221 t.Fatal(err)
222 }
223
224
225 const gobuildid = "testbuildid"
226 h := hash.Sum32([]byte(gobuildid))
227 gobuildidHash := string(h[:20])
228
229 tests := []struct{ name, ldflags, expect string }{
230 {"default", "", gobuildidHash},
231 {"gobuildid", "-B=gobuildid", gobuildidHash},
232 {"specific", "-B=0x0123456789abcdef", "\x01\x23\x45\x67\x89\xab\xcd\xef"},
233 {"none", "-B=none", ""},
234 }
235 if testenv.HasCGO() && runtime.GOOS != "solaris" && runtime.GOOS != "illumos" {
236
237
238 for _, test := range tests {
239 t1 := test
240 t1.name += "_external"
241 t1.ldflags += " -linkmode=external"
242 tests = append(tests, t1)
243 }
244 }
245 for _, test := range tests {
246 t.Run(test.name, func(t *testing.T) {
247 exe := filepath.Join(tmpdir, test.name)
248 cmd := goCmd(t, "build", "-ldflags=-buildid="+gobuildid+" "+test.ldflags, "-o", exe, goFile)
249 if out, err := cmd.CombinedOutput(); err != nil {
250 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
251 }
252 gnuBuildID, err := buildid.ReadELFNote(exe, string(ld.ELF_NOTE_BUILDINFO_NAME), ld.ELF_NOTE_BUILDINFO_TAG)
253 if err != nil {
254 t.Fatalf("can't read GNU build ID")
255 }
256 if string(gnuBuildID) != test.expect {
257 t.Errorf("build id mismatch: got %x, want %x", gnuBuildID, test.expect)
258 }
259 })
260 }
261 }
262
263 func TestMergeNoteSections(t *testing.T) {
264 testenv.MustHaveGoBuild(t)
265 expected := 1
266
267 switch runtime.GOOS {
268 case "linux", "dragonfly":
269 case "openbsd", "netbsd", "freebsd":
270
271 expected = 2
272 default:
273 t.Skip("We should only test on elf output.")
274 }
275 t.Parallel()
276
277 goFile := filepath.Join(t.TempDir(), "notes.go")
278 if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil {
279 t.Fatal(err)
280 }
281 outFile := filepath.Join(t.TempDir(), "notes.exe")
282
283 id := "0xf4e8cd51ce8bae2996dc3b74639cdeaa1f7fee5f"
284 cmd := goCmd(t, "build", "-o", outFile, "-ldflags", "-B "+id, goFile)
285 cmd.Dir = t.TempDir()
286 if out, err := cmd.CombinedOutput(); err != nil {
287 t.Logf("%s", out)
288 t.Fatal(err)
289 }
290
291 ef, err := elf.Open(outFile)
292 if err != nil {
293 t.Fatalf("open elf file failed:%v", err)
294 }
295 defer ef.Close()
296 sec := ef.Section(".note.gnu.build-id")
297 if sec == nil {
298 t.Fatalf("can't find gnu build id")
299 }
300
301 sec = ef.Section(".note.go.buildid")
302 if sec == nil {
303 t.Fatalf("can't find go build id")
304 }
305 cnt := 0
306 for _, ph := range ef.Progs {
307 if ph.Type == elf.PT_NOTE {
308 cnt += 1
309 }
310 }
311 if cnt != expected {
312 t.Fatalf("want %d PT_NOTE segment, got %d", expected, cnt)
313 }
314 }
315
316 const pieSourceTemplate = `
317 package main
318
319 import "fmt"
320
321 // Force the creation of a lot of type descriptors that will go into
322 // the .data.rel.ro section.
323 {{range $index, $element := .}}var V{{$index}} interface{} = [{{$index}}]int{}
324 {{end}}
325
326 func main() {
327 {{range $index, $element := .}} fmt.Println(V{{$index}})
328 {{end}}
329 }
330 `
331
332 func TestPIESize(t *testing.T) {
333 testenv.MustHaveGoBuild(t)
334
335
336
337
338 testenv.MustHaveCGO(t)
339
340 if !platform.BuildModeSupported(runtime.Compiler, "pie", runtime.GOOS, runtime.GOARCH) {
341 t.Skip("-buildmode=pie not supported")
342 }
343
344 t.Parallel()
345
346 tmpl := template.Must(template.New("pie").Parse(pieSourceTemplate))
347
348 writeGo := func(t *testing.T, dir string) {
349 f, err := os.Create(filepath.Join(dir, "pie.go"))
350 if err != nil {
351 t.Fatal(err)
352 }
353
354
355
356
357 if err := tmpl.Execute(f, make([]byte, 100)); err != nil {
358 t.Fatal(err)
359 }
360
361 if err := f.Close(); err != nil {
362 t.Fatal(err)
363 }
364 }
365
366 var linkmodes []string
367 if platform.InternalLinkPIESupported(runtime.GOOS, runtime.GOARCH) {
368 linkmodes = append(linkmodes, "internal")
369 }
370 linkmodes = append(linkmodes, "external")
371
372 for _, linkmode := range linkmodes {
373 t.Run(fmt.Sprintf("TestPieSize-%v", linkmode), func(t *testing.T) {
374 t.Parallel()
375
376 dir := t.TempDir()
377
378 writeGo(t, dir)
379
380 binexe := filepath.Join(dir, "exe")
381 binpie := filepath.Join(dir, "pie")
382 binexe += linkmode
383 binpie += linkmode
384
385 build := func(bin, mode string) error {
386 cmd := goCmd(t, "build", "-o", bin, "-buildmode="+mode, "-ldflags=-linkmode="+linkmode)
387 cmd.Args = append(cmd.Args, "pie.go")
388 cmd.Dir = dir
389 t.Logf("%v", cmd.Args)
390 out, err := cmd.CombinedOutput()
391 if len(out) > 0 {
392 t.Logf("%s", out)
393 }
394 if err != nil {
395 t.Log(err)
396 }
397 return err
398 }
399
400 var errexe, errpie error
401 var wg sync.WaitGroup
402 wg.Add(2)
403 go func() {
404 defer wg.Done()
405 errexe = build(binexe, "exe")
406 }()
407 go func() {
408 defer wg.Done()
409 errpie = build(binpie, "pie")
410 }()
411 wg.Wait()
412 if errexe != nil || errpie != nil {
413 if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
414 testenv.SkipFlaky(t, 58806)
415 }
416 t.Fatal("link failed")
417 }
418
419 var sizeexe, sizepie uint64
420 if fi, err := os.Stat(binexe); err != nil {
421 t.Fatal(err)
422 } else {
423 sizeexe = uint64(fi.Size())
424 }
425 if fi, err := os.Stat(binpie); err != nil {
426 t.Fatal(err)
427 } else {
428 sizepie = uint64(fi.Size())
429 }
430
431 elfexe, err := elf.Open(binexe)
432 if err != nil {
433 t.Fatal(err)
434 }
435 defer elfexe.Close()
436
437 elfpie, err := elf.Open(binpie)
438 if err != nil {
439 t.Fatal(err)
440 }
441 defer elfpie.Close()
442
443
444
445
446
447
448
449
450
451
452
453
454 textsize := func(ef *elf.File, name string) uint64 {
455 for _, s := range ef.Sections {
456 if s.Name == ".text" {
457 return s.Size
458 }
459 }
460 t.Fatalf("%s: no .text section", name)
461 return 0
462 }
463 textexe := textsize(elfexe, binexe)
464 textpie := textsize(elfpie, binpie)
465
466 dynsize := func(ef *elf.File) uint64 {
467 var ret uint64
468 for _, s := range ef.Sections {
469 if s.Flags&elf.SHF_ALLOC == 0 {
470 continue
471 }
472 switch s.Type {
473 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:
474 ret += s.Size
475 }
476 if s.Flags&elf.SHF_WRITE != 0 && (strings.Contains(s.Name, ".got") || strings.Contains(s.Name, ".plt")) {
477 ret += s.Size
478 }
479 }
480 return ret
481 }
482
483 dynexe := dynsize(elfexe)
484 dynpie := dynsize(elfpie)
485
486 extrasize := func(ef *elf.File) uint64 {
487 var ret uint64
488
489 for _, s := range ef.Sections {
490 if s.Flags&elf.SHF_ALLOC == 0 {
491 ret += s.Size
492 }
493 }
494
495 var prev *elf.Prog
496 for _, seg := range ef.Progs {
497 if seg.Type != elf.PT_LOAD {
498 continue
499 }
500 if prev != nil {
501 ret += seg.Off - prev.Off - prev.Filesz
502 }
503 prev = seg
504 }
505 return ret
506 }
507
508 extraexe := extrasize(elfexe)
509 extrapie := extrasize(elfpie)
510
511 if sizepie < sizeexe || sizepie-extrapie < sizeexe-extraexe {
512 return
513 }
514 diffReal := (sizepie - extrapie) - (sizeexe - extraexe)
515 diffExpected := (textpie + dynpie) - (textexe + dynexe)
516
517 t.Logf("real size difference %#x, expected %#x", diffReal, diffExpected)
518
519 if diffReal > (diffExpected + diffExpected/10) {
520 t.Errorf("PIE unexpectedly large: got difference of %d (%d - %d), expected difference %d", diffReal, sizepie, sizeexe, diffExpected)
521 }
522 })
523 }
524 }
525
526 func TestIssue51939(t *testing.T) {
527 testenv.MustHaveGoBuild(t)
528 t.Parallel()
529 td := t.TempDir()
530 goFile := filepath.Join(td, "issue51939.go")
531 if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil {
532 t.Fatal(err)
533 }
534 outFile := filepath.Join(td, "issue51939.exe")
535 cmd := goCmd(t, "build", "-o", outFile, goFile)
536 if out, err := cmd.CombinedOutput(); err != nil {
537 t.Logf("%s", out)
538 t.Fatal(err)
539 }
540
541 ef, err := elf.Open(outFile)
542 if err != nil {
543 t.Fatal(err)
544 }
545
546 for _, s := range ef.Sections {
547 if s.Flags&elf.SHF_ALLOC == 0 && s.Addr != 0 {
548 t.Errorf("section %s should not allocated with addr %x", s.Name, s.Addr)
549 }
550 }
551 }
552
553 func TestFlagR(t *testing.T) {
554
555
556
557
558 testenv.MustHaveGoBuild(t)
559 t.Parallel()
560 tmpdir := t.TempDir()
561 src := filepath.Join(tmpdir, "x.go")
562 if err := os.WriteFile(src, []byte(goSource), 0444); err != nil {
563 t.Fatal(err)
564 }
565 exe := filepath.Join(tmpdir, "x.exe")
566
567 cmd := goCmd(t, "build", "-ldflags=-R=0x100000", "-o", exe, src)
568 if out, err := cmd.CombinedOutput(); err != nil {
569 t.Fatalf("build failed: %v, output:\n%s", err, out)
570 }
571
572 cmd = testenv.Command(t, exe)
573 if out, err := cmd.CombinedOutput(); err != nil {
574 t.Errorf("executable failed to run: %v\n%s", err, out)
575 }
576 }
577
578 func TestFlagD(t *testing.T) {
579
580
581 t.Parallel()
582 testFlagD(t, "0x10000000", "", 0x10000000)
583 }
584
585 func TestFlagDUnaligned(t *testing.T) {
586
587 t.Parallel()
588 testFlagDError(t, "0x10000123", "", "invalid -D value 0x10000123")
589 }
590
591 func TestFlagDWithR(t *testing.T) {
592
593 t.Parallel()
594 testFlagDError(t, "0x30001234", "8192", "invalid -D value 0x30001234")
595 }
596
597 func testFlagD(t *testing.T, dataAddr string, roundQuantum string, expectedAddr uint64) {
598 testenv.MustHaveGoBuild(t)
599 tmpdir := t.TempDir()
600 src := filepath.Join(tmpdir, "x.go")
601 if err := os.WriteFile(src, []byte(goSourceWithData), 0444); err != nil {
602 t.Fatal(err)
603 }
604 exe := filepath.Join(tmpdir, "x.exe")
605
606
607 ldflags := "-D=" + dataAddr
608 if roundQuantum != "" {
609 ldflags += " -R=" + roundQuantum
610 }
611
612 cmd := goCmd(t, "build", "-ldflags="+ldflags, "-o", exe, src)
613 if out, err := cmd.CombinedOutput(); err != nil {
614 t.Fatalf("build failed: %v, output:\n%s", err, out)
615 }
616
617 cmd = testenv.Command(t, exe)
618 if out, err := cmd.CombinedOutput(); err != nil {
619 t.Errorf("executable failed to run: %v\n%s", err, out)
620 }
621
622 ef, err := elf.Open(exe)
623 if err != nil {
624 t.Fatalf("open elf file failed: %v", err)
625 }
626 defer ef.Close()
627
628
629 var firstDataSection *elf.Section
630 for _, sec := range ef.Sections {
631 if sec.Type == elf.SHT_PROGBITS || sec.Type == elf.SHT_NOBITS {
632
633
634 isWrite := sec.Flags&elf.SHF_WRITE != 0
635 isExec := sec.Flags&elf.SHF_EXECINSTR != 0
636 isAlloc := sec.Flags&elf.SHF_ALLOC != 0
637 isTLS := sec.Flags&elf.SHF_TLS != 0
638
639 if isWrite && !isExec && isAlloc && !isTLS {
640 if firstDataSection == nil || sec.Addr < firstDataSection.Addr {
641 firstDataSection = sec
642 }
643 }
644 }
645 }
646
647 if firstDataSection == nil {
648 t.Fatalf("can't find any writable data sections")
649 }
650 if firstDataSection.Addr != expectedAddr {
651 t.Errorf("data section starts at 0x%x for section %s, expected 0x%x",
652 firstDataSection.Addr, firstDataSection.Name, expectedAddr)
653 }
654 }
655
656 func testFlagDError(t *testing.T, dataAddr string, roundQuantum string, expectedError string) {
657 testenv.MustHaveGoBuild(t)
658 tmpdir := t.TempDir()
659 src := filepath.Join(tmpdir, "x.go")
660 if err := os.WriteFile(src, []byte(goSourceWithData), 0444); err != nil {
661 t.Fatal(err)
662 }
663 exe := filepath.Join(tmpdir, "x.exe")
664
665
666 ldflags := "-D=" + dataAddr
667 if roundQuantum != "" {
668 ldflags += " -R=" + roundQuantum
669 }
670
671 cmd := goCmd(t, "build", "-ldflags="+ldflags, "-o", exe, src)
672 out, err := cmd.CombinedOutput()
673 if err == nil {
674 t.Fatalf("expected build to fail with unaligned data address, but it succeeded")
675 }
676 if !strings.Contains(string(out), expectedError) {
677 t.Errorf("expected error message to contain %q, got:\n%s", expectedError, out)
678 }
679 }
680
681 func TestELFHeadersSorted(t *testing.T) {
682 for _, buildmode := range []string{"exe", "pie"} {
683 t.Run(buildmode, func(t *testing.T) {
684 testELFHeadersSorted(t, buildmode)
685 })
686 }
687 }
688
689 func testELFHeadersSorted(t *testing.T, buildmode string) {
690 testenv.MustHaveGoBuild(t)
691
692
693
694
695 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
696 if buildmode == "pie" {
697 testenv.MustInternalLinkPIE(t)
698 }
699
700 t.Parallel()
701
702 tmpdir := t.TempDir()
703 src := filepath.Join(tmpdir, "x.go")
704 if err := os.WriteFile(src, []byte(goSourceWithData), 0o444); err != nil {
705 t.Fatal(err)
706 }
707
708 exe := filepath.Join(tmpdir, "x.exe")
709 cmd := goCmd(t, "build", "-buildmode="+buildmode, "-ldflags=-linkmode=internal", "-o", exe, src)
710 if out, err := cmd.CombinedOutput(); err != nil {
711 t.Fatalf("build failed: %v, output:\n%s", err, out)
712 }
713
714
715 f, err := os.Open(exe)
716 if err != nil {
717 t.Fatal(err)
718 }
719 defer f.Close()
720
721 var ident [elf.EI_NIDENT]byte
722 if _, err := f.Read(ident[:]); err != nil {
723 t.Fatal(err)
724 }
725
726 var bo binary.ByteOrder
727 switch elf.Data(ident[elf.EI_DATA]) {
728 case elf.ELFDATA2LSB:
729 bo = binary.LittleEndian
730 case elf.ELFDATA2MSB:
731 bo = binary.BigEndian
732 default:
733 t.Fatalf("unrecognized data encoding %d", ident[elf.EI_DATA])
734 }
735
736 var shoff int64
737 var shsize int
738 switch elf.Class(ident[elf.EI_CLASS]) {
739 case elf.ELFCLASS32:
740 var hdr elf.Header32
741 data := make([]byte, unsafe.Sizeof(hdr))
742 if _, err := f.ReadAt(data, 0); err != nil {
743 t.Fatal(err)
744 }
745 shoff = int64(bo.Uint32(data[unsafe.Offsetof(hdr.Shoff):]))
746 shsize = int(unsafe.Sizeof(elf.Section32{}))
747
748 case elf.ELFCLASS64:
749 var hdr elf.Header64
750 data := make([]byte, unsafe.Sizeof(hdr))
751 if _, err := f.ReadAt(data, 0); err != nil {
752 t.Fatal(err)
753 }
754 shoff = int64(bo.Uint64(data[unsafe.Offsetof(hdr.Shoff):]))
755 shsize = int(unsafe.Sizeof(elf.Section64{}))
756
757 default:
758 t.Fatalf("unrecognized class %d", ident[elf.EI_CLASS])
759 }
760
761 if shoff > 0 {
762 data := make([]byte, shsize)
763 if _, err := f.ReadAt(data, shoff); err != nil {
764 t.Fatal(err)
765 }
766 for i, c := range data {
767 if c != 0 {
768 t.Errorf("section header 0 byte %d is %d, should be zero", i, c)
769 }
770 }
771 }
772
773 ef, err := elf.NewFile(f)
774 if err != nil {
775 t.Fatal(err)
776 }
777 defer ef.Close()
778
779
780
781
782
783 i := 1
784 lastAddr := uint64(0)
785 for i < len(ef.Sections) {
786 sec := ef.Sections[i]
787 if sec.Flags&elf.SHF_ALLOC == 0 {
788 break
789 }
790 if sec.Addr < lastAddr {
791 t.Errorf("section %d %q address %#x less than previous address %#x", i, sec.Name, sec.Addr, lastAddr)
792 }
793 lastAddr = sec.Addr
794 i++
795 }
796
797 firstUnalc := i
798 for i < len(ef.Sections) {
799 sec := ef.Sections[i]
800 if sec.Flags&elf.SHF_ALLOC != 0 {
801 t.Errorf("allocated section %d %q follows first unallocated section %d %q", i, sec.Name, firstUnalc, ef.Sections[firstUnalc].Name)
802 }
803 i++
804 }
805 }
806
View as plain text