1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24#ifdef SDL_LOADSO_DLCOMPAT
25
26/* Please note that dlcompat apparently ships in current Mac OS X versions
27 *  as a system library that provides compatibility with the Unix "dlopen"
28 *  interface. In order to allow SDL to work on older OS X releases and also
29 *  not conflict with the system lib on newer versions, we include dlcompat
30 *  in SDL and change the symbols to prevent symbol clash with any existing
31 *  system libraries.  --ryan.
32 */
33
34/* here is the dlcompat license: */
35
36/*
37Copyright (c) 2002 Jorge Acereda  <jacereda@users.sourceforge.net> &
38                   Peter O'Gorman <ogorman@users.sourceforge.net>
39
40Portions may be copyright others, see the AUTHORS file included with this
41distribution.
42
43Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
44
45Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
46
47Permission is hereby granted, free of charge, to any person obtaining
48a copy of this software and associated documentation files (the
49"Software"), to deal in the Software without restriction, including
50without limitation the rights to use, copy, modify, merge, publish,
51distribute, sublicense, and/or sell copies of the Software, and to
52permit persons to whom the Software is furnished to do so, subject to
53the following conditions:
54
55The above copyright notice and this permission notice shall be
56included in all copies or substantial portions of the Software.
57
58THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
59EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
60MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
61NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
62LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
63OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
64WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
65*/
66
67#include <pthread.h>
68#include <sys/types.h>
69#include <sys/stat.h>
70#include <stdarg.h>
71#include <limits.h>
72#include <mach-o/dyld.h>
73#include <mach-o/nlist.h>
74#include <mach-o/getsect.h>
75
76#include "SDL_stdinc.h"
77
78/* Just playing to see if it would compile with the freebsd headers, it does,
79 * but because of the different values for RTLD_LOCAL etc, it would break binary
80 * compat... oh well
81 */
82#ifndef __BSD_VISIBLE
83#define __BSD_VISIBLE 1
84#endif
85
86/*include "dlfcn.h"*/
87#ifdef __cplusplus
88extern "C" {
89#endif
90
91#if defined (__GNUC__) && __GNUC__ > 3
92#define dl_restrict __restrict
93#else
94#define dl_restrict
95#endif
96
97#if 0
98#ifndef _POSIX_SOURCE
99/*
100 * Structure filled in by dladdr().
101 */
102typedef struct SDL_OSX_dl_info {
103        const char      *dli_fname;     /* Pathname of shared object */
104        void            *dli_fbase;     /* Base address of shared object */
105        const char      *dli_sname;     /* Name of nearest symbol */
106        void            *dli_saddr;     /* Address of nearest symbol */
107} SDL_OSX_Dl_info;
108
109static int SDL_OSX_dladdr(const void * dl_restrict, SDL_OSX_Dl_info * dl_restrict);
110#endif /* ! _POSIX_SOURCE */
111#endif /* 0 */
112
113static int SDL_OSX_dlclose(void * handle);
114static const char * SDL_OSX_dlerror(void);
115static void * SDL_OSX_dlopen(const char *path, int mode);
116static void * SDL_OSX_dlsym(void * dl_restrict handle, const char * dl_restrict symbol);
117
118#define RTLD_LAZY	0x1
119#define RTLD_NOW	0x2
120#define RTLD_LOCAL	0x4
121#define RTLD_GLOBAL	0x8
122
123#ifndef _POSIX_SOURCE
124#define RTLD_NOLOAD	0x10
125#define RTLD_NODELETE	0x80
126
127/*
128 * Special handle arguments for SDL_OSX_dlsym().
129 */
130#define	RTLD_NEXT		((void *) -1)	/* Search subsequent objects. */
131#define	RTLD_DEFAULT	((void *) -2)	/* Use default search algorithm. */
132#endif /* ! _POSIX_SOURCE */
133
134#ifdef __cplusplus
135}
136#endif
137
138#ifndef dl_restrict
139#define dl_restrict __restrict
140#endif
141/* This is not available on 10.1 */
142#ifndef LC_LOAD_WEAK_DYLIB
143#define	LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)
144#endif
145
146/* With this stuff here, this thing may actually compile/run on 10.0 systems
147 * Not that I have a 10.0 system to test it on anylonger
148 */
149#ifndef LC_REQ_DYLD
150#define LC_REQ_DYLD 0x80000000
151#endif
152#ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
153#define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4
154#endif
155#ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR
156#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1
157#endif
158#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
159#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0
160#endif
161#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
162#define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4
163#endif
164/* These symbols will be looked for in dyld */
165static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0;
166static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0;
167static NSSymbol(*dyld_NSLookupSymbolInImage)
168	(const struct mach_header *, const char *, unsigned long) = 0;
169
170/* Define this to make dlcompat reuse data block. This way in theory we save
171 * a little bit of overhead. However we then couldn't correctly catch excess
172 * calls to SDL_OSX_dlclose(). Hence we don't use this feature
173 */
174#undef REUSE_STATUS
175
176/* Size of the internal error message buffer (used by dlerror()) */
177#define ERR_STR_LEN			251
178
179/* Maximum number of search paths supported by getSearchPath */
180#define MAX_SEARCH_PATHS	32
181
182
183#define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF')
184#define MAGIC_DYLIB_MOD ((NSModule) 'DYMO')
185
186/* internal flags */
187#define DL_IN_LIST 0x01
188
189/* our mutex */
190static pthread_mutex_t dlcompat_mutex;
191/* Our thread specific storage
192 */
193static pthread_key_t dlerror_key;
194
195struct dlthread
196{
197	int lockcnt;
198	unsigned char errset;
199	char errstr[ERR_STR_LEN];
200};
201
202/* This is our central data structure. Whenever a module is loaded via
203 * SDL_OSX_dlopen(), we create such a struct.
204 */
205struct dlstatus
206{
207	struct dlstatus *next;		/* pointer to next element in the linked list */
208	NSModule module;
209	const struct mach_header *lib;
210	int refs;					/* reference count */
211	int mode;					/* mode in which this module was loaded */
212	dev_t device;
213	ino_t inode;
214	int flags;					/* Any internal flags we may need */
215};
216
217/* Head node of the dlstatus list */
218static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 };
219static struct dlstatus *stqueue = &mainStatus;
220
221
222/* Storage for the last error message (used by dlerror()) */
223/* static char err_str[ERR_STR_LEN]; */
224/* static int err_filled = 0; */
225
226/* Prototypes to internal functions */
227static void debug(const char *fmt, ...);
228static void error(const char *str, ...);
229static const char *safegetenv(const char *s);
230static const char *searchList(void);
231static const char *getSearchPath(int i);
232static const char *getFullPath(int i, const char *file);
233static const struct stat *findFile(const char *file, const char **fullPath);
234static int isValidStatus(struct dlstatus *status);
235static inline int isFlagSet(int mode, int flag);
236static struct dlstatus *lookupStatus(const struct stat *sbuf);
237static void insertStatus(struct dlstatus *dls, const struct stat *sbuf);
238static int promoteLocalToGlobal(struct dlstatus *dls);
239static void *reference(struct dlstatus *dls, int mode);
240static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError);
241static struct dlstatus *allocStatus(void);
242static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode);
243static NSSymbol search_linked_libs(const struct mach_header *mh, const char *symbol);
244static const char *get_lib_name(const struct mach_header *mh);
245static const struct mach_header *get_mach_header_from_NSModule(NSModule mod);
246static void dlcompat_init_func(void);
247static inline void dlcompat_init_check(void);
248static inline void dolock(void);
249static inline void dounlock(void);
250static void dlerrorfree(void *data);
251static void resetdlerror(void);
252static const struct mach_header *my_find_image(const char *name);
253static const struct mach_header *image_for_address(const void *address);
254static inline char *dyld_error_str(void);
255
256#if FINK_BUILD
257/* Two Global Functions */
258static void *dlsym_prepend_underscore(void *handle, const char *symbol);
259static void *dlsym_auto_underscore(void *handle, const char *symbol);
260
261/* And their _intern counterparts */
262static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol);
263static void *dlsym_auto_underscore_intern(void *handle, const char *symbol);
264#endif
265
266/* Functions */
267
268static void debug(const char *fmt, ...)
269{
270#if DEBUG > 1
271	va_list arg;
272	va_start(arg, fmt);
273	fprintf(stderr, "DLDEBUG: ");
274	vfprintf(stderr, fmt, arg);
275	fprintf(stderr, "\n");
276	fflush(stderr);
277	va_end(arg);
278#endif
279}
280
281static void error(const char *str, ...)
282{
283	va_list arg;
284	struct dlthread  *tss;
285	char * err_str;
286	va_start(arg, str);
287	tss = pthread_getspecific(dlerror_key);
288	err_str = tss->errstr;
289	SDL_strlcpy(err_str, "dlcompat: ", ERR_STR_LEN);
290	vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg);
291	va_end(arg);
292	debug("ERROR: %s\n", err_str);
293	tss->errset = 1;
294}
295
296static void warning(const char *str)
297{
298#if DEBUG > 0
299	fprintf(stderr, "WARNING: dlcompat: %s\n", str);
300#endif
301}
302
303static const char *safegetenv(const char *s)
304{
305	const char *ss = SDL_getenv(s);
306	return ss ? ss : "";
307}
308
309/* because this is only used for debugging and error reporting functions, we
310 * don't really care about how elegant it is... it could use the load
311 * commands to find the install name of the library, but...
312 */
313static const char *get_lib_name(const struct mach_header *mh)
314{
315	unsigned long count = _dyld_image_count();
316	unsigned long i;
317	const char *val = NULL;
318	if (mh)
319	{
320		for (i = 0; i < count; i++)
321		{
322			if (mh == _dyld_get_image_header(i))
323			{
324				val = _dyld_get_image_name(i);
325				break;
326			}
327		}
328	}
329	return val;
330}
331
332/* Returns the mach_header for the module bu going through all the loaded images
333 * and finding the one with the same name as the module. There really ought to be
334 * an api for doing this, would be faster, but there isn't one right now
335 */
336static const struct mach_header *get_mach_header_from_NSModule(NSModule mod)
337{
338	const char *mod_name = NSNameOfModule(mod);
339	const struct mach_header *mh = NULL;
340	unsigned long count = _dyld_image_count();
341	unsigned long i;
342	debug("Module name: %s", mod_name);
343	for (i = 0; i < count; i++)
344	{
345		if (!SDL_strcmp(mod_name, _dyld_get_image_name(i)))
346		{
347			mh = _dyld_get_image_header(i);
348			break;
349		}
350	}
351	return mh;
352}
353
354
355/* Compute and return a list of all directories that we should search when
356 * trying to locate a module. We first look at the values of LD_LIBRARY_PATH
357 * and DYLD_LIBRARY_PATH, and then finally fall back to looking into
358 * /usr/lib and /lib. Since both of the environments variables can contain a
359 * list of colon seperated paths, we simply concat them and the two other paths
360 * into one big string, which we then can easily parse.
361 * Splitting this string into the actual path list is done by getSearchPath()
362 */
363static const char *searchList()
364{
365	size_t buf_size;
366	static char *buf=NULL;
367	const char *ldlp = safegetenv("LD_LIBRARY_PATH");
368	const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH");
369	const char *stdpath = SDL_getenv("DYLD_FALLBACK_LIBRARY_PATH");
370	if (!stdpath)
371		stdpath = "/usr/local/lib:/lib:/usr/lib";
372	if (!buf)
373	{
374		buf_size = SDL_strlen(ldlp) + SDL_strlen(dyldlp) + SDL_strlen(stdpath) + 4;
375		buf = SDL_malloc(buf_size);
376		SDL_snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""),
377				 stdpath, '\0');
378	}
379	return buf;
380}
381
382/* Returns the ith search path from the list as computed by searchList() */
383static const char *getSearchPath(int i)
384{
385	static const char *list = 0;
386	static char **path = (char **)0;
387	static int end = 0;
388	static int numsize = MAX_SEARCH_PATHS;
389	static char **tmp;
390	/* So we can call SDL_free() in the "destructor" we use i=-1 to return the alloc'd array */
391	if (i == -1)
392	{
393		return (const char*)path;
394	}
395	if (!path)
396	{
397		path = (char **)SDL_calloc(MAX_SEARCH_PATHS, sizeof(char **));
398	}
399	if (!list && !end)
400		list = searchList();
401	if (i >= (numsize))
402	{
403		debug("Increasing size for long PATH");
404		tmp = (char **)SDL_calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **));
405		if (tmp)
406		{
407			SDL_memcpy(tmp, path, sizeof(char **) * numsize);
408			SDL_free(path);
409			path = tmp;
410			numsize += MAX_SEARCH_PATHS;
411		}
412		else
413		{
414			return 0;
415		}
416	}
417
418	while (!path[i] && !end)
419	{
420		path[i] = strsep((char **)&list, ":");
421
422		if (path[i][0] == 0)
423			path[i] = 0;
424		end = (list == 0);
425	}
426	return path[i];
427}
428
429static const char *getFullPath(int i, const char *file)
430{
431	static char buf[PATH_MAX];
432	const char *path = getSearchPath(i);
433	if (path)
434	{
435		SDL_snprintf(buf, PATH_MAX, "%s/%s", path, file);
436	}
437	return path ? buf : 0;
438}
439
440/* Given a file name, try to determine the full path for that file. Starts
441 * its search in the current directory, and then tries all paths in the
442 * search list in the order they are specified there.
443 */
444static const struct stat *findFile(const char *file, const char **fullPath)
445{
446	int i = 0;
447	static struct stat sbuf;
448	char *fileName;
449	debug("finding file %s", file);
450	*fullPath = file;
451	if (0 == stat(file, &sbuf))
452		return &sbuf;
453	if (SDL_strchr(file, '/'))
454		return 0;				/* If the path had a / we don't look in env var places */
455	fileName = NULL;
456	if (!fileName)
457		fileName = (char *)file;
458	while ((*fullPath = getFullPath(i++, fileName)))
459	{
460		if (0 == stat(*fullPath, &sbuf))
461			return &sbuf;
462	}
463	;
464	return 0;
465}
466
467/* Determine whether a given dlstatus is valid or not */
468static int isValidStatus(struct dlstatus *status)
469{
470	/* Walk the list to verify status is contained in it */
471	struct dlstatus *dls = stqueue;
472	while (dls && status != dls)
473		dls = dls->next;
474	if (dls == 0)
475		error("invalid handle");
476	else if ((dls->module == 0) || (dls->refs == 0))
477		error("handle to closed library");
478	else
479		return TRUE;
480	return FALSE;
481}
482
483static inline int isFlagSet(int mode, int flag)
484{
485	return (mode & flag) == flag;
486}
487
488static struct dlstatus *lookupStatus(const struct stat *sbuf)
489{
490	struct dlstatus *dls = stqueue;
491	debug("looking for status");
492	while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0
493				   || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode))
494		dls = dls->next;
495	return dls;
496}
497
498static void insertStatus(struct dlstatus *dls, const struct stat *sbuf)
499{
500	debug("inserting status");
501	dls->inode = sbuf->st_ino;
502	dls->device = sbuf->st_dev;
503	dls->refs = 0;
504	dls->mode = 0;
505	if ((dls->flags & DL_IN_LIST) == 0)
506	{
507		dls->next = stqueue;
508		stqueue = dls;
509		dls->flags |= DL_IN_LIST;
510	}
511}
512
513static struct dlstatus *allocStatus()
514{
515	struct dlstatus *dls;
516#ifdef REUSE_STATUS
517	dls = stqueue;
518	while (dls && dls->module)
519		dls = dls->next;
520	if (!dls)
521#endif
522		dls = SDL_calloc(sizeof(*dls),1);
523	return dls;
524}
525
526static int promoteLocalToGlobal(struct dlstatus *dls)
527{
528	static int (*p) (NSModule module) = 0;
529	debug("promoting");
530	if (!p)
531		_dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (void **)&p);
532	return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module));
533}
534
535static void *reference(struct dlstatus *dls, int mode)
536{
537	if (dls)
538	{
539		if (dls->module == MAGIC_DYLIB_MOD && isFlagSet(mode, RTLD_LOCAL))
540		{
541			warning("trying to open a .dylib with RTLD_LOCAL");
542			error("unable to open a .dylib with RTLD_LOCAL");
543			return NULL;
544		}
545		if (isFlagSet(mode, RTLD_GLOBAL) &&
546			!isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls))
547		{
548			error("unable to promote local module to global");
549			return NULL;
550		}
551		dls->mode |= mode;
552		dls->refs++;
553	}
554	else
555		debug("reference called with NULL argument");
556
557	return dls;
558}
559
560static const struct mach_header *my_find_image(const char *name)
561{
562	const struct mach_header *mh = 0;
563	const char *id = NULL;
564	int i = _dyld_image_count();
565	int j;
566	mh = (struct mach_header *)
567		dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED |
568						NSADDIMAGE_OPTION_RETURN_ON_ERROR);
569	if (!mh)
570	{
571		for (j = 0; j < i; j++)
572		{
573			id = _dyld_get_image_name(j);
574			if (!SDL_strcmp(id, name))
575			{
576				mh = _dyld_get_image_header(j);
577				break;
578			}
579		}
580	}
581	return mh;
582}
583
584/*
585 * dyld adds libraries by first adding the directly dependant libraries in link order, and
586 * then adding the dependencies for those libraries, so we should do the same... but we don't
587 * bother adding the extra dependencies, if the symbols are neither in the loaded image nor
588 * any of it's direct dependencies, then it probably isn't there.
589 */
590static NSSymbol search_linked_libs(const struct mach_header * mh, const char *symbol)
591{
592	unsigned int n;
593	struct load_command *lc = 0;
594	struct mach_header *wh;
595	NSSymbol nssym = 0;
596	if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
597	{
598		lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
599		for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
600		{
601			if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd))
602			{
603				if ((wh = (struct mach_header *)
604					 my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset +
605											(char *)lc))))
606				{
607					if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol))
608					{
609						nssym = dyld_NSLookupSymbolInImage(wh,
610														   symbol,
611														   NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
612														   NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
613						break;
614					}
615				}
616			}
617		}
618		if ((!nssym) && NSIsSymbolNameDefined(symbol))
619		{
620			/* I've never seen this debug message...*/
621			debug("Symbol \"%s\" is defined but was not found", symbol);
622		}
623	}
624	return nssym;
625}
626
627/* Up to the caller to SDL_free() returned string */
628static inline char *dyld_error_str()
629{
630	NSLinkEditErrors dylder;
631	int dylderno;
632	const char *dylderrstr;
633	const char *dyldfile;
634	char* retStr = NULL;
635	NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr);
636	if (dylderrstr && *dylderrstr)
637	{
638		retStr = SDL_strdup(dylderrstr);
639	}
640	return retStr;
641}
642
643static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError)
644{
645  NSSymbol nssym = 0;
646#ifdef __GCC__
647	void *caller = __builtin_return_address(1);	/* Be *very* careful about inlining */
648#else
649	void *caller = NULL;
650#endif
651	const struct mach_header *caller_mh = 0;
652	char *savedErrorStr = NULL;
653	resetdlerror();
654#ifndef RTLD_SELF
655#define	RTLD_SELF		((void *) -3)
656#endif
657	if (NULL == dls)
658		dls = RTLD_SELF;
659	if ((RTLD_NEXT == dls) || (RTLD_SELF == dls))
660	{
661		if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage && caller)
662		  {
663			caller_mh = image_for_address(caller);
664			if (RTLD_SELF == dls)
665			{
666				/* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE
667				 * But it appears to work anyway, and looking at the code in dyld_libfuncs.c
668				 * this is acceptable.
669				 */
670				if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol))
671				{
672					nssym = dyld_NSLookupSymbolInImage(caller_mh,
673													   symbol,
674													   NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
675													   NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
676				}
677			}
678			if (!nssym)
679			{
680				if (RTLD_SELF == dls)
681					savedErrorStr = dyld_error_str();
682				nssym = search_linked_libs(caller_mh, symbol);
683			}
684		}
685		else
686		{
687			if (canSetError)
688				error("RTLD_SELF and RTLD_NEXT are not supported");
689			return NULL;
690		}
691	}
692	if (!nssym)
693	{
694
695		if (RTLD_DEFAULT == dls)
696		{
697			dls = &mainStatus;
698		}
699		if (!isValidStatus(dls))
700			return NULL;
701
702		if (dls->module != MAGIC_DYLIB_MOD)
703		{
704			nssym = NSLookupSymbolInModule(dls->module, symbol);
705			if (!nssym && NSIsSymbolNameDefined(symbol))
706			{
707				debug("Searching dependencies");
708				savedErrorStr = dyld_error_str();
709				nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol);
710			}
711		}
712		else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
713		{
714			if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol))
715			{
716				nssym = dyld_NSLookupSymbolInImage(dls->lib,
717												   symbol,
718												   NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
719												   NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
720			}
721			else if (NSIsSymbolNameDefined(symbol))
722			{
723				debug("Searching dependencies");
724				savedErrorStr = dyld_error_str();
725				nssym = search_linked_libs(dls->lib, symbol);
726			}
727		}
728		else if (dls->module == MAGIC_DYLIB_MOD)
729		{
730			/* Global context, use NSLookupAndBindSymbol */
731			if (NSIsSymbolNameDefined(symbol))
732			{
733				/* There doesn't seem to be a return on error option for this call???
734				   this is potentially broken, if binding fails, it will improperly
735				   exit the application. */
736				nssym = NSLookupAndBindSymbol(symbol);
737			}
738			else
739			{
740				if (savedErrorStr)
741					SDL_free(savedErrorStr);
742				savedErrorStr = SDL_malloc(256);
743				SDL_snprintf(savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol);
744			}
745		}
746	}
747	/* Error reporting */
748	if (!nssym)
749	{
750		if (!savedErrorStr || !SDL_strlen(savedErrorStr))
751		{
752			if (savedErrorStr)
753				SDL_free(savedErrorStr);
754			savedErrorStr = SDL_malloc(256);
755			SDL_snprintf(savedErrorStr, 256,"Symbol \"%s\" not found",symbol);
756		}
757		if (canSetError)
758		{
759			error(savedErrorStr);
760		}
761		else
762		{
763			debug(savedErrorStr);
764		}
765		if (savedErrorStr)
766			SDL_free(savedErrorStr);
767		return NULL;
768	}
769	return NSAddressOfSymbol(nssym);
770}
771
772static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode)
773{
774	NSObjectFileImage ofi = 0;
775	NSObjectFileImageReturnCode ofirc;
776	struct dlstatus *dls;
777	NSLinkEditErrors ler;
778	int lerno;
779	const char *errstr;
780	const char *file;
781	void (*init) (void);
782
783	ofirc = NSCreateObjectFileImageFromFile(path, &ofi);
784	switch (ofirc)
785	{
786		case NSObjectFileImageSuccess:
787			break;
788		case NSObjectFileImageInappropriateFile:
789			if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
790			{
791				if (isFlagSet(mode, RTLD_LOCAL))
792				{
793					warning("trying to open a .dylib with RTLD_LOCAL");
794					error("unable to open this file with RTLD_LOCAL");
795					return NULL;
796				}
797			}
798			else
799			{
800				error("opening this file is unsupported on this system");
801				return NULL;
802			}
803			break;
804		case NSObjectFileImageFailure:
805			error("object file setup failure");
806			return NULL;
807		case NSObjectFileImageArch:
808			error("no object for this architecture");
809			return NULL;
810		case NSObjectFileImageFormat:
811			error("bad object file format");
812			return NULL;
813		case NSObjectFileImageAccess:
814			error("can't read object file");
815			return NULL;
816		default:
817			error("unknown error from NSCreateObjectFileImageFromFile()");
818			return NULL;
819	}
820	dls = lookupStatus(sbuf);
821	if (!dls)
822	{
823		dls = allocStatus();
824	}
825	if (!dls)
826	{
827		error("unable to allocate memory");
828		return NULL;
829	}
830	//	dls->lib = 0;
831	if (ofirc == NSObjectFileImageInappropriateFile)
832	{
833		if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR)))
834		{
835			debug("Dynamic lib loaded at %ld", dls->lib);
836			ofi = MAGIC_DYLIB_OFI;
837			dls->module = MAGIC_DYLIB_MOD;
838			ofirc = NSObjectFileImageSuccess;
839			/* Although it is possible with a bit of work to modify this so it works and
840			   functions with RTLD_NOW, I don't deem it necessary at the moment */
841		}
842		if (!(dls->module))
843		{
844			NSLinkEditError(&ler, &lerno, &file, &errstr);
845			if (!errstr || (!SDL_strlen(errstr)))
846				error("Can't open this file type");
847			else
848				error(errstr);
849			if ((dls->flags & DL_IN_LIST) == 0)
850			{
851				SDL_free(dls);
852			}
853			return NULL;
854		}
855	}
856	else
857	{
858		dls->module = NSLinkModule(ofi, path,
859								   NSLINKMODULE_OPTION_RETURN_ON_ERROR |
860								   NSLINKMODULE_OPTION_PRIVATE |
861								   (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0));
862		NSDestroyObjectFileImage(ofi);
863		if (dls->module)
864		{
865			dls->lib = get_mach_header_from_NSModule(dls->module);
866		}
867	}
868	if (!dls->module)
869	{
870		NSLinkEditError(&ler, &lerno, &file, &errstr);
871		if ((dls->flags & DL_IN_LIST) == 0)
872		{
873			SDL_free(dls);
874		}
875		error(errstr);
876		return NULL;
877	}
878
879	insertStatus(dls, sbuf);
880	dls = reference(dls, mode);
881	if ((init = dlsymIntern(dls, "__init", 0)))
882	{
883		debug("calling _init()");
884		init();
885	}
886	return dls;
887}
888
889inline static void dlcompat_init_check(void)
890{
891	static pthread_mutex_t l = PTHREAD_MUTEX_INITIALIZER;
892	static int init_done = 0;
893
894	pthread_mutex_lock(&l);
895	if (!init_done) {
896		dlcompat_init_func();
897		init_done = 1;
898	}
899	pthread_mutex_unlock(&l);
900}
901
902static void dlcompat_init_func(void)
903{
904        _dyld_func_lookup("__dyld_NSAddImage", (void **)&dyld_NSAddImage);
905	_dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage",
906			  (void **)&dyld_NSIsSymbolNameDefinedInImage);
907	_dyld_func_lookup("__dyld_NSLookupSymbolInImage", (void **)&dyld_NSLookupSymbolInImage);
908	if (pthread_mutex_init(&dlcompat_mutex, NULL))
909	    exit(1);
910	if (pthread_key_create(&dlerror_key, &dlerrorfree))
911	    exit(1);
912}
913
914static void resetdlerror()
915{
916	struct dlthread *tss;
917	tss = pthread_getspecific(dlerror_key);
918	tss->errset = 0;
919}
920
921static void dlerrorfree(void *data)
922{
923	SDL_free(data);
924}
925
926/* We kind of want a recursive lock here, but meet a little trouble
927 * because they are not available pre OS X 10.2, so we fake it
928 * using thread specific storage to keep a lock count
929 */
930static inline void dolock(void)
931{
932	int err = 0;
933	struct dlthread *tss;
934	dlcompat_init_check();
935	tss = pthread_getspecific(dlerror_key);
936	if (!tss)
937	{
938		tss = SDL_malloc(sizeof(struct dlthread));
939		tss->lockcnt = 0;
940		tss->errset = 0;
941		if (pthread_setspecific(dlerror_key, tss))
942		{
943			fprintf(stderr,"dlcompat: pthread_setspecific failed\n");
944			exit(1);
945		}
946	}
947	if (!tss->lockcnt)
948		err = pthread_mutex_lock(&dlcompat_mutex);
949	tss->lockcnt = tss->lockcnt +1;
950	if (err)
951		exit(err);
952}
953
954static inline void dounlock(void)
955{
956	int err = 0;
957	struct dlthread *tss;
958	tss = pthread_getspecific(dlerror_key);
959	tss->lockcnt = tss->lockcnt -1;
960	if (!tss->lockcnt)
961		err = pthread_mutex_unlock(&dlcompat_mutex);
962	if (err)
963		exit(err);
964}
965
966static void *SDL_OSX_dlopen(const char *path, int mode)
967{
968	const struct stat *sbuf;
969	struct dlstatus *dls;
970	const char *fullPath;
971
972	dolock();
973	resetdlerror();
974	if (!path)
975	{
976		dls = &mainStatus;
977		goto dlopenok;
978	}
979	if (!(sbuf = findFile(path, &fullPath)))
980	{
981		error("file \"%s\" not found", path);
982		goto dlopenerror;
983	}
984	/* Now checks that it hasn't been closed already */
985	if ((dls = lookupStatus(sbuf)) && (dls->refs > 0))
986	{
987		/* debug("status found"); */
988		dls = reference(dls, mode);
989		goto dlopenok;
990	}
991#ifdef 	RTLD_NOLOAD
992	if (isFlagSet(mode, RTLD_NOLOAD))
993	{
994		error("no existing handle and RTLD_NOLOAD specified");
995		goto dlopenerror;
996	}
997#endif
998	if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW))
999	{
1000		error("how can I load something both RTLD_LAZY and RTLD_NOW?");
1001		goto dlopenerror;
1002	}
1003	dls = loadModule(fullPath, sbuf, mode);
1004
1005  dlopenok:
1006	dounlock();
1007	return (void *)dls;
1008  dlopenerror:
1009	dounlock();
1010	return NULL;
1011}
1012
1013#if !FINK_BUILD
1014static void *SDL_OSX_dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
1015{
1016	int sym_len = SDL_strlen(symbol);
1017	void *value = NULL;
1018	char *malloc_sym = NULL;
1019	dolock();
1020	malloc_sym = SDL_malloc(sym_len + 2);
1021	if (malloc_sym)
1022	{
1023		SDL_snprintf(malloc_sym, sym_len+2, "_%s", symbol);
1024		value = dlsymIntern(handle, malloc_sym, 1);
1025		SDL_free(malloc_sym);
1026	}
1027	else
1028	{
1029		error("Unable to allocate memory");
1030		goto dlsymerror;
1031	}
1032	dounlock();
1033	return value;
1034  dlsymerror:
1035	dounlock();
1036	return NULL;
1037}
1038#endif
1039
1040#if FINK_BUILD
1041
1042static void *dlsym_prepend_underscore(void *handle, const char *symbol)
1043{
1044	void *answer;
1045	dolock();
1046	answer = dlsym_prepend_underscore_intern(handle, symbol);
1047	dounlock();
1048	return answer;
1049}
1050
1051static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol)
1052{
1053/*
1054 *	A quick and easy way for porting packages which call dlsym(handle,"sym")
1055 *	If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then
1056 *	this function will be called, and will add the required underscore.
1057 *
1058 *	Note that I haven't figured out yet which should be "standard", prepend
1059 *	the underscore always, or not at all. These global functions need to go away
1060 *	for opendarwin.
1061 */
1062	int sym_len = SDL_strlen(symbol);
1063	void *value = NULL;
1064	char *malloc_sym = NULL;
1065	malloc_sym = SDL_malloc(sym_len + 2);
1066	if (malloc_sym)
1067	{
1068		SDL_snprintf(malloc_sym, sym_len+2, "_%s", symbol);
1069		value = dlsymIntern(handle, malloc_sym, 1);
1070		SDL_free(malloc_sym);
1071	}
1072	else
1073	{
1074		error("Unable to allocate memory");
1075	}
1076	return value;
1077}
1078
1079static void *dlsym_auto_underscore(void *handle, const char *symbol)
1080{
1081	void *answer;
1082	dolock();
1083	answer = dlsym_auto_underscore_intern(handle, symbol);
1084	dounlock();
1085	return answer;
1086
1087}
1088static void *dlsym_auto_underscore_intern(void *handle, const char *symbol)
1089{
1090	struct dlstatus *dls = handle;
1091	void *addr = 0;
1092	addr = dlsymIntern(dls, symbol, 0);
1093	if (!addr)
1094		addr = dlsym_prepend_underscore_intern(handle, symbol);
1095	return addr;
1096}
1097
1098
1099static void *SDL_OSX_dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
1100{
1101	struct dlstatus *dls = handle;
1102	void *addr = 0;
1103	dolock();
1104	addr = dlsymIntern(dls, symbol, 1);
1105	dounlock();
1106	return addr;
1107}
1108#endif
1109
1110static int SDL_OSX_dlclose(void *handle)
1111{
1112	struct dlstatus *dls = handle;
1113	dolock();
1114	resetdlerror();
1115	if (!isValidStatus(dls))
1116	{
1117		goto dlcloseerror;
1118	}
1119	if (dls->module == MAGIC_DYLIB_MOD)
1120	{
1121		const char *name;
1122		if (!dls->lib)
1123		{
1124			name = "global context";
1125		}
1126		else
1127		{
1128			name = get_lib_name(dls->lib);
1129		}
1130		warning("trying to close a .dylib!");
1131		error("Not closing \"%s\" - dynamic libraries cannot be closed", name);
1132		goto dlcloseerror;
1133	}
1134	if (!dls->module)
1135	{
1136		error("module already closed");
1137		goto dlcloseerror;
1138	}
1139
1140	if (dls->refs == 1)
1141	{
1142		unsigned long options = 0;
1143		void (*fini) (void);
1144		if ((fini = dlsymIntern(dls, "__fini", 0)))
1145		{
1146			debug("calling _fini()");
1147			fini();
1148		}
1149		options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
1150#ifdef RTLD_NODELETE
1151		if (isFlagSet(dls->mode, RTLD_NODELETE))
1152			options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
1153#endif
1154		if (!NSUnLinkModule(dls->module, options))
1155		{
1156			error("unable to unlink module");
1157			goto dlcloseerror;
1158		}
1159		dls->refs--;
1160		dls->module = 0;
1161		/* Note: the dlstatus struct dls is neither removed from the list
1162		 * nor is the memory it occupies freed. This shouldn't pose a
1163		 * problem in mostly all cases, though.
1164		 */
1165	}
1166	dounlock();
1167	return 0;
1168  dlcloseerror:
1169	dounlock();
1170	return 1;
1171}
1172
1173static const char *SDL_OSX_dlerror(void)
1174{
1175	struct dlthread  *tss;
1176	const char * err_str = NULL;
1177	dlcompat_init_check();
1178	tss = pthread_getspecific(dlerror_key);
1179	if (tss != NULL && tss->errset != 0) {
1180		tss->errset = 0;
1181		err_str = tss->errstr;
1182	}
1183	return (err_str);
1184}
1185
1186/* Given an address, return the mach_header for the image containing it
1187 * or zero if the given address is not contained in any loaded images.
1188 */
1189static const struct mach_header *image_for_address(const void *address)
1190{
1191	unsigned long i;
1192	unsigned long j;
1193	unsigned long count = _dyld_image_count();
1194	const struct mach_header *mh = 0;
1195	struct load_command *lc = 0;
1196	unsigned long addr = 0;
1197	for (i = 0; i < count; i++)
1198	{
1199		addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i);
1200		mh = _dyld_get_image_header(i);
1201		if (mh)
1202		{
1203			lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1204			for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
1205			{
1206				if (LC_SEGMENT == lc->cmd &&
1207					addr >= ((struct segment_command *)lc)->vmaddr &&
1208					addr <
1209					((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
1210				{
1211					goto image_found;
1212				}
1213			}
1214		}
1215		mh = 0;
1216	}
1217  image_found:
1218	return mh;
1219}
1220
1221#if 0 /* unused */
1222static int SDL_OSX_dladdr(const void * dl_restrict p, SDL_OSX_Dl_info * dl_restrict info)
1223{
1224/*
1225	FIXME: USe the routine image_for_address.
1226*/
1227	unsigned long i;
1228	unsigned long j;
1229	unsigned long count = _dyld_image_count();
1230	struct mach_header *mh = 0;
1231	struct load_command *lc = 0;
1232	unsigned long addr = NULL;
1233	unsigned long table_off = (unsigned long)0;
1234	int found = 0;
1235	if (!info)
1236		return 0;
1237	dolock();
1238	resetdlerror();
1239	info->dli_fname = 0;
1240	info->dli_fbase = 0;
1241	info->dli_sname = 0;
1242	info->dli_saddr = 0;
1243/* Some of this was swiped from code posted by Douglas Davidson <ddavidso AT apple DOT com>
1244 * to darwin-development AT lists DOT apple DOT com and slightly modified
1245 */
1246	for (i = 0; i < count; i++)
1247	{
1248		addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i);
1249		mh = _dyld_get_image_header(i);
1250		if (mh)
1251		{
1252			lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1253			for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
1254			{
1255				if (LC_SEGMENT == lc->cmd &&
1256					addr >= ((struct segment_command *)lc)->vmaddr &&
1257					addr <
1258					((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
1259				{
1260					info->dli_fname = _dyld_get_image_name(i);
1261					info->dli_fbase = (void *)mh;
1262					found = 1;
1263					break;
1264				}
1265			}
1266			if (found)
1267				break;
1268		}
1269	}
1270	if (!found)
1271	{
1272		dounlock();
1273		return 0;
1274	}
1275	lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1276	for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
1277	{
1278		if (LC_SEGMENT == lc->cmd)
1279		{
1280			if (!SDL_strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT"))
1281				break;
1282		}
1283	}
1284	table_off =
1285		((unsigned long)((struct segment_command *)lc)->vmaddr) -
1286		((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i);
1287	debug("table off %x", table_off);
1288
1289	lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1290	for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
1291	{
1292		if (LC_SYMTAB == lc->cmd)
1293		{
1294
1295			struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off);
1296			unsigned long numsyms = ((struct symtab_command *)lc)->nsyms;
1297			struct nlist *nearest = NULL;
1298			unsigned long diff = 0xffffffff;
1299			unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off);
1300			debug("symtable %x", symtable);
1301			for (i = 0; i < numsyms; i++)
1302			{
1303				/* Ignore the following kinds of Symbols */
1304				if ((!symtable->n_value)	/* Undefined */
1305					|| (symtable->n_type >= N_PEXT)	/* Debug symbol */
1306					|| (!(symtable->n_type & N_EXT))	/* Local Symbol */
1307					)
1308				{
1309					symtable++;
1310					continue;
1311				}
1312				if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr)))
1313				{
1314					diff = (unsigned long)symtable->n_value - addr;
1315					nearest = symtable;
1316				}
1317				symtable++;
1318			}
1319			if (nearest)
1320			{
1321				info->dli_saddr = nearest->n_value + ((void *)p - addr);
1322				info->dli_sname = (char *)(strtable + nearest->n_un.n_strx);
1323			}
1324		}
1325	}
1326	dounlock();
1327	return 1;
1328}
1329#endif
1330
1331/*
1332 * Implement the dlfunc() interface, which behaves exactly the same as
1333 * dlsym() except that it returns a function pointer instead of a data
1334 * pointer.  This can be used by applications to avoid compiler warnings
1335 * about undefined behavior, and is intended as prior art for future
1336 * POSIX standardization.  This function requires that all pointer types
1337 * have the same representation, which is true on all platforms FreeBSD
1338 * runs on, but is not guaranteed by the C standard.
1339 */
1340#if 0
1341static dlfunc_t SDL_OSX_dlfunc(void * dl_restrict handle, const char * dl_restrict symbol)
1342{
1343	union
1344	{
1345		void *d;
1346		dlfunc_t f;
1347	} rv;
1348	int sym_len = SDL_strlen(symbol);
1349	char *malloc_sym = NULL;
1350	dolock();
1351	malloc_sym = SDL_malloc(sym_len + 2);
1352	if (malloc_sym)
1353	{
1354		SDL_snprintf(malloc_sym, sym_len+2, "_%s", symbol);
1355		rv.d = dlsymIntern(handle, malloc_sym, 1);
1356		SDL_free(malloc_sym);
1357	}
1358	else
1359	{
1360		error("Unable to allocate memory");
1361		goto dlfuncerror;
1362	}
1363	dounlock();
1364	return rv.f;
1365  dlfuncerror:
1366	dounlock();
1367	return NULL;
1368}
1369#endif
1370
1371
1372
1373/* dlcompat ends, here's the SDL interface...  --ryan.  */
1374
1375
1376/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1377/* System dependent library loading routines                           */
1378
1379#include "SDL_loadso.h"
1380
1381void *SDL_LoadObject(const char *sofile)
1382{
1383	void *handle = SDL_OSX_dlopen(sofile, RTLD_NOW);
1384	const char *loaderror = SDL_OSX_dlerror();
1385	if ( handle == NULL ) {
1386		SDL_SetError("Failed loading %s: %s", sofile, loaderror);
1387	}
1388	return(handle);
1389}
1390
1391void *SDL_LoadFunction(void *handle, const char *name)
1392{
1393	void *symbol = SDL_OSX_dlsym(handle, name);
1394	if ( symbol == NULL ) {
1395		SDL_SetError("Failed loading %s: %s", name, SDL_OSX_dlerror());
1396	}
1397	return(symbol);
1398}
1399
1400void SDL_UnloadObject(void *handle)
1401{
1402	if ( handle != NULL ) {
1403		SDL_OSX_dlclose(handle);
1404	}
1405}
1406
1407#endif /* SDL_LOADSO_DLCOMPAT */
1408