1
2
3
4
5
6
7 package ld
8
9 import (
10 "debug/macho"
11 "fmt"
12 "internal/testenv"
13 "os"
14 "path/filepath"
15 "testing"
16 )
17
18 func TestMachoSectionsReadOnly(t *testing.T) {
19 t.Parallel()
20 testenv.MustHaveGoBuild(t)
21
22 const (
23 prog = `package main; func main() {}`
24 progC = `package main; import "C"; func main() {}`
25 )
26
27 tests := []struct {
28 name string
29 args []string
30 prog string
31 wantSecsRO []string
32 mustHaveCGO bool
33 mustInternalLink bool
34 }{
35 {
36 name: "linkmode-internal",
37 args: []string{"-ldflags", "-linkmode=internal"},
38 prog: prog,
39 mustInternalLink: true,
40 wantSecsRO: []string{"__got", "__rodata", "__itablink", "__typelink", "__gosymtab", "__gopclntab"},
41 },
42 {
43 name: "linkmode-external",
44 args: []string{"-ldflags", "-linkmode=external"},
45 prog: prog,
46 mustHaveCGO: true,
47 wantSecsRO: []string{"__got", "__rodata", "__itablink", "__typelink", "__gopclntab"},
48 },
49 {
50 name: "cgo-linkmode-internal",
51 args: []string{"-ldflags", "-linkmode=external"},
52 prog: progC,
53 mustHaveCGO: true,
54 mustInternalLink: true,
55 wantSecsRO: []string{"__got", "__rodata", "__itablink", "__typelink", "__gopclntab"},
56 },
57 {
58 name: "cgo-linkmode-external",
59 args: []string{"-ldflags", "-linkmode=external"},
60 prog: progC,
61 mustHaveCGO: true,
62 wantSecsRO: []string{"__got", "__rodata", "__itablink", "__typelink", "__gopclntab"},
63 },
64 }
65
66 for _, test := range tests {
67 t.Run(test.name, func(t *testing.T) {
68 if test.mustInternalLink {
69 testenv.MustInternalLink(t, test.mustHaveCGO)
70 }
71 if test.mustHaveCGO {
72 testenv.MustHaveCGO(t)
73 }
74
75 var (
76 dir = t.TempDir()
77 src = filepath.Join(dir, fmt.Sprintf("macho_%s.go", test.name))
78 binFile = filepath.Join(dir, test.name)
79 )
80
81 if err := os.WriteFile(src, []byte(test.prog), 0666); err != nil {
82 t.Fatal(err)
83 }
84
85 cmdArgs := append([]string{"build", "-o", binFile}, append(test.args, src)...)
86 cmd := testenv.Command(t, testenv.GoToolPath(t), cmdArgs...)
87
88 if out, err := cmd.CombinedOutput(); err != nil {
89 t.Fatalf("failed to build %v: %v:\n%s", cmd.Args, err, out)
90 }
91
92 fi, err := os.Open(binFile)
93 if err != nil {
94 t.Fatalf("failed to open built file: %v", err)
95 }
96 defer fi.Close()
97
98 machoFile, err := macho.NewFile(fi)
99 if err != nil {
100 t.Fatalf("failed to parse macho file: %v", err)
101 }
102 defer machoFile.Close()
103
104
105 segs := make(map[string]*macho.Segment)
106 for _, l := range machoFile.Loads {
107 if s, ok := l.(*macho.Segment); ok {
108 segs[s.Name] = s
109 }
110 }
111
112 for _, wsroname := range test.wantSecsRO {
113
114
115 var wsro *macho.Section
116 foundRO := false
117 for _, s := range machoFile.Sections {
118 if s.Name == wsroname {
119 seg := segs[s.Seg]
120 if seg == nil {
121 t.Fatalf("test %s: can't locate segment for %q section",
122 test.name, wsroname)
123 }
124 if seg.Flag == 0x10 {
125 foundRO = true
126 wsro = s
127 break
128 }
129 }
130 }
131 if wsro == nil {
132 t.Fatalf("test %s: can't locate %q section",
133 test.name, wsroname)
134 continue
135 }
136 if !foundRO {
137
138
139
140 t.Logf("test %s: %q section not in readonly segment",
141 wsro.Name, test.name)
142 t.Logf("section %s location: st=0x%x en=0x%x\n",
143 wsro.Name, wsro.Addr, wsro.Addr+wsro.Size)
144 t.Logf("sec %s found in this segment: ", wsro.Seg)
145 t.Logf("\nall segments: \n")
146 for _, l := range machoFile.Loads {
147 if s, ok := l.(*macho.Segment); ok {
148 t.Logf("cmd=%s fl=%d st=0x%x en=0x%x\n",
149 s.Cmd, s.Flag, s.Addr, s.Addr+s.Filesz)
150 }
151 }
152 t.Fatalf("test %s failed", test.name)
153 }
154 }
155 })
156 }
157 }
158
View as plain text