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 if hdr.Iundefsym > uint32(len(f.Symtab.Syms)) {
349 return nil, &FormatError{offset, fmt.Sprintf(
350 "undefined symbols index in dynamic symbol table command is greater than symbol table length (%d > %d)",
351 hdr.Iundefsym, len(f.Symtab.Syms)), nil}
352 } else if hdr.Iundefsym+hdr.Nundefsym > uint32(len(f.Symtab.Syms)) {
353 return nil, &FormatError{offset, fmt.Sprintf(
354 "number of undefined symbols after index in dynamic symbol table command is greater than symbol table length (%d > %d)",
355 hdr.Iundefsym+hdr.Nundefsym, len(f.Symtab.Syms)), nil}
356 }
357 dat := make([]byte, hdr.Nindirectsyms*4)
358 if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
359 return nil, err
360 }
361 x := make([]uint32, hdr.Nindirectsyms)
362 if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
363 return nil, err
364 }
365 st := new(Dysymtab)
366 st.LoadBytes = LoadBytes(cmddat)
367 st.DysymtabCmd = hdr
368 st.IndirectSyms = x
369 f.Loads[i] = st
370 f.Dysymtab = st
371
372 case LoadCmdSegment:
373 var seg32 Segment32
374 b := bytes.NewReader(cmddat)
375 if err := binary.Read(b, bo, &seg32); err != nil {
376 return nil, err
377 }
378 s = new(Segment)
379 s.LoadBytes = cmddat
380 s.Cmd = cmd
381 s.Len = siz
382 s.Name = cstring(seg32.Name[0:])
383 s.Addr = uint64(seg32.Addr)
384 s.Memsz = uint64(seg32.Memsz)
385 s.Offset = uint64(seg32.Offset)
386 s.Filesz = uint64(seg32.Filesz)
387 s.Maxprot = seg32.Maxprot
388 s.Prot = seg32.Prot
389 s.Nsect = seg32.Nsect
390 s.Flag = seg32.Flag
391 f.Loads[i] = s
392 for i := 0; i < int(s.Nsect); i++ {
393 var sh32 Section32
394 if err := binary.Read(b, bo, &sh32); err != nil {
395 return nil, err
396 }
397 sh := new(Section)
398 sh.Name = cstring(sh32.Name[0:])
399 sh.Seg = cstring(sh32.Seg[0:])
400 sh.Addr = uint64(sh32.Addr)
401 sh.Size = uint64(sh32.Size)
402 sh.Offset = sh32.Offset
403 sh.Align = sh32.Align
404 sh.Reloff = sh32.Reloff
405 sh.Nreloc = sh32.Nreloc
406 sh.Flags = sh32.Flags
407 if err := f.pushSection(sh, r); err != nil {
408 return nil, err
409 }
410 }
411
412 case LoadCmdSegment64:
413 var seg64 Segment64
414 b := bytes.NewReader(cmddat)
415 if err := binary.Read(b, bo, &seg64); err != nil {
416 return nil, err
417 }
418 s = new(Segment)
419 s.LoadBytes = cmddat
420 s.Cmd = cmd
421 s.Len = siz
422 s.Name = cstring(seg64.Name[0:])
423 s.Addr = seg64.Addr
424 s.Memsz = seg64.Memsz
425 s.Offset = seg64.Offset
426 s.Filesz = seg64.Filesz
427 s.Maxprot = seg64.Maxprot
428 s.Prot = seg64.Prot
429 s.Nsect = seg64.Nsect
430 s.Flag = seg64.Flag
431 f.Loads[i] = s
432 for i := 0; i < int(s.Nsect); i++ {
433 var sh64 Section64
434 if err := binary.Read(b, bo, &sh64); err != nil {
435 return nil, err
436 }
437 sh := new(Section)
438 sh.Name = cstring(sh64.Name[0:])
439 sh.Seg = cstring(sh64.Seg[0:])
440 sh.Addr = sh64.Addr
441 sh.Size = sh64.Size
442 sh.Offset = sh64.Offset
443 sh.Align = sh64.Align
444 sh.Reloff = sh64.Reloff
445 sh.Nreloc = sh64.Nreloc
446 sh.Flags = sh64.Flags
447 if err := f.pushSection(sh, r); err != nil {
448 return nil, err
449 }
450 }
451 }
452 if s != nil {
453 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
454 s.ReaderAt = s.sr
455 }
456 }
457 return f, nil
458 }
459
460 func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
461 bo := f.ByteOrder
462 symtab := make([]Symbol, hdr.Nsyms)
463 b := bytes.NewReader(symdat)
464 for i := range symtab {
465 var n Nlist64
466 if f.Magic == Magic64 {
467 if err := binary.Read(b, bo, &n); err != nil {
468 return nil, err
469 }
470 } else {
471 var n32 Nlist32
472 if err := binary.Read(b, bo, &n32); err != nil {
473 return nil, err
474 }
475 n.Name = n32.Name
476 n.Type = n32.Type
477 n.Sect = n32.Sect
478 n.Desc = n32.Desc
479 n.Value = uint64(n32.Value)
480 }
481 sym := &symtab[i]
482 if n.Name >= uint32(len(strtab)) {
483 return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
484 }
485
486 name := cstring(strtab[n.Name:])
487 if strings.Contains(name, ".") && name[0] == '_' {
488 name = name[1:]
489 }
490 sym.Name = name
491 sym.Type = n.Type
492 sym.Sect = n.Sect
493 sym.Desc = n.Desc
494 sym.Value = n.Value
495 }
496 st := new(Symtab)
497 st.LoadBytes = LoadBytes(cmddat)
498 st.Syms = symtab
499 return st, nil
500 }
501
502 type relocInfo struct {
503 Addr uint32
504 Symnum uint32
505 }
506
507 func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
508 f.Sections = append(f.Sections, sh)
509 sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
510 sh.ReaderAt = sh.sr
511
512 if sh.Nreloc > 0 {
513 reldat := make([]byte, int(sh.Nreloc)*8)
514 if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil {
515 return err
516 }
517 b := bytes.NewReader(reldat)
518
519 bo := f.ByteOrder
520
521 sh.Relocs = make([]Reloc, sh.Nreloc)
522 for i := range sh.Relocs {
523 rel := &sh.Relocs[i]
524
525 var ri relocInfo
526 if err := binary.Read(b, bo, &ri); err != nil {
527 return err
528 }
529
530 if ri.Addr&(1<<31) != 0 {
531 rel.Addr = ri.Addr & (1<<24 - 1)
532 rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
533 rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
534 rel.Pcrel = ri.Addr&(1<<30) != 0
535 rel.Value = ri.Symnum
536 rel.Scattered = true
537 } else {
538 switch bo {
539 case binary.LittleEndian:
540 rel.Addr = ri.Addr
541 rel.Value = ri.Symnum & (1<<24 - 1)
542 rel.Pcrel = ri.Symnum&(1<<24) != 0
543 rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
544 rel.Extern = ri.Symnum&(1<<27) != 0
545 rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
546 case binary.BigEndian:
547 rel.Addr = ri.Addr
548 rel.Value = ri.Symnum >> 8
549 rel.Pcrel = ri.Symnum&(1<<7) != 0
550 rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
551 rel.Extern = ri.Symnum&(1<<4) != 0
552 rel.Type = uint8(ri.Symnum & (1<<4 - 1))
553 default:
554 panic("unreachable")
555 }
556 }
557 }
558 }
559
560 return nil
561 }
562
563 func cstring(b []byte) string {
564 i := bytes.IndexByte(b, 0)
565 if i == -1 {
566 i = len(b)
567 }
568 return string(b[0:i])
569 }
570
571
572 func (f *File) Segment(name string) *Segment {
573 for _, l := range f.Loads {
574 if s, ok := l.(*Segment); ok && s.Name == name {
575 return s
576 }
577 }
578 return nil
579 }
580
581
582
583 func (f *File) Section(name string) *Section {
584 for _, s := range f.Sections {
585 if s.Name == name {
586 return s
587 }
588 }
589 return nil
590 }
591
592
593 func (f *File) DWARF() (*dwarf.Data, error) {
594 dwarfSuffix := func(s *Section) string {
595 switch {
596 case strings.HasPrefix(s.Name, "__debug_"):
597 return s.Name[8:]
598 case strings.HasPrefix(s.Name, "__zdebug_"):
599 return s.Name[9:]
600 default:
601 return ""
602 }
603
604 }
605 sectionData := func(s *Section) ([]byte, error) {
606 b, err := s.Data()
607 if err != nil && uint64(len(b)) < s.Size {
608 return nil, err
609 }
610
611 if len(b) >= 12 && string(b[:4]) == "ZLIB" {
612 dlen := binary.BigEndian.Uint64(b[4:12])
613 dbuf := make([]byte, dlen)
614 r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
615 if err != nil {
616 return nil, err
617 }
618 if _, err := io.ReadFull(r, dbuf); err != nil {
619 return nil, err
620 }
621 if err := r.Close(); err != nil {
622 return nil, err
623 }
624 b = dbuf
625 }
626 return b, nil
627 }
628
629
630
631
632 var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
633 for _, s := range f.Sections {
634 suffix := dwarfSuffix(s)
635 if suffix == "" {
636 continue
637 }
638 if _, ok := dat[suffix]; !ok {
639 continue
640 }
641 b, err := sectionData(s)
642 if err != nil {
643 return nil, err
644 }
645 dat[suffix] = b
646 }
647
648 d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
649 if err != nil {
650 return nil, err
651 }
652
653
654 for i, s := range f.Sections {
655 suffix := dwarfSuffix(s)
656 if suffix == "" {
657 continue
658 }
659 if _, ok := dat[suffix]; ok {
660
661 continue
662 }
663
664 b, err := sectionData(s)
665 if err != nil {
666 return nil, err
667 }
668
669 if suffix == "types" {
670 err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
671 } else {
672 err = d.AddSection(".debug_"+suffix, b)
673 }
674 if err != nil {
675 return nil, err
676 }
677 }
678
679 return d, nil
680 }
681
682
683
684
685 func (f *File) ImportedSymbols() ([]string, error) {
686 if f.Dysymtab == nil || f.Symtab == nil {
687 return nil, &FormatError{0, "missing symbol table", nil}
688 }
689
690 st := f.Symtab
691 dt := f.Dysymtab
692 var all []string
693 for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
694 all = append(all, s.Name)
695 }
696 return all, nil
697 }
698
699
700
701
702 func (f *File) ImportedLibraries() ([]string, error) {
703 var all []string
704 for _, l := range f.Loads {
705 if lib, ok := l.(*Dylib); ok {
706 all = append(all, lib.Name)
707 }
708 }
709 return all, nil
710 }
711
View as plain text