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