SDL-1.2.14
[sdl_omap.git] / src / loadso / macosx / SDL_dlcompat.c
CommitLineData
e14743d1 1/*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 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 */