1
2
3
4
5 package macho
6
7 import (
8 "encoding/binary"
9 "fmt"
10 "io"
11 "os"
12 )
13
14
15 type FatFile struct {
16 Magic uint32
17 Arches []FatArch
18 closer io.Closer
19 }
20
21
22 type FatArchHeader struct {
23 Cpu Cpu
24 SubCpu uint32
25 Offset uint32
26 Size uint32
27 Align uint32
28 }
29
30 const fatArchHeaderSize = 5 * 4
31
32
33 type FatArch struct {
34 FatArchHeader
35 *File
36 }
37
38
39
40 var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil}
41
42
43
44
45 func NewFatFile(r io.ReaderAt) (*FatFile, error) {
46 var ff FatFile
47 sr := io.NewSectionReader(r, 0, 1<<63-1)
48
49
50
51 err := binary.Read(sr, binary.BigEndian, &ff.Magic)
52 if err != nil {
53 return nil, &FormatError{0, "error reading magic number", nil}
54 } else if ff.Magic != MagicFat {
55
56
57 var buf [4]byte
58 binary.BigEndian.PutUint32(buf[:], ff.Magic)
59 leMagic := binary.LittleEndian.Uint32(buf[:])
60 if leMagic == Magic32 || leMagic == Magic64 {
61 return nil, ErrNotFat
62 } else {
63 return nil, &FormatError{0, "invalid magic number", nil}
64 }
65 }
66 offset := int64(4)
67
68
69 var narch uint32
70 err = binary.Read(sr, binary.BigEndian, &narch)
71 if err != nil {
72 return nil, &FormatError{offset, "invalid fat_header", nil}
73 }
74 offset += 4
75
76 if narch < 1 {
77 return nil, &FormatError{offset, "file contains no images", nil}
78 }
79
80
81
82 seenArches := make(map[uint64]bool, narch)
83
84 var machoType Type
85
86
87
88 ff.Arches = make([]FatArch, narch)
89 for i := uint32(0); i < narch; i++ {
90 fa := &ff.Arches[i]
91 err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader)
92 if err != nil {
93 return nil, &FormatError{offset, "invalid fat_arch header", nil}
94 }
95 offset += fatArchHeaderSize
96
97 fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size))
98 fa.File, err = NewFile(fr)
99 if err != nil {
100 return nil, err
101 }
102
103
104 seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu)
105 if o, k := seenArches[seenArch]; o || k {
106 return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil}
107 }
108 seenArches[seenArch] = true
109
110
111 if i == 0 {
112 machoType = fa.Type
113 } else {
114 if fa.Type != machoType {
115 return nil, &FormatError{offset, fmt.Sprintf("Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", i, fa.Type, machoType), nil}
116 }
117 }
118 }
119
120 return &ff, nil
121 }
122
123
124
125 func OpenFat(name string) (*FatFile, error) {
126 f, err := os.Open(name)
127 if err != nil {
128 return nil, err
129 }
130 ff, err := NewFatFile(f)
131 if err != nil {
132 f.Close()
133 return nil, err
134 }
135 ff.closer = f
136 return ff, nil
137 }
138
139 func (ff *FatFile) Close() error {
140 var err error
141 if ff.closer != nil {
142 err = ff.closer.Close()
143 ff.closer = nil
144 }
145 return err
146 }
147
View as plain text