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