]>
Commit | Line | Data |
---|---|---|
d696c285 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
a645023d | 3 | * Copyright (c) 2006-2010 Apple Inc. All rights reserved. |
d696c285 A |
4 | * |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #include <sys/types.h> | |
26 | #include <sys/stat.h> | |
27 | #include <sys/mman.h> | |
28 | #include <stdarg.h> | |
29 | #include <stdio.h> | |
ec29ba20 | 30 | #include <stdlib.h> |
d696c285 A |
31 | #include <fcntl.h> |
32 | #include <unistd.h> | |
55e3d2f6 | 33 | #include <errno.h> |
d696c285 A |
34 | |
35 | #include <vector> | |
a61fdf0a | 36 | #include <set> |
d425e388 A |
37 | #include <unordered_set> |
38 | ||
39 | #include "configure.h" | |
d696c285 A |
40 | |
41 | #include "MachOFileAbstraction.hpp" | |
42 | #include "Architectures.hpp" | |
43 | ||
44 | ||
45 | __attribute__((noreturn)) | |
46 | void throwf(const char* format, ...) | |
47 | { | |
48 | va_list list; | |
49 | char* p; | |
50 | va_start(list, format); | |
51 | vasprintf(&p, format, list); | |
52 | va_end(list); | |
53 | ||
54 | const char* t = p; | |
55 | throw t; | |
56 | } | |
57 | ||
a645023d A |
58 | static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) |
59 | { | |
60 | uint64_t result = 0; | |
61 | int bit = 0; | |
62 | do { | |
63 | if (p == end) | |
64 | throwf("malformed uleb128"); | |
65 | ||
66 | uint64_t slice = *p & 0x7f; | |
67 | ||
68 | if (bit >= 64 || slice << bit >> bit != slice) | |
69 | throwf("uleb128 too big"); | |
70 | else { | |
71 | result |= (slice << bit); | |
72 | bit += 7; | |
73 | } | |
74 | } | |
75 | while (*p++ & 0x80); | |
76 | return result; | |
77 | } | |
d696c285 | 78 | |
afe874b1 A |
79 | |
80 | static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) | |
81 | { | |
82 | int64_t result = 0; | |
83 | int bit = 0; | |
84 | uint8_t byte; | |
85 | do { | |
86 | if (p == end) | |
87 | throwf("malformed sleb128"); | |
88 | byte = *p++; | |
89 | result |= ((byte & 0x7f) << bit); | |
90 | bit += 7; | |
91 | } while (byte & 0x80); | |
92 | // sign extend negative numbers | |
93 | if ( (byte & 0x40) != 0 ) | |
94 | result |= (-1LL) << bit; | |
95 | return result; | |
96 | } | |
97 | ||
98 | ||
d696c285 A |
99 | template <typename A> |
100 | class MachOChecker | |
101 | { | |
102 | public: | |
103 | static bool validFile(const uint8_t* fileContent); | |
ec29ba20 A |
104 | static MachOChecker<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot) |
105 | { return new MachOChecker<A>(fileContent, fileLength, path, verifierDstRoot); } | |
d696c285 A |
106 | virtual ~MachOChecker() {} |
107 | ||
108 | ||
109 | private: | |
110 | typedef typename A::P P; | |
111 | typedef typename A::P::E E; | |
112 | typedef typename A::P::uint_t pint_t; | |
113 | ||
d425e388 A |
114 | // utility classes for using std::unordered_map with c-strings |
115 | struct CStringHash { | |
116 | size_t operator()(const char* __s) const { | |
117 | size_t __h = 0; | |
118 | for ( ; *__s; ++__s) | |
119 | __h = 5 * __h + *__s; | |
120 | return __h; | |
121 | }; | |
122 | }; | |
123 | struct CStringEquals | |
a61fdf0a | 124 | { |
a61fdf0a A |
125 | bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } |
126 | }; | |
127 | ||
d425e388 | 128 | typedef std::unordered_set<const char*, CStringHash, CStringEquals> StringSet; |
a61fdf0a | 129 | |
ec29ba20 | 130 | MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot); |
d696c285 A |
131 | void checkMachHeader(); |
132 | void checkLoadCommands(); | |
133 | void checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect); | |
134 | uint8_t loadCommandSizeMask(); | |
a61fdf0a | 135 | void checkSymbolTable(); |
afe874b1 | 136 | void checkInitTerms(); |
d696c285 | 137 | void checkIndirectSymbolTable(); |
69a49097 A |
138 | void checkRelocations(); |
139 | void checkExternalReloation(const macho_relocation_info<P>* reloc); | |
140 | void checkLocalReloation(const macho_relocation_info<P>* reloc); | |
ec29ba20 A |
141 | void verify(); |
142 | void verifyInstallName(); | |
143 | void verifyNoRpaths(); | |
144 | void verifyNoFlatLookups(); | |
145 | ||
69a49097 A |
146 | pint_t relocBase(); |
147 | bool addressInWritableSegment(pint_t address); | |
a645023d A |
148 | bool hasTextRelocInRange(pint_t start, pint_t end); |
149 | pint_t segStartAddress(uint8_t segIndex); | |
afe874b1 A |
150 | bool addressIsRebaseSite(pint_t addr); |
151 | bool addressIsBindingSite(pint_t addr); | |
152 | pint_t getInitialStackPointer(const macho_thread_command<P>*); | |
153 | pint_t getEntryPoint(const macho_thread_command<P>*); | |
ec29ba20 | 154 | const char* archName(); |
afe874b1 A |
155 | |
156 | ||
d696c285 | 157 | const char* fPath; |
ec29ba20 | 158 | const char* fDstRoot; |
d696c285 A |
159 | const macho_header<P>* fHeader; |
160 | uint32_t fLength; | |
ec29ba20 | 161 | const char* fInstallName; |
d696c285 A |
162 | const char* fStrings; |
163 | const char* fStringsEnd; | |
164 | const macho_nlist<P>* fSymbols; | |
165 | uint32_t fSymbolCount; | |
a61fdf0a | 166 | const macho_dysymtab_command<P>* fDynamicSymbolTable; |
d696c285 A |
167 | const uint32_t* fIndirectTable; |
168 | uint32_t fIndirectTableCount; | |
69a49097 A |
169 | const macho_relocation_info<P>* fLocalRelocations; |
170 | uint32_t fLocalRelocationsCount; | |
171 | const macho_relocation_info<P>* fExternalRelocations; | |
172 | uint32_t fExternalRelocationsCount; | |
69a49097 | 173 | bool fWriteableSegmentWithAddrOver4G; |
afe874b1 | 174 | bool fSlidableImage; |
ec29ba20 | 175 | bool fHasLC_RPATH; |
69a49097 A |
176 | const macho_segment_command<P>* fFirstSegment; |
177 | const macho_segment_command<P>* fFirstWritableSegment; | |
afe874b1 | 178 | const macho_segment_command<P>* fTEXTSegment; |
a645023d | 179 | const macho_dyld_info_command<P>* fDyldInfo; |
c211e7c9 | 180 | uint32_t fSectionCount; |
a645023d | 181 | std::vector<const macho_segment_command<P>*>fSegments; |
d696c285 A |
182 | }; |
183 | ||
184 | ||
d696c285 A |
185 | template <> |
186 | bool MachOChecker<x86>::validFile(const uint8_t* fileContent) | |
187 | { | |
188 | const macho_header<P>* header = (const macho_header<P>*)fileContent; | |
189 | if ( header->magic() != MH_MAGIC ) | |
190 | return false; | |
191 | if ( header->cputype() != CPU_TYPE_I386 ) | |
192 | return false; | |
193 | switch (header->filetype()) { | |
194 | case MH_EXECUTE: | |
195 | case MH_DYLIB: | |
196 | case MH_BUNDLE: | |
197 | case MH_DYLINKER: | |
198 | return true; | |
199 | } | |
200 | return false; | |
201 | } | |
202 | ||
69a49097 A |
203 | template <> |
204 | bool MachOChecker<x86_64>::validFile(const uint8_t* fileContent) | |
205 | { | |
206 | const macho_header<P>* header = (const macho_header<P>*)fileContent; | |
207 | if ( header->magic() != MH_MAGIC_64 ) | |
208 | return false; | |
209 | if ( header->cputype() != CPU_TYPE_X86_64 ) | |
210 | return false; | |
211 | switch (header->filetype()) { | |
212 | case MH_EXECUTE: | |
213 | case MH_DYLIB: | |
214 | case MH_BUNDLE: | |
215 | case MH_DYLINKER: | |
216 | return true; | |
217 | } | |
218 | return false; | |
219 | } | |
d696c285 | 220 | |
ec29ba20 | 221 | #if SUPPORT_ARCH_arm_any |
2f2f92e4 A |
222 | template <> |
223 | bool MachOChecker<arm>::validFile(const uint8_t* fileContent) | |
224 | { | |
225 | const macho_header<P>* header = (const macho_header<P>*)fileContent; | |
226 | if ( header->magic() != MH_MAGIC ) | |
227 | return false; | |
228 | if ( header->cputype() != CPU_TYPE_ARM ) | |
229 | return false; | |
230 | switch (header->filetype()) { | |
231 | case MH_EXECUTE: | |
232 | case MH_DYLIB: | |
233 | case MH_BUNDLE: | |
234 | case MH_DYLINKER: | |
235 | return true; | |
236 | } | |
237 | return false; | |
238 | } | |
ec29ba20 | 239 | #endif |
d696c285 | 240 | |
f80fe69f A |
241 | #if SUPPORT_ARCH_arm64 |
242 | template <> | |
243 | bool MachOChecker<arm64>::validFile(const uint8_t* fileContent) | |
244 | { | |
245 | const macho_header<P>* header = (const macho_header<P>*)fileContent; | |
246 | if ( header->magic() != MH_MAGIC_64 ) | |
247 | return false; | |
248 | if ( header->cputype() != CPU_TYPE_ARM64 ) | |
249 | return false; | |
250 | switch (header->filetype()) { | |
251 | case MH_EXECUTE: | |
252 | case MH_DYLIB: | |
253 | case MH_BUNDLE: | |
254 | case MH_DYLINKER: | |
255 | return true; | |
256 | } | |
257 | return false; | |
258 | } | |
259 | #endif | |
260 | ||
0a8dc3df A |
261 | |
262 | template <> uint8_t MachOChecker<ppc>::loadCommandSizeMask() { return 0x03; } | |
263 | template <> uint8_t MachOChecker<ppc64>::loadCommandSizeMask() { return 0x07; } | |
d696c285 | 264 | template <> uint8_t MachOChecker<x86>::loadCommandSizeMask() { return 0x03; } |
69a49097 | 265 | template <> uint8_t MachOChecker<x86_64>::loadCommandSizeMask() { return 0x07; } |
2f2f92e4 | 266 | template <> uint8_t MachOChecker<arm>::loadCommandSizeMask() { return 0x03; } |
f80fe69f | 267 | template <> uint8_t MachOChecker<arm64>::loadCommandSizeMask() { return 0x07; } |
d696c285 | 268 | |
afe874b1 | 269 | |
afe874b1 A |
270 | template <> |
271 | x86::P::uint_t MachOChecker<x86>::getInitialStackPointer(const macho_thread_command<x86::P>* threadInfo) | |
272 | { | |
273 | return threadInfo->thread_register(7); | |
274 | } | |
275 | ||
276 | template <> | |
277 | x86_64::P::uint_t MachOChecker<x86_64>::getInitialStackPointer(const macho_thread_command<x86_64::P>* threadInfo) | |
278 | { | |
279 | return threadInfo->thread_register(7); | |
280 | } | |
281 | ||
282 | template <> | |
283 | arm::P::uint_t MachOChecker<arm>::getInitialStackPointer(const macho_thread_command<arm::P>* threadInfo) | |
284 | { | |
285 | return threadInfo->thread_register(13); | |
286 | } | |
287 | ||
f80fe69f A |
288 | template <> |
289 | arm64::P::uint_t MachOChecker<arm64>::getInitialStackPointer(const macho_thread_command<arm64::P>* threadInfo) | |
290 | { | |
291 | throw "LC_UNIXTHREAD not supported for arm64"; | |
292 | } | |
afe874b1 | 293 | |
afe874b1 | 294 | |
0a8dc3df A |
295 | template <> |
296 | ppc::P::uint_t MachOChecker<ppc>::getEntryPoint(const macho_thread_command<ppc::P>* threadInfo) | |
297 | { | |
298 | return threadInfo->thread_register(0); | |
299 | } | |
300 | ||
301 | ||
afe874b1 A |
302 | template <> |
303 | x86::P::uint_t MachOChecker<x86>::getEntryPoint(const macho_thread_command<x86::P>* threadInfo) | |
304 | { | |
305 | return threadInfo->thread_register(10); | |
306 | } | |
307 | ||
308 | template <> | |
309 | x86_64::P::uint_t MachOChecker<x86_64>::getEntryPoint(const macho_thread_command<x86_64::P>* threadInfo) | |
310 | { | |
311 | return threadInfo->thread_register(16); | |
312 | } | |
313 | ||
314 | template <> | |
315 | arm::P::uint_t MachOChecker<arm>::getEntryPoint(const macho_thread_command<arm::P>* threadInfo) | |
316 | { | |
317 | return threadInfo->thread_register(15); | |
318 | } | |
319 | ||
f80fe69f A |
320 | template <> |
321 | arm64::P::uint_t MachOChecker<arm64>::getEntryPoint(const macho_thread_command<arm64::P>* threadInfo) | |
322 | { | |
323 | throw "LC_UNIXTHREAD not supported for arm64"; | |
324 | } | |
ec29ba20 A |
325 | |
326 | ||
327 | template <typename A> | |
328 | const char* MachOChecker<A>::archName() | |
329 | { | |
330 | switch ( fHeader->cputype() ) { | |
331 | case CPU_TYPE_I386: | |
332 | return "i386"; | |
333 | case CPU_TYPE_X86_64: | |
334 | if ( fHeader->cpusubtype() == CPU_SUBTYPE_X86_64_H ) | |
335 | return "x86_64h"; | |
336 | else | |
337 | return "x86_64"; | |
338 | case CPU_TYPE_ARM: | |
339 | switch ( fHeader->cpusubtype() ) { | |
340 | case CPU_SUBTYPE_ARM_V7: | |
341 | return "armv7"; | |
342 | case CPU_SUBTYPE_ARM_V7S: | |
343 | return "armv7s"; | |
344 | case CPU_SUBTYPE_ARM_V7K: | |
345 | return "armv7k"; | |
346 | } | |
347 | return "arm"; | |
348 | case CPU_TYPE_ARM64: | |
349 | return "arm64"; | |
350 | } | |
351 | return "unknown"; | |
352 | } | |
353 | ||
354 | ||
d696c285 | 355 | template <typename A> |
ec29ba20 A |
356 | MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot) |
357 | : fHeader(NULL), fLength(fileLength), fInstallName(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0), | |
69a49097 | 358 | fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0), |
ec29ba20 | 359 | fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fHasLC_RPATH(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), |
afe874b1 | 360 | fTEXTSegment(NULL), fDyldInfo(NULL), fSectionCount(0) |
d696c285 A |
361 | { |
362 | // sanity check | |
363 | if ( ! validFile(fileContent) ) | |
364 | throw "not a mach-o file that can be checked"; | |
365 | ||
366 | fPath = strdup(path); | |
ec29ba20 | 367 | fDstRoot = verifierDstRoot ? strdup(verifierDstRoot) : NULL; |
d696c285 A |
368 | fHeader = (const macho_header<P>*)fileContent; |
369 | ||
370 | // sanity check header | |
371 | checkMachHeader(); | |
372 | ||
373 | // check load commands | |
374 | checkLoadCommands(); | |
375 | ||
376 | checkIndirectSymbolTable(); | |
377 | ||
69a49097 | 378 | checkRelocations(); |
a61fdf0a A |
379 | |
380 | checkSymbolTable(); | |
afe874b1 A |
381 | |
382 | checkInitTerms(); | |
ec29ba20 A |
383 | |
384 | if ( verifierDstRoot != NULL ) | |
385 | verify(); | |
d696c285 A |
386 | } |
387 | ||
388 | ||
389 | template <typename A> | |
390 | void MachOChecker<A>::checkMachHeader() | |
391 | { | |
392 | if ( (fHeader->sizeofcmds() + sizeof(macho_header<P>)) > fLength ) | |
393 | throw "sizeofcmds in mach_header is larger than file"; | |
394 | ||
395 | uint32_t flags = fHeader->flags(); | |
ec29ba20 | 396 | const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFC000000; |
d696c285 A |
397 | if ( flags & invalidBits ) |
398 | throw "invalid bits in mach_header flags"; | |
a61fdf0a A |
399 | if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) |
400 | throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs"; | |
afe874b1 A |
401 | |
402 | switch ( fHeader->filetype() ) { | |
403 | case MH_EXECUTE: | |
404 | fSlidableImage = ( flags & MH_PIE ); | |
405 | break; | |
406 | case MH_DYLIB: | |
407 | case MH_BUNDLE: | |
408 | fSlidableImage = true; | |
409 | break; | |
410 | default: | |
411 | throw "not a mach-o file type supported by this tool"; | |
412 | } | |
d696c285 A |
413 | } |
414 | ||
415 | template <typename A> | |
416 | void MachOChecker<A>::checkLoadCommands() | |
417 | { | |
418 | // check that all load commands fit within the load command space file | |
2f2f92e4 | 419 | const macho_encryption_info_command<P>* encryption_info = NULL; |
afe874b1 | 420 | const macho_thread_command<P>* threadInfo = NULL; |
ebf6f434 | 421 | const macho_entry_point_command<P>* entryPoint = NULL; |
d696c285 A |
422 | const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength; |
423 | const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds(); | |
424 | const uint32_t cmd_count = fHeader->ncmds(); | |
425 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
426 | const macho_load_command<P>* cmd = cmds; | |
ec29ba20 | 427 | const macho_dylib_command<P>* dylibID; |
d696c285 A |
428 | for (uint32_t i = 0; i < cmd_count; ++i) { |
429 | uint32_t size = cmd->cmdsize(); | |
430 | if ( (size & this->loadCommandSizeMask()) != 0 ) | |
431 | throwf("load command #%d has a unaligned size", i); | |
432 | const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); | |
433 | if ( endOfCmd > endOfLoadCommands ) | |
434 | throwf("load command #%d extends beyond the end of the load commands", i); | |
435 | if ( endOfCmd > endOfFile ) | |
436 | throwf("load command #%d extends beyond the end of the file", i); | |
437 | switch ( cmd->cmd() ) { | |
438 | case macho_segment_command<P>::CMD: | |
439 | case LC_SYMTAB: | |
d696c285 A |
440 | case LC_DYSYMTAB: |
441 | case LC_LOAD_DYLIB: | |
d696c285 | 442 | case LC_ID_DYLINKER: |
ec29ba20 | 443 | case LC_LOAD_DYLINKER: |
d696c285 A |
444 | case macho_routines_command<P>::CMD: |
445 | case LC_SUB_FRAMEWORK: | |
d696c285 A |
446 | case LC_SUB_CLIENT: |
447 | case LC_TWOLEVEL_HINTS: | |
448 | case LC_PREBIND_CKSUM: | |
449 | case LC_LOAD_WEAK_DYLIB: | |
2f2f92e4 | 450 | case LC_LAZY_LOAD_DYLIB: |
d696c285 | 451 | case LC_UUID: |
a61fdf0a A |
452 | case LC_REEXPORT_DYLIB: |
453 | case LC_SEGMENT_SPLIT_INFO: | |
2f2f92e4 | 454 | case LC_CODE_SIGNATURE: |
a645023d A |
455 | case LC_LOAD_UPWARD_DYLIB: |
456 | case LC_VERSION_MIN_MACOSX: | |
457 | case LC_VERSION_MIN_IPHONEOS: | |
82b4b32b A |
458 | case LC_VERSION_MIN_TVOS: |
459 | case LC_VERSION_MIN_WATCHOS: | |
ebf6f434 A |
460 | case LC_FUNCTION_STARTS: |
461 | case LC_DYLD_ENVIRONMENT: | |
462 | case LC_DATA_IN_CODE: | |
463 | case LC_DYLIB_CODE_SIGN_DRS: | |
464 | case LC_SOURCE_VERSION: | |
a645023d | 465 | break; |
ec29ba20 A |
466 | case LC_RPATH: |
467 | fHasLC_RPATH = true; | |
468 | break; | |
469 | case LC_ID_DYLIB: | |
470 | dylibID = (macho_dylib_command<P>*)cmd; | |
471 | if ( dylibID->name_offset() > size ) | |
472 | throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID->name_offset(), size); | |
473 | if ( (dylibID->name_offset() + strlen(dylibID->name()) + 1) > size ) | |
474 | throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command"); | |
475 | fInstallName = dylibID->name(); | |
476 | break; | |
55e3d2f6 A |
477 | case LC_DYLD_INFO: |
478 | case LC_DYLD_INFO_ONLY: | |
a645023d | 479 | fDyldInfo = (macho_dyld_info_command<P>*)cmd; |
2f2f92e4 A |
480 | break; |
481 | case LC_ENCRYPTION_INFO: | |
f80fe69f | 482 | case LC_ENCRYPTION_INFO_64: |
2f2f92e4 | 483 | encryption_info = (macho_encryption_info_command<P>*)cmd; |
a61fdf0a A |
484 | break; |
485 | case LC_SUB_UMBRELLA: | |
486 | case LC_SUB_LIBRARY: | |
487 | if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS ) | |
488 | throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA"; | |
d696c285 | 489 | break; |
ebf6f434 A |
490 | case LC_MAIN: |
491 | if ( fHeader->filetype() != MH_EXECUTE ) | |
492 | throw "LC_MAIN can only be used in MH_EXECUTE file types"; | |
493 | entryPoint = (macho_entry_point_command<P>*)cmd; | |
494 | break; | |
afe874b1 A |
495 | case LC_UNIXTHREAD: |
496 | if ( fHeader->filetype() != MH_EXECUTE ) | |
497 | throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types"; | |
498 | threadInfo = (macho_thread_command<P>*)cmd; | |
499 | break; | |
d696c285 A |
500 | default: |
501 | throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd()); | |
502 | } | |
503 | cmd = (const macho_load_command<P>*)endOfCmd; | |
504 | } | |
505 | ||
506 | // check segments | |
507 | cmd = cmds; | |
508 | std::vector<std::pair<pint_t, pint_t> > segmentAddressRanges; | |
509 | std::vector<std::pair<pint_t, pint_t> > segmentFileOffsetRanges; | |
510 | const macho_segment_command<P>* linkEditSegment = NULL; | |
afe874b1 | 511 | const macho_segment_command<P>* stackSegment = NULL; |
d696c285 A |
512 | for (uint32_t i = 0; i < cmd_count; ++i) { |
513 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
514 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
a645023d | 515 | fSegments.push_back(segCmd); |
d696c285 A |
516 | if ( segCmd->cmdsize() != (sizeof(macho_segment_command<P>) + segCmd->nsects() * sizeof(macho_section_content<P>)) ) |
517 | throw "invalid segment load command size"; | |
518 | ||
519 | // see if this overlaps another segment address range | |
520 | uint64_t startAddr = segCmd->vmaddr(); | |
521 | uint64_t endAddr = startAddr + segCmd->vmsize(); | |
522 | for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentAddressRanges.begin(); it != segmentAddressRanges.end(); ++it) { | |
523 | if ( it->first < startAddr ) { | |
524 | if ( it->second > startAddr ) | |
525 | throw "overlapping segment vm addresses"; | |
526 | } | |
527 | else if ( it->first > startAddr ) { | |
528 | if ( it->first < endAddr ) | |
529 | throw "overlapping segment vm addresses"; | |
530 | } | |
531 | else { | |
532 | throw "overlapping segment vm addresses"; | |
533 | } | |
ec29ba20 | 534 | segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); |
d696c285 A |
535 | } |
536 | // see if this overlaps another segment file offset range | |
537 | uint64_t startOffset = segCmd->fileoff(); | |
538 | uint64_t endOffset = startOffset + segCmd->filesize(); | |
539 | for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentFileOffsetRanges.begin(); it != segmentFileOffsetRanges.end(); ++it) { | |
540 | if ( it->first < startOffset ) { | |
541 | if ( it->second > startOffset ) | |
542 | throw "overlapping segment file data"; | |
543 | } | |
544 | else if ( it->first > startOffset ) { | |
545 | if ( it->first < endOffset ) | |
546 | throw "overlapping segment file data"; | |
547 | } | |
548 | else { | |
549 | throw "overlapping segment file data"; | |
550 | } | |
ec29ba20 | 551 | segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); |
d696c285 A |
552 | // check is within file bounds |
553 | if ( (startOffset > fLength) || (endOffset > fLength) ) | |
554 | throw "segment file data is past end of file"; | |
555 | } | |
556 | // verify it fits in file | |
557 | if ( startOffset > fLength ) | |
558 | throw "segment fileoff does not fit in file"; | |
559 | if ( endOffset > fLength ) | |
560 | throw "segment fileoff+filesize does not fit in file"; | |
561 | ||
afe874b1 | 562 | // record special segments |
d696c285 A |
563 | if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) |
564 | linkEditSegment = segCmd; | |
afe874b1 A |
565 | else if ( strcmp(segCmd->segname(), "__UNIXSTACK") == 0 ) |
566 | stackSegment = segCmd; | |
69a49097 A |
567 | |
568 | // cache interesting segments | |
569 | if ( fFirstSegment == NULL ) | |
570 | fFirstSegment = segCmd; | |
afe874b1 A |
571 | if ( (fTEXTSegment == NULL) && (strcmp(segCmd->segname(), "__TEXT") == 0) ) |
572 | fTEXTSegment = segCmd; | |
a61fdf0a A |
573 | if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) { |
574 | if ( fFirstWritableSegment == NULL ) | |
575 | fFirstWritableSegment = segCmd; | |
576 | if ( segCmd->vmaddr() > 0x100000000ULL ) | |
577 | fWriteableSegmentWithAddrOver4G = true; | |
578 | } | |
afe874b1 | 579 | |
d696c285 A |
580 | // check section ranges |
581 | const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>)); | |
582 | const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()]; | |
583 | for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
afe874b1 | 584 | // check all non-zero sized sections are within segment |
d696c285 A |
585 | if ( sect->addr() < startAddr ) |
586 | throwf("section %s vm address not within segment", sect->sectname()); | |
587 | if ( (sect->addr()+sect->size()) > endAddr ) | |
588 | throwf("section %s vm address not within segment", sect->sectname()); | |
a645023d A |
589 | if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) |
590 | && ((sect->flags() & SECTION_TYPE) != S_THREAD_LOCAL_ZEROFILL) | |
afe874b1 A |
591 | && (segCmd->filesize() != 0) |
592 | && (sect->size() != 0) ) { | |
d696c285 A |
593 | if ( sect->offset() < startOffset ) |
594 | throwf("section %s file offset not within segment", sect->sectname()); | |
595 | if ( (sect->offset()+sect->size()) > endOffset ) | |
596 | throwf("section %s file offset not within segment", sect->sectname()); | |
597 | } | |
598 | checkSection(segCmd, sect); | |
c211e7c9 | 599 | ++fSectionCount; |
d696c285 A |
600 | } |
601 | } | |
602 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
603 | } | |
604 | ||
605 | // verify there was a LINKEDIT segment | |
606 | if ( linkEditSegment == NULL ) | |
607 | throw "no __LINKEDIT segment"; | |
608 | ||
afe874b1 A |
609 | // verify there was an executable __TEXT segment and load commands are in it |
610 | if ( fTEXTSegment == NULL ) | |
611 | throw "no __TEXT segment"; | |
612 | if ( fTEXTSegment->initprot() != (VM_PROT_READ|VM_PROT_EXECUTE) ) | |
613 | throw "__TEXT segment does not have r-x init permissions"; | |
614 | //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) ) | |
615 | // throw "__TEXT segment does not have rwx max permissions"; | |
616 | if ( fTEXTSegment->fileoff() != 0 ) | |
617 | throw "__TEXT segment does not start at mach_header"; | |
618 | if ( fTEXTSegment->filesize() < (sizeof(macho_header<P>)+fHeader->sizeofcmds()) ) | |
619 | throw "__TEXT segment smaller than load commands"; | |
620 | ||
621 | // verify if custom stack used, that stack is in __UNIXSTACK segment | |
622 | if ( threadInfo != NULL ) { | |
623 | pint_t initialSP = getInitialStackPointer(threadInfo); | |
624 | if ( initialSP != 0 ) { | |
625 | if ( stackSegment == NULL ) | |
626 | throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment"; | |
627 | if ( (initialSP < stackSegment->vmaddr()) || (initialSP > (stackSegment->vmaddr()+stackSegment->vmsize())) ) | |
628 | throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment"; | |
629 | } | |
630 | } | |
631 | ||
632 | // verify __UNIXSTACK is zero fill | |
633 | if ( stackSegment != NULL ) { | |
634 | if ( (stackSegment->filesize() != 0) || (stackSegment->fileoff() != 0) ) | |
635 | throw "__UNIXSTACK is not a zero-fill segment"; | |
636 | if ( stackSegment->vmsize() < 4096 ) | |
637 | throw "__UNIXSTACK segment is too small"; | |
638 | } | |
639 | ||
640 | // verify entry point is in __TEXT segment | |
641 | if ( threadInfo != NULL ) { | |
642 | pint_t initialPC = getEntryPoint(threadInfo); | |
643 | if ( (initialPC < fTEXTSegment->vmaddr()) || (initialPC >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) ) | |
644 | throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC); | |
645 | } | |
ebf6f434 A |
646 | else if ( entryPoint != NULL ) { |
647 | pint_t initialOffset = entryPoint->entryoff(); | |
648 | if ( (initialOffset < fTEXTSegment->fileoff()) || (initialOffset >= (fTEXTSegment->fileoff()+fTEXTSegment->filesize())) ) | |
649 | throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset); | |
650 | } | |
afe874b1 | 651 | |
d696c285 A |
652 | // checks for executables |
653 | bool isStaticExecutable = false; | |
654 | if ( fHeader->filetype() == MH_EXECUTE ) { | |
655 | isStaticExecutable = true; | |
656 | cmd = cmds; | |
657 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
658 | switch ( cmd->cmd() ) { | |
659 | case LC_LOAD_DYLINKER: | |
660 | // the existence of a dyld load command makes a executable dynamic | |
661 | isStaticExecutable = false; | |
662 | break; | |
663 | } | |
664 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
665 | } | |
666 | if ( isStaticExecutable ) { | |
ebf6f434 | 667 | if ( (fHeader->flags() != MH_NOUNDEFS) && (fHeader->flags() != (MH_NOUNDEFS|MH_PIE)) ) |
d696c285 A |
668 | throw "invalid bits in mach_header flags for static executable"; |
669 | } | |
670 | } | |
671 | ||
2f2f92e4 A |
672 | // verify encryption info |
673 | if ( encryption_info != NULL ) { | |
0a8dc3df A |
674 | switch ( fHeader->filetype() ) { |
675 | case MH_EXECUTE: case MH_DYLIB: case MH_BUNDLE: | |
676 | break; // okay | |
677 | default: | |
678 | throw "LC_ENCRYPTION_INFO load command is not allowed in this file type"; | |
679 | } | |
2f2f92e4 A |
680 | if ( encryption_info->cryptoff() < (sizeof(macho_header<P>) + fHeader->sizeofcmds()) ) |
681 | throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands"; | |
682 | if ( (encryption_info->cryptoff() % 4096) != 0 ) | |
683 | throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned"; | |
684 | if ( (encryption_info->cryptsize() % 4096) != 0 ) | |
685 | throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized"; | |
686 | for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentFileOffsetRanges.begin(); | |
687 | it != segmentFileOffsetRanges.end(); ++it) { | |
688 | if ( (it->first <= encryption_info->cryptoff()) && (encryption_info->cryptoff() < it->second) ) { | |
689 | if ( (encryption_info->cryptoff() + encryption_info->cryptsize()) > it->second ) | |
690 | throw "LC_ENCRYPTION_INFO load command is not contained within one segment"; | |
691 | } | |
692 | } | |
693 | } | |
694 | ||
ec29ba20 A |
695 | // verify dylib has LC_ID_DYLIB |
696 | if ( fHeader->filetype() == MH_DYLIB ) { | |
697 | if ( fInstallName == NULL ) | |
698 | throw "MH_DYLIB missing LC_ID_DYLIB"; | |
699 | } | |
700 | else { | |
701 | if ( fInstallName != NULL ) | |
702 | throw "LC_ID_DYLIB found but file type is not MH_DYLIB"; | |
703 | } | |
704 | ||
a61fdf0a | 705 | // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO |
d696c285 A |
706 | cmd = cmds; |
707 | bool foundDynamicSymTab = false; | |
708 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
709 | switch ( cmd->cmd() ) { | |
710 | case LC_SYMTAB: | |
711 | { | |
712 | const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd; | |
713 | fSymbolCount = symtab->nsyms(); | |
714 | fSymbols = (const macho_nlist<P>*)((char*)fHeader + symtab->symoff()); | |
715 | if ( symtab->symoff() < linkEditSegment->fileoff() ) | |
716 | throw "symbol table not in __LINKEDIT"; | |
ec29ba20 A |
717 | if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist<P>*)) > symtab->stroff() ) |
718 | throw "symbol table overlaps string pool"; | |
a61fdf0a A |
719 | if ( (symtab->symoff() % sizeof(pint_t)) != 0 ) |
720 | throw "symbol table start not pointer aligned"; | |
d696c285 A |
721 | fStrings = (char*)fHeader + symtab->stroff(); |
722 | fStringsEnd = fStrings + symtab->strsize(); | |
723 | if ( symtab->stroff() < linkEditSegment->fileoff() ) | |
724 | throw "string pool not in __LINKEDIT"; | |
725 | if ( (symtab->stroff()+symtab->strsize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) | |
726 | throw "string pool extends beyond __LINKEDIT"; | |
a61fdf0a A |
727 | if ( (symtab->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed |
728 | throw "string pool start not pointer aligned"; | |
d696c285 A |
729 | } |
730 | break; | |
731 | case LC_DYSYMTAB: | |
732 | { | |
ebf6f434 | 733 | if ( isStaticExecutable &&! fSlidableImage ) |
d696c285 A |
734 | throw "LC_DYSYMTAB should not be used in static executable"; |
735 | foundDynamicSymTab = true; | |
a645023d | 736 | fDynamicSymbolTable = (macho_dysymtab_command<P>*)cmd; |
a61fdf0a A |
737 | fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff()); |
738 | fIndirectTableCount = fDynamicSymbolTable->nindirectsyms(); | |
74cfe461 | 739 | if ( fIndirectTableCount != 0 ) { |
a61fdf0a | 740 | if ( fDynamicSymbolTable->indirectsymoff() < linkEditSegment->fileoff() ) |
74cfe461 | 741 | throw "indirect symbol table not in __LINKEDIT"; |
a61fdf0a | 742 | if ( (fDynamicSymbolTable->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) |
74cfe461 | 743 | throw "indirect symbol table not in __LINKEDIT"; |
a61fdf0a A |
744 | if ( (fDynamicSymbolTable->indirectsymoff() % sizeof(pint_t)) != 0 ) |
745 | throw "indirect symbol table not pointer aligned"; | |
74cfe461 | 746 | } |
a61fdf0a | 747 | fLocalRelocationsCount = fDynamicSymbolTable->nlocrel(); |
69a49097 | 748 | if ( fLocalRelocationsCount != 0 ) { |
a61fdf0a A |
749 | fLocalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + fDynamicSymbolTable->locreloff()); |
750 | if ( fDynamicSymbolTable->locreloff() < linkEditSegment->fileoff() ) | |
69a49097 | 751 | throw "local relocations not in __LINKEDIT"; |
a61fdf0a | 752 | if ( (fDynamicSymbolTable->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) |
69a49097 | 753 | throw "local relocations not in __LINKEDIT"; |
a61fdf0a A |
754 | if ( (fDynamicSymbolTable->locreloff() % sizeof(pint_t)) != 0 ) |
755 | throw "local relocations table not pointer aligned"; | |
69a49097 | 756 | } |
a61fdf0a | 757 | fExternalRelocationsCount = fDynamicSymbolTable->nextrel(); |
69a49097 | 758 | if ( fExternalRelocationsCount != 0 ) { |
a61fdf0a A |
759 | fExternalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + fDynamicSymbolTable->extreloff()); |
760 | if ( fDynamicSymbolTable->extreloff() < linkEditSegment->fileoff() ) | |
761 | throw "external relocations not in __LINKEDIT"; | |
762 | if ( (fDynamicSymbolTable->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) | |
763 | throw "external relocations not in __LINKEDIT"; | |
764 | if ( (fDynamicSymbolTable->extreloff() % sizeof(pint_t)) != 0 ) | |
765 | throw "external relocations table not pointer aligned"; | |
69a49097 | 766 | } |
d696c285 A |
767 | } |
768 | break; | |
a61fdf0a A |
769 | case LC_SEGMENT_SPLIT_INFO: |
770 | { | |
771 | if ( isStaticExecutable ) | |
772 | throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable"; | |
a645023d | 773 | const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd; |
a61fdf0a A |
774 | if ( info->dataoff() < linkEditSegment->fileoff() ) |
775 | throw "split seg info not in __LINKEDIT"; | |
776 | if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) | |
777 | throw "split seg info not in __LINKEDIT"; | |
778 | if ( (info->dataoff() % sizeof(pint_t)) != 0 ) | |
779 | throw "split seg info table not pointer aligned"; | |
780 | if ( (info->datasize() % sizeof(pint_t)) != 0 ) | |
781 | throw "split seg info size not a multiple of pointer size"; | |
782 | } | |
783 | break; | |
a645023d A |
784 | case LC_FUNCTION_STARTS: |
785 | { | |
786 | const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd; | |
787 | if ( info->dataoff() < linkEditSegment->fileoff() ) | |
788 | throw "function starts data not in __LINKEDIT"; | |
789 | if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) | |
790 | throw "function starts data not in __LINKEDIT"; | |
791 | if ( (info->dataoff() % sizeof(pint_t)) != 0 ) | |
792 | throw "function starts data table not pointer aligned"; | |
793 | if ( (info->datasize() % sizeof(pint_t)) != 0 ) | |
794 | throw "function starts data size not a multiple of pointer size"; | |
795 | } | |
796 | break; | |
ebf6f434 A |
797 | case LC_DATA_IN_CODE: |
798 | { | |
799 | const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd; | |
800 | if ( info->dataoff() < linkEditSegment->fileoff() ) | |
801 | throw "data-in-code data not in __LINKEDIT"; | |
802 | if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) | |
803 | throw "data-in-code data not in __LINKEDIT"; | |
804 | if ( (info->dataoff() % sizeof(pint_t)) != 0 ) | |
805 | throw "data-in-code data table not pointer aligned"; | |
806 | if ( (info->datasize() % sizeof(pint_t)) != 0 ) | |
807 | throw "data-in-code data size not a multiple of pointer size"; | |
808 | } | |
809 | break; | |
810 | case LC_DYLIB_CODE_SIGN_DRS: | |
811 | { | |
812 | const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd; | |
813 | if ( info->dataoff() < linkEditSegment->fileoff() ) | |
814 | throw "dependent dylib DR data not in __LINKEDIT"; | |
815 | if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) | |
816 | throw "dependent dylib DR data not in __LINKEDIT"; | |
817 | if ( (info->dataoff() % sizeof(pint_t)) != 0 ) | |
818 | throw "dependent dylib DR data table not pointer aligned"; | |
819 | if ( (info->datasize() % sizeof(pint_t)) != 0 ) | |
820 | throw "dependent dylib DR data size not a multiple of pointer size"; | |
821 | } | |
822 | break; | |
d696c285 A |
823 | } |
824 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
825 | } | |
826 | if ( !isStaticExecutable && !foundDynamicSymTab ) | |
827 | throw "missing dynamic symbol table"; | |
828 | if ( fStrings == NULL ) | |
829 | throw "missing symbol table"; | |
a61fdf0a | 830 | |
d696c285 A |
831 | } |
832 | ||
833 | template <typename A> | |
834 | void MachOChecker<A>::checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect) | |
835 | { | |
836 | uint8_t sectionType = (sect->flags() & SECTION_TYPE); | |
837 | if ( sectionType == S_ZEROFILL ) { | |
838 | if ( sect->offset() != 0 ) | |
839 | throwf("section offset should be zero for zero-fill section %s", sect->sectname()); | |
840 | } | |
841 | ||
afe874b1 A |
842 | // check section's segment name matches segment |
843 | // if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 ) | |
844 | // throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname()); | |
845 | ||
d696c285 A |
846 | // more section tests here |
847 | } | |
848 | ||
afe874b1 | 849 | |
ec29ba20 A |
850 | template <typename A> |
851 | void MachOChecker<A>::verify() | |
852 | { | |
853 | bool sharedCacheCandidate = false; | |
854 | if ( fInstallName != NULL ) { | |
855 | if ( (strncmp(fInstallName, "/usr/lib/", 9) == 0) || (strncmp(fInstallName, "/System/Library/", 16) == 0) ) { | |
856 | sharedCacheCandidate = true; | |
857 | verifyInstallName(); | |
858 | verifyNoRpaths(); | |
859 | } | |
860 | } | |
861 | verifyNoFlatLookups(); | |
862 | } | |
863 | ||
864 | ||
865 | template <typename A> | |
866 | void MachOChecker<A>::verifyInstallName() | |
867 | { | |
868 | // Don't allow @rpath to be used as -install_name for OS dylibs | |
869 | if ( strncmp(fInstallName, "@rpath/", 7) == 0 ) { | |
870 | printf("os_dylib_rpath_install_name\tfatal\t-install_name uses @rpath in arch %s\n", archName()); | |
871 | } | |
872 | else { | |
873 | // Verify -install_name match actual path of dylib | |
874 | const char* installPathWithinDstRoot = &fPath[strlen(fDstRoot)]; | |
875 | if ( strcmp(installPathWithinDstRoot, fInstallName) != 0 ) { | |
876 | // see if install name is a symlink to actual file | |
877 | bool symlinkToDylib = false; | |
878 | char absDstPath[PATH_MAX]; | |
879 | if ( realpath(fDstRoot, absDstPath) != NULL ) { | |
880 | char fullInstallNamePath[PATH_MAX]; | |
881 | strlcpy(fullInstallNamePath, absDstPath, PATH_MAX); | |
882 | strlcat(fullInstallNamePath, fInstallName, PATH_MAX); | |
883 | char absInstallNamePath[PATH_MAX]; | |
884 | if ( realpath(fullInstallNamePath, absInstallNamePath) != NULL ) { | |
885 | char absFPath[PATH_MAX]; | |
886 | if ( realpath(fPath, absFPath) != NULL ) { | |
887 | if ( strcmp(absInstallNamePath, absFPath) == 0 ) | |
888 | symlinkToDylib = true; | |
889 | } | |
890 | } | |
891 | } | |
892 | if ( !symlinkToDylib ) | |
893 | printf("os_dylib_bad_install_name\twarn\t-install_name does not match install location in arch %s\n", archName()); | |
894 | } | |
895 | } | |
afe874b1 | 896 | |
ec29ba20 A |
897 | } |
898 | ||
899 | template <typename A> | |
900 | void MachOChecker<A>::verifyNoRpaths() | |
901 | { | |
902 | // Don't allow OS dylibs to add rpaths | |
903 | if ( fHasLC_RPATH ) { | |
904 | printf("os_dylib_rpath\twarn\tcontains LC_RPATH load command in arch %s\n", archName()); | |
905 | } | |
906 | } | |
907 | ||
908 | ||
909 | template <typename A> | |
910 | void MachOChecker<A>::verifyNoFlatLookups() | |
911 | { | |
912 | if ( (fHeader->flags() & MH_TWOLEVEL) == 0 ) { | |
913 | printf("os_dylib_flat_namespace\twarn\tbuilt with -flat_namespace in arch %s\n", archName()); | |
914 | return; | |
915 | } | |
916 | ||
917 | if ( fDynamicSymbolTable != NULL ) { | |
918 | const macho_nlist<P>* const undefinesStart = &fSymbols[fDynamicSymbolTable->iundefsym()]; | |
919 | const macho_nlist<P>* const undefinesEnd = &undefinesStart[fDynamicSymbolTable->nundefsym()]; | |
920 | for(const macho_nlist<P>* sym = undefinesStart; sym < undefinesEnd; ++sym) { | |
921 | //printf("0x%04X %s\n", sym->n_desc(), &fStrings[sym->n_strx()]); | |
922 | if ( GET_LIBRARY_ORDINAL(sym->n_desc()) == DYNAMIC_LOOKUP_ORDINAL ) { | |
923 | const char* symName = &fStrings[sym->n_strx()]; | |
924 | printf("os_dylib_undefined_dynamic_lookup\twarn\tbuilt with -undefined dynamic_lookup for symbol %s in arch %s\n", symName, archName()); | |
925 | } | |
926 | } | |
927 | } | |
928 | } | |
afe874b1 | 929 | |
d696c285 A |
930 | template <typename A> |
931 | void MachOChecker<A>::checkIndirectSymbolTable() | |
932 | { | |
a645023d A |
933 | // static executables don't have indirect symbol table |
934 | if ( fDynamicSymbolTable == NULL ) | |
935 | return; | |
d696c285 A |
936 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); |
937 | const uint32_t cmd_count = fHeader->ncmds(); | |
938 | const macho_load_command<P>* cmd = cmds; | |
939 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
940 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
941 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
942 | const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>)); | |
943 | const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()]; | |
944 | for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
945 | // make sure all magic sections that use indirect symbol table fit within it | |
946 | uint32_t start = 0; | |
947 | uint32_t elementSize = 0; | |
948 | switch ( sect->flags() & SECTION_TYPE ) { | |
949 | case S_SYMBOL_STUBS: | |
950 | elementSize = sect->reserved2(); | |
951 | start = sect->reserved1(); | |
952 | break; | |
953 | case S_LAZY_SYMBOL_POINTERS: | |
954 | case S_NON_LAZY_SYMBOL_POINTERS: | |
955 | elementSize = sizeof(pint_t); | |
956 | start = sect->reserved1(); | |
957 | break; | |
958 | } | |
959 | if ( elementSize != 0 ) { | |
960 | uint32_t count = sect->size() / elementSize; | |
961 | if ( (count*elementSize) != sect->size() ) | |
962 | throwf("%s section size is not an even multiple of element size", sect->sectname()); | |
963 | if ( (start+count) > fIndirectTableCount ) | |
964 | throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect->sectname(), start+count, fIndirectTableCount ); | |
965 | } | |
966 | } | |
967 | } | |
968 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
969 | } | |
ec29ba20 A |
970 | |
971 | ||
972 | if ( fDynamicSymbolTable->ilocalsym() != 0 ) | |
973 | throwf("start of local symbols (%d) not at start of symbol table", fDynamicSymbolTable->ilocalsym()); | |
974 | ||
975 | if ( fDynamicSymbolTable->ilocalsym() > fSymbolCount ) | |
976 | throwf("start of local symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->ilocalsym(), fSymbolCount); | |
977 | if ( fDynamicSymbolTable->ilocalsym() + fDynamicSymbolTable->nlocalsym() > fSymbolCount ) { | |
978 | throwf("local symbols out of range (%d+%d > %d) in indirect symbol table", | |
979 | fDynamicSymbolTable->ilocalsym(), fDynamicSymbolTable->nlocalsym(), fSymbolCount); | |
980 | } | |
981 | ||
982 | if ( fDynamicSymbolTable->iextdefsym() > fSymbolCount ) | |
983 | throwf("start of extern symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->iextdefsym(), fSymbolCount); | |
984 | if ( fDynamicSymbolTable->iextdefsym() != fDynamicSymbolTable->ilocalsym() + fDynamicSymbolTable->nlocalsym() ) { | |
985 | throwf("start of extern symbols (%d) not contiguous to local symbols (%d+%d) in indirect symbol table", | |
986 | fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->ilocalsym(), fDynamicSymbolTable->nlocalsym() ); | |
987 | } | |
988 | if ( fDynamicSymbolTable->iextdefsym() + fDynamicSymbolTable->nextdefsym() > fSymbolCount ) { | |
989 | throwf("extern symbols out of range (%d+%d > %d) in indirect symbol table", | |
990 | fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->nextdefsym(), fSymbolCount); | |
991 | } | |
992 | ||
993 | if ( fDynamicSymbolTable->iundefsym() > fSymbolCount ) | |
994 | throwf("start of undefined symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->iundefsym(), fSymbolCount); | |
995 | if ( fDynamicSymbolTable->iundefsym() != fDynamicSymbolTable->iextdefsym() + fDynamicSymbolTable->nextdefsym() ) { | |
996 | throwf("start of undefined symbols (%d) not contiguous to extern symbols (%d+%d) in indirect symbol table", | |
997 | fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->nextdefsym()); | |
998 | } | |
999 | if ( fDynamicSymbolTable->iundefsym() + fDynamicSymbolTable->nundefsym() > fSymbolCount ) { | |
1000 | throwf("undefined symbols out of range (%d+%d > %d) in indirect symbol table", | |
1001 | fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->nundefsym(), fSymbolCount); | |
1002 | } | |
1003 | ||
1004 | if ( fDynamicSymbolTable->iundefsym() + fDynamicSymbolTable->nundefsym() != fSymbolCount ) { | |
1005 | throwf("end undefined symbols (%d+%d) not at end of all symbols (%d) in indirect symbol table", | |
1006 | fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->nundefsym(), fSymbolCount ); | |
1007 | } | |
d696c285 A |
1008 | } |
1009 | ||
1010 | ||
afe874b1 A |
1011 | |
1012 | ||
a61fdf0a A |
1013 | template <typename A> |
1014 | void MachOChecker<A>::checkSymbolTable() | |
1015 | { | |
1016 | // verify no duplicate external symbol names | |
1017 | if ( fDynamicSymbolTable != NULL ) { | |
1018 | StringSet externalNames; | |
1019 | const macho_nlist<P>* const exportedStart = &fSymbols[fDynamicSymbolTable->iextdefsym()]; | |
1020 | const macho_nlist<P>* const exportedEnd = &exportedStart[fDynamicSymbolTable->nextdefsym()]; | |
55e3d2f6 A |
1021 | int i = fDynamicSymbolTable->iextdefsym(); |
1022 | for(const macho_nlist<P>* p = exportedStart; p < exportedEnd; ++p, ++i) { | |
a61fdf0a | 1023 | const char* symName = &fStrings[p->n_strx()]; |
55e3d2f6 A |
1024 | if ( symName > fStringsEnd ) |
1025 | throw "string index out of range"; | |
1026 | //fprintf(stderr, "sym[%d] = %s\n", i, symName); | |
a61fdf0a A |
1027 | if ( externalNames.find(symName) != externalNames.end() ) |
1028 | throwf("duplicate external symbol: %s", symName); | |
a645023d A |
1029 | if ( (p->n_type() & N_EXT) == 0 ) |
1030 | throwf("non-external symbol in external symbol range: %s", symName); | |
1031 | // don't add N_INDR to externalNames because there is likely an undefine with same name | |
1032 | if ( (p->n_type() & N_INDR) == 0 ) | |
1033 | externalNames.insert(symName); | |
a61fdf0a | 1034 | } |
55e3d2f6 A |
1035 | // verify no undefines with same name as an external symbol |
1036 | const macho_nlist<P>* const undefinesStart = &fSymbols[fDynamicSymbolTable->iundefsym()]; | |
1037 | const macho_nlist<P>* const undefinesEnd = &undefinesStart[fDynamicSymbolTable->nundefsym()]; | |
1038 | for(const macho_nlist<P>* p = undefinesStart; p < undefinesEnd; ++p) { | |
1039 | const char* symName = &fStrings[p->n_strx()]; | |
1040 | if ( symName > fStringsEnd ) | |
1041 | throw "string index out of range"; | |
1042 | if ( externalNames.find(symName) != externalNames.end() ) | |
1043 | throwf("undefine with same name as external symbol: %s", symName); | |
1044 | } | |
c211e7c9 A |
1045 | // verify all N_SECT values are valid |
1046 | for(const macho_nlist<P>* p = fSymbols; p < &fSymbols[fSymbolCount]; ++p) { | |
1047 | uint8_t type = p->n_type(); | |
1048 | if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) { | |
1049 | if ( p->n_sect() > fSectionCount ) { | |
1050 | throwf("symbol '%s' has n_sect=%d which is too large", &fStrings[p->n_strx()], p->n_sect()); | |
1051 | } | |
1052 | } | |
1053 | } | |
a61fdf0a A |
1054 | } |
1055 | } | |
1056 | ||
69a49097 | 1057 | |
afe874b1 A |
1058 | template <typename A> |
1059 | void MachOChecker<A>::checkInitTerms() | |
1060 | { | |
1061 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
1062 | const uint32_t cmd_count = fHeader->ncmds(); | |
1063 | const macho_load_command<P>* cmd = cmds; | |
1064 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1065 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
1066 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
1067 | const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>)); | |
1068 | const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()]; | |
1069 | for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
1070 | // make sure all magic sections that use indirect symbol table fit within it | |
1071 | uint32_t count; | |
1072 | pint_t* arrayStart; | |
1073 | pint_t* arrayEnd; | |
1074 | const char* kind = "initializer"; | |
1075 | switch ( sect->flags() & SECTION_TYPE ) { | |
1076 | case S_MOD_TERM_FUNC_POINTERS: | |
1077 | kind = "terminator"; | |
1078 | // fall through | |
1079 | case S_MOD_INIT_FUNC_POINTERS: | |
1080 | count = sect->size() / sizeof(pint_t); | |
1081 | if ( (count*sizeof(pint_t)) != sect->size() ) | |
1082 | throwf("%s section size is not an even multiple of element size", sect->sectname()); | |
1083 | if ( (sect->addr() % sizeof(pint_t)) != 0 ) | |
1084 | throwf("%s section size is not pointer size aligned", sect->sectname()); | |
1085 | // check each pointer in array points within TEXT | |
1086 | arrayStart = (pint_t*)((char*)fHeader + sect->offset()); | |
1087 | arrayEnd = (pint_t*)((char*)fHeader + sect->offset() + sect->size()); | |
1088 | for (pint_t* p=arrayStart; p < arrayEnd; ++p) { | |
1089 | pint_t pointer = P::getP(*p); | |
1090 | if ( (pointer < fTEXTSegment->vmaddr()) || (pointer >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) ) | |
1091 | throwf("%s 0x%08llX points outside __TEXT segment", kind, (long long)pointer); | |
1092 | } | |
1093 | // check each pointer in array will be rebased and not bound | |
1094 | if ( fSlidableImage ) { | |
1095 | pint_t sectionBeginAddr = sect->addr(); | |
1096 | pint_t sectionEndddr = sect->addr() + sect->size(); | |
1097 | for(pint_t addr = sectionBeginAddr; addr < sectionEndddr; addr += sizeof(pint_t)) { | |
1098 | if ( addressIsBindingSite(addr) ) | |
1099 | throwf("%s at 0x%0llX has binding to external symbol", kind, (long long)addr); | |
1100 | if ( ! addressIsRebaseSite(addr) ) | |
1101 | throwf("%s at 0x%0llX is not rebased", kind, (long long)addr); | |
1102 | } | |
1103 | } | |
1104 | break; | |
1105 | } | |
1106 | } | |
1107 | } | |
1108 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
1109 | } | |
1110 | ||
1111 | } | |
1112 | ||
1113 | ||
69a49097 A |
1114 | |
1115 | template <> | |
1116 | x86::P::uint_t MachOChecker<x86>::relocBase() | |
1117 | { | |
1118 | if ( fHeader->flags() & MH_SPLIT_SEGS ) | |
1119 | return fFirstWritableSegment->vmaddr(); | |
1120 | else | |
1121 | return fFirstSegment->vmaddr(); | |
1122 | } | |
1123 | ||
1124 | template <> | |
1125 | x86_64::P::uint_t MachOChecker<x86_64>::relocBase() | |
1126 | { | |
1127 | // check for split-seg | |
1128 | return fFirstWritableSegment->vmaddr(); | |
1129 | } | |
1130 | ||
2f2f92e4 A |
1131 | template <> |
1132 | arm::P::uint_t MachOChecker<arm>::relocBase() | |
1133 | { | |
1134 | if ( fHeader->flags() & MH_SPLIT_SEGS ) | |
1135 | return fFirstWritableSegment->vmaddr(); | |
1136 | else | |
1137 | return fFirstSegment->vmaddr(); | |
1138 | } | |
1139 | ||
f80fe69f A |
1140 | template <> |
1141 | arm64::P::uint_t MachOChecker<arm64>::relocBase() | |
1142 | { | |
1143 | return fFirstWritableSegment->vmaddr(); | |
1144 | } | |
69a49097 | 1145 | |
0a8dc3df | 1146 | |
69a49097 A |
1147 | template <typename A> |
1148 | bool MachOChecker<A>::addressInWritableSegment(pint_t address) | |
1149 | { | |
1150 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
1151 | const uint32_t cmd_count = fHeader->ncmds(); | |
1152 | const macho_load_command<P>* cmd = cmds; | |
1153 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1154 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
1155 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
2f2f92e4 A |
1156 | if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) ) { |
1157 | // if segment is writable, we are fine | |
1158 | if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) | |
69a49097 | 1159 | return true; |
2f2f92e4 A |
1160 | // could be a text reloc, make sure section bit is set |
1161 | const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>)); | |
1162 | const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()]; | |
1163 | for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
1164 | if ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) { | |
1165 | // found section for this address, if has relocs we are fine | |
1166 | return ( (sect->flags() & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC)) != 0 ); | |
1167 | } | |
1168 | } | |
69a49097 A |
1169 | } |
1170 | } | |
1171 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
1172 | } | |
1173 | return false; | |
1174 | } | |
1175 | ||
1176 | ||
69a49097 A |
1177 | |
1178 | template <> | |
1179 | void MachOChecker<x86>::checkExternalReloation(const macho_relocation_info<P>* reloc) | |
1180 | { | |
a61fdf0a A |
1181 | if ( reloc->r_length() != 2 ) |
1182 | throw "bad external relocation length"; | |
1183 | if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) | |
1184 | throw "unknown external relocation type"; | |
1185 | if ( reloc->r_pcrel() != 0 ) | |
1186 | throw "bad external relocation pc_rel"; | |
1187 | if ( reloc->r_extern() == 0 ) | |
1188 | throw "local relocation found with external relocations"; | |
1189 | if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) | |
1190 | throw "external relocation address not in writable segment"; | |
1191 | // FIX: check r_symbol | |
69a49097 A |
1192 | } |
1193 | ||
1194 | ||
1195 | template <> | |
1196 | void MachOChecker<x86_64>::checkExternalReloation(const macho_relocation_info<P>* reloc) | |
1197 | { | |
1198 | if ( reloc->r_length() != 3 ) | |
1199 | throw "bad external relocation length"; | |
1200 | if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) | |
1201 | throw "unknown external relocation type"; | |
1202 | if ( reloc->r_pcrel() != 0 ) | |
1203 | throw "bad external relocation pc_rel"; | |
1204 | if ( reloc->r_extern() == 0 ) | |
1205 | throw "local relocation found with external relocations"; | |
1206 | if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) | |
1207 | throw "exernal relocation address not in writable segment"; | |
1208 | // FIX: check r_symbol | |
1209 | } | |
1210 | ||
ebf6f434 | 1211 | #if SUPPORT_ARCH_arm_any |
2f2f92e4 A |
1212 | template <> |
1213 | void MachOChecker<arm>::checkExternalReloation(const macho_relocation_info<P>* reloc) | |
1214 | { | |
1215 | if ( reloc->r_length() != 2 ) | |
1216 | throw "bad external relocation length"; | |
ec29ba20 | 1217 | if ( reloc->r_type() != ARM_RELOC_VANILLA ) |
2f2f92e4 A |
1218 | throw "unknown external relocation type"; |
1219 | if ( reloc->r_pcrel() != 0 ) | |
1220 | throw "bad external relocation pc_rel"; | |
1221 | if ( reloc->r_extern() == 0 ) | |
1222 | throw "local relocation found with external relocations"; | |
1223 | if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) | |
1224 | throw "external relocation address not in writable segment"; | |
1225 | // FIX: check r_symbol | |
1226 | } | |
ebf6f434 | 1227 | #endif |
2f2f92e4 | 1228 | |
f80fe69f A |
1229 | #if SUPPORT_ARCH_arm64 |
1230 | template <> | |
1231 | void MachOChecker<arm64>::checkExternalReloation(const macho_relocation_info<P>* reloc) | |
1232 | { | |
1233 | throw "external relocations not used for arm64"; | |
1234 | } | |
1235 | #endif | |
1236 | ||
2f2f92e4 | 1237 | |
69a49097 A |
1238 | template <> |
1239 | void MachOChecker<x86>::checkLocalReloation(const macho_relocation_info<P>* reloc) | |
1240 | { | |
1241 | // FIX | |
1242 | } | |
1243 | ||
1244 | template <> | |
1245 | void MachOChecker<x86_64>::checkLocalReloation(const macho_relocation_info<P>* reloc) | |
1246 | { | |
1247 | if ( reloc->r_length() != 3 ) | |
1248 | throw "bad local relocation length"; | |
1249 | if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) | |
1250 | throw "unknown local relocation type"; | |
1251 | if ( reloc->r_pcrel() != 0 ) | |
1252 | throw "bad local relocation pc_rel"; | |
1253 | if ( reloc->r_extern() != 0 ) | |
1254 | throw "external relocation found with local relocations"; | |
1255 | if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) | |
1256 | throw "local relocation address not in writable segment"; | |
1257 | } | |
1258 | ||
ebf6f434 | 1259 | #if SUPPORT_ARCH_arm_any |
2f2f92e4 A |
1260 | template <> |
1261 | void MachOChecker<arm>::checkLocalReloation(const macho_relocation_info<P>* reloc) | |
1262 | { | |
1263 | if ( reloc->r_address() & R_SCATTERED ) { | |
1264 | // scattered | |
1265 | const macho_scattered_relocation_info<P>* sreloc = (const macho_scattered_relocation_info<P>*)reloc; | |
1266 | if ( sreloc->r_length() != 2 ) | |
1267 | throw "bad local scattered relocation length"; | |
1268 | if ( sreloc->r_type() != ARM_RELOC_PB_LA_PTR ) | |
1269 | throw "bad local scattered relocation type"; | |
1270 | } | |
1271 | else { | |
1272 | if ( reloc->r_length() != 2 ) | |
1273 | throw "bad local relocation length"; | |
1274 | if ( reloc->r_extern() != 0 ) | |
1275 | throw "external relocation found with local relocations"; | |
1276 | if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) | |
1277 | throw "local relocation address not in writable segment"; | |
1278 | } | |
1279 | } | |
ebf6f434 | 1280 | #endif |
69a49097 | 1281 | |
f80fe69f A |
1282 | #if SUPPORT_ARCH_arm64 |
1283 | template <> | |
1284 | void MachOChecker<arm64>::checkLocalReloation(const macho_relocation_info<P>* reloc) | |
1285 | { | |
1286 | throw "local relocations not used for arm64"; | |
1287 | } | |
1288 | #endif | |
1289 | ||
0a8dc3df | 1290 | |
69a49097 A |
1291 | template <typename A> |
1292 | void MachOChecker<A>::checkRelocations() | |
1293 | { | |
a61fdf0a A |
1294 | // external relocations should be sorted to minimize dyld symbol lookups |
1295 | // therefore every reloc with the same r_symbolnum value should be contiguous | |
1296 | std::set<uint32_t> previouslySeenSymbolIndexes; | |
1297 | uint32_t lastSymbolIndex = 0xFFFFFFFF; | |
69a49097 A |
1298 | const macho_relocation_info<P>* const externRelocsEnd = &fExternalRelocations[fExternalRelocationsCount]; |
1299 | for (const macho_relocation_info<P>* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) { | |
1300 | this->checkExternalReloation(reloc); | |
a61fdf0a A |
1301 | if ( reloc->r_symbolnum() != lastSymbolIndex ) { |
1302 | if ( previouslySeenSymbolIndexes.count(reloc->r_symbolnum()) != 0 ) | |
1303 | throw "external relocations not sorted"; | |
1304 | previouslySeenSymbolIndexes.insert(lastSymbolIndex); | |
1305 | lastSymbolIndex = reloc->r_symbolnum(); | |
1306 | } | |
69a49097 A |
1307 | } |
1308 | ||
1309 | const macho_relocation_info<P>* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount]; | |
1310 | for (const macho_relocation_info<P>* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { | |
1311 | this->checkLocalReloation(reloc); | |
1312 | } | |
a645023d A |
1313 | |
1314 | // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs | |
1315 | const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); | |
1316 | const uint32_t cmd_count = fHeader->ncmds(); | |
1317 | const macho_load_command<P>* cmd = cmds; | |
1318 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1319 | if ( cmd->cmd() == macho_segment_command<P>::CMD ) { | |
1320 | const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; | |
1321 | // if segment is writable, we are fine | |
1322 | if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) | |
1323 | continue; | |
1324 | // look at sections that have text reloc bit set | |
1325 | const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>)); | |
1326 | const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()]; | |
1327 | for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { | |
1328 | if ( (sect->flags() & S_ATTR_LOC_RELOC) != 0 ) { | |
1329 | if ( ! hasTextRelocInRange(sect->addr(), sect->addr()+sect->size()) ) { | |
1330 | throwf("section %s has attribute set that it has relocs, but it has none", sect->sectname()); | |
1331 | } | |
1332 | } | |
1333 | } | |
1334 | } | |
1335 | cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); | |
1336 | } | |
69a49097 A |
1337 | } |
1338 | ||
a645023d A |
1339 | template <typename A> |
1340 | typename A::P::uint_t MachOChecker<A>::segStartAddress(uint8_t segIndex) | |
1341 | { | |
1342 | if ( segIndex > fSegments.size() ) | |
1343 | throw "segment index out of range"; | |
1344 | return fSegments[segIndex]->vmaddr(); | |
1345 | } | |
1346 | ||
1347 | template <typename A> | |
1348 | bool MachOChecker<A>::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd) | |
1349 | { | |
1350 | // look at local relocs | |
1351 | const macho_relocation_info<P>* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount]; | |
1352 | for (const macho_relocation_info<P>* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { | |
1353 | pint_t relocAddress = reloc->r_address() + this->relocBase(); | |
1354 | if ( (rangeStart <= relocAddress) && (relocAddress < rangeEnd) ) | |
1355 | return true; | |
1356 | } | |
1357 | // look rebase info | |
1358 | if ( fDyldInfo != NULL ) { | |
1359 | const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->rebase_off(); | |
1360 | const uint8_t* end = &p[fDyldInfo->rebase_size()]; | |
1361 | ||
1362 | uint8_t type = 0; | |
1363 | uint64_t segOffset = 0; | |
1364 | uint32_t count; | |
1365 | uint32_t skip; | |
1366 | int segIndex; | |
1367 | pint_t segStartAddr = 0; | |
1368 | pint_t addr; | |
1369 | bool done = false; | |
1370 | while ( !done && (p < end) ) { | |
1371 | uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; | |
1372 | uint8_t opcode = *p & REBASE_OPCODE_MASK; | |
1373 | ++p; | |
1374 | switch (opcode) { | |
1375 | case REBASE_OPCODE_DONE: | |
1376 | done = true; | |
1377 | break; | |
1378 | case REBASE_OPCODE_SET_TYPE_IMM: | |
1379 | type = immediate; | |
1380 | break; | |
1381 | case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
1382 | segIndex = immediate; | |
1383 | segStartAddr = segStartAddress(segIndex); | |
1384 | segOffset = read_uleb128(p, end); | |
1385 | break; | |
1386 | case REBASE_OPCODE_ADD_ADDR_ULEB: | |
1387 | segOffset += read_uleb128(p, end); | |
1388 | break; | |
1389 | case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: | |
1390 | segOffset += immediate*sizeof(pint_t); | |
1391 | break; | |
1392 | case REBASE_OPCODE_DO_REBASE_IMM_TIMES: | |
1393 | for (int i=0; i < immediate; ++i) { | |
1394 | addr = segStartAddr+segOffset; | |
1395 | if ( (rangeStart <= addr) && (addr < rangeEnd) ) | |
1396 | return true; | |
1397 | //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); | |
1398 | segOffset += sizeof(pint_t); | |
1399 | } | |
1400 | break; | |
1401 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: | |
1402 | count = read_uleb128(p, end); | |
1403 | for (uint32_t i=0; i < count; ++i) { | |
1404 | addr = segStartAddr+segOffset; | |
1405 | if ( (rangeStart <= addr) && (addr < rangeEnd) ) | |
1406 | return true; | |
1407 | //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); | |
1408 | segOffset += sizeof(pint_t); | |
1409 | } | |
1410 | break; | |
1411 | case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: | |
1412 | addr = segStartAddr+segOffset; | |
1413 | if ( (rangeStart <= addr) && (addr < rangeEnd) ) | |
1414 | return true; | |
1415 | //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); | |
1416 | segOffset += read_uleb128(p, end) + sizeof(pint_t); | |
1417 | break; | |
1418 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: | |
1419 | count = read_uleb128(p, end); | |
1420 | skip = read_uleb128(p, end); | |
1421 | for (uint32_t i=0; i < count; ++i) { | |
1422 | addr = segStartAddr+segOffset; | |
1423 | if ( (rangeStart <= addr) && (addr < rangeEnd) ) | |
1424 | return true; | |
1425 | //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); | |
1426 | segOffset += skip + sizeof(pint_t); | |
1427 | } | |
1428 | break; | |
1429 | default: | |
1430 | throwf("bad rebase opcode %d", *p); | |
1431 | } | |
1432 | } | |
1433 | } | |
ebf6f434 | 1434 | return false; |
a645023d | 1435 | } |
69a49097 | 1436 | |
afe874b1 A |
1437 | template <typename A> |
1438 | bool MachOChecker<A>::addressIsRebaseSite(pint_t targetAddr) | |
1439 | { | |
1440 | // look at local relocs | |
1441 | const macho_relocation_info<P>* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount]; | |
1442 | for (const macho_relocation_info<P>* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { | |
1443 | pint_t relocAddress = reloc->r_address() + this->relocBase(); | |
1444 | if ( relocAddress == targetAddr ) | |
1445 | return true; | |
1446 | } | |
1447 | // look rebase info | |
1448 | if ( fDyldInfo != NULL ) { | |
1449 | const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->rebase_off(); | |
1450 | const uint8_t* end = &p[fDyldInfo->rebase_size()]; | |
1451 | ||
1452 | uint8_t type = 0; | |
1453 | uint64_t segOffset = 0; | |
1454 | uint32_t count; | |
1455 | uint32_t skip; | |
1456 | int segIndex; | |
1457 | pint_t segStartAddr = 0; | |
1458 | pint_t addr; | |
1459 | bool done = false; | |
1460 | while ( !done && (p < end) ) { | |
1461 | uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; | |
1462 | uint8_t opcode = *p & REBASE_OPCODE_MASK; | |
1463 | ++p; | |
1464 | switch (opcode) { | |
1465 | case REBASE_OPCODE_DONE: | |
1466 | done = true; | |
1467 | break; | |
1468 | case REBASE_OPCODE_SET_TYPE_IMM: | |
1469 | type = immediate; | |
1470 | break; | |
1471 | case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
1472 | segIndex = immediate; | |
1473 | segStartAddr = segStartAddress(segIndex); | |
1474 | segOffset = read_uleb128(p, end); | |
1475 | break; | |
1476 | case REBASE_OPCODE_ADD_ADDR_ULEB: | |
1477 | segOffset += read_uleb128(p, end); | |
1478 | break; | |
1479 | case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: | |
1480 | segOffset += immediate*sizeof(pint_t); | |
1481 | break; | |
1482 | case REBASE_OPCODE_DO_REBASE_IMM_TIMES: | |
1483 | for (int i=0; i < immediate; ++i) { | |
1484 | addr = segStartAddr+segOffset; | |
1485 | if ( addr == targetAddr ) | |
1486 | return true; | |
1487 | //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); | |
1488 | segOffset += sizeof(pint_t); | |
1489 | } | |
1490 | break; | |
1491 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: | |
1492 | count = read_uleb128(p, end); | |
1493 | for (uint32_t i=0; i < count; ++i) { | |
1494 | addr = segStartAddr+segOffset; | |
1495 | if ( addr == targetAddr ) | |
1496 | return true; | |
1497 | //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); | |
1498 | segOffset += sizeof(pint_t); | |
1499 | } | |
1500 | break; | |
1501 | case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: | |
1502 | addr = segStartAddr+segOffset; | |
1503 | if ( addr == targetAddr ) | |
1504 | return true; | |
1505 | //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); | |
1506 | segOffset += read_uleb128(p, end) + sizeof(pint_t); | |
1507 | break; | |
1508 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: | |
1509 | count = read_uleb128(p, end); | |
1510 | skip = read_uleb128(p, end); | |
1511 | for (uint32_t i=0; i < count; ++i) { | |
1512 | addr = segStartAddr+segOffset; | |
1513 | if ( addr == targetAddr ) | |
1514 | return true; | |
1515 | //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); | |
1516 | segOffset += skip + sizeof(pint_t); | |
1517 | } | |
1518 | break; | |
1519 | default: | |
1520 | throwf("bad rebase opcode %d", *p); | |
1521 | } | |
1522 | } | |
1523 | } | |
1524 | return false; | |
1525 | } | |
1526 | ||
1527 | ||
1528 | template <typename A> | |
1529 | bool MachOChecker<A>::addressIsBindingSite(pint_t targetAddr) | |
1530 | { | |
1531 | // look at external relocs | |
1532 | const macho_relocation_info<P>* const externRelocsEnd = &fExternalRelocations[fExternalRelocationsCount]; | |
1533 | for (const macho_relocation_info<P>* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) { | |
1534 | pint_t relocAddress = reloc->r_address() + this->relocBase(); | |
1535 | if ( relocAddress == targetAddr ) | |
1536 | return true; | |
1537 | } | |
1538 | // look bind info | |
1539 | if ( fDyldInfo != NULL ) { | |
1540 | const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->bind_off(); | |
1541 | const uint8_t* end = &p[fDyldInfo->bind_size()]; | |
1542 | ||
1543 | uint8_t type = 0; | |
1544 | uint64_t segOffset = 0; | |
1545 | uint32_t count; | |
1546 | uint32_t skip; | |
1547 | uint8_t flags; | |
1548 | const char* symbolName = NULL; | |
1549 | int libraryOrdinal = 0; | |
1550 | int segIndex; | |
1551 | int64_t addend = 0; | |
1552 | pint_t segStartAddr = 0; | |
1553 | pint_t addr; | |
1554 | bool done = false; | |
1555 | while ( !done && (p < end) ) { | |
1556 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1557 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1558 | ++p; | |
1559 | switch (opcode) { | |
1560 | case BIND_OPCODE_DONE: | |
1561 | done = true; | |
1562 | break; | |
1563 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
1564 | libraryOrdinal = immediate; | |
1565 | break; | |
1566 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
1567 | libraryOrdinal = read_uleb128(p, end); | |
1568 | break; | |
1569 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
1570 | // the special ordinals are negative numbers | |
1571 | if ( immediate == 0 ) | |
1572 | libraryOrdinal = 0; | |
1573 | else { | |
1574 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
1575 | libraryOrdinal = signExtended; | |
1576 | } | |
1577 | break; | |
1578 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1579 | symbolName = (char*)p; | |
1580 | while (*p != '\0') | |
1581 | ++p; | |
1582 | ++p; | |
1583 | break; | |
1584 | case BIND_OPCODE_SET_TYPE_IMM: | |
1585 | type = immediate; | |
1586 | break; | |
1587 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1588 | addend = read_sleb128(p, end); | |
1589 | break; | |
1590 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
1591 | segIndex = immediate; | |
1592 | segStartAddr = segStartAddress(segIndex); | |
1593 | segOffset = read_uleb128(p, end); | |
1594 | break; | |
1595 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1596 | segOffset += read_uleb128(p, end); | |
1597 | break; | |
1598 | case BIND_OPCODE_DO_BIND: | |
1599 | if ( (segStartAddr+segOffset) == targetAddr ) | |
1600 | return true; | |
1601 | segOffset += sizeof(pint_t); | |
1602 | break; | |
1603 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1604 | if ( (segStartAddr+segOffset) == targetAddr ) | |
1605 | return true; | |
1606 | segOffset += read_uleb128(p, end) + sizeof(pint_t); | |
1607 | break; | |
1608 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1609 | if ( (segStartAddr+segOffset) == targetAddr ) | |
1610 | return true; | |
1611 | segOffset += immediate*sizeof(pint_t) + sizeof(pint_t); | |
1612 | break; | |
1613 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1614 | count = read_uleb128(p, end); | |
1615 | skip = read_uleb128(p, end); | |
1616 | for (uint32_t i=0; i < count; ++i) { | |
1617 | if ( (segStartAddr+segOffset) == targetAddr ) | |
1618 | return true; | |
1619 | segOffset += skip + sizeof(pint_t); | |
1620 | } | |
1621 | break; | |
1622 | default: | |
1623 | throwf("bad bind opcode %d", *p); | |
1624 | } | |
1625 | } | |
1626 | } | |
1627 | return false; | |
1628 | } | |
1629 | ||
1630 | ||
ec29ba20 | 1631 | static void check(const char* path, const char* verifierDstRoot) |
d696c285 A |
1632 | { |
1633 | struct stat stat_buf; | |
1634 | ||
1635 | try { | |
1636 | int fd = ::open(path, O_RDONLY, 0); | |
1637 | if ( fd == -1 ) | |
1638 | throw "cannot open file"; | |
55e3d2f6 A |
1639 | if ( ::fstat(fd, &stat_buf) != 0 ) |
1640 | throwf("fstat(%s) failed, errno=%d\n", path, errno); | |
d696c285 | 1641 | uint32_t length = stat_buf.st_size; |
69a49097 A |
1642 | uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); |
1643 | if ( p == ((uint8_t*)(-1)) ) | |
1644 | throw "cannot map file"; | |
d696c285 A |
1645 | ::close(fd); |
1646 | const mach_header* mh = (mach_header*)p; | |
1647 | if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { | |
1648 | const struct fat_header* fh = (struct fat_header*)p; | |
1649 | const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); | |
a61fdf0a A |
1650 | for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { |
1651 | size_t offset = OSSwapBigToHostInt32(archs[i].offset); | |
1652 | size_t size = OSSwapBigToHostInt32(archs[i].size); | |
1653 | unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype); | |
1654 | ||
1655 | switch(cputype) { | |
a61fdf0a A |
1656 | case CPU_TYPE_I386: |
1657 | if ( MachOChecker<x86>::validFile(p + offset) ) | |
ec29ba20 | 1658 | MachOChecker<x86>::make(p + offset, size, path, verifierDstRoot); |
d696c285 A |
1659 | else |
1660 | throw "in universal file, i386 slice does not contain i386 mach-o"; | |
a61fdf0a | 1661 | break; |
a61fdf0a A |
1662 | case CPU_TYPE_X86_64: |
1663 | if ( MachOChecker<x86_64>::validFile(p + offset) ) | |
ec29ba20 | 1664 | MachOChecker<x86_64>::make(p + offset, size, path, verifierDstRoot); |
69a49097 A |
1665 | else |
1666 | throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; | |
a61fdf0a | 1667 | break; |
ebf6f434 | 1668 | #if SUPPORT_ARCH_arm_any |
2f2f92e4 A |
1669 | case CPU_TYPE_ARM: |
1670 | if ( MachOChecker<arm>::validFile(p + offset) ) | |
ec29ba20 | 1671 | MachOChecker<arm>::make(p + offset, size, path, verifierDstRoot); |
2f2f92e4 A |
1672 | else |
1673 | throw "in universal file, arm slice does not contain arm mach-o"; | |
1674 | break; | |
ec29ba20 A |
1675 | #endif |
1676 | #if SUPPORT_ARCH_arm64 | |
1677 | case CPU_TYPE_ARM64: | |
1678 | if ( MachOChecker<arm64>::validFile(p + offset) ) | |
1679 | MachOChecker<arm64>::make(p + offset, size, path, verifierDstRoot); | |
1680 | else | |
1681 | throw "in universal file, arm64 slice does not contain arm mach-o"; | |
1682 | break; | |
ebf6f434 | 1683 | #endif |
a61fdf0a A |
1684 | default: |
1685 | throwf("in universal file, unknown architecture slice 0x%x\n", cputype); | |
d696c285 A |
1686 | } |
1687 | } | |
1688 | } | |
1689 | else if ( MachOChecker<x86>::validFile(p) ) { | |
ec29ba20 | 1690 | MachOChecker<x86>::make(p, length, path, verifierDstRoot); |
d696c285 | 1691 | } |
69a49097 | 1692 | else if ( MachOChecker<x86_64>::validFile(p) ) { |
ec29ba20 | 1693 | MachOChecker<x86_64>::make(p, length, path, verifierDstRoot); |
69a49097 | 1694 | } |
ebf6f434 | 1695 | #if SUPPORT_ARCH_arm_any |
2f2f92e4 | 1696 | else if ( MachOChecker<arm>::validFile(p) ) { |
ec29ba20 | 1697 | MachOChecker<arm>::make(p, length, path, verifierDstRoot); |
2f2f92e4 | 1698 | } |
f80fe69f A |
1699 | #endif |
1700 | #if SUPPORT_ARCH_arm64 | |
1701 | else if ( MachOChecker<arm64>::validFile(p) ) { | |
ec29ba20 | 1702 | MachOChecker<arm64>::make(p, length, path, verifierDstRoot); |
f80fe69f | 1703 | } |
ebf6f434 | 1704 | #endif |
d696c285 A |
1705 | else { |
1706 | throw "not a known file type"; | |
1707 | } | |
1708 | } | |
1709 | catch (const char* msg) { | |
1710 | throwf("%s in %s", msg, path); | |
1711 | } | |
1712 | } | |
1713 | ||
1714 | ||
1715 | int main(int argc, const char* argv[]) | |
1716 | { | |
afe874b1 | 1717 | bool progress = false; |
ec29ba20 | 1718 | const char* verifierDstRoot = NULL; |
afe874b1 A |
1719 | int result = 0; |
1720 | for(int i=1; i < argc; ++i) { | |
1721 | const char* arg = argv[i]; | |
1722 | if ( arg[0] == '-' ) { | |
1723 | if ( strcmp(arg, "-progress") == 0 ) { | |
1724 | progress = true; | |
d696c285 | 1725 | } |
ec29ba20 A |
1726 | else if ( strcmp(arg, "-verifier_dstroot") == 0 ) { |
1727 | verifierDstRoot = argv[++i]; | |
1728 | } | |
1729 | else if ( strcmp(arg, "-verifier_error_list") == 0 ) { | |
1730 | printf("os_dylib_rpath_install_name\tOS dylibs (those in /usr/lib/ or /System/Library/) must be built with -install_name that is an absolute path - not an @rpath\n"); | |
1731 | printf("os_dylib_bad_install_name\tOS dylibs (those in /usr/lib/ or /System/Library/) must be built with -install_name matching their file system location\n"); | |
1732 | printf("os_dylib_rpath\tOS dylibs should not contain LC_RPATH load commands (from -rpath linker option)\n"); | |
1733 | printf("os_dylib_flat_namespace\tOS dylibs should not be built with -flat_namespace\n"); | |
1734 | printf("os_dylib_undefined_dynamic_lookup\tOS dylibs should not be built with -undefined dynamic_lookup\n"); | |
1735 | printf("os_dylib_malformed\the mach-o is malformed\n"); | |
1736 | return 0; | |
1737 | } | |
d696c285 | 1738 | else { |
afe874b1 A |
1739 | throwf("unknown option: %s\n", arg); |
1740 | } | |
1741 | } | |
1742 | else { | |
1743 | bool success = true; | |
1744 | try { | |
ec29ba20 | 1745 | check(arg, verifierDstRoot); |
d696c285 | 1746 | } |
afe874b1 | 1747 | catch (const char* msg) { |
ec29ba20 A |
1748 | if ( verifierDstRoot ) { |
1749 | printf("os_dylib_malformed\twarn\t%s\n", msg); | |
1750 | } | |
1751 | else { | |
1752 | fprintf(stderr, "machocheck failed: %s\n", msg); | |
1753 | result = 1; | |
1754 | success = false; | |
1755 | } | |
afe874b1 A |
1756 | } |
1757 | if ( success && progress ) | |
1758 | printf("ok: %s\n", arg); | |
d696c285 A |
1759 | } |
1760 | } | |
d696c285 | 1761 | |
afe874b1 | 1762 | return result; |
d696c285 A |
1763 | } |
1764 |