1 | /* |
---|
2 | * This file was modified by Christoph Pfisterer <cp@chrisp.de> |
---|
3 | * on Tue, Jan 23 2001. See the file "ChangeLog" for details of what |
---|
4 | * was changed. |
---|
5 | * |
---|
6 | * |
---|
7 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. |
---|
8 | * |
---|
9 | * @APPLE_LICENSE_HEADER_START@ |
---|
10 | * |
---|
11 | * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights |
---|
12 | * Reserved. This file contains Original Code and/or Modifications of |
---|
13 | * Original Code as defined in and that are subject to the Apple Public |
---|
14 | * Source License Version 1.1 (the "License"). You may not use this file |
---|
15 | * except in compliance with the License. Please obtain a copy of the |
---|
16 | * License at http://www.apple.com/publicsource and read it before using |
---|
17 | * this file. |
---|
18 | * |
---|
19 | * The Original Code and all software distributed under the License are |
---|
20 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
---|
21 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
---|
22 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
---|
23 | * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the |
---|
24 | * License for the specific language governing rights and limitations |
---|
25 | * under the License. |
---|
26 | * |
---|
27 | * @APPLE_LICENSE_HEADER_END@ |
---|
28 | */ |
---|
29 | #include <stdio.h> |
---|
30 | #include <stdlib.h> |
---|
31 | #include <string.h> |
---|
32 | #include <errno.h> |
---|
33 | #include <sys/types.h> |
---|
34 | #include <sys/stat.h> |
---|
35 | #include <limits.h> |
---|
36 | #include "mach-o/dyld.h" |
---|
37 | #include "dlfcn.h" |
---|
38 | |
---|
39 | /* |
---|
40 | * debugging macros |
---|
41 | */ |
---|
42 | #if DEBUG > 0 |
---|
43 | #define DEBUG_PRINT(format) fprintf(stderr,(format));fflush(stderr) |
---|
44 | #define DEBUG_PRINT1(format,arg1) fprintf(stderr,(format),(arg1));\ |
---|
45 | fflush(stderr) |
---|
46 | #define DEBUG_PRINT2(format,arg1,arg2) fprintf(stderr,(format),\ |
---|
47 | (arg1),(arg2));fflush(stderr) |
---|
48 | #define DEBUG_PRINT3(format,arg1,arg2,arg3) fprintf(stderr,(format),\ |
---|
49 | (arg1),(arg2),(arg3));fflush(stderr) |
---|
50 | #else |
---|
51 | #define DEBUG_PRINT(format) /**/ |
---|
52 | #define DEBUG_PRINT1(format,arg1) /**/ |
---|
53 | #define DEBUG_PRINT2(format,arg1,arg2) /**/ |
---|
54 | #define DEBUG_PRINT3(format,arg1,arg2,arg3) /**/ |
---|
55 | #undef DEBUG |
---|
56 | #endif |
---|
57 | |
---|
58 | /* |
---|
59 | * The structure of a dlopen() handle. |
---|
60 | */ |
---|
61 | struct dlopen_handle { |
---|
62 | dev_t dev; /* the path's device and inode number from stat(2) */ |
---|
63 | ino_t ino; |
---|
64 | int dlopen_mode; /* current dlopen mode for this handle */ |
---|
65 | int dlopen_count; /* number of times dlopen() called on this handle */ |
---|
66 | NSModule module; /* the NSModule returned by NSLinkModule() */ |
---|
67 | struct dlopen_handle *prev; |
---|
68 | struct dlopen_handle *next; |
---|
69 | }; |
---|
70 | static struct dlopen_handle *dlopen_handles = NULL; |
---|
71 | static const struct dlopen_handle main_program_handle = {NULL}; |
---|
72 | static char *dlerror_pointer = NULL; |
---|
73 | |
---|
74 | /* |
---|
75 | * NSMakePrivateModulePublic() is not part of the public dyld API so we define |
---|
76 | * it here. The internal dyld function pointer for |
---|
77 | * __dyld_NSMakePrivateModulePublic is returned so thats all that maters to get |
---|
78 | * the functionality need to implement the dlopen() interfaces. |
---|
79 | */ |
---|
80 | static |
---|
81 | int |
---|
82 | NSMakePrivateModulePublic( |
---|
83 | NSModule module) |
---|
84 | { |
---|
85 | static int (*p)(NSModule module) = NULL; |
---|
86 | |
---|
87 | if(p == NULL) |
---|
88 | _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", |
---|
89 | (unsigned long *)&p); |
---|
90 | if(p == NULL){ |
---|
91 | #ifdef DEBUG |
---|
92 | printf("_dyld_func_lookup of __dyld_NSMakePrivateModulePublic " |
---|
93 | "failed\n"); |
---|
94 | #endif |
---|
95 | return(FALSE); |
---|
96 | } |
---|
97 | return(p(module)); |
---|
98 | } |
---|
99 | |
---|
100 | /* |
---|
101 | * helper routine: search for a named module in various locations |
---|
102 | */ |
---|
103 | static |
---|
104 | int |
---|
105 | _dl_search_paths( |
---|
106 | const char *filename, |
---|
107 | char *pathbuf, |
---|
108 | struct stat *stat_buf) |
---|
109 | { |
---|
110 | const char *pathspec; |
---|
111 | const char *element; |
---|
112 | const char *p; |
---|
113 | char *q; |
---|
114 | char *pathbuf_end; |
---|
115 | const char *envvars[] = { |
---|
116 | "$DYLD_LIBRARY_PATH", |
---|
117 | "$LD_LIBRARY_PATH", |
---|
118 | "/usr/lib:/lib", |
---|
119 | NULL }; |
---|
120 | int envvar_index; |
---|
121 | |
---|
122 | pathbuf_end = pathbuf + PATH_MAX - 8; |
---|
123 | |
---|
124 | for(envvar_index = 0; envvars[envvar_index]; envvar_index++){ |
---|
125 | if(envvars[envvar_index][0] == '$'){ |
---|
126 | pathspec = getenv(envvars[envvar_index]+1); |
---|
127 | } |
---|
128 | else { |
---|
129 | pathspec = envvars[envvar_index]; |
---|
130 | } |
---|
131 | |
---|
132 | if(pathspec != NULL){ |
---|
133 | element = pathspec; |
---|
134 | while(*element){ |
---|
135 | /* extract path list element */ |
---|
136 | p = element; |
---|
137 | q = pathbuf; |
---|
138 | while(*p && *p != PATHS_DELIMITER && q < pathbuf_end) { |
---|
139 | *q++ = *p++; |
---|
140 | } |
---|
141 | if(q == pathbuf){ /* empty element */ |
---|
142 | if(*p){ |
---|
143 | element = p+1; |
---|
144 | continue; |
---|
145 | } |
---|
146 | break; |
---|
147 | } |
---|
148 | if (*p){ |
---|
149 | element = p+1; |
---|
150 | } |
---|
151 | else{ |
---|
152 | element = p; /* this terminates the loop */ |
---|
153 | } |
---|
154 | |
---|
155 | /* add slash if neccessary */ |
---|
156 | if(*(q-1) != '/' && q < pathbuf_end){ |
---|
157 | *q++ = '/'; |
---|
158 | } |
---|
159 | |
---|
160 | /* append module name */ |
---|
161 | p = filename; |
---|
162 | while(*p && q < pathbuf_end) *q++ = *p++; |
---|
163 | *q++ = 0; |
---|
164 | |
---|
165 | if(q >= pathbuf_end){ |
---|
166 | /* maybe add an error message here */ |
---|
167 | break; |
---|
168 | } |
---|
169 | |
---|
170 | if(stat(pathbuf, stat_buf) == 0){ |
---|
171 | return 0; |
---|
172 | } |
---|
173 | } |
---|
174 | } |
---|
175 | } |
---|
176 | |
---|
177 | /* we have searched everywhere, now we give up */ |
---|
178 | return -1; |
---|
179 | } |
---|
180 | |
---|
181 | /* |
---|
182 | * dlopen() the MacOS X version of the FreeBSD dlopen() interface. |
---|
183 | */ |
---|
184 | void * |
---|
185 | dlopen( |
---|
186 | const char *path, |
---|
187 | int mode) |
---|
188 | { |
---|
189 | const char *module_path; |
---|
190 | void *retval; |
---|
191 | struct stat stat_buf; |
---|
192 | NSObjectFileImage objectFileImage; |
---|
193 | NSObjectFileImageReturnCode ofile_result_code; |
---|
194 | NSModule module; |
---|
195 | struct dlopen_handle *p; |
---|
196 | unsigned long options; |
---|
197 | NSSymbol NSSymbol; |
---|
198 | void (*init)(void); |
---|
199 | char pathbuf[PATH_MAX]; |
---|
200 | |
---|
201 | DEBUG_PRINT2("libdl: dlopen(%s,0x%x) -> ", path, (unsigned int)mode); |
---|
202 | |
---|
203 | dlerror_pointer = NULL; |
---|
204 | /* |
---|
205 | * A NULL path is to indicate the caller wants a handle for the |
---|
206 | * main program. |
---|
207 | */ |
---|
208 | if(path == NULL){ |
---|
209 | retval = (void *)&main_program_handle; |
---|
210 | DEBUG_PRINT1("main / %p\n", retval); |
---|
211 | return(retval); |
---|
212 | } |
---|
213 | |
---|
214 | /* see if the path exists and if so get the device and inode number */ |
---|
215 | if(stat(path, &stat_buf) == -1){ |
---|
216 | dlerror_pointer = strerror(errno); |
---|
217 | |
---|
218 | if(path[0] == '/'){ |
---|
219 | DEBUG_PRINT1("ERROR (stat): %s\n", dlerror_pointer); |
---|
220 | return(NULL); |
---|
221 | } |
---|
222 | |
---|
223 | /* search for the module in various places */ |
---|
224 | if(_dl_search_paths(path, pathbuf, &stat_buf)){ |
---|
225 | /* dlerror_pointer is unmodified */ |
---|
226 | DEBUG_PRINT1("ERROR (stat): %s\n", dlerror_pointer); |
---|
227 | return(NULL); |
---|
228 | } |
---|
229 | DEBUG_PRINT1("found %s -> ", pathbuf); |
---|
230 | module_path = pathbuf; |
---|
231 | dlerror_pointer = NULL; |
---|
232 | } |
---|
233 | else{ |
---|
234 | module_path = path; |
---|
235 | } |
---|
236 | |
---|
237 | /* |
---|
238 | * If we don't want an unshared handle see if we already have a handle |
---|
239 | * for this path. |
---|
240 | */ |
---|
241 | if((mode & RTLD_UNSHARED) != RTLD_UNSHARED){ |
---|
242 | p = dlopen_handles; |
---|
243 | while(p != NULL){ |
---|
244 | if(p->dev == stat_buf.st_dev && p->ino == stat_buf.st_ino){ |
---|
245 | /* skip unshared handles */ |
---|
246 | if((p->dlopen_mode & RTLD_UNSHARED) == RTLD_UNSHARED) |
---|
247 | continue; |
---|
248 | /* |
---|
249 | * We have already created a handle for this path. The |
---|
250 | * caller might be trying to promote an RTLD_LOCAL handle |
---|
251 | * to a RTLD_GLOBAL. Or just looking it up with |
---|
252 | * RTLD_NOLOAD. |
---|
253 | */ |
---|
254 | if((p->dlopen_mode & RTLD_LOCAL) == RTLD_LOCAL && |
---|
255 | (mode & RTLD_GLOBAL) == RTLD_GLOBAL){ |
---|
256 | /* promote the handle */ |
---|
257 | if(NSMakePrivateModulePublic(p->module) == TRUE){ |
---|
258 | p->dlopen_mode &= ~RTLD_LOCAL; |
---|
259 | p->dlopen_mode |= RTLD_GLOBAL; |
---|
260 | p->dlopen_count++; |
---|
261 | DEBUG_PRINT1("%p\n", p); |
---|
262 | return(p); |
---|
263 | } |
---|
264 | else{ |
---|
265 | dlerror_pointer = "can't promote handle from " |
---|
266 | "RTLD_LOCAL to RTLD_GLOBAL"; |
---|
267 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
268 | return(NULL); |
---|
269 | } |
---|
270 | } |
---|
271 | p->dlopen_count++; |
---|
272 | DEBUG_PRINT1("%p\n", p); |
---|
273 | return(p); |
---|
274 | } |
---|
275 | p = p->next; |
---|
276 | } |
---|
277 | } |
---|
278 | |
---|
279 | /* |
---|
280 | * We do not have a handle for this path if we were just trying to |
---|
281 | * look it up return NULL to indicate we don't have it. |
---|
282 | */ |
---|
283 | if((mode & RTLD_NOLOAD) == RTLD_NOLOAD){ |
---|
284 | dlerror_pointer = "no existing handle for path RTLD_NOLOAD test"; |
---|
285 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
286 | return(NULL); |
---|
287 | } |
---|
288 | |
---|
289 | /* try to create an object file image from this path */ |
---|
290 | ofile_result_code = NSCreateObjectFileImageFromFile(module_path, |
---|
291 | &objectFileImage); |
---|
292 | if(ofile_result_code != NSObjectFileImageSuccess){ |
---|
293 | switch(ofile_result_code){ |
---|
294 | case NSObjectFileImageFailure: |
---|
295 | dlerror_pointer = "object file setup failure"; |
---|
296 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
297 | return(NULL); |
---|
298 | case NSObjectFileImageInappropriateFile: |
---|
299 | dlerror_pointer = "not a Mach-O MH_BUNDLE file type"; |
---|
300 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
301 | return(NULL); |
---|
302 | case NSObjectFileImageArch: |
---|
303 | dlerror_pointer = "no object for this architecture"; |
---|
304 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
305 | return(NULL); |
---|
306 | case NSObjectFileImageFormat: |
---|
307 | dlerror_pointer = "bad object file format"; |
---|
308 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
309 | return(NULL); |
---|
310 | case NSObjectFileImageAccess: |
---|
311 | dlerror_pointer = "can't read object file"; |
---|
312 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
313 | return(NULL); |
---|
314 | default: |
---|
315 | dlerror_pointer = "unknown error from " |
---|
316 | "NSCreateObjectFileImageFromFile()"; |
---|
317 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
318 | return(NULL); |
---|
319 | } |
---|
320 | } |
---|
321 | |
---|
322 | /* try to link in this object file image */ |
---|
323 | options = NSLINKMODULE_OPTION_PRIVATE; |
---|
324 | if((mode & RTLD_NOW) == RTLD_NOW) |
---|
325 | options |= NSLINKMODULE_OPTION_BINDNOW; |
---|
326 | module = NSLinkModule(objectFileImage, module_path, options); |
---|
327 | NSDestroyObjectFileImage(objectFileImage) ; |
---|
328 | if(module == NULL){ |
---|
329 | dlerror_pointer = "NSLinkModule() failed for dlopen()"; |
---|
330 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
331 | return(NULL); |
---|
332 | } |
---|
333 | |
---|
334 | /* |
---|
335 | * If the handle is to be global promote the handle. It is done this |
---|
336 | * way to avoid multiply defined symbols. |
---|
337 | */ |
---|
338 | if((mode & RTLD_GLOBAL) == RTLD_GLOBAL){ |
---|
339 | if(NSMakePrivateModulePublic(module) == FALSE){ |
---|
340 | dlerror_pointer = "can't promote handle from RTLD_LOCAL to " |
---|
341 | "RTLD_GLOBAL"; |
---|
342 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
343 | return(NULL); |
---|
344 | } |
---|
345 | } |
---|
346 | |
---|
347 | p = malloc(sizeof(struct dlopen_handle)); |
---|
348 | if(p == NULL){ |
---|
349 | dlerror_pointer = "can't allocate memory for the dlopen handle"; |
---|
350 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
351 | return(NULL); |
---|
352 | } |
---|
353 | |
---|
354 | /* fill in the handle */ |
---|
355 | p->dev = stat_buf.st_dev; |
---|
356 | p->ino = stat_buf.st_ino; |
---|
357 | if(mode & RTLD_GLOBAL) |
---|
358 | p->dlopen_mode = RTLD_GLOBAL; |
---|
359 | else |
---|
360 | p->dlopen_mode = RTLD_LOCAL; |
---|
361 | p->dlopen_mode |= (mode & RTLD_UNSHARED) | |
---|
362 | (mode & RTLD_NODELETE) | |
---|
363 | (mode & RTLD_LAZY_UNDEF); |
---|
364 | p->dlopen_count = 1; |
---|
365 | p->module = module; |
---|
366 | p->prev = NULL; |
---|
367 | p->next = dlopen_handles; |
---|
368 | if(dlopen_handles != NULL) |
---|
369 | dlopen_handles->prev = p; |
---|
370 | dlopen_handles = p; |
---|
371 | |
---|
372 | /* call the init function if one exists */ |
---|
373 | NSSymbol = NSLookupSymbolInModule(p->module, "__init"); |
---|
374 | if(NSSymbol != NULL){ |
---|
375 | init = NSAddressOfSymbol(NSSymbol); |
---|
376 | init(); |
---|
377 | } |
---|
378 | |
---|
379 | DEBUG_PRINT1("%p\n", p); |
---|
380 | return(p); |
---|
381 | } |
---|
382 | |
---|
383 | /* |
---|
384 | * dlsym() the MacOS X version of the FreeBSD dlopen() interface. |
---|
385 | */ |
---|
386 | void * |
---|
387 | dlsym( |
---|
388 | void * handle, |
---|
389 | const char *symbol) |
---|
390 | { |
---|
391 | struct dlopen_handle *dlopen_handle, *p; |
---|
392 | NSSymbol NSSymbol; |
---|
393 | void *address; |
---|
394 | |
---|
395 | DEBUG_PRINT2("libdl: dlsym(%p,%s) -> ", handle, symbol); |
---|
396 | |
---|
397 | dlopen_handle = (struct dlopen_handle *)handle; |
---|
398 | |
---|
399 | /* |
---|
400 | * If this is the handle for the main program do a global lookup. |
---|
401 | */ |
---|
402 | if(dlopen_handle == (struct dlopen_handle *)&main_program_handle){ |
---|
403 | if(NSIsSymbolNameDefined(symbol) == TRUE){ |
---|
404 | NSSymbol = NSLookupAndBindSymbol(symbol); |
---|
405 | address = NSAddressOfSymbol(NSSymbol); |
---|
406 | dlerror_pointer = NULL; |
---|
407 | DEBUG_PRINT1("%p\n", address); |
---|
408 | return(address); |
---|
409 | } |
---|
410 | else{ |
---|
411 | dlerror_pointer = "symbol not found"; |
---|
412 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
413 | return(NULL); |
---|
414 | } |
---|
415 | } |
---|
416 | |
---|
417 | /* |
---|
418 | * Find this handle and do a lookup in just this module. |
---|
419 | */ |
---|
420 | p = dlopen_handles; |
---|
421 | while(p != NULL){ |
---|
422 | if(dlopen_handle == p){ |
---|
423 | NSSymbol = NSLookupSymbolInModule(p->module, symbol); |
---|
424 | if(NSSymbol != NULL){ |
---|
425 | address = NSAddressOfSymbol(NSSymbol); |
---|
426 | dlerror_pointer = NULL; |
---|
427 | DEBUG_PRINT1("%p\n", address); |
---|
428 | return(address); |
---|
429 | } |
---|
430 | else{ |
---|
431 | dlerror_pointer = "symbol not found"; |
---|
432 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
433 | return(NULL); |
---|
434 | } |
---|
435 | } |
---|
436 | p = p->next; |
---|
437 | } |
---|
438 | |
---|
439 | dlerror_pointer = "bad handle passed to dlsym()"; |
---|
440 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
441 | return(NULL); |
---|
442 | } |
---|
443 | |
---|
444 | /* |
---|
445 | * dlerror() the MacOS X version of the FreeBSD dlopen() interface. |
---|
446 | */ |
---|
447 | const char * |
---|
448 | dlerror( |
---|
449 | void) |
---|
450 | { |
---|
451 | const char *p; |
---|
452 | |
---|
453 | p = (const char *)dlerror_pointer; |
---|
454 | dlerror_pointer = NULL; |
---|
455 | return(p); |
---|
456 | } |
---|
457 | |
---|
458 | /* |
---|
459 | * dlclose() the MacOS X version of the FreeBSD dlopen() interface. |
---|
460 | */ |
---|
461 | int |
---|
462 | dlclose( |
---|
463 | void * handle) |
---|
464 | { |
---|
465 | struct dlopen_handle *p, *q; |
---|
466 | unsigned long options; |
---|
467 | NSSymbol NSSymbol; |
---|
468 | void (*fini)(void); |
---|
469 | |
---|
470 | DEBUG_PRINT1("libdl: dlclose(%p) -> ", handle); |
---|
471 | |
---|
472 | dlerror_pointer = NULL; |
---|
473 | q = (struct dlopen_handle *)handle; |
---|
474 | p = dlopen_handles; |
---|
475 | while(p != NULL){ |
---|
476 | if(p == q){ |
---|
477 | /* if the dlopen() count is not zero we are done */ |
---|
478 | p->dlopen_count--; |
---|
479 | if(p->dlopen_count != 0){ |
---|
480 | DEBUG_PRINT("OK"); |
---|
481 | return(0); |
---|
482 | } |
---|
483 | |
---|
484 | /* call the fini function if one exists */ |
---|
485 | NSSymbol = NSLookupSymbolInModule(p->module, "__fini"); |
---|
486 | if(NSSymbol != NULL){ |
---|
487 | fini = NSAddressOfSymbol(NSSymbol); |
---|
488 | fini(); |
---|
489 | } |
---|
490 | |
---|
491 | /* unlink the module for this handle */ |
---|
492 | options = 0; |
---|
493 | if(p->dlopen_mode & RTLD_NODELETE) |
---|
494 | options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; |
---|
495 | if(p->dlopen_mode & RTLD_LAZY_UNDEF) |
---|
496 | options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; |
---|
497 | if(NSUnLinkModule(p->module, options) == FALSE){ |
---|
498 | dlerror_pointer = "NSUnLinkModule() failed for dlclose()"; |
---|
499 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
500 | return(-1); |
---|
501 | } |
---|
502 | if(p->prev != NULL) |
---|
503 | p->prev->next = p->next; |
---|
504 | if(p->next != NULL) |
---|
505 | p->next->prev = p->prev; |
---|
506 | if(dlopen_handles == p) |
---|
507 | dlopen_handles = p->next; |
---|
508 | free(p); |
---|
509 | DEBUG_PRINT("OK"); |
---|
510 | return(0); |
---|
511 | } |
---|
512 | p = p->next; |
---|
513 | } |
---|
514 | dlerror_pointer = "invalid handle passed to dlclose()"; |
---|
515 | DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer); |
---|
516 | return(-1); |
---|
517 | } |
---|