#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "stuff/bool.h"
#include "mach-o/dyld.h"
#include "ofi.h"
#include "dlfcn.h"
struct dlopen_handle {
dev_t dev;
ino_t ino;
int dlopen_mode;
int dlopen_count;
NSModule module;
struct dlopen_handle *prev;
struct dlopen_handle *next;
};
static struct dlopen_handle *dlopen_handles = NULL;
static const struct dlopen_handle main_program_handle = {NULL};
static char *dlerror_pointer = NULL;
static
enum bool
NSMakePrivateModulePublic(
NSModule module)
{
static enum bool (*p)(NSModule module) = NULL;
if(p == NULL)
_dyld_func_lookup("__dyld_NSMakePrivateModulePublic",
(unsigned long *)&p);
if(p == NULL){
#ifdef DEBUG
printf("_dyld_func_lookup of __dyld_NSMakePrivateModulePublic "
"failed\n");
#endif
return(FALSE);
}
return(p(module));
}
void *
dlopen(
const char *path,
int mode)
{
void *retval;
struct stat stat_buf;
NSObjectFileImage objectFileImage;
NSObjectFileImageReturnCode ofile_result_code;
NSModule module;
NSLinkEditErrors NSLinkEditError_errorClass;
int NSLinkEditError_errorNumber;
char *NSLinkEditError_fileName, *NSLinkEditError_errorString;
struct dlopen_handle *p;
unsigned long options;
NSSymbol NSSymbol;
void (*init)(void);
dlerror_pointer = NULL;
if(path == NULL){
retval = (void *)&main_program_handle;
return(retval);
}
if(stat(path, &stat_buf) == -1){
dlerror_pointer = strerror(errno);
return(NULL);
}
if((mode & RTLD_UNSHARED) != RTLD_UNSHARED){
p = dlopen_handles;
while(p != NULL){
if(p->dev == stat_buf.st_dev && p->ino == stat_buf.st_ino){
if((p->dlopen_mode & RTLD_UNSHARED) == RTLD_UNSHARED)
continue;
if((p->dlopen_mode & RTLD_LOCAL) == RTLD_LOCAL &&
(mode & RTLD_GLOBAL) == RTLD_GLOBAL){
if(NSMakePrivateModulePublic(p->module) == TRUE){
p->dlopen_mode &= ~RTLD_LOCAL;
p->dlopen_mode |= RTLD_GLOBAL;
p->dlopen_count++;
return(p);
}
else{
dlerror_pointer = "can't promote handle from "
"RTLD_LOCAL to RTLD_GLOBAL";
return(NULL);
}
}
p->dlopen_count++;
return(p);
}
p = p->next;
}
}
if((mode & RTLD_NOLOAD) == RTLD_NOLOAD){
dlerror_pointer = "no existing handle for path RTLD_NOLOAD test";
return(NULL);
}
ofile_result_code = NSCreateObjectFileImageFromFile(path,
&objectFileImage);
if(ofile_result_code != NSObjectFileImageSuccess){
switch(ofile_result_code){
case NSObjectFileImageFailure:
dlerror_pointer = "object file setup failure";
return(NULL);
case NSObjectFileImageInappropriateFile:
dlerror_pointer = "not a Mach-O MH_BUNDLE file type";
return(NULL);
case NSObjectFileImageArch:
dlerror_pointer = "no object for this architecture";
return(NULL);
case NSObjectFileImageFormat:
dlerror_pointer = "bad object file format";
return(NULL);
case NSObjectFileImageAccess:
dlerror_pointer = "can't read object file";
return(NULL);
default:
dlerror_pointer = "unknown error from "
"NSCreateObjectFileImageFromFile()";
return(NULL);
}
}
options = NSLINKMODULE_OPTION_PRIVATE |
NSLINKMODULE_OPTION_RETURN_ON_ERROR;
if((mode & RTLD_NOW) == RTLD_NOW)
options |= NSLINKMODULE_OPTION_BINDNOW;
module = NSLinkModule(objectFileImage, path, options);
NSDestroyObjectFileImage(objectFileImage);
if(module == NULL){
NSLinkEditError(&NSLinkEditError_errorClass,
&NSLinkEditError_errorNumber,
(const char **)&NSLinkEditError_fileName,
(const char **)&NSLinkEditError_errorString);
if(NSLinkEditError_errorClass == NSLinkEditUnixResourceError)
errno = NSLinkEditError_errorNumber;
dlerror_pointer = NSLinkEditError_errorString;
return(NULL);
}
if((mode & RTLD_GLOBAL) == RTLD_GLOBAL){
if(NSMakePrivateModulePublic(module) == FALSE){
dlerror_pointer = "can't promote handle from RTLD_LOCAL to "
"RTLD_GLOBAL";
return(NULL);
}
}
p = malloc(sizeof(struct dlopen_handle));
if(p == NULL){
dlerror_pointer = "can't allocate memory for the dlopen handle";
return(NULL);
}
p->dev = stat_buf.st_dev;
p->ino = stat_buf.st_ino;
if(mode & RTLD_GLOBAL)
p->dlopen_mode = RTLD_GLOBAL;
else
p->dlopen_mode = RTLD_LOCAL;
p->dlopen_mode |= (mode & RTLD_UNSHARED) |
(mode & RTLD_NODELETE) |
(mode & RTLD_LAZY_UNDEF);
p->dlopen_count = 1;
p->module = module;
p->prev = NULL;
p->next = dlopen_handles;
if(dlopen_handles != NULL)
dlopen_handles->prev = p;
dlopen_handles = p;
NSSymbol = NSLookupSymbolInModule(p->module, "__init");
if(NSSymbol != NULL){
init = NSAddressOfSymbol(NSSymbol);
init();
}
return(p);
}
void *
dlsym(
void * handle,
const char *symbol)
{
struct dlopen_handle *dlopen_handle, *p;
NSSymbol NSSymbol;
void *address;
char *_symbol;
_symbol = malloc(strlen(symbol) + 2);
if(_symbol == NULL){
dlerror_pointer = "can't allocate memory for symbol name with "
"leading underbar";
return(NULL);
}
strcpy(_symbol, "_");
strcat(_symbol, symbol);
dlopen_handle = (struct dlopen_handle *)handle;
if(dlopen_handle == (struct dlopen_handle *)&main_program_handle){
if(NSIsSymbolNameDefined(_symbol) == TRUE){
NSSymbol = NSLookupAndBindSymbol(_symbol);
address = NSAddressOfSymbol(NSSymbol);
dlerror_pointer = NULL;
free(_symbol);
return(address);
}
else{
dlerror_pointer = "symbol not found";
free(_symbol);
return(NULL);
}
}
p = dlopen_handles;
while(p != NULL){
if(dlopen_handle == p){
NSSymbol = NSLookupSymbolInModule(p->module, _symbol);
if(NSSymbol != NULL){
address = NSAddressOfSymbol(NSSymbol);
dlerror_pointer = NULL;
free(_symbol);
return(address);
}
else{
dlerror_pointer = "symbol not found";
free(_symbol);
return(NULL);
}
}
p = p->next;
}
dlerror_pointer = "bad handle passed to dlsym()";
free(_symbol);
return(NULL);
}
const char *
dlerror(
void)
{
const char *p;
p = (const char *)dlerror_pointer;
dlerror_pointer = NULL;
return(p);
}
int
dlclose(
void * handle)
{
struct dlopen_handle *p, *q;
unsigned long options;
NSSymbol NSSymbol;
void (*fini)(void);
dlerror_pointer = NULL;
q = (struct dlopen_handle *)handle;
p = dlopen_handles;
while(p != NULL){
if(p == q){
p->dlopen_count--;
if(p->dlopen_count != 0)
return(0);
NSSymbol = NSLookupSymbolInModule(p->module, "__fini");
if(NSSymbol != NULL){
fini = NSAddressOfSymbol(NSSymbol);
fini();
}
options = 0;
if(p->dlopen_mode & RTLD_NODELETE)
options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
if(p->dlopen_mode & RTLD_LAZY_UNDEF)
options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
if(NSUnLinkModule(p->module, options) == FALSE){
dlerror_pointer = "NSUnLinkModule() failed for dlclose()";
return(-1);
}
if(p->prev != NULL)
p->prev->next = p->next;
if(p->next != NULL)
p->next->prev = p->prev;
if(dlopen_handles == p)
dlopen_handles = p->next;
free(p);
return(0);
}
p = p->next;
}
dlerror_pointer = "invalid handle passed to dlclose()";
return(-1);
}