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