5f494590493ed7bfe8f8ec5098b0d6933fae7912
[sbcl.git] / src / runtime / darwin-dlshim.c
1 /*
2  * These functions emulate a small subset of the dlopen / dlsym
3  * functionality under Darwin's Mach-O dyld system.
4  */
5
6 /*
7  * This software is part of the SBCL system. See the README file for
8  * more information.
9  *
10  * This software is derived from the CMU CL system, which was
11  * written at Carnegie Mellon University and released into the
12  * public domain. The software is in the public domain and is
13  * provided with absolutely no warranty. See the COPYING and CREDITS
14  * files for more information.
15  */
16
17
18 #include <mach-o/dyld.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "darwin-dlshim.h"
23
24 /* Darwin does not define the standard ELF
25  * dlopen/dlclose/dlsym/dlerror interface to shared libraries, so this
26  * is an attempt at a minimal wrapper to allow SBCL to work without
27  * external dependency on pogma's dlcompat library.
28  */
29
30 /* For now, there is no RTLD_GLOBAL emulation either. */
31
32 static char dl_self; /* I'm going to abuse this */
33
34 static int callback_count;
35 static struct mach_header* last_header;
36
37 #define DLSYM_ERROR 1
38 #define DLOPEN_ERROR 2
39
40 static int last_error = 0;
41
42 void
43 dlshim_image_callback(struct mach_header* ptr, unsigned long phooey)
44 {
45     callback_count++;
46     last_header = ptr;
47 }
48
49 int
50 lib_path_count(void)
51 {
52     char* libpath;
53     int i;
54     int count;
55     libpath = getenv("DYLD_LIBRARY_PATH");
56     count = 1;
57     if (libpath) {
58         for (i = 0; libpath[i] != '\0'; i++) {
59             if (libpath[i] == ':') count++;
60         }
61     }
62     return count;
63 }
64
65 const char*
66 lib_path_prefixify(int index, const char* filename)
67 {
68     static char* retbuf = NULL;
69     int fi, li, i, count;
70     char* libpath;
71     if (!retbuf) {
72         retbuf = (char*) malloc(1024*sizeof(char));
73     }
74     count = 0;
75     fi = 0;
76     li = -1;
77     libpath = getenv("DYLD_LIBRARY_PATH");
78     if (libpath) {
79         i = 0;
80         while (count != index && libpath[i] != '\0') {
81             if (libpath[i] == ':') count++;
82             i++;
83         }
84         fi = i;
85         while (libpath[i] != '\0' && libpath[i] != ':') {
86             i++;
87         }
88         li = i - 1;
89     }
90     if (li - fi > 0) {
91         if (li - fi + 1 > 1022 - strlen(filename)) {
92             retbuf =
93                 (char*) realloc(retbuf, (li - fi + 3 + strlen(filename))*sizeof(char));
94         }
95         memcpy(retbuf, libpath + fi, (li - fi + 1)*sizeof(char));
96         retbuf[li - fi + 1] = '/';
97         memcpy(retbuf + li - fi + 2, filename, strlen(filename) + 1);
98         return retbuf;
99     } else {
100         return filename;
101     }
102 }
103
104 const void*
105 dlopen(const char* filename, int flags)
106 {
107     static char has_callback = 0;
108     if (!has_callback) {
109         _dyld_register_func_for_add_image(dlshim_image_callback);
110     }
111     if (!filename) {
112         return &dl_self;
113     } else {
114         const struct mach_header* img = NULL;
115         if (!img)
116             img = NSAddImage(filename, NSADDIMAGE_OPTION_RETURN_ON_ERROR);
117         if (!img)
118             img = NSAddImage(filename,
119                              NSADDIMAGE_OPTION_RETURN_ON_ERROR |
120                              NSADDIMAGE_OPTION_WITH_SEARCHING);
121         if (!img) {
122             NSObjectFileImage fileImage;
123             callback_count = 0;
124             last_header = NULL;
125             if (NSCreateObjectFileImageFromFile(filename, &fileImage)
126                 == NSObjectFileImageSuccess) {
127                 NSLinkModule(fileImage, filename,
128                              NSLINKMODULE_OPTION_BINDNOW |
129                              ((flags & RTLD_GLOBAL)?NSLINKMODULE_OPTION_PRIVATE:0) |
130                              NSLINKMODULE_OPTION_RETURN_ON_ERROR);
131                 if (callback_count && last_header)
132                     img = last_header;
133             }
134         }
135         if (!img) {
136             NSObjectFileImage fileImage;
137             int i, maxi;
138             const char* prefixfilename;
139             maxi = lib_path_count();
140             for (i = 0; i < maxi && !img; i++) {
141                 prefixfilename = lib_path_prefixify(i, filename);
142                 callback_count = 0;
143                 last_header = NULL;
144                 if (NSCreateObjectFileImageFromFile(prefixfilename, &fileImage)
145                     == NSObjectFileImageSuccess) {
146                     NSLinkModule(fileImage, filename,
147                                  NSLINKMODULE_OPTION_BINDNOW |
148                                  ((flags & RTLD_GLOBAL)?NSLINKMODULE_OPTION_PRIVATE:0) |
149                                  NSLINKMODULE_OPTION_RETURN_ON_ERROR);
150                     if (callback_count && last_header)
151                         img = last_header;
152                 }
153             }
154         }
155         if (img) {
156             if (flags & RTLD_NOW) {
157                 NSLookupSymbolInImage(img, "",
158                                       NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY |
159                                       NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
160             }
161             if (NSIsSymbolNameDefinedInImage(img, "__init")) {
162                 NSSymbol* initsymbol;
163                 void (*initfunc) (void);
164                 initsymbol = NSLookupSymbolInImage(img, "__init", 0);
165                 initfunc = NSAddressOfSymbol(initsymbol);
166                 initfunc();
167             }
168         } else
169             last_error = DLOPEN_ERROR;
170         return img;
171     }
172 }
173
174 const char*
175 dlerror()
176 {
177     NSLinkEditErrors c;
178     int errorNumber;
179     const char *fileName, *errorString;
180     char *result = NULL;
181
182     if (last_error) {
183         NSLinkEditError(&c, &errorNumber, &fileName, &errorString);
184         /* The errorString obtained by the above is too verbose for
185          * our needs, so we just translate the errno.
186          *
187          * We also have simple fallbacks in case we've somehow lost
188          * the context before this point. */
189         if (errorNumber) {
190             result = strerror(errorNumber);
191         } else if (DLSYM_ERROR == last_error) {
192             result = "dlsym(3) failed";
193         } else if (DLOPEN_ERROR == last_error) {
194             result = "dlopen(3) failed";
195         }
196         last_error = 0;
197     }
198
199     return result;
200 }
201
202 void*
203 dlsym(void* handle, char* symbol)
204 {
205     if (handle == &dl_self) {
206         if (NSIsSymbolNameDefined(symbol)) {
207             NSSymbol* retsym;
208             retsym = NSLookupAndBindSymbol(symbol);
209             return NSAddressOfSymbol(retsym);
210         } else {
211             last_error = DLSYM_ERROR;
212             return NULL;
213         }
214     } else {
215         if (NSIsSymbolNameDefinedInImage(handle, symbol)) {
216             NSSymbol* retsym;
217             retsym = NSLookupSymbolInImage(handle, symbol, 0);
218             return NSAddressOfSymbol(retsym);
219         } else {
220             last_error = DLSYM_ERROR;
221             return NULL;
222         }
223     }
224 }
225
226 int
227 dlclose(void *handle)
228 {
229     /* dlclose is not implemented, and never will be for dylibs.
230      * return -1 to signal an error; it's not used by SBCL anyhow */
231     return -1;
232 }