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 */ 61struct 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}; 70static struct dlopen_handle *sasl_dlopen_handles = NULL; 71static const struct dlopen_handle sasl_main_program_handle = {NULL}; 72static char *sasl_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 81// Jaguar does not have an enum named bool 82enum bool { false, true }; 83 84static 85enum bool 86NSMakePrivateModulePublic( 87NSModule module) 88{ 89 static enum bool (*p)(NSModule module) = NULL; 90 91 if(p == NULL) 92 _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", 93 (unsigned long *)&p); 94 if(p == NULL){ 95#ifdef DEBUG 96 printf("_dyld_func_lookup of __dyld_NSMakePrivateModulePublic " 97 "failed\n"); 98#endif 99 return(FALSE); 100 } 101 return(p(module)); 102} 103 104/* 105 * helper routine: search for a named module in various locations 106 */ 107static 108int 109_dl_search_paths( 110const char *filename, 111char *pathbuf, 112struct stat *stat_buf) 113{ 114 const char *pathspec; 115 const char *element; 116 const char *p; 117 char *q; 118 char *pathbuf_end; 119 const char *envvars[] = { 120 "$DYLD_LIBRARY_PATH", 121 "$LD_LIBRARY_PATH", 122 "/usr/lib:/lib", 123 NULL }; 124 int envvar_index; 125 126 pathbuf_end = pathbuf + PATH_MAX - 8; 127 128 for(envvar_index = 0; envvars[envvar_index]; envvar_index++){ 129 if(envvars[envvar_index][0] == '$'){ 130 pathspec = getenv(envvars[envvar_index]+1); 131 } 132 else { 133 pathspec = envvars[envvar_index]; 134 } 135 136 if(pathspec != NULL){ 137 element = pathspec; 138 while(*element){ 139 /* extract path list element */ 140 p = element; 141 q = pathbuf; 142 while(*p && *p != ':' && q < pathbuf_end) *q++ = *p++; 143 if(q == pathbuf){ /* empty element */ 144 if(*p){ 145 element = p+1; 146 continue; 147 } 148 break; 149 } 150 if (*p){ 151 element = p+1; 152 } 153 else{ 154 element = p; /* this terminates the loop */ 155 } 156 157 /* add slash if neccessary */ 158 if(*(q-1) != '/' && q < pathbuf_end){ 159 *q++ = '/'; 160 } 161 162 /* append module name */ 163 p = filename; 164 while(*p && q < pathbuf_end) *q++ = *p++; 165 *q++ = 0; 166 167 if(q >= pathbuf_end){ 168 /* maybe add an error message here */ 169 break; 170 } 171 172 if(stat(pathbuf, stat_buf) == 0){ 173 return 0; 174 } 175 } 176 } 177 } 178 179 /* we have searched everywhere, now we give up */ 180 return -1; 181} 182 183/* 184 * dlopen() the MacOS X version of the FreeBSD dlopen() interface. 185 */ 186void * 187sasl_dlopen( 188const char *path, 189int mode) 190{ 191 const char *module_path; 192 void *retval; 193 struct stat stat_buf; 194 NSObjectFileImage objectFileImage; 195 NSObjectFileImageReturnCode ofile_result_code; 196 NSModule module; 197 struct dlopen_handle *p; 198 unsigned long options; 199 NSSymbol NSSymbol; 200 void (*init)(void); 201 char pathbuf[PATH_MAX]; 202 203 DEBUG_PRINT2("libdl: dlopen(%s,0x%x) -> ", path, (unsigned int)mode); 204 205 sasl_dlerror_pointer = NULL; 206 /* 207 * A NULL path is to indicate the caller wants a handle for the 208 * main program. 209 */ 210 if(path == NULL){ 211 retval = (void *)&sasl_main_program_handle; 212 DEBUG_PRINT1("main / %p\n", retval); 213 return(retval); 214 } 215 216 /* see if the path exists and if so get the device and inode number */ 217 if(stat(path, &stat_buf) == -1){ 218 sasl_dlerror_pointer = strerror(errno); 219 220 if(path[0] == '/'){ 221 DEBUG_PRINT1("ERROR (stat): %s\n", sasl_dlerror_pointer); 222 return(NULL); 223 } 224 225 /* search for the module in various places */ 226 if(_dl_search_paths(path, pathbuf, &stat_buf)){ 227 /* sasl_dlerror_pointer is unmodified */ 228 DEBUG_PRINT1("ERROR (stat): %s\n", sasl_dlerror_pointer); 229 return(NULL); 230 } 231 DEBUG_PRINT1("found %s -> ", pathbuf); 232 module_path = pathbuf; 233 sasl_dlerror_pointer = NULL; 234 } 235 else{ 236 module_path = path; 237 } 238 239 /* 240 * If we don't want an unshared handle see if we already have a handle 241 * for this path. 242 */ 243 if((mode & RTLD_UNSHARED) != RTLD_UNSHARED){ 244 p = sasl_dlopen_handles; 245 while(p != NULL){ 246 if(p->dev == stat_buf.st_dev && p->ino == stat_buf.st_ino){ 247 /* skip unshared handles */ 248 if((p->dlopen_mode & RTLD_UNSHARED) == RTLD_UNSHARED) 249 continue; 250 /* 251 * We have already created a handle for this path. The 252 * caller might be trying to promote an RTLD_LOCAL handle 253 * to a RTLD_GLOBAL. Or just looking it up with 254 * RTLD_NOLOAD. 255 */ 256 if((p->dlopen_mode & RTLD_LOCAL) == RTLD_LOCAL && 257 (mode & RTLD_GLOBAL) == RTLD_GLOBAL){ 258 /* promote the handle */ 259 if(NSMakePrivateModulePublic(p->module) == TRUE){ 260 p->dlopen_mode &= ~RTLD_LOCAL; 261 p->dlopen_mode |= RTLD_GLOBAL; 262 p->dlopen_count++; 263 DEBUG_PRINT1("%p\n", p); 264 return(p); 265 } 266 else{ 267 sasl_dlerror_pointer = "can't promote handle from " 268 "RTLD_LOCAL to RTLD_GLOBAL"; 269 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 270 return(NULL); 271 } 272 } 273 p->dlopen_count++; 274 DEBUG_PRINT1("%p\n", p); 275 return(p); 276 } 277 p = p->next; 278 } 279 } 280 281 /* 282 * We do not have a handle for this path if we were just trying to 283 * look it up return NULL to indicate we don't have it. 284 */ 285 if((mode & RTLD_NOLOAD) == RTLD_NOLOAD){ 286 sasl_dlerror_pointer = "no existing handle for path RTLD_NOLOAD test"; 287 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 288 return(NULL); 289 } 290 291 /* try to create an object file image from this path */ 292 ofile_result_code = NSCreateObjectFileImageFromFile(module_path, 293 &objectFileImage); 294 if(ofile_result_code != NSObjectFileImageSuccess){ 295 switch(ofile_result_code){ 296 case NSObjectFileImageFailure: 297 sasl_dlerror_pointer = "object file setup failure"; 298 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 299 return(NULL); 300 case NSObjectFileImageInappropriateFile: 301 sasl_dlerror_pointer = "not a Mach-O MH_BUNDLE file type"; 302 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 303 return(NULL); 304 case NSObjectFileImageArch: 305 sasl_dlerror_pointer = "no object for this architecture"; 306 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 307 return(NULL); 308 case NSObjectFileImageFormat: 309 sasl_dlerror_pointer = "bad object file format"; 310 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 311 return(NULL); 312 case NSObjectFileImageAccess: 313 sasl_dlerror_pointer = "can't read object file"; 314 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 315 return(NULL); 316 default: 317 sasl_dlerror_pointer = "unknown error from " 318 "NSCreateObjectFileImageFromFile()"; 319 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 320 return(NULL); 321 } 322 } 323 324 /* try to link in this object file image */ 325 options = NSLINKMODULE_OPTION_PRIVATE; 326 if((mode & RTLD_NOW) == RTLD_NOW) 327 options |= NSLINKMODULE_OPTION_BINDNOW; 328 module = NSLinkModule(objectFileImage, module_path, options); 329 NSDestroyObjectFileImage(objectFileImage) ; 330 if(module == NULL){ 331 sasl_dlerror_pointer = "NSLinkModule() failed for dlopen()"; 332 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 333 return(NULL); 334 } 335 336 /* 337 * If the handle is to be global promote the handle. It is done this 338 * way to avoid multiply defined symbols. 339 */ 340 if((mode & RTLD_GLOBAL) == RTLD_GLOBAL){ 341 if(NSMakePrivateModulePublic(module) == FALSE){ 342 sasl_dlerror_pointer = "can't promote handle from RTLD_LOCAL to " 343 "RTLD_GLOBAL"; 344 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 345 return(NULL); 346 } 347 } 348 349 p = malloc(sizeof(struct dlopen_handle)); 350 if(p == NULL){ 351 sasl_dlerror_pointer = "can't allocate memory for the dlopen handle"; 352 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 353 return(NULL); 354 } 355 356 /* fill in the handle */ 357 p->dev = stat_buf.st_dev; 358 p->ino = stat_buf.st_ino; 359 if(mode & RTLD_GLOBAL) 360 p->dlopen_mode = RTLD_GLOBAL; 361 else 362 p->dlopen_mode = RTLD_LOCAL; 363 p->dlopen_mode |= (mode & RTLD_UNSHARED) | 364 (mode & RTLD_NODELETE) | 365 (mode & RTLD_LAZY_UNDEF); 366 p->dlopen_count = 1; 367 p->module = module; 368 p->prev = NULL; 369 p->next = sasl_dlopen_handles; 370 if(sasl_dlopen_handles != NULL) 371 sasl_dlopen_handles->prev = p; 372 sasl_dlopen_handles = p; 373 374 /* call the init function if one exists */ 375 NSSymbol = NSLookupSymbolInModule(p->module, "__init"); 376 if(NSSymbol != NULL){ 377 init = NSAddressOfSymbol(NSSymbol); 378 init(); 379 } 380 381 DEBUG_PRINT1("%p\n", p); 382 return(p); 383} 384 385/* 386 * dlsym() the MacOS X version of the FreeBSD dlopen() interface. 387 */ 388void * 389sasl_dlsym( 390void * handle, 391const char *symbol) 392{ 393 struct dlopen_handle *dlopen_handle, *p; 394 NSSymbol NSSymbol; 395 void *address; 396 397 DEBUG_PRINT2("libdl: dlsym(%p,%s) -> ", handle, symbol); 398 399 dlopen_handle = (struct dlopen_handle *)handle; 400 401 /* 402 * If this is the handle for the main program do a global lookup. 403 */ 404 if(dlopen_handle == (struct dlopen_handle *)&sasl_main_program_handle){ 405 if(NSIsSymbolNameDefined(symbol) == TRUE){ 406 NSSymbol = NSLookupAndBindSymbol(symbol); 407 address = NSAddressOfSymbol(NSSymbol); 408 sasl_dlerror_pointer = NULL; 409 DEBUG_PRINT1("%p\n", address); 410 return(address); 411 } 412 else{ 413 sasl_dlerror_pointer = "symbol not found"; 414 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 415 return(NULL); 416 } 417 } 418 419 /* 420 * Find this handle and do a lookup in just this module. 421 */ 422 p = sasl_dlopen_handles; 423 while(p != NULL){ 424 if(dlopen_handle == p){ 425 NSSymbol = NSLookupSymbolInModule(p->module, symbol); 426 if(NSSymbol != NULL){ 427 address = NSAddressOfSymbol(NSSymbol); 428 sasl_dlerror_pointer = NULL; 429 DEBUG_PRINT1("%p\n", address); 430 return(address); 431 } 432 else{ 433 sasl_dlerror_pointer = "symbol not found"; 434 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 435 return(NULL); 436 } 437 } 438 p = p->next; 439 } 440 441 sasl_dlerror_pointer = "bad handle passed to dlsym()"; 442 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 443 return(NULL); 444} 445 446/* 447 * dlerror() the MacOS X version of the FreeBSD dlopen() interface. 448 */ 449const char * 450sasl_dlerror( 451void) 452{ 453 const char *p; 454 455 p = (const char *)sasl_dlerror_pointer; 456 sasl_dlerror_pointer = NULL; 457 return(p); 458} 459 460/* 461 * dlclose() the MacOS X version of the FreeBSD dlopen() interface. 462 */ 463int 464sasl_dlclose( 465void * handle) 466{ 467 struct dlopen_handle *p, *q; 468 unsigned long options; 469 NSSymbol NSSymbol; 470 void (*fini)(void); 471 472 DEBUG_PRINT1("libdl: dlclose(%p) -> ", handle); 473 474 sasl_dlerror_pointer = NULL; 475 q = (struct dlopen_handle *)handle; 476 p = sasl_dlopen_handles; 477 while(p != NULL){ 478 if(p == q){ 479 /* if the dlopen() count is not zero we are done */ 480 p->dlopen_count--; 481 if(p->dlopen_count != 0){ 482 DEBUG_PRINT("OK"); 483 return(0); 484 } 485 486 /* call the fini function if one exists */ 487 NSSymbol = NSLookupSymbolInModule(p->module, "__fini"); 488 if(NSSymbol != NULL){ 489 fini = NSAddressOfSymbol(NSSymbol); 490 fini(); 491 } 492 493 /* unlink the module for this handle */ 494 options = 0; 495 if(p->dlopen_mode & RTLD_NODELETE) 496 options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; 497 if(p->dlopen_mode & RTLD_LAZY_UNDEF) 498 options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; 499 if(NSUnLinkModule(p->module, options) == FALSE){ 500 sasl_dlerror_pointer = "NSUnLinkModule() failed for dlclose()"; 501 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 502 return(-1); 503 } 504 if(p->prev != NULL) 505 p->prev->next = p->next; 506 if(p->next != NULL) 507 p->next->prev = p->prev; 508 if(sasl_dlopen_handles == p) 509 sasl_dlopen_handles = p->next; 510 free(p); 511 DEBUG_PRINT("OK"); 512 return(0); 513 } 514 p = p->next; 515 } 516 sasl_dlerror_pointer = "invalid handle passed to dlclose()"; 517 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 518 return(-1); 519} 520