]> git.saurik.com Git - apple/dyld.git/blob - launch-cache/MachOLayout.hpp
dyld-353.2.1.tar.gz
[apple/dyld.git] / launch-cache / MachOLayout.hpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2006-2011 Apple Inc. All rights reserved.
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 #ifndef __MACHO_LAYOUT__
26 #define __MACHO_LAYOUT__
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/errno.h>
31 #include <sys/mman.h>
32 #include <mach/mach.h>
33 #include <limits.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <mach-o/loader.h>
40 #include <mach-o/fat.h>
41
42 #include <vector>
43 #include <set>
44 #include <unordered_map>
45
46 #include "MachOFileAbstraction.hpp"
47 #include "Architectures.hpp"
48
49
50 void throwf(const char* format, ...) __attribute__((format(printf, 1, 2)));
51
52 __attribute__((noreturn))
53 void throwf(const char* format, ...)
54 {
55 va_list list;
56 char* p;
57 va_start(list, format);
58 vasprintf(&p, format, list);
59 va_end(list);
60
61 const char* t = p;
62 throw t;
63 }
64
65
66 class MachOLayoutAbstraction
67 {
68 public:
69 struct Segment
70 {
71 public:
72 Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t align,
73 uint32_t prot, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize),
74 fOrigFileOffset(offset), fOrigFileSize(file_size), fOrigPermissions(prot),
75 fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align),
76 fPermissions(prot), fNewAddress(0), fMappedAddress(NULL) {
77 strlcpy(fOrigName, segName, 16);
78 }
79
80 uint64_t address() const { return fOrigAddress; }
81 uint64_t size() const { return fSize; }
82 uint64_t fileOffset() const { return fFileOffset; }
83 uint64_t fileSize() const { return fFileSize; }
84 uint32_t permissions() const { return fPermissions; }
85 bool readable() const { return fPermissions & VM_PROT_READ; }
86 bool writable() const { return fPermissions & VM_PROT_WRITE; }
87 bool executable() const { return fPermissions & VM_PROT_EXECUTE; }
88 uint64_t alignment() const { return fAlignment; }
89 const char* name() const { return fOrigName; }
90 uint64_t newAddress() const { return fNewAddress; }
91 void* mappedAddress() const { return fMappedAddress; }
92 void setNewAddress(uint64_t addr) { fNewAddress = addr; }
93 void setMappedAddress(void* addr) { fMappedAddress = addr; }
94 void setSize(uint64_t new_size) { fSize = new_size; }
95 void setFileOffset(uint64_t new_off) { fFileOffset = new_off; }
96 void setFileSize(uint64_t new_size) { fFileSize = new_size; }
97 void setWritable(bool w) { if (w) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; }
98 void reset() { fSize=fOrigSize; fFileOffset=fOrigFileOffset; fFileSize=fOrigFileSize; fPermissions=fOrigPermissions; }
99 private:
100 uint64_t fOrigAddress;
101 uint64_t fOrigSize;
102 uint64_t fOrigFileOffset;
103 uint64_t fOrigFileSize;
104 uint32_t fOrigPermissions;
105 char fOrigName[16];
106 uint64_t fSize;
107 uint64_t fFileOffset;
108 uint64_t fFileSize;
109 uint64_t fAlignment;
110 uint32_t fPermissions;
111 uint64_t fNewAddress;
112 void* fMappedAddress;
113 };
114
115 struct Library
116 {
117 const char* name;
118 uint32_t currentVersion;
119 uint32_t compatibilityVersion;
120 bool weakImport;
121 };
122
123
124 virtual ArchPair getArchPair() const = 0;
125 virtual const char* getFilePath() const = 0;
126 virtual uint64_t getOffsetInUniversalFile() const = 0;
127 virtual uint32_t getFileType() const = 0;
128 virtual uint32_t getFlags() const = 0;
129 virtual Library getID() const = 0;
130 virtual bool isDylib() const = 0;
131 virtual bool isSplitSeg() const = 0;
132 virtual bool hasSplitSegInfo() const = 0;
133 virtual bool isRootOwned() const = 0;
134 virtual bool inSharableLocation() const = 0;
135 virtual bool hasDynamicLookupLinkage() const = 0;
136 virtual bool hasMainExecutableLookupLinkage() const = 0;
137 virtual bool isTwoLevelNamespace() const = 0;
138 virtual bool hasDyldInfo() const = 0;
139 virtual bool hasMultipleReadWriteSegments() const = 0;
140 virtual uint32_t getNameFileOffset() const = 0;
141 virtual time_t getLastModTime() const = 0;
142 virtual ino_t getInode() const = 0;
143 virtual std::vector<Segment>& getSegments() = 0;
144 virtual const std::vector<Segment>& getSegments() const = 0;
145 virtual const Segment* getSegment(const char* name) const = 0;
146 virtual const std::vector<Library>& getLibraries() const = 0;
147 virtual uint64_t getBaseAddress() const = 0;
148 virtual uint64_t getVMSize() const = 0;
149 virtual uint64_t getBaseExecutableAddress() const = 0;
150 virtual uint64_t getBaseWritableAddress() const = 0;
151 virtual uint64_t getBaseReadOnlyAddress() const = 0;
152 virtual uint64_t getExecutableVMSize() const = 0;
153 virtual uint64_t getWritableVMSize() const = 0;
154 virtual uint64_t getReadOnlyVMSize() const = 0;
155 // need getDyldInfoExports because export info uses ULEB encoding and size could grow
156 virtual const uint8_t* getDyldInfoExports() const = 0;
157 virtual void setDyldInfoExports(const uint8_t* newExports) const = 0;
158 virtual void uuid(uuid_t u) const = 0;
159 };
160
161
162
163
164 template <typename A>
165 class MachOLayout : public MachOLayoutAbstraction
166 {
167 public:
168 MachOLayout(const void* machHeader, uint64_t offset, const char* path,
169 ino_t inode, time_t modTime, uid_t uid);
170 virtual ~MachOLayout() {}
171
172 virtual ArchPair getArchPair() const { return fArchPair; }
173 virtual const char* getFilePath() const { return fPath; }
174 virtual uint64_t getOffsetInUniversalFile() const { return fOffset; }
175 virtual uint32_t getFileType() const { return fFileType; }
176 virtual uint32_t getFlags() const { return fFlags; }
177 virtual Library getID() const { return fDylibID; }
178 virtual bool isDylib() const { return fIsDylib; }
179 virtual bool isSplitSeg() const;
180 virtual bool hasSplitSegInfo() const { return fHasSplitSegInfo; }
181 virtual bool isRootOwned() const { return fRootOwned; }
182 virtual bool inSharableLocation() const { return fShareableLocation; }
183 virtual bool hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; }
184 virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; }
185 virtual bool isTwoLevelNamespace() const { return (fFlags & MH_TWOLEVEL); }
186 virtual bool hasDyldInfo() const { return fHasDyldInfo; }
187 virtual bool hasMultipleReadWriteSegments() const { return fHasTooManyWritableSegments; }
188 virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; }
189 virtual time_t getLastModTime() const { return fMTime; }
190 virtual ino_t getInode() const { return fInode; }
191 virtual std::vector<Segment>& getSegments() { return fSegments; }
192 virtual const std::vector<Segment>& getSegments() const { return fSegments; }
193 virtual const Segment* getSegment(const char* name) const;
194 virtual const std::vector<Library>& getLibraries() const { return fLibraries; }
195 virtual uint64_t getBaseAddress() const { return fLowSegment->address(); }
196 virtual uint64_t getVMSize() const { return fVMSize; }
197 virtual uint64_t getBaseExecutableAddress() const { return fLowExecutableSegment->address(); }
198 virtual uint64_t getBaseWritableAddress() const { return fLowWritableSegment->address(); }
199 virtual uint64_t getBaseReadOnlyAddress() const { return fLowReadOnlySegment->address(); }
200 virtual uint64_t getExecutableVMSize() const { return fVMExecutableSize; }
201 virtual uint64_t getWritableVMSize() const { return fVMWritablSize; }
202 virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; }
203 virtual const uint8_t* getDyldInfoExports() const { return fDyldInfoExports; }
204 virtual void setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; }
205 virtual void uuid(uuid_t u) const { memcpy(u, fUUID, 16); }
206
207 private:
208 typedef typename A::P P;
209 typedef typename A::P::E E;
210 typedef typename A::P::uint_t pint_t;
211
212 uint64_t segmentSize(const macho_segment_command<typename A::P>* segCmd) const;
213 uint64_t segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const;
214 uint64_t segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const;
215 bool validReadWriteSeg(const Segment& seg) const;
216
217 static cpu_type_t arch();
218
219 const char* fPath;
220 uint64_t fOffset;
221 uint32_t fFileType;
222 ArchPair fArchPair;
223 uint32_t fFlags;
224 std::vector<Segment> fSegments;
225 std::vector<Library> fLibraries;
226 const Segment* fLowSegment;
227 const Segment* fLowExecutableSegment;
228 const Segment* fLowWritableSegment;
229 const Segment* fLowReadOnlySegment;
230 Library fDylibID;
231 uint32_t fNameFileOffset;
232 time_t fMTime;
233 ino_t fInode;
234 uint64_t fVMSize;
235 uint64_t fVMExecutableSize;
236 uint64_t fVMWritablSize;
237 uint64_t fVMReadOnlySize;
238 bool fHasSplitSegInfo;
239 bool fRootOwned;
240 bool fShareableLocation;
241 bool fDynamicLookupLinkage;
242 bool fMainExecutableLookupLinkage;
243 bool fIsDylib;
244 bool fHasDyldInfo;
245 bool fHasTooManyWritableSegments;
246 mutable const uint8_t* fDyldInfoExports;
247 uuid_t fUUID;
248 };
249
250
251
252 class UniversalMachOLayout
253 {
254 public:
255 UniversalMachOLayout(const char* path, const std::set<ArchPair>* onlyArchs=NULL);
256 ~UniversalMachOLayout() {}
257
258 static const UniversalMachOLayout& find(const char* path, const std::set<ArchPair>* onlyArchs=NULL);
259 const MachOLayoutAbstraction* getSlice(ArchPair ap) const;
260 const std::vector<MachOLayoutAbstraction*>& allLayouts() const { return fLayouts; }
261
262 private:
263 class CStringHash {
264 public:
265 size_t operator()(const char* __s) const {
266 size_t __h = 0;
267 for ( ; *__s; ++__s)
268 __h = 5 * __h + *__s;
269 return __h;
270 };
271 };
272 struct CStringEquals {
273 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
274 };
275 typedef std::unordered_map<const char*, const UniversalMachOLayout*, CStringHash, CStringEquals> PathToNode;
276
277 static bool requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType);
278
279 static PathToNode fgLayoutCache;
280 const char* fPath;
281 std::vector<MachOLayoutAbstraction*> fLayouts;
282 };
283
284 UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache;
285
286
287
288
289 const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const
290 {
291 // use matching cputype and cpusubtype
292 for(std::vector<MachOLayoutAbstraction*>::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) {
293 const MachOLayoutAbstraction* layout = *it;
294 if ( layout->getArchPair().arch == ap.arch ) {
295 switch ( ap.arch ) {
296 case CPU_TYPE_ARM:
297 case CPU_TYPE_X86_64:
298 if ( (layout->getArchPair().subtype & ~CPU_SUBTYPE_MASK) == (ap.subtype & ~CPU_SUBTYPE_MASK) )
299 return layout;
300 break;
301 default:
302 return layout;
303 }
304 }
305 }
306 // if requesting x86_64h and it did not exist, try x86_64 as a fallback
307 if ((ap.arch == CPU_TYPE_X86_64) && (ap.subtype == CPU_SUBTYPE_X86_64_H)) {
308 ap.subtype = CPU_SUBTYPE_X86_64_ALL;
309 return this->getSlice(ap);
310 }
311 return NULL;
312 }
313
314
315 const UniversalMachOLayout& UniversalMachOLayout::find(const char* path, const std::set<ArchPair>* onlyArchs)
316 {
317 // look in cache
318 PathToNode::iterator pos = fgLayoutCache.find(path);
319 if ( pos != fgLayoutCache.end() )
320 return *pos->second;
321
322 // create UniversalMachOLayout
323 const UniversalMachOLayout* result = new UniversalMachOLayout(path, onlyArchs);
324
325 // add it to cache
326 fgLayoutCache[result->fPath] = result;
327
328 return *result;
329 }
330
331
332 bool UniversalMachOLayout::requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType)
333 {
334 if ( onlyArchs == NULL )
335 return true;
336 // must match cputype and cpusubtype
337 for (std::set<ArchPair>::const_iterator it = onlyArchs->begin(); it != onlyArchs->end(); ++it) {
338 ArchPair anArch = *it;
339 if ( cpuType == anArch.arch ) {
340 switch ( cpuType ) {
341 case CPU_TYPE_ARM:
342 if ( cpuSubType == anArch.subtype )
343 return true;
344 break;
345 default:
346 return true;
347 }
348 }
349 }
350 return false;
351 }
352
353
354 UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set<ArchPair>* onlyArchs)
355 : fPath(strdup(path))
356 {
357 // map in whole file
358 int fd = ::open(path, O_RDONLY, 0);
359 if ( fd == -1 ) {
360 int err = errno;
361 if ( err == ENOENT )
362 throwf("file not found");
363 else
364 throwf("can't open file, errno=%d", err);
365 }
366 struct stat stat_buf;
367 if ( fstat(fd, &stat_buf) == -1)
368 throwf("can't stat open file %s, errno=%d", path, errno);
369 if ( stat_buf.st_size < 20 )
370 throwf("file too small %s", path);
371 uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
372 if ( p == (uint8_t*)(-1) )
373 throwf("can't map file %s, errno=%d", path, errno);
374 ::close(fd);
375
376 try {
377 // if fat file, process each architecture
378 const fat_header* fh = (fat_header*)p;
379 const mach_header* mh = (mach_header*)p;
380 if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
381 // Fat header is always big-endian
382 const struct fat_arch* slices = (struct fat_arch*)(p + sizeof(struct fat_header));
383 const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch);
384 for (uint32_t i=0; i < sliceCount; ++i) {
385 if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(slices[i].cputype), OSSwapBigToHostInt32(slices[i].cpusubtype)) ) {
386 uint32_t fileOffset = OSSwapBigToHostInt32(slices[i].offset);
387 if ( fileOffset > stat_buf.st_size ) {
388 throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s",
389 i, OSSwapBigToHostInt32(slices[i].cputype), path);
390 }
391 if ( (fileOffset+OSSwapBigToHostInt32(slices[i].size)) > stat_buf.st_size ) {
392 throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s",
393 i, OSSwapBigToHostInt32(slices[i].cputype), path);
394 }
395 try {
396 switch ( OSSwapBigToHostInt32(slices[i].cputype) ) {
397 case CPU_TYPE_I386:
398 fLayouts.push_back(new MachOLayout<x86>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
399 break;
400 case CPU_TYPE_X86_64:
401 fLayouts.push_back(new MachOLayout<x86_64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
402 break;
403 case CPU_TYPE_ARM:
404 fLayouts.push_back(new MachOLayout<arm>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
405 break;
406 case CPU_TYPE_ARM64:
407 fLayouts.push_back(new MachOLayout<arm64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
408 break;
409 default:
410 throw "unknown slice in fat file";
411 }
412 }
413 catch (const char* msg) {
414 fprintf(stderr, "warning: %s for %s\n", msg, path);
415 }
416 }
417 }
418 }
419 else {
420 try {
421 if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
422 if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
423 fLayouts.push_back(new MachOLayout<x86>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
424 }
425 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
426 if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
427 fLayouts.push_back(new MachOLayout<x86_64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
428 }
429 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
430 if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
431 fLayouts.push_back(new MachOLayout<arm>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
432 }
433 else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM64)) {
434 if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
435 fLayouts.push_back(new MachOLayout<arm64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
436 }
437 else {
438 throw "unknown file format";
439 }
440 }
441 catch (const char* msg) {
442 fprintf(stderr, "warning: %s for %s\n", msg, path);
443 }
444 }
445 }
446 catch (...) {
447 ::munmap(p, stat_buf.st_size);
448 throw;
449 }
450 }
451
452
453 template <typename A>
454 uint64_t MachOLayout<A>::segmentSize(const macho_segment_command<typename A::P>* segCmd) const
455 {
456 // <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
457 if ( segCmd->nsects() > 0 ) {
458 const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
459 const macho_section<P>* const lastSection = &sectionsStart[segCmd->nsects()-1];
460 uint64_t endSectAddr = lastSection->addr() + lastSection->size();
461 uint64_t endSectAddrPage = (endSectAddr + 4095) & (-4096);
462 if ( endSectAddrPage < (segCmd->vmaddr() + segCmd->vmsize()) ) {
463 uint64_t size = endSectAddrPage - segCmd->vmaddr();
464 //if ( size != segCmd->vmsize() )
465 // fprintf(stderr, "trim %s size=0x%08llX instead of 0x%08llX for %s\n",
466 // segCmd->segname(), size, segCmd->vmsize(), getFilePath());
467 return size;
468 }
469 }
470 return segCmd->vmsize();
471 }
472
473 template <typename A>
474 uint64_t MachOLayout<A>::segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const
475 {
476 int p2align = 12;
477 if ( segCmd->nsects() > 0 ) {
478 const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
479 const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()-1];
480 for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
481 if ( sect->align() > p2align )
482 p2align = sect->align();
483 }
484 }
485 return (1 << p2align);
486 }
487
488 template <typename A>
489 uint64_t MachOLayout<A>::segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const
490 {
491 // <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
492 if ( segCmd->nsects() > 0 ) {
493 uint64_t endOffset = segCmd->fileoff();
494 const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
495 const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
496 for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
497 if ( sect->offset() != 0 )
498 endOffset = sect->offset() + sect->size();
499 }
500 uint64_t size = (endOffset - segCmd->fileoff() + 4095) & (-4096);
501 //if ( size != segCmd->filesize() )
502 // fprintf(stderr, "trim %s filesize=0x%08llX instead of 0x%08llX for %s\n",
503 // segCmd->segname(), size, segCmd->filesize(), getFilePath());
504 return size;
505 }
506 return segCmd->filesize();
507 }
508
509
510 template <typename A>
511 MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid)
512 : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0),
513 fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false),
514 fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL)
515 {
516 fDylibID.name = NULL;
517 fDylibID.currentVersion = 0;
518 fDylibID.compatibilityVersion = 0;
519 bzero(fUUID, sizeof(fUUID));
520
521 const macho_header<P>* mh = (const macho_header<P>*)machHeader;
522 if ( mh->cputype() != arch() )
523 throw "Layout object is wrong architecture";
524 switch ( mh->filetype() ) {
525 case MH_DYLIB:
526 fIsDylib = true;
527 break;
528 case MH_BUNDLE:
529 case MH_EXECUTE:
530 case MH_DYLIB_STUB:
531 case MH_DYLINKER:
532 break;
533 default:
534 throw "file is not a mach-o final linked image";
535 }
536 fFlags = mh->flags();
537 fFileType = mh->filetype();
538 fArchPair.arch = mh->cputype();
539 fArchPair.subtype = mh->cpusubtype();
540
541 const macho_dyld_info_command<P>* dyldInfo = NULL;
542 const macho_symtab_command<P>* symbolTableCmd = NULL;
543 const macho_dysymtab_command<P>* dynamicSymbolTableCmd = NULL;
544 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
545 const uint32_t cmd_count = mh->ncmds();
546 const macho_load_command<P>* cmd = cmds;
547 for (uint32_t i = 0; i < cmd_count; ++i) {
548 switch ( cmd->cmd() ) {
549 case LC_ID_DYLIB:
550 {
551 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
552 fDylibID.name = strdup(dylib->name());
553 fDylibID.currentVersion = dylib->current_version();
554 fDylibID.compatibilityVersion = dylib->compatibility_version();
555 fNameFileOffset = dylib->name() - (char*)machHeader;
556 fShareableLocation = ( (strncmp(fDylibID.name, "/usr/lib/", 9) == 0) || (strncmp(fDylibID.name, "/System/Library/", 16) == 0) );
557 }
558 break;
559 case LC_LOAD_DYLIB:
560 case LC_LOAD_WEAK_DYLIB:
561 case LC_REEXPORT_DYLIB:
562 case LC_LOAD_UPWARD_DYLIB:
563 {
564 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
565 Library lib;
566 lib.name = strdup(dylib->name());
567 lib.currentVersion = dylib->current_version();
568 lib.compatibilityVersion = dylib->compatibility_version();
569 lib.weakImport = ( cmd->cmd() == LC_LOAD_WEAK_DYLIB );
570 fLibraries.push_back(lib);
571 }
572 break;
573 case LC_SEGMENT_SPLIT_INFO:
574 fHasSplitSegInfo = true;
575 break;
576 case macho_segment_command<P>::CMD:
577 {
578 const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
579 fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(),
580 segmentFileSize(segCmd), segmentAlignment(segCmd), segCmd->initprot(), segCmd->segname()));
581 }
582 break;
583 case LC_SYMTAB:
584 symbolTableCmd = (macho_symtab_command<P>*)cmd;
585 break;
586 case LC_DYSYMTAB:
587 dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)cmd;
588 break;
589 case LC_DYLD_INFO:
590 case LC_DYLD_INFO_ONLY:
591 fHasDyldInfo = true;
592 dyldInfo = (struct macho_dyld_info_command<P>*)cmd;
593 break;
594 case LC_UUID:
595 {
596 const macho_uuid_command<P>* uc = (macho_uuid_command<P>*)cmd;
597 memcpy(&fUUID, uc->uuid(), 16);
598 }
599 break;
600 }
601 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
602 }
603
604 fLowSegment = NULL;
605 fLowExecutableSegment = NULL;
606 fLowWritableSegment = NULL;
607 fLowReadOnlySegment = NULL;
608 fVMExecutableSize = 0;
609 fVMWritablSize = 0;
610 fVMReadOnlySize = 0;
611 fVMSize = 0;
612 const Segment* highSegment = NULL;
613 for(std::vector<Segment>::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) {
614 const Segment& seg = *it;
615 if ( (fLowSegment == NULL) || (seg.address() < fLowSegment->address()) )
616 fLowSegment = &seg;
617 if ( (highSegment == NULL) || (seg.address() > highSegment->address()) )
618 highSegment = &seg;
619 if ( seg.executable() ) {
620 if ( (fLowExecutableSegment == NULL) || (seg.address() < fLowExecutableSegment->address()) )
621 fLowExecutableSegment = &seg;
622 fVMExecutableSize += seg.size();
623 }
624 else if ( seg.writable()) {
625 if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) )
626 fLowWritableSegment = &seg;
627 fVMWritablSize += seg.size();
628 if ( !validReadWriteSeg(seg) ) {
629 fHasTooManyWritableSegments = true;
630 }
631 }
632 else {
633 if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) )
634 fLowReadOnlySegment = &seg;
635 fVMReadOnlySize += seg.size();
636 }
637 }
638 if ( (highSegment != NULL) && (fLowSegment != NULL) )
639 fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096);
640
641 // scan undefines looking, for magic ordinals
642 if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) {
643 const macho_nlist<P>* symbolTable = (macho_nlist<P>*)((uint8_t*)machHeader + symbolTableCmd->symoff());
644 const uint32_t startUndefs = dynamicSymbolTableCmd->iundefsym();
645 const uint32_t endUndefs = startUndefs + dynamicSymbolTableCmd->nundefsym();
646 for (uint32_t i=startUndefs; i < endUndefs; ++i) {
647 uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc());
648 if ( ordinal == DYNAMIC_LOOKUP_ORDINAL )
649 fDynamicLookupLinkage = true;
650 else if ( ordinal == EXECUTABLE_ORDINAL )
651 fMainExecutableLookupLinkage = true;
652 }
653 }
654
655 if ( dyldInfo != NULL ) {
656 if ( dyldInfo->export_off() != 0 ) {
657 fDyldInfoExports = (uint8_t*)machHeader + dyldInfo->export_off();
658 }
659 }
660
661 }
662
663 template <> cpu_type_t MachOLayout<x86>::arch() { return CPU_TYPE_I386; }
664 template <> cpu_type_t MachOLayout<x86_64>::arch() { return CPU_TYPE_X86_64; }
665 template <> cpu_type_t MachOLayout<arm>::arch() { return CPU_TYPE_ARM; }
666 template <> cpu_type_t MachOLayout<arm64>::arch() { return CPU_TYPE_ARM64; }
667
668 template <>
669 bool MachOLayout<x86>::validReadWriteSeg(const Segment& seg) const
670 {
671 return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0);
672 }
673
674 template <typename A>
675 bool MachOLayout<A>::validReadWriteSeg(const Segment& seg) const
676 {
677 return (strcmp(seg.name(), "__DATA") == 0);
678 }
679
680
681 template <>
682 bool MachOLayout<x86>::isSplitSeg() const
683 {
684 return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
685 }
686
687 template <>
688 bool MachOLayout<arm>::isSplitSeg() const
689 {
690 return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
691 }
692
693 template <typename A>
694 bool MachOLayout<A>::isSplitSeg() const
695 {
696 return false;
697 }
698
699 template <typename A>
700 const MachOLayoutAbstraction::Segment* MachOLayout<A>::getSegment(const char* name) const
701 {
702 for(std::vector<Segment>::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) {
703 const Segment& seg = *it;
704 if ( strcmp(seg.name(), name) == 0 )
705 return &seg;
706 }
707 return NULL;
708 }
709
710
711
712 #endif // __MACHO_LAYOUT__
713
714
715