diff src/loadso/macosx/SDL_loadso.c @ 1173:e9cf8c1b4590

Split up src/SDL_loadso.c into platform directories.
author Ryan C. Gordon <icculus@icculus.org>
date Thu, 17 Nov 2005 03:15:05 +0000
parents
children 173c063d4f55
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/loadso/macosx/SDL_loadso.c	Thu Nov 17 03:15:05 2005 +0000
@@ -0,0 +1,1419 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2004 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifdef SAVE_RCSID
+static char rcsid =
+ "@(#) $Id$";
+#endif
+
+/* Please note that dlcompat apparently ships in current Mac OS X versions
+ *  as a system library that provides compatibility with the Unix "dlopen"
+ *  interface. In order to allow SDL to work on older OS X releases and also
+ *  not conflict with the system lib on newer versions, we include dlcompat
+ *  in SDL and change the symbols to prevent symbol clash with any existing
+ *  system libraries.  --ryan.
+ */
+
+/* here is the dlcompat license: */
+
+/*
+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 "dlfcn.h"*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__GNUC__) && __GNUC__ > 3
+#define dl_restrict __restrict
+#else
+#define dl_restrict
+#endif
+
+#ifndef _POSIX_SOURCE
+/*
+ * Structure filled in by dladdr().
+ */
+typedef struct SDL_OSX_dl_info {
+        const char      *dli_fname;     /* Pathname of shared object */
+        void            *dli_fbase;     /* Base address of shared object */
+        const char      *dli_sname;     /* Name of nearest symbol */
+        void            *dli_saddr;     /* Address of nearest symbol */
+} SDL_OSX_Dl_info;
+
+static int SDL_OSX_dladdr(const void * dl_restrict, SDL_OSX_Dl_info * dl_restrict);
+#endif /* ! _POSIX_SOURCE */
+
+static int SDL_OSX_dlclose(void * handle);
+static char * SDL_OSX_dlerror(void);
+static void * SDL_OSX_dlopen(const char *path, int mode);
+static void * SDL_OSX_dlsym(void * dl_restrict handle, const char * dl_restrict symbol);
+
+#define RTLD_LAZY	0x1
+#define RTLD_NOW	0x2
+#define RTLD_LOCAL	0x4
+#define RTLD_GLOBAL	0x8
+
+#ifndef _POSIX_SOURCE
+#define RTLD_NOLOAD	0x10
+#define RTLD_NODELETE	0x80
+
+/*
+ * Special handle arguments for SDL_OSX_dlsym().
+ */
+#define	RTLD_NEXT		((void *) -1)	/* Search subsequent objects. */
+#define	RTLD_DEFAULT	((void *) -2)	/* Use default search algorithm. */
+#endif /* ! _POSIX_SOURCE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#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 SDL_OSX_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
+ * SDL_OSX_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 dlcompat_init_check(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 inline const char *dyld_error_str(void);
+
+#if FINK_BUILD
+/* Two Global Functions */
+static void *dlsym_prepend_underscore(void *handle, const char *symbol);
+static 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 = calloc(sizeof(*dls),1);
+	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_LOCAL))
+		{
+			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.
+ */
+static NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol)
+{
+	unsigned 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;
+#ifdef __GCC__  
+	void *caller = __builtin_return_address(1);	/* Be *very* careful about inlining */
+#else
+	void *caller = NULL;
+#endif
+	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)
+		  {
+			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_LOCAL))
+				{
+					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;
+}
+
+inline static void dlcompat_init_check(void)
+{
+	static pthread_mutex_t l = PTHREAD_MUTEX_INITIALIZER;
+	static int init_done = 0;
+
+	pthread_mutex_lock(&l);
+	if (!init_done) {
+		dlcompat_init_func();
+		init_done = 1;
+	}
+	pthread_mutex_unlock(&l);
+}
+
+static void dlcompat_init_func(void)
+{
+        _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);
+}
+
+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;
+	dlcompat_init_check();
+	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);
+}
+
+static void *SDL_OSX_dlopen(const char *path, int mode)
+{
+	const struct stat *sbuf;
+	struct dlstatus *dls;
+	const char *fullPath;
+
+	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
+static void *SDL_OSX_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
+
+static 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;
+}
+
+static 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;
+}
+
+
+static void *SDL_OSX_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
+
+static int SDL_OSX_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();
+		}
+		options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
+#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;
+}
+
+static char *SDL_OSX_dlerror(void)
+{
+	struct dlthread  *tss;
+	const char * err_str = NULL;
+	dlcompat_init_check();
+	tss = pthread_getspecific(dlerror_key);
+	if (tss != NULL && tss->errset != 0) {
+		tss->errset = 0;	
+		err_str = tss->errstr;
+	}
+	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.
+ */
+static 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;
+}
+
+static int SDL_OSX_dladdr(const void * dl_restrict p, SDL_OSX_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 
+static dlfunc_t SDL_OSX_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
+
+
+
+/* dlcompat ends, here's the SDL interface...  --ryan.  */
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* System dependent library loading routines                           */
+
+#if !SDL_INTERNAL_BUILDING_LOADSO
+#error Do not compile directly...compile src/SDL_loadso.c instead!
+#endif
+
+#if !defined(MACOSX)
+#error Compiling for the wrong platform?
+#elif defined(USE_DLOPEN)
+#error Do not use USE_DLOPEN on Mac OS X.
+#endif
+
+#include "SDL_types.h"
+#include "SDL_error.h"
+#include "SDL_loadso.h"
+
+void *SDL_LoadObject(const char *sofile)
+{
+	void *handle = SDL_OSX_dlopen(sofile, RTLD_NOW);
+	const char *loaderror = (char *)SDL_OSX_dlerror();
+	if ( handle == NULL ) {
+		SDL_SetError("Failed loading %s: %s", sofile, loaderror);
+	}
+	return(handle);
+}
+
+void *SDL_LoadFunction(void *handle, const char *name)
+{
+	void *symbol = SDL_OSX_dlsym(handle, name);
+	if ( symbol == NULL ) {
+		SDL_SetError("Failed loading %s: %s", name, (const char *)SDL_OSX_dlerror());
+	}
+	return(symbol);
+}
+
+void SDL_UnloadObject(void *handle)
+{
+	if ( handle != NULL ) {
+		SDL_OSX_dlclose(handle);
+	}
+}
+
+