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 /*
37 Copyright (c) 2002 Jorge Acereda  <jacereda@users.sourceforge.net> &
38                    Peter O'Gorman <ogorman@users.sourceforge.net>
39 
40 Portions may be copyright others, see the AUTHORS file included with this
41 distribution.
42 
43 Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
44 
45 Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
46 
47 Permission is hereby granted, free of charge, to any person obtaining
48 a copy of this software and associated documentation files (the
49 "Software"), to deal in the Software without restriction, including
50 without limitation the rights to use, copy, modify, merge, publish,
51 distribute, sublicense, and/or sell copies of the Software, and to
52 permit persons to whom the Software is furnished to do so, subject to
53 the following conditions:
54 
55 The above copyright notice and this permission notice shall be
56 included in all copies or substantial portions of the Software.
57 
58 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
59 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
60 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
61 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
62 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
63 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
64 WITH 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
88 extern "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  */
102 typedef 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 
109 static int SDL_OSX_dladdr(const void * dl_restrict, SDL_OSX_Dl_info * dl_restrict);
110 #endif /* ! _POSIX_SOURCE */
111 #endif /* 0 */
112 
113 static int SDL_OSX_dlclose(void * handle);
114 static const char * SDL_OSX_dlerror(void);
115 static void * SDL_OSX_dlopen(const char *path, int mode);
116 static 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 */
165 static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0;
166 static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0;
167 static 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 */
190 static pthread_mutex_t dlcompat_mutex;
191 /* Our thread specific storage
192  */
193 static pthread_key_t dlerror_key;
194 
195 struct 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  */
205 struct 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 */
218 static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 };
219 static 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 */
227 static void debug(const char *fmt, ...);
228 static void error(const char *str, ...);
229 static const char *safegetenv(const char *s);
230 static const char *searchList(void);
231 static const char *getSearchPath(int i);
232 static const char *getFullPath(int i, const char *file);
233 static const struct stat *findFile(const char *file, const char **fullPath);
234 static int isValidStatus(struct dlstatus *status);
235 static inline int isFlagSet(int mode, int flag);
236 static struct dlstatus *lookupStatus(const struct stat *sbuf);
237 static void insertStatus(struct dlstatus *dls, const struct stat *sbuf);
238 static int promoteLocalToGlobal(struct dlstatus *dls);
239 static void *reference(struct dlstatus *dls, int mode);
240 static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError);
241 static struct dlstatus *allocStatus(void);
242 static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode);
243 static NSSymbol search_linked_libs(const struct mach_header *mh, const char *symbol);
244 static const char *get_lib_name(const struct mach_header *mh);
245 static const struct mach_header *get_mach_header_from_NSModule(NSModule mod);
246 static void dlcompat_init_func(void);
247 static inline void dlcompat_init_check(void);
248 static inline void dolock(void);
249 static inline void dounlock(void);
250 static void dlerrorfree(void *data);
251 static void resetdlerror(void);
252 static const struct mach_header *my_find_image(const char *name);
253 static const struct mach_header *image_for_address(const void *address);
254 static inline char *dyld_error_str(void);
255 
256 #if FINK_BUILD
257 /* Two Global Functions */
258 static void *dlsym_prepend_underscore(void *handle, const char *symbol);
259 static void *dlsym_auto_underscore(void *handle, const char *symbol);
260 
261 /* And their _intern counterparts */
262 static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol);
263 static void *dlsym_auto_underscore_intern(void *handle, const char *symbol);
264 #endif
265 
266 /* Functions */
267 
debug(const char * fmt,...)268 static 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 
error(const char * str,...)281 static 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 
warning(const char * str)296 static void warning(const char *str)
297 {
298 #if DEBUG > 0
299 	fprintf(stderr, "WARNING: dlcompat: %s\n", str);
300 #endif
301 }
302 
safegetenv(const char * s)303 static 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  */
get_lib_name(const struct mach_header * mh)313 static 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  */
get_mach_header_from_NSModule(NSModule mod)336 static 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  */
searchList()363 static 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() */
getSearchPath(int i)383 static 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 
getFullPath(int i,const char * file)429 static 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  */
findFile(const char * file,const char ** fullPath)444 static 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 */
isValidStatus(struct dlstatus * status)468 static 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 
isFlagSet(int mode,int flag)483 static inline int isFlagSet(int mode, int flag)
484 {
485 	return (mode & flag) == flag;
486 }
487 
lookupStatus(const struct stat * sbuf)488 static 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 
insertStatus(struct dlstatus * dls,const struct stat * sbuf)498 static 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 
allocStatus()513 static 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 
promoteLocalToGlobal(struct dlstatus * dls)526 static 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 
reference(struct dlstatus * dls,int mode)535 static 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 
my_find_image(const char * name)560 static 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  */
search_linked_libs(const struct mach_header * mh,const char * symbol)590 static 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 */
dyld_error_str()628 static 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 
dlsymIntern(struct dlstatus * dls,const char * symbol,int canSetError)643 static 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 
loadModule(const char * path,const struct stat * sbuf,int mode)772 static 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 
dlcompat_init_check(void)889 inline 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 
dlcompat_init_func(void)902 static 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 
resetdlerror()914 static void resetdlerror()
915 {
916 	struct dlthread *tss;
917 	tss = pthread_getspecific(dlerror_key);
918 	tss->errset = 0;
919 }
920 
dlerrorfree(void * data)921 static 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  */
dolock(void)930 static 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 
dounlock(void)954 static 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 
SDL_OSX_dlopen(const char * path,int mode)966 static 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
SDL_OSX_dlsym(void * dl_restrict handle,const char * dl_restrict symbol)1014 static 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 
dlsym_prepend_underscore(void * handle,const char * symbol)1042 static 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 
dlsym_prepend_underscore_intern(void * handle,const char * symbol)1051 static 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 
dlsym_auto_underscore(void * handle,const char * symbol)1079 static 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 }
dlsym_auto_underscore_intern(void * handle,const char * symbol)1088 static 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 
SDL_OSX_dlsym(void * dl_restrict handle,const char * dl_restrict symbol)1099 static 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 
SDL_OSX_dlclose(void * handle)1110 static 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 
SDL_OSX_dlerror(void)1173 static 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  */
image_for_address(const void * address)1189 static 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 */
1222 static 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
1341 static 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 
SDL_LoadObject(const char * sofile)1381 void *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 
SDL_LoadFunction(void * handle,const char * name)1391 void *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 
SDL_UnloadObject(void * handle)1400 void SDL_UnloadObject(void *handle)
1401 {
1402 	if ( handle != NULL ) {
1403 		SDL_OSX_dlclose(handle);
1404 	}
1405 }
1406 
1407 #endif /* SDL_LOADSO_DLCOMPAT */
1408