1
2
3
4
5 package archive
6
7 import (
8 "bytes"
9 "debug/elf"
10 "debug/macho"
11 "debug/pe"
12 "fmt"
13 "internal/testenv"
14 "internal/xcoff"
15 "io"
16 "io/ioutil"
17 "os"
18 "os/exec"
19 "path/filepath"
20 "runtime"
21 "testing"
22 "unicode/utf8"
23 )
24
25 var (
26 buildDir string
27 go1obj string
28 go2obj string
29 goarchive string
30 cgoarchive string
31 )
32
33 func TestMain(m *testing.M) {
34 if !testenv.HasGoBuild() {
35 return
36 }
37
38 if err := buildGoobj(); err != nil {
39 fmt.Println(err)
40 os.RemoveAll(buildDir)
41 os.Exit(1)
42 }
43
44 exit := m.Run()
45
46 os.RemoveAll(buildDir)
47 os.Exit(exit)
48 }
49
50 func copyDir(dst, src string) error {
51 err := os.MkdirAll(dst, 0777)
52 if err != nil {
53 return err
54 }
55 fis, err := ioutil.ReadDir(src)
56 if err != nil {
57 return err
58 }
59 for _, fi := range fis {
60 err = copyFile(filepath.Join(dst, fi.Name()), filepath.Join(src, fi.Name()))
61 if err != nil {
62 return err
63 }
64 }
65 return nil
66 }
67
68 func copyFile(dst, src string) (err error) {
69 var s, d *os.File
70 s, err = os.Open(src)
71 if err != nil {
72 return err
73 }
74 defer s.Close()
75 d, err = os.Create(dst)
76 if err != nil {
77 return err
78 }
79 defer func() {
80 e := d.Close()
81 if err == nil {
82 err = e
83 }
84 }()
85 _, err = io.Copy(d, s)
86 if err != nil {
87 return err
88 }
89 return nil
90 }
91
92 func buildGoobj() error {
93 var err error
94
95 buildDir, err = ioutil.TempDir("", "TestGoobj")
96 if err != nil {
97 return err
98 }
99
100 go1obj = filepath.Join(buildDir, "go1.o")
101 go2obj = filepath.Join(buildDir, "go2.o")
102 goarchive = filepath.Join(buildDir, "go.a")
103
104 gotool, err := testenv.GoTool()
105 if err != nil {
106 return err
107 }
108
109 go1src := filepath.Join("testdata", "go1.go")
110 go2src := filepath.Join("testdata", "go2.go")
111
112 out, err := exec.Command(gotool, "tool", "compile", "-o", go1obj, go1src).CombinedOutput()
113 if err != nil {
114 return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go1obj, go1src, err, out)
115 }
116 out, err = exec.Command(gotool, "tool", "compile", "-o", go2obj, go2src).CombinedOutput()
117 if err != nil {
118 return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go2obj, go2src, err, out)
119 }
120 out, err = exec.Command(gotool, "tool", "pack", "c", goarchive, go1obj, go2obj).CombinedOutput()
121 if err != nil {
122 return fmt.Errorf("go tool pack c %s %s %s: %v\n%s", goarchive, go1obj, go2obj, err, out)
123 }
124
125 if testenv.HasCGO() {
126 gopath := filepath.Join(buildDir, "gopath")
127 err = copyDir(filepath.Join(gopath, "src", "mycgo"), filepath.Join("testdata", "mycgo"))
128 if err == nil {
129 err = ioutil.WriteFile(filepath.Join(gopath, "src", "mycgo", "go.mod"), []byte("module mycgo\n"), 0666)
130 }
131 if err != nil {
132 return err
133 }
134 cmd := exec.Command(gotool, "install", "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "mycgo")
135 cmd.Dir = filepath.Join(gopath, "src", "mycgo")
136 cmd.Env = append(os.Environ(), "GOPATH="+gopath)
137 out, err = cmd.CombinedOutput()
138 if err != nil {
139 return fmt.Errorf("go install mycgo: %v\n%s", err, out)
140 }
141 pat := filepath.Join(gopath, "pkg", "*", "mycgo.a")
142 ms, err := filepath.Glob(pat)
143 if err != nil {
144 return err
145 }
146 if len(ms) == 0 {
147 return fmt.Errorf("cannot found paths for pattern %s", pat)
148 }
149 cgoarchive = ms[0]
150 }
151
152 return nil
153 }
154
155 func TestParseGoobj(t *testing.T) {
156 path := go1obj
157
158 f, err := os.Open(path)
159 if err != nil {
160 t.Fatal(err)
161 }
162 defer f.Close()
163
164 a, err := Parse(f, false)
165 if err != nil {
166 t.Fatal(err)
167 }
168 if len(a.Entries) != 2 {
169 t.Errorf("expect 2 entry, found %d", len(a.Entries))
170 }
171 for _, e := range a.Entries {
172 if e.Type == EntryPkgDef {
173 continue
174 }
175 if e.Type != EntryGoObj {
176 t.Errorf("wrong type of object: want EntryGoObj, got %v", e.Type)
177 }
178 if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
179 t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
180 }
181 }
182 }
183
184 func TestParseArchive(t *testing.T) {
185 path := goarchive
186
187 f, err := os.Open(path)
188 if err != nil {
189 t.Fatal(err)
190 }
191 defer f.Close()
192
193 a, err := Parse(f, false)
194 if err != nil {
195 t.Fatal(err)
196 }
197 if len(a.Entries) != 3 {
198 t.Errorf("expect 3 entry, found %d", len(a.Entries))
199 }
200 var found1 bool
201 var found2 bool
202 for _, e := range a.Entries {
203 if e.Type == EntryPkgDef {
204 continue
205 }
206 if e.Type != EntryGoObj {
207 t.Errorf("wrong type of object: want EntryGoObj, got %v", e.Type)
208 }
209 if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
210 t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
211 }
212 if e.Name == "go1.o" {
213 found1 = true
214 }
215 if e.Name == "go2.o" {
216 found2 = true
217 }
218 }
219 if !found1 {
220 t.Errorf(`object "go1.o" not found`)
221 }
222 if !found2 {
223 t.Errorf(`object "go2.o" not found`)
224 }
225 }
226
227 func TestParseCGOArchive(t *testing.T) {
228 testenv.MustHaveCGO(t)
229
230 path := cgoarchive
231
232 f, err := os.Open(path)
233 if err != nil {
234 t.Fatal(err)
235 }
236 defer f.Close()
237
238 a, err := Parse(f, false)
239 if err != nil {
240 t.Fatal(err)
241 }
242
243 c1 := "c1"
244 c2 := "c2"
245 switch runtime.GOOS {
246 case "darwin", "ios":
247 c1 = "_" + c1
248 c2 = "_" + c2
249 case "windows":
250 if runtime.GOARCH == "386" {
251 c1 = "_" + c1
252 c2 = "_" + c2
253 }
254 case "aix":
255 c1 = "." + c1
256 c2 = "." + c2
257 }
258
259 var foundgo, found1, found2 bool
260
261 for _, e := range a.Entries {
262 switch e.Type {
263 default:
264 t.Errorf("unknown object type")
265 case EntryPkgDef:
266 continue
267 case EntryGoObj:
268 foundgo = true
269 if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
270 t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
271 }
272 continue
273 case EntryNativeObj:
274 }
275
276 obj := io.NewSectionReader(f, e.Offset, e.Size)
277 switch runtime.GOOS {
278 case "darwin", "ios":
279 mf, err := macho.NewFile(obj)
280 if err != nil {
281 t.Fatal(err)
282 }
283 if mf.Symtab == nil {
284 continue
285 }
286 for _, s := range mf.Symtab.Syms {
287 switch s.Name {
288 case c1:
289 found1 = true
290 case c2:
291 found2 = true
292 }
293 }
294 case "windows":
295 pf, err := pe.NewFile(obj)
296 if err != nil {
297 t.Fatal(err)
298 }
299 for _, s := range pf.Symbols {
300 switch s.Name {
301 case c1:
302 found1 = true
303 case c2:
304 found2 = true
305 }
306 }
307 case "aix":
308 xf, err := xcoff.NewFile(obj)
309 if err != nil {
310 t.Fatal(err)
311 }
312 for _, s := range xf.Symbols {
313 switch s.Name {
314 case c1:
315 found1 = true
316 case c2:
317 found2 = true
318 }
319 }
320 default:
321 ef, err := elf.NewFile(obj)
322 if err != nil {
323 t.Fatal(err)
324 }
325 syms, err := ef.Symbols()
326 if err != nil {
327 t.Fatal(err)
328 }
329 for _, s := range syms {
330 switch s.Name {
331 case c1:
332 found1 = true
333 case c2:
334 found2 = true
335 }
336 }
337 }
338 }
339
340 if !foundgo {
341 t.Errorf(`go object not found`)
342 }
343 if !found1 {
344 t.Errorf(`symbol %q not found`, c1)
345 }
346 if !found2 {
347 t.Errorf(`symbol %q not found`, c2)
348 }
349 }
350
351 func TestExactly16Bytes(t *testing.T) {
352 var tests = []string{
353 "",
354 "a",
355 "日本語",
356 "1234567890123456",
357 "12345678901234567890",
358 "1234567890123本語4567890",
359 "12345678901234日本語567890",
360 "123456789012345日本語67890",
361 "1234567890123456日本語7890",
362 "1234567890123456日本語7日本語890",
363 }
364 for _, str := range tests {
365 got := exactly16Bytes(str)
366 if len(got) != 16 {
367 t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
368 }
369
370 for _, c := range got {
371 if c == utf8.RuneError {
372 t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
373 }
374 }
375 }
376 }
377
View as plain text