SDL-1.2.14
[sdl_omap.git] / src / loadso / macosx / SDL_dlcompat.c
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 /*
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
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
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
296 static void warning(const char *str)
297 {
298 #if DEBUG > 0
299         fprintf(stderr, "WARNING: dlcompat: %s\n", str);
300 #endif
301 }
302
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  */
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  */
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  */
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() */
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
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  */
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 */
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
483 static inline int isFlagSet(int mode, int flag)
484 {
485         return (mode & flag) == flag;
486 }
487
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
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
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
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
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
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  */
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 */
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
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
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
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
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
914 static void resetdlerror()
915 {
916         struct dlthread *tss;
917         tss = pthread_getspecific(dlerror_key);
918         tss->errset = 0;
919 }
920
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  */ 
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
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
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
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
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
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
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 }
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
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
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
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  */
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
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
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
1400 void SDL_UnloadObject(void *handle)
1401 {
1402         if ( handle != NULL ) {
1403                 SDL_OSX_dlclose(handle);
1404         }
1405 }
1406
1407 #endif /* SDL_LOADSO_DLCOMPAT */