1
2
3
4
5
6
7 package ld
8
9 import (
10 "debug/elf"
11 "fmt"
12 "internal/testenv"
13 "os"
14 "os/exec"
15 "path/filepath"
16 "runtime"
17 "sort"
18 "strings"
19 "testing"
20 )
21
22 func TestDynSymShInfo(t *testing.T) {
23 t.Parallel()
24 testenv.MustHaveGoBuild(t)
25 dir := t.TempDir()
26
27 const prog = `
28 package main
29
30 import "net"
31
32 func main() {
33 net.Dial("", "")
34 }
35 `
36 src := filepath.Join(dir, "issue33358.go")
37 if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
38 t.Fatal(err)
39 }
40
41 binFile := filepath.Join(dir, "issue33358")
42 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, src)
43 if out, err := cmd.CombinedOutput(); err != nil {
44 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
45 }
46
47 fi, err := os.Open(binFile)
48 if err != nil {
49 t.Fatalf("failed to open built file: %v", err)
50 }
51 defer fi.Close()
52
53 elfFile, err := elf.NewFile(fi)
54 if err != nil {
55 t.Skip("The system may not support ELF, skipped.")
56 }
57
58 section := elfFile.Section(".dynsym")
59 if section == nil {
60 t.Fatal("no dynsym")
61 }
62
63 symbols, err := elfFile.DynamicSymbols()
64 if err != nil {
65 t.Fatalf("failed to get dynamic symbols: %v", err)
66 }
67
68 var numLocalSymbols uint32
69 for i, s := range symbols {
70 if elf.ST_BIND(s.Info) != elf.STB_LOCAL {
71 numLocalSymbols = uint32(i + 1)
72 break
73 }
74 }
75
76 if section.Info != numLocalSymbols {
77 t.Fatalf("Unexpected sh info, want greater than 0, got: %d", section.Info)
78 }
79 }
80
81 func TestNoDuplicateNeededEntries(t *testing.T) {
82 testenv.MustHaveGoBuild(t)
83 testenv.MustHaveCGO(t)
84
85
86
87 pair := runtime.GOOS + "-" + runtime.GOARCH
88 switch pair {
89 case "linux-amd64", "linux-arm64", "freebsd-amd64", "openbsd-amd64":
90 default:
91 t.Skip("no need for test on " + pair)
92 }
93
94 t.Parallel()
95
96 dir := t.TempDir()
97 path := filepath.Join(dir, "x")
98 argv := []string{"build", "-o", path, "./testdata/issue39256"}
99 out, err := testenv.Command(t, testenv.GoToolPath(t), argv...).CombinedOutput()
100 if err != nil {
101 t.Fatalf("Build failure: %s\n%s\n", err, string(out))
102 }
103
104 f, err := elf.Open(path)
105 if err != nil {
106 t.Fatalf("Failed to open ELF file: %v", err)
107 }
108 libs, err := f.ImportedLibraries()
109 if err != nil {
110 t.Fatalf("Failed to read imported libraries: %v", err)
111 }
112
113 var count int
114 for _, lib := range libs {
115 if lib == "libc.so" || strings.HasPrefix(lib, "libc.so.") {
116 count++
117 }
118 }
119
120 if got, want := count, 1; got != want {
121 t.Errorf("Got %d entries for `libc.so`, want %d", got, want)
122 }
123 }
124
125 func TestShStrTabAttributesIssue62600(t *testing.T) {
126 t.Parallel()
127 testenv.MustHaveGoBuild(t)
128 dir := t.TempDir()
129
130 const prog = `
131 package main
132
133 func main() {
134 println("whee")
135 }
136 `
137 src := filepath.Join(dir, "issue62600.go")
138 if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
139 t.Fatal(err)
140 }
141
142 binFile := filepath.Join(dir, "issue62600")
143 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, src)
144 if out, err := cmd.CombinedOutput(); err != nil {
145 t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
146 }
147
148 fi, err := os.Open(binFile)
149 if err != nil {
150 t.Fatalf("failed to open built file: %v", err)
151 }
152 defer fi.Close()
153
154 elfFile, err := elf.NewFile(fi)
155 if err != nil {
156 t.Skip("The system may not support ELF, skipped.")
157 }
158
159 section := elfFile.Section(".shstrtab")
160 if section == nil {
161 t.Fatal("no .shstrtab")
162 }
163
164
165
166
167 if section.Addr != 0 {
168 t.Fatalf("expected Addr == 0 for .shstrtab got %x", section.Addr)
169 }
170 if section.Size == 0 {
171 t.Fatal("expected nonzero Size for .shstrtab got 0")
172 }
173 if section.Flags&elf.SHF_ALLOC != 0 {
174 t.Fatal("expected zero alloc flag got nonzero for .shstrtab")
175 }
176 for idx, p := range elfFile.Progs {
177 if section.Offset >= p.Off && section.Offset < p.Off+p.Filesz {
178 t.Fatalf("badly formed .shstrtab, is contained in segment %d", idx)
179 }
180 }
181 }
182
183 func TestElfBindNow(t *testing.T) {
184 t.Parallel()
185 testenv.MustHaveGoBuild(t)
186
187 const (
188 prog = `package main; func main() {}`
189
190 progC = `package main; import "C"; func main() {}`
191 )
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206 tests := []struct {
207 name string
208 args []string
209 prog string
210 wantSecsRO []string
211 wantSecsROIfPresent []string
212 mustHaveBuildModePIE bool
213 mustHaveCGO bool
214 mustInternalLink bool
215 wantDfBindNow bool
216 wantDf1Now bool
217 wantDf1Pie bool
218 }{
219 {name: "default", prog: prog},
220 {
221 name: "pie-linkmode-internal",
222 args: []string{"-buildmode=pie", "-ldflags", "-linkmode=internal"},
223 prog: prog,
224 mustHaveBuildModePIE: true,
225 mustInternalLink: true,
226 wantDf1Pie: true,
227 wantSecsRO: []string{".dynamic"},
228 wantSecsROIfPresent: []string{".got"},
229 },
230 {
231 name: "bindnow-linkmode-internal",
232 args: []string{"-ldflags", "-bindnow -linkmode=internal"},
233 prog: progC,
234 mustHaveCGO: true,
235 mustInternalLink: true,
236 wantDfBindNow: true,
237 wantDf1Now: true,
238 },
239 {
240 name: "bindnow-pie-linkmode-internal",
241 args: []string{"-buildmode=pie", "-ldflags", "-bindnow -linkmode=internal"},
242 prog: prog,
243 mustHaveBuildModePIE: true,
244 mustInternalLink: true,
245 wantDfBindNow: true,
246 wantDf1Now: true,
247 wantDf1Pie: true,
248 wantSecsRO: []string{".dynamic"},
249 wantSecsROIfPresent: []string{".got", ".got.plt"},
250 },
251 {
252 name: "bindnow-pie-linkmode-external",
253 args: []string{"-buildmode=pie", "-ldflags", "-bindnow -linkmode=external"},
254 prog: prog,
255 mustHaveBuildModePIE: true,
256 mustHaveCGO: true,
257 wantDfBindNow: true,
258 wantDf1Now: true,
259 wantDf1Pie: true,
260 wantSecsRO: []string{".dynamic"},
261 },
262 }
263
264 gotDynFlag := func(flags []uint64, dynFlag uint64) bool {
265 for _, flag := range flags {
266 if gotFlag := dynFlag&flag != 0; gotFlag {
267 return true
268 }
269 }
270 return false
271 }
272
273 segContainsSec := func(p *elf.Prog, s *elf.Section) bool {
274 return s.Addr >= p.Vaddr &&
275 s.Addr+s.FileSize <= p.Vaddr+p.Filesz
276 }
277
278 for _, test := range tests {
279 t.Run(test.name, func(t *testing.T) {
280 if test.mustInternalLink {
281
282 testenv.MustInternalLink(t, testenv.SpecialBuildTypes{Cgo: test.mustHaveCGO})
283 }
284 if test.mustHaveCGO {
285 testenv.MustHaveCGO(t)
286 }
287 if test.mustHaveBuildModePIE {
288 testenv.MustHaveBuildMode(t, "pie")
289 }
290 if test.mustHaveBuildModePIE && test.mustInternalLink {
291 testenv.MustInternalLinkPIE(t)
292 }
293
294 var (
295 dir = t.TempDir()
296 src = filepath.Join(dir, fmt.Sprintf("elf_%s.go", test.name))
297 binFile = filepath.Join(dir, test.name)
298 )
299
300 if err := os.WriteFile(src, []byte(test.prog), 0666); err != nil {
301 t.Fatal(err)
302 }
303
304 cmdArgs := append([]string{"build", "-o", binFile}, append(test.args, src)...)
305 cmd := testenv.Command(t, testenv.GoToolPath(t), cmdArgs...)
306
307 if out, err := cmd.CombinedOutput(); err != nil {
308 t.Fatalf("failed to build %v: %v:\n%s", cmd.Args, err, out)
309 }
310
311 fi, err := os.Open(binFile)
312 if err != nil {
313 t.Fatalf("failed to open built file: %v", err)
314 }
315 defer fi.Close()
316
317 elfFile, err := elf.NewFile(fi)
318 if err != nil {
319 t.Skip("The system may not support ELF, skipped.")
320 }
321 defer elfFile.Close()
322
323 flags, err := elfFile.DynValue(elf.DT_FLAGS)
324 if err != nil {
325 t.Fatalf("failed to get DT_FLAGS: %v", err)
326 }
327
328 flags1, err := elfFile.DynValue(elf.DT_FLAGS_1)
329 if err != nil {
330 t.Fatalf("failed to get DT_FLAGS_1: %v", err)
331 }
332
333 gotDfBindNow := gotDynFlag(flags, uint64(elf.DF_BIND_NOW))
334 gotDf1Now := gotDynFlag(flags1, uint64(elf.DF_1_NOW))
335
336 bindNowFlagsMatch := gotDfBindNow == test.wantDfBindNow && gotDf1Now == test.wantDf1Now
337
338
339 if !test.mustInternalLink {
340 bindNowFlagsMatch = gotDfBindNow == test.wantDfBindNow || gotDf1Now == test.wantDf1Now
341 }
342
343 if !bindNowFlagsMatch {
344 t.Fatalf("Dynamic flags mismatch:\n"+
345 "DT_FLAGS BIND_NOW got: %v, want: %v\n"+
346 "DT_FLAGS_1 DF_1_NOW got: %v, want: %v",
347 gotDfBindNow, test.wantDfBindNow, gotDf1Now, test.wantDf1Now)
348 }
349
350 if gotDf1Pie := gotDynFlag(flags1, uint64(elf.DF_1_PIE)); gotDf1Pie != test.wantDf1Pie {
351 t.Fatalf("DT_FLAGS_1 DF_1_PIE got: %v, want: %v", gotDf1Pie, test.wantDf1Pie)
352 }
353
354 wsrolists := [][]string{test.wantSecsRO, test.wantSecsROIfPresent}
355 for k, wsrolist := range wsrolists {
356 for _, wsroname := range wsrolist {
357
358 var wsro *elf.Section
359 for _, s := range elfFile.Sections {
360 if s.Name == wsroname {
361 wsro = s
362 break
363 }
364 }
365 if wsro == nil {
366 if k == 0 {
367 t.Fatalf("test %s: can't locate %q section",
368 test.name, wsroname)
369 }
370 continue
371 }
372
373
374
375 foundRO := false
376 foundSegs := []*elf.Prog{}
377 for _, p := range elfFile.Progs {
378 if segContainsSec(p, wsro) {
379 foundSegs = append(foundSegs, p)
380 if p.Flags == elf.PF_R {
381 foundRO = true
382 }
383 }
384 }
385 if !foundRO {
386
387
388
389 t.Logf("test %s: %q section not in readonly segment",
390 wsro.Name, test.name)
391 t.Logf("section %s location: st=0x%x en=0x%x\n",
392 wsro.Name, wsro.Addr, wsro.Addr+wsro.FileSize)
393 t.Logf("sec %s found in these segments: ", wsro.Name)
394 for _, p := range foundSegs {
395 t.Logf(" %q", p.Type)
396 }
397 t.Logf("\nall segments: \n")
398 for k, p := range elfFile.Progs {
399 t.Logf("%d t=%s fl=%s st=0x%x en=0x%x\n",
400 k, p.Type, p.Flags, p.Vaddr, p.Vaddr+p.Filesz)
401 }
402 t.Fatalf("test %s failed", test.name)
403 }
404 }
405 }
406 })
407 }
408 }
409
410
411
412
413 const ifacecallsProg = `
414 package main
415
416 import "reflect"
417
418 type A string
419 type B int
420 type C float64
421
422 type describer interface{ What() string }
423 type timer interface{ When() int }
424 type rationale interface{ Why() error }
425
426 func (a *A) What() string { return "string" }
427 func (b *B) What() string { return "int" }
428 func (b *B) When() int { return int(*b) }
429 func (b *B) Why() error { return nil }
430 func (c *C) What() string { return "float64" }
431
432 func i_am_dead(c C) {
433 var d describer = &c
434 println(d.What())
435 }
436
437 func example(a A, b B) describer {
438 if b == 1 {
439 return &a
440 }
441 return &b
442 }
443
444 func ouch(a any, what string) string {
445 cv := reflect.ValueOf(a).MethodByName(what).Call(nil)
446 return cv[0].String()
447 }
448
449 func main() {
450 println(example("", 1).What())
451 println(ouch(example("", 1), "What"))
452 }
453
454 `
455
456 func TestRelroSectionOverlapIssue67261(t *testing.T) {
457 t.Parallel()
458 testenv.MustHaveGoBuild(t)
459 testenv.MustHaveBuildMode(t, "pie")
460 testenv.MustInternalLinkPIE(t)
461
462
463
464
465
466
467
468 dir := t.TempDir()
469 src := filepath.Join(dir, "e.go")
470 binFile := filepath.Join(dir, "e.exe")
471
472 if err := os.WriteFile(src, []byte(ifacecallsProg), 0666); err != nil {
473 t.Fatal(err)
474 }
475
476 cmdArgs := []string{"build", "-o", binFile, "-buildmode=pie", "-ldflags=linkmode=internal", src}
477 cmd := testenv.Command(t, testenv.GoToolPath(t), cmdArgs...)
478
479 if out, err := cmd.CombinedOutput(); err != nil {
480 t.Fatalf("failed to build %v: %v:\n%s", cmd.Args, err, out)
481 }
482
483 fi, err := os.Open(binFile)
484 if err != nil {
485 t.Fatalf("failed to open built file: %v", err)
486 }
487 defer fi.Close()
488
489 elfFile, err := elf.NewFile(fi)
490 if err != nil {
491 t.Skip("The system may not support ELF, skipped.")
492 }
493 defer elfFile.Close()
494
495
496
497 secs := []*elf.Section{}
498 for _, s := range elfFile.Sections {
499 if s.Type != elf.SHT_PROGBITS && s.Type != elf.SHT_DYNAMIC {
500 continue
501 }
502 if s.Addr == 0 || s.Size == 0 {
503 continue
504 }
505 secs = append(secs, s)
506 }
507
508 secOverlaps := func(s1, s2 *elf.Section) bool {
509 st1 := s1.Addr
510 st2 := s2.Addr
511 en1 := s1.Addr + s1.Size
512 en2 := s2.Addr + s2.Size
513 return max(st1, st2) < min(en1, en2)
514 }
515
516
517 sort.SliceStable(secs, func(i, j int) bool {
518 return secs[i].Addr < secs[j].Addr
519 })
520
521
522 foundOverlap := false
523 for i := 0; i < len(secs)-1; i++ {
524 for j := i + 1; j < len(secs); j++ {
525 s := secs[i]
526 sn := secs[j]
527 if secOverlaps(s, sn) {
528 t.Errorf("unexpected: section %d:%q (addr=%x size=%x) overlaps section %d:%q (addr=%x size=%x)", i, s.Name, s.Addr, s.Size, i+1, sn.Name, sn.Addr, sn.Size)
529 foundOverlap = true
530 }
531 }
532 }
533 if foundOverlap {
534
535 t.Logf("** section list follows\n")
536 for i := range secs {
537 s := secs[i]
538 fmt.Printf(" | %2d: ad=0x%08x en=0x%08x sz=0x%08x t=%s %q\n",
539 i, s.Addr, s.Addr+s.Size, s.Size, s.Type, s.Name)
540 }
541 }
542
543
544 testenv.MustHaveCGO(t)
545
546
547
548
549
550
551
552
553
554
555 stripExecs := []string{}
556 ecmd := testenv.Command(t, testenv.GoToolPath(t), "env", "CC")
557 if out, err := ecmd.CombinedOutput(); err != nil {
558 t.Fatalf("go env CC failed: %v:\n%s", err, out)
559 } else {
560 ccprog := strings.TrimSpace(string(out))
561 tries := []string{"strip", "llvm-strip"}
562 for _, try := range tries {
563 cmd := testenv.Command(t, ccprog, "-print-prog-name="+try)
564 if out, err := cmd.CombinedOutput(); err != nil {
565 t.Fatalf("print-prog-name failed: %+v %v:\n%s",
566 cmd.Args, err, out)
567 } else {
568 sprog := strings.TrimSpace(string(out))
569 stripExecs = append(stripExecs, sprog)
570 }
571 }
572 }
573
574
575
576
577 for k, sprog := range stripExecs {
578 if _, err := os.Stat(sprog); err != nil {
579 sp1, err := exec.LookPath(sprog)
580 if err != nil || sp1 == "" {
581 continue
582 }
583 sprog = sp1
584 }
585 targ := fmt.Sprintf("p%d.exe", k)
586 scmd := testenv.Command(t, sprog, "-o", targ, binFile)
587 scmd.Dir = dir
588 if sout, serr := scmd.CombinedOutput(); serr != nil {
589 t.Fatalf("failed to strip %v: %v:\n%s", scmd.Args, serr, sout)
590 } else {
591
592 if len(string(sout)) != 0 {
593 t.Errorf("unexpected output from %s:\n%s\n", sprog, string(sout))
594 }
595 }
596 rcmd := testenv.Command(t, filepath.Join(dir, targ))
597 if out, err := rcmd.CombinedOutput(); err != nil {
598 t.Errorf("binary stripped by %s failed: %v:\n%s",
599 scmd.Args, err, string(out))
600 }
601 }
602
603 }
604
View as plain text