#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <_simple.h>
#include <sys/errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <TargetConditionals.h>
#include <malloc/malloc.h>
#include <mach-o/dyld_priv.h>
#include <mach-o/dyld_images.h>
#include <algorithm>
#include "dlfcn.h"
#include "AllImages.h"
#include "Loading.h"
#include "Logging.h"
#include "Diagnostics.h"
#include "DyldSharedCache.h"
#include "APIs.h"
namespace dyld3 {
void parseDlHandle(void* h, const MachOLoaded** mh, bool* dontContinue);
#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* path, NSObjectFileImage* ofi)
{
log_apis("NSCreateObjectFileImageFromFile(\"%s\", %p)\n", path, ofi);
struct stat statbuf;
if ( ::stat(path, &statbuf) == -1 )
return NSObjectFileImageFailure;
OFIInfo result;
result.path = strdup(path);
result.memSource = nullptr;
result.memLength = 0;
result.loadAddress = nullptr;
result.imageNum = 0;
*ofi = gAllImages.addNSObjectFileImage(result);
log_apis("NSCreateObjectFileImageFromFile() => %p\n", *ofi);
return NSObjectFileImageSuccess;
}
NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memImage, size_t memImageSize, NSObjectFileImage *ofi)
{
log_apis("NSCreateObjectFileImageFromMemory(%p, 0x%0lX, %p)\n", memImage, memImageSize, ofi);
__block Diagnostics diag;
bool usable = false;
const MachOFile* mf = (MachOFile*)memImage;
if ( mf->hasMachOMagic() && mf->isMachO(diag, memImageSize) ) {
usable = (gAllImages.archs().grade(mf->cputype, mf->cpusubtype) != 0);
}
else if ( const FatFile* ff = FatFile::isFatFile(memImage) ) {
uint64_t sliceOffset;
uint64_t sliceLen;
bool missingSlice;
if ( ff->isFatFileWithSlice(diag, memImageSize, gAllImages.archs(), sliceOffset, sliceLen, missingSlice) ) {
mf = (MachOFile*)((long)memImage+sliceOffset);
if ( mf->isMachO(diag, sliceLen) ) {
usable = true;
}
}
}
if ( usable ) {
if ( !mf->supportsPlatform(Platform::macOS) )
usable = false;
}
if ( !usable ) {
log_apis("NSCreateObjectFileImageFromMemory() not mach-o\n");
return NSObjectFileImageFailure;
}
if ( !mf->isBundle() ) {
log_apis("NSCreateObjectFileImageFromMemory() not a bundle\n");
return NSObjectFileImageInappropriateFile;
}
OFIInfo result;
result.path = nullptr;
result.memSource = memImage;
result.memLength = memImageSize;
result.loadAddress = nullptr;
result.imageNum = 0;
*ofi = gAllImages.addNSObjectFileImage(result);
log_apis("NSCreateObjectFileImageFromMemory() => %p\n", *ofi);
return NSObjectFileImageSuccess;
}
NSModule NSLinkModule(NSObjectFileImage ofi, const char* moduleName, uint32_t options)
{
DYLD_LOAD_LOCK_THIS_BLOCK
log_apis("NSLinkModule(%p, \"%s\", 0x%08X)\n", ofi, moduleName, options);
__block const char* path = nullptr;
bool foundImage = gAllImages.forNSObjectFileImage(ofi, ^(OFIInfo &image) {
if ( image.memSource != nullptr ) {
image.path = nullptr;
char tempFileName[PATH_MAX];
const char* tmpDir = getenv("TMPDIR");
if ( (tmpDir != nullptr) && (strlen(tmpDir) > 2) ) {
strlcpy(tempFileName, tmpDir, PATH_MAX);
if ( tmpDir[strlen(tmpDir)-1] != '/' )
strlcat(tempFileName, "/", PATH_MAX);
}
else
strlcpy(tempFileName,"/tmp/", PATH_MAX);
strlcat(tempFileName, "NSCreateObjectFileImageFromMemory-XXXXXXXX", PATH_MAX);
int fd = ::mkstemp(tempFileName);
if ( fd != -1 ) {
ssize_t writtenSize = ::pwrite(fd, image.memSource, image.memLength, 0);
if ( writtenSize == image.memLength ) {
image.path = strdup(tempFileName);
}
else {
log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
}
::close(fd);
}
}
path = image.path;
});
if (!foundImage) {
log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
return nullptr;
}
if (!path)
return nullptr;
Diagnostics diag;
void* callerAddress = __builtin_return_address(1); const MachOLoaded* loadAddress = gAllImages.dlopen(diag, path, false, false, false, false, true, callerAddress);
if ( diag.hasError() ) {
log_apis(" NSLinkModule: failed: %s\n", diag.errorMessage());
return nullptr;
}
gAllImages.forNSObjectFileImage(ofi, ^(OFIInfo &image) {
image.loadAddress = loadAddress;
if ( image.memSource != nullptr ) {
log_apis(" NSLinkModule: delete temp file: %s\n", image.path);
::unlink(image.path);
}
});
log_apis("NSLinkModule() => %p\n", loadAddress);
return (NSModule)loadAddress;
}
bool NSUnLinkModule(NSModule module, uint32_t options)
{
DYLD_LOAD_LOCK_THIS_BLOCK
log_apis("NSUnLinkModule(%p, 0x%08X)\n", module, options);
__block const mach_header* mh = nullptr;
gAllImages.infoForImageMappedAt(module, ^(const LoadedImage& foundImage, uint8_t permissions) {
mh = foundImage.loadedAddress();
});
if ( mh != nullptr )
gAllImages.decRefCount(mh);
log_apis("NSUnLinkModule() => %d\n", mh != nullptr);
return mh != nullptr;
}
bool NSDestroyObjectFileImage(NSObjectFileImage imageHandle)
{
log_apis("NSDestroyObjectFileImage(%p)\n", imageHandle);
__block const void* memSource = nullptr;
__block size_t memLength = 0;
__block const char* path = nullptr;
bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
memSource = image.memSource;
memLength = image.memLength;
path = image.path;
});
if (!foundImage)
return false;
gAllImages.removeNSObjectFileImage(imageHandle);
if ( memSource != nullptr ) {
if ( malloc_size(memSource) != 0 )
free((void*)(memSource));
else
vm_deallocate(mach_task_self(), (vm_address_t)memSource, memLength);
}
free((void*)path);
return true;
}
uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage)
{
halt("NSSymbolDefinitionCountInObjectFileImage() is obsolete");
}
const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal)
{
halt("NSSymbolDefinitionNameInObjectFileImage() is obsolete");
}
uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage)
{
halt("NSSymbolReferenceCountInObjectFileImage() is obsolete");
}
const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition)
{
halt("NSSymbolReferenceNameInObjectFileImage() is obsolete");
}
bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage imageHandle, const char* symbolName)
{
log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", imageHandle, symbolName);
__block bool hasSymbol = false;
bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
void* addr;
bool resultPointsToInstructions = false;
hasSymbol = image.loadAddress->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions);
});
if (!foundImage)
return false;
return hasSymbol;
}
void* NSGetSectionDataInObjectFileImage(NSObjectFileImage imageHandle, const char* segmentName, const char* sectionName, size_t* size)
{
__block const void* result = nullptr;
bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
uint64_t sz;
result = image.loadAddress->findSectionContent(segmentName, sectionName, sz);
*size = (size_t)sz;
});
if (!foundImage)
return nullptr;
return (void*)result;
}
const char* NSNameOfModule(NSModule m)
{
log_apis("NSNameOfModule(%p)\n", m);
__block const char* result = nullptr;
gAllImages.infoForImageMappedAt(m, ^(const LoadedImage& foundImage, uint8_t permissions) {
result = gAllImages.imagePath(foundImage.image());
});
return result;
}
const char* NSLibraryNameForModule(NSModule m)
{
log_apis("NSLibraryNameForModule(%p)\n", m);
__block const char* result = nullptr;
gAllImages.infoForImageMappedAt(m, ^(const LoadedImage& foundImage, uint8_t permissions) {
result = gAllImages.imagePath(foundImage.image());
});
return result;
}
static bool flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress)
{
__block bool result = false;
gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) {
bool resultPointsToInstructions = false;
if ( loadedImage.loadedAddress()->hasExportedSymbol(symbolName, nullptr, symbolAddress, &resultPointsToInstructions) ) {
*foundInImageAtLoadAddress = loadedImage.loadedAddress();
stop = true;
result = true;
}
});
return result;
}
bool NSIsSymbolNameDefined(const char* symbolName)
{
log_apis("NSIsSymbolNameDefined(%s)\n", symbolName);
const mach_header* foundInImageAtLoadAddress;
void* address;
return flatFindSymbol(symbolName, &address, &foundInImageAtLoadAddress);
}
bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint)
{
log_apis("NSIsSymbolNameDefinedWithHint(%s, %s)\n", symbolName, libraryNameHint);
const mach_header* foundInImageAtLoadAddress;
void* address;
return flatFindSymbol(symbolName, &address, &foundInImageAtLoadAddress);
}
bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symbolName)
{
log_apis("NSIsSymbolNameDefinedInImage(%p, %s)\n", mh, symbolName);
void* addr;
bool resultPointsToInstructions = false;
return ((MachOLoaded*)mh)->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions);
}
NSSymbol NSLookupAndBindSymbol(const char* symbolName)
{
log_apis("NSLookupAndBindSymbol(%s)\n", symbolName);
const mach_header* foundInImageAtLoadAddress;
void* symbolAddress;
if ( flatFindSymbol(symbolName, &symbolAddress, &foundInImageAtLoadAddress) ) {
return (NSSymbol)symbolAddress;
}
return nullptr;
}
NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint)
{
log_apis("NSLookupAndBindSymbolWithHint(%s, %s)\n", symbolName, libraryNameHint);
const mach_header* foundInImageAtLoadAddress;
void* symbolAddress;
if ( flatFindSymbol(symbolName, &symbolAddress, &foundInImageAtLoadAddress) ) {
return (NSSymbol)symbolAddress;
}
return nullptr;
}
NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName)
{
log_apis("NSLookupSymbolInModule(%p. %s)\n", module, symbolName);
const MachOLoaded* mh = (const MachOLoaded*)module;
void* addr;
bool resultPointsToInstructions = false;
if ( mh->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions) ) {
return (NSSymbol)addr;
}
return nullptr;
}
NSSymbol NSLookupSymbolInImage(const mach_header* mh, const char* symbolName, uint32_t options)
{
log_apis("NSLookupSymbolInImage(%p, \"%s\", 0x%08X)\n", mh, symbolName, options);
void* addr;
bool resultPointsToInstructions = false;
if ( ((MachOLoaded*)mh)->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions) ) {
log_apis(" NSLookupSymbolInImage() => %p\n", addr);
return (NSSymbol)addr;
}
if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR ) {
log_apis(" NSLookupSymbolInImage() => NULL\n");
return nullptr;
}
return nullptr;
}
const char* NSNameOfSymbol(NSSymbol symbol)
{
halt("NSNameOfSymbol() is obsolete");
}
void* NSAddressOfSymbol(NSSymbol symbol)
{
log_apis("NSAddressOfSymbol(%p)\n", symbol);
return (void*)symbol;
}
NSModule NSModuleForSymbol(NSSymbol symbol)
{
log_apis("NSModuleForSymbol(%p)\n", symbol);
__block NSModule result = nullptr;
gAllImages.infoForImageMappedAt(symbol, ^(const LoadedImage& foundImage, uint8_t permissions) {
result = (NSModule)foundImage.loadedAddress();
});
return result;
}
void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString)
{
log_apis("NSLinkEditError(%p, %p, %p, %p)\n", c, errorNumber, fileName, errorString);
*c = NSLinkEditOtherError;
*errorNumber = 0;
*fileName = NULL;
*errorString = NULL;
}
bool NSAddLibrary(const char* pathName)
{
log_apis("NSAddLibrary(%s)\n", pathName);
void* callerAddress = __builtin_return_address(1); return ( dlopen_internal(pathName, 0, callerAddress) != nullptr);
}
bool NSAddLibraryWithSearching(const char* pathName)
{
log_apis("NSAddLibraryWithSearching(%s)\n", pathName);
void* callerAddress = __builtin_return_address(1); return ( dlopen_internal(pathName, 0, callerAddress) != nullptr);
}
const mach_header* NSAddImage(const char* imageName, uint32_t options)
{
log_apis("NSAddImage(\"%s\", 0x%08X)\n", imageName, options);
uint32_t dloptions = 0;
if ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 )
dloptions |= RTLD_NOLOAD;
void* callerAddress = __builtin_return_address(1); void* h = dlopen_internal(imageName, dloptions, callerAddress);
if ( h != nullptr ) {
const MachOLoaded* mh;
bool dontContinue;
parseDlHandle(h, &mh, &dontContinue);
return mh;
}
if ( (options & (NSADDIMAGE_OPTION_RETURN_ON_ERROR|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED)) == 0 ) {
halt("NSAddImage() image not found");
}
return nullptr;
}
void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers)
{
halt("NSInstallLinkEditErrorHandlers() is obsolete");
}
bool _dyld_present(void)
{
log_apis("_dyld_present()\n");
return true;
}
bool _dyld_launched_prebound(void)
{
halt("_dyld_launched_prebound() is obsolete");
}
bool _dyld_all_twolevel_modules_prebound(void)
{
halt("_dyld_all_twolevel_modules_prebound() is obsolete");
}
bool _dyld_bind_fully_image_containing_address(const void* address)
{
log_apis("_dyld_bind_fully_image_containing_address(%p)\n", address);
return true;
}
bool _dyld_image_containing_address(const void* address)
{
log_apis("_dyld_image_containing_address(%p)\n", address);
return (dyld_image_header_containing_address(address) != nullptr);
}
void _dyld_lookup_and_bind(const char* symbolName, void **address, NSModule* module)
{
log_apis("_dyld_lookup_and_bind(%s, %p, %p)\n", symbolName, address, module);
const mach_header* foundInImageAtLoadAddress;
if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
*module = (NSModule)foundInImageAtLoadAddress;
return;
}
*address = 0;
*module = 0;
}
void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* libraryNameHint, void** address, NSModule* module)
{
log_apis("_dyld_lookup_and_bind_with_hint(%s, %s, %p, %p)\n", symbolName, libraryNameHint, address, module);
const mach_header* foundInImageAtLoadAddress;
if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
*module = (NSModule)foundInImageAtLoadAddress;
return;
}
*address = 0;
*module = 0;
}
void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModule* module)
{
log_apis("_dyld_lookup_and_bind_fully(%s, %p, %p)\n", symbolName, address, module);
const mach_header* foundInImageAtLoadAddress;
if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
*module = (NSModule)foundInImageAtLoadAddress;
return;
}
*address = 0;
*module = 0;
}
const struct mach_header* _dyld_get_image_header_containing_address(const void* address)
{
log_apis("_dyld_get_image_header_containing_address(%p)\n", address);
return dyld_image_header_containing_address(address);
}
#endif
}