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