Left: | ||
Right: |
OLD | NEW |
---|---|
(Empty) | |
1 /* Copyright 2014, Google Inc. | |
2 All rights reserved. | |
3 | |
4 Redistribution and use in source and binary forms, with or without | |
5 modification, are permitted provided that the following conditions are | |
6 met: | |
7 | |
8 * Redistributions of source code must retain the above copyright | |
9 notice, this list of conditions and the following disclaimer. | |
10 * Redistributions in binary form must reproduce the above | |
11 copyright notice, this list of conditions and the following disclaimer | |
12 in the documentation and/or other materials provided with the | |
13 distribution. | |
14 * Neither the name of Google Inc. nor the names of its | |
15 contributors may be used to endorse or promote products derived from | |
16 this software without specific prior written permission. | |
17 | |
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
31 package main | |
32 | |
33 import ( | |
34 "encoding/binary" | |
35 "errors" | |
36 "fmt" | |
37 "os" | |
38 "reflect" | |
39 "unsafe" | |
40 ) | |
41 | |
42 /* | |
43 #include <mach-o/fat.h> | |
44 #include <mach-o/loader.h> | |
45 #include <string.h> | |
46 | |
47 #include "arch_constants.h" | |
48 */ | |
49 import "C" | |
50 | |
51 var ( | |
52 ErrNotMachO = errors.New("GetMachOImageInfo: file is not a suppor ted Mach-O image") | |
53 ErrUnsupportedArch = errors.New("GetMachOImageInfo: unknown architecture detected") | |
54 ) | |
55 | |
56 const ( | |
57 ArchI386 = "i386" | |
58 ArchX86_64 = "x86_64" | |
59 ) | |
60 | |
61 type MachOType int | |
62 | |
63 const ( | |
64 MachODylib MachOType = C.kMachHeaderFtypeDylib | |
65 MachOBundle = C.kMachHeaderFtypeBundle | |
66 MachOExe = C.kMachHeaderFtypeExe | |
67 ) | |
68 | |
69 type ImageInfo struct { | |
70 Type MachOType | |
71 Arch string | |
72 } | |
73 | |
74 // GetMachOImageInfo will read the file at filepath and determine if it is | |
75 // Mach-O. If it is, it will return a slice of ImageInfo that describe the | |
76 // images in the file (may be more than one if it is a fat image). | |
77 func GetMachOImageInfo(filepath string) ([]ImageInfo, error) { | |
78 f, err := os.Open(filepath) | |
79 if err != nil { | |
80 return nil, err | |
81 } | |
82 defer f.Close() | |
83 | |
84 // Read the magic number to determine the type of file this is. | |
85 var magic uint32 | |
86 err = binary.Read(f, binary.LittleEndian, &magic) | |
87 if err != nil { | |
88 return nil, err | |
89 } | |
90 | |
91 // Rewind the file since the magic number is a field in the header | |
92 // structs. | |
93 f.Seek(0, 0) | |
Mark Mentovai
2014/02/03 22:01:46
You said “done” but you didn’t actually fix this.
rsesek
2014/02/03 22:36:20
Done for realz.
| |
94 | |
95 switch magic { | |
96 case C.kMachHeaderMagic32: | |
97 return readThin32Header(f) | |
98 case C.kMachHeaderMagic64: | |
99 return readThin64Header(f) | |
100 case C.kMachHeaderCigamFat: // Fat header is big-endian but was read in little. | |
101 return readFatHeader(f) | |
102 } | |
103 | |
104 return nil, ErrNotMachO | |
105 } | |
106 | |
107 func readThin32Header(f *os.File) ([]ImageInfo, error) { | |
108 var machHeader C.struct_mach_header | |
109 err := readStruct(f, binary.LittleEndian, unsafe.Pointer(&machHeader), C .struct_mach_header{}) | |
110 if err != nil { | |
111 return nil, err | |
112 } | |
113 | |
114 if machHeader.magic != C.kMachHeaderMagic32 { | |
115 return nil, fmt.Errorf("readThin32Header: unexpected magic numbe r %#x", machHeader.magic) | |
116 } | |
117 | |
118 arch := cpuTypeToArch(machHeader.cputype) | |
119 if arch == "" { | |
120 return nil, ErrUnsupportedArch | |
121 } | |
122 return []ImageInfo{{MachOType(machHeader.filetype), arch}}, nil | |
123 } | |
124 | |
125 func readThin64Header(f *os.File) ([]ImageInfo, error) { | |
126 var machHeader C.struct_mach_header_64 | |
127 err := readStruct(f, binary.LittleEndian, unsafe.Pointer(&machHeader), C .struct_mach_header_64{}) | |
128 if err != nil { | |
129 return nil, err | |
130 } | |
131 | |
132 if machHeader.magic != C.kMachHeaderMagic64 { | |
133 return nil, fmt.Errorf("readThin64Header: unexpected magic numbe r %#x", machHeader.magic) | |
134 } | |
135 | |
136 arch := cpuTypeToArch(machHeader.cputype) | |
137 if arch == "" { | |
138 return nil, ErrUnsupportedArch | |
139 } | |
140 return []ImageInfo{{MachOType(machHeader.filetype), arch}}, nil | |
141 } | |
142 | |
143 func readFatHeader(f *os.File) ([]ImageInfo, error) { | |
144 var fatHeader C.struct_fat_header | |
145 err := readStruct(f, binary.BigEndian, unsafe.Pointer(&fatHeader), C.str uct_fat_header{}) | |
146 if err != nil { | |
147 return nil, err | |
148 } | |
149 | |
150 if fatHeader.magic != C.kMachHeaderMagicFat { | |
151 return nil, fmt.Errorf("readFatHeader: unexpected magic number % #x", fatHeader.magic) | |
152 } | |
153 | |
154 // Read the fat_arch headers. | |
155 headers := make([]C.struct_fat_arch, fatHeader.nfat_arch) | |
156 for i := 0; i < int(fatHeader.nfat_arch); i++ { | |
157 var fatArch C.struct_fat_arch | |
158 err = readStruct(f, binary.BigEndian, unsafe.Pointer(&fatArch), C.struct_fat_arch{}) | |
159 if err != nil { | |
160 return nil, fmt.Errorf("readFatHeader: %v", err) | |
161 } | |
162 headers[i] = fatArch | |
163 } | |
164 | |
165 seenArches := make(map[string]int) | |
166 | |
167 // Now go to each arch in the fat image and read its mach header. | |
168 infos := make([]ImageInfo, 0, len(headers)) | |
169 for _, header := range headers { | |
170 f.Seek(int64(header.offset), os.SEEK_SET) | |
171 | |
172 var thinarch []ImageInfo | |
173 var expectedArch string | |
174 switch header.cputype { | |
175 case C.kCPUType_i386: | |
176 thinarch, err = readThin32Header(f) | |
177 expectedArch = ArchI386 | |
178 case C.kCPUType_x86_64: | |
179 thinarch, err = readThin64Header(f) | |
180 expectedArch = ArchX86_64 | |
181 default: | |
182 err = ErrUnsupportedArch | |
183 } | |
184 | |
185 if thinarch[0].Arch != expectedArch { | |
186 return nil, fmt.Errorf("readFatHeader: expected arch %d, got %d", thinarch[0].Arch, expectedArch) | |
187 } | |
188 | |
189 if err != nil { | |
190 return nil, err | |
191 } | |
192 | |
193 infos = append(infos, thinarch[0]) | |
194 seenArches[thinarch[0].Arch]++ | |
195 } | |
196 | |
197 for arch, count := range seenArches { | |
198 if count != 1 { | |
199 return nil, fmt.Errorf("readFatHeader: duplicate arch %s detected", arch) | |
200 } | |
201 } | |
202 | |
203 return infos, nil | |
204 } | |
205 | |
206 // TODO(rsesek): Support more arches. | |
207 func cpuTypeToArch(cpu C.cpu_type_t) string { | |
208 switch cpu { | |
209 case C.kCPUType_i386: | |
210 return ArchI386 | |
211 case C.kCPUType_x86_64: | |
212 return ArchX86_64 | |
213 default: | |
214 return "" | |
215 } | |
216 } | |
217 | |
218 // readStruct is a incomplete version of binary.Read that uses unsafe pointers | |
219 // to set values in unexported fields. From |f|, this will read the fields of | |
220 // the |destType| template instance, in the specified byte |order|, and place | |
221 // the resulting memory into |dest|. | |
222 func readStruct(f *os.File, order binary.ByteOrder, dest unsafe.Pointer, destTyp e interface{}) error { | |
223 rv := reflect.ValueOf(destType) | |
224 rt := rv.Type() | |
225 destPtr := uintptr(dest) | |
226 | |
227 for i := 0; i < rv.NumField(); i++ { | |
228 field := rv.Field(i) | |
229 fieldType := rt.Field(i) | |
230 | |
231 var vp unsafe.Pointer | |
232 var err error | |
233 | |
234 switch field.Kind() { | |
235 case reflect.Int32: | |
236 var v int32 | |
237 vp = unsafe.Pointer(&v) | |
238 err = binary.Read(f, order, &v) | |
239 case reflect.Uint32: | |
240 var v uint32 | |
241 vp = unsafe.Pointer(&v) | |
242 err = binary.Read(f, order, &v) | |
243 default: | |
244 err = fmt.Errorf("readStruct: unsupported type %v", fiel dType) | |
245 } | |
246 | |
247 if err != nil { | |
248 return err | |
249 } | |
250 | |
251 memcpy(destPtr+fieldType.Offset, vp, fieldType.Type.Size()) | |
252 } | |
253 return nil | |
254 } | |
255 | |
256 func memcpy(dest uintptr, value unsafe.Pointer, size uintptr) { | |
257 C.memcpy(unsafe.Pointer(dest), value, C.size_t(size)) | |
258 } | |
OLD | NEW |