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