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 50void throwf(const char* format, ...) __attribute__((format(printf, 1, 2))); 51 52__attribute__((noreturn)) 53void 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 66class MachOLayoutAbstraction 67{ 68public: 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 164template <typename A> 165class MachOLayout : public MachOLayoutAbstraction 166{ 167public: 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 207private: 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 252class UniversalMachOLayout 253{ 254public: 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 262private: 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 284UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache; 285 286 287 288 289const 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 315const 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 332bool 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 354UniversalMachOLayout::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 453template <typename A> 454uint64_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 = §ionsStart[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 473template <typename A> 474uint64_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 = §ionsStart[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 488template <typename A> 489uint64_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 = §ionsStart[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 510template <typename A> 511MachOLayout<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 663template <> cpu_type_t MachOLayout<x86>::arch() { return CPU_TYPE_I386; } 664template <> cpu_type_t MachOLayout<x86_64>::arch() { return CPU_TYPE_X86_64; } 665template <> cpu_type_t MachOLayout<arm>::arch() { return CPU_TYPE_ARM; } 666template <> cpu_type_t MachOLayout<arm64>::arch() { return CPU_TYPE_ARM64; } 667 668template <> 669bool MachOLayout<x86>::validReadWriteSeg(const Segment& seg) const 670{ 671 return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0); 672} 673 674template <typename A> 675bool MachOLayout<A>::validReadWriteSeg(const Segment& seg) const 676{ 677 return (strcmp(seg.name(), "__DATA") == 0); 678} 679 680 681template <> 682bool MachOLayout<x86>::isSplitSeg() const 683{ 684 return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 ); 685} 686 687template <> 688bool MachOLayout<arm>::isSplitSeg() const 689{ 690 return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 ); 691} 692 693template <typename A> 694bool MachOLayout<A>::isSplitSeg() const 695{ 696 return false; 697} 698 699template <typename A> 700const 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 716