X-Git-Url: https://git.saurik.com/apple/dyld.git/blobdiff_plain/412ebb8e3cc35d457058c31310d89ef96b7c416d..4f17e7a6e9f7d1db9fed2c9e01feed490a9f0303:/src/ImageLoaderMachO.cpp?ds=sidebyside diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index b068b7a..d552ef3 100644 --- a/src/ImageLoaderMachO.cpp +++ b/src/ImageLoaderMachO.cpp @@ -40,13 +40,17 @@ #include #include #include +#include #include #include #include +#include #include "ImageLoaderMachO.h" #include "ImageLoaderMachOCompressed.h" +#if SUPPORT_CLASSIC_MACHO #include "ImageLoaderMachOClassic.h" +#endif #include "mach-o/dyld_images.h" // use stack guard random value to add padding between dylibs @@ -56,16 +60,27 @@ extern "C" long __stack_chk_guard; #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ #endif +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + + // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables #if __LP64__ #define LC_SEGMENT_COMMAND LC_SEGMENT_64 #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT struct macho_segment_command : public segment_command_64 {}; struct macho_section : public section_64 {}; struct macho_routines_command : public routines_command_64 {}; #else #define LC_SEGMENT_COMMAND LC_SEGMENT #define LC_ROUTINES_COMMAND LC_ROUTINES + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 struct macho_segment_command : public segment_command {}; struct macho_section : public section {}; struct macho_routines_command : public routines_command {}; @@ -77,9 +92,9 @@ uint32_t ImageLoaderMachO::fgSymbolTrieSearchs = 0; ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, uint32_t segOffsets[], unsigned int libCount) - : ImageLoader(path, libCount), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), + : ImageLoader(path, libCount), fCoveredCodeLength(0), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), fEHFrameSectionOffset(0), fUnwindInfoSectionOffset(0), fDylibIDOffset(0), - fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), +fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), #if TEXT_RELOC_SUPPORT fTextSegmentRebases(false), fTextSegmentBinds(false), @@ -88,7 +103,7 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns fReadOnlyImportSegment(false), #endif fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false), - fHasInitializers(false), fHasTerminators(false), fGoodFirstSegment(false), fRegisteredAsRequiresCoalescing(false) + fHasInitializers(false), fHasTerminators(false), fRegisteredAsRequiresCoalescing(false) { fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0); @@ -103,7 +118,7 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns // ignore zero-sized segments if ( segCmd->vmsize != 0 ) { // record offset of load command - segOffsets[segIndex++] = (uint8_t*)segCmd - fMachOData; + segOffsets[segIndex++] = (uint32_t)((uint8_t*)segCmd - fMachOData); } } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); @@ -113,28 +128,95 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns // determine if this mach-o file has classic or compressed LINKEDIT and number of segments it has -void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed, - unsigned int* segCount, unsigned int* libCount, - const linkedit_data_command** codeSigCmd) +void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed, + unsigned int* segCount, unsigned int* libCount, const LinkContext& context, + const linkedit_data_command** codeSigCmd, + const encryption_info_command** encryptCmd) { *compressed = false; *segCount = 0; *libCount = 0; *codeSigCmd = NULL; + *encryptCmd = NULL; + const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); + const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + mh->sizeofcmds); - const struct load_command* cmd = cmds; + const struct load_command* cmd = startCmds; + bool foundLoadCommandSegment = false; for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize; + struct macho_segment_command* segCmd; + if ( cmdLength < 8 ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) too small in %s", + i, cmdLength, path); + } + const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); + if ( (nextCmd > endCmds) || (nextCmd < cmd) ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) would exceed sizeofcmds (%u) in %s", + i, cmdLength, mh->sizeofcmds, path); + } switch (cmd->cmd) { case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: *compressed = true; break; case LC_SEGMENT_COMMAND: + segCmd = (struct macho_segment_command*)cmd; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // rdar://problem/19617624 allow unmapped segments on OSX (but not iOS) + if ( (segCmd->filesize > segCmd->vmsize) && (segCmd->vmsize != 0) ) +#else + if ( segCmd->filesize > segCmd->vmsize ) +#endif + dyld::throwf("malformed mach-o image: segment load command %s filesize is larger than vmsize", segCmd->segname); // ignore zero-sized segments - if ( ((struct macho_segment_command*)cmd)->vmsize != 0 ) + if ( segCmd->vmsize != 0 ) *segCount += 1; + if ( context.codeSigningEnforced ) { + uintptr_t vmStart = segCmd->vmaddr; + uintptr_t vmSize = segCmd->vmsize; + uintptr_t vmEnd = vmStart + vmSize; + uintptr_t fileStart = segCmd->fileoff; + uintptr_t fileSize = segCmd->filesize; + if ( (intptr_t)(vmEnd) < 0) + dyld::throwf("malformed mach-o image: segment load command %s vmsize too large", segCmd->segname); + if ( vmStart > vmEnd ) + dyld::throwf("malformed mach-o image: segment load command %s wraps around address space", segCmd->segname); + if ( vmSize != fileSize ) { + if ( (segCmd->initprot == 0) && (fileSize != 0) ) + dyld::throwf("malformed mach-o image: unaccessable segment %s has filesize != 0", segCmd->segname); + else if ( vmSize < fileSize ) + dyld::throwf("malformed mach-o image: segment %s has vmsize < filesize", segCmd->segname); + } + if ( inCache ) { + if ( (fileSize != 0) && (segCmd->initprot == (VM_PROT_READ | VM_PROT_EXECUTE)) ) { + if ( foundLoadCommandSegment ) + throw "load commands in multiple segments"; + foundLoadCommandSegment = true; + } + } + else if ( (fileStart < mh->sizeofcmds) && (fileSize != 0) ) { + // all load commands must be in an executable segment + if ( (fileStart != 0) || (fileSize < (mh->sizeofcmds+sizeof(macho_header))) ) + dyld::throwf("malformed mach-o image: segment %s does not span all load commands", segCmd->segname); + if ( segCmd->initprot != (VM_PROT_READ | VM_PROT_EXECUTE) ) + dyld::throwf("malformed mach-o image: load commands found in segment %s with wrong permissions", segCmd->segname); + if ( foundLoadCommandSegment ) + throw "load commands in multiple segments"; + foundLoadCommandSegment = true; + } + + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if (!inCache && sect->offset != 0 && ((sect->offset + sect->size) > (segCmd->fileoff + segCmd->filesize))) + dyld::throwf("malformed mach-o image: section %s,%s of '%s' exceeds segment %s booundary", sect->segname, sect->sectname, path, segCmd->segname); + } + } + break; + case LC_SEGMENT_COMMAND_WRONG: + dyld::throwf("malformed mach-o image: wrong LC_SEGMENT[_64] for architecture"); break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: @@ -145,18 +227,68 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat case LC_CODE_SIGNATURE: *codeSigCmd = (struct linkedit_data_command*)cmd; // only support one LC_CODE_SIGNATURE per image break; + case LC_ENCRYPTION_INFO: + case LC_ENCRYPTION_INFO_64: + *encryptCmd = (struct encryption_info_command*)cmd; // only support one LC_ENCRYPTION_INFO[_64] per image + break; } - uint32_t cmdLength = cmd->cmdsize; - cmd = (const struct load_command*)(((char*)cmd)+cmdLength); - if ( cmd > endCmds ) { - dyld::throwf("malformed mach-o image: load command #%d length (%u) would exceed sizeofcmds (%u) in %s", - i, cmdLength, mh->sizeofcmds, path); + cmd = nextCmd; + } + + if ( context.codeSigningEnforced && !foundLoadCommandSegment ) + throw "load commands not in a segment"; + + // verify every segment does not overlap another segment + if ( context.codeSigningEnforced ) { + uintptr_t lastFileStart = 0; + uintptr_t linkeditFileStart = 0; + const struct load_command* cmd1 = startCmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd1->cmd == LC_SEGMENT_COMMAND ) { + struct macho_segment_command* segCmd1 = (struct macho_segment_command*)cmd1; + uintptr_t vmStart1 = segCmd1->vmaddr; + uintptr_t vmEnd1 = segCmd1->vmaddr + segCmd1->vmsize; + uintptr_t fileStart1 = segCmd1->fileoff; + uintptr_t fileEnd1 = segCmd1->fileoff + segCmd1->filesize; + + if (fileStart1 > lastFileStart) + lastFileStart = fileStart1; + + if ( strcmp(&segCmd1->segname[0], "__LINKEDIT") == 0 ) { + linkeditFileStart = fileStart1; + } + + const struct load_command* cmd2 = startCmds; + for (uint32_t j = 0; j < cmd_count; ++j) { + if ( cmd2 == cmd1 ) + continue; + if ( cmd2->cmd == LC_SEGMENT_COMMAND ) { + struct macho_segment_command* segCmd2 = (struct macho_segment_command*)cmd2; + uintptr_t vmStart2 = segCmd2->vmaddr; + uintptr_t vmEnd2 = segCmd2->vmaddr + segCmd2->vmsize; + uintptr_t fileStart2 = segCmd2->fileoff; + uintptr_t fileEnd2 = segCmd2->fileoff + segCmd2->filesize; + if ( ((vmStart2 <= vmStart1) && (vmEnd2 > vmStart1) && (vmEnd1 > vmStart1)) + || ((vmStart2 >= vmStart1) && (vmStart2 < vmEnd1) && (vmEnd2 > vmStart2)) ) + dyld::throwf("malformed mach-o image: segment %s vm overlaps segment %s", segCmd1->segname, segCmd2->segname); + if ( ((fileStart2 <= fileStart1) && (fileEnd2 > fileStart1) && (fileEnd1 > fileStart1)) + || ((fileStart2 >= fileStart1) && (fileStart2 < fileEnd1) && (fileEnd2 > fileStart2)) ) + dyld::throwf("malformed mach-o image: segment %s file content overlaps segment %s", segCmd1->segname, segCmd2->segname); + } + cmd2 = (const struct load_command*)(((char*)cmd2)+cmd2->cmdsize); + } + } + cmd1 = (const struct load_command*)(((char*)cmd1)+cmd1->cmdsize); } + + if (lastFileStart != linkeditFileStart) + dyld::throwf("malformed mach-o image: __LINKEDIT must be last segment"); } + // fSegmentsArrayCount is only 8-bits if ( *segCount > 255 ) dyld::throwf("malformed mach-o image: more than 255 segments in %s", path); - + // fSegmentsArrayCount is only 8-bits if ( *libCount > 4095 ) dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path); @@ -176,12 +308,17 @@ ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, &codeSigCmd); + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); else +#if SUPPORT_CLASSIC_MACHO return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif } @@ -204,12 +341,17 @@ ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, con unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount, &codeSigCmd); + const encryption_info_command* encryptCmd; + sniffLoadCommands((const macho_header*)fileData, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); // instantiate concrete class based on content of load commands if ( compressed ) - return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); + return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, dataSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, encryptCmd, context); else - return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, dataSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif } // create image by using cached mach-o file @@ -220,12 +362,17 @@ ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, cons unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, &codeSigCmd); + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, path, true, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); else +#if SUPPORT_CLASSIC_MACHO return ImageLoaderMachOClassic::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif } // create image by copying an in-memory mach-o file @@ -235,26 +382,49 @@ ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, con unsigned int segCount; unsigned int libCount; const linkedit_data_command* sigcmd; - sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount, &sigcmd); + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, moduleName, false, &compressed, &segCount, &libCount, context, &sigcmd, &encryptCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); else +#if SUPPORT_CLASSIC_MACHO return ImageLoaderMachOClassic::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif } +int ImageLoaderMachO::crashIfInvalidCodeSignature() +{ + // Now that segments are mapped in, try reading from first executable segment. + // If code signing is enabled the kernel will validate the code signature + // when paging in, and kill the process if invalid. + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + // return read value to ensure compiler does not optimize away load + int* p = (int*)segActualLoadAddress(i); + return *p; + } + } + return 0; +} + -void ImageLoaderMachO::parseLoadCmds() +void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) { // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide for(unsigned int i=0; i < fSegmentsCount; ++i) { // set up pointer to __LINKEDIT segment - if ( strcmp(segName(i),"__LINKEDIT") == 0 ) + if ( strcmp(segName(i),"__LINKEDIT") == 0 ) { + if ( context.codeSigningEnforced && (segFileOffset(i) > fCoveredCodeLength)) + dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName()); fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i)); + } #if TEXT_RELOC_SUPPORT // __TEXT segment always starts at beginning of file and contains mach_header and load commands - if ( strcmp(segName(i),"__TEXT") == 0 ) { + if ( segExecutable(i) ) { if ( segHasRebaseFixUps(i) && (fSlide != 0) ) fTextSegmentRebases = true; if ( segHasBindFixUps(i) ) @@ -287,6 +457,8 @@ void ImageLoaderMachO::parseLoadCmds() const dyld_info_command* dyldInfo = NULL; const macho_nlist* symbolTable = NULL; const char* symbolTableStrings = NULL; + const struct load_command* firstUnknownCmd = NULL; + const struct version_min_command* minOSVersionCmd = NULL; const dysymtab_command* dynSymbolTable = NULL; const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; @@ -334,9 +506,9 @@ void ImageLoaderMachO::parseLoadCmds() else if ( type == S_DTRACE_DOF ) fHasDOFSections = true; else if ( isTextSeg && (strcmp(sect->sectname, "__eh_frame") == 0) ) - fEHFrameSectionOffset = (uint8_t*)sect - fMachOData; + fEHFrameSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) ) - fUnwindInfoSectionOffset = (uint8_t*)sect - fMachOData;; + fUnwindInfoSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); } } break; @@ -345,21 +517,43 @@ void ImageLoaderMachO::parseLoadCmds() break; case LC_ID_DYLIB: { - fDylibIDOffset = (uint8_t*)cmd - fMachOData; + fDylibIDOffset = (uint32_t)((uint8_t*)cmd - fMachOData); } break; case LC_RPATH: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: case LC_LOAD_UPWARD_DYLIB: + case LC_MAIN: // do nothing, just prevent LC_REQ_DYLD exception from occuring break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + minOSVersionCmd = (version_min_command*)cmd; + break; default: - if ( (cmd->cmd & LC_REQ_DYLD) != 0 ) - dyld::throwf("unknown required load command 0x%08X", cmd->cmd); + if ( (cmd->cmd & LC_REQ_DYLD) != 0 ) { + if ( firstUnknownCmd == NULL ) + firstUnknownCmd = cmd; + } + break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } + if ( firstUnknownCmd != NULL ) { + if ( minOSVersionCmd != NULL ) { + dyld::throwf("cannot load '%s' because it was built for OS version %u.%u (load command 0x%08X is unknown)", + this->getShortName(), + minOSVersionCmd->version >> 16, ((minOSVersionCmd->version >> 8) & 0xff), + firstUnknownCmd->cmd); + } + else { + dyld::throwf("cannot load '%s' (load command 0x%08X is unknown)", this->getShortName(), firstUnknownCmd->cmd); + } + } + if ( dyldInfo != NULL ) this->setDyldInfo(dyldInfo); @@ -477,6 +671,7 @@ uintptr_t ImageLoaderMachO::segActualEndAddress(unsigned int segIndex) const bool ImageLoaderMachO::segHasRebaseFixUps(unsigned int segIndex) const { +#if TEXT_RELOC_SUPPORT // scan sections for fix-up bit const macho_segment_command* segCmd = segLoadCommand(segIndex); const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); @@ -485,11 +680,13 @@ bool ImageLoaderMachO::segHasRebaseFixUps(unsigned int segIndex) const if ( (sect->flags & S_ATTR_LOC_RELOC) != 0 ) return true; } +#endif return false; } bool ImageLoaderMachO::segHasBindFixUps(unsigned int segIndex) const { +#if TEXT_RELOC_SUPPORT // scan sections for fix-up bit const macho_segment_command* segCmd = segLoadCommand(segIndex); const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); @@ -498,6 +695,7 @@ bool ImageLoaderMachO::segHasBindFixUps(unsigned int segIndex) const if ( (sect->flags & S_ATTR_EXT_RELOC) != 0 ) return true; } +#endif return false; } @@ -547,7 +745,7 @@ void ImageLoaderMachO::preFetchDATA(int fd, uint64_t offsetInFat, const LinkCont // prefetch writable segment that have mmap'ed regions radvisory advice; advice.ra_offset = offsetInFat + segFileOffset(i); - advice.ra_count = segFileSize(i); + advice.ra_count = (int)segFileSize(i); // limit prefetch to 1MB (256 pages) if ( advice.ra_count > 1024*1024 ) advice.ra_count = 1024*1024; @@ -646,19 +844,98 @@ void ImageLoaderMachO::setSlide(intptr_t slide) fSlide = slide; } -#if CODESIGNING_SUPPORT -void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile) +void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context) { - fsignatures_t siginfo; - siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file - siginfo.fs_blob_start=(void*)(codeSigCmd->dataoff); // start of CD in mach-o file - siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD - int result = fcntl(fd, F_ADDFILESIGS, &siginfo); - if ( result == -1 ) - dyld::log("dyld: F_ADDFILESIGS failed for %s with errno=%d\n", this->getPath(), errno); - //dyld::log("dyld: registered code signature for %s\n", this->getPath()); + // if dylib being loaded has no code signature load command + if ( codeSigCmd == NULL ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + bool codeSigningEnforced = context.codeSigningEnforced; + if ( context.mainExecutableCodeSigned && !codeSigningEnforced ) { + static bool codeSignEnforcementDynamicallyEnabled = false; + if ( !codeSignEnforcementDynamicallyEnabled ) { + uint32_t flags; + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { + if ( flags & CS_ENFORCEMENT ) { + codeSignEnforcementDynamicallyEnabled = true; + } + } + } + codeSigningEnforced = codeSignEnforcementDynamicallyEnabled; + } + // if we require dylibs to be code signed + if ( codeSigningEnforced ) { + // if there is a non-load command based code signature, use it + off_t offset = (off_t)offsetInFatFile; + if ( fcntl(fd, F_FINDSIGS, &offset, sizeof(offset)) != -1 ) + return; + // otherwise gracefully return from dlopen() + dyld::throwf("required code signature missing for '%s'\n", this->getPath()); + } +#endif + //Since we don't have a range for the signature we have to assume full coverage + fCoveredCodeLength = UINT64_MAX; + } + else { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // ignore code signatures in binaries built with pre-10.9 tools + if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { + return; + } +#endif + fsignatures_t siginfo; + siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file + siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of CD in mach-o file + siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD + int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); + +#if TARGET_IPHONE_SIMULATOR + // rdar://problem/18759224> check range covered by the code directory after loading + // Attempt to fallback only if we are in the simulator + + if ( result == -1 ) { + result = fcntl(fd, F_ADDFILESIGS, &siginfo); + siginfo.fs_file_start = codeSigCmd->dataoff; + } +#endif + + if ( result == -1 ) { + if ( (errno == EPERM) || (errno == EBADEXEC) ) + dyld::throwf("code signature invalid for '%s'\n", this->getPath()); + if ( context.verboseCodeSignatures ) + dyld::log("dyld: Failed registering code signature for %s, errno=%d\n", this->getPath(), errno); + siginfo.fs_file_start = UINT64_MAX; + } else if ( context.verboseCodeSignatures ) { + dyld::log("dyld: Registered code signature for %s\n", this->getPath()); + } + fCoveredCodeLength = siginfo.fs_file_start; + } } + +void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // rdar://problem/21839703> 15A226d: dyld crashes in mageLoaderMachO::validateFirstPages during dlopen() after encountering an mmap failure + // We need to ignore older code signatures because they will be bad. + if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { + return; + } #endif + if (codeSigCmd != NULL) { + if ( context.verboseMapping ) + dyld::log("dyld: validate pages: %llu\n", (unsigned long long)offsetInFat); + + void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, MAP_SHARED, fd, offsetInFat); + if ( fdata == MAP_FAILED ) { + if ( context.processRequiresLibraryValidation ) + dyld::throwf("cannot load image with wrong team ID in process using Library Validation"); + else + dyld::throwf("mmap() errno=%d validating first page of '%s'", errno, getInstallPath()); + } + if ( memcmp(fdata, fileData, lenFileData) != 0 ) + dyld::throwf("mmap() page compare failed for '%s'", getInstallPath()); + munmap(fdata, lenFileData); + } +} const char* ImageLoaderMachO::getInstallPath() const @@ -673,11 +950,6 @@ const char* ImageLoaderMachO::getInstallPath() const void ImageLoaderMachO::registerInterposing() { // mach-o files advertise interposing by having a __DATA __interpose section - uintptr_t textStart = this->segActualLoadAddress(0); - uintptr_t textEnd = this->segActualEndAddress(0); - // verify that the first segment load command is for a read-only segment - if ( ! fGoodFirstSegment ) - return; struct InterposeData { uintptr_t replacement; uintptr_t replacee; }; const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; @@ -692,14 +964,16 @@ void ImageLoaderMachO::registerInterposing() for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { if ( ((sect->flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sect->sectname, "__interpose") == 0) && (strcmp(seg->segname, "__DATA") == 0)) ) { const InterposeData* interposeArray = (InterposeData*)(sect->addr + fSlide); - const unsigned int count = sect->size / sizeof(InterposeData); - for (uint32_t i=0; i < count; ++i) { + const size_t count = sect->size / sizeof(InterposeData); + for (size_t i=0; i < count; ++i) { ImageLoader::InterposeTuple tuple; tuple.replacement = interposeArray[i].replacement; - tuple.replacementImage = this; + tuple.neverImage = this; + tuple.onlyImage = NULL; tuple.replacee = interposeArray[i].replacee; // verify that replacement is in this image - if ( (tuple.replacement >= textStart) && (tuple.replacement < textEnd) ) { + if ( this->containsAddress((void*)tuple.replacement) ) { + // chain to any existing interpositions for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { if ( it->replacee == tuple.replacee ) { tuple.replacee = it->replacement; @@ -717,6 +991,72 @@ void ImageLoaderMachO::registerInterposing() } } +uint32_t ImageLoaderMachO::sdkVersion() const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const struct version_min_command* versCmd; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + return versCmd->sdk; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header)); + const struct load_command* cmd = cmds; + const struct version_min_command* versCmd; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + return versCmd->version; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +uint32_t ImageLoaderMachO::minOSVersion() const +{ + return ImageLoaderMachO::minOSVersion(machHeader()); +} + + +void* ImageLoaderMachO::getThreadPC() const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_MAIN ) { + entry_point_command* mainCmd = (entry_point_command*)cmd; + void* entry = (void*)(mainCmd->entryoff + (char*)fMachOData); + // verify entry point is in image + if ( this->containsAddress(entry) ) + return entry; + else + throw "LC_MAIN entryoff is out of range"; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return NULL; +} + void* ImageLoaderMachO::getMain() const { @@ -727,30 +1067,31 @@ void* ImageLoaderMachO::getMain() const switch (cmd->cmd) { case LC_UNIXTHREAD: { - #if __ppc__ - const ppc_thread_state_t* registers = (ppc_thread_state_t*)(((char*)cmd) + 16); - return (void*)(registers->srr0 + fSlide); - #elif __ppc64__ - const ppc_thread_state64_t* registers = (ppc_thread_state64_t*)(((char*)cmd) + 16); - return (void*)(registers->srr0 + fSlide); - #elif __i386__ + #if __i386__ const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); - return (void*)(registers->eip + fSlide); + void* entry = (void*)(registers->eip + fSlide); #elif __x86_64__ const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); - return (void*)(registers->rip + fSlide); + void* entry = (void*)(registers->rip + fSlide); #elif __arm__ const arm_thread_state_t* registers = (arm_thread_state_t*)(((char*)cmd) + 16); - return (void*)(registers->__pc + fSlide); + void* entry = (void*)(registers->__pc + fSlide); + #elif __arm64__ + const arm_thread_state64_t* registers = (arm_thread_state64_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->__pc + fSlide); #else #warning need processor specific code #endif + // verify entry point is in image + if ( this->containsAddress(entry) ) { + return entry; + } } break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - return NULL; + throw "no valid entry point"; } bool ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh) @@ -860,8 +1201,8 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorpath.offset; - if ( strncmp(path, "@loader_path/", 13) == 0 ) { - if ( context.processIsRestricted && (context.mainExecutable == this) ) { + if ( (strncmp(path, "@loader_path", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) { + if ( context.processIsRestricted && !context.processRequiresLibraryValidation && (context.mainExecutable == this) ) { dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath()); break; } @@ -870,15 +1211,14 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorgetPath()); break; } @@ -887,14 +1227,13 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorgetPath()); break; } @@ -904,8 +1243,8 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorncmds; @@ -1010,22 +1350,21 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) #if TEXT_RELOC_SUPPORT void ImageLoaderMachO::makeTextSegmentWritable(const LinkContext& context, bool writeable) { - int textSegmentIndex = 0; for(unsigned int i=0; i < fSegmentsCount; ++i) { - if ( strcmp(segName(i), "__TEXT") == 0 ) { - textSegmentIndex = i; - break; + if ( segExecutable(i) ) { + if ( writeable ) { + segMakeWritable(i, context); + } + else { + #if !__i386__ && !__x86_64__ + // some processors require range to be invalidated before it is made executable + sys_icache_invalidate((void*)segActualLoadAddress(i), segSize(textSegmentIndex)); + #endif + segProtect(i, context); + } } } - if ( writeable ) { - segMakeWritable(textSegmentIndex, context); - } - else { - // iPhoneOS requires range to be invalidated before it is made executable - sys_icache_invalidate((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex)); - segProtect(textSegmentIndex, context); - } } #endif @@ -1064,18 +1403,9 @@ uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const Li uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, const LinkContext& context, bool runResolver) const { - uintptr_t result = exportedSymbolAddress(context, sym, runResolver); + uintptr_t result = exportedSymbolAddress(context, sym, requestor, runResolver); // check for interposing overrides - for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { - // replace all references to 'replacee' with 'replacement' - if ( (result == it->replacee) && (requestor != it->replacementImage) ) { - if ( context.verboseInterposing ) { - dyld::log("dyld interposing: replace 0x%lX with 0x%lX in %s\n", - it->replacee, it->replacement, this->getPath()); - } - result = it->replacement; - } - } + result = interposedAddress(context, result, requestor); return result; } @@ -1214,11 +1544,13 @@ bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segme void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const LinkContext& context, const char* symbol, - const char* referencedFrom, const char* expectedIn) + const char* referencedFrom, const char* fromVersMismatch, + const char* expectedIn) { // record values for possible use by CrashReporter or Finder (*context.setErrorStrings)(dyld_error_kind_symbol_missing, referencedFrom, expectedIn, symbol); - dyld::throwf("Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n", symbol, referencedFrom, expectedIn); + dyld::throwf("Symbol not found: %s\n Referenced from: %s%s\n Expected in: %s\n", + symbol, referencedFrom, fromVersMismatch, expectedIn); } const mach_header* ImageLoaderMachO::machHeader() const @@ -1286,7 +1618,7 @@ uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t l break; case BIND_TYPE_TEXT_PCREL32: loc32 = (uint32_t*)locationToFix; - value32 = (uint32_t)newValue - (((uintptr_t)locationToFix) + 4); + value32 = (uint32_t)(newValue - (((uintptr_t)locationToFix) + 4)); if ( *loc32 != value32 ) *loc32 = value32; break; @@ -1306,15 +1638,13 @@ uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t l #if SUPPORT_OLD_CRT_INITIALIZATION // first 16 bytes of "start" in crt1.o -#if __ppc__ - static uint32_t sStandardEntryPointInstructions[4] = { 0x7c3a0b78, 0x3821fffc, 0x54210034, 0x38000000 }; -#elif __i386__ +#if __i386__ static uint8_t sStandardEntryPointInstructions[16] = { 0x6a, 0x00, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0x8b, 0x5d, 0x04, 0x89, 0x5c, 0x24 }; #endif #endif struct DATAdyld { - void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper_interface + void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup // the following only exist in main executables built for 10.5 or later ProgramVars vars; @@ -1322,7 +1652,6 @@ struct DATAdyld { // These are defined in dyldStartup.s extern "C" void stub_binding_helper(); -extern "C" bool dyld_func_lookup(const char* name, uintptr_t* address); void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) @@ -1331,28 +1660,29 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) const uint32_t cmd_count = mh->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; const struct load_command* cmd; - // set up __dyld section - // optimizations: - // 1) do nothing if image is in dyld shared cache and dyld loaded at same address as when cache built - // 2) first read __dyld value, if already correct don't write, this prevents dirtying a page - if ( !fInSharedCache || !context.dyldLoadedAtSameAddressNeededBySharedCache ) { + // There used to be some optimizations to skip this section scan, but we need to handle the + // __dyld section in libdyld.dylib, so everything needs to be scanned for now. + // CrashTracer: 1,295 crashes in bash at bash: getenv + if ( true ) { cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { if ( cmd->cmd == LC_SEGMENT_COMMAND ) { const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if ( strcmp(seg->segname, "__DATA") == 0 ) { + if ( strncmp(seg->segname, "__DATA", 6) == 0 ) { const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { if ( strcmp(sect->sectname, "__dyld" ) == 0 ) { struct DATAdyld* dd = (struct DATAdyld*)(sect->addr + fSlide); + #if !__arm64__ && !__ARM_ARCH_7K__ if ( sect->size > offsetof(DATAdyld, dyldLazyBinder) ) { if ( dd->dyldLazyBinder != (void*)&stub_binding_helper ) dd->dyldLazyBinder = (void*)&stub_binding_helper; } + #endif // !__arm64__ if ( sect->size > offsetof(DATAdyld, dyldFuncLookup) ) { - if ( dd->dyldFuncLookup != (void*)&dyld_func_lookup ) - dd->dyldFuncLookup = (void*)&dyld_func_lookup; + if ( dd->dyldFuncLookup != (void*)&_dyld_func_lookup ) + dd->dyldFuncLookup = (void*)&_dyld_func_lookup; } if ( mh->filetype == MH_EXECUTE ) { // there are two ways to get the program variables @@ -1377,6 +1707,16 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) #endif } } + else if ( mh->filetype == MH_DYLIB ) { + const char* installPath = this->getInstallPath(); + if ( (installPath != NULL) && (strncmp(installPath, "/usr/lib/", 9) == 0) ) { + if ( sect->size > offsetof(DATAdyld, vars) ) { + // use ProgramVars from libdyld.dylib but tweak mh field to correct value + dd->vars.mh = context.mainExecutable->machHeader(); + context.setNewProgramVars(dd->vars); + } + } + } } else if ( (strcmp(sect->sectname, "__program_vars" ) == 0) && (mh->filetype == MH_EXECUTE) ) { // this is a Mac OS X 10.6 or later main executable @@ -1451,16 +1791,6 @@ bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const void ImageLoaderMachO::doImageInit(const LinkContext& context) { if ( fHasDashInit ) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED - // verify initializers are in first segment for dylibs - if ( this->isDylib() && !fGoodFirstSegment ) { - if ( context.verboseInit ) - dyld::log("dyld: ignoring -init in %s\n", this->getPath()); - return; - } - uintptr_t textStart = this->segActualLoadAddress(0); - uintptr_t textEnd = this->segActualEndAddress(0); -#endif const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; const struct load_command* cmd = cmds; @@ -1468,20 +1798,13 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context) switch (cmd->cmd) { case LC_ROUTINES_COMMAND: Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide); -#if __IPHONE_OS_VERSION_MIN_REQUIRED - // verify initializers are in first segment for dylibs - if ( this->isDylib() && (((uintptr_t)func >= textEnd) || ((uintptr_t)func < textStart)) ) { - if ( context.verboseInit ) - dyld::log("dyld: ignoring out of bounds initializer function %p in %s\n", func, this->getPath()); - } - else { -#endif - if ( context.verboseInit ) - dyld::log("dyld: calling -init function 0x%p in %s\n", func, this->getPath()); - func(context.argc, context.argv, context.envp, context.apple, &context.programVars); -#if __IPHONE_OS_VERSION_MIN_REQUIRED + // verify initializers are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); } -#endif + if ( context.verboseInit ) + dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath()); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); @@ -1492,16 +1815,6 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context) void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) { if ( fHasInitializers ) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED - // verify initializers are in first segment for dylibs - if ( this->isDylib() && !fGoodFirstSegment ) { - if ( context.verboseInit ) - dyld::log("dyld: ignoring all initializers in %s\n", this->getPath()); - return; - } - uintptr_t textStart = this->segActualLoadAddress(0); - uintptr_t textEnd = this->segActualEndAddress(0); -#endif const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; const struct load_command* cmd = cmds; @@ -1514,23 +1827,16 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) const uint8_t type = sect->flags & SECTION_TYPE; if ( type == S_MOD_INIT_FUNC_POINTERS ) { Initializer* inits = (Initializer*)(sect->addr + fSlide); - const uint32_t count = sect->size / sizeof(uintptr_t); - for (uint32_t i=0; i < count; ++i) { + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t i=0; i < count; ++i) { Initializer func = inits[i]; -#if __IPHONE_OS_VERSION_MIN_REQUIRED - // verify initializers are in first segment for dylibs - if ( this->isDylib() && (((uintptr_t)func >= textEnd) || ((uintptr_t)func < textStart)) ) { - if ( context.verboseInit ) - dyld::log("dyld: ignoring out of bounds initializer function %p in %s\n", func, this->getPath()); - } - else { -#endif - if ( context.verboseInit ) - dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); - func(context.argc, context.argv, context.envp, context.apple, &context.programVars); -#if __IPHONE_OS_VERSION_MIN_REQUIRED + // verify initializers are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); } -#endif + if ( context.verboseInit ) + dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); } } } @@ -1617,9 +1923,13 @@ void ImageLoaderMachO::doTermination(const LinkContext& context) const uint8_t type = sect->flags & SECTION_TYPE; if ( type == S_MOD_TERM_FUNC_POINTERS ) { Terminator* terms = (Terminator*)(sect->addr + fSlide); - const uint32_t count = sect->size / sizeof(uintptr_t); - for (uint32_t i=count; i > 0; --i) { + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t i=count; i > 0; --i) { Terminator func = terms[i-1]; + // verify terminators are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("termination function %p not in mapped image for %s\n", func, this->getPath()); + } if ( context.verboseInit ) dyld::log("dyld: calling termination function %p in %s\n", func, this->getPath()); func(); @@ -1655,7 +1965,13 @@ intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) uintptr_t highAddr = 0; for(unsigned int i=0, e=segmentCount(); i < e; ++i) { const uintptr_t segLow = segPreferredLoadAddress(i); - const uintptr_t segHigh = (segLow + segSize(i) + 4095) & -4096; + const uintptr_t segHigh = dyld_page_round(segLow + segSize(i)); + if ( segLow < highAddr ) { + if ( dyld_page_size > 4096 ) + dyld::throwf("can't map segments into 16KB pages"); + else + dyld::throwf("overlapping segments"); + } if ( segLow < lowAddr ) lowAddr = segLow; if ( segHigh > highAddr ) @@ -1692,16 +2008,16 @@ uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoad // in PIE programs, load initial dylibs after main executable so they don't have fixed addresses either if ( fgNextPIEDylibAddress != 0 ) { // add small (0-3 pages) random padding between dylibs - addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*4096; + addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*dyld_page_size; //dyld::log("padding 0x%08llX, guard=0x%08llX\n", (long long)(addr - fgNextPIEDylibAddress), (long long)(__stack_chk_guard)); - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_FIXED); + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); if ( r == KERN_SUCCESS ) { fgNextPIEDylibAddress = addr + size; return addr; } fgNextPIEDylibAddress = 0; } - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE); + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_DYLIB)); if ( r != KERN_SUCCESS ) throw "out of address space"; @@ -1712,7 +2028,7 @@ bool ImageLoaderMachO::reserveAddressRange(uintptr_t start, size_t length) { vm_address_t addr = start; vm_size_t size = length; - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); if ( r != KERN_SUCCESS ) return false; return true; @@ -1724,31 +2040,21 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF { // find address range for image intptr_t slide = this->assignSegmentAddresses(context); - if ( context.verboseMapping ) - dyld::log("dyld: Mapping %s\n", this->getPath()); - // verify that the first segment load command is for a r-x segment - // that starts at begining of file and is larger than all load commands - uintptr_t firstSegMappedStart = segPreferredLoadAddress(0) + slide; - uintptr_t firstSegMappedEnd = firstSegMappedStart + this->segSize(0); - if ( (this->segLoadCommand(0)->initprot == (VM_PROT_EXECUTE|VM_PROT_READ)) - && (this->segFileOffset(0) == 0) - && (this->segFileSize(0) != 0) - && (this->segSize(0) > ((macho_header*)fMachOData)->sizeofcmds) ) { - fGoodFirstSegment = true; + if ( context.verboseMapping ) { + if ( offsetInFat != 0 ) + dyld::log("dyld: Mapping %s (slice offset=%llu)\n", this->getPath(), (unsigned long long)offsetInFat); + else + dyld::log("dyld: Mapping %s\n", this->getPath()); } // map in all segments for(unsigned int i=0, e=segmentCount(); i < e; ++i) { vm_offset_t fileOffset = segFileOffset(i) + offsetInFat; vm_size_t size = segFileSize(i); uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide; - // verify other segments map after first - if ( (i != 0) && (requestedLoadAddress < firstSegMappedEnd) ) - fGoodFirstSegment = false; int protection = 0; if ( !segUnaccessible(i) ) { // If has text-relocs, don't set x-bit initially. // Instead set it later after text-relocs have been done. - // The iPhone OS does not like it when you make executable code writable. if ( segExecutable(i) && !(segHasRebaseFixUps(i) && (slide != 0)) ) protection |= PROT_EXEC; if ( segReadable(i) ) @@ -1767,7 +2073,7 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF dyld::throwf("truncated mach-o error: segment %s extends to %llu which is past end of file %llu", segName(i), (uint64_t)(fileOffset+size), fileLen); } - void* loadAddress = mmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); + void* loadAddress = xmmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); if ( loadAddress == ((void*)(-1)) ) { dyld::throwf("mmap() error %d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s", errno, requestedLoadAddress, (uintptr_t)size, segName(i), getPath());