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}; 70/* APPLE: prefix sasl_ to fix conflict with System */ 71static struct dlopen_handle *sasl_dlopen_handles = NULL; 72static const struct dlopen_handle sasl_main_program_handle = {NULL}; 73static char *sasl_dlerror_pointer = NULL; 74 75/* 76 * NSMakePrivateModulePublic() is not part of the public dyld API so we define 77 * it here. The internal dyld function pointer for 78 * __dyld_NSMakePrivateModulePublic is returned so thats all that maters to get 79 * the functionality need to implement the dlopen() interfaces. 80 */ 81static 82int 83NSMakePrivateModulePublic( 84NSModule module) 85{ 86 static int (*p)(NSModule module) = NULL; 87 88 if(p == NULL) 89 _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", 90 (unsigned long *)&p); 91 if(p == NULL){ 92#ifdef DEBUG 93 printf("_dyld_func_lookup of __dyld_NSMakePrivateModulePublic " 94 "failed\n"); 95#endif 96 return(FALSE); 97 } 98 return(p(module)); 99} 100 101/* 102 * helper routine: search for a named module in various locations 103 */ 104static 105int 106_dl_search_paths( 107const char *filename, 108char *pathbuf, 109struct stat *stat_buf) 110{ 111 const char *pathspec; 112 const char *element; 113 const char *p; 114 char *q; 115 char *pathbuf_end; 116 const char *envvars[] = { 117 "$DYLD_LIBRARY_PATH", 118 "$LD_LIBRARY_PATH", 119 "/usr/lib:/lib", 120 NULL }; 121 int envvar_index; 122 123 pathbuf_end = pathbuf + PATH_MAX - 8; 124 125 for(envvar_index = 0; envvars[envvar_index]; envvar_index++){ 126 if(envvars[envvar_index][0] == '$'){ 127 pathspec = getenv(envvars[envvar_index]+1); 128 } 129 else { 130 pathspec = envvars[envvar_index]; 131 } 132 133 if(pathspec != NULL){ 134 element = pathspec; 135 while(*element){ 136 /* extract path list element */ 137 p = element; 138 q = pathbuf; 139 while(*p && *p != ':' && q < pathbuf_end) 140 *q++ = *p++; 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 */ 184void * 185sasl_dlopen( 186const char *path, 187int 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 sasl_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 *)&sasl_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 sasl_dlerror_pointer = strerror(errno); 217 218 if(path[0] == '/'){ 219 DEBUG_PRINT1("ERROR (stat): %s\n", sasl_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 /* sasl_dlerror_pointer is unmodified */ 226 DEBUG_PRINT1("ERROR (stat): %s\n", sasl_dlerror_pointer); 227 return(NULL); 228 } 229 DEBUG_PRINT1("found %s -> ", pathbuf); 230 module_path = pathbuf; 231 sasl_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 = sasl_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 sasl_dlerror_pointer = "can't promote handle from " 266 "RTLD_LOCAL to RTLD_GLOBAL"; 267 DEBUG_PRINT1("ERROR: %s\n", sasl_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 sasl_dlerror_pointer = "no existing handle for path RTLD_NOLOAD test"; 285 DEBUG_PRINT1("ERROR: %s\n", sasl_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 sasl_dlerror_pointer = "object file setup failure"; 296 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 297 return(NULL); 298 case NSObjectFileImageInappropriateFile: 299 sasl_dlerror_pointer = "not a Mach-O MH_BUNDLE file type"; 300 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 301 return(NULL); 302 case NSObjectFileImageArch: 303 sasl_dlerror_pointer = "no object for this architecture"; 304 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 305 return(NULL); 306 case NSObjectFileImageFormat: 307 sasl_dlerror_pointer = "bad object file format"; 308 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 309 return(NULL); 310 case NSObjectFileImageAccess: 311 sasl_dlerror_pointer = "can't read object file"; 312 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 313 return(NULL); 314 default: 315 sasl_dlerror_pointer = "unknown error from " 316 "NSCreateObjectFileImageFromFile()"; 317 DEBUG_PRINT1("ERROR: %s\n", sasl_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 sasl_dlerror_pointer = "NSLinkModule() failed for dlopen()"; 330 DEBUG_PRINT1("ERROR: %s\n", sasl_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 sasl_dlerror_pointer = "can't promote handle from RTLD_LOCAL to " 341 "RTLD_GLOBAL"; 342 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 343 return(NULL); 344 } 345 } 346 347 p = malloc(sizeof(struct dlopen_handle)); 348 if(p == NULL){ 349 sasl_dlerror_pointer = "can't allocate memory for the dlopen handle"; 350 DEBUG_PRINT1("ERROR: %s\n", sasl_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 = sasl_dlopen_handles; 368 if(sasl_dlopen_handles != NULL) 369 sasl_dlopen_handles->prev = p; 370 sasl_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 */ 386void * 387sasl_dlsym( 388void * handle, 389const 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 *)&sasl_main_program_handle){ 403 if(NSIsSymbolNameDefined(symbol) == TRUE){ 404 NSSymbol = NSLookupAndBindSymbol(symbol); 405 address = NSAddressOfSymbol(NSSymbol); 406 sasl_dlerror_pointer = NULL; 407 DEBUG_PRINT1("%p\n", address); 408 return(address); 409 } 410 else{ 411 sasl_dlerror_pointer = "symbol not found"; 412 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 413 return(NULL); 414 } 415 } 416 417 /* 418 * Find this handle and do a lookup in just this module. 419 */ 420 p = sasl_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 sasl_dlerror_pointer = NULL; 427 DEBUG_PRINT1("%p\n", address); 428 return(address); 429 } 430 else{ 431 sasl_dlerror_pointer = "symbol not found"; 432 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 433 return(NULL); 434 } 435 } 436 p = p->next; 437 } 438 439 sasl_dlerror_pointer = "bad handle passed to dlsym()"; 440 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 441 return(NULL); 442} 443 444/* 445 * dlerror() the MacOS X version of the FreeBSD dlopen() interface. 446 */ 447const char * 448sasl_dlerror( 449void) 450{ 451 const char *p; 452 453 p = (const char *)sasl_dlerror_pointer; 454 sasl_dlerror_pointer = NULL; 455 return(p); 456} 457 458/* 459 * dlclose() the MacOS X version of the FreeBSD dlopen() interface. 460 */ 461int 462sasl_dlclose( 463void * 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 sasl_dlerror_pointer = NULL; 473 q = (struct dlopen_handle *)handle; 474 p = sasl_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 sasl_dlerror_pointer = "NSUnLinkModule() failed for dlclose()"; 499 DEBUG_PRINT1("ERROR: %s\n", sasl_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(sasl_dlopen_handles == p) 507 sasl_dlopen_handles = p->next; 508 free(p); 509 DEBUG_PRINT("OK"); 510 return(0); 511 } 512 p = p->next; 513 } 514 sasl_dlerror_pointer = "invalid handle passed to dlclose()"; 515 DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); 516 return(-1); 517} 518