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