Merged revisions 127663 via svnmerge from
[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                 retStr = ast_malloc(strlen(dylderrstr) + 1);
549                 strcpy((char*)retStr, dylderrstr);
550         }
551         return retStr;
552 }
553
554 static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError)
555 {
556         NSSymbol *nssym = 0;
557         void *caller = __builtin_return_address(1);     /* Be *very* careful about inlining */
558         const struct mach_header *caller_mh = 0;
559         const char* savedErrorStr = NULL;
560         resetdlerror();
561 #ifndef RTLD_SELF
562 #define RTLD_SELF               ((void *) -3)
563 #endif
564         if (NULL == dls)
565                 dls = RTLD_SELF;
566         if ((RTLD_NEXT == dls) || (RTLD_SELF == dls)) {
567                 if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) {
568                         caller_mh = image_for_address(caller);
569                         if (RTLD_SELF == dls) {
570                                 /* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE
571                                  * But it appears to work anyway, and looking at the code in dyld_libfuncs.c
572                                  * this is acceptable.
573                                  */
574                                 if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol)) {
575                                         nssym = dyld_NSLookupSymbolInImage(caller_mh,
576                                                                                                            symbol,
577                                                                                                            NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
578                                                                                                            NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
579                                 }
580                         }
581                         if (!nssym) {
582                                 if (RTLD_SELF == dls)
583                                         savedErrorStr = dyld_error_str();
584                                 nssym = search_linked_libs(caller_mh, symbol);
585                         }
586                 } else {
587                         if (canSetError)
588                                 error("RTLD_SELF and RTLD_NEXT are not supported");
589                         return NULL;
590                 }
591         }
592         if (!nssym) {
593
594                 if (RTLD_DEFAULT == dls) {
595                         dls = &mainStatus;
596                 }
597                 if (!isValidStatus(dls))
598                         return NULL;
599
600                 if (dls->module != MAGIC_DYLIB_MOD) {
601                         nssym = NSLookupSymbolInModule(dls->module, symbol);
602                         if (!nssym && NSIsSymbolNameDefined(symbol))
603                         {
604                                 debug("Searching dependencies");
605                                 savedErrorStr = dyld_error_str();
606                                 nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol);
607                         }
608                 } else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) {
609                         if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol)) {
610                                 nssym = dyld_NSLookupSymbolInImage(dls->lib,
611                                                                                                    symbol,
612                                                                                                    NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
613                                                                                                    NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
614                         } else if (NSIsSymbolNameDefined(symbol)) {
615                                 debug("Searching dependencies");
616                                 savedErrorStr = dyld_error_str();
617                                 nssym = search_linked_libs(dls->lib, symbol);
618                         }
619                 } else if (dls->module == MAGIC_DYLIB_MOD) {
620                         /* Global context, use NSLookupAndBindSymbol */
621                         if (NSIsSymbolNameDefined(symbol)) {
622                                 /* There doesn't seem to be a return on error option for this call???
623                                    this is potentially broken, if binding fails, it will improperly
624                                    exit the application. */
625                                 nssym = NSLookupAndBindSymbol(symbol);
626                         } else {
627                                 if (savedErrorStr)
628                                         ast_free(savedErrorStr);                        
629                                 savedErrorStr = ast_malloc(256);
630                                 snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context", symbol);     
631                         }
632                 }
633         }
634         /* Error reporting */
635         if (!nssym) {
636                 if (!savedErrorStr || !strlen(savedErrorStr)) {
637                         if (savedErrorStr)
638                                 ast_free(savedErrorStr);
639                         savedErrorStr = ast_malloc(256);
640                         snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not found", symbol);
641                 }
642                 if (canSetError) {
643                         error(savedErrorStr);
644                 } else {
645                         debug(savedErrorStr);
646                 }
647                 if (savedErrorStr)
648                         ast_free(savedErrorStr);
649                 return NULL;
650         }
651         return NSAddressOfSymbol(nssym);
652 }
653
654 static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode)
655 {
656         NSObjectFileImage ofi = 0;
657         NSObjectFileImageReturnCode ofirc;
658         struct dlstatus *dls;
659         NSLinkEditErrors ler;
660         int lerno;
661         const char *errstr;
662         const char *file;
663         void (*init) (void);
664         ofirc = NSCreateObjectFileImageFromFile(path, &ofi);
665         switch (ofirc) {
666         case NSObjectFileImageSuccess:
667                 break;
668         case NSObjectFileImageInappropriateFile:
669                 if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) {
670                         if (!isFlagSet(mode, RTLD_GLOBAL)) {
671                                 warning("trying to open a .dylib with RTLD_LOCAL");
672                                 error("unable to open this file with RTLD_LOCAL");
673                                 return NULL;
674                         }
675                 } else {
676                         error("opening this file is unsupported on this system");
677                         return NULL;
678                 }
679                 break;
680         case NSObjectFileImageFailure:
681                 error("object file setup failure");
682                 return NULL;
683         case NSObjectFileImageArch:
684                 error("no object for this architecture");
685                 return NULL;
686         case NSObjectFileImageFormat:
687                 error("bad object file format");
688                 return NULL;
689         case NSObjectFileImageAccess:
690                 error("can't read object file");
691                 return NULL;
692         default:
693                 error("unknown error from NSCreateObjectFileImageFromFile()");
694                 return NULL;
695         }
696         dls = lookupStatus(sbuf);
697         if (!dls) {
698                 dls = allocStatus();
699         }
700         if (!dls) {
701                 error("unable to allocate memory");
702                 return NULL;
703         }
704         dls->lib = 0;
705         if (ofirc == NSObjectFileImageInappropriateFile) {
706                 if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR))) {
707                         debug("Dynamic lib loaded at %ld", dls->lib);
708                         ofi = MAGIC_DYLIB_OFI;
709                         dls->module = MAGIC_DYLIB_MOD;
710                         ofirc = NSObjectFileImageSuccess;
711                         /* Although it is possible with a bit of work to modify this so it works and
712                            functions with RTLD_NOW, I don't deem it necessary at the moment */
713                 }
714                 if (!(dls->module)) {
715                         NSLinkEditError(&ler, &lerno, &file, &errstr);
716                         if (!errstr || (!strlen(errstr)))
717                                 error("Can't open this file type");
718                         else
719                                 error(errstr);
720                         if ((dls->flags & DL_IN_LIST) == 0)
721                         {
722                                 ast_free(dls);
723                         }
724                         return NULL;
725                 }
726         } else {
727                 dls->module = NSLinkModule(ofi, path,
728                                                                    NSLINKMODULE_OPTION_RETURN_ON_ERROR |
729                                                                    NSLINKMODULE_OPTION_PRIVATE |
730                                                                    (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0));
731                 NSDestroyObjectFileImage(ofi);
732                 if (dls->module) {
733                         dls->lib = get_mach_header_from_NSModule(dls->module);
734                 }
735         }
736         if (!dls->module) {
737                 NSLinkEditError(&ler, &lerno, &file, &errstr);
738                 if ((dls->flags & DL_IN_LIST) == 0) {
739                         ast_free(dls);
740                 }
741                 error(errstr);
742                 return NULL;
743         }
744
745         insertStatus(dls, sbuf);
746         dls = reference(dls, mode);
747         if ((init = dlsymIntern(dls, "__init", 0))) {
748                 debug("calling _init()");
749                 init();
750         }
751         return dls;
752 }
753
754 static void dlcompat_init_func(void)
755 {
756         static int inited = 0;
757         if (!inited) {
758                 inited = 1;
759                 _dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage);
760                 _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage",
761                                                   (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage);
762                 _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage);
763                 if (pthread_mutex_init(&dlcompat_mutex, NULL))
764                         exit(1);
765                 if (pthread_key_create(&dlerror_key, &dlerrorfree))
766                         exit(1);
767                 /* And be neat and tidy and clean up after ourselves */ 
768                 atexit(dlcompat_cleanup);
769         }
770 }
771
772 #if 0
773 #pragma CALL_ON_LOAD dlcompat_init_func
774 #endif
775
776 static void dlcompat_cleanup(void)
777 {
778         struct dlstatus *dls;
779         struct dlstatus *next;
780         char *data;
781         data = (char *)searchList();
782         if ( data )
783                 ast_free(data);
784         data =  (char *)getSearchPath(-1);
785         if ( data )
786                 ast_free(data);
787         pthread_mutex_destroy(&dlcompat_mutex);
788         pthread_key_delete(dlerror_key);
789         next = stqueue;
790         while (next && (next != &mainStatus)) {
791                 dls = next;
792                 next = dls->next;
793                 ast_free(dls);
794         }
795 }
796
797 static void resetdlerror()
798 {
799         struct dlthread *tss;
800         tss = pthread_getspecific(dlerror_key);
801         tss->errset = 0;
802 }
803
804 static void dlerrorfree(void *data)
805 {
806         ast_free(data);
807 }
808
809 /* We kind of want a recursive lock here, but meet a little trouble
810  * because they are not available pre OS X 10.2, so we fake it
811  * using thread specific storage to keep a lock count
812  */ 
813 static inline void dolock(void)
814 {
815         int err = 0;
816         struct dlthread *tss;
817         tss = pthread_getspecific(dlerror_key);
818         if (!tss) {
819                 tss = ast_malloc(sizeof(*tss));
820                 tss->lockcnt = 0;
821                 tss->errset = 0;
822                 if (pthread_setspecific(dlerror_key, tss)) {
823                         fprintf(stderr, "dlcompat: pthread_setspecific failed\n");
824                         exit(1);
825                 }
826         }
827         if (!tss->lockcnt)
828                 err = pthread_mutex_lock(&dlcompat_mutex);
829         tss->lockcnt = tss->lockcnt + 1;        
830         if (err)
831                 exit(err);
832 }
833
834 static inline void dounlock(void)
835 {
836         int err = 0;
837         struct dlthread *tss;
838         tss = pthread_getspecific(dlerror_key);
839         tss->lockcnt = tss->lockcnt -1;
840         if (!tss->lockcnt)
841                 err = pthread_mutex_unlock(&dlcompat_mutex);
842         if (err)
843                 exit(err);
844 }
845
846 void *dlopen(const char *path, int mode)
847 {
848         const struct stat *sbuf;
849         struct dlstatus *dls;
850         const char *fullPath;
851         dlcompat_init_func();           /* Just in case */
852         dolock();
853         resetdlerror();
854         if (!path) {
855                 dls = &mainStatus;
856                 goto dlopenok;
857         }
858         if (!(sbuf = findFile(path, &fullPath))) {
859                 error("file \"%s\" not found", path);
860                 goto dlopenerror;
861         }
862         /* Now checks that it hasn't been closed already */
863         if ((dls = lookupStatus(sbuf)) && (dls->refs > 0)) {
864                 /* debug("status found"); */
865                 dls = reference(dls, mode);
866                 goto dlopenok;
867         }
868 #ifdef  RTLD_NOLOAD
869         if (isFlagSet(mode, RTLD_NOLOAD)) {
870                 error("no existing handle and RTLD_NOLOAD specified");
871                 goto dlopenerror;
872         }
873 #endif
874         if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW)) {
875                 error("how can I load something both RTLD_LAZY and RTLD_NOW?");
876                 goto dlopenerror;
877         }
878         dls = loadModule(fullPath, sbuf, mode);
879         
880 dlopenok:
881         dounlock();
882         return (void *)dls;
883 dlopenerror:
884         dounlock();
885         return NULL;
886 }
887
888 #if !FINK_BUILD
889 void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
890 {
891         int sym_len = strlen(symbol);
892         void *value = NULL;
893         char *malloc_sym = NULL;
894         dolock();
895         malloc_sym = ast_malloc(sym_len + 2);
896         if (malloc_sym) {
897                 sprintf(malloc_sym, "_%s", symbol);
898                 value = dlsymIntern(handle, malloc_sym, 1);
899                 ast_free(malloc_sym);
900         } else {
901                 error("Unable to allocate memory");
902                 goto dlsymerror;
903         }
904         dounlock();
905         return value;
906 dlsymerror:
907         dounlock();
908         return NULL;
909 }
910 #endif
911
912 #if FINK_BUILD
913
914 void *dlsym_prepend_underscore(void *handle, const char *symbol)
915 {
916         void *answer;
917         dolock();
918         answer = dlsym_prepend_underscore_intern(handle, symbol);
919         dounlock();
920         return answer;
921 }
922
923 static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol)
924 {
925 /*
926  *      A quick and easy way for porting packages which call dlsym(handle,"sym")
927  *      If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then
928  *      this function will be called, and will add the required underscore.
929  *      
930  *      Note that I haven't figured out yet which should be "standard", prepend
931  *      the underscore always, or not at all. These global functions need to go away
932  *      for opendarwin.
933  */
934         int sym_len = strlen(symbol);
935         void *value = NULL;
936         char *malloc_sym = NULL;
937         malloc_sym = ast_malloc(sym_len + 2);
938         if (malloc_sym) {
939                 sprintf(malloc_sym, "_%s", symbol);
940                 value = dlsymIntern(handle, malloc_sym, 1);
941                 ast_free(malloc_sym);
942         } else {
943                 error("Unable to allocate memory");
944         }
945         return value;
946 }
947
948 void *dlsym_auto_underscore(void *handle, const char *symbol)
949 {
950         void *answer;
951         dolock();
952         answer = dlsym_auto_underscore_intern(handle, symbol);
953         dounlock();
954         return answer;
955
956 }
957 static void *dlsym_auto_underscore_intern(void *handle, const char *symbol)
958 {
959         struct dlstatus *dls = handle;
960         void *addr = 0;
961         addr = dlsymIntern(dls, symbol, 0);
962         if (!addr)
963                 addr = dlsym_prepend_underscore_intern(handle, symbol);
964         return addr;
965 }
966
967
968 void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
969 {
970         struct dlstatus *dls = handle;
971         void *addr = 0;
972         dolock();
973         addr = dlsymIntern(dls, symbol, 1);
974         dounlock();
975         return addr;
976 }
977 #endif
978
979 int dlclose(void *handle)
980 {
981         struct dlstatus *dls = handle;
982         dolock();
983         resetdlerror();
984         if (!isValidStatus(dls)) {
985                 goto dlcloseerror;
986         }
987         if (dls->module == MAGIC_DYLIB_MOD) {
988                 const char *name;
989                 if (!dls->lib) {
990                         name = "global context";
991                 } else {
992                         name = get_lib_name(dls->lib);
993                 }
994                 warning("trying to close a .dylib!");
995                 error("Not closing \"%s\" - dynamic libraries cannot be closed", name);
996                 goto dlcloseerror;
997         }
998         if (!dls->module) {
999                 error("module already closed");
1000                 goto dlcloseerror;
1001         }
1002         
1003         if (dls->refs == 1) {
1004                 unsigned long options = 0;
1005                 void (*fini) (void);
1006                 if ((fini = dlsymIntern(dls, "__fini", 0))) {
1007                         debug("calling _fini()");
1008                         fini();
1009                 }
1010 #ifdef __ppc__
1011                 options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
1012 #endif
1013 #if 1
1014 /*  Currently, if a module contains c++ static destructors and it is unloaded, we
1015  *  get a segfault in atexit(), due to compiler and dynamic loader differences of
1016  *  opinion, this works around that.
1017  *  I really need a way to figure out from code if this is still necessary.
1018  */
1019                 if ((const struct section *)NULL !=
1020                         getsectbynamefromheader(get_mach_header_from_NSModule(dls->module),
1021                                                                         "__DATA", "__mod_term_func")) {
1022                         options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
1023                 }
1024 #endif
1025 #ifdef RTLD_NODELETE
1026                 if (isFlagSet(dls->mode, RTLD_NODELETE))
1027                         options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
1028 #endif
1029                 if (!NSUnLinkModule(dls->module, options)) {
1030                         error("unable to unlink module");
1031                         goto dlcloseerror;
1032                 }
1033                 dls->refs--;
1034                 dls->module = 0;
1035                 /* Note: the dlstatus struct dls is neither removed from the list
1036                  * nor is the memory it occupies freed. This shouldn't pose a 
1037                  * problem in mostly all cases, though.
1038                  */
1039         }
1040         dounlock();
1041         return 0;
1042 dlcloseerror:
1043         dounlock();
1044         return 1;
1045 }
1046
1047 const char *dlerror(void)
1048 {
1049         struct dlthread  *tss;
1050         char * err_str;
1051         tss = pthread_getspecific(dlerror_key);
1052         err_str = tss->errstr;
1053         tss = pthread_getspecific(dlerror_key);
1054         if (tss->errset == 0)
1055                 return 0;
1056         tss->errset = 0;        
1057         return (err_str );
1058 }
1059
1060 /* Given an address, return the mach_header for the image containing it
1061  * or zero if the given address is not contained in any loaded images.
1062  */
1063 const struct mach_header *image_for_address(const void *address)
1064 {
1065         unsigned long i;
1066         unsigned long j;
1067         unsigned long count = _dyld_image_count();
1068         struct mach_header *mh = 0;
1069         struct load_command *lc = 0;
1070         unsigned long addr = NULL;
1071         for (i = 0; i < count; i++) {
1072                 addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i);
1073                 mh = _dyld_get_image_header(i);
1074                 if (mh) {
1075                         lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1076                         for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) {
1077                                 if (LC_SEGMENT == lc->cmd &&
1078                                         addr >= ((struct segment_command *)lc)->vmaddr &&
1079                                         addr <
1080                                         ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) {
1081                                         goto image_found;
1082                                 }
1083                         }
1084                 }
1085                 mh = 0;
1086         }
1087 image_found:
1088         return mh;
1089 }
1090
1091 int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info)
1092 {
1093 /*
1094         FIXME: USe the routine image_for_address.
1095 */
1096         unsigned long i;
1097         unsigned long j;
1098         unsigned long count = _dyld_image_count();
1099         struct mach_header *mh = 0;
1100         struct load_command *lc = 0;
1101         unsigned long addr = NULL;
1102         unsigned long table_off = (unsigned long)0;
1103         int found = 0;
1104         if (!info)
1105                 return 0;
1106         dolock();
1107         resetdlerror();
1108         info->dli_fname = 0;
1109         info->dli_fbase = 0;
1110         info->dli_sname = 0;
1111         info->dli_saddr = 0;
1112 /* Some of this was swiped from code posted by Douglas Davidson <ddavidso AT apple DOT com>
1113  * to darwin-development AT lists DOT apple DOT com and slightly modified
1114  */
1115         for (i = 0; i < count; i++) {
1116                 addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i);
1117                 mh = _dyld_get_image_header(i);
1118                 if (mh) {
1119                         lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1120                         for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) {
1121                                 if (LC_SEGMENT == lc->cmd &&
1122                                         addr >= ((struct segment_command *)lc)->vmaddr &&
1123                                         addr <
1124                                         ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) {
1125                                         info->dli_fname = _dyld_get_image_name(i);
1126                                         info->dli_fbase = (void *)mh;
1127                                         found = 1;
1128                                         break;
1129                                 }
1130                         }
1131                         if (found)
1132                                 break;
1133                 }
1134         }
1135         if (!found) {
1136                 dounlock();
1137                 return 0;
1138         }
1139         lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1140         for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) {
1141                 if (LC_SEGMENT == lc->cmd) {
1142                         if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT"))
1143                                 break;
1144                 }
1145         }
1146         table_off =
1147                 ((unsigned long)((struct segment_command *)lc)->vmaddr) -
1148                 ((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i);
1149         debug("table off %x", table_off);
1150
1151         lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1152         for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) {
1153                 if (LC_SYMTAB == lc->cmd) {
1154
1155                         struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off);
1156                         unsigned long numsyms = ((struct symtab_command *)lc)->nsyms;
1157                         struct nlist *nearest = NULL;
1158                         unsigned long diff = 0xffffffff;
1159                         unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off);
1160                         debug("symtable %x", symtable);
1161                         for (i = 0; i < numsyms; i++) {
1162                                 /* Ignore the following kinds of Symbols */
1163                                 if ((!symtable->n_value)                /* Undefined */
1164                                         || (symtable->n_type >= N_PEXT)     /* Debug symbol */
1165                                         || (!(symtable->n_type & N_EXT))    /* Local Symbol */
1166                                         ) {
1167                                         symtable++;
1168                                         continue;
1169                                 }
1170                                 if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr))) {
1171                                         diff = (unsigned long)symtable->n_value - addr;
1172                                         nearest = symtable;
1173                                 }
1174                                 symtable++;
1175                         }
1176                         if (nearest) {
1177                                 info->dli_saddr = nearest->n_value + ((void *)p - addr);
1178                                 info->dli_sname = (char *)(strtable + nearest->n_un.n_strx);
1179                         }
1180                 }
1181         }
1182         dounlock();
1183         return 1;
1184 }
1185
1186
1187 /*
1188  * Implement the dlfunc() interface, which behaves exactly the same as
1189  * dlsym() except that it returns a function pointer instead of a data
1190  * pointer.  This can be used by applications to avoid compiler warnings
1191  * about undefined behavior, and is intended as prior art for future
1192  * POSIX standardization.  This function requires that all pointer types
1193  * have the same representation, which is true on all platforms FreeBSD
1194  * runs on, but is not guaranteed by the C standard.
1195  */
1196 #if 0 
1197 dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol)
1198 {
1199         union
1200         {
1201                 void *d;
1202                 dlfunc_t f;
1203         } rv;
1204         int sym_len = strlen(symbol);
1205         char *malloc_sym = NULL;
1206         dolock();
1207         malloc_sym = ast_malloc(sym_len + 2);
1208         if (malloc_sym)
1209         {
1210                 sprintf(malloc_sym, "_%s", symbol);
1211                 rv.d = dlsymIntern(handle, malloc_sym, 1);
1212                 ast_free(malloc_sym);
1213         }
1214         else
1215         {
1216                 error("Unable to allocate memory");
1217                 goto dlfuncerror;
1218         }
1219         dounlock();
1220         return rv.f;
1221   dlfuncerror:
1222         dounlock();
1223         return NULL;
1224 }
1225 #endif