Commit | Line | Data |
---|---|---|
8d138742 CE |
1 | /* loader-dyld.c -- dynamic linking on darwin and OS X |
2 | ||
3 | Copyright (C) 1998, 1999, 2000, 2004, 2006, | |
4 | 2007, 2008 Free Software Foundation, Inc. | |
5 | Written by Peter O'Gorman, 1998 | |
6 | ||
7 | NOTE: The canonical source of this file is maintained with the | |
8 | GNU Libtool package. Report bugs to bug-libtool@gnu.org. | |
9 | ||
10 | GNU Libltdl is free software; you can redistribute it and/or | |
11 | modify it under the terms of the GNU Lesser General Public | |
12 | License as published by the Free Software Foundation; either | |
13 | version 2 of the License, or (at your option) any later version. | |
14 | ||
15 | As a special exception to the GNU Lesser General Public License, | |
16 | if you distribute this file as part of a program or library that | |
17 | is built using GNU Libtool, you may include this file under the | |
18 | same distribution terms that you use for the rest of that program. | |
19 | ||
20 | GNU Libltdl is distributed in the hope that it will be useful, | |
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | GNU Lesser General Public License for more details. | |
24 | ||
25 | You should have received a copy of the GNU Lesser General Public | |
26 | License along with GNU Libltdl; see the file COPYING.LIB. If not, a | |
27 | copy can be downloaded from http://www.gnu.org/licenses/lgpl.html, | |
28 | or obtained by writing to the Free Software Foundation, Inc., | |
29 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
30 | */ | |
31 | ||
32 | #include "lt__private.h" | |
33 | #include "lt_dlloader.h" | |
34 | ||
35 | /* Use the preprocessor to rename non-static symbols to avoid namespace | |
36 | collisions when the loader code is statically linked into libltdl. | |
37 | Use the "<module_name>_LTX_" prefix so that the symbol addresses can | |
38 | be fetched from the preloaded symbol list by lt_dlsym(): */ | |
39 | #define get_vtable dyld_LTX_get_vtable | |
40 | ||
41 | LT_BEGIN_C_DECLS | |
42 | LT_SCOPE lt_dlvtable *get_vtable (lt_user_data loader_data); | |
43 | LT_END_C_DECLS | |
44 | ||
45 | ||
46 | /* Boilerplate code to set up the vtable for hooking this loader into | |
47 | libltdl's loader list: */ | |
48 | static int vl_init (lt_user_data loader_data); | |
49 | static int vl_exit (lt_user_data loader_data); | |
50 | static lt_module vm_open (lt_user_data loader_data, const char *filename, | |
51 | lt_dladvise advise); | |
52 | static int vm_close (lt_user_data loader_data, lt_module module); | |
53 | static void * vm_sym (lt_user_data loader_data, lt_module module, | |
54 | const char *symbolname); | |
55 | ||
56 | static lt_dlvtable *vtable = 0; | |
57 | ||
58 | /* Return the vtable for this loader, only the name and sym_prefix | |
59 | attributes (plus the virtual function implementations, obviously) | |
60 | change between loaders. */ | |
61 | lt_dlvtable * | |
62 | get_vtable (lt_user_data loader_data) | |
63 | { | |
64 | if (!vtable) | |
65 | { | |
66 | vtable = lt__zalloc (sizeof *vtable); | |
67 | } | |
68 | ||
69 | if (vtable && !vtable->name) | |
70 | { | |
71 | vtable->name = "lt_dyld"; | |
72 | vtable->sym_prefix = "_"; | |
73 | vtable->dlloader_init = vl_init; | |
74 | vtable->module_open = vm_open; | |
75 | vtable->module_close = vm_close; | |
76 | vtable->find_sym = vm_sym; | |
77 | vtable->dlloader_exit = vl_exit; | |
78 | vtable->dlloader_data = loader_data; | |
79 | vtable->priority = LT_DLLOADER_APPEND; | |
80 | } | |
81 | ||
82 | if (vtable && (vtable->dlloader_data != loader_data)) | |
83 | { | |
84 | LT__SETERROR (INIT_LOADER); | |
85 | return 0; | |
86 | } | |
87 | ||
88 | return vtable; | |
89 | } | |
90 | ||
91 | ||
92 | ||
93 | /* --- IMPLEMENTATION --- */ | |
94 | ||
95 | ||
96 | #if defined(HAVE_MACH_O_DYLD_H) | |
97 | # if !defined(__APPLE_CC__) && !defined(__MWERKS__) && !defined(__private_extern__) | |
98 | /* Is this correct? Does it still function properly? */ | |
99 | # define __private_extern__ extern | |
100 | # endif | |
101 | # include <mach-o/dyld.h> | |
102 | #endif | |
103 | ||
104 | #include <mach-o/getsect.h> | |
105 | ||
106 | /* We have to put some stuff here that isn't in older dyld.h files */ | |
107 | #if !defined(ENUM_DYLD_BOOL) | |
108 | # define ENUM_DYLD_BOOL | |
109 | # undef FALSE | |
110 | # undef TRUE | |
111 | enum DYLD_BOOL { | |
112 | FALSE, | |
113 | TRUE | |
114 | }; | |
115 | #endif | |
116 | #if !defined(LC_REQ_DYLD) | |
117 | # define LC_REQ_DYLD 0x80000000 | |
118 | #endif | |
119 | #if !defined(LC_LOAD_WEAK_DYLIB) | |
120 | # define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) | |
121 | #endif | |
122 | ||
123 | #if !defined(NSADDIMAGE_OPTION_NONE) | |
124 | # define NSADDIMAGE_OPTION_NONE 0x0 | |
125 | #endif | |
126 | #if !defined(NSADDIMAGE_OPTION_RETURN_ON_ERROR) | |
127 | # define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 | |
128 | #endif | |
129 | #if !defined(NSADDIMAGE_OPTION_WITH_SEARCHING) | |
130 | # define NSADDIMAGE_OPTION_WITH_SEARCHING 0x2 | |
131 | #endif | |
132 | #if !defined(NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) | |
133 | # define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 | |
134 | #endif | |
135 | #if !defined(NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME) | |
136 | # define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 | |
137 | #endif | |
138 | ||
139 | #if !defined(NSLOOKUPSYMBOLINIMAGE_OPTION_BIND) | |
140 | # define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 | |
141 | #endif | |
142 | #if !defined(NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW) | |
143 | # define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW 0x1 | |
144 | #endif | |
145 | #if !defined(NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY) | |
146 | # define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY 0x2 | |
147 | #endif | |
148 | #if !defined(NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR) | |
149 | # define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 | |
150 | #endif | |
151 | ||
152 | #define LT__SYMLOOKUP_OPTS (NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW \ | |
153 | | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR) | |
154 | ||
155 | #if defined(__BIG_ENDIAN__) | |
156 | # define LT__MAGIC MH_MAGIC | |
157 | #else | |
158 | # define LT__MAGIC MH_CIGAM | |
159 | #endif | |
160 | ||
161 | #define DYLD__SETMYERROR(errmsg) LT__SETERRORSTR (dylderror (errmsg)) | |
162 | #define DYLD__SETERROR(errcode) DYLD__SETMYERROR (LT__STRERROR (errcode)) | |
163 | ||
164 | typedef struct mach_header mach_header; | |
165 | typedef struct dylib_command dylib_command; | |
166 | ||
167 | static const char *dylderror (const char *errmsg); | |
168 | static const mach_header *lt__nsmodule_get_header (NSModule module); | |
169 | static const char *lt__header_get_instnam (const mach_header *mh); | |
170 | static const mach_header *lt__match_loadedlib (const char *name); | |
171 | static NSSymbol lt__linkedlib_symbol (const char *symname, const mach_header *mh); | |
172 | ||
173 | static const mach_header *(*lt__addimage) (const char *image_name, | |
174 | unsigned long options) = 0; | |
175 | static NSSymbol (*lt__image_symbol) (const mach_header *image, | |
176 | const char *symbolName, | |
177 | unsigned long options) = 0; | |
178 | static enum DYLD_BOOL (*lt__image_symbol_p) (const mach_header *image, | |
179 | const char *symbolName) = 0; | |
180 | static enum DYLD_BOOL (*lt__module_export) (NSModule module) = 0; | |
181 | ||
182 | static int dyld_cannot_close = 0; | |
183 | ||
184 | ||
185 | /* A function called through the vtable when this loader is no | |
186 | longer needed by the application. */ | |
187 | static int | |
188 | vl_exit (lt_user_data LT__UNUSED loader_data) | |
189 | { | |
190 | vtable = NULL; | |
191 | return 0; | |
192 | } | |
193 | ||
194 | /* A function called through the vtable to initialise this loader. */ | |
195 | static int | |
196 | vl_init (lt_user_data loader_data) | |
197 | { | |
198 | int errors = 0; | |
199 | ||
200 | if (! dyld_cannot_close) | |
201 | { | |
202 | if (!_dyld_present ()) | |
203 | { | |
204 | ++errors; | |
205 | } | |
206 | else | |
207 | { | |
208 | (void) _dyld_func_lookup ("__dyld_NSAddImage", | |
209 | (unsigned long*) <__addimage); | |
210 | (void) _dyld_func_lookup ("__dyld_NSLookupSymbolInImage", | |
211 | (unsigned long*)<__image_symbol); | |
212 | (void) _dyld_func_lookup ("__dyld_NSIsSymbolNameDefinedInImage", | |
213 | (unsigned long*) <__image_symbol_p); | |
214 | (void) _dyld_func_lookup ("__dyld_NSMakePrivateModulePublic", | |
215 | (unsigned long*) <__module_export); | |
216 | dyld_cannot_close = lt_dladderror ("can't close a dylib"); | |
217 | } | |
218 | } | |
219 | ||
220 | return errors; | |
221 | } | |
222 | ||
223 | ||
224 | /* A function called through the vtable to open a module with this | |
225 | loader. Returns an opaque representation of the newly opened | |
226 | module for processing with this loader's other vtable functions. */ | |
227 | static lt_module | |
228 | vm_open (lt_user_data loader_data, const char *filename, | |
229 | lt_dladvise LT__UNUSED advise) | |
230 | { | |
231 | lt_module module = 0; | |
232 | NSObjectFileImage ofi = 0; | |
233 | ||
234 | if (!filename) | |
235 | { | |
236 | return (lt_module) -1; | |
237 | } | |
238 | ||
239 | switch (NSCreateObjectFileImageFromFile (filename, &ofi)) | |
240 | { | |
241 | case NSObjectFileImageSuccess: | |
242 | module = NSLinkModule (ofi, filename, NSLINKMODULE_OPTION_RETURN_ON_ERROR | |
243 | | NSLINKMODULE_OPTION_PRIVATE | |
244 | | NSLINKMODULE_OPTION_BINDNOW); | |
245 | NSDestroyObjectFileImage (ofi); | |
246 | ||
247 | if (module) | |
248 | { | |
249 | lt__module_export (module); | |
250 | } | |
251 | break; | |
252 | ||
253 | case NSObjectFileImageInappropriateFile: | |
254 | if (lt__image_symbol_p && lt__image_symbol) | |
255 | { | |
256 | module = (lt_module) lt__addimage(filename, | |
257 | NSADDIMAGE_OPTION_RETURN_ON_ERROR); | |
258 | } | |
259 | break; | |
260 | ||
261 | case NSObjectFileImageFailure: | |
262 | case NSObjectFileImageArch: | |
263 | case NSObjectFileImageFormat: | |
264 | case NSObjectFileImageAccess: | |
265 | /*NOWORK*/ | |
266 | break; | |
267 | } | |
268 | ||
269 | if (!module) | |
270 | { | |
271 | DYLD__SETERROR (CANNOT_OPEN); | |
272 | } | |
273 | ||
274 | return module; | |
275 | } | |
276 | ||
277 | ||
278 | /* A function called through the vtable when a particular module | |
279 | should be unloaded. */ | |
280 | static int | |
281 | vm_close (lt_user_data loader_data, lt_module module) | |
282 | { | |
283 | int errors = 0; | |
284 | ||
285 | if (module != (lt_module) -1) | |
286 | { | |
287 | const mach_header *mh = (const mach_header *) module; | |
288 | int flags = 0; | |
289 | if (mh->magic == LT__MAGIC) | |
290 | { | |
291 | lt_dlseterror (dyld_cannot_close); | |
292 | ++errors; | |
293 | } | |
294 | else | |
295 | { | |
296 | /* Currently, if a module contains c++ static destructors and it | |
297 | is unloaded, we get a segfault in atexit(), due to compiler and | |
298 | dynamic loader differences of opinion, this works around that. */ | |
299 | if ((const struct section *) NULL != | |
300 | getsectbynamefromheader (lt__nsmodule_get_header (module), | |
301 | "__DATA", "__mod_term_func")) | |
302 | { | |
303 | flags |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; | |
304 | } | |
305 | #if defined(__ppc__) | |
306 | flags |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; | |
307 | #endif | |
308 | if (!NSUnLinkModule (module, flags)) | |
309 | { | |
310 | DYLD__SETERROR (CANNOT_CLOSE); | |
311 | ++errors; | |
312 | } | |
313 | } | |
314 | } | |
315 | ||
316 | return errors; | |
317 | } | |
318 | ||
319 | /* A function called through the vtable to get the address of | |
320 | a symbol loaded from a particular module. */ | |
321 | static void * | |
322 | vm_sym (lt_user_data loader_data, lt_module module, const char *name) | |
323 | { | |
324 | NSSymbol *nssym = 0; | |
325 | const mach_header *mh = (const mach_header *) module; | |
326 | char saveError[256] = "Symbol not found"; | |
327 | ||
328 | if (module == (lt_module) -1) | |
329 | { | |
330 | void *address, *unused; | |
331 | _dyld_lookup_and_bind (name, (unsigned long*) &address, &unused); | |
332 | return address; | |
333 | } | |
334 | ||
335 | if (mh->magic == LT__MAGIC) | |
336 | { | |
337 | if (lt__image_symbol_p && lt__image_symbol) | |
338 | { | |
339 | if (lt__image_symbol_p (mh, name)) | |
340 | { | |
341 | nssym = lt__image_symbol (mh, name, LT__SYMLOOKUP_OPTS); | |
342 | } | |
343 | } | |
344 | ||
345 | } | |
346 | else | |
347 | { | |
348 | nssym = NSLookupSymbolInModule (module, name); | |
349 | } | |
350 | ||
351 | if (!nssym) | |
352 | { | |
353 | strncpy (saveError, dylderror (LT__STRERROR (SYMBOL_NOT_FOUND)), 255); | |
354 | saveError[255] = 0; | |
355 | if (!mh) | |
356 | { | |
357 | mh = (mach_header *)lt__nsmodule_get_header (module); | |
358 | } | |
359 | nssym = lt__linkedlib_symbol (name, mh); | |
360 | } | |
361 | ||
362 | if (!nssym) | |
363 | { | |
364 | LT__SETERRORSTR (saveError); | |
365 | } | |
366 | ||
367 | return nssym ? NSAddressOfSymbol (nssym) : 0; | |
368 | } | |
369 | ||
370 | ||
371 | ||
372 | ||
373 | /* --- HELPER FUNCTIONS --- */ | |
374 | ||
375 | ||
376 | /* Return the dyld error string, or the passed in error string if none. */ | |
377 | static const char * | |
378 | dylderror (const char *errmsg) | |
379 | { | |
380 | NSLinkEditErrors ler; | |
381 | int lerno; | |
382 | const char *file; | |
383 | const char *errstr; | |
384 | ||
385 | NSLinkEditError (&ler, &lerno, &file, &errstr); | |
386 | ||
387 | if (! (errstr && *errstr)) | |
388 | { | |
389 | errstr = errmsg; | |
390 | } | |
391 | ||
392 | return errstr; | |
393 | } | |
394 | ||
395 | /* There should probably be an apple dyld api for this. */ | |
396 | static const mach_header * | |
397 | lt__nsmodule_get_header (NSModule module) | |
398 | { | |
399 | int i = _dyld_image_count(); | |
400 | const char *modname = NSNameOfModule (module); | |
401 | const mach_header *mh = 0; | |
402 | ||
403 | if (!modname) | |
404 | return NULL; | |
405 | ||
406 | while (i > 0) | |
407 | { | |
408 | --i; | |
409 | if (strneq (_dyld_get_image_name (i), modname)) | |
410 | { | |
411 | mh = _dyld_get_image_header (i); | |
412 | break; | |
413 | } | |
414 | } | |
415 | ||
416 | return mh; | |
417 | } | |
418 | ||
419 | /* NSAddImage is also used to get the loaded image, but it only works if | |
420 | the lib is installed, for uninstalled libs we need to check the | |
421 | install_names against each other. Note that this is still broken if | |
422 | DYLD_IMAGE_SUFFIX is set and a different lib was loaded as a result. */ | |
423 | static const char * | |
424 | lt__header_get_instnam (const mach_header *mh) | |
425 | { | |
426 | unsigned long offset = sizeof(mach_header); | |
427 | const char* result = 0; | |
428 | int j; | |
429 | ||
430 | for (j = 0; j < mh->ncmds; j++) | |
431 | { | |
432 | struct load_command *lc; | |
433 | ||
434 | lc = (struct load_command*) (((unsigned long) mh) + offset); | |
435 | if (LC_ID_DYLIB == lc->cmd) | |
436 | { | |
437 | result=(char*)(((dylib_command*) lc)->dylib.name.offset + | |
438 | (unsigned long) lc); | |
439 | } | |
440 | offset += lc->cmdsize; | |
441 | } | |
442 | ||
443 | return result; | |
444 | } | |
445 | ||
446 | static const mach_header * | |
447 | lt__match_loadedlib (const char *name) | |
448 | { | |
449 | const mach_header *mh = 0; | |
450 | int i = _dyld_image_count(); | |
451 | ||
452 | while (i > 0) | |
453 | { | |
454 | const char *id; | |
455 | ||
456 | --i; | |
457 | id = lt__header_get_instnam (_dyld_get_image_header (i)); | |
458 | if (id && strneq (id, name)) | |
459 | { | |
460 | mh = _dyld_get_image_header (i); | |
461 | break; | |
462 | } | |
463 | } | |
464 | ||
465 | return mh; | |
466 | } | |
467 | ||
468 | /* Safe to assume our mh is good. */ | |
469 | static NSSymbol | |
470 | lt__linkedlib_symbol (const char *symname, const mach_header *mh) | |
471 | { | |
472 | NSSymbol symbol = 0; | |
473 | ||
474 | if (lt__image_symbol && NSIsSymbolNameDefined (symname)) | |
475 | { | |
476 | unsigned long offset = sizeof(mach_header); | |
477 | struct load_command *lc; | |
478 | int j; | |
479 | ||
480 | for (j = 0; j < mh->ncmds; j++) | |
481 | { | |
482 | lc = (struct load_command*) (((unsigned long) mh) + offset); | |
483 | if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd)) | |
484 | { | |
485 | unsigned long base = ((dylib_command *) lc)->dylib.name.offset; | |
486 | char *name = (char *) (base + (unsigned long) lc); | |
487 | const mach_header *mh1 = lt__match_loadedlib (name); | |
488 | ||
489 | if (!mh1) | |
490 | { | |
491 | /* Maybe NSAddImage can find it */ | |
492 | mh1 = lt__addimage (name, | |
493 | NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED | |
494 | | NSADDIMAGE_OPTION_WITH_SEARCHING | |
495 | | NSADDIMAGE_OPTION_RETURN_ON_ERROR); | |
496 | } | |
497 | ||
498 | if (mh1) | |
499 | { | |
500 | symbol = lt__image_symbol (mh1, symname, LT__SYMLOOKUP_OPTS); | |
501 | if (symbol) | |
502 | break; | |
503 | } | |
504 | } | |
505 | ||
506 | offset += lc->cmdsize; | |
507 | } | |
508 | } | |
509 | ||
510 | return symbol; | |
511 | } |