various cleanups, remove support for CVS checkouts
[asterisk-bristuff.git] / dlfcn.c
blobcc6fe40f92959743cb7833743451966cc5f7e7de
1 /*
2 Copyright (c) 2002 Jorge Acereda <jacereda@users.sourceforge.net> &
3 Peter O'Gorman <ogorman@users.sourceforge.net>
5 Portions may be copyright others, see the AUTHORS file included with this
6 distribution.
8 Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
10 Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
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:
20 The above copyright notice and this permission notice shall be
21 included in all copies or substantial portions of the Software.
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.
32 #include <pthread.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <stdarg.h>
39 #include <limits.h>
40 #include <mach-o/dyld.h>
41 #include <mach-o/nlist.h>
42 #include <mach-o/getsect.h>
43 /* Just playing to see if it would compile with the freebsd headers, it does,
44 * but because of the different values for RTLD_LOCAL etc, it would break binary
45 * compat... oh well
47 #ifndef __BSD_VISIBLE
48 #define __BSD_VISIBLE 1
49 #endif
51 #include "asterisk/dlfcn-compat.h"
53 #ifndef dl_restrict
54 #define dl_restrict __restrict
55 #endif
56 /* This is not available on 10.1 */
57 #ifndef LC_LOAD_WEAK_DYLIB
58 #define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)
59 #endif
61 /* With this stuff here, this thing may actually compile/run on 10.0 systems
62 * Not that I have a 10.0 system to test it on anylonger
64 #ifndef LC_REQ_DYLD
65 #define LC_REQ_DYLD 0x80000000
66 #endif
67 #ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
68 #define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4
69 #endif
70 #ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR
71 #define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1
72 #endif
73 #ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
74 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0
75 #endif
76 #ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
77 #define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4
78 #endif
79 /* These symbols will be looked for in dyld */
80 static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0;
81 static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0;
82 static NSSymbol(*dyld_NSLookupSymbolInImage)
83 (const struct mach_header *, const char *, unsigned long) = 0;
85 /* Define this to make dlcompat reuse data block. This way in theory we save
86 * a little bit of overhead. However we then couldn't correctly catch excess
87 * calls to dlclose(). Hence we don't use this feature
89 #undef REUSE_STATUS
91 /* Size of the internal error message buffer (used by dlerror()) */
92 #define ERR_STR_LEN 251
94 /* Maximum number of search paths supported by getSearchPath */
95 #define MAX_SEARCH_PATHS 32
98 #define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF')
99 #define MAGIC_DYLIB_MOD ((NSModule) 'DYMO')
101 /* internal flags */
102 #define DL_IN_LIST 0x01
104 /* our mutex */
105 static pthread_mutex_t dlcompat_mutex;
106 /* Our thread specific storage
108 static pthread_key_t dlerror_key;
110 struct dlthread
112 int lockcnt;
113 unsigned char errset;
114 char errstr[ERR_STR_LEN];
117 /* This is our central data structure. Whenever a module is loaded via
118 * dlopen(), we create such a struct.
120 struct dlstatus
122 struct dlstatus *next; /* pointer to next element in the linked list */
123 NSModule module;
124 const struct mach_header *lib;
125 int refs; /* reference count */
126 int mode; /* mode in which this module was loaded */
127 dev_t device;
128 ino_t inode;
129 int flags; /* Any internal flags we may need */
132 /* Head node of the dlstatus list */
133 static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 };
134 static struct dlstatus *stqueue = &mainStatus;
137 /* Storage for the last error message (used by dlerror()) */
138 /* static char err_str[ERR_STR_LEN]; */
139 /* static int err_filled = 0; */
141 /* Prototypes to internal functions */
142 static void debug(const char *fmt, ...);
143 static void error(const char *str, ...);
144 static const char *safegetenv(const char *s);
145 static const char *searchList(void);
146 static const char *getSearchPath(int i);
147 static const char *getFullPath(int i, const char *file);
148 static const struct stat *findFile(const char *file, const char **fullPath);
149 static int isValidStatus(struct dlstatus *status);
150 static inline int isFlagSet(int mode, int flag);
151 static struct dlstatus *lookupStatus(const struct stat *sbuf);
152 static void insertStatus(struct dlstatus *dls, const struct stat *sbuf);
153 static int promoteLocalToGlobal(struct dlstatus *dls);
154 static void *reference(struct dlstatus *dls, int mode);
155 static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError);
156 static struct dlstatus *allocStatus(void);
157 static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode);
158 static NSSymbol *search_linked_libs(const struct mach_header *mh, const char *symbol);
159 static const char *get_lib_name(const struct mach_header *mh);
160 static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod);
161 static void dlcompat_init_func(void);
162 static inline void dolock(void);
163 static inline void dounlock(void);
164 static void dlerrorfree(void *data);
165 static void resetdlerror(void);
166 static const struct mach_header *my_find_image(const char *name);
167 static const struct mach_header *image_for_address(const void *address);
168 static void dlcompat_cleanup(void);
169 static inline const char *dyld_error_str(void);
171 #if FINK_BUILD
172 /* Two Global Functions */
173 void *dlsym_prepend_underscore(void *handle, const char *symbol);
174 void *dlsym_auto_underscore(void *handle, const char *symbol);
176 /* And their _intern counterparts */
177 static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol);
178 static void *dlsym_auto_underscore_intern(void *handle, const char *symbol);
179 #endif
181 /* Functions */
183 static void debug(const char *fmt, ...)
185 #if DEBUG > 1
186 va_list arg;
187 va_start(arg, fmt);
188 fprintf(stderr, "DLDEBUG: ");
189 vfprintf(stderr, fmt, arg);
190 fprintf(stderr, "\n");
191 fflush(stderr);
192 va_end(arg);
193 #endif
196 static void error(const char *str, ...)
198 va_list arg;
199 struct dlthread *tss;
200 char * err_str;
201 va_start(arg, str);
202 tss = pthread_getspecific(dlerror_key);
203 err_str = tss->errstr;
204 strncpy(err_str, "dlcompat: ", ERR_STR_LEN);
205 vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg);
206 va_end(arg);
207 debug("ERROR: %s\n", err_str);
208 tss->errset = 1;
211 static void warning(const char *str)
213 #if DEBUG > 0
214 fprintf(stderr, "WARNING: dlcompat: %s\n", str);
215 #endif
218 static const char *safegetenv(const char *s)
220 const char *ss = getenv(s);
221 return ss ? ss : "";
224 /* because this is only used for debugging and error reporting functions, we
225 * don't really care about how elegant it is... it could use the load
226 * commands to find the install name of the library, but...
228 static const char *get_lib_name(const struct mach_header *mh)
230 unsigned long count = _dyld_image_count();
231 unsigned long i;
232 const char *val = NULL;
233 if (mh)
235 for (i = 0; i < count; i++)
237 if (mh == _dyld_get_image_header(i))
239 val = _dyld_get_image_name(i);
240 break;
244 return val;
247 /* Returns the mach_header for the module bu going through all the loaded images
248 * and finding the one with the same name as the module. There really ought to be
249 * an api for doing this, would be faster, but there isn't one right now
251 static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod)
253 const char *mod_name = NSNameOfModule(mod);
254 struct mach_header *mh = NULL;
255 unsigned long count = _dyld_image_count();
256 unsigned long i;
257 debug("Module name: %s", mod_name);
258 for (i = 0; i < count; i++)
260 if (!strcmp(mod_name, _dyld_get_image_name(i)))
262 mh = _dyld_get_image_header(i);
263 break;
266 return mh;
270 /* Compute and return a list of all directories that we should search when
271 * trying to locate a module. We first look at the values of LD_LIBRARY_PATH
272 * and DYLD_LIBRARY_PATH, and then finally fall back to looking into
273 * /usr/lib and /lib. Since both of the environments variables can contain a
274 * list of colon separated paths, we simply concat them and the two other paths
275 * into one big string, which we then can easily parse.
276 * Splitting this string into the actual path list is done by getSearchPath()
278 static const char *searchList()
280 size_t buf_size;
281 static char *buf=NULL;
282 const char *ldlp = safegetenv("LD_LIBRARY_PATH");
283 const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH");
284 const char *stdpath = getenv("DYLD_FALLBACK_LIBRARY_PATH");
285 if (!stdpath)
286 stdpath = "/usr/local/lib:/lib:/usr/lib";
287 if (!buf)
289 buf_size = strlen(ldlp) + strlen(dyldlp) + strlen(stdpath) + 4;
290 buf = malloc(buf_size);
291 snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""),
292 stdpath, '\0');
294 return buf;
297 /* Returns the ith search path from the list as computed by searchList() */
298 static const char *getSearchPath(int i)
300 static const char *list = 0;
301 static char **path = (char **)0;
302 static int end = 0;
303 static int numsize = MAX_SEARCH_PATHS;
304 static char **tmp;
305 /* So we can call free() in the "destructor" we use i=-1 to return the alloc'd array */
306 if (i == -1)
308 return (const char*)path;
310 if (!path)
312 path = (char **)calloc(MAX_SEARCH_PATHS, sizeof(char **));
314 if (!list && !end)
315 list = searchList();
316 if (i >= (numsize))
318 debug("Increasing size for long PATH");
319 tmp = (char **)calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **));
320 if (tmp)
322 memcpy(tmp, path, sizeof(char **) * numsize);
323 free(path);
324 path = tmp;
325 numsize += MAX_SEARCH_PATHS;
327 else
329 return 0;
333 while (!path[i] && !end)
335 path[i] = strsep((char **)&list, ":");
337 if (path[i][0] == 0)
338 path[i] = 0;
339 end = (list == 0);
341 return path[i];
344 static const char *getFullPath(int i, const char *file)
346 static char buf[PATH_MAX];
347 const char *path = getSearchPath(i);
348 if (path)
350 snprintf(buf, PATH_MAX, "%s/%s", path, file);
352 return path ? buf : 0;
355 /* Given a file name, try to determine the full path for that file. Starts
356 * its search in the current directory, and then tries all paths in the
357 * search list in the order they are specified there.
359 static const struct stat *findFile(const char *file, const char **fullPath)
361 int i = 0;
362 static struct stat sbuf;
363 char *fileName;
364 debug("finding file %s", file);
365 *fullPath = file;
366 if (0 == stat(file, &sbuf))
367 return &sbuf;
368 if (strchr(file, '/'))
369 return 0; /* If the path had a / we don't look in env var places */
370 fileName = NULL;
371 if (!fileName)
372 fileName = (char *)file;
373 while ((*fullPath = getFullPath(i++, fileName)))
375 if (0 == stat(*fullPath, &sbuf))
376 return &sbuf;
379 return 0;
382 /* Determine whether a given dlstatus is valid or not */
383 static int isValidStatus(struct dlstatus *status)
385 /* Walk the list to verify status is contained in it */
386 struct dlstatus *dls = stqueue;
387 while (dls && status != dls)
388 dls = dls->next;
389 if (dls == 0)
390 error("invalid handle");
391 else if ((dls->module == 0) || (dls->refs == 0))
392 error("handle to closed library");
393 else
394 return TRUE;
395 return FALSE;
398 static inline int isFlagSet(int mode, int flag)
400 return (mode & flag) == flag;
403 static struct dlstatus *lookupStatus(const struct stat *sbuf)
405 struct dlstatus *dls = stqueue;
406 debug("looking for status");
407 while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0
408 || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode))
409 dls = dls->next;
410 return dls;
413 static void insertStatus(struct dlstatus *dls, const struct stat *sbuf)
415 debug("inserting status");
416 dls->inode = sbuf->st_ino;
417 dls->device = sbuf->st_dev;
418 dls->refs = 0;
419 dls->mode = 0;
420 if ((dls->flags & DL_IN_LIST) == 0)
422 dls->next = stqueue;
423 stqueue = dls;
424 dls->flags |= DL_IN_LIST;
428 static struct dlstatus *allocStatus()
430 struct dlstatus *dls;
431 #ifdef REUSE_STATUS
432 dls = stqueue;
433 while (dls && dls->module)
434 dls = dls->next;
435 if (!dls)
436 #endif
437 dls = malloc(sizeof(*dls));
438 dls->flags = 0;
439 return dls;
442 static int promoteLocalToGlobal(struct dlstatus *dls)
444 static int (*p) (NSModule module) = 0;
445 debug("promoting");
446 if (!p)
447 _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p);
448 return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module));
451 static void *reference(struct dlstatus *dls, int mode)
453 if (dls)
455 if (dls->module == MAGIC_DYLIB_MOD && !isFlagSet(mode, RTLD_GLOBAL))
457 warning("trying to open a .dylib with RTLD_LOCAL");
458 error("unable to open a .dylib with RTLD_LOCAL");
459 return NULL;
461 if (isFlagSet(mode, RTLD_GLOBAL) &&
462 !isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls))
464 error("unable to promote local module to global");
465 return NULL;
467 dls->mode |= mode;
468 dls->refs++;
470 else
471 debug("reference called with NULL argument");
473 return dls;
476 static const struct mach_header *my_find_image(const char *name)
478 const struct mach_header *mh = 0;
479 const char *id = NULL;
480 int i = _dyld_image_count();
481 int j;
482 mh = (struct mach_header *)
483 dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED |
484 NSADDIMAGE_OPTION_RETURN_ON_ERROR);
485 if (!mh)
487 for (j = 0; j < i; j++)
489 id = _dyld_get_image_name(j);
490 if (!strcmp(id, name))
492 mh = _dyld_get_image_header(j);
493 break;
497 return mh;
501 * dyld adds libraries by first adding the directly dependant libraries in link order, and
502 * then adding the dependencies for those libraries, so we should do the same... but we don't
503 * bother adding the extra dependencies, if the symbols are neither in the loaded image nor
504 * any of it's direct dependencies, then it probably isn't there.
506 NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol)
508 int n;
509 struct load_command *lc = 0;
510 struct mach_header *wh;
511 NSSymbol *nssym = 0;
512 if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
514 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
515 for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
517 if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd))
519 if ((wh = (struct mach_header *)
520 my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset +
521 (char *)lc))))
523 if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol))
525 nssym = dyld_NSLookupSymbolInImage(wh,
526 symbol,
527 NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
528 NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
529 break;
534 if ((!nssym) && NSIsSymbolNameDefined(symbol))
536 /* I've never seen this debug message...*/
537 debug("Symbol \"%s\" is defined but was not found", symbol);
540 return nssym;
543 /* Up to the caller to free() returned string */
544 static inline const char *dyld_error_str()
546 NSLinkEditErrors dylder;
547 int dylderno;
548 const char *dylderrstr;
549 const char *dyldfile;
550 const char* retStr = NULL;
551 NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr);
552 if (dylderrstr && strlen(dylderrstr))
554 retStr = malloc(strlen(dylderrstr) +1);
555 strcpy((char*)retStr,dylderrstr);
557 return retStr;
560 static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError)
562 NSSymbol *nssym = 0;
563 void *caller = __builtin_return_address(1); /* Be *very* careful about inlining */
564 const struct mach_header *caller_mh = 0;
565 const char* savedErrorStr = NULL;
566 resetdlerror();
567 #ifndef RTLD_SELF
568 #define RTLD_SELF ((void *) -3)
569 #endif
570 if (NULL == dls)
571 dls = RTLD_SELF;
572 if ((RTLD_NEXT == dls) || (RTLD_SELF == dls))
574 if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
576 caller_mh = image_for_address(caller);
577 if (RTLD_SELF == dls)
579 /* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE
580 * But it appears to work anyway, and looking at the code in dyld_libfuncs.c
581 * this is acceptable.
583 if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol))
585 nssym = dyld_NSLookupSymbolInImage(caller_mh,
586 symbol,
587 NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
588 NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
591 if (!nssym)
593 if (RTLD_SELF == dls)
594 savedErrorStr = dyld_error_str();
595 nssym = search_linked_libs(caller_mh, symbol);
598 else
600 if (canSetError)
601 error("RTLD_SELF and RTLD_NEXT are not supported");
602 return NULL;
605 if (!nssym)
608 if (RTLD_DEFAULT == dls)
610 dls = &mainStatus;
612 if (!isValidStatus(dls))
613 return NULL;
615 if (dls->module != MAGIC_DYLIB_MOD)
617 nssym = NSLookupSymbolInModule(dls->module, symbol);
618 if (!nssym && NSIsSymbolNameDefined(symbol))
620 debug("Searching dependencies");
621 savedErrorStr = dyld_error_str();
622 nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol);
625 else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
627 if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol))
629 nssym = dyld_NSLookupSymbolInImage(dls->lib,
630 symbol,
631 NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
632 NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
634 else if (NSIsSymbolNameDefined(symbol))
636 debug("Searching dependencies");
637 savedErrorStr = dyld_error_str();
638 nssym = search_linked_libs(dls->lib, symbol);
641 else if (dls->module == MAGIC_DYLIB_MOD)
643 /* Global context, use NSLookupAndBindSymbol */
644 if (NSIsSymbolNameDefined(symbol))
646 /* There doesn't seem to be a return on error option for this call???
647 this is potentially broken, if binding fails, it will improperly
648 exit the application. */
649 nssym = NSLookupAndBindSymbol(symbol);
651 else
653 if (savedErrorStr)
654 free((char*)savedErrorStr);
655 savedErrorStr = malloc(256);
656 snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol);
660 /* Error reporting */
661 if (!nssym)
663 if (!savedErrorStr || !strlen(savedErrorStr))
665 if (savedErrorStr)
666 free((char*)savedErrorStr);
667 savedErrorStr = malloc(256);
668 snprintf((char*)savedErrorStr, 256,"Symbol \"%s\" not found",symbol);
670 if (canSetError)
672 error(savedErrorStr);
674 else
676 debug(savedErrorStr);
678 if (savedErrorStr)
679 free((char*)savedErrorStr);
680 return NULL;
682 return NSAddressOfSymbol(nssym);
685 static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode)
687 NSObjectFileImage ofi = 0;
688 NSObjectFileImageReturnCode ofirc;
689 struct dlstatus *dls;
690 NSLinkEditErrors ler;
691 int lerno;
692 const char *errstr;
693 const char *file;
694 void (*init) (void);
695 ofirc = NSCreateObjectFileImageFromFile(path, &ofi);
696 switch (ofirc)
698 case NSObjectFileImageSuccess:
699 break;
700 case NSObjectFileImageInappropriateFile:
701 if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
703 if (!isFlagSet(mode, RTLD_GLOBAL))
705 warning("trying to open a .dylib with RTLD_LOCAL");
706 error("unable to open this file with RTLD_LOCAL");
707 return NULL;
710 else
712 error("opening this file is unsupported on this system");
713 return NULL;
715 break;
716 case NSObjectFileImageFailure:
717 error("object file setup failure");
718 return NULL;
719 case NSObjectFileImageArch:
720 error("no object for this architecture");
721 return NULL;
722 case NSObjectFileImageFormat:
723 error("bad object file format");
724 return NULL;
725 case NSObjectFileImageAccess:
726 error("can't read object file");
727 return NULL;
728 default:
729 error("unknown error from NSCreateObjectFileImageFromFile()");
730 return NULL;
732 dls = lookupStatus(sbuf);
733 if (!dls)
735 dls = allocStatus();
737 if (!dls)
739 error("unable to allocate memory");
740 return NULL;
742 dls->lib = 0;
743 if (ofirc == NSObjectFileImageInappropriateFile)
745 if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR)))
747 debug("Dynamic lib loaded at %ld", dls->lib);
748 ofi = MAGIC_DYLIB_OFI;
749 dls->module = MAGIC_DYLIB_MOD;
750 ofirc = NSObjectFileImageSuccess;
751 /* Although it is possible with a bit of work to modify this so it works and
752 functions with RTLD_NOW, I don't deem it necessary at the moment */
754 if (!(dls->module))
756 NSLinkEditError(&ler, &lerno, &file, &errstr);
757 if (!errstr || (!strlen(errstr)))
758 error("Can't open this file type");
759 else
760 error(errstr);
761 if ((dls->flags & DL_IN_LIST) == 0)
763 free(dls);
765 return NULL;
768 else
770 dls->module = NSLinkModule(ofi, path,
771 NSLINKMODULE_OPTION_RETURN_ON_ERROR |
772 NSLINKMODULE_OPTION_PRIVATE |
773 (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0));
774 NSDestroyObjectFileImage(ofi);
775 if (dls->module)
777 dls->lib = get_mach_header_from_NSModule(dls->module);
780 if (!dls->module)
782 NSLinkEditError(&ler, &lerno, &file, &errstr);
783 if ((dls->flags & DL_IN_LIST) == 0)
785 free(dls);
787 error(errstr);
788 return NULL;
791 insertStatus(dls, sbuf);
792 dls = reference(dls, mode);
793 if ((init = dlsymIntern(dls, "__init", 0)))
795 debug("calling _init()");
796 init();
798 return dls;
801 static void dlcompat_init_func(void)
803 static int inited = 0;
804 if (!inited)
806 inited = 1;
807 _dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage);
808 _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage",
809 (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage);
810 _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage);
811 if (pthread_mutex_init(&dlcompat_mutex, NULL))
812 exit(1);
813 if (pthread_key_create(&dlerror_key, &dlerrorfree))
814 exit(1);
815 /* And be neat and tidy and clean up after ourselves */
816 atexit(dlcompat_cleanup);
820 #if 0
821 #pragma CALL_ON_LOAD dlcompat_init_func
822 #endif
824 static void dlcompat_cleanup(void)
826 struct dlstatus *dls;
827 struct dlstatus *next;
828 char *data;
829 data = (char *)searchList();
830 if ( data )
831 free( data );
832 data = (char *)getSearchPath(-1);
833 if ( data )
834 free( data );
835 pthread_mutex_destroy(&dlcompat_mutex);
836 pthread_key_delete(dlerror_key);
837 next = stqueue;
838 while (next && (next != &mainStatus))
840 dls = next;
841 next = dls->next;
842 free(dls);
846 static void resetdlerror()
848 struct dlthread *tss;
849 tss = pthread_getspecific(dlerror_key);
850 tss->errset = 0;
853 static void dlerrorfree(void *data)
855 free(data);
858 /* We kind of want a recursive lock here, but meet a little trouble
859 * because they are not available pre OS X 10.2, so we fake it
860 * using thread specific storage to keep a lock count
862 static inline void dolock(void)
864 int err = 0;
865 struct dlthread *tss;
866 tss = pthread_getspecific(dlerror_key);
867 if (!tss)
869 tss = malloc(sizeof(struct dlthread));
870 tss->lockcnt = 0;
871 tss->errset = 0;
872 if (pthread_setspecific(dlerror_key, tss))
874 fprintf(stderr,"dlcompat: pthread_setspecific failed\n");
875 exit(1);
878 if (!tss->lockcnt)
879 err = pthread_mutex_lock(&dlcompat_mutex);
880 tss->lockcnt = tss->lockcnt +1;
881 if (err)
882 exit(err);
885 static inline void dounlock(void)
887 int err = 0;
888 struct dlthread *tss;
889 tss = pthread_getspecific(dlerror_key);
890 tss->lockcnt = tss->lockcnt -1;
891 if (!tss->lockcnt)
892 err = pthread_mutex_unlock(&dlcompat_mutex);
893 if (err)
894 exit(err);
897 void *dlopen(const char *path, int mode)
899 const struct stat *sbuf;
900 struct dlstatus *dls;
901 const char *fullPath;
902 dlcompat_init_func(); /* Just in case */
903 dolock();
904 resetdlerror();
905 if (!path)
907 dls = &mainStatus;
908 goto dlopenok;
910 if (!(sbuf = findFile(path, &fullPath)))
912 error("file \"%s\" not found", path);
913 goto dlopenerror;
915 /* Now checks that it hasn't been closed already */
916 if ((dls = lookupStatus(sbuf)) && (dls->refs > 0))
918 /* debug("status found"); */
919 dls = reference(dls, mode);
920 goto dlopenok;
922 #ifdef RTLD_NOLOAD
923 if (isFlagSet(mode, RTLD_NOLOAD))
925 error("no existing handle and RTLD_NOLOAD specified");
926 goto dlopenerror;
928 #endif
929 if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW))
931 error("how can I load something both RTLD_LAZY and RTLD_NOW?");
932 goto dlopenerror;
934 dls = loadModule(fullPath, sbuf, mode);
936 dlopenok:
937 dounlock();
938 return (void *)dls;
939 dlopenerror:
940 dounlock();
941 return NULL;
944 #if !FINK_BUILD
945 void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
947 int sym_len = strlen(symbol);
948 void *value = NULL;
949 char *malloc_sym = NULL;
950 dolock();
951 malloc_sym = malloc(sym_len + 2);
952 if (malloc_sym)
954 sprintf(malloc_sym, "_%s", symbol);
955 value = dlsymIntern(handle, malloc_sym, 1);
956 free(malloc_sym);
958 else
960 error("Unable to allocate memory");
961 goto dlsymerror;
963 dounlock();
964 return value;
965 dlsymerror:
966 dounlock();
967 return NULL;
969 #endif
971 #if FINK_BUILD
973 void *dlsym_prepend_underscore(void *handle, const char *symbol)
975 void *answer;
976 dolock();
977 answer = dlsym_prepend_underscore_intern(handle, symbol);
978 dounlock();
979 return answer;
982 static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol)
985 * A quick and easy way for porting packages which call dlsym(handle,"sym")
986 * If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then
987 * this function will be called, and will add the required underscore.
989 * Note that I haven't figured out yet which should be "standard", prepend
990 * the underscore always, or not at all. These global functions need to go away
991 * for opendarwin.
993 int sym_len = strlen(symbol);
994 void *value = NULL;
995 char *malloc_sym = NULL;
996 malloc_sym = malloc(sym_len + 2);
997 if (malloc_sym)
999 sprintf(malloc_sym, "_%s", symbol);
1000 value = dlsymIntern(handle, malloc_sym, 1);
1001 free(malloc_sym);
1003 else
1005 error("Unable to allocate memory");
1007 return value;
1010 void *dlsym_auto_underscore(void *handle, const char *symbol)
1012 void *answer;
1013 dolock();
1014 answer = dlsym_auto_underscore_intern(handle, symbol);
1015 dounlock();
1016 return answer;
1019 static void *dlsym_auto_underscore_intern(void *handle, const char *symbol)
1021 struct dlstatus *dls = handle;
1022 void *addr = 0;
1023 addr = dlsymIntern(dls, symbol, 0);
1024 if (!addr)
1025 addr = dlsym_prepend_underscore_intern(handle, symbol);
1026 return addr;
1030 void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
1032 struct dlstatus *dls = handle;
1033 void *addr = 0;
1034 dolock();
1035 addr = dlsymIntern(dls, symbol, 1);
1036 dounlock();
1037 return addr;
1039 #endif
1041 int dlclose(void *handle)
1043 struct dlstatus *dls = handle;
1044 dolock();
1045 resetdlerror();
1046 if (!isValidStatus(dls))
1048 goto dlcloseerror;
1050 if (dls->module == MAGIC_DYLIB_MOD)
1052 const char *name;
1053 if (!dls->lib)
1055 name = "global context";
1057 else
1059 name = get_lib_name(dls->lib);
1061 warning("trying to close a .dylib!");
1062 error("Not closing \"%s\" - dynamic libraries cannot be closed", name);
1063 goto dlcloseerror;
1065 if (!dls->module)
1067 error("module already closed");
1068 goto dlcloseerror;
1071 if (dls->refs == 1)
1073 unsigned long options = 0;
1074 void (*fini) (void);
1075 if ((fini = dlsymIntern(dls, "__fini", 0)))
1077 debug("calling _fini()");
1078 fini();
1080 #ifdef __ppc__
1081 options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
1082 #endif
1083 #if 1
1084 /* Currently, if a module contains c++ static destructors and it is unloaded, we
1085 * get a segfault in atexit(), due to compiler and dynamic loader differences of
1086 * opinion, this works around that.
1087 * I really need a way to figure out from code if this is still necessary.
1089 if ((const struct section *)NULL !=
1090 getsectbynamefromheader(get_mach_header_from_NSModule(dls->module),
1091 "__DATA", "__mod_term_func"))
1093 options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
1095 #endif
1096 #ifdef RTLD_NODELETE
1097 if (isFlagSet(dls->mode, RTLD_NODELETE))
1098 options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
1099 #endif
1100 if (!NSUnLinkModule(dls->module, options))
1102 error("unable to unlink module");
1103 goto dlcloseerror;
1105 dls->refs--;
1106 dls->module = 0;
1107 /* Note: the dlstatus struct dls is neither removed from the list
1108 * nor is the memory it occupies freed. This shouldn't pose a
1109 * problem in mostly all cases, though.
1112 dounlock();
1113 return 0;
1114 dlcloseerror:
1115 dounlock();
1116 return 1;
1119 const char *dlerror(void)
1121 struct dlthread *tss;
1122 char * err_str;
1123 tss = pthread_getspecific(dlerror_key);
1124 err_str = tss->errstr;
1125 tss = pthread_getspecific(dlerror_key);
1126 if (tss->errset == 0)
1127 return 0;
1128 tss->errset = 0;
1129 return (err_str );
1132 /* Given an address, return the mach_header for the image containing it
1133 * or zero if the given address is not contained in any loaded images.
1135 const struct mach_header *image_for_address(const void *address)
1137 unsigned long i;
1138 unsigned long j;
1139 unsigned long count = _dyld_image_count();
1140 struct mach_header *mh = 0;
1141 struct load_command *lc = 0;
1142 unsigned long addr = NULL;
1143 for (i = 0; i < count; i++)
1145 addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i);
1146 mh = _dyld_get_image_header(i);
1147 if (mh)
1149 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1150 for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
1152 if (LC_SEGMENT == lc->cmd &&
1153 addr >= ((struct segment_command *)lc)->vmaddr &&
1154 addr <
1155 ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
1157 goto image_found;
1161 mh = 0;
1163 image_found:
1164 return mh;
1167 int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info)
1170 FIXME: USe the routine image_for_address.
1172 unsigned long i;
1173 unsigned long j;
1174 unsigned long count = _dyld_image_count();
1175 struct mach_header *mh = 0;
1176 struct load_command *lc = 0;
1177 unsigned long addr = NULL;
1178 unsigned long table_off = (unsigned long)0;
1179 int found = 0;
1180 if (!info)
1181 return 0;
1182 dolock();
1183 resetdlerror();
1184 info->dli_fname = 0;
1185 info->dli_fbase = 0;
1186 info->dli_sname = 0;
1187 info->dli_saddr = 0;
1188 /* Some of this was swiped from code posted by Douglas Davidson <ddavidso AT apple DOT com>
1189 * to darwin-development AT lists DOT apple DOT com and slightly modified
1191 for (i = 0; i < count; i++)
1193 addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i);
1194 mh = _dyld_get_image_header(i);
1195 if (mh)
1197 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1198 for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
1200 if (LC_SEGMENT == lc->cmd &&
1201 addr >= ((struct segment_command *)lc)->vmaddr &&
1202 addr <
1203 ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
1205 info->dli_fname = _dyld_get_image_name(i);
1206 info->dli_fbase = (void *)mh;
1207 found = 1;
1208 break;
1211 if (found)
1212 break;
1215 if (!found)
1217 dounlock();
1218 return 0;
1220 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1221 for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
1223 if (LC_SEGMENT == lc->cmd)
1225 if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT"))
1226 break;
1229 table_off =
1230 ((unsigned long)((struct segment_command *)lc)->vmaddr) -
1231 ((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i);
1232 debug("table off %x", table_off);
1234 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
1235 for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
1237 if (LC_SYMTAB == lc->cmd)
1240 struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off);
1241 unsigned long numsyms = ((struct symtab_command *)lc)->nsyms;
1242 struct nlist *nearest = NULL;
1243 unsigned long diff = 0xffffffff;
1244 unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off);
1245 debug("symtable %x", symtable);
1246 for (i = 0; i < numsyms; i++)
1248 /* Ignore the following kinds of Symbols */
1249 if ((!symtable->n_value) /* Undefined */
1250 || (symtable->n_type >= N_PEXT) /* Debug symbol */
1251 || (!(symtable->n_type & N_EXT)) /* Local Symbol */
1254 symtable++;
1255 continue;
1257 if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr)))
1259 diff = (unsigned long)symtable->n_value - addr;
1260 nearest = symtable;
1262 symtable++;
1264 if (nearest)
1266 info->dli_saddr = nearest->n_value + ((void *)p - addr);
1267 info->dli_sname = (char *)(strtable + nearest->n_un.n_strx);
1271 dounlock();
1272 return 1;
1277 * Implement the dlfunc() interface, which behaves exactly the same as
1278 * dlsym() except that it returns a function pointer instead of a data
1279 * pointer. This can be used by applications to avoid compiler warnings
1280 * about undefined behavior, and is intended as prior art for future
1281 * POSIX standardization. This function requires that all pointer types
1282 * have the same representation, which is true on all platforms FreeBSD
1283 * runs on, but is not guaranteed by the C standard.
1285 #if 0
1286 dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol)
1288 union
1290 void *d;
1291 dlfunc_t f;
1292 } rv;
1293 int sym_len = strlen(symbol);
1294 char *malloc_sym = NULL;
1295 dolock();
1296 malloc_sym = malloc(sym_len + 2);
1297 if (malloc_sym)
1299 sprintf(malloc_sym, "_%s", symbol);
1300 rv.d = dlsymIntern(handle, malloc_sym, 1);
1301 free(malloc_sym);
1303 else
1305 error("Unable to allocate memory");
1306 goto dlfuncerror;
1308 dounlock();
1309 return rv.f;
1310 dlfuncerror:
1311 dounlock();
1312 return NULL;
1314 #endif