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