X-Git-Url: https://git.saurik.com/apple/dyld.git/blobdiff_plain/412ebb8e3cc35d457058c31310d89ef96b7c416d..64db26dd8d8b4bbcbca664ab56354d7c038f0e6b:/launch-cache/MachOLayout.hpp diff --git a/launch-cache/MachOLayout.hpp b/launch-cache/MachOLayout.hpp index e23cbac..4d90a1e 100644 --- a/launch-cache/MachOLayout.hpp +++ b/launch-cache/MachOLayout.hpp @@ -41,7 +41,7 @@ #include #include -#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -69,11 +69,11 @@ public: struct Segment { public: - Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, + Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t align, uint32_t prot, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize), fOrigFileOffset(offset), fOrigFileSize(file_size), fOrigPermissions(prot), - fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fPermissions(prot), - fNewAddress(0), fMappedAddress(NULL) { + fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align), + fPermissions(prot), fNewAddress(0), fMappedAddress(NULL) { strlcpy(fOrigName, segName, 16); } @@ -85,6 +85,7 @@ public: bool readable() const { return fPermissions & VM_PROT_READ; } bool writable() const { return fPermissions & VM_PROT_WRITE; } bool executable() const { return fPermissions & VM_PROT_EXECUTE; } + uint64_t alignment() const { return fAlignment; } const char* name() const { return fOrigName; } uint64_t newAddress() const { return fNewAddress; } void* mappedAddress() const { return fMappedAddress; } @@ -105,6 +106,7 @@ public: uint64_t fSize; uint64_t fFileOffset; uint64_t fFileSize; + uint64_t fAlignment; uint32_t fPermissions; uint64_t fNewAddress; void* fMappedAddress; @@ -134,11 +136,13 @@ public: virtual bool hasMainExecutableLookupLinkage() const = 0; virtual bool isTwoLevelNamespace() const = 0; virtual bool hasDyldInfo() const = 0; + virtual bool hasMultipleReadWriteSegments() const = 0; virtual uint32_t getNameFileOffset() const = 0; virtual time_t getLastModTime() const = 0; virtual ino_t getInode() const = 0; virtual std::vector& getSegments() = 0; virtual const std::vector& getSegments() const = 0; + virtual const Segment* getSegment(const char* name) const = 0; virtual const std::vector& getLibraries() const = 0; virtual uint64_t getBaseAddress() const = 0; virtual uint64_t getVMSize() const = 0; @@ -151,6 +155,7 @@ public: // need getDyldInfoExports because export info uses ULEB encoding and size could grow virtual const uint8_t* getDyldInfoExports() const = 0; virtual void setDyldInfoExports(const uint8_t* newExports) const = 0; + virtual void uuid(uuid_t u) const = 0; }; @@ -179,11 +184,13 @@ public: virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; } virtual bool isTwoLevelNamespace() const { return (fFlags & MH_TWOLEVEL); } virtual bool hasDyldInfo() const { return fHasDyldInfo; } + virtual bool hasMultipleReadWriteSegments() const { return fHasTooManyWritableSegments; } virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; } virtual time_t getLastModTime() const { return fMTime; } virtual ino_t getInode() const { return fInode; } virtual std::vector& getSegments() { return fSegments; } virtual const std::vector& getSegments() const { return fSegments; } + virtual const Segment* getSegment(const char* name) const; virtual const std::vector& getLibraries() const { return fLibraries; } virtual uint64_t getBaseAddress() const { return fLowSegment->address(); } virtual uint64_t getVMSize() const { return fVMSize; } @@ -195,12 +202,18 @@ public: virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; } virtual const uint8_t* getDyldInfoExports() const { return fDyldInfoExports; } virtual void setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; } + virtual void uuid(uuid_t u) const { memcpy(u, fUUID, 16); } private: typedef typename A::P P; typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; + uint64_t segmentSize(const macho_segment_command* segCmd) const; + uint64_t segmentFileSize(const macho_segment_command* segCmd) const; + uint64_t segmentAlignment(const macho_segment_command* segCmd) const; + bool validReadWriteSeg(const Segment& seg) const; + static cpu_type_t arch(); const char* fPath; @@ -229,7 +242,9 @@ private: bool fMainExecutableLookupLinkage; bool fIsDylib; bool fHasDyldInfo; + bool fHasTooManyWritableSegments; mutable const uint8_t* fDyldInfoExports; + uuid_t fUUID; }; @@ -245,10 +260,19 @@ public: const std::vector& allLayouts() const { return fLayouts; } private: + class CStringHash { + public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; + }; struct CStringEquals { bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; - typedef __gnu_cxx::hash_map, CStringEquals> PathToNode; + typedef std::unordered_map PathToNode; static bool requestedSlice(const std::set* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType); @@ -270,14 +294,20 @@ const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const if ( layout->getArchPair().arch == ap.arch ) { switch ( ap.arch ) { case CPU_TYPE_ARM: - if ( layout->getArchPair().subtype == ap.subtype ) + case CPU_TYPE_X86_64: + if ( (layout->getArchPair().subtype & ~CPU_SUBTYPE_MASK) == (ap.subtype & ~CPU_SUBTYPE_MASK) ) return layout; break; - default: + default: return layout; } } } + // if requesting x86_64h and it did not exist, try x86_64 as a fallback + if ((ap.arch == CPU_TYPE_X86_64) && (ap.subtype == CPU_SUBTYPE_X86_64_H)) { + ap.subtype = CPU_SUBTYPE_X86_64_ALL; + return this->getSlice(ap); + } return NULL; } @@ -364,9 +394,6 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); - break; case CPU_TYPE_I386: fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); break; @@ -376,8 +403,8 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); break; - case CPU_TYPE_POWERPC64: - // ignore ppc64 slices + case CPU_TYPE_ARM64: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); break; default: throw "unknown slice in fat file"; @@ -391,11 +418,7 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::setmagic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { - if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(mh->cputype), OSSwapBigToHostInt32(mh->cpusubtype)) ) - fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); - } - else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { + if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } @@ -407,8 +430,9 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::setcputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } - else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { - // ignore ppc64 slices + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM64)) { + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } else { throw "unknown file format"; @@ -426,16 +450,74 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set +uint64_t MachOLayout::segmentSize(const macho_segment_command* segCmd) const +{ + // segments may have 16KB alignment padding at end, if so we can remove that in cache + if ( segCmd->nsects() > 0 ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const lastSection = §ionsStart[segCmd->nsects()-1]; + uint64_t endSectAddr = lastSection->addr() + lastSection->size(); + uint64_t endSectAddrPage = (endSectAddr + 4095) & (-4096); + if ( endSectAddrPage < (segCmd->vmaddr() + segCmd->vmsize()) ) { + uint64_t size = endSectAddrPage - segCmd->vmaddr(); + //if ( size != segCmd->vmsize() ) + // fprintf(stderr, "trim %s size=0x%08llX instead of 0x%08llX for %s\n", + // segCmd->segname(), size, segCmd->vmsize(), getFilePath()); + return size; + } + } + return segCmd->vmsize(); +} + +template +uint64_t MachOLayout::segmentAlignment(const macho_segment_command* segCmd) const +{ + int p2align = 12; + if ( segCmd->nsects() > 0 ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()-1]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->align() > p2align ) + p2align = sect->align(); + } + } + return (1 << p2align); +} + +template +uint64_t MachOLayout::segmentFileSize(const macho_segment_command* segCmd) const +{ + // segments may have 16KB alignment padding at end, if so we can remove that in cache + if ( segCmd->nsects() > 0 ) { + uint64_t endOffset = segCmd->fileoff(); + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->offset() != 0 ) + endOffset = sect->offset() + sect->size(); + } + uint64_t size = (endOffset - segCmd->fileoff() + 4095) & (-4096); + //if ( size != segCmd->filesize() ) + // fprintf(stderr, "trim %s filesize=0x%08llX instead of 0x%08llX for %s\n", + // segCmd->segname(), size, segCmd->filesize(), getFilePath()); + return size; + } + return segCmd->filesize(); +} + + template MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid) : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0), fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false), - fHasDyldInfo(false), fDyldInfoExports(NULL) + fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL) { fDylibID.name = NULL; fDylibID.currentVersion = 0; fDylibID.compatibilityVersion = 0; - + bzero(fUUID, sizeof(fUUID)); + const macho_header

* mh = (const macho_header

*)machHeader; if ( mh->cputype() != arch() ) throw "Layout object is wrong architecture"; @@ -493,9 +575,9 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* break; case macho_segment_command

::CMD: { - macho_segment_command

* segCmd = (macho_segment_command

*)cmd; - fSegments.push_back(Segment(segCmd->vmaddr(), segCmd->vmsize(), segCmd->fileoff(), - segCmd->filesize(), segCmd->initprot(), segCmd->segname())); + const macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(), + segmentFileSize(segCmd), segmentAlignment(segCmd), segCmd->initprot(), segCmd->segname())); } break; case LC_SYMTAB: @@ -509,6 +591,12 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* fHasDyldInfo = true; dyldInfo = (struct macho_dyld_info_command

*)cmd; break; + case LC_UUID: + { + const macho_uuid_command

* uc = (macho_uuid_command

*)cmd; + memcpy(&fUUID, uc->uuid(), 16); + } + break; } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -537,6 +625,9 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) ) fLowWritableSegment = &seg; fVMWritablSize += seg.size(); + if ( !validReadWriteSeg(seg) ) { + fHasTooManyWritableSegments = true; + } } else { if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) ) @@ -546,7 +637,7 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* } if ( (highSegment != NULL) && (fLowSegment != NULL) ) fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096); - + // scan undefines looking, for magic ordinals if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) { const macho_nlist

* symbolTable = (macho_nlist

*)((uint8_t*)machHeader + symbolTableCmd->symoff()); @@ -569,18 +660,24 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* } -template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_POWERPC; } template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_I386; } template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_X86_64; } template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_ARM; } - +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_ARM64; } template <> -bool MachOLayout::isSplitSeg() const +bool MachOLayout::validReadWriteSeg(const Segment& seg) const { - return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 ); + return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0); } +template +bool MachOLayout::validReadWriteSeg(const Segment& seg) const +{ + return (strcmp(seg.name(), "__DATA") == 0); +} + + template <> bool MachOLayout::isSplitSeg() const { @@ -599,6 +696,18 @@ bool MachOLayout::isSplitSeg() const return false; } +template +const MachOLayoutAbstraction::Segment* MachOLayout::getSegment(const char* name) const +{ + for(std::vector::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) { + const Segment& seg = *it; + if ( strcmp(seg.name(), name) == 0 ) + return &seg; + } + return NULL; +} + + #endif // __MACHO_LAYOUT__