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