#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/param.h>
#include <mach/mach_time.h> // mach_absolute_time()
#include <sys/types.h>
#include <sys/stat.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <libkern/OSByteOrder.h>
#include <mach/mach.h>
#include <sys/sysctl.h>
#include <vector>
#include "mach-o/dyld_gdb.h"
#include "dyld.h"
#include "ImageLoader.h"
#include "ImageLoaderMachO.h"
#include "dyldLibSystemThreadHelpers.h"
#define CPU_TYPE_MASK 0x00FFFFFF
void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
void removeImageFromAllImages(const mach_header* mh);
#if OLD_GDB_DYLD_INTERFACE
void addImageForgdb(const mach_header* mh, uintptr_t slide, const char* physicalPath, const char* logicalPath);
void removeImageForgdb(const struct mach_header* mh);
#endif
extern "C" {
char error_string[1024];
}
namespace dyld {
struct EnvironmentVariables {
const char* const * DYLD_FRAMEWORK_PATH;
const char* const * DYLD_FALLBACK_FRAMEWORK_PATH;
const char* const * DYLD_LIBRARY_PATH;
const char* const * DYLD_FALLBACK_LIBRARY_PATH;
const char* const * DYLD_ROOT_PATH;
const char* const * DYLD_INSERT_LIBRARIES;
const char* const * LD_LIBRARY_PATH; bool DYLD_PRINT_LIBRARIES;
bool DYLD_PRINT_LIBRARIES_POST_LAUNCH;
bool DYLD_BIND_AT_LAUNCH;
bool DYLD_PRINT_STATISTICS;
bool DYLD_PRINT_OPTS;
bool DYLD_PRINT_ENV;
};
static const char* sExecPath = NULL;
static const struct mach_header* sMainExecutableMachHeader = NULL;
static cpu_type_t sHostCPU;
static cpu_subtype_t sHostCPUsubtype;
static ImageLoader* sMainExecutable = NULL;
static bool sAllImagesMightContainUnlinkedImages; static std::vector<ImageLoader*> sAllImages;
static std::vector<ImageLoader*> sImageRoots;
static std::vector<ImageLoader*> sImageFilesNeedingTermination;
static std::vector<ImageLoader*> sImagesToNotifyAboutOtherImages;
static std::vector<ImageCallback> sAddImageCallbacks;
static std::vector<ImageCallback> sRemoveImageCallbacks;
static ImageLoader* sLastImageByAddressCache;
static EnvironmentVariables sEnv;
static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL };
static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL };
static BundleNotificationCallBack sBundleNotifier = NULL;
static BundleLocatorCallBack sBundleLocation = NULL;
static UndefinedHandler sUndefinedHandler = NULL;
ImageLoader::LinkContext gLinkContext;
bool gLogAPIs = false;
const struct ThreadingHelpers* gThreadHelpers = NULL;
class FileOpener {
public:
FileOpener(const char* path);
~FileOpener();
int getFileDescriptor() { return fd; }
private:
int fd;
};
FileOpener::FileOpener(const char* path)
{
fd = open(path, O_RDONLY, 0);
}
FileOpener::~FileOpener()
{
close(fd);
}
static uint32_t imageNotification(ImageLoader* image, uint32_t startIndex)
{
const uint32_t callbackCount = sAddImageCallbacks.size();
for (uint32_t i=startIndex; i < callbackCount; ++i) {
ImageCallback cb = sAddImageCallbacks[i];
(cb)(image->machHeader(), image->getSlide());
}
return callbackCount;
}
static void notifyAdding(std::vector<ImageLoader*>& images)
{
unsigned int len = images.size();
if ( len != 0 ) {
dyld_image_info infos[len];
for (unsigned int i=0; i < len; ++i) {
dyld_image_info* p = &infos[i];
ImageLoader* image = images[i];
p->imageLoadAddress = image->machHeader();
p->imageFilePath = image->getPath();
p->imageFileModDate = image->lastModified();
}
addImagesToAllImages(len, infos);
for (std::vector<ImageLoader*>::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) {
(*it)->doNotification(dyld_image_adding, len, infos);
}
}
}
static void addRootImage(ImageLoader* image)
{
sImageRoots.push_back(image);
}
static void addImageNeedingNotification(ImageLoader* image)
{
sImagesToNotifyAboutOtherImages.push_back(image);
}
static void addImage(ImageLoader* image)
{
sAllImages.push_back(image);
if ( sEnv.DYLD_PRINT_LIBRARIES || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) {
uint64_t offset = image->getOffsetInFatFile();
if ( offset == 0 )
fprintf(stderr, "dyld: loaded: %s\n", image->getPath());
else
fprintf(stderr, "dyld: loaded: %s, cpu-sub-type: %d\n", image->getPath(), image->machHeader()->cpusubtype);
}
#if OLD_GDB_DYLD_INTERFACE
addImageForgdb(image->machHeader(), image->getSlide(), image->getPath(), image->getLogicalPath());
#endif
}
void removeImage(ImageLoader* image)
{
for (std::vector<ImageLoader*>::iterator it=sImageFilesNeedingTermination.begin(); it != sImageFilesNeedingTermination.end(); it++) {
if ( *it == image ) {
sImageFilesNeedingTermination.erase(it);
image->doTermination(gLinkContext);
break;
}
}
for (std::vector<ImageCallback>::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) {
(*it)(image->machHeader(), image->getSlide());
}
for (std::vector<ImageLoader*>::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) {
dyld_image_info info;
info.imageLoadAddress = image->machHeader();
info.imageFilePath = image->getPath();
info.imageFileModDate = image->lastModified();
(*it)->doNotification(dyld_image_removing, 1, &info);
}
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
if ( *it == image ) {
sAllImages.erase(it);
break;
}
}
if ( sLastImageByAddressCache == image )
sLastImageByAddressCache = NULL;
for (std::vector<ImageLoader*>::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) {
if ( *it == image ) {
sImagesToNotifyAboutOtherImages.erase(it);
break;
}
}
for (std::vector<ImageLoader*>::iterator it=sImageRoots.begin(); it != sImageRoots.end(); it++) {
if ( *it == image ) {
sImageRoots.erase(it);
break;
}
}
removeImageFromAllImages(image->machHeader());
#if OLD_GDB_DYLD_INTERFACE
removeImageForgdb(image->machHeader());
gdb_dyld_state_changed();
#endif
}
static void terminationRecorder(ImageLoader* image)
{
sImageFilesNeedingTermination.push_back(image);
}
const char* getExecutablePath()
{
return sExecPath;
}
void initializeMainExecutable()
{
const int rootCount = sImageRoots.size();
for(int i=0; i < rootCount; ++i) {
ImageLoader* image = sImageRoots[i];
image->runInitializers(gLinkContext);
}
if ( sEnv.DYLD_PRINT_STATISTICS )
ImageLoaderMachO::printStatistics(sAllImages.size());
}
bool mainExecutablePrebound()
{
return sMainExecutable->usablePrebinding(gLinkContext);
}
ImageLoader* mainExecutable()
{
return sMainExecutable;
}
void runTerminators()
{
const unsigned int imageCount = sImageFilesNeedingTermination.size();
for(unsigned int i=imageCount; i > 0; --i){
ImageLoader* image = sImageFilesNeedingTermination[i-1];
image->doTermination(gLinkContext);
}
sImageFilesNeedingTermination.clear();
}
static const char** parseColonList(const char* list)
{
if ( list[0] == '\0' )
return NULL;
int colonCount = 0;
for(const char* s=list; *s != '\0'; ++s) {
if (*s == ':')
++colonCount;
}
int index = 0;
const char* start = list;
char** result = new char*[colonCount+2];
for(const char* s=list; *s != '\0'; ++s) {
if (*s == ':') {
int len = s-start;
char* str = new char[len+1];
strncpy(str, start, len);
str[len] = '\0';
start = &s[1];
result[index++] = str;
}
}
int len = strlen(start);
char* str = new char[len+1];
strcpy(str, start);
result[index++] = str;
result[index] = NULL;
return (const char**)result;
}
static bool riskyUser()
{
static bool checked = false;
static bool risky = false;
if ( !checked ) {
risky = ( getuid() != 0 && (getuid() != geteuid() || getgid() != getegid()) );
checked = true;
}
return risky;
}
static bool disableIfBadUser(char* rhs)
{
bool didDisable = false;
if ( riskyUser() ) {
*rhs ='\0';
didDisable = true;
}
return didDisable;
}
static void paths_expand_roots(const char **paths, const char *key, const char *val)
{
if(NULL != key) {
size_t keyLen = strlen(key);
for(int i=0; paths[i] != NULL; ++i) {
if ( strncmp(paths[i], key, keyLen) == 0 ) {
char* newPath = new char[strlen(val) + (strlen(paths[i]) - keyLen) + 1];
strcpy(newPath, val);
strcat(newPath, &paths[i][keyLen]);
paths[i] = newPath;
}
}
}
return;
}
static void removePathWithPrefix(const char* paths[], const char* prefix)
{
size_t prefixLen = strlen(prefix);
for(int s=0,d=0; (paths[d] != NULL) && (paths[s] != NULL); ++s, ++d) {
if ( strncmp(paths[s], prefix, prefixLen) == 0 )
++s;
paths[d] = paths[s];
}
}
#if 0
static void paths_dump(const char **paths)
{
const char **strs = paths;
while(*strs != NULL)
{
fprintf(stderr, "\"%s\"\n", *strs);
strs++;
}
return;
}
#endif
static void printOptions(const char* argv[])
{
uint32_t i = 0;
while ( NULL != argv[i] ) {
fprintf(stderr, "opt[%i] = \"%s\"\n", i, argv[i]);
i++;
}
}
static void printEnvironmentVariables(const char* envp[])
{
while ( NULL != *envp ) {
fprintf(stderr, "%s\n", *envp);
envp++;
}
}
void processDyldEnvironmentVarible(const char* key, const char* value)
{
if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) {
if ( !disableIfBadUser((char*)value) )
sEnv.DYLD_FRAMEWORK_PATH = parseColonList(value);
}
else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
if ( !disableIfBadUser((char*)value) )
sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = parseColonList(value);
}
else if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) {
if ( !disableIfBadUser((char*)value) )
sEnv.DYLD_LIBRARY_PATH = parseColonList(value);
}
else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
if ( !disableIfBadUser((char*)value) )
sEnv.DYLD_FALLBACK_LIBRARY_PATH = parseColonList(value);
}
else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) {
if ( !disableIfBadUser((char*)value) ) {
if ( strcmp(value, "/") != 0 ) {
sEnv.DYLD_ROOT_PATH = parseColonList(value);
for (int i=0; sEnv.DYLD_ROOT_PATH[i] != NULL; ++i) {
if ( sEnv.DYLD_ROOT_PATH[i][0] != '/' ) {
fprintf(stderr, "dyld: warning DYLD_ROOT_PATH not used because it contains a non-absolute path\n");
sEnv.DYLD_ROOT_PATH = NULL;
break;
}
}
}
}
}
else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
if ( !disableIfBadUser((char*)value) )
gLinkContext.imageSuffix = value;
}
else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
if ( !disableIfBadUser((char*)value) )
sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value);
}
else if ( strcmp(key, "DYLD_DEBUG_TRACE") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_DEBUG_TRACE not supported\n");
}
else if ( strcmp(key, "DYLD_ERROR_PRINT") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_ERROR_PRINT not supported\n");
}
else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) {
sEnv.DYLD_PRINT_OPTS = true;
}
else if ( strcmp(key, "DYLD_PRINT_ENV") == 0 ) {
sEnv.DYLD_PRINT_ENV = true;
}
else if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) {
sEnv.DYLD_PRINT_LIBRARIES = true;
}
else if ( strcmp(key, "DYLD_PRINT_LIBRARIES_POST_LAUNCH") == 0 ) {
sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH = true;
}
else if ( strcmp(key, "DYLD_TRACE") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_TRACE not supported\n");
}
else if ( strcmp(key, "DYLD_EBADEXEC_ONLY") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_EBADEXEC_ONLY not supported\n");
}
else if ( strcmp(key, "DYLD_BIND_AT_LAUNCH") == 0 ) {
sEnv.DYLD_BIND_AT_LAUNCH = true;
}
else if ( strcmp(key, "DYLD_FORCE_FLAT_NAMESPACE") == 0 ) {
gLinkContext.bindFlat = true;
}
else if ( strcmp(key, "DYLD_DEAD_LOCK_HANG") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_DEAD_LOCK_HANG not supported\n");
}
else if ( strcmp(key, "DYLD_ABORT_MULTIPLE_INITS") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_ABORT_MULTIPLE_INITS not supported\n");
}
else if ( strcmp(key, "DYLD_NEW_LOCAL_SHARED_REGIONS") == 0 ) {
gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
}
else if ( strcmp(key, "DYLD_SLIDE_AND_PACK_DYLIBS") == 0 ) {
gLinkContext.slideAndPackDylibs = true;
}
else if ( strcmp(key, "DYLD_NO_FIX_PREBINDING") == 0 ) {
}
else if ( strcmp(key, "DYLD_PREBIND_DEBUG") == 0 ) {
gLinkContext.verbosePrebinding = true;
}
else if ( strcmp(key, "DYLD_HINTS_DEBUG") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_HINTS_DEBUG not supported\n");
}
else if ( strcmp(key, "DYLD_SAMPLE_DEBUG") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_SAMPLE_DEBUG not supported\n");
}
else if ( strcmp(key, "DYLD_EXECUTABLE_PATH_DEBUG") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_EXECUTABLE_PATH_DEBUG not supported\n");
}
else if ( strcmp(key, "DYLD_TWO_LEVEL_DEBUG") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_TWO_LEVEL_DEBUG not supported\n");
}
else if ( strcmp(key, "DYLD_LAZY_INITIALIZERS") == 0 ) {
fprintf(stderr, "dyld: warning DYLD_LAZY_INITIALIZERS not supported\n");
}
else if ( strcmp(key, "DYLD_PRINT_INITIALIZERS") == 0 ) {
gLinkContext.verboseInit = true;
}
else if ( strcmp(key, "DYLD_PRINT_STATISTICS") == 0 ) {
sEnv.DYLD_PRINT_STATISTICS = true;
}
else if ( strcmp(key, "DYLD_PRINT_SEGMENTS") == 0 ) {
gLinkContext.verboseMapping = true;
}
else if ( strcmp(key, "DYLD_PRINT_BINDINGS") == 0 ) {
gLinkContext.verboseBind = true;
}
else if ( strcmp(key, "DYLD_PRINT_REBASINGS") == 0 ) {
gLinkContext.verboseRebase = true;
}
else if ( strcmp(key, "DYLD_PRINT_APIS") == 0 ) {
gLogAPIs = true;
}
else if ( strcmp(key, "DYLD_PRINT_WARNINGS") == 0 ) {
gLinkContext.verboseWarnings = true;
}
else if ( strcmp(key, "DYLD_SHARED_REGION") == 0 ) {
if ( strcmp(value, "private") == 0 ) {
gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
}
else if ( strcmp(value, "avoid") == 0 ) {
gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
}
else if ( strcmp(value, "use") == 0 ) {
gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion;
}
else if ( value[0] == '\0' ) {
gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion;
}
else {
fprintf(stderr, "dyld: warning unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n");
}
}
else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) {
if ( strcmp(value, "all") == 0 ) {
gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
}
else if ( strcmp(value, "app") == 0 ) {
gLinkContext.prebindUsage = ImageLoader::kUseAllButAppPredbinding;
}
else if ( strcmp(value, "nonsplit") == 0 ) {
gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding;
}
else if ( value[0] == '\0' ) {
gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding;
}
else {
fprintf(stderr, "dyld: warning unknown option to DYLD_IGNORE_PREBINDING. Valid options are: all, app, nonsplit\n");
}
}
else {
fprintf(stderr, "dyld: warning, unknown environment variable: %s\n", key);
}
}
static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron)
{
const char* home = NULL;
const char** p;
for(p = envp; *p != NULL; p++) {
const char* keyEqualsValue = *p;
if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) {
const char* equals = strchr(keyEqualsValue, '=');
if ( (equals != NULL) && !ignoreEnviron ) {
const char* value = &equals[1];
const int keyLen = equals-keyEqualsValue;
char key[keyLen+1];
strncpy(key, keyEqualsValue, keyLen);
key[keyLen] = '\0';
processDyldEnvironmentVarible(key, value);
}
}
else if ( strncmp(keyEqualsValue, "HOME=", 5) == 0 ) {
home = &keyEqualsValue[5];
}
else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) {
const char* path = &keyEqualsValue[16];
if ( !disableIfBadUser((char*)path) )
sEnv.LD_LIBRARY_PATH = parseColonList(path);
}
}
if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) {
const char** paths = sFrameworkFallbackPaths;
if ( home != NULL ) {
if ( riskyUser() )
removePathWithPrefix(paths, "$HOME");
else
paths_expand_roots(paths, "$HOME", home);
}
sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = paths;
}
if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) {
const char** paths = sLibraryFallbackPaths;
if ( home != NULL ) {
if ( riskyUser() )
removePathWithPrefix(paths, "$HOME");
else
paths_expand_roots(paths, "$HOME", home);
}
sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths;
}
}
static void getHostInfo()
{
#if 0
struct host_basic_info info;
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
mach_port_t hostPort = mach_host_self();
kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
mach_port_deallocate(mach_task_self(), hostPort);
if ( result != KERN_SUCCESS )
throw "host_info() failed";
sHostCPU = info.cpu_type;
sHostCPUsubtype = info.cpu_subtype;
#endif
size_t valSize = sizeof(sHostCPU);
if (sysctlbyname ("hw.cputype", &sHostCPU, &valSize, NULL, 0) != 0)
throw "sysctlbyname(hw.cputype) failed";
valSize = sizeof(sHostCPUsubtype);
if (sysctlbyname ("hw.cpusubtype", &sHostCPUsubtype, &valSize, NULL, 0) != 0)
throw "sysctlbyname(hw.cpusubtype) failed";
}
bool validImage(ImageLoader* possibleImage)
{
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i) {
if ( possibleImage == sAllImages[i] ) {
return true;
}
}
return false;
}
uint32_t getImageCount()
{
if ( sAllImagesMightContainUnlinkedImages ) {
uint32_t count = 0;
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
if ( (*it)->isLinked() )
++count;
}
return count;
}
else {
return sAllImages.size();
}
}
ImageLoader* getIndexedImage(unsigned int index)
{
if ( sAllImagesMightContainUnlinkedImages ) {
uint32_t count = 0;
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
if ( (*it)->isLinked() ) {
if ( index == count )
return *it;
++count;
}
}
}
else {
if ( index < sAllImages.size() )
return sAllImages[index];
}
return NULL;
}
ImageLoader* findImageByMachHeader(const struct mach_header* target)
{
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i) {
ImageLoader* anImage = sAllImages[i];
if ( anImage->machHeader() == target )
return anImage;
}
return NULL;
}
ImageLoader* findImageContainingAddress(const void* addr)
{
#if FIND_STATS
static int cacheHit = 0;
static int cacheMiss = 0;
static int cacheNotMacho = 0;
if ( ((cacheHit+cacheMiss+cacheNotMacho) % 100) == 0 )
fprintf(stderr, "findImageContainingAddress(): cache hit = %d, miss = %d, unknown = %d\n", cacheHit, cacheMiss, cacheNotMacho);
#endif
if ( (sLastImageByAddressCache != NULL) && sLastImageByAddressCache->containsAddress(addr) ) {
#if FIND_STATS
++cacheHit;
#endif
return sLastImageByAddressCache;
}
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i) {
ImageLoader* anImage = sAllImages[i];
if ( anImage->containsAddress(addr) ) {
sLastImageByAddressCache = anImage;
#if FIND_STATS
++cacheMiss;
#endif
return anImage;
}
}
#if FIND_STATS
++cacheNotMacho;
#endif
return NULL;
}
ImageLoader* findImageContainingAddressThreadSafe(const void* addr)
{
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i) {
ImageLoader* anImage = sAllImages[i];
if ( anImage->containsAddress(addr) ) {
return anImage;
}
}
return NULL;
}
void forEachImageDo( void (*callback)(ImageLoader*, void* userData), void* userData)
{
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i) {
ImageLoader* anImage = sAllImages[i];
(*callback)(anImage, userData);
}
}
ImageLoader* findLoadedImage(const struct stat& stat_buf)
{
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i){
ImageLoader* anImage = sAllImages[i];
if ( anImage->statMatch(stat_buf) )
return anImage;
}
return NULL;
}
static const char* strrstr(const char* str, const char* sub)
{
const int sublen = strlen(sub);
for(const char* p = &str[strlen(str)]; p != str; --p) {
if ( strncmp(p, sub, sublen) == 0 )
return p;
}
return NULL;
}
static const char* getFrameworkPartialPath(const char* path)
{
const char* dirDot = strrstr(path, ".framework/");
if ( dirDot != NULL ) {
const char* dirStart = dirDot;
for ( ; dirStart >= path; --dirStart) {
if ( (*dirStart == '/') || (dirStart == path) ) {
const char* frameworkStart = &dirStart[1];
if ( dirStart == path )
--frameworkStart;
int len = dirDot - frameworkStart;
char framework[len+1];
strncpy(framework, frameworkStart, len);
framework[len] = '\0';
const char* leaf = strrchr(path, '/');
if ( leaf != NULL ) {
if ( strcmp(framework, &leaf[1]) == 0 ) {
return frameworkStart;
}
if ( gLinkContext.imageSuffix != NULL ) {
if ( strncmp(framework, &leaf[1], len) == 0 ) {
if ( strcmp( gLinkContext.imageSuffix, &leaf[len+1]) == 0 )
return frameworkStart;
}
}
}
}
}
}
return NULL;
}
static const char* getLibraryLeafName(const char* path)
{
const char* start = strrchr(path, '/');
if ( start != NULL )
return &start[1];
else
return path;
}
const cpu_subtype_t CPU_SUBTYPE_END_OF_LIST = -1;
const int kPPC_RowCount = 4;
static const cpu_subtype_t kPPC32[kPPC_RowCount][6] = {
{ CPU_SUBTYPE_POWERPC_970, CPU_SUBTYPE_POWERPC_7450, CPU_SUBTYPE_POWERPC_7400, CPU_SUBTYPE_POWERPC_750, CPU_SUBTYPE_POWERPC_ALL, CPU_SUBTYPE_END_OF_LIST },
{ CPU_SUBTYPE_POWERPC_7450, CPU_SUBTYPE_POWERPC_7400, CPU_SUBTYPE_POWERPC_750, CPU_SUBTYPE_POWERPC_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST },
{ CPU_SUBTYPE_POWERPC_7400, CPU_SUBTYPE_POWERPC_7450, CPU_SUBTYPE_POWERPC_750, CPU_SUBTYPE_POWERPC_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST },
{ CPU_SUBTYPE_POWERPC_750, CPU_SUBTYPE_POWERPC_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }
};
const int kPPC64_RowCount = 1;
static const cpu_subtype_t kPPC64[kPPC64_RowCount][3] = {
{ CPU_SUBTYPE_POWERPC_970, CPU_SUBTYPE_POWERPC_ALL, CPU_SUBTYPE_END_OF_LIST },
};
static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t subtype)
{
switch (cpu) {
case CPU_TYPE_POWERPC:
for (int i=0; i < kPPC_RowCount ; ++i) {
if ( kPPC32[i][0] == subtype )
return kPPC32[i];
}
break;
case CPU_TYPE_POWERPC64:
for (int i=0; i < kPPC64_RowCount ; ++i) {
if ( kPPC64[i][0] == subtype )
return kPPC64[i];
}
break;
case CPU_TYPE_I386:
break;
}
return NULL;
}
static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[], const fat_header* fh, uint64_t* offset, uint64_t* len)
{
const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header));
for (uint32_t subTypeIndex=0; list[subTypeIndex] != CPU_SUBTYPE_END_OF_LIST; ++subTypeIndex) {
for(uint32_t fatIndex=0; fatIndex < OSSwapBigToHostInt32(fh->nfat_arch); ++fatIndex) {
if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[fatIndex].cputype) == cpu)
&& (list[subTypeIndex] == archs[fatIndex].cpusubtype) ) {
*offset = OSSwapBigToHostInt32(archs[fatIndex].offset);
*len = OSSwapBigToHostInt32(archs[fatIndex].size);
return true;
}
}
}
return false;
}
static bool fatFindExactMatch(cpu_type_t cpu, cpu_subtype_t subtype, const fat_header* fh, uint64_t* offset, uint64_t* len)
{
const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header));
for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu)
&& ((cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == subtype) ) {
*offset = OSSwapBigToHostInt32(archs[i].offset);
*len = OSSwapBigToHostInt32(archs[i].size);
return true;
}
}
return false;
}
static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* offset, uint64_t* len)
{
const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header));
for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) {
switch (cpu) {
case CPU_TYPE_POWERPC:
case CPU_TYPE_POWERPC64:
if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_POWERPC_ALL ) {
*offset = OSSwapBigToHostInt32(archs[i].offset);
*len = OSSwapBigToHostInt32(archs[i].size);
return true;
}
break;
case CPU_TYPE_I386:
if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_I386_ALL ) {
*offset = OSSwapBigToHostInt32(archs[i].offset);
*len = OSSwapBigToHostInt32(archs[i].size);
return true;
}
break;
}
}
}
return false;
}
static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len)
{
const cpu_type_t cpu = sMainExecutableMachHeader->cputype;
if ( (cpu & CPU_TYPE_MASK) == sHostCPU ) {
const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(cpu, sHostCPUsubtype);
if ( subTypePreferenceList != NULL )
return fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, offset, len);
if ( fatFindExactMatch(cpu, sHostCPUsubtype, fh, offset, len) )
return true;
}
return fatFindRunsOnAllCPUs(cpu, fh, offset, len);
}
bool isCompatibleMachO(const uint8_t* firstPage)
{
const mach_header* mh = (mach_header*)firstPage;
if ( mh->magic == sMainExecutableMachHeader->magic ) {
if ( mh->cputype == sMainExecutableMachHeader->cputype ) {
if ( (mh->cputype & CPU_TYPE_MASK) == sHostCPU ) {
const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype);
if ( subTypePreferenceList != NULL ) {
for (const cpu_subtype_t* p = subTypePreferenceList; *p != CPU_SUBTYPE_END_OF_LIST; ++p) {
if ( *p == mh->cpusubtype )
return true;
}
throw "incompatible cpu-subtype";
}
if ( mh->cpusubtype == sHostCPUsubtype )
return true;
}
switch (mh->cputype) {
case CPU_TYPE_POWERPC:
case CPU_TYPE_POWERPC64:
if ( mh->cpusubtype == CPU_SUBTYPE_POWERPC_ALL )
return true;
break;
case CPU_TYPE_I386:
if ( mh->cpusubtype == CPU_SUBTYPE_I386_ALL )
return true;
break;
}
}
}
return false;
}
static ImageLoader* instantiateFromLoadedImage(const struct mach_header* mh, const char* path)
{
if ( isCompatibleMachO((const uint8_t*)mh) ) {
ImageLoader* image = new ImageLoaderMachO(path, mh, 0, gLinkContext);
addImage(image);
return image;
}
return NULL;
}
static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, const LoadContext& context)
{
uint64_t fileOffset = 0;
uint64_t fileLength = stat_buf.st_size;
#if __ppc64__
if ( *((uint32_t*)((char*)(&stat_buf)+0x60)) == 0xFEFEFEFE )
fileLength = *((uint64_t*)((char*)(&stat_buf)+0x30)); #endif
if ( (stat_buf.st_mode & S_IFMT) != S_IFREG )
throw "not a file";
if ( fileLength < 4096 ) {
throw "file to short";
}
uint8_t firstPage[4096];
pread(fd, firstPage, 4096,0);
const fat_header* fileStartAsFat = (fat_header*)firstPage;
if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) {
pread(fd, firstPage, 4096, fileOffset);
}
else {
throw "no matching architecture in universal wrapper";
}
}
if ( isCompatibleMachO(firstPage) ) {
char realFilePath[PATH_MAX];
if ( gLinkContext.slideAndPackDylibs ) {
if ( realpath(path, realFilePath) != NULL )
path = realFilePath;
}
ImageLoader* image = new ImageLoaderMachO(path, fd, firstPage, fileOffset, fileLength, stat_buf, gLinkContext);
const char* loadedImageInstallPath = image->getInstallPath();
if ( image->isDylib() && (loadedImageInstallPath != NULL) && (loadedImageInstallPath[0] == '/') ) {
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* anImage = *it;
const char* installPath = anImage->getInstallPath();
if ( installPath != NULL) {
if ( strcmp(loadedImageInstallPath, installPath) == 0 ) {
delete image;
return anImage;
}
}
}
}
if ( context.mustBeBundle && !image->isBundle() )
throw "not a bundle";
if ( context.mustBeDylib && !image->isDylib() )
throw "not a dylib";
if ( ! image->isBundle() )
addImage(image);
return image;
}
switch (*(uint32_t*)firstPage) {
case MH_MAGIC:
case MH_CIGAM:
case MH_MAGIC_64:
case MH_CIGAM_64:
throw "mach-o, but wrong architecture";
default:
throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
firstPage[0], firstPage[1], firstPage[2], firstPage[3], firstPage[4], firstPage[5], firstPage[6],firstPage[7]);
}
}
static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
{
ImageLoader* image = NULL;
FileOpener file(path);
if ( file.getFileDescriptor() == -1 )
return NULL;
struct stat stat_buf;
#if __ppc64__
memset(&stat_buf, 254, sizeof(struct stat)); #endif
if ( fstat(file.getFileDescriptor(), &stat_buf) == -1)
throw "stat error";
image = findLoadedImage(stat_buf);
if ( image != NULL )
return image;
if ( context.dontLoad )
return NULL;
try {
return loadPhase6(file.getFileDescriptor(), stat_buf, path, context);
}
catch (const char* msg) {
char* newMsg = new char[strlen(msg) + strlen(path) + 8];
sprintf(newMsg, "%s: %s", path, msg);
exceptions->push_back(newMsg);
return NULL;
}
}
static ImageLoader* loadPhase5check(const char* path, const LoadContext& context)
{
uint32_t hash = ImageLoader::hash(path);
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* anImage = *it;
if ( anImage->getPathHash() == hash )
if ( strcmp(path, anImage->getPath()) == 0 ) {
if ( !context.mustBeDylib || anImage->isDylib() )
return anImage;
}
if ( context.matchByInstallName || anImage->matchInstallPath() ) {
const char* installPath = anImage->getInstallPath();
if ( installPath != NULL) {
if ( strcmp(path, installPath) == 0 ) {
if ( !context.mustBeDylib || anImage->isDylib() )
return anImage;
}
}
}
}
return NULL;
}
static ImageLoader* loadPhase5(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
{
if ( exceptions != NULL )
return loadPhase5open(path, context, exceptions);
else
return loadPhase5check(path, context);
}
static ImageLoader* loadPhase4(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
{
ImageLoader* image = NULL;
if ( gLinkContext.imageSuffix != NULL ) {
char pathWithSuffix[strlen(path)+strlen( gLinkContext.imageSuffix)+2];
ImageLoader::addSuffix(path, gLinkContext.imageSuffix, pathWithSuffix);
image = loadPhase5(pathWithSuffix, context, exceptions);
}
if ( image == NULL )
image = loadPhase5(path, context, exceptions);
return image;
}
static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
{
ImageLoader* image = NULL;
if ( strncmp(path, "@executable_path/", 17) == 0 ) {
const char* executablePath = sExecPath;
char newPath[strlen(executablePath) + strlen(path)];
strcpy(newPath, executablePath);
char* addPoint = strrchr(newPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &path[17]);
else
strcpy(newPath, &path[17]);
image = loadPhase4(newPath, context, exceptions);
if ( image != NULL )
return image;
char resolvedPath[PATH_MAX];
if ( realpath(sExecPath, resolvedPath) != NULL ) {
char newRealPath[strlen(resolvedPath) + strlen(path)];
strcpy(newRealPath, resolvedPath);
char* addPoint = strrchr(newRealPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &path[17]);
else
strcpy(newRealPath, &path[17]);
image = loadPhase4(newRealPath, context, exceptions);
if ( image != NULL )
return image;
}
}
else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) {
char newPath[strlen(context.origin) + strlen(path)];
strcpy(newPath, context.origin);
char* addPoint = strrchr(newPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &path[13]);
else
strcpy(newPath, &path[13]);
image = loadPhase4(newPath, context, exceptions);
if ( image != NULL )
return image;
char resolvedPath[PATH_MAX];
if ( realpath(context.origin, resolvedPath) != NULL ) {
char newRealPath[strlen(resolvedPath) + strlen(path)];
strcpy(newRealPath, resolvedPath);
char* addPoint = strrchr(newRealPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &path[13]);
else
strcpy(newRealPath, &path[13]);
image = loadPhase4(newRealPath, context, exceptions);
if ( image != NULL )
return image;
}
}
return loadPhase4(path, context, exceptions);
}
static ImageLoader* loadPhase2(const char* path, const LoadContext& context,
const char* const frameworkPaths[], const char* const libraryPaths[],
std::vector<const char*>* exceptions)
{
ImageLoader* image = NULL;
const char* frameworkPartialPath = getFrameworkPartialPath(path);
if ( frameworkPaths != NULL ) {
if ( frameworkPartialPath != NULL ) {
const int frameworkPartialPathLen = strlen(frameworkPartialPath);
for(const char* const* fp = frameworkPaths; *fp != NULL; ++fp) {
char npath[strlen(*fp)+frameworkPartialPathLen+8];
strcpy(npath, *fp);
strcat(npath, "/");
strcat(npath, frameworkPartialPath);
image = loadPhase4(npath, context, exceptions);
if ( image != NULL )
return image;
}
}
}
if ( libraryPaths != NULL ) {
const char* libraryLeafName = getLibraryLeafName(path);
const int libraryLeafNameLen = strlen(libraryLeafName);
for(const char* const* lp = libraryPaths; *lp != NULL; ++lp) {
char libpath[strlen(*lp)+libraryLeafNameLen+8];
strcpy(libpath, *lp);
strcat(libpath, "/");
strcat(libpath, libraryLeafName);
image = loadPhase4(libpath, context, exceptions);
if ( image != NULL )
return image;
}
}
return NULL;
}
static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
{
ImageLoader* image = NULL;
if ( context.useLdLibraryPath && (sEnv.LD_LIBRARY_PATH != NULL) ) {
image = loadPhase2(path, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions);
if ( image != NULL )
return image;
}
if ( context.useSearchPaths && ((sEnv.DYLD_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_LIBRARY_PATH != NULL)) ) {
image = loadPhase2(path, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, exceptions);
if ( image != NULL )
return image;
}
image = loadPhase3(path, context, exceptions);
if ( image != NULL )
return image;
if ( (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_FALLBACK_LIBRARY_PATH != NULL)) ) {
image = loadPhase2(path, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, sEnv.DYLD_FALLBACK_LIBRARY_PATH, exceptions);
if ( image != NULL )
return image;
}
return NULL;
}
static ImageLoader* loadPhase0(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
{
if ( (sEnv.DYLD_ROOT_PATH != NULL) && (path[0] == '/') ) {
for(const char* const* rootPath = sEnv.DYLD_ROOT_PATH ; *rootPath != NULL; ++rootPath) {
char newPath[strlen(*rootPath) + strlen(path)+2];
strcpy(newPath, *rootPath);
strcat(newPath, path);
ImageLoader* image = loadPhase1(newPath, context, exceptions);
if ( image != NULL )
return image;
}
}
return loadPhase1(path, context, exceptions);
}
ImageLoader* load(const char* path, const LoadContext& context)
{
char realPath[PATH_MAX];
if ( context.useSearchPaths && ( gLinkContext.imageSuffix != NULL) ) {
if ( realpath(path, realPath) != NULL )
path = realPath;
}
ImageLoader* image = loadPhase0(path, context, NULL);
if ( image != NULL )
return image;
std::vector<const char*> exceptions;
image = loadPhase0(path, context, &exceptions);
if ( image != NULL )
return image;
else if ( context.dontLoad )
return NULL;
else if ( exceptions.size() == 0 )
throw "image not found";
else {
const char* msgStart = "no suitable image found. Did find:";
const char* delim = "\n\t";
size_t allsizes = strlen(msgStart)+8;
for (unsigned int i=0; i < exceptions.size(); ++i)
allsizes += (strlen(exceptions[i]) + strlen(delim));
char* fullMsg = new char[allsizes];
strcpy(fullMsg, msgStart);
for (unsigned int i=0; i < exceptions.size(); ++i) {
strcat(fullMsg, delim);
strcat(fullMsg, exceptions[i]);
}
throw (const char*)fullMsg;
}
}
ImageLoader* cloneImage(ImageLoader* image)
{
const uint64_t offsetInFat = image->getOffsetInFatFile();
FileOpener file(image->getPath());
struct stat stat_buf;
#if __ppc64__
memset(&stat_buf, 254, sizeof(struct stat)); #endif
if ( fstat(file.getFileDescriptor(), &stat_buf) == -1)
throw "stat error";
uint8_t firstPage[4096];
pread(file.getFileDescriptor(), firstPage, 4096, offsetInFat);
uint64_t lenInFat = stat_buf.st_size - offsetInFat;
if ( isCompatibleMachO(firstPage) ) {
ImageLoader* clone = new ImageLoaderMachO(image->getPath(), file.getFileDescriptor(), firstPage, offsetInFat, lenInFat, stat_buf, gLinkContext);
if ( ! image->isBundle() )
addImage(clone);
return clone;
}
throw "can't clone image";
}
ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName)
{
const fat_header* memStartAsFat = (fat_header*)mem;
uint64_t fileOffset = 0;
uint64_t fileLength = len;
if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) {
mem = &mem[fileOffset];
len = fileLength;
}
else {
throw "no matching architecture in universal wrapper";
}
}
if ( isCompatibleMachO(mem) ) {
ImageLoader* image = new ImageLoaderMachO(moduleName, (mach_header*)mem, len, gLinkContext);
if ( ! image->isBundle() )
addImage(image);
return image;
}
switch (*(uint32_t*)mem) {
case MH_MAGIC:
case MH_CIGAM:
case MH_MAGIC_64:
case MH_CIGAM_64:
throw "mach-o, but wrong architecture";
default:
throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
mem[0], mem[1], mem[2], mem[3], mem[4], mem[5], mem[6],mem[7]);
}
}
void registerAddCallback(ImageCallback func)
{
sAddImageCallbacks.push_back(func);
const int rootCount = sImageRoots.size();
for(int i=0; i < rootCount; ++i) {
ImageLoader* image = sImageRoots[i];
image->runNotification(gLinkContext, sAddImageCallbacks.size());
}
}
void registerRemoveCallback(ImageCallback func)
{
sRemoveImageCallbacks.push_back(func);
}
void clearErrorMessage()
{
error_string[0] = '\0';
}
void setErrorMessage(const char* message)
{
strncpy(error_string, message, sizeof(error_string)-1);
error_string[sizeof(error_string)-1] = '\0';
}
const char* getErrorMessage()
{
return error_string;
}
void halt(const char* message)
{
fprintf(stderr, "dyld: %s\n", message);
setErrorMessage(message);
strncpy(error_string, message, sizeof(error_string)-1);
error_string[sizeof(error_string)-1] = '\0';
#if __ppc__ || __ppc64__
__asm__ ("trap");
#elif __i386__
__asm__ ("int3");
#else
#error unknown architecture
#endif
abort(); }
uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer)
{
uintptr_t result = 0;
#if 0 // rdar://problem/3811777 turn off locking until deadlock is resolved
if ( gThreadHelpers != NULL )
(*gThreadHelpers->lockForReading)();
#endif
try {
ImageLoader* target;
#if __i386__
if ( mh == NULL )
target = dyld::findImageContainingAddressThreadSafe(lazyPointer);
else
target = dyld::findImageByMachHeader(mh);
#else
target = dyld::findImageByMachHeader(mh);
#endif
if ( target == NULL )
throw "image not found for lazy pointer";
result = target->doBindLazySymbol(lazyPointer, gLinkContext);
}
catch (const char* message) {
fprintf(stderr, "dyld: lazy symbol binding failed: %s\n", message);
halt(message);
}
#if 0
if ( gThreadHelpers != NULL )
(*gThreadHelpers->unlockForReading)();
#endif
return result;
}
void registerZeroLinkHandlers(BundleNotificationCallBack notify, BundleLocatorCallBack locate)
{
sBundleNotifier = notify;
sBundleLocation = locate;
}
void registerUndefinedHandler(UndefinedHandler handler)
{
sUndefinedHandler = handler;
}
static void undefinedHandler(const char* symboName)
{
if ( sUndefinedHandler != NULL ) {
(*sUndefinedHandler)(symboName);
}
}
static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, ImageLoader** image)
{
if ( sBundleLocation != NULL ) {
ImageLoader* zlImage = (*sBundleLocation)(name);
if ( zlImage == ((ImageLoader*)(-1)) ) {
undefinedHandler(name);
zlImage = (*sBundleLocation)(name);
}
if ( zlImage == ((ImageLoader*)(-1)) )
return false;
if ( zlImage != NULL ) {
*sym = zlImage->findExportedSymbol(name, NULL, false, image);
if ( *sym != NULL ) {
*image = zlImage;
return true;
}
}
else {
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i){
ImageLoader* anImage = sAllImages[i];
if ( anImage->isBundle() && !anImage->hasHiddenExports() ) {
*sym = anImage->findExportedSymbol(name, NULL, false, image);
if ( *sym != NULL ) {
return true;
}
}
}
}
}
ImageLoader* firstWeakImage = NULL;
const ImageLoader::Symbol* firstWeakSym = NULL;
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i){
ImageLoader* anImage = sAllImages[i];
if ( ! anImage->hasHiddenExports() && (!onlyInCoalesced || anImage->hasCoalescedExports()) ) {
*sym = anImage->findExportedSymbol(name, NULL, false, image);
if ( *sym != NULL ) {
if ( ((*image)->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) {
if ( firstWeakImage == NULL ) {
firstWeakImage = *image;
firstWeakSym = *sym;
}
}
else {
return true;
}
}
}
}
if ( firstWeakSym != NULL ) {
*sym = firstWeakSym;
*image = firstWeakImage;
return true;
}
return false;
}
bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, ImageLoader** image)
{
return findExportedSymbol(name, false, sym, image);
}
bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, ImageLoader** image)
{
return findExportedSymbol(name, true, sym, image);
}
bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, ImageLoader** image)
{
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i){
ImageLoader* anImage = sAllImages[i];
if ( ! anImage->isBundle() && ((librarySubstring==NULL) || (strstr(anImage->getPath(), librarySubstring) != NULL)) ) {
*sym = anImage->findExportedSymbol(name, NULL, false, image);
if ( *sym != NULL ) {
return true;
}
}
}
return false;
}
static void getMappedRegions(ImageLoader::RegionsVector& regions)
{
const unsigned int imageCount = sAllImages.size();
for(unsigned int i=0; i < imageCount; ++i){
ImageLoader* anImage = sAllImages[i];
anImage->addMappedRegions(regions);
}
}
static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const char* rpath[])
{
dyld::LoadContext context;
context.useSearchPaths = search;
context.useLdLibraryPath = false;
context.dontLoad = false;
context.mustBeBundle = false;
context.mustBeDylib = true;
context.matchByInstallName = false;
context.origin = origin;
context.rpath = rpath;
return load(libraryName, context);
}
static void setContext(int argc, const char* argv[], const char* envp[], const char* apple[])
{
gLinkContext.loadLibrary = &libraryLocator;
gLinkContext.imageNotification = &imageNotification;
gLinkContext.terminationRecorder = &terminationRecorder;
gLinkContext.flatExportFinder = &flatFindExportedSymbol;
gLinkContext.coalescedExportFinder = &findCoalescedExportedSymbol;
gLinkContext.undefinedHandler = &undefinedHandler;
gLinkContext.addImageNeedingNotification = &addImageNeedingNotification;
gLinkContext.notifyAdding = ¬ifyAdding;
gLinkContext.getAllMappedRegions = &getMappedRegions;
gLinkContext.bindingHandler = NULL;
gLinkContext.bindingOptions = ImageLoader::kBindingNone;
gLinkContext.mainExecutable = sMainExecutable;
gLinkContext.argc = argc;
gLinkContext.argv = argv;
gLinkContext.envp = envp;
gLinkContext.apple = apple;
}
static bool checkEmulation()
{
#if __i386__
int mib[] = { CTL_KERN, KERN_CLASSIC, getpid() };
int is_classic = 0;
size_t len = sizeof(int);
int ret = sysctl(mib, 3, &is_classic, &len, NULL, 0);
if ((ret != -1) && is_classic) {
gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
return true;
}
#endif
return false;
}
void link(ImageLoader* image, ImageLoader::BindingLaziness bindness, ImageLoader::InitializerRunning runInitializers)
{
if ( image->isBundle() )
addImage(image);
if ( !image->isLinked() )
addRootImage(image);
if ( sBundleNotifier != NULL && image->isBundle() ) {
const int logicalLen = strlen(image->getLogicalPath());
char logAndPhys[strlen(image->getPath())+logicalLen+2];
strcpy(logAndPhys, image->getLogicalPath());
strcpy(&logAndPhys[logicalLen+1], image->getPath());
(*sBundleNotifier)(logAndPhys, image);
}
try {
image->link(gLinkContext, bindness, runInitializers, sAddImageCallbacks.size());
}
catch (const char* msg) {
sAllImagesMightContainUnlinkedImages = true;
throw msg;
}
#if OLD_GDB_DYLD_INTERFACE
gdb_dyld_state_changed();
#endif
}
extern "C" {
extern int __pthread_tsd_first;
}
uintptr_t
_main(const struct mach_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[])
{
__pthread_tsd_first = 1;
bool isEmulated = checkEmulation();
sExecPath = apple[0];
if (isEmulated) {
sExecPath = strdup(apple[0] + strlen(apple[0]) + 1);
}
if ( sExecPath[0] != '/' ) {
char cwdbuff[MAXPATHLEN];
if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
char* s = new char[strlen(cwdbuff) + strlen(sExecPath) + 2];
strcpy(s, cwdbuff);
strcat(s, "/");
strcat(s, sExecPath);
sExecPath = s;
}
}
uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
checkEnvironmentVariables(envp, isEmulated);
if ( sEnv.DYLD_PRINT_OPTS )
printOptions(argv);
if ( sEnv.DYLD_PRINT_ENV )
printEnvironmentVariables(envp);
getHostInfo();
setContext(argc, argv, envp, apple);
ImageLoader::BindingLaziness bindness = sEnv.DYLD_BIND_AT_LAUNCH ? ImageLoader::kLazyAndNonLazy : ImageLoader::kNonLazyOnly;
int insertLibrariesCount = 0;
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) {
insertLibrariesCount++;
}
}
ImageLoader* insertedImages[insertLibrariesCount];
if ( insertLibrariesCount > 0 ) {
for (int i=0; i < insertLibrariesCount; ++i) {
try {
LoadContext context;
context.useSearchPaths = false;
context.useLdLibraryPath = false;
context.dontLoad = false;
context.mustBeBundle = false;
context.mustBeDylib = true;
context.matchByInstallName = false;
context.origin = NULL; context.rpath = NULL;
insertedImages[i] = load(sEnv.DYLD_INSERT_LIBRARIES[i], context);
}
catch (...) {
char buf[strlen(sEnv.DYLD_INSERT_LIBRARIES[i])+50];
sprintf(buf, "could not load inserted library: %s\n", sEnv.DYLD_INSERT_LIBRARIES[i]);
insertedImages[i] = NULL;
halt(buf);
}
}
}
try {
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, sExecPath);
gLinkContext.mainExecutable = sMainExecutable;
if ( sMainExecutable->forceFlat() ) {
gLinkContext.bindFlat = true;
gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
}
link(sMainExecutable, bindness, ImageLoader::kDontRunInitializers);
result = (uintptr_t)sMainExecutable->getMain();
}
catch(const char* message) {
halt(message);
}
catch(...) {
fprintf(stderr, "dyld: launch failed\n");
}
if ( insertLibrariesCount > 0 ) {
for (int i=0; i < insertLibrariesCount; ++i) {
try {
if ( insertedImages[i] != NULL )
link(insertedImages[i], bindness, ImageLoader::kDontRunInitializers);
}
catch (const char* message) {
char buf[strlen(sEnv.DYLD_INSERT_LIBRARIES[i])+50+strlen(message)];
sprintf(buf, "could not link inserted library: %s\n%s\n", sEnv.DYLD_INSERT_LIBRARIES[i], message);
halt(buf);
}
}
}
return result;
}
};