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