ImageLoaderMachOClassic.cpp [plain text]
#define __srr0 srr0
#define __eip eip
#define __rip rip
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <mach/mach.h>
#include <mach/thread_status.h>
#include <mach-o/loader.h>
#include <mach-o/reloc.h>
#include <mach-o/nlist.h>
#include <sys/sysctl.h>
#include <libkern/OSAtomic.h>
#include <libkern/OSCacheControl.h>
#if __ppc__ || __ppc64__
#include <mach-o/ppc/reloc.h>
#endif
#if __x86_64__
#include <mach-o/x86_64/reloc.h>
#endif
#if __arm__
#include <mach-o/arm/reloc.h>
#endif
#include "ImageLoaderMachOClassic.h"
#include "mach-o/dyld_images.h"
#if __ppc__
#include <ppc_intrinsics.h>
#else
#define astrcmp(a,b) strcmp(a,b)
#endif
extern "C" void fast_stub_binding_helper_interface();
#if __x86_64__
#define POINTER_RELOC X86_64_RELOC_UNSIGNED
#else
#define POINTER_RELOC GENERIC_RELOC_VANILLA
#endif
#if __LP64__
#define RELOC_SIZE 3
#define LC_SEGMENT_COMMAND LC_SEGMENT_64
#define LC_ROUTINES_COMMAND LC_ROUTINES_64
struct macho_segment_command : public segment_command_64 {};
struct macho_section : public section_64 {};
struct macho_routines_command : public routines_command_64 {};
#else
#define RELOC_SIZE 2
#define LC_SEGMENT_COMMAND LC_SEGMENT
#define LC_ROUTINES_COMMAND LC_ROUTINES
struct macho_segment_command : public segment_command {};
struct macho_section : public section {};
struct macho_routines_command : public routines_command {};
#endif
ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path,
unsigned int segCount, unsigned int libCount, const LinkContext& context)
{
ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount);
image->setSlide(slide);
if ( slide != 0 )
fgNextPIEDylibAddress = (uintptr_t)image->getEnd();
image->instantiateFinish(context);
image->setMapped(context);
#if __i386__
if ( image->fReadOnlyImportSegment ) {
for(unsigned int i=0; i < image->fSegmentsCount; ++i) {
if ( image->segIsReadOnlyImport(i) )
image->segMakeWritable(i, context);
}
}
#endif
if ( context.verboseMapping ) {
dyld::log("dyld: Main executable mapped %s\n", path);
for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) {
const char* name = image->segName(i);
if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) )
dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i));
else
dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i));
}
}
return image;
}
ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData,
uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info,
unsigned int segCount, unsigned int libCount,
const struct linkedit_data_command* codeSigCmd, const LinkContext& context)
{
ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart((macho_header*)fileData, path, segCount, libCount);
try {
image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime);
#if CODESIGNING_SUPPORT
if ( codeSigCmd != NULL )
image->loadCodeSignature(codeSigCmd, fd, offsetInFat);
#endif
image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context);
image->instantiateFinish(context);
const char* installName = image->getInstallPath();
if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') )
image->setPathUnowned(installName);
else if ( path[0] != '/' ) {
char realPath[MAXPATHLEN];
if ( realpath(path, realPath) != NULL )
image->setPath(realPath);
else
image->setPath(path);
}
else
image->setPath(path);
image->setMapped(context);
if ( !context.preFetchDisabled && !image->isPrebindable())
image->preFetchDATA(fd, offsetInFat, context);
}
catch (...) {
delete image;
throw;
}
return image;
}
ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info,
unsigned int segCount, unsigned int libCount, const LinkContext& context)
{
ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount);
try {
image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime);
image->fInSharedCache = true;
image->setNeverUnload();
if ( context.verboseMapping ) {
dyld::log("dyld: Using shared cached for %s\n", path);
for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) {
dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i));
}
}
image->instantiateFinish(context);
image->setMapped(context);
}
catch (...) {
delete image;
throw;
}
return image;
}
ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len,
unsigned int segCount, unsigned int libCount, const LinkContext& context)
{
ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, moduleName, segCount, libCount);
try {
if ( mh->filetype == MH_EXECUTE )
throw "can't load another MH_EXECUTE";
image->ImageLoaderMachO::mapSegments((const void*)mh, len, context);
image->setNeverUnload();
if ( moduleName != NULL )
image->setPath(moduleName);
image->instantiateFinish(context);
image->setMapped(context);
}
catch (...) {
delete image;
throw;
}
return image;
}
ImageLoaderMachOClassic::ImageLoaderMachOClassic(const macho_header* mh, const char* path,
unsigned int segCount, uint32_t segOffsets[], unsigned int libCount)
: ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fStrings(NULL), fSymbolTable(NULL), fDynamicInfo(NULL)
{
}
ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateStart(const macho_header* mh, const char* path,
unsigned int segCount, unsigned int libCount)
{
size_t size = sizeof(ImageLoaderMachOClassic) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*);
ImageLoaderMachOClassic* allocatedSpace = static_cast<ImageLoaderMachOClassic*>(malloc(size));
if ( allocatedSpace == NULL )
throw "malloc failed";
uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOClassic)));
bzero(&segOffsets[segCount], libCount*sizeof(void*)); return new (allocatedSpace) ImageLoaderMachOClassic(mh, path, segCount, segOffsets, libCount);
}
void ImageLoaderMachOClassic::instantiateFinish(const LinkContext& context)
{
this->parseLoadCmds();
}
ImageLoaderMachOClassic::~ImageLoaderMachOClassic()
{
destroy();
}
uint32_t* ImageLoaderMachOClassic::segmentCommandOffsets() const
{
return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic)));
}
ImageLoader* ImageLoaderMachOClassic::libImage(unsigned int libIndex) const
{
const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t)));
return (ImageLoader*)(images[libIndex] & (-4));
}
bool ImageLoaderMachOClassic::libReExported(unsigned int libIndex) const
{
const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t)));
return ((images[libIndex] & 1) != 0);
}
bool ImageLoaderMachOClassic::libIsUpward(unsigned int libIndex) const
{
const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t)));
return ((images[libIndex] & 2) != 0);
}
void ImageLoaderMachOClassic::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward)
{
uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t)));
uintptr_t value = (uintptr_t)image;
if ( reExported )
value |= 1;
if ( upward )
value |= 2;
images[libIndex] = value;
}
void ImageLoaderMachOClassic::setSymbolTableInfo(const macho_nlist* symbols, const char* strings, const dysymtab_command* dynSym)
{
fSymbolTable = symbols;
fStrings = strings;
fDynamicInfo = dynSym;
}
void ImageLoaderMachOClassic::prefetchLINKEDIT(const LinkContext& context)
{
uintptr_t symbolTableStart = (uintptr_t)fSymbolTable;
uintptr_t stringTableStart = (uintptr_t)fStrings;
uintptr_t start;
if ( segPreferredLoadAddress(0) != (uintptr_t)fMachOData ) {
start = (uintptr_t)fMachOData + fDynamicInfo->locreloff;
}
else {
start = symbolTableStart + fDynamicInfo->iextdefsym * sizeof(macho_nlist);
}
uintptr_t end = stringTableStart;
if ( fDynamicInfo->nundefsym != 0 )
end += fSymbolTable[fDynamicInfo->iundefsym+fDynamicInfo->nundefsym-1].n_un.n_strx;
else if ( fDynamicInfo->nextdefsym != 0 )
end += fSymbolTable[fDynamicInfo->iextdefsym+fDynamicInfo->nextdefsym-1].n_un.n_strx;
start = start & (-4096);
end = (end + 4095) & (-4096);
if ( (end-start) > 4096 ) {
madvise((void*)start, end-start, MADV_WILLNEED);
fgTotalBytesPreFetched += (end-start);
if ( context.verboseMapping ) {
dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", "__LINKEDIT", start, end-1);
}
}
}
#if SPLIT_SEG_DYLIB_SUPPORT
unsigned int
ImageLoaderMachOClassic::getExtraZeroFillEntriesCount()
{
unsigned int extraZeroFillEntries = 0;
for(unsigned int i=0; i < fSegmentsCount; ++i) {
if ( segHasTrailingZeroFill(i) )
++extraZeroFillEntries;
}
return extraZeroFillEntries;
}
void
ImageLoaderMachOClassic::initMappingTable(uint64_t offsetInFat,
shared_file_mapping_np *mappingTable)
{
for(unsigned int i=0,entryIndex=0; i < fSegmentsCount; ++i, ++entryIndex) {
shared_file_mapping_np* entry = &mappingTable[entryIndex];
entry->sfm_address = segActualLoadAddress(i);
entry->sfm_size = segFileSize(i);
entry->sfm_file_offset = segFileOffset(i) + offsetInFat;
entry->sfm_init_prot = VM_PROT_NONE;
if ( !segUnaccessible(i) ) {
if ( segExecutable(i) )
entry->sfm_init_prot |= VM_PROT_EXECUTE;
if ( segReadable(i) )
entry->sfm_init_prot |= VM_PROT_READ;
if ( segWriteable(i) )
entry->sfm_init_prot |= VM_PROT_WRITE | VM_PROT_COW;
}
entry->sfm_max_prot = entry->sfm_init_prot;
if ( segHasTrailingZeroFill(i) ) {
shared_file_mapping_np* zfentry = &mappingTable[++entryIndex];
zfentry->sfm_address = entry->sfm_address + segFileSize(i);
zfentry->sfm_size = segSize(i) - segFileSize(i);
zfentry->sfm_file_offset = 0;
zfentry->sfm_init_prot = entry->sfm_init_prot | VM_PROT_COW | VM_PROT_ZF;
zfentry->sfm_max_prot = zfentry->sfm_init_prot;
}
}
}
int
ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd,
uint64_t offsetInFat,
uint64_t lenInFat,
uint64_t fileLen,
const LinkContext& context)
{
uintptr_t nextAltLoadAddress = 0;
const unsigned int segmentCount = fSegmentsCount;
const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount();
const unsigned int regionCount = segmentCount+extraZeroFillEntries;
shared_file_mapping_np regions[regionCount];
initMappingTable(offsetInFat, regions);
int r = -1;
bool foundRoom = false;
while ( ! foundRoom ) {
foundRoom = true;
for(unsigned int i=0; i < regionCount; ++i) {
vm_address_t addr = nextAltLoadAddress + regions[i].sfm_address - regions[0].sfm_address;
vm_size_t size = regions[i].sfm_size ;
r = vm_allocate(mach_task_self(), &addr, size, false );
if ( 0 != r ) {
for(unsigned int j=0; j < i; ++j) {
vm_address_t addr = nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address;
vm_size_t size = regions[j].sfm_size ;
(void)vm_deallocate(mach_task_self(), addr, size);
}
nextAltLoadAddress += 0x00100000; if ( (SHARED_REGION_BASE <= nextAltLoadAddress) && (nextAltLoadAddress < (SHARED_REGION_BASE + SHARED_REGION_SIZE)) )
nextAltLoadAddress = (SHARED_REGION_BASE + SHARED_REGION_SIZE);
if ( nextAltLoadAddress > 0xFF000000 )
throw "can't map split seg anywhere";
foundRoom = false;
break;
}
}
}
uintptr_t slide = nextAltLoadAddress - regions[0].sfm_address;
this->setSlide(slide);
for(unsigned int i=0; i < regionCount; ++i) {
if ( ((regions[i].sfm_init_prot & VM_PROT_ZF) != 0) || (regions[i].sfm_size == 0) ) {
}
else {
void* mmapAddress = (void*)(uintptr_t)(regions[i].sfm_address + slide);
size_t size = regions[i].sfm_size;
int protection = 0;
if ( regions[i].sfm_init_prot & VM_PROT_EXECUTE )
protection |= PROT_EXEC;
if ( regions[i].sfm_init_prot & VM_PROT_READ )
protection |= PROT_READ;
if ( regions[i].sfm_init_prot & VM_PROT_WRITE )
protection |= PROT_WRITE;
off_t offset = regions[i].sfm_file_offset;
mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset);
if ( mmapAddress == ((void*)(-1)) )
throw "mmap error";
}
}
if ( context.verboseMapping ) {
dyld::log("dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide, this->getPath());
for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){
const shared_file_mapping_np* entry = ®ions[entryIndex];
if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 )
dyld::log("%18s at 0x%08lX->0x%08lX\n",
segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1);
if ( entryIndex < (regionCount-1) ) {
const shared_file_mapping_np* nextEntry = ®ions[entryIndex+1];
if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) {
uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address;
dyld::log("%18s at 0x%08lX->0x%08lX (zerofill)\n",
segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1));
++entryIndex;
}
}
}
}
return r;
}
#endif // SPLIT_SEG_DYLIB_SUPPORT
void ImageLoaderMachOClassic::mapSegmentsClassic(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context)
{
if ( !fIsSplitSeg )
return ImageLoaderMachO::mapSegments(fd, offsetInFat, lenInFat, fileLen, context);
#if SPLIT_SEG_SHARED_REGION_SUPPORT
if ( ! context.dyldLoadedAtSameAddressNeededBySharedCache ) {
if ( mapSplitSegDylibInfoSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) == 0)
return;
}
#endif
#if SPLIT_SEG_DYLIB_SUPPORT
if ( mapSplitSegDylibOutsideSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) != 0 )
#endif
throw "mapping error";
}
#if SPLIT_SEG_SHARED_REGION_SUPPORT
static int _shared_region_map_np(int fd, uint32_t count, const shared_file_mapping_np mappings[])
{
return syscall(295, fd, count, mappings);
}
int
ImageLoaderMachOClassic::mapSplitSegDylibInfoSharedRegion(int fd,
uint64_t offsetInFat,
uint64_t lenInFat,
uint64_t fileLen,
const LinkContext& context)
{
const unsigned int segmentCount = fSegmentsCount;
const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount();
const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries;
shared_file_mapping_np mappingTable[mappingTableCount];
initMappingTable(offsetInFat, mappingTable);
int r = _shared_region_map_np(fd, mappingTableCount, mappingTable);
if ( 0 == r ) {
this->setNeverUnload();
if ( context.verboseMapping ) {
dyld::log("dyld: Mapping split-seg shared %s\n", this->getPath());
for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){
const shared_file_mapping_np* entry = &mappingTable[entryIndex];
if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 )
dyld::log("%18s at 0x%08lX->0x%08lX\n",
segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1);
if ( entryIndex < (mappingTableCount-1) ) {
const shared_file_mapping_np* nextEntry = &mappingTable[entryIndex+1];
if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) {
uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address;
dyld::log("%18s at 0x%08lX->0x%08lX\n",
segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset),
(uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1));
++entryIndex;
}
}
}
}
}
return r;
}
#endif // SPLIT_SEG_SHARED_REGION_SUPPORT
bool ImageLoaderMachOClassic::isSubframeworkOf(const LinkContext& context, const ImageLoader* parent) const
{
if ( fInUmbrella ) {
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_SUB_FRAMEWORK) {
const struct sub_framework_command* subf = (struct sub_framework_command*)cmd;
const char* exportThruName = (char*)cmd + subf->umbrella.offset;
const char* parentInstallPath = parent->getInstallPath();
if ( parentInstallPath != NULL ) {
const char* lastSlash = strrchr(parentInstallPath, '/');
if ( lastSlash != NULL ) {
if ( strcmp(&lastSlash[1], exportThruName) == 0 )
return true;
if ( context.imageSuffix != NULL ) {
char reexportAndSuffix[strlen(context.imageSuffix)+strlen(exportThruName)+1];
strcpy(reexportAndSuffix, exportThruName);
strcat(reexportAndSuffix, context.imageSuffix);
if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 )
return true;
}
}
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
return false;
}
bool ImageLoaderMachOClassic::hasSubLibrary(const LinkContext& context, const ImageLoader* child) const
{
if ( fHasSubLibraries ) {
const char* childInstallPath = child->getInstallPath();
if ( childInstallPath != NULL ) {
const char* lastSlash = strrchr(childInstallPath, '/');
if ( lastSlash != NULL ) {
const char* firstDot = strchr(lastSlash, '.');
int len;
if ( firstDot == NULL )
len = strlen(lastSlash);
else
len = firstDot-lastSlash-1;
char childLeafName[len+1];
strncpy(childLeafName, &lastSlash[1], len);
childLeafName[len] = '\0';
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) {
switch (cmd->cmd) {
case LC_SUB_LIBRARY:
{
const struct sub_library_command* lib = (struct sub_library_command*)cmd;
const char* aSubLibName = (char*)cmd + lib->sub_library.offset;
if ( strcmp(aSubLibName, childLeafName) == 0 )
return true;
if ( context.imageSuffix != NULL ) {
char aSubLibNameAndSuffix[strlen(context.imageSuffix)+strlen(aSubLibName)+1];
strcpy(aSubLibNameAndSuffix, aSubLibName);
strcat(aSubLibNameAndSuffix, context.imageSuffix);
if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 )
return true;
}
}
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
}
if ( fHasSubUmbrella ) {
const char* childInstallPath = child->getInstallPath();
if ( childInstallPath != NULL ) {
const char* lastSlash = strrchr(childInstallPath, '/');
if ( lastSlash != NULL ) {
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) {
switch (cmd->cmd) {
case LC_SUB_UMBRELLA:
{
const struct sub_umbrella_command* um = (struct sub_umbrella_command*)cmd;
const char* aSubUmbrellaName = (char*)cmd + um->sub_umbrella.offset;
if ( strcmp(aSubUmbrellaName, &lastSlash[1]) == 0 )
return true;
if ( context.imageSuffix != NULL ) {
char umbrellaAndSuffix[strlen(context.imageSuffix)+strlen(aSubUmbrellaName)+1];
strcpy(umbrellaAndSuffix, aSubUmbrellaName);
strcat(umbrellaAndSuffix, context.imageSuffix);
if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 )
return true;
}
}
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
}
return false;
}
uintptr_t ImageLoaderMachOClassic::getFirstWritableSegmentAddress()
{
for(unsigned int i=0; i < fSegmentsCount; ++i) {
if ( segWriteable(i) )
return segActualLoadAddress(i);
}
throw "no writable segment";
}
uintptr_t ImageLoaderMachOClassic::getRelocBase()
{
#if __x86_64__
return getFirstWritableSegmentAddress();
#else
if ( fIsSplitSeg )
return getFirstWritableSegmentAddress();
else
return segActualLoadAddress(0);
#endif
}
#if __ppc__
static inline void otherRelocsPPC(uintptr_t* locationToFix, uint8_t relocationType, uint16_t otherHalf, uintptr_t slide)
{
struct ppcInstruction { uint16_t opcode; int16_t immediateValue; };
ppcInstruction* instruction = (ppcInstruction*)locationToFix;
switch ( relocationType )
{
case PPC_RELOC_LO16:
instruction->immediateValue = ((otherHalf << 16) | instruction->immediateValue) + slide;
break;
case PPC_RELOC_HI16:
instruction->immediateValue = ((((instruction->immediateValue << 16) | otherHalf) + slide) >> 16);
break;
case PPC_RELOC_HA16:
int16_t signedOtherHalf = (int16_t)(otherHalf & 0xffff);
uint32_t temp = (instruction->immediateValue << 16) + signedOtherHalf + slide;
if ( (temp & 0x00008000) != 0 )
temp += 0x00008000;
instruction->immediateValue = temp >> 16;
}
}
#endif
#if PREBOUND_IMAGE_SUPPORT
void ImageLoaderMachOClassic::resetPreboundLazyPointers(const LinkContext& context)
{
const uintptr_t relocBase = this->getRelocBase();
register const uintptr_t slide = this->fSlide;
const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]);
const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel];
for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
if ( (reloc->r_address & R_SCATTERED) != 0 ) {
const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc;
if (sreloc->r_length == RELOC_SIZE) {
uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase);
switch(sreloc->r_type) {
#if __ppc__
case PPC_RELOC_PB_LA_PTR:
*locationToFix = sreloc->r_value + slide;
break;
#endif
#if __i386__
case GENERIC_RELOC_PB_LA_PTR:
*locationToFix = sreloc->r_value + slide;
break;
#endif
#if __arm__
case ARM_RELOC_PB_LA_PTR:
*locationToFix = sreloc->r_value + slide;
break;
#endif
}
}
}
}
}
#endif
void ImageLoaderMachOClassic::rebase(const LinkContext& context)
{
CRSetCrashLogMessage2(this->getPath());
register const uintptr_t slide = this->fSlide;
const uintptr_t relocBase = this->getRelocBase();
if ( !context.preFetchDisabled && !this->isPrebindable())
this->prefetchLINKEDIT(context);
const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]);
const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel];
for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
try {
#if LINKEDIT_USAGE_DEBUG
noteAccessedLinkEditAddress(reloc);
#endif
#if __x86_64__
if ( reloc->r_length != 3 )
throw "bad local relocation length";
if ( reloc->r_type != X86_64_RELOC_UNSIGNED )
throw "unknown local relocation type";
if ( reloc->r_pcrel != 0 )
throw "bad local relocation pc_rel";
if ( reloc->r_extern != 0 )
throw "extern relocation found with local relocations";
*((uintptr_t*)(reloc->r_address + relocBase)) += slide;
#else
if ( (reloc->r_address & R_SCATTERED) == 0 ) {
if ( reloc->r_symbolnum == R_ABS ) {
}
else if (reloc->r_length == RELOC_SIZE) {
switch(reloc->r_type) {
case GENERIC_RELOC_VANILLA:
*((uintptr_t*)(reloc->r_address + relocBase)) += slide;
break;
#if __ppc__
case PPC_RELOC_HI16:
case PPC_RELOC_LO16:
case PPC_RELOC_HA16:
otherRelocsPPC((uintptr_t*)(reloc->r_address + relocBase), reloc->r_type, reloc[1].r_address, slide);
++reloc; break;
#endif
default:
throw "unknown local relocation type";
}
}
else {
throw "bad local relocation length";
}
}
else {
const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc;
if (sreloc->r_length == RELOC_SIZE) {
uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase);
switch(sreloc->r_type) {
case GENERIC_RELOC_VANILLA:
*locationToFix += slide;
break;
#if __ppc__
case PPC_RELOC_HI16:
case PPC_RELOC_LO16:
case PPC_RELOC_HA16:
++reloc; otherRelocsPPC(locationToFix, sreloc->r_type, reloc->r_address, slide);
break;
case PPC_RELOC_PB_LA_PTR:
break;
#elif __ppc64__
case PPC_RELOC_PB_LA_PTR:
*locationToFix += slide;
break;
#elif __i386__
case GENERIC_RELOC_PB_LA_PTR:
break;
#elif __arm__
case ARM_RELOC_PB_LA_PTR:
break;
#endif
default:
throw "unknown local scattered relocation type";
}
}
else {
throw "bad local scattered relocation length";
}
}
#endif }
catch (const char* msg) {
const uint8_t* r = (uint8_t*)reloc;
dyld::throwf("%s in %s. reloc record at %p: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
msg, this->getPath(), reloc, r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]);
}
}
fgTotalRebaseFixups += fDynamicInfo->nlocrel;
CRSetCrashLogMessage2(NULL);
}
const struct macho_nlist* ImageLoaderMachOClassic::binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[],
const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const
{
int32_t high = symbolCount-1;
int32_t mid = hintIndex;
if ( mid >= (int32_t)symbolCount )
mid = symbolCount/2;
++ImageLoaderMachO::fgSymbolTableBinarySearchs;
++fgTotalBindImageSearches;
for (int32_t low = 0; low <= high; mid = (low+high)/2) {
const uint32_t index = toc[mid].symbol_index;
const struct macho_nlist* pivot = &symbols[index];
const char* pivotStr = &stringPool[pivot->n_un.n_strx];
#if LINKEDIT_USAGE_DEBUG
noteAccessedLinkEditAddress(&toc[mid]);
noteAccessedLinkEditAddress(pivot);
noteAccessedLinkEditAddress(pivotStr);
#endif
int cmp = astrcmp(key, pivotStr);
if ( cmp == 0 )
return pivot;
if ( cmp > 0 ) {
low = mid + 1;
}
else {
high = mid - 1;
}
}
return NULL;
}
const struct macho_nlist* ImageLoaderMachOClassic::binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const
{
++fgTotalBindImageSearches;
++ImageLoaderMachO::fgSymbolTableBinarySearchs;
const struct macho_nlist* base = symbols;
for (uint32_t n = symbolCount; n > 0; n /= 2) {
const struct macho_nlist* pivot = &base[n/2];
const char* pivotStr = &stringPool[pivot->n_un.n_strx];
#if LINKEDIT_USAGE_DEBUG
noteAccessedLinkEditAddress(pivot);
noteAccessedLinkEditAddress(pivotStr);
#endif
int cmp = astrcmp(key, pivotStr);
if ( cmp == 0 )
return pivot;
if ( cmp > 0 ) {
base = &pivot[1];
--n;
}
else {
}
}
return NULL;
}
const ImageLoader::Symbol* ImageLoaderMachOClassic::findExportedSymbol(const char* name, const ImageLoader** foundIn) const
{
const struct macho_nlist* sym = NULL;
if ( fDynamicInfo->tocoff == 0 )
sym = binarySearch(name, fStrings, &fSymbolTable[fDynamicInfo->iextdefsym], fDynamicInfo->nextdefsym);
else
sym = binarySearchWithToc(name, fStrings, fSymbolTable, (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff],
fDynamicInfo->ntoc, fDynamicInfo->nextdefsym);
if ( sym != NULL ) {
if ( foundIn != NULL )
*foundIn = (ImageLoader*)this;
return (const Symbol*)sym;
}
return NULL;
}
bool ImageLoaderMachOClassic::containsSymbol(const void* addr) const
{
return ( (fSymbolTable <= addr) && (addr < fStrings) );
}
uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const
{
const struct macho_nlist* sym = (macho_nlist*)symbol;
uintptr_t result = sym->n_value + fSlide;
#if __arm__
if (sym->n_desc & N_ARM_THUMB_DEF)
result |= 1;
#endif
return result;
}
bool ImageLoaderMachOClassic::exportedSymbolIsWeakDefintion(const Symbol* symbol) const
{
const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol;
return ( (nlistSym->n_desc & N_WEAK_DEF) != 0 );
}
const char* ImageLoaderMachOClassic::exportedSymbolName(const Symbol* symbol) const
{
const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol;
return &fStrings[nlistSym->n_un.n_strx];
}
unsigned int ImageLoaderMachOClassic::exportedSymbolCount() const
{
return fDynamicInfo->nextdefsym;
}
const ImageLoader::Symbol* ImageLoaderMachOClassic::exportedSymbolIndexed(unsigned int index) const
{
if ( index < fDynamicInfo->nextdefsym ) {
const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym + index];
return (const ImageLoader::Symbol*)sym;
}
return NULL;
}
unsigned int ImageLoaderMachOClassic::importedSymbolCount() const
{
return fDynamicInfo->nundefsym;
}
const ImageLoader::Symbol* ImageLoaderMachOClassic::importedSymbolIndexed(unsigned int index) const
{
if ( index < fDynamicInfo->nundefsym ) {
const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iundefsym + index];
return (const ImageLoader::Symbol*)sym;
}
return NULL;
}
const char* ImageLoaderMachOClassic::importedSymbolName(const Symbol* symbol) const
{
const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol;
return &fStrings[nlistSym->n_un.n_strx];
}
bool ImageLoaderMachOClassic::symbolIsWeakDefinition(const struct macho_nlist* symbol)
{
if ( ((symbol->n_type & N_TYPE) == N_SECT) && ((symbol->n_desc & N_WEAK_DEF) != 0) )
return true;
return false;
}
bool ImageLoaderMachOClassic::symbolIsWeakReference(const struct macho_nlist* symbol)
{
if ( ((symbol->n_type & N_TYPE) != N_SECT) && ((symbol->n_desc & N_REF_TO_WEAK) != 0) )
return true;
return false;
}
uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context, bool runResolver) const
{
return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context, runResolver);
}
uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol,
bool twoLevel, bool dontCoalesce, const ImageLoader** foundIn)
{
++fgTotalBindSymbolsResolved;
const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx];
#if LINKEDIT_USAGE_DEBUG
noteAccessedLinkEditAddress(undefinedSymbol);
noteAccessedLinkEditAddress(symbolName);
#endif
if ( context.bindFlat || !twoLevel ) {
if ( ((undefinedSymbol->n_type & N_PEXT) != 0) && ((undefinedSymbol->n_type & N_TYPE) == N_SECT) ) {
uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false);
*foundIn = this;
return addr;
}
const Symbol* sym;
if ( context.flatExportFinder(symbolName, &sym, foundIn) ) {
if ( (*foundIn != this) && !(*foundIn)->neverUnload() )
this->addDynamicReference(*foundIn);
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
}
if ( this->isBundle() && this->hasHiddenExports() ) {
sym = this->findExportedSymbol(symbolName, foundIn);
if ( sym != NULL )
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
}
if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) {
return 0;
}
throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace");
}
else {
if ( !context.prebinding && !dontCoalesce && (symbolIsWeakReference(undefinedSymbol) || symbolIsWeakDefinition(undefinedSymbol)) ) {
const Symbol* sym;
if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) {
if ( (*foundIn != this) && !(*foundIn)->neverUnload() )
this->addDynamicReference(*foundIn);
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
}
}
if ( (undefinedSymbol->n_type & N_TYPE) == N_SECT ) {
uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false);
*foundIn = this;
return addr;
}
ImageLoader* target = NULL;
uint8_t ord = GET_LIBRARY_ORDINAL(undefinedSymbol->n_desc);
if ( ord == EXECUTABLE_ORDINAL ) {
target = context.mainExecutable;
}
else if ( ord == SELF_LIBRARY_ORDINAL ) {
target = this;
}
else if ( ord == DYNAMIC_LOOKUP_ORDINAL ) {
const Symbol* sym;
if ( context.flatExportFinder(symbolName, &sym, foundIn) )
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
context.undefinedHandler(symbolName);
if ( context.flatExportFinder(symbolName, &sym, foundIn) )
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
throwSymbolNotFound(context, symbolName, this->getPath(), "dynamic lookup");
}
else if ( ord <= libraryCount() ) {
target = libImage(ord-1);
if ( target == NULL ) {
return 0;
}
}
else {
dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s",
ord, libraryCount(), symbolName, this->getPath());
}
if ( target == NULL ) {
throw "symbol not found";
}
const Symbol* sym = target->findExportedSymbol(symbolName, true, foundIn);
if ( sym!= NULL ) {
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
}
else if ( (undefinedSymbol->n_type & N_PEXT) != 0 ) {
*foundIn = this;
return this->getSymbolAddress(undefinedSymbol, context, false);
}
else if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) {
return 0;
}
throwSymbolNotFound(context, symbolName, this->getPath(), target->getPath());
}
}
bool ImageLoaderMachOClassic::isAddrInSection(uintptr_t addr, uint8_t sectionIndex)
{
uint8_t currentSectionIndex = 1;
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_SEGMENT_COMMAND ) {
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
if ( (currentSectionIndex <= sectionIndex) && (sectionIndex < currentSectionIndex+seg->nsects) ) {
const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const section = §ionsStart[sectionIndex-currentSectionIndex];
return ( (section->addr <= addr) && (addr < section->addr+section->size) );
}
else {
currentSectionIndex += seg->nsects;
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
return false;
}
void ImageLoaderMachOClassic::doBindExternalRelocations(const LinkContext& context)
{
const uintptr_t relocBase = this->getRelocBase();
const bool twoLevel = this->usesTwoLevelNameSpace();
const bool prebound = this->isPrebindable();
#if TEXT_RELOC_SUPPORT
if ( fTextSegmentBinds )
this->makeTextSegmentWritable(context, true);
#endif
const struct macho_nlist* lastUndefinedSymbol = NULL;
uintptr_t symbolAddr = 0;
const ImageLoader* image = NULL;
const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]);
const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel];
for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
if (reloc->r_length == RELOC_SIZE) {
switch(reloc->r_type) {
case POINTER_RELOC:
{
const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum];
uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase));
uintptr_t value = *location;
bool symbolAddrCached = true;
#if __i386__
if ( reloc->r_pcrel ) {
value += (uintptr_t)location + 4 - fSlide;
}
#endif
if ( prebound ) {
if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) {
if ( (value == undefinedSymbol->n_value) || this->isAddrInSection(value, undefinedSymbol->n_sect) ) {
value -= undefinedSymbol->n_value;
#if __arm__
if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 )
value -= 1;
#endif
}
else
value = 0;
}
#if __arm__
else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) {
value -= (undefinedSymbol->n_value+1);
}
#endif
else {
value -= undefinedSymbol->n_value;
}
}
if ( undefinedSymbol != lastUndefinedSymbol ) {
bool dontCoalesce = true;
if ( symbolIsWeakReference(undefinedSymbol) ) {
dontCoalesce = false;
}
symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, dontCoalesce, &image);
lastUndefinedSymbol = undefinedSymbol;
symbolAddrCached = false;
}
if ( context.verboseBind ) {
const char *path = NULL;
if ( image != NULL ) {
path = image->getShortName();
}
const char* cachedString = "(cached)";
if ( !symbolAddrCached )
cachedString = "";
if ( value == 0 ) {
dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s\n",
this->getShortName(), (uintptr_t)location,
path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString);
}
else {
dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s + %ld\n",
this->getShortName(), (uintptr_t)location,
path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString, value);
}
}
value += symbolAddr;
#if __i386__
if ( reloc->r_pcrel ) {
*location = value - ((uintptr_t)location + 4);
}
else {
if ( !prebound || (*location != value) )
*location = value;
}
#else
if ( !prebound || (*location != value) )
*location = value;
#endif
++fgTotalBindFixups;
}
break;
default:
throw "unknown external relocation type";
}
}
else {
throw "bad external relocation length";
}
}
#if TEXT_RELOC_SUPPORT
if ( fTextSegmentBinds ) {
this->makeTextSegmentWritable(context, true);
}
#endif
}
uintptr_t ImageLoaderMachOClassic::bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, const ImageLoader* targetImage, const LinkContext& context)
{
if ( context.verboseBind ) {
const char* path = NULL;
if ( targetImage != NULL )
path = targetImage->getShortName();
dyld::log("dyld: bind indirect sym: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n",
this->getShortName(), symbolName, (((sect->flags & SECTION_TYPE)==S_NON_LAZY_SYMBOL_POINTERS) ? "non_lazy_ptr" : "lazy_ptr"),
((path != NULL) ? path : "<weak_import-not-found>"), symbolName, (uintptr_t)ptrToBind, targetAddr);
}
if ( context.bindingHandler != NULL ) {
const char* path = NULL;
if ( targetImage != NULL )
path = targetImage->getShortName();
targetAddr = (uintptr_t)context.bindingHandler(path, symbolName, (void *)targetAddr);
}
#if __i386__
if ( ((sect->flags & SECTION_TYPE) == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) {
uint32_t rel32 = targetAddr - (((uint32_t)ptrToBind)+5);
bool done = false;
while ( !done ) {
volatile int64_t* jumpPtr = (int64_t*)ptrToBind;
int pad = 0;
if ( (((uint32_t)ptrToBind + 8) & 0x00000FFC) == 0x00000000 ) {
jumpPtr = (int64_t*)((uint32_t)ptrToBind - 3);
pad = 3;
}
int64_t oldEntry = *jumpPtr;
union {
int64_t int64;
uint8_t bytes[8];
} newEntry;
newEntry.int64 = oldEntry;
newEntry.bytes[pad+0] = 0xE9; newEntry.bytes[pad+1] = rel32 & 0xFF;
newEntry.bytes[pad+2] = (rel32 >> 8) & 0xFF;
newEntry.bytes[pad+3] = (rel32 >> 16) & 0xFF;
newEntry.bytes[pad+4] = (rel32 >> 24) & 0xFF;
done = OSAtomicCompareAndSwap64Barrier(oldEntry, newEntry.int64, (int64_t*)jumpPtr);
}
}
else
#endif
*ptrToBind = targetAddr;
return targetAddr;
}
uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)())
{
throw "compressed LINKEDIT lazy binder called with classic LINKEDIT";
}
uintptr_t ImageLoaderMachOClassic::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context)
{
const bool twoLevel = this->usesTwoLevelNameSpace();
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 uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff];
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd) {
case LC_SEGMENT_COMMAND:
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
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) {
const uint8_t type = sect->flags & SECTION_TYPE;
uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL;
if ( type == S_LAZY_SYMBOL_POINTERS ) {
const uint32_t pointerCount = sect->size / sizeof(uintptr_t);
uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide);
if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) {
const uint32_t indirectTableOffset = sect->reserved1;
const uint32_t lazyIndex = lazyPointer - symbolPointers;
symbolIndex = indirectTable[indirectTableOffset + lazyIndex];
}
}
#if __i386__
else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) {
uint8_t* const jmpTableBase = (uint8_t*)(sect->addr + fSlide);
uint8_t* const jmpTableEnd = jmpTableBase + sect->size;
uint8_t* const jmpTableEntryToPatch = ((uint8_t*)lazyPointer) - 5;
lazyPointer = (uintptr_t*)jmpTableEntryToPatch;
if ( (jmpTableEntryToPatch >= jmpTableBase) && (jmpTableEntryToPatch < jmpTableEnd) ) {
const uint32_t indirectTableOffset = sect->reserved1;
const uint32_t entryIndex = (jmpTableEntryToPatch - jmpTableBase)/5;
symbolIndex = indirectTable[indirectTableOffset + entryIndex];
}
}
#endif
if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) {
const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx];
const ImageLoader* image = NULL;
uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, false, &image);
symbolAddr = this->bindIndirectSymbol(lazyPointer, sect, symbolName, symbolAddr, image, context);
++fgTotalLazyBindFixups;
return symbolAddr;
}
}
}
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath());
}
void ImageLoaderMachOClassic::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder)
{
it.image = this;
it.symbolName = " ";
it.loadOrder = loadOrder;
it.weakSymbol = false;
it.symbolMatches = false;
it.done = false;
it.type = 0;
if ( fDynamicInfo->tocoff != 0 ) {
it.curIndex = 0;
it.endIndex = fDynamicInfo->ntoc;
}
else {
it.curIndex = 0;
it.endIndex = fDynamicInfo->nextdefsym;
}
}
bool ImageLoaderMachOClassic::incrementCoalIterator(CoalIterator& it)
{
if ( it.done )
return false;
if ( fDynamicInfo->tocoff != 0 ) {
if ( it.curIndex >= fDynamicInfo->ntoc ) {
it.done = true;
it.symbolName = "~~~";
return true;
}
else {
const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff];
const uint32_t index = toc[it.curIndex].symbol_index;
const struct macho_nlist* sym = &fSymbolTable[index];
const char* symStr = &fStrings[sym->n_un.n_strx];
it.symbolName = symStr;
it.weakSymbol = (sym->n_desc & N_WEAK_DEF);
it.symbolMatches = false;
it.type = 0; it.curIndex++;
return false;
}
}
else {
if ( it.curIndex >= fDynamicInfo->nextdefsym ) {
it.done = true;
it.symbolName = "~~~";
return true;
}
else {
const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym+it.curIndex];
const char* symStr = &fStrings[sym->n_un.n_strx];
it.symbolName = symStr;
it.weakSymbol = (sym->n_desc & N_WEAK_DEF);
it.symbolMatches = false;
it.type = 0; it.curIndex++;
return false;
}
}
return false;
}
uintptr_t ImageLoaderMachOClassic::getAddressCoalIterator(CoalIterator& it, const LinkContext& context)
{
uint32_t symbol_index = 0;
if ( fDynamicInfo->tocoff != 0 ) {
const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff];
symbol_index = toc[it.curIndex-1].symbol_index;
}
else {
symbol_index = fDynamicInfo->iextdefsym+it.curIndex-1;
}
const struct macho_nlist* sym = &fSymbolTable[symbol_index];
#if __arm__
if (sym->n_desc & N_ARM_THUMB_DEF)
return (sym->n_value | 1) + fSlide ;
else
return sym->n_value + fSlide;
#else
return sym->n_value + fSlide;
#endif
}
void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context)
{
if ( !this->usesTwoLevelNameSpace() )
return;
if ( this->getState() < dyld_image_state_bound )
return;
uint32_t symbol_index = 0;
if ( fDynamicInfo->tocoff != 0 ) {
const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff];
symbol_index = toc[it.curIndex-1].symbol_index;
}
else {
symbol_index = fDynamicInfo->iextdefsym+it.curIndex-1;
}
if ( !symbolIsWeakReference(&fSymbolTable[symbol_index]) && !symbolIsWeakDefinition(&fSymbolTable[symbol_index]) ) {
return;
}
if ( it.type )
return;
bool boundSomething = false;
const uintptr_t relocBase = this->getRelocBase();
const bool prebound = this->isPrebindable();
const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]);
const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel];
for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
if ( reloc->r_symbolnum == symbol_index ) {
const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum];
const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx];
uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase));
const uintptr_t initialValue = *location;
uintptr_t addend = 0;
if ( prebound ) {
if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) {
if ( (initialValue == undefinedSymbol->n_value) || this->isAddrInSection(initialValue, undefinedSymbol->n_sect) ) {
addend = initialValue - undefinedSymbol->n_value;
#if __arm__
if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 )
addend &= -2;
#endif
}
}
#if __arm__
else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) {
addend = initialValue - (undefinedSymbol->n_value+1);
}
#endif
else {
addend = initialValue - undefinedSymbol->n_value;
}
}
else {
if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) {
addend = initialValue - (undefinedSymbol->n_value + fSlide);
#if __arm__
if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 )
addend &= -2;
#endif
}
else {
addend = initialValue;
}
}
uint8_t type = BIND_TYPE_POINTER;
#if __i386__
if ( reloc->r_pcrel )
type = BIND_TYPE_TEXT_PCREL32;
#endif
this->bindLocation(context, (uintptr_t)location, value, targetImage, type, symbolName, addend, "weak ");
boundSomething = true;
}
}
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 uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff];
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;
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) {
uint32_t elementSize = sizeof(uintptr_t);
switch ( sect->flags & SECTION_TYPE ) {
#if __i386__
case S_SYMBOL_STUBS:
if ( ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) ==0) || (sect->reserved2 != 5) )
continue;
elementSize = 5;
#endif
case S_NON_LAZY_SYMBOL_POINTERS:
case S_LAZY_SYMBOL_POINTERS:
{
uint32_t elementCount = sect->size / elementSize;
const uint32_t indirectTableOffset = sect->reserved1;
uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide);
for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) {
if ( indirectTable[indirectTableOffset + j] == symbol_index ) {
this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, it.symbolName, value, targetImage, context);
boundSomething = true;
}
}
}
break;
}
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
if ( boundSomething && (targetImage != this) && !targetImage->neverUnload() )
this->addDynamicReference(targetImage);
it.type = 1;
}
void ImageLoaderMachOClassic::bindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys)
{
const bool twoLevel = this->usesTwoLevelNameSpace();
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 uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff];
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd) {
case LC_SEGMENT_COMMAND:
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
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) {
bool isLazySymbol = false;
const uint8_t type = sect->flags & SECTION_TYPE;
uint32_t elementSize = sizeof(uintptr_t);
uint32_t elementCount = sect->size / elementSize;
if ( type == S_NON_LAZY_SYMBOL_POINTERS ) {
if ( ! bindNonLazys )
continue;
}
else if ( type == S_LAZY_SYMBOL_POINTERS ) {
fgTotalPossibleLazyBindFixups += elementCount;
isLazySymbol = true;
if ( ! bindLazys )
continue;
}
#if __i386__
else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) {
elementCount = sect->size / 5;
elementSize = 5;
fgTotalPossibleLazyBindFixups += elementCount;
isLazySymbol = true;
if ( ! bindLazys )
continue;
}
#endif
else {
continue;
}
const uint32_t indirectTableOffset = sect->reserved1;
uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide);
for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) {
#if LINKEDIT_USAGE_DEBUG
noteAccessedLinkEditAddress(&indirectTable[indirectTableOffset + j]);
#endif
uint32_t symbolIndex = indirectTable[indirectTableOffset + j];
if ( symbolIndex == INDIRECT_SYMBOL_LOCAL) {
*((uintptr_t*)ptrToBind) += this->fSlide;
}
else if ( symbolIndex == INDIRECT_SYMBOL_ABS) {
}
else {
const struct macho_nlist* sym = &fSymbolTable[symbolIndex];
if ( symbolIndex == 0 ) {
if ( ((const macho_header*)fMachOData)->filetype == MH_EXECUTE ) {
static bool alreadyWarned = false;
if ( (sym->n_type & N_TYPE) != N_UNDF ) {
if ( context.verboseWarnings && !alreadyWarned ) {
dyld::log("dyld: malformed executable '%s', skipping indirect symbol to %s\n",
this->getPath(), &fStrings[sym->n_un.n_strx]);
alreadyWarned = true;
}
continue;
}
}
}
const ImageLoader* image = NULL;
bool dontCoalesce = true;
if ( bindLazys && isLazySymbol ) {
dontCoalesce = false;
}
if ( symbolIsWeakReference(sym) ) {
dontCoalesce = false;
}
uintptr_t symbolAddr = resolveUndefined(context, sym, twoLevel, dontCoalesce, &image);
symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image, context);
++fgTotalBindFixups;
}
}
}
}
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
#if __i386__
void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context)
{
if ( ! this->usablePrebinding(context) ) {
const macho_header* mh = (macho_header*)fMachOData;
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 = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd) {
case LC_SEGMENT_COMMAND:
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
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) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) {
const uint32_t indirectTableOffset = sect->reserved1;
const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff];
uint8_t* start = (uint8_t*)(sect->addr + this->fSlide);
uint8_t* end = start + sect->size;
uintptr_t dyldHandler = (uintptr_t)&fast_stub_binding_helper_interface;
uint32_t entryIndex = 0;
for (uint8_t* entry = start; entry < end; entry += 5, ++entryIndex) {
bool installLazyHandler = true;
if ( ((uint32_t)entry & 0xFFFFFFC0) != ((uint32_t)entry+4 & 0xFFFFFFC0) ) {
uint32_t symbolIndex = indirectTable[indirectTableOffset + entryIndex];
if ( symbolIndex != INDIRECT_SYMBOL_ABS ) {
const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx];
const ImageLoader* image = NULL;
try {
uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), false, &image);
symbolAddr = this->bindIndirectSymbol((uintptr_t*)entry, sect, symbolName, symbolAddr, image, context);
++fgTotalBindFixups;
uint32_t rel32 = symbolAddr - (((uint32_t)entry)+5);
entry[0] = 0xE9; entry[1] = rel32 & 0xFF;
entry[2] = (rel32 >> 8) & 0xFF;
entry[3] = (rel32 >> 16) & 0xFF;
entry[4] = (rel32 >> 24) & 0xFF;
installLazyHandler = false;
}
catch (const char* msg) {
}
}
}
if ( installLazyHandler ) {
uint32_t rel32 = dyldHandler - (((uint32_t)entry)+5);
entry[0] = 0xE8; entry[1] = rel32 & 0xFF;
entry[2] = (rel32 >> 8) & 0xFF;
entry[3] = (rel32 >> 16) & 0xFF;
entry[4] = (rel32 >> 24) & 0xFF;
}
}
}
}
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
#endif // __i386__
void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound)
{
CRSetCrashLogMessage2(this->getPath());
#if __i386__
this->initializeLazyStubs(context);
#endif
if ( this->usablePrebinding(context) ) {
}
else {
#if TEXT_RELOC_SUPPORT
if ( fTextSegmentBinds )
this->makeTextSegmentWritable(context, true);
#endif
this->doBindExternalRelocations(context);
this->bindIndirectSymbolPointers(context, true, forceLazysBound || fInSharedCache);
#if TEXT_RELOC_SUPPORT
if ( fTextSegmentBinds )
this->makeTextSegmentWritable(context, false);
#endif
}
this->setupLazyPointerHandler(context);
CRSetCrashLogMessage2(NULL);
}
void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context)
{
this->bindIndirectSymbolPointers(context, false, true);
}
void ImageLoaderMachOClassic::doInterpose(const LinkContext& context)
{
if ( context.verboseInterposing )
dyld::log("dyld: interposing %lu tuples onto: %s\n", fgInterposingTuples.size(), this->getPath());
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) {
switch (cmd->cmd) {
case LC_SEGMENT_COMMAND:
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
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) {
const uint8_t type = sect->flags & SECTION_TYPE;
if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) {
const uint32_t pointerCount = sect->size / sizeof(uintptr_t);
uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide);
for (uint32_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) {
for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
if ( (symbolPointers[pointerIndex] == it->replacee) && (this != it->replacementImage) ) {
if ( context.verboseInterposing ) {
dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n",
&symbolPointers[pointerIndex], it->replacee, it->replacement, this->getPath());
}
symbolPointers[pointerIndex] = it->replacement;
}
}
}
}
#if __i386__
else if ( (type == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) {
uint8_t* start = (uint8_t*)(sect->addr + this->fSlide);
uint8_t* end = start + sect->size;
for (uint8_t* entry = start; entry < end; entry += 5) {
if ( entry[0] == 0xE9 ) { uint32_t rel32 = *((uint32_t*)&entry[1]); uint32_t target = (uint32_t)&entry[5] + rel32;
for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
if ( (it->replacee == target) && (this != it->replacementImage) ) {
if ( context.verboseInterposing ) {
dyld::log("dyld: interposing: at %p replace JMP 0x%lX with JMP 0x%lX in %s\n",
&entry[1], it->replacee, it->replacement, this->getPath());
}
uint32_t newRel32 = it->replacement - (uint32_t)&entry[5];
*((uint32_t*)&entry[1]) = newRel32; }
}
}
}
}
#endif
}
}
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
const uintptr_t relocBase = this->getRelocBase();
const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]);
const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel];
for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
if (reloc->r_length == RELOC_SIZE) {
switch(reloc->r_type) {
case POINTER_RELOC:
{
uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase));
for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
if ( (*location == it->replacee) && (this != it->replacementImage) ) {
if ( context.verboseInterposing ) {
dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n",
location, it->replacee, it->replacement, this->getPath());
}
*location = it->replacement;
}
}
}
break;
}
}
}
}
const char* ImageLoaderMachOClassic::findClosestSymbol(const void* addr, const void** closestAddr) const
{
uintptr_t targetAddress = (uintptr_t)addr - fSlide;
const struct macho_nlist* bestSymbol = NULL;
const struct macho_nlist* const globalsStart = &fSymbolTable[fDynamicInfo->iextdefsym];
const struct macho_nlist* const globalsEnd= &globalsStart[fDynamicInfo->nextdefsym];
for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) {
if ( (s->n_type & N_TYPE) == N_SECT ) {
if ( bestSymbol == NULL ) {
if ( s->n_value <= targetAddress )
bestSymbol = s;
}
else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
bestSymbol = s;
}
}
}
const struct macho_nlist* const localsStart = &fSymbolTable[fDynamicInfo->ilocalsym];
const struct macho_nlist* const localsEnd= &localsStart[fDynamicInfo->nlocalsym];
for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) {
if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
if ( bestSymbol == NULL ) {
if ( s->n_value <= targetAddress )
bestSymbol = s;
}
else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) {
bestSymbol = s;
}
}
}
if ( bestSymbol != NULL ) {
#if __arm__
if (bestSymbol->n_desc & N_ARM_THUMB_DEF)
*closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide);
else
*closestAddr = (void*)(bestSymbol->n_value + fSlide);
#else
*closestAddr = (void*)(bestSymbol->n_value + fSlide);
#endif
return &fStrings[bestSymbol->n_un.n_strx];
}
return NULL;
}