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