Source file src/pkg/debug/macho/file.go
1
2
3
4
5
6 package macho
7
8
9
10 import (
11 "bytes"
12 "compress/zlib"
13 "debug/dwarf"
14 "encoding/binary"
15 "fmt"
16 "io"
17 "os"
18 "strings"
19 )
20
21
22 type File struct {
23 FileHeader
24 ByteOrder binary.ByteOrder
25 Loads []Load
26 Sections []*Section
27
28 Symtab *Symtab
29 Dysymtab *Dysymtab
30
31 closer io.Closer
32 }
33
34
35 type Load interface {
36 Raw() []byte
37 }
38
39
40 type LoadBytes []byte
41
42 func (b LoadBytes) Raw() []byte { return b }
43
44
45 type SegmentHeader struct {
46 Cmd LoadCmd
47 Len uint32
48 Name string
49 Addr uint64
50 Memsz uint64
51 Offset uint64
52 Filesz uint64
53 Maxprot uint32
54 Prot uint32
55 Nsect uint32
56 Flag uint32
57 }
58
59
60 type Segment struct {
61 LoadBytes
62 SegmentHeader
63
64
65
66
67
68
69
70 io.ReaderAt
71 sr *io.SectionReader
72 }
73
74
75 func (s *Segment) Data() ([]byte, error) {
76 dat := make([]byte, s.sr.Size())
77 n, err := s.sr.ReadAt(dat, 0)
78 if n == len(dat) {
79 err = nil
80 }
81 return dat[0:n], err
82 }
83
84
85 func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
86
87 type SectionHeader struct {
88 Name string
89 Seg string
90 Addr uint64
91 Size uint64
92 Offset uint32
93 Align uint32
94 Reloff uint32
95 Nreloc uint32
96 Flags uint32
97 }
98
99
100 type Reloc struct {
101 Addr uint32
102 Value uint32
103
104
105
106 Type uint8
107 Len uint8
108 Pcrel bool
109 Extern bool
110 Scattered bool
111 }
112
113 type Section struct {
114 SectionHeader
115 Relocs []Reloc
116
117
118
119
120
121
122
123 io.ReaderAt
124 sr *io.SectionReader
125 }
126
127
128 func (s *Section) Data() ([]byte, error) {
129 dat := make([]byte, s.sr.Size())
130 n, err := s.sr.ReadAt(dat, 0)
131 if n == len(dat) {
132 err = nil
133 }
134 return dat[0:n], err
135 }
136
137
138 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
139
140
141 type Dylib struct {
142 LoadBytes
143 Name string
144 Time uint32
145 CurrentVersion uint32
146 CompatVersion uint32
147 }
148
149
150 type Symtab struct {
151 LoadBytes
152 SymtabCmd
153 Syms []Symbol
154 }
155
156
157 type Dysymtab struct {
158 LoadBytes
159 DysymtabCmd
160 IndirectSyms []uint32
161 }
162
163
164 type Rpath struct {
165 LoadBytes
166 Path string
167 }
168
169
170 type Symbol struct {
171 Name string
172 Type uint8
173 Sect uint8
174 Desc uint16
175 Value uint64
176 }
177
178
181
182
183
184 type FormatError struct {
185 off int64
186 msg string
187 val interface{}
188 }
189
190 func (e *FormatError) Error() string {
191 msg := e.msg
192 if e.val != nil {
193 msg += fmt.Sprintf(" '%v'", e.val)
194 }
195 msg += fmt.Sprintf(" in record at byte %#x", e.off)
196 return msg
197 }
198
199
200 func Open(name string) (*File, error) {
201 f, err := os.Open(name)
202 if err != nil {
203 return nil, err
204 }
205 ff, err := NewFile(f)
206 if err != nil {
207 f.Close()
208 return nil, err
209 }
210 ff.closer = f
211 return ff, nil
212 }
213
214
215
216
217 func (f *File) Close() error {
218 var err error
219 if f.closer != nil {
220 err = f.closer.Close()
221 f.closer = nil
222 }
223 return err
224 }
225
226
227
228 func NewFile(r io.ReaderAt) (*File, error) {
229 f := new(File)
230 sr := io.NewSectionReader(r, 0, 1<<63-1)
231
232
233
234 var ident [4]byte
235 if _, err := r.ReadAt(ident[0:], 0); err != nil {
236 return nil, err
237 }
238 be := binary.BigEndian.Uint32(ident[0:])
239 le := binary.LittleEndian.Uint32(ident[0:])
240 switch Magic32 &^ 1 {
241 case be &^ 1:
242 f.ByteOrder = binary.BigEndian
243 f.Magic = be
244 case le &^ 1:
245 f.ByteOrder = binary.LittleEndian
246 f.Magic = le
247 default:
248 return nil, &FormatError{0, "invalid magic number", nil}
249 }
250
251
252 if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil {
253 return nil, err
254 }
255
256
257 offset := int64(fileHeaderSize32)
258 if f.Magic == Magic64 {
259 offset = fileHeaderSize64
260 }
261 dat := make([]byte, f.Cmdsz)
262 if _, err := r.ReadAt(dat, offset); err != nil {
263 return nil, err
264 }
265 f.Loads = make([]Load, f.Ncmd)
266 bo := f.ByteOrder
267 for i := range f.Loads {
268
269 if len(dat) < 8 {
270 return nil, &FormatError{offset, "command block too small", nil}
271 }
272 cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
273 if siz < 8 || siz > uint32(len(dat)) {
274 return nil, &FormatError{offset, "invalid command block size", nil}
275 }
276 var cmddat []byte
277 cmddat, dat = dat[0:siz], dat[siz:]
278 offset += int64(siz)
279 var s *Segment
280 switch cmd {
281 default:
282 f.Loads[i] = LoadBytes(cmddat)
283
284 case LoadCmdRpath:
285 var hdr RpathCmd
286 b := bytes.NewReader(cmddat)
287 if err := binary.Read(b, bo, &hdr); err != nil {
288 return nil, err
289 }
290 l := new(Rpath)
291 if hdr.Path >= uint32(len(cmddat)) {
292 return nil, &FormatError{offset, "invalid path in rpath command", hdr.Path}
293 }
294 l.Path = cstring(cmddat[hdr.Path:])
295 l.LoadBytes = LoadBytes(cmddat)
296 f.Loads[i] = l
297
298 case LoadCmdDylib:
299 var hdr DylibCmd
300 b := bytes.NewReader(cmddat)
301 if err := binary.Read(b, bo, &hdr); err != nil {
302 return nil, err
303 }
304 l := new(Dylib)
305 if hdr.Name >= uint32(len(cmddat)) {
306 return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
307 }
308 l.Name = cstring(cmddat[hdr.Name:])
309 l.Time = hdr.Time
310 l.CurrentVersion = hdr.CurrentVersion
311 l.CompatVersion = hdr.CompatVersion
312 l.LoadBytes = LoadBytes(cmddat)
313 f.Loads[i] = l
314
315 case LoadCmdSymtab:
316 var hdr SymtabCmd
317 b := bytes.NewReader(cmddat)
318 if err := binary.Read(b, bo, &hdr); err != nil {
319 return nil, err
320 }
321 strtab := make([]byte, hdr.Strsize)
322 if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
323 return nil, err
324 }
325 var symsz int
326 if f.Magic == Magic64 {
327 symsz = 16
328 } else {
329 symsz = 12
330 }
331 symdat := make([]byte, int(hdr.Nsyms)*symsz)
332 if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil {
333 return nil, err
334 }
335 st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
336 if err != nil {
337 return nil, err
338 }
339 f.Loads[i] = st
340 f.Symtab = st
341
342 case LoadCmdDysymtab:
343 var hdr DysymtabCmd
344 b := bytes.NewReader(cmddat)
345 if err := binary.Read(b, bo, &hdr); err != nil {
346 return nil, err
347 }
348 dat := make([]byte, hdr.Nindirectsyms*4)
349 if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
350 return nil, err
351 }
352 x := make([]uint32, hdr.Nindirectsyms)
353 if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
354 return nil, err
355 }
356 st := new(Dysymtab)
357 st.LoadBytes = LoadBytes(cmddat)
358 st.DysymtabCmd = hdr
359 st.IndirectSyms = x
360 f.Loads[i] = st
361 f.Dysymtab = st
362
363 case LoadCmdSegment:
364 var seg32 Segment32
365 b := bytes.NewReader(cmddat)
366 if err := binary.Read(b, bo, &seg32); err != nil {
367 return nil, err
368 }
369 s = new(Segment)
370 s.LoadBytes = cmddat
371 s.Cmd = cmd
372 s.Len = siz
373 s.Name = cstring(seg32.Name[0:])
374 s.Addr = uint64(seg32.Addr)
375 s.Memsz = uint64(seg32.Memsz)
376 s.Offset = uint64(seg32.Offset)
377 s.Filesz = uint64(seg32.Filesz)
378 s.Maxprot = seg32.Maxprot
379 s.Prot = seg32.Prot
380 s.Nsect = seg32.Nsect
381 s.Flag = seg32.Flag
382 f.Loads[i] = s
383 for i := 0; i < int(s.Nsect); i++ {
384 var sh32 Section32
385 if err := binary.Read(b, bo, &sh32); err != nil {
386 return nil, err
387 }
388 sh := new(Section)
389 sh.Name = cstring(sh32.Name[0:])
390 sh.Seg = cstring(sh32.Seg[0:])
391 sh.Addr = uint64(sh32.Addr)
392 sh.Size = uint64(sh32.Size)
393 sh.Offset = sh32.Offset
394 sh.Align = sh32.Align
395 sh.Reloff = sh32.Reloff
396 sh.Nreloc = sh32.Nreloc
397 sh.Flags = sh32.Flags
398 if err := f.pushSection(sh, r); err != nil {
399 return nil, err
400 }
401 }
402
403 case LoadCmdSegment64:
404 var seg64 Segment64
405 b := bytes.NewReader(cmddat)
406 if err := binary.Read(b, bo, &seg64); err != nil {
407 return nil, err
408 }
409 s = new(Segment)
410 s.LoadBytes = cmddat
411 s.Cmd = cmd
412 s.Len = siz
413 s.Name = cstring(seg64.Name[0:])
414 s.Addr = seg64.Addr
415 s.Memsz = seg64.Memsz
416 s.Offset = seg64.Offset
417 s.Filesz = seg64.Filesz
418 s.Maxprot = seg64.Maxprot
419 s.Prot = seg64.Prot
420 s.Nsect = seg64.Nsect
421 s.Flag = seg64.Flag
422 f.Loads[i] = s
423 for i := 0; i < int(s.Nsect); i++ {
424 var sh64 Section64
425 if err := binary.Read(b, bo, &sh64); err != nil {
426 return nil, err
427 }
428 sh := new(Section)
429 sh.Name = cstring(sh64.Name[0:])
430 sh.Seg = cstring(sh64.Seg[0:])
431 sh.Addr = sh64.Addr
432 sh.Size = sh64.Size
433 sh.Offset = sh64.Offset
434 sh.Align = sh64.Align
435 sh.Reloff = sh64.Reloff
436 sh.Nreloc = sh64.Nreloc
437 sh.Flags = sh64.Flags
438 if err := f.pushSection(sh, r); err != nil {
439 return nil, err
440 }
441 }
442 }
443 if s != nil {
444 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
445 s.ReaderAt = s.sr
446 }
447 }
448 return f, nil
449 }
450
451 func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
452 bo := f.ByteOrder
453 symtab := make([]Symbol, hdr.Nsyms)
454 b := bytes.NewReader(symdat)
455 for i := range symtab {
456 var n Nlist64
457 if f.Magic == Magic64 {
458 if err := binary.Read(b, bo, &n); err != nil {
459 return nil, err
460 }
461 } else {
462 var n32 Nlist32
463 if err := binary.Read(b, bo, &n32); err != nil {
464 return nil, err
465 }
466 n.Name = n32.Name
467 n.Type = n32.Type
468 n.Sect = n32.Sect
469 n.Desc = n32.Desc
470 n.Value = uint64(n32.Value)
471 }
472 sym := &symtab[i]
473 if n.Name >= uint32(len(strtab)) {
474 return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
475 }
476 sym.Name = cstring(strtab[n.Name:])
477 sym.Type = n.Type
478 sym.Sect = n.Sect
479 sym.Desc = n.Desc
480 sym.Value = n.Value
481 }
482 st := new(Symtab)
483 st.LoadBytes = LoadBytes(cmddat)
484 st.Syms = symtab
485 return st, nil
486 }
487
488 type relocInfo struct {
489 Addr uint32
490 Symnum uint32
491 }
492
493 func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
494 f.Sections = append(f.Sections, sh)
495 sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
496 sh.ReaderAt = sh.sr
497
498 if sh.Nreloc > 0 {
499 reldat := make([]byte, int(sh.Nreloc)*8)
500 if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil {
501 return err
502 }
503 b := bytes.NewReader(reldat)
504
505 bo := f.ByteOrder
506
507 sh.Relocs = make([]Reloc, sh.Nreloc)
508 for i := range sh.Relocs {
509 rel := &sh.Relocs[i]
510
511 var ri relocInfo
512 if err := binary.Read(b, bo, &ri); err != nil {
513 return err
514 }
515
516 if ri.Addr&(1<<31) != 0 {
517 rel.Addr = ri.Addr & (1<<24 - 1)
518 rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
519 rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
520 rel.Pcrel = ri.Addr&(1<<30) != 0
521 rel.Value = ri.Symnum
522 rel.Scattered = true
523 } else {
524 switch bo {
525 case binary.LittleEndian:
526 rel.Addr = ri.Addr
527 rel.Value = ri.Symnum & (1<<24 - 1)
528 rel.Pcrel = ri.Symnum&(1<<24) != 0
529 rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
530 rel.Extern = ri.Symnum&(1<<27) != 0
531 rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
532 case binary.BigEndian:
533 rel.Addr = ri.Addr
534 rel.Value = ri.Symnum >> 8
535 rel.Pcrel = ri.Symnum&(1<<7) != 0
536 rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
537 rel.Extern = ri.Symnum&(1<<4) != 0
538 rel.Type = uint8(ri.Symnum & (1<<4 - 1))
539 default:
540 panic("unreachable")
541 }
542 }
543 }
544 }
545
546 return nil
547 }
548
549 func cstring(b []byte) string {
550 i := bytes.IndexByte(b, 0)
551 if i == -1 {
552 i = len(b)
553 }
554 return string(b[0:i])
555 }
556
557
558 func (f *File) Segment(name string) *Segment {
559 for _, l := range f.Loads {
560 if s, ok := l.(*Segment); ok && s.Name == name {
561 return s
562 }
563 }
564 return nil
565 }
566
567
568
569 func (f *File) Section(name string) *Section {
570 for _, s := range f.Sections {
571 if s.Name == name {
572 return s
573 }
574 }
575 return nil
576 }
577
578
579 func (f *File) DWARF() (*dwarf.Data, error) {
580 dwarfSuffix := func(s *Section) string {
581 switch {
582 case strings.HasPrefix(s.Name, "__debug_"):
583 return s.Name[8:]
584 case strings.HasPrefix(s.Name, "__zdebug_"):
585 return s.Name[9:]
586 default:
587 return ""
588 }
589
590 }
591 sectionData := func(s *Section) ([]byte, error) {
592 b, err := s.Data()
593 if err != nil && uint64(len(b)) < s.Size {
594 return nil, err
595 }
596
597 if len(b) >= 12 && string(b[:4]) == "ZLIB" {
598 dlen := binary.BigEndian.Uint64(b[4:12])
599 dbuf := make([]byte, dlen)
600 r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
601 if err != nil {
602 return nil, err
603 }
604 if _, err := io.ReadFull(r, dbuf); err != nil {
605 return nil, err
606 }
607 if err := r.Close(); err != nil {
608 return nil, err
609 }
610 b = dbuf
611 }
612 return b, nil
613 }
614
615
616
617
618 var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
619 for _, s := range f.Sections {
620 suffix := dwarfSuffix(s)
621 if suffix == "" {
622 continue
623 }
624 if _, ok := dat[suffix]; !ok {
625 continue
626 }
627 b, err := sectionData(s)
628 if err != nil {
629 return nil, err
630 }
631 dat[suffix] = b
632 }
633
634 d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
635 if err != nil {
636 return nil, err
637 }
638
639
640 for i, s := range f.Sections {
641 suffix := dwarfSuffix(s)
642 if suffix != "types" {
643 continue
644 }
645
646 b, err := sectionData(s)
647 if err != nil {
648 return nil, err
649 }
650
651 err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
652 if err != nil {
653 return nil, err
654 }
655 }
656
657 return d, nil
658 }
659
660
661
662
663 func (f *File) ImportedSymbols() ([]string, error) {
664 if f.Dysymtab == nil || f.Symtab == nil {
665 return nil, &FormatError{0, "missing symbol table", nil}
666 }
667
668 st := f.Symtab
669 dt := f.Dysymtab
670 var all []string
671 for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
672 all = append(all, s.Name)
673 }
674 return all, nil
675 }
676
677
678
679
680 func (f *File) ImportedLibraries() ([]string, error) {
681 var all []string
682 for _, l := range f.Loads {
683 if lib, ok := l.(*Dylib); ok {
684 all = append(all, lib.Name)
685 }
686 }
687 return all, nil
688 }
689
View as plain text