mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-25 22:18:07 +00:00 
			
		
		
		
	git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1674 65c4cc65-6c06-0410-ace0-fbb531ad65f3
		
			
				
	
	
		
			1314 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1314 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
| Copyright (c) 2002 Jorge Acereda  <jacereda@users.sourceforge.net> &
 | |
|                    Peter O'Gorman <ogorman@users.sourceforge.net>
 | |
|                    
 | |
| Portions may be copyright others, see the AUTHORS file included with this
 | |
| distribution.                  
 | |
| 
 | |
| Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
 | |
| 
 | |
| Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
 | |
| 
 | |
| Permission is hereby granted, free of charge, to any person obtaining
 | |
| a copy of this software and associated documentation files (the
 | |
| "Software"), to deal in the Software without restriction, including
 | |
| without limitation the rights to use, copy, modify, merge, publish,
 | |
| distribute, sublicense, and/or sell copies of the Software, and to
 | |
| permit persons to whom the Software is furnished to do so, subject to
 | |
| the following conditions:
 | |
| 
 | |
| The above copyright notice and this permission notice shall be
 | |
| included in all copies or substantial portions of the Software.
 | |
| 
 | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 | |
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 | |
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | |
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | |
| */
 | |
| 
 | |
| #include <pthread.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <stdarg.h>
 | |
| #include <limits.h>
 | |
| #include <mach-o/dyld.h>
 | |
| #include <mach-o/nlist.h>
 | |
| #include <mach-o/getsect.h>
 | |
| /* Just playing to see if it would compile with the freebsd headers, it does,
 | |
|  * but because of the different values for RTLD_LOCAL etc, it would break binary
 | |
|  * compat... oh well
 | |
|  */
 | |
| #ifndef __BSD_VISIBLE
 | |
| #define __BSD_VISIBLE 1
 | |
| #endif
 | |
| #include <asterisk/dlfcn-compat.h>
 | |
| 
 | |
| #ifndef dl_restrict
 | |
| #define dl_restrict __restrict
 | |
| #endif
 | |
| /* This is not available on 10.1 */
 | |
| #ifndef LC_LOAD_WEAK_DYLIB
 | |
| #define	LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)
 | |
| #endif
 | |
| 
 | |
| /* With this stuff here, this thing may actually compile/run on 10.0 systems
 | |
|  * Not that I have a 10.0 system to test it on anylonger
 | |
|  */
 | |
| #ifndef LC_REQ_DYLD
 | |
| #define LC_REQ_DYLD 0x80000000
 | |
| #endif
 | |
| #ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
 | |
| #define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4
 | |
| #endif
 | |
| #ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR
 | |
| #define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1
 | |
| #endif
 | |
| #ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
 | |
| #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0
 | |
| #endif
 | |
| #ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
 | |
| #define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4
 | |
| #endif
 | |
| /* These symbols will be looked for in dyld */
 | |
| static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0;
 | |
| static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0;
 | |
| static NSSymbol(*dyld_NSLookupSymbolInImage)
 | |
| 	(const struct mach_header *, const char *, unsigned long) = 0;
 | |
| 
 | |
| /* Define this to make dlcompat reuse data block. This way in theory we save
 | |
|  * a little bit of overhead. However we then couldn't correctly catch excess
 | |
|  * calls to dlclose(). Hence we don't use this feature
 | |
|  */
 | |
| #undef REUSE_STATUS
 | |
| 
 | |
| /* Size of the internal error message buffer (used by dlerror()) */
 | |
| #define ERR_STR_LEN			251
 | |
| 
 | |
| /* Maximum number of search paths supported by getSearchPath */
 | |
| #define MAX_SEARCH_PATHS	32
 | |
| 
 | |
| 
 | |
| #define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF')
 | |
| #define MAGIC_DYLIB_MOD ((NSModule) 'DYMO')
 | |
| 
 | |
| /* internal flags */
 | |
| #define DL_IN_LIST 0x01
 | |
| 
 | |
| /* our mutex */
 | |
| static pthread_mutex_t dlcompat_mutex;
 | |
| /* Our thread specific storage
 | |
|  */
 | |
| static pthread_key_t dlerror_key;
 | |
| 
 | |
| struct dlthread
 | |
| {
 | |
| 	int lockcnt;
 | |
| 	unsigned char errset;
 | |
| 	char errstr[ERR_STR_LEN];
 | |
| };
 | |
| 
 | |
| /* This is our central data structure. Whenever a module is loaded via
 | |
|  * dlopen(), we create such a struct.
 | |
|  */
 | |
| struct dlstatus
 | |
| {
 | |
| 	struct dlstatus *next;		/* pointer to next element in the linked list */
 | |
| 	NSModule module;
 | |
| 	const struct mach_header *lib;
 | |
| 	int refs;					/* reference count */
 | |
| 	int mode;					/* mode in which this module was loaded */
 | |
| 	dev_t device;
 | |
| 	ino_t inode;
 | |
| 	int flags;					/* Any internal flags we may need */
 | |
| };
 | |
| 
 | |
| /* Head node of the dlstatus list */
 | |
| static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 };
 | |
| static struct dlstatus *stqueue = &mainStatus;
 | |
| 
 | |
| 
 | |
| /* Storage for the last error message (used by dlerror()) */
 | |
| /* static char err_str[ERR_STR_LEN]; */
 | |
| /* static int err_filled = 0; */
 | |
| 
 | |
| /* Prototypes to internal functions */
 | |
| static void debug(const char *fmt, ...);
 | |
| static void error(const char *str, ...);
 | |
| static const char *safegetenv(const char *s);
 | |
| static const char *searchList(void);
 | |
| static const char *getSearchPath(int i);
 | |
| static const char *getFullPath(int i, const char *file);
 | |
| static const struct stat *findFile(const char *file, const char **fullPath);
 | |
| static int isValidStatus(struct dlstatus *status);
 | |
| static inline int isFlagSet(int mode, int flag);
 | |
| static struct dlstatus *lookupStatus(const struct stat *sbuf);
 | |
| static void insertStatus(struct dlstatus *dls, const struct stat *sbuf);
 | |
| static int promoteLocalToGlobal(struct dlstatus *dls);
 | |
| static void *reference(struct dlstatus *dls, int mode);
 | |
| static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError);
 | |
| static struct dlstatus *allocStatus(void);
 | |
| static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode);
 | |
| static NSSymbol *search_linked_libs(const struct mach_header *mh, const char *symbol);
 | |
| static const char *get_lib_name(const struct mach_header *mh);
 | |
| static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod);
 | |
| static void dlcompat_init_func(void);
 | |
| static inline void dolock(void);
 | |
| static inline void dounlock(void);
 | |
| static void dlerrorfree(void *data);
 | |
| static void resetdlerror(void);
 | |
| static const struct mach_header *my_find_image(const char *name);
 | |
| static const struct mach_header *image_for_address(const void *address);
 | |
| static void dlcompat_cleanup(void);
 | |
| static inline const char *dyld_error_str(void);
 | |
| 
 | |
| #if FINK_BUILD
 | |
| /* Two Global Functions */
 | |
| void *dlsym_prepend_underscore(void *handle, const char *symbol);
 | |
| void *dlsym_auto_underscore(void *handle, const char *symbol);
 | |
| 
 | |
| /* And their _intern counterparts */
 | |
| static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol);
 | |
| static void *dlsym_auto_underscore_intern(void *handle, const char *symbol);
 | |
| #endif
 | |
| 
 | |
| /* Functions */
 | |
| 
 | |
| static void debug(const char *fmt, ...)
 | |
| {
 | |
| #if DEBUG > 1
 | |
| 	va_list arg;
 | |
| 	va_start(arg, fmt);
 | |
| 	fprintf(stderr, "DLDEBUG: ");
 | |
| 	vfprintf(stderr, fmt, arg);
 | |
| 	fprintf(stderr, "\n");
 | |
| 	fflush(stderr);
 | |
| 	va_end(arg);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static void error(const char *str, ...)
 | |
| {
 | |
| 	va_list arg;
 | |
| 	struct dlthread  *tss;
 | |
| 	char * err_str;
 | |
| 	va_start(arg, str);
 | |
| 	tss = pthread_getspecific(dlerror_key);
 | |
| 	err_str = tss->errstr;
 | |
| 	strncpy(err_str, "dlcompat: ", ERR_STR_LEN);
 | |
| 	vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg);
 | |
| 	va_end(arg);
 | |
| 	debug("ERROR: %s\n", err_str);
 | |
| 	tss->errset = 1;
 | |
| }
 | |
| 
 | |
| static void warning(const char *str)
 | |
| {
 | |
| #if DEBUG > 0
 | |
| 	fprintf(stderr, "WARNING: dlcompat: %s\n", str);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static const char *safegetenv(const char *s)
 | |
| {
 | |
| 	const char *ss = getenv(s);
 | |
| 	return ss ? ss : "";
 | |
| }
 | |
| 
 | |
| /* because this is only used for debugging and error reporting functions, we
 | |
|  * don't really care about how elegant it is... it could use the load
 | |
|  * commands to find the install name of the library, but...
 | |
|  */
 | |
| static const char *get_lib_name(const struct mach_header *mh)
 | |
| {
 | |
| 	unsigned long count = _dyld_image_count();
 | |
| 	unsigned long i;
 | |
| 	const char *val = NULL;
 | |
| 	if (mh)
 | |
| 	{
 | |
| 		for (i = 0; i < count; i++)
 | |
| 		{
 | |
| 			if (mh == _dyld_get_image_header(i))
 | |
| 			{
 | |
| 				val = _dyld_get_image_name(i);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return val;
 | |
| }
 | |
| 
 | |
| /* Returns the mach_header for the module bu going through all the loaded images
 | |
|  * and finding the one with the same name as the module. There really ought to be
 | |
|  * an api for doing this, would be faster, but there isn't one right now
 | |
|  */
 | |
| static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod)
 | |
| {
 | |
| 	const char *mod_name = NSNameOfModule(mod);
 | |
| 	struct mach_header *mh = NULL;
 | |
| 	unsigned long count = _dyld_image_count();
 | |
| 	unsigned long i;
 | |
| 	debug("Module name: %s", mod_name);
 | |
| 	for (i = 0; i < count; i++)
 | |
| 	{
 | |
| 		if (!strcmp(mod_name, _dyld_get_image_name(i)))
 | |
| 		{
 | |
| 			mh = _dyld_get_image_header(i);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	return mh;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Compute and return a list of all directories that we should search when
 | |
|  * trying to locate a module. We first look at the values of LD_LIBRARY_PATH
 | |
|  * and DYLD_LIBRARY_PATH, and then finally fall back to looking into
 | |
|  * /usr/lib and /lib. Since both of the environments variables can contain a
 | |
|  * list of colon seperated paths, we simply concat them and the two other paths
 | |
|  * into one big string, which we then can easily parse.
 | |
|  * Splitting this string into the actual path list is done by getSearchPath()
 | |
|  */
 | |
| static const char *searchList()
 | |
| {
 | |
| 	size_t buf_size;
 | |
| 	static char *buf=NULL;
 | |
| 	const char *ldlp = safegetenv("LD_LIBRARY_PATH");
 | |
| 	const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH");
 | |
| 	const char *stdpath = getenv("DYLD_FALLBACK_LIBRARY_PATH");
 | |
| 	if (!stdpath)
 | |
| 		stdpath = "/usr/local/lib:/lib:/usr/lib";
 | |
| 	if (!buf)
 | |
| 	{	
 | |
| 		buf_size = strlen(ldlp) + strlen(dyldlp) + strlen(stdpath) + 4;
 | |
| 		buf = malloc(buf_size);
 | |
| 		snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""),
 | |
| 				 stdpath, '\0');
 | |
| 	}
 | |
| 	return buf;
 | |
| }
 | |
| 
 | |
| /* Returns the ith search path from the list as computed by searchList() */
 | |
| static const char *getSearchPath(int i)
 | |
| {
 | |
| 	static const char *list = 0;
 | |
| 	static char **path = (char **)0;
 | |
| 	static int end = 0;
 | |
| 	static int numsize = MAX_SEARCH_PATHS;
 | |
| 	static char **tmp;
 | |
| 	/* So we can call free() in the "destructor" we use i=-1 to return the alloc'd array */
 | |
| 	if (i == -1)
 | |
| 	{
 | |
| 		return (const char*)path;
 | |
| 	}
 | |
| 	if (!path)
 | |
| 	{
 | |
| 		path = (char **)calloc(MAX_SEARCH_PATHS, sizeof(char **));
 | |
| 	}
 | |
| 	if (!list && !end)
 | |
| 		list = searchList();
 | |
| 	if (i >= (numsize))
 | |
| 	{
 | |
| 		debug("Increasing size for long PATH");
 | |
| 		tmp = (char **)calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **));
 | |
| 		if (tmp)
 | |
| 		{
 | |
| 			memcpy(tmp, path, sizeof(char **) * numsize);
 | |
| 			free(path);
 | |
| 			path = tmp;
 | |
| 			numsize += MAX_SEARCH_PATHS;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	while (!path[i] && !end)
 | |
| 	{
 | |
| 		path[i] = strsep((char **)&list, ":");
 | |
| 
 | |
| 		if (path[i][0] == 0)
 | |
| 			path[i] = 0;
 | |
| 		end = (list == 0);
 | |
| 	}
 | |
| 	return path[i];
 | |
| }
 | |
| 
 | |
| static const char *getFullPath(int i, const char *file)
 | |
| {
 | |
| 	static char buf[PATH_MAX];
 | |
| 	const char *path = getSearchPath(i);
 | |
| 	if (path)
 | |
| 	{
 | |
| 		snprintf(buf, PATH_MAX, "%s/%s", path, file);
 | |
| 	}
 | |
| 	return path ? buf : 0;
 | |
| }
 | |
| 
 | |
| /* Given a file name, try to determine the full path for that file. Starts
 | |
|  * its search in the current directory, and then tries all paths in the 
 | |
|  * search list in the order they are specified there.
 | |
|  */
 | |
| static const struct stat *findFile(const char *file, const char **fullPath)
 | |
| {
 | |
| 	int i = 0;
 | |
| 	static struct stat sbuf;
 | |
| 	char *fileName;
 | |
| 	debug("finding file %s", file);
 | |
| 	*fullPath = file;
 | |
| 	if (0 == stat(file, &sbuf))
 | |
| 		return &sbuf;
 | |
| 	if (strchr(file, '/'))
 | |
| 		return 0;				/* If the path had a / we don't look in env var places */
 | |
| 	fileName = NULL;
 | |
| 	if (!fileName)
 | |
| 		fileName = (char *)file;
 | |
| 	while ((*fullPath = getFullPath(i++, fileName)))
 | |
| 	{
 | |
| 		if (0 == stat(*fullPath, &sbuf))
 | |
| 			return &sbuf;
 | |
| 	}
 | |
| 	;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Determine whether a given dlstatus is valid or not */
 | |
| static int isValidStatus(struct dlstatus *status)
 | |
| {
 | |
| 	/* Walk the list to verify status is contained in it */
 | |
| 	struct dlstatus *dls = stqueue;
 | |
| 	while (dls && status != dls)
 | |
| 		dls = dls->next;
 | |
| 	if (dls == 0)
 | |
| 		error("invalid handle");
 | |
| 	else if ((dls->module == 0) || (dls->refs == 0))
 | |
| 		error("handle to closed library");
 | |
| 	else
 | |
| 		return TRUE;
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| static inline int isFlagSet(int mode, int flag)
 | |
| {
 | |
| 	return (mode & flag) == flag;
 | |
| }
 | |
| 
 | |
| static struct dlstatus *lookupStatus(const struct stat *sbuf)
 | |
| {
 | |
| 	struct dlstatus *dls = stqueue;
 | |
| 	debug("looking for status");
 | |
| 	while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0
 | |
| 				   || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode))
 | |
| 		dls = dls->next;
 | |
| 	return dls;
 | |
| }
 | |
| 
 | |
| static void insertStatus(struct dlstatus *dls, const struct stat *sbuf)
 | |
| {
 | |
| 	debug("inserting status");
 | |
| 	dls->inode = sbuf->st_ino;
 | |
| 	dls->device = sbuf->st_dev;
 | |
| 	dls->refs = 0;
 | |
| 	dls->mode = 0;
 | |
| 	if ((dls->flags & DL_IN_LIST) == 0)
 | |
| 	{
 | |
| 		dls->next = stqueue;
 | |
| 		stqueue = dls;
 | |
| 		dls->flags |= DL_IN_LIST;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static struct dlstatus *allocStatus()
 | |
| {
 | |
| 	struct dlstatus *dls;
 | |
| #ifdef REUSE_STATUS
 | |
| 	dls = stqueue;
 | |
| 	while (dls && dls->module)
 | |
| 		dls = dls->next;
 | |
| 	if (!dls)
 | |
| #endif
 | |
| 		dls = malloc(sizeof(*dls));
 | |
| 	dls->flags = 0;
 | |
| 	return dls;
 | |
| }
 | |
| 
 | |
| static int promoteLocalToGlobal(struct dlstatus *dls)
 | |
| {
 | |
| 	static int (*p) (NSModule module) = 0;
 | |
| 	debug("promoting");
 | |
| 	if (!p)
 | |
| 		_dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p);
 | |
| 	return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module));
 | |
| }
 | |
| 
 | |
| static void *reference(struct dlstatus *dls, int mode)
 | |
| {
 | |
| 	if (dls)
 | |
| 	{
 | |
| 		if (dls->module == MAGIC_DYLIB_MOD && !isFlagSet(mode, RTLD_GLOBAL))
 | |
| 		{
 | |
| 			warning("trying to open a .dylib with RTLD_LOCAL");
 | |
| 			error("unable to open a .dylib with RTLD_LOCAL");
 | |
| 			return NULL;
 | |
| 		}
 | |
| 		if (isFlagSet(mode, RTLD_GLOBAL) &&
 | |
| 			!isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls))
 | |
| 		{
 | |
| 			error("unable to promote local module to global");
 | |
| 			return NULL;
 | |
| 		}
 | |
| 		dls->mode |= mode;
 | |
| 		dls->refs++;
 | |
| 	}
 | |
| 	else
 | |
| 		debug("reference called with NULL argument");
 | |
| 
 | |
| 	return dls;
 | |
| }
 | |
| 
 | |
| static const struct mach_header *my_find_image(const char *name)
 | |
| {
 | |
| 	const struct mach_header *mh = 0;
 | |
| 	const char *id = NULL;
 | |
| 	int i = _dyld_image_count();
 | |
| 	int j;
 | |
| 	mh = (struct mach_header *)
 | |
| 		dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED |
 | |
| 						NSADDIMAGE_OPTION_RETURN_ON_ERROR);
 | |
| 	if (!mh)
 | |
| 	{
 | |
| 		for (j = 0; j < i; j++)
 | |
| 		{
 | |
| 			id = _dyld_get_image_name(j);
 | |
| 			if (!strcmp(id, name))
 | |
| 			{
 | |
| 				mh = _dyld_get_image_header(j);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return mh;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * dyld adds libraries by first adding the directly dependant libraries in link order, and
 | |
|  * then adding the dependencies for those libraries, so we should do the same... but we don't
 | |
|  * bother adding the extra dependencies, if the symbols are neither in the loaded image nor
 | |
|  * any of it's direct dependencies, then it probably isn't there.
 | |
|  */
 | |
| NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol)
 | |
| {
 | |
| 	int n;
 | |
| 	struct load_command *lc = 0;
 | |
| 	struct mach_header *wh;
 | |
| 	NSSymbol *nssym = 0;
 | |
| 	if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
 | |
| 	{
 | |
| 		lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
 | |
| 		for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
 | |
| 		{
 | |
| 			if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd))
 | |
| 			{
 | |
| 				if ((wh = (struct mach_header *)
 | |
| 					 my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset +
 | |
| 											(char *)lc))))
 | |
| 				{
 | |
| 					if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol))
 | |
| 					{
 | |
| 						nssym = dyld_NSLookupSymbolInImage(wh,
 | |
| 														   symbol,
 | |
| 														   NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
 | |
| 														   NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
 | |
| 						break;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if ((!nssym) && NSIsSymbolNameDefined(symbol))
 | |
| 		{
 | |
| 			/* I've never seen this debug message...*/
 | |
| 			debug("Symbol \"%s\" is defined but was not found", symbol);
 | |
| 		}
 | |
| 	}
 | |
| 	return nssym;
 | |
| }
 | |
| 
 | |
| /* Up to the caller to free() returned string */
 | |
| static inline const char *dyld_error_str()
 | |
| {
 | |
| 	NSLinkEditErrors dylder;
 | |
| 	int dylderno;
 | |
| 	const char *dylderrstr;
 | |
| 	const char *dyldfile;
 | |
| 	const char* retStr = NULL;
 | |
| 	NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr);
 | |
| 	if (dylderrstr && strlen(dylderrstr))
 | |
| 	{
 | |
| 		retStr = malloc(strlen(dylderrstr) +1);
 | |
| 		strcpy((char*)retStr,dylderrstr);
 | |
| 	}
 | |
| 	return retStr;
 | |
| }
 | |
| 
 | |
| static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError)
 | |
| {
 | |
| 	NSSymbol *nssym = 0;
 | |
| 	void *caller = __builtin_return_address(1);	/* Be *very* careful about inlining */
 | |
| 	const struct mach_header *caller_mh = 0;
 | |
| 	const char* savedErrorStr = NULL;
 | |
| 	resetdlerror();
 | |
| #ifndef RTLD_SELF
 | |
| #define	RTLD_SELF		((void *) -3)
 | |
| #endif
 | |
| 	if (NULL == dls)
 | |
| 		dls = RTLD_SELF;
 | |
| 	if ((RTLD_NEXT == dls) || (RTLD_SELF == dls))
 | |
| 	{
 | |
| 		if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
 | |
| 		{
 | |
| 			caller_mh = image_for_address(caller);
 | |
| 			if (RTLD_SELF == dls)
 | |
| 			{
 | |
| 				/* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE
 | |
| 				 * But it appears to work anyway, and looking at the code in dyld_libfuncs.c
 | |
| 				 * this is acceptable.
 | |
| 				 */
 | |
| 				if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol))
 | |
| 				{
 | |
| 					nssym = dyld_NSLookupSymbolInImage(caller_mh,
 | |
| 													   symbol,
 | |
| 													   NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
 | |
| 													   NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
 | |
| 				}
 | |
| 			}
 | |
| 			if (!nssym)
 | |
| 			{
 | |
| 				if (RTLD_SELF == dls)
 | |
| 					savedErrorStr = dyld_error_str();
 | |
| 				nssym = search_linked_libs(caller_mh, symbol);
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			if (canSetError)
 | |
| 				error("RTLD_SELF and RTLD_NEXT are not supported");
 | |
| 			return NULL;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!nssym)
 | |
| 	{
 | |
| 
 | |
| 		if (RTLD_DEFAULT == dls)
 | |
| 		{
 | |
| 			dls = &mainStatus;
 | |
| 		}
 | |
| 		if (!isValidStatus(dls))
 | |
| 			return NULL;
 | |
| 
 | |
| 		if (dls->module != MAGIC_DYLIB_MOD)
 | |
| 		{
 | |
| 			nssym = NSLookupSymbolInModule(dls->module, symbol);
 | |
| 			if (!nssym && NSIsSymbolNameDefined(symbol))
 | |
| 			{
 | |
| 				debug("Searching dependencies");
 | |
| 				savedErrorStr = dyld_error_str();
 | |
| 				nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol);
 | |
| 			}
 | |
| 		}
 | |
| 		else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
 | |
| 		{
 | |
| 			if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol))
 | |
| 			{
 | |
| 				nssym = dyld_NSLookupSymbolInImage(dls->lib,
 | |
| 												   symbol,
 | |
| 												   NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
 | |
| 												   NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
 | |
| 			}
 | |
| 			else if (NSIsSymbolNameDefined(symbol))
 | |
| 			{
 | |
| 				debug("Searching dependencies");
 | |
| 				savedErrorStr = dyld_error_str();
 | |
| 				nssym = search_linked_libs(dls->lib, symbol);
 | |
| 			}
 | |
| 		}
 | |
| 		else if (dls->module == MAGIC_DYLIB_MOD)
 | |
| 		{
 | |
| 			/* Global context, use NSLookupAndBindSymbol */
 | |
| 			if (NSIsSymbolNameDefined(symbol))
 | |
| 			{
 | |
| 				/* There doesn't seem to be a return on error option for this call???
 | |
| 				   this is potentially broken, if binding fails, it will improperly
 | |
| 				   exit the application. */
 | |
| 				nssym = NSLookupAndBindSymbol(symbol);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				if (savedErrorStr)
 | |
| 					free((char*)savedErrorStr);			
 | |
| 				savedErrorStr = malloc(256);
 | |
| 				snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol);	
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	/* Error reporting */
 | |
| 	if (!nssym)
 | |
| 	{
 | |
| 		if (!savedErrorStr || !strlen(savedErrorStr))
 | |
| 		{
 | |
| 			if (savedErrorStr)
 | |
| 				free((char*)savedErrorStr);
 | |
| 			savedErrorStr = malloc(256);
 | |
| 			snprintf((char*)savedErrorStr, 256,"Symbol \"%s\" not found",symbol);
 | |
| 		}
 | |
| 		if (canSetError)
 | |
| 		{
 | |
| 			error(savedErrorStr);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			debug(savedErrorStr);
 | |
| 		}
 | |
| 		if (savedErrorStr)
 | |
| 			free((char*)savedErrorStr);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	return NSAddressOfSymbol(nssym);
 | |
| }
 | |
| 
 | |
| static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode)
 | |
| {
 | |
| 	NSObjectFileImage ofi = 0;
 | |
| 	NSObjectFileImageReturnCode ofirc;
 | |
| 	struct dlstatus *dls;
 | |
| 	NSLinkEditErrors ler;
 | |
| 	int lerno;
 | |
| 	const char *errstr;
 | |
| 	const char *file;
 | |
| 	void (*init) (void);
 | |
| 	ofirc = NSCreateObjectFileImageFromFile(path, &ofi);
 | |
| 	switch (ofirc)
 | |
| 	{
 | |
| 		case NSObjectFileImageSuccess:
 | |
| 			break;
 | |
| 		case NSObjectFileImageInappropriateFile:
 | |
| 			if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
 | |
| 			{
 | |
| 				if (!isFlagSet(mode, RTLD_GLOBAL))
 | |
| 				{
 | |
| 					warning("trying to open a .dylib with RTLD_LOCAL");
 | |
| 					error("unable to open this file with RTLD_LOCAL");
 | |
| 					return NULL;
 | |
| 				}
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				error("opening this file is unsupported on this system");
 | |
| 				return NULL;
 | |
| 			}
 | |
| 			break;
 | |
| 		case NSObjectFileImageFailure:
 | |
| 			error("object file setup failure");
 | |
| 			return NULL;
 | |
| 		case NSObjectFileImageArch:
 | |
| 			error("no object for this architecture");
 | |
| 			return NULL;
 | |
| 		case NSObjectFileImageFormat:
 | |
| 			error("bad object file format");
 | |
| 			return NULL;
 | |
| 		case NSObjectFileImageAccess:
 | |
| 			error("can't read object file");
 | |
| 			return NULL;
 | |
| 		default:
 | |
| 			error("unknown error from NSCreateObjectFileImageFromFile()");
 | |
| 			return NULL;
 | |
| 	}
 | |
| 	dls = lookupStatus(sbuf);
 | |
| 	if (!dls)
 | |
| 	{
 | |
| 		dls = allocStatus();
 | |
| 	}
 | |
| 	if (!dls)
 | |
| 	{
 | |
| 		error("unable to allocate memory");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	dls->lib = 0;
 | |
| 	if (ofirc == NSObjectFileImageInappropriateFile)
 | |
| 	{
 | |
| 		if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR)))
 | |
| 		{
 | |
| 			debug("Dynamic lib loaded at %ld", dls->lib);
 | |
| 			ofi = MAGIC_DYLIB_OFI;
 | |
| 			dls->module = MAGIC_DYLIB_MOD;
 | |
| 			ofirc = NSObjectFileImageSuccess;
 | |
| 			/* Although it is possible with a bit of work to modify this so it works and
 | |
| 			   functions with RTLD_NOW, I don't deem it necessary at the moment */
 | |
| 		}
 | |
| 		if (!(dls->module))
 | |
| 		{
 | |
| 			NSLinkEditError(&ler, &lerno, &file, &errstr);
 | |
| 			if (!errstr || (!strlen(errstr)))
 | |
| 				error("Can't open this file type");
 | |
| 			else
 | |
| 				error(errstr);
 | |
| 			if ((dls->flags & DL_IN_LIST) == 0)
 | |
| 			{
 | |
| 				free(dls);
 | |
| 			}
 | |
| 			return NULL;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		dls->module = NSLinkModule(ofi, path,
 | |
| 								   NSLINKMODULE_OPTION_RETURN_ON_ERROR |
 | |
| 								   NSLINKMODULE_OPTION_PRIVATE |
 | |
| 								   (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0));
 | |
| 		NSDestroyObjectFileImage(ofi);
 | |
| 		if (dls->module)
 | |
| 		{
 | |
| 			dls->lib = get_mach_header_from_NSModule(dls->module);
 | |
| 		}
 | |
| 	}
 | |
| 	if (!dls->module)
 | |
| 	{
 | |
| 		NSLinkEditError(&ler, &lerno, &file, &errstr);
 | |
| 		if ((dls->flags & DL_IN_LIST) == 0)
 | |
| 		{
 | |
| 			free(dls);
 | |
| 		}
 | |
| 		error(errstr);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	insertStatus(dls, sbuf);
 | |
| 	dls = reference(dls, mode);
 | |
| 	if ((init = dlsymIntern(dls, "__init", 0)))
 | |
| 	{
 | |
| 		debug("calling _init()");
 | |
| 		init();
 | |
| 	}
 | |
| 	return dls;
 | |
| }
 | |
| 
 | |
| static void dlcompat_init_func(void)
 | |
| {
 | |
| 	static int inited = 0;
 | |
| 	if (!inited)
 | |
| 	{
 | |
| 		inited = 1;
 | |
| 		_dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage);
 | |
| 		_dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage",
 | |
| 						  (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage);
 | |
| 		_dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage);
 | |
| 		if (pthread_mutex_init(&dlcompat_mutex, NULL))
 | |
| 			exit(1);
 | |
| 		if (pthread_key_create(&dlerror_key, &dlerrorfree))
 | |
| 			exit(1);
 | |
| 		/* And be neat and tidy and clean up after ourselves */	
 | |
| 		atexit(dlcompat_cleanup);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| #pragma CALL_ON_LOAD dlcompat_init_func
 | |
| #endif
 | |
| 
 | |
| static void dlcompat_cleanup(void)
 | |
| {
 | |
| 	struct dlstatus *dls;
 | |
| 	struct dlstatus *next;
 | |
| 	char *data;
 | |
| 	data = (char *)searchList();
 | |
| 	if ( data )
 | |
| 		free( data );
 | |
| 	data = 	(char *)getSearchPath(-1);
 | |
| 	if ( data )
 | |
| 		free( data );
 | |
| 	pthread_mutex_destroy(&dlcompat_mutex);
 | |
| 	pthread_key_delete(dlerror_key);
 | |
| 	next = stqueue;
 | |
| 	while (next && (next != &mainStatus))
 | |
| 	{
 | |
| 		dls = next;
 | |
| 		next = dls->next;
 | |
| 		free(dls);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void resetdlerror()
 | |
| {
 | |
| 	struct dlthread *tss;
 | |
| 	tss = pthread_getspecific(dlerror_key);
 | |
| 	tss->errset = 0;
 | |
| }
 | |
| 
 | |
| static void dlerrorfree(void *data)
 | |
| {
 | |
| 	free(data);
 | |
| }
 | |
| 
 | |
| /* We kind of want a recursive lock here, but meet a little trouble
 | |
|  * because they are not available pre OS X 10.2, so we fake it
 | |
|  * using thread specific storage to keep a lock count
 | |
|  */ 
 | |
| static inline void dolock(void)
 | |
| {
 | |
| 	int err = 0;
 | |
| 	struct dlthread *tss;
 | |
| 	tss = pthread_getspecific(dlerror_key);
 | |
| 	if (!tss)
 | |
| 	{
 | |
| 		tss = malloc(sizeof(struct dlthread));
 | |
| 		tss->lockcnt = 0;
 | |
| 		tss->errset = 0;
 | |
| 		if (pthread_setspecific(dlerror_key, tss))
 | |
| 		{
 | |
| 			fprintf(stderr,"dlcompat: pthread_setspecific failed\n");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	if (!tss->lockcnt)
 | |
| 		err = pthread_mutex_lock(&dlcompat_mutex);
 | |
| 	tss->lockcnt = tss->lockcnt +1;	
 | |
| 	if (err)
 | |
| 		exit(err);
 | |
| }
 | |
| 
 | |
| static inline void dounlock(void)
 | |
| {
 | |
| 	int err = 0;
 | |
| 	struct dlthread *tss;
 | |
| 	tss = pthread_getspecific(dlerror_key);
 | |
| 	tss->lockcnt = tss->lockcnt -1;
 | |
| 	if (!tss->lockcnt)
 | |
| 		err = pthread_mutex_unlock(&dlcompat_mutex);
 | |
| 	if (err)
 | |
| 		exit(err);
 | |
| }
 | |
| 
 | |
| void *dlopen(const char *path, int mode)
 | |
| {
 | |
| 	const struct stat *sbuf;
 | |
| 	struct dlstatus *dls;
 | |
| 	const char *fullPath;
 | |
| 	dlcompat_init_func();		/* Just in case */
 | |
| 	dolock();
 | |
| 	resetdlerror();
 | |
| 	if (!path)
 | |
| 	{
 | |
| 		dls = &mainStatus;
 | |
| 		goto dlopenok;
 | |
| 	}
 | |
| 	if (!(sbuf = findFile(path, &fullPath)))
 | |
| 	{
 | |
| 		error("file \"%s\" not found", path);
 | |
| 		goto dlopenerror;
 | |
| 	}
 | |
| 	/* Now checks that it hasn't been closed already */
 | |
| 	if ((dls = lookupStatus(sbuf)) && (dls->refs > 0))
 | |
| 	{
 | |
| 		/* debug("status found"); */
 | |
| 		dls = reference(dls, mode);
 | |
| 		goto dlopenok;
 | |
| 	}
 | |
| #ifdef 	RTLD_NOLOAD
 | |
| 	if (isFlagSet(mode, RTLD_NOLOAD))
 | |
| 	{
 | |
| 		error("no existing handle and RTLD_NOLOAD specified");
 | |
| 		goto dlopenerror;
 | |
| 	}
 | |
| #endif
 | |
| 	if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW))
 | |
| 	{
 | |
| 		error("how can I load something both RTLD_LAZY and RTLD_NOW?");
 | |
| 		goto dlopenerror;
 | |
| 	}
 | |
| 	dls = loadModule(fullPath, sbuf, mode);
 | |
| 	
 | |
|   dlopenok:
 | |
| 	dounlock();
 | |
| 	return (void *)dls;
 | |
|   dlopenerror:
 | |
| 	dounlock();
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| #if !FINK_BUILD
 | |
| void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
 | |
| {
 | |
| 	int sym_len = strlen(symbol);
 | |
| 	void *value = NULL;
 | |
| 	char *malloc_sym = NULL;
 | |
| 	dolock();
 | |
| 	malloc_sym = malloc(sym_len + 2);
 | |
| 	if (malloc_sym)
 | |
| 	{
 | |
| 		sprintf(malloc_sym, "_%s", symbol);
 | |
| 		value = dlsymIntern(handle, malloc_sym, 1);
 | |
| 		free(malloc_sym);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		error("Unable to allocate memory");
 | |
| 		goto dlsymerror;
 | |
| 	}
 | |
| 	dounlock();
 | |
| 	return value;
 | |
|   dlsymerror:
 | |
| 	dounlock();
 | |
| 	return NULL;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if FINK_BUILD
 | |
| 
 | |
| void *dlsym_prepend_underscore(void *handle, const char *symbol)
 | |
| {
 | |
| 	void *answer;
 | |
| 	dolock();
 | |
| 	answer = dlsym_prepend_underscore_intern(handle, symbol);
 | |
| 	dounlock();
 | |
| 	return answer;
 | |
| }
 | |
| 
 | |
| static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol)
 | |
| {
 | |
| /*
 | |
|  *	A quick and easy way for porting packages which call dlsym(handle,"sym")
 | |
|  *	If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then
 | |
|  *	this function will be called, and will add the required underscore.
 | |
|  *	
 | |
|  *	Note that I haven't figured out yet which should be "standard", prepend
 | |
|  *	the underscore always, or not at all. These global functions need to go away
 | |
|  *	for opendarwin.
 | |
|  */
 | |
| 	int sym_len = strlen(symbol);
 | |
| 	void *value = NULL;
 | |
| 	char *malloc_sym = NULL;
 | |
| 	malloc_sym = malloc(sym_len + 2);
 | |
| 	if (malloc_sym)
 | |
| 	{
 | |
| 		sprintf(malloc_sym, "_%s", symbol);
 | |
| 		value = dlsymIntern(handle, malloc_sym, 1);
 | |
| 		free(malloc_sym);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		error("Unable to allocate memory");
 | |
| 	}
 | |
| 	return value;
 | |
| }
 | |
| 
 | |
| void *dlsym_auto_underscore(void *handle, const char *symbol)
 | |
| {
 | |
| 	void *answer;
 | |
| 	dolock();
 | |
| 	answer = dlsym_auto_underscore_intern(handle, symbol);
 | |
| 	dounlock();
 | |
| 	return answer;
 | |
| 
 | |
| }
 | |
| static void *dlsym_auto_underscore_intern(void *handle, const char *symbol)
 | |
| {
 | |
| 	struct dlstatus *dls = handle;
 | |
| 	void *addr = 0;
 | |
| 	addr = dlsymIntern(dls, symbol, 0);
 | |
| 	if (!addr)
 | |
| 		addr = dlsym_prepend_underscore_intern(handle, symbol);
 | |
| 	return addr;
 | |
| }
 | |
| 
 | |
| 
 | |
| void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
 | |
| {
 | |
| 	struct dlstatus *dls = handle;
 | |
| 	void *addr = 0;
 | |
| 	dolock();
 | |
| 	addr = dlsymIntern(dls, symbol, 1);
 | |
| 	dounlock();
 | |
| 	return addr;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int dlclose(void *handle)
 | |
| {
 | |
| 	struct dlstatus *dls = handle;
 | |
| 	dolock();
 | |
| 	resetdlerror();
 | |
| 	if (!isValidStatus(dls))
 | |
| 	{
 | |
| 		goto dlcloseerror;
 | |
| 	}
 | |
| 	if (dls->module == MAGIC_DYLIB_MOD)
 | |
| 	{
 | |
| 		const char *name;
 | |
| 		if (!dls->lib)
 | |
| 		{
 | |
| 			name = "global context";
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			name = get_lib_name(dls->lib);
 | |
| 		}
 | |
| 		warning("trying to close a .dylib!");
 | |
| 		error("Not closing \"%s\" - dynamic libraries cannot be closed", name);
 | |
| 		goto dlcloseerror;
 | |
| 	}
 | |
| 	if (!dls->module)
 | |
| 	{
 | |
| 		error("module already closed");
 | |
| 		goto dlcloseerror;
 | |
| 	}
 | |
| 	
 | |
| 	if (dls->refs == 1)
 | |
| 	{
 | |
| 		unsigned long options = 0;
 | |
| 		void (*fini) (void);
 | |
| 		if ((fini = dlsymIntern(dls, "__fini", 0)))
 | |
| 		{
 | |
| 			debug("calling _fini()");
 | |
| 			fini();
 | |
| 		}
 | |
| #ifdef __ppc__
 | |
| 		options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
 | |
| #endif
 | |
| #if 1
 | |
| /*  Currently, if a module contains c++ static destructors and it is unloaded, we
 | |
|  *  get a segfault in atexit(), due to compiler and dynamic loader differences of
 | |
|  *  opinion, this works around that.
 | |
|  *  I really need a way to figure out from code if this is still necessary.
 | |
|  */
 | |
| 		if ((const struct section *)NULL !=
 | |
| 			getsectbynamefromheader(get_mach_header_from_NSModule(dls->module),
 | |
| 									"__DATA", "__mod_term_func"))
 | |
| 		{
 | |
| 			options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
 | |
| 		}
 | |
| #endif
 | |
| #ifdef RTLD_NODELETE
 | |
| 		if (isFlagSet(dls->mode, RTLD_NODELETE))
 | |
| 			options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
 | |
| #endif
 | |
| 		if (!NSUnLinkModule(dls->module, options))
 | |
| 		{
 | |
| 			error("unable to unlink module");
 | |
| 			goto dlcloseerror;
 | |
| 		}
 | |
| 		dls->refs--;
 | |
| 		dls->module = 0;
 | |
| 		/* Note: the dlstatus struct dls is neither removed from the list
 | |
| 		 * nor is the memory it occupies freed. This shouldn't pose a 
 | |
| 		 * problem in mostly all cases, though.
 | |
| 		 */
 | |
| 	}
 | |
| 	dounlock();
 | |
| 	return 0;
 | |
|   dlcloseerror:
 | |
| 	dounlock();
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| const char *dlerror(void)
 | |
| {
 | |
| 	struct dlthread  *tss;
 | |
| 	char * err_str;
 | |
| 	tss = pthread_getspecific(dlerror_key);
 | |
| 	err_str = tss->errstr;
 | |
| 	tss = pthread_getspecific(dlerror_key);
 | |
| 	if (tss->errset == 0)
 | |
| 		return 0;
 | |
| 	tss->errset = 0;	
 | |
| 	return (err_str );
 | |
| }
 | |
| 
 | |
| /* Given an address, return the mach_header for the image containing it
 | |
|  * or zero if the given address is not contained in any loaded images.
 | |
|  */
 | |
| const struct mach_header *image_for_address(const void *address)
 | |
| {
 | |
| 	unsigned long i;
 | |
| 	unsigned long j;
 | |
| 	unsigned long count = _dyld_image_count();
 | |
| 	struct mach_header *mh = 0;
 | |
| 	struct load_command *lc = 0;
 | |
| 	unsigned long addr = NULL;
 | |
| 	for (i = 0; i < count; i++)
 | |
| 	{
 | |
| 		addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i);
 | |
| 		mh = _dyld_get_image_header(i);
 | |
| 		if (mh)
 | |
| 		{
 | |
| 			lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
 | |
| 			for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
 | |
| 			{
 | |
| 				if (LC_SEGMENT == lc->cmd &&
 | |
| 					addr >= ((struct segment_command *)lc)->vmaddr &&
 | |
| 					addr <
 | |
| 					((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
 | |
| 				{
 | |
| 					goto image_found;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		mh = 0;
 | |
| 	}
 | |
|   image_found:
 | |
| 	return mh;
 | |
| }
 | |
| 
 | |
| int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info)
 | |
| {
 | |
| /*
 | |
| 	FIXME: USe the routine image_for_address.
 | |
| */
 | |
| 	unsigned long i;
 | |
| 	unsigned long j;
 | |
| 	unsigned long count = _dyld_image_count();
 | |
| 	struct mach_header *mh = 0;
 | |
| 	struct load_command *lc = 0;
 | |
| 	unsigned long addr = NULL;
 | |
| 	unsigned long table_off = (unsigned long)0;
 | |
| 	int found = 0;
 | |
| 	if (!info)
 | |
| 		return 0;
 | |
| 	dolock();
 | |
| 	resetdlerror();
 | |
| 	info->dli_fname = 0;
 | |
| 	info->dli_fbase = 0;
 | |
| 	info->dli_sname = 0;
 | |
| 	info->dli_saddr = 0;
 | |
| /* Some of this was swiped from code posted by Douglas Davidson <ddavidso AT apple DOT com>
 | |
|  * to darwin-development AT lists DOT apple DOT com and slightly modified
 | |
|  */
 | |
| 	for (i = 0; i < count; i++)
 | |
| 	{
 | |
| 		addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i);
 | |
| 		mh = _dyld_get_image_header(i);
 | |
| 		if (mh)
 | |
| 		{
 | |
| 			lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
 | |
| 			for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
 | |
| 			{
 | |
| 				if (LC_SEGMENT == lc->cmd &&
 | |
| 					addr >= ((struct segment_command *)lc)->vmaddr &&
 | |
| 					addr <
 | |
| 					((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
 | |
| 				{
 | |
| 					info->dli_fname = _dyld_get_image_name(i);
 | |
| 					info->dli_fbase = (void *)mh;
 | |
| 					found = 1;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 			if (found)
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!found)
 | |
| 	{
 | |
| 		dounlock();
 | |
| 		return 0;
 | |
| 	}
 | |
| 	lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
 | |
| 	for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
 | |
| 	{
 | |
| 		if (LC_SEGMENT == lc->cmd)
 | |
| 		{
 | |
| 			if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT"))
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 	table_off =
 | |
| 		((unsigned long)((struct segment_command *)lc)->vmaddr) -
 | |
| 		((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i);
 | |
| 	debug("table off %x", table_off);
 | |
| 
 | |
| 	lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
 | |
| 	for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
 | |
| 	{
 | |
| 		if (LC_SYMTAB == lc->cmd)
 | |
| 		{
 | |
| 
 | |
| 			struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off);
 | |
| 			unsigned long numsyms = ((struct symtab_command *)lc)->nsyms;
 | |
| 			struct nlist *nearest = NULL;
 | |
| 			unsigned long diff = 0xffffffff;
 | |
| 			unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off);
 | |
| 			debug("symtable %x", symtable);
 | |
| 			for (i = 0; i < numsyms; i++)
 | |
| 			{
 | |
| 				/* Ignore the following kinds of Symbols */
 | |
| 				if ((!symtable->n_value)	/* Undefined */
 | |
| 					|| (symtable->n_type >= N_PEXT)	/* Debug symbol */
 | |
| 					|| (!(symtable->n_type & N_EXT))	/* Local Symbol */
 | |
| 					)
 | |
| 				{
 | |
| 					symtable++;
 | |
| 					continue;
 | |
| 				}
 | |
| 				if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr)))
 | |
| 				{
 | |
| 					diff = (unsigned long)symtable->n_value - addr;
 | |
| 					nearest = symtable;
 | |
| 				}
 | |
| 				symtable++;
 | |
| 			}
 | |
| 			if (nearest)
 | |
| 			{
 | |
| 				info->dli_saddr = nearest->n_value + ((void *)p - addr);
 | |
| 				info->dli_sname = (char *)(strtable + nearest->n_un.n_strx);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	dounlock();
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Implement the dlfunc() interface, which behaves exactly the same as
 | |
|  * dlsym() except that it returns a function pointer instead of a data
 | |
|  * pointer.  This can be used by applications to avoid compiler warnings
 | |
|  * about undefined behavior, and is intended as prior art for future
 | |
|  * POSIX standardization.  This function requires that all pointer types
 | |
|  * have the same representation, which is true on all platforms FreeBSD
 | |
|  * runs on, but is not guaranteed by the C standard.
 | |
|  */
 | |
| #if 0 
 | |
| dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol)
 | |
| {
 | |
| 	union
 | |
| 	{
 | |
| 		void *d;
 | |
| 		dlfunc_t f;
 | |
| 	} rv;
 | |
| 	int sym_len = strlen(symbol);
 | |
| 	char *malloc_sym = NULL;
 | |
| 	dolock();
 | |
| 	malloc_sym = malloc(sym_len + 2);
 | |
| 	if (malloc_sym)
 | |
| 	{
 | |
| 		sprintf(malloc_sym, "_%s", symbol);
 | |
| 		rv.d = dlsymIntern(handle, malloc_sym, 1);
 | |
| 		free(malloc_sym);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		error("Unable to allocate memory");
 | |
| 		goto dlfuncerror;
 | |
| 	}
 | |
| 	dounlock();
 | |
| 	return rv.f;
 | |
|   dlfuncerror:
 | |
| 	dounlock();
 | |
| 	return NULL;
 | |
| }
 | |
| #endif
 |