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
477 name := cstring(strtab[n.Name:])
478 if strings.Contains(name, ".") && name[0] == '_' {
479 name = name[1:]
480 }
481 sym.Name = name
482 sym.Type = n.Type
483 sym.Sect = n.Sect
484 sym.Desc = n.Desc
485 sym.Value = n.Value
486 }
487 st := new(Symtab)
488 st.LoadBytes = LoadBytes(cmddat)
489 st.Syms = symtab
490 return st, nil
491 }
492
493 type relocInfo struct {
494 Addr uint32
495 Symnum uint32
496 }
497
498 func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
499 f.Sections = append(f.Sections, sh)
500 sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
501 sh.ReaderAt = sh.sr
502
503 if sh.Nreloc > 0 {
504 reldat := make([]byte, int(sh.Nreloc)*8)
505 if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil {
506 return err
507 }
508 b := bytes.NewReader(reldat)
509
510 bo := f.ByteOrder
511
512 sh.Relocs = make([]Reloc, sh.Nreloc)
513 for i := range sh.Relocs {
514 rel := &sh.Relocs[i]
515
516 var ri relocInfo
517 if err := binary.Read(b, bo, &ri); err != nil {
518 return err
519 }
520
521 if ri.Addr&(1<<31) != 0 {
522 rel.Addr = ri.Addr & (1<<24 - 1)
523 rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
524 rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
525 rel.Pcrel = ri.Addr&(1<<30) != 0
526 rel.Value = ri.Symnum
527 rel.Scattered = true
528 } else {
529 switch bo {
530 case binary.LittleEndian:
531 rel.Addr = ri.Addr
532 rel.Value = ri.Symnum & (1<<24 - 1)
533 rel.Pcrel = ri.Symnum&(1<<24) != 0
534 rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
535 rel.Extern = ri.Symnum&(1<<27) != 0
536 rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
537 case binary.BigEndian:
538 rel.Addr = ri.Addr
539 rel.Value = ri.Symnum >> 8
540 rel.Pcrel = ri.Symnum&(1<<7) != 0
541 rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
542 rel.Extern = ri.Symnum&(1<<4) != 0
543 rel.Type = uint8(ri.Symnum & (1<<4 - 1))
544 default:
545 panic("unreachable")
546 }
547 }
548 }
549 }
550
551 return nil
552 }
553
554 func cstring(b []byte) string {
555 i := bytes.IndexByte(b, 0)
556 if i == -1 {
557 i = len(b)
558 }
559 return string(b[0:i])
560 }
561
562
563 func (f *File) Segment(name string) *Segment {
564 for _, l := range f.Loads {
565 if s, ok := l.(*Segment); ok && s.Name == name {
566 return s
567 }
568 }
569 return nil
570 }
571
572
573
574 func (f *File) Section(name string) *Section {
575 for _, s := range f.Sections {
576 if s.Name == name {
577 return s
578 }
579 }
580 return nil
581 }
582
583
584 func (f *File) DWARF() (*dwarf.Data, error) {
585 dwarfSuffix := func(s *Section) string {
586 switch {
587 case strings.HasPrefix(s.Name, "__debug_"):
588 return s.Name[8:]
589 case strings.HasPrefix(s.Name, "__zdebug_"):
590 return s.Name[9:]
591 default:
592 return ""
593 }
594
595 }
596 sectionData := func(s *Section) ([]byte, error) {
597 b, err := s.Data()
598 if err != nil && uint64(len(b)) < s.Size {
599 return nil, err
600 }
601
602 if len(b) >= 12 && string(b[:4]) == "ZLIB" {
603 dlen := binary.BigEndian.Uint64(b[4:12])
604 dbuf := make([]byte, dlen)
605 r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
606 if err != nil {
607 return nil, err
608 }
609 if _, err := io.ReadFull(r, dbuf); err != nil {
610 return nil, err
611 }
612 if err := r.Close(); err != nil {
613 return nil, err
614 }
615 b = dbuf
616 }
617 return b, nil
618 }
619
620
621
622
623 var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
624 for _, s := range f.Sections {
625 suffix := dwarfSuffix(s)
626 if suffix == "" {
627 continue
628 }
629 if _, ok := dat[suffix]; !ok {
630 continue
631 }
632 b, err := sectionData(s)
633 if err != nil {
634 return nil, err
635 }
636 dat[suffix] = b
637 }
638
639 d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
640 if err != nil {
641 return nil, err
642 }
643
644
645 for i, s := range f.Sections {
646 suffix := dwarfSuffix(s)
647 if suffix != "types" {
648 continue
649 }
650
651 b, err := sectionData(s)
652 if err != nil {
653 return nil, err
654 }
655
656 err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
657 if err != nil {
658 return nil, err
659 }
660 }
661
662 return d, nil
663 }
664
665
666
667
668 func (f *File) ImportedSymbols() ([]string, error) {
669 if f.Dysymtab == nil || f.Symtab == nil {
670 return nil, &FormatError{0, "missing symbol table", nil}
671 }
672
673 st := f.Symtab
674 dt := f.Dysymtab
675 var all []string
676 for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
677 all = append(all, s.Name)
678 }
679 return all, nil
680 }
681
682
683
684
685 func (f *File) ImportedLibraries() ([]string, error) {
686 var all []string
687 for _, l := range f.Loads {
688 if lib, ok := l.(*Dylib); ok {
689 all = append(all, lib.Name)
690 }
691 }
692 return all, nil
693 }
694
View as plain text