#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include "mach-o/dyld.h"
#include "dlfcn.h"
#if DEBUG > 0
#define DEBUG_PRINT(format) fprintf(stderr,(format));fflush(stderr)
#define DEBUG_PRINT1(format,arg1) fprintf(stderr,(format),(arg1));\
fflush(stderr)
#define DEBUG_PRINT2(format,arg1,arg2) fprintf(stderr,(format),\
(arg1),(arg2));fflush(stderr)
#define DEBUG_PRINT3(format,arg1,arg2,arg3) fprintf(stderr,(format),\
(arg1),(arg2),(arg3));fflush(stderr)
#else
#define DEBUG_PRINT(format)
#define DEBUG_PRINT1(format,arg1)
#define DEBUG_PRINT2(format,arg1,arg2)
#define DEBUG_PRINT3(format,arg1,arg2,arg3)
#undef DEBUG
#endif
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 DYLD_BOOL
NSMakePrivateModulePublic(
NSModule module)
{
static enum DYLD_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));
}
static
int
_dl_search_paths(
const char *filename,
char *pathbuf,
struct stat *stat_buf)
{
const char *pathspec;
const char *element;
const char *p;
char *q;
char *pathbuf_end;
const char *envvars[] = {
"$DYLD_LIBRARY_PATH",
"$LD_LIBRARY_PATH",
"/usr/lib:/lib",
NULL };
int envvar_index;
pathbuf_end = pathbuf + PATH_MAX - 8;
for(envvar_index = 0; envvars[envvar_index]; envvar_index++){
if(envvars[envvar_index][0] == '$'){
pathspec = getenv(envvars[envvar_index]+1);
}
else {
pathspec = envvars[envvar_index];
}
if(pathspec != NULL){
element = pathspec;
while(*element){
p = element;
q = pathbuf;
while(*p && *p != ':' && q < pathbuf_end) *q++ = *p++;
if(q == pathbuf){
if(*p){
element = p+1;
continue;
}
break;
}
if (*p){
element = p+1;
}
else{
element = p;
}
if(*(q-1) != '/' && q < pathbuf_end){
*q++ = '/';
}
p = filename;
while(*p && q < pathbuf_end) *q++ = *p++;
*q++ = 0;
if(q >= pathbuf_end){
break;
}
if(stat(pathbuf, stat_buf) == 0){
return 0;
}
}
}
}
return -1;
}
void *
dlopen(
const char *path,
int mode)
{
const char *module_path;
void *retval;
struct stat stat_buf;
NSObjectFileImage objectFileImage;
NSObjectFileImageReturnCode ofile_result_code;
NSModule module;
struct dlopen_handle *p;
unsigned long options;
NSSymbol NSSymbol;
void (*init)(void);
char pathbuf[PATH_MAX];
DEBUG_PRINT2("libdl: dlopen(%s,0x%x) -> ", path, (unsigned int)mode);
dlerror_pointer = NULL;
if(path == NULL){
retval = (void *)&main_program_handle;
DEBUG_PRINT1("main / %p\n", retval);
return(retval);
}
if(stat(path, &stat_buf) == -1){
dlerror_pointer = strerror(errno);
if(path[0] == '/'){
DEBUG_PRINT1("ERROR (stat): %s\n", dlerror_pointer);
return(NULL);
}
if(_dl_search_paths(path, pathbuf, &stat_buf)){
DEBUG_PRINT1("ERROR (stat): %s\n", dlerror_pointer);
return(NULL);
}
DEBUG_PRINT1("found %s -> ", pathbuf);
module_path = pathbuf;
dlerror_pointer = NULL;
}
else{
module_path = path;
}
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++;
DEBUG_PRINT1("%p\n", p);
return(p);
}
else{
dlerror_pointer = "can't promote handle from "
"RTLD_LOCAL to RTLD_GLOBAL";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
}
}
p->dlopen_count++;
DEBUG_PRINT1("%p\n", p);
return(p);
}
p = p->next;
}
}
if((mode & RTLD_NOLOAD) == RTLD_NOLOAD){
dlerror_pointer = "no existing handle for path RTLD_NOLOAD test";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
}
ofile_result_code = NSCreateObjectFileImageFromFile(module_path,
&objectFileImage);
if(ofile_result_code != NSObjectFileImageSuccess){
switch(ofile_result_code){
case NSObjectFileImageFailure:
dlerror_pointer = "object file setup failure";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
case NSObjectFileImageInappropriateFile:
dlerror_pointer = "not a Mach-O MH_BUNDLE file type";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
case NSObjectFileImageArch:
dlerror_pointer = "no object for this architecture";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
case NSObjectFileImageFormat:
dlerror_pointer = "bad object file format";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
case NSObjectFileImageAccess:
dlerror_pointer = "can't read object file";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
default:
dlerror_pointer = "unknown error from "
"NSCreateObjectFileImageFromFile()";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
}
}
options = NSLINKMODULE_OPTION_PRIVATE;
if((mode & RTLD_NOW) == RTLD_NOW)
options |= NSLINKMODULE_OPTION_BINDNOW;
module = NSLinkModule(objectFileImage, module_path, options);
NSDestroyObjectFileImage(objectFileImage) ;
if(module == NULL){
dlerror_pointer = "NSLinkModule() failed for dlopen()";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
}
if((mode & RTLD_GLOBAL) == RTLD_GLOBAL){
if(NSMakePrivateModulePublic(module) == FALSE){
dlerror_pointer = "can't promote handle from RTLD_LOCAL to "
"RTLD_GLOBAL";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
}
}
p = malloc(sizeof(struct dlopen_handle));
if(p == NULL){
dlerror_pointer = "can't allocate memory for the dlopen handle";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
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();
}
DEBUG_PRINT1("%p\n", p);
return(p);
}
void *
dlsym(
void * handle,
const char *symbol)
{
struct dlopen_handle *dlopen_handle, *p;
NSSymbol NSSymbol;
void *address;
DEBUG_PRINT2("libdl: dlsym(%p,%s) -> ", handle, 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;
DEBUG_PRINT1("%p\n", address);
return(address);
}
else{
dlerror_pointer = "symbol not found";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
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;
DEBUG_PRINT1("%p\n", address);
return(address);
}
else{
dlerror_pointer = "symbol not found";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(NULL);
}
}
p = p->next;
}
dlerror_pointer = "bad handle passed to dlsym()";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
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);
DEBUG_PRINT1("libdl: dlclose(%p) -> ", handle);
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){
DEBUG_PRINT("OK");
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()";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
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);
DEBUG_PRINT("OK");
return(0);
}
p = p->next;
}
dlerror_pointer = "invalid handle passed to dlclose()";
DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer);
return(-1);
}