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