1/*
2 * tclLoadDyld.c --
3 *
4 *	This procedure provides a version of the TclLoadFile that works with
5 *	Apple's dyld dynamic loading.
6 *	Original version of his file (superseded long ago) provided by
7 *	Wilfredo Sanchez (wsanchez@apple.com).
8 *
9 * Copyright (c) 1995 Apple Computer, Inc.
10 * Copyright (c) 2001-2007 Daniel A. Steffen <das@users.sourceforge.net>
11 *
12 * See the file "license.terms" for information on usage and redistribution of
13 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 *
15 * RCS: @(#) $Id: tclLoadDyld.c,v 1.29.2.1 2009/04/10 18:10:44 das Exp $
16 */
17
18#include "tclInt.h"
19
20#ifndef MODULE_SCOPE
21#define MODULE_SCOPE extern
22#endif
23
24#ifndef TCL_DYLD_USE_DLFCN
25/*
26 * Use preferred dlfcn API on 10.4 and later
27 */
28#   if !defined(NO_DLFCN_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1040
29#	define TCL_DYLD_USE_DLFCN 1
30#   else
31#	define TCL_DYLD_USE_DLFCN 0
32#   endif
33#endif
34#ifndef TCL_DYLD_USE_NSMODULE
35/*
36 * Use deprecated NSModule API only to support 10.3 and earlier:
37 */
38#   if MAC_OS_X_VERSION_MIN_REQUIRED < 1040
39#	define TCL_DYLD_USE_NSMODULE 1
40#   else
41#	define TCL_DYLD_USE_NSMODULE 0
42#   endif
43#endif
44
45#if TCL_DYLD_USE_DLFCN
46#include <dlfcn.h>
47#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
48/*
49 * Support for weakly importing dlfcn API.
50 */
51extern void *dlopen(const char *path, int mode) WEAK_IMPORT_ATTRIBUTE;
52extern void *dlsym(void *handle, const char *symbol) WEAK_IMPORT_ATTRIBUTE;
53extern int dlclose(void *handle) WEAK_IMPORT_ATTRIBUTE;
54extern char *dlerror(void) WEAK_IMPORT_ATTRIBUTE;
55#endif
56#endif
57
58#if TCL_DYLD_USE_NSMODULE || defined(TCL_LOAD_FROM_MEMORY)
59#include <mach-o/dyld.h>
60#include <mach-o/fat.h>
61#include <mach-o/swap.h>
62#include <mach-o/arch.h>
63#include <libkern/OSByteOrder.h>
64#include <mach/mach.h>
65#include <stdbool.h>
66
67typedef struct Tcl_DyldModuleHandle {
68    struct Tcl_DyldModuleHandle *nextPtr;
69    NSModule module;
70} Tcl_DyldModuleHandle;
71#endif /* TCL_DYLD_USE_NSMODULE */
72
73typedef struct Tcl_DyldLoadHandle {
74#if TCL_DYLD_USE_DLFCN
75    void *dlHandle;
76#endif
77#if TCL_DYLD_USE_NSMODULE || defined(TCL_LOAD_FROM_MEMORY)
78    const struct mach_header *dyldLibHeader;
79    Tcl_DyldModuleHandle *modulePtr;
80#endif
81} Tcl_DyldLoadHandle;
82
83#if (TCL_DYLD_USE_DLFCN && MAC_OS_X_VERSION_MIN_REQUIRED < 1040) || \
84	defined(TCL_LOAD_FROM_MEMORY)
85MODULE_SCOPE long tclMacOSXDarwinRelease;
86#endif
87
88#ifdef TCL_DEBUG_LOAD
89#define TclLoadDbgMsg(m, ...) do { \
90	    fprintf(stderr, "%s:%d: %s(): " m ".\n", \
91	    strrchr(__FILE__, '/')+1, __LINE__, __func__, ##__VA_ARGS__); \
92	} while (0)
93#else
94#define TclLoadDbgMsg(m, ...)
95#endif
96
97#if TCL_DYLD_USE_NSMODULE || defined(TCL_LOAD_FROM_MEMORY)
98/*
99 *----------------------------------------------------------------------
100 *
101 * DyldOFIErrorMsg --
102 *
103 *	Converts a numerical NSObjectFileImage error into an error message
104 *	string.
105 *
106 * Results:
107 *	Error message string.
108 *
109 * Side effects:
110 *	None.
111 *
112 *----------------------------------------------------------------------
113 */
114
115static CONST char*
116DyldOFIErrorMsg(
117    int err)
118{
119    switch(err) {
120    case NSObjectFileImageSuccess:
121	return NULL;
122    case NSObjectFileImageFailure:
123	return "object file setup failure";
124    case NSObjectFileImageInappropriateFile:
125	return "not a Mach-O MH_BUNDLE file";
126    case NSObjectFileImageArch:
127	return "no object for this architecture";
128    case NSObjectFileImageFormat:
129	return "bad object file format";
130    case NSObjectFileImageAccess:
131	return "can't read object file";
132    default:
133	return "unknown error";
134    }
135}
136#endif /* TCL_DYLD_USE_NSMODULE */
137
138/*
139 *----------------------------------------------------------------------
140 *
141 * TclpDlopen --
142 *
143 *	Dynamically loads a binary code file into memory and returns a handle
144 *	to the new code.
145 *
146 * Results:
147 *	A standard Tcl completion code. If an error occurs, an error message
148 *	is left in the interpreter's result.
149 *
150 * Side effects:
151 *	New code suddenly appears in memory.
152 *
153 *----------------------------------------------------------------------
154 */
155
156MODULE_SCOPE int
157TclpDlopen(
158    Tcl_Interp *interp,		/* Used for error reporting. */
159    Tcl_Obj *pathPtr,		/* Name of the file containing the desired
160				 * code (UTF-8). */
161    Tcl_LoadHandle *loadHandle, /* Filled with token for dynamically loaded
162				 * file which will be passed back to
163				 * (*unloadProcPtr)() to unload the file. */
164    Tcl_FSUnloadFileProc **unloadProcPtr)
165				/* Filled with address of Tcl_FSUnloadFileProc
166				 * function which should be used for this
167				 * file. */
168{
169    Tcl_DyldLoadHandle *dyldLoadHandle;
170#if TCL_DYLD_USE_DLFCN
171    void *dlHandle = NULL;
172#endif
173#if TCL_DYLD_USE_NSMODULE || defined(TCL_LOAD_FROM_MEMORY)
174    const struct mach_header *dyldLibHeader = NULL;
175    Tcl_DyldModuleHandle *modulePtr = NULL;
176#endif
177#if TCL_DYLD_USE_NSMODULE
178    NSLinkEditErrors editError;
179    int errorNumber;
180    const char *errorName, *objFileImageErrMsg = NULL;
181#endif
182    const char *errMsg = NULL;
183    int result;
184    Tcl_DString ds;
185    char *fileName = NULL;
186    const char *nativePath, *nativeFileName = NULL;
187
188    /*
189     * First try the full path the user gave us. This is particularly
190     * important if the cwd is inside a vfs, and we are trying to load using a
191     * relative path.
192     */
193
194    nativePath = Tcl_FSGetNativePath(pathPtr);
195
196#if TCL_DYLD_USE_DLFCN
197#if MAC_OS_X_VERSION_MIN_REQUIRED < 1040
198    if (tclMacOSXDarwinRelease >= 8)
199#endif
200    {
201	dlHandle = dlopen(nativePath, RTLD_NOW | RTLD_GLOBAL);
202	if (!dlHandle) {
203	    /*
204	     * Let the OS loader examine the binary search path for whatever
205	     * string the user gave us which hopefully refers to a file on the
206	     * binary path.
207	     */
208
209	    fileName = Tcl_GetString(pathPtr);
210	    nativeFileName = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds);
211	    dlHandle = dlopen(nativeFileName, RTLD_NOW | RTLD_GLOBAL);
212	}
213	if (dlHandle) {
214	    TclLoadDbgMsg("dlopen() successful");
215	} else {
216	    errMsg = dlerror();
217	    TclLoadDbgMsg("dlopen() failed: %s", errMsg);
218	}
219    }
220    if (!dlHandle)
221#endif /* TCL_DYLD_USE_DLFCN */
222    {
223#if TCL_DYLD_USE_NSMODULE
224	dyldLibHeader = NSAddImage(nativePath,
225		NSADDIMAGE_OPTION_RETURN_ON_ERROR);
226	if (dyldLibHeader) {
227	    TclLoadDbgMsg("NSAddImage() successful");
228	} else {
229	    NSLinkEditError(&editError, &errorNumber, &errorName, &errMsg);
230	    if (editError == NSLinkEditFileAccessError) {
231		/*
232		 * The requested file was not found. Let the OS loader examine
233		 * the binary search path for whatever string the user gave us
234		 * which hopefully refers to a file on the binary path.
235		 */
236
237		if (!fileName) {
238		    fileName = Tcl_GetString(pathPtr);
239		    nativeFileName = Tcl_UtfToExternalDString(NULL, fileName,
240			    -1, &ds);
241		}
242		dyldLibHeader = NSAddImage(nativeFileName,
243			NSADDIMAGE_OPTION_WITH_SEARCHING |
244			NSADDIMAGE_OPTION_RETURN_ON_ERROR);
245		if (dyldLibHeader) {
246		    TclLoadDbgMsg("NSAddImage() successful");
247		} else {
248		    NSLinkEditError(&editError, &errorNumber, &errorName,
249			    &errMsg);
250		    TclLoadDbgMsg("NSAddImage() failed: %s", errMsg);
251		}
252	    } else if ((editError == NSLinkEditFileFormatError
253		    && errorNumber == EBADMACHO)
254		    || editError == NSLinkEditOtherError){
255		NSObjectFileImageReturnCode err;
256		NSObjectFileImage dyldObjFileImage;
257		NSModule module;
258
259		/*
260		 * The requested file was found but was not of type MH_DYLIB,
261		 * attempt to load it as a MH_BUNDLE.
262		 */
263
264		err = NSCreateObjectFileImageFromFile(nativePath,
265			&dyldObjFileImage);
266		if (err == NSObjectFileImageSuccess && dyldObjFileImage) {
267		    TclLoadDbgMsg("NSCreateObjectFileImageFromFile() "
268			    "successful");
269		    module = NSLinkModule(dyldObjFileImage, nativePath,
270			    NSLINKMODULE_OPTION_BINDNOW
271			    | NSLINKMODULE_OPTION_RETURN_ON_ERROR);
272		    NSDestroyObjectFileImage(dyldObjFileImage);
273		    if (module) {
274			modulePtr = (Tcl_DyldModuleHandle *)
275				ckalloc(sizeof(Tcl_DyldModuleHandle));
276			modulePtr->module = module;
277			modulePtr->nextPtr = NULL;
278			TclLoadDbgMsg("NSLinkModule() successful");
279		    } else {
280			NSLinkEditError(&editError, &errorNumber, &errorName,
281				&errMsg);
282			TclLoadDbgMsg("NSLinkModule() failed: %s", errMsg);
283		    }
284		} else {
285		    objFileImageErrMsg = DyldOFIErrorMsg(err);
286		    TclLoadDbgMsg("NSCreateObjectFileImageFromFile() failed: "
287			    "%s", objFileImageErrMsg);
288		}
289	    }
290	}
291#endif /* TCL_DYLD_USE_NSMODULE */
292    }
293    if (0
294#if TCL_DYLD_USE_DLFCN
295	    || dlHandle
296#endif
297#if TCL_DYLD_USE_NSMODULE
298	    || dyldLibHeader || modulePtr
299#endif
300    ) {
301	dyldLoadHandle = (Tcl_DyldLoadHandle *)
302		ckalloc(sizeof(Tcl_DyldLoadHandle));
303#if TCL_DYLD_USE_DLFCN
304	dyldLoadHandle->dlHandle = dlHandle;
305#endif
306#if TCL_DYLD_USE_NSMODULE || defined(TCL_LOAD_FROM_MEMORY)
307	dyldLoadHandle->dyldLibHeader = dyldLibHeader;
308	dyldLoadHandle->modulePtr = modulePtr;
309#endif
310	*loadHandle = (Tcl_LoadHandle) dyldLoadHandle;
311	*unloadProcPtr = &TclpUnloadFile;
312	result = TCL_OK;
313    } else {
314	Tcl_AppendResult(interp, errMsg, NULL);
315#if TCL_DYLD_USE_NSMODULE
316	if (objFileImageErrMsg) {
317	    Tcl_AppendResult(interp, "\nNSCreateObjectFileImageFromFile() "
318		    "error: ", objFileImageErrMsg, NULL);
319	}
320#endif
321	result = TCL_ERROR;
322    }
323    if(fileName) {
324	Tcl_DStringFree(&ds);
325    }
326    return result;
327}
328
329/*
330 *----------------------------------------------------------------------
331 *
332 * TclpFindSymbol --
333 *
334 *	Looks up a symbol, by name, through a handle associated with a
335 *	previously loaded piece of code (shared library).
336 *
337 * Results:
338 *	Returns a pointer to the function associated with 'symbol' if it is
339 *	found. Otherwise returns NULL and may leave an error message in the
340 *	interp's result.
341 *
342 *----------------------------------------------------------------------
343 */
344
345MODULE_SCOPE Tcl_PackageInitProc *
346TclpFindSymbol(
347    Tcl_Interp *interp,		/* For error reporting. */
348    Tcl_LoadHandle loadHandle,	/* Handle from TclpDlopen. */
349    CONST char *symbol)		/* Symbol name to look up. */
350{
351    Tcl_DyldLoadHandle *dyldLoadHandle = (Tcl_DyldLoadHandle *) loadHandle;
352    Tcl_PackageInitProc *proc = NULL;
353    const char *errMsg = NULL;
354    Tcl_DString ds;
355    const char *native;
356
357    native = Tcl_UtfToExternalDString(NULL, symbol, -1, &ds);
358#if TCL_DYLD_USE_DLFCN
359    if (dyldLoadHandle->dlHandle) {
360	proc = dlsym(dyldLoadHandle->dlHandle, native);
361	if (proc) {
362	    TclLoadDbgMsg("dlsym() successful");
363	} else {
364	    errMsg = dlerror();
365	    TclLoadDbgMsg("dlsym() failed: %s", errMsg);
366	}
367    } else
368#endif /* TCL_DYLD_USE_DLFCN */
369    {
370#if TCL_DYLD_USE_NSMODULE || defined(TCL_LOAD_FROM_MEMORY)
371	NSSymbol nsSymbol = NULL;
372	Tcl_DString newName;
373
374	/*
375	 * dyld adds an underscore to the beginning of symbol names.
376	 */
377
378	Tcl_DStringInit(&newName);
379	Tcl_DStringAppend(&newName, "_", 1);
380	native = Tcl_DStringAppend(&newName, native, -1);
381	if (dyldLoadHandle->dyldLibHeader) {
382	    nsSymbol = NSLookupSymbolInImage(dyldLoadHandle->dyldLibHeader,
383		    native, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW |
384		    NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
385	    if (nsSymbol) {
386		TclLoadDbgMsg("NSLookupSymbolInImage() successful");
387#ifdef DYLD_SUPPORTS_DYLIB_UNLOADING
388		/*
389		 * Until dyld supports unloading of MY_DYLIB binaries, the
390		 * following is not needed.
391		 */
392
393		NSModule module = NSModuleForSymbol(nsSymbol);
394		Tcl_DyldModuleHandle *modulePtr = dyldLoadHandle->modulePtr;
395
396		while (modulePtr != NULL) {
397		    if (module == modulePtr->module) {
398			break;
399		    }
400		    modulePtr = modulePtr->nextPtr;
401		}
402		if (modulePtr == NULL) {
403		    modulePtr = (Tcl_DyldModuleHandle *)
404			    ckalloc(sizeof(Tcl_DyldModuleHandle));
405		    modulePtr->module = module;
406		    modulePtr->nextPtr = dyldLoadHandle->modulePtr;
407		    dyldLoadHandle->modulePtr = modulePtr;
408		}
409#endif /* DYLD_SUPPORTS_DYLIB_UNLOADING */
410	    } else {
411		NSLinkEditErrors editError;
412		int errorNumber;
413		const char *errorName;
414
415		NSLinkEditError(&editError, &errorNumber, &errorName, &errMsg);
416		TclLoadDbgMsg("NSLookupSymbolInImage() failed: %s", errMsg);
417	    }
418	} else if (dyldLoadHandle->modulePtr) {
419	    nsSymbol = NSLookupSymbolInModule(
420		    dyldLoadHandle->modulePtr->module, native);
421	    if (nsSymbol) {
422		TclLoadDbgMsg("NSLookupSymbolInModule() successful");
423	    } else {
424		TclLoadDbgMsg("NSLookupSymbolInModule() failed");
425	    }
426	}
427	if (nsSymbol) {
428	    proc = NSAddressOfSymbol(nsSymbol);
429	    if (proc) {
430		TclLoadDbgMsg("NSAddressOfSymbol() successful");
431	    } else {
432		TclLoadDbgMsg("NSAddressOfSymbol() failed");
433	    }
434	}
435	Tcl_DStringFree(&newName);
436#endif /* TCL_DYLD_USE_NSMODULE */
437    }
438    Tcl_DStringFree(&ds);
439    if (errMsg) {
440	Tcl_AppendResult(interp, errMsg, NULL);
441    }
442    return proc;
443}
444
445/*
446 *----------------------------------------------------------------------
447 *
448 * TclpUnloadFile --
449 *
450 *	Unloads a dynamically loaded binary code file from memory. Code
451 *	pointers in the formerly loaded file are no longer valid after calling
452 *	this function.
453 *
454 * Results:
455 *	None.
456 *
457 * Side effects:
458 *	Code dissapears from memory. Note that dyld currently only supports
459 *	unloading of binaries of type MH_BUNDLE loaded with NSLinkModule() in
460 *	TclpDlopen() above.
461 *
462 *----------------------------------------------------------------------
463 */
464
465MODULE_SCOPE void
466TclpUnloadFile(
467    Tcl_LoadHandle loadHandle)	/* loadHandle returned by a previous call to
468				 * TclpDlopen(). The loadHandle is a token
469				 * that represents the loaded file. */
470{
471    Tcl_DyldLoadHandle *dyldLoadHandle = (Tcl_DyldLoadHandle *) loadHandle;
472
473#if TCL_DYLD_USE_DLFCN
474    if (dyldLoadHandle->dlHandle) {
475	int result;
476
477	result = dlclose(dyldLoadHandle->dlHandle);
478	if (!result) {
479	    TclLoadDbgMsg("dlclose() successful");
480	} else {
481	    TclLoadDbgMsg("dlclose() failed: %s", dlerror());
482	}
483    } else
484#endif /* TCL_DYLD_USE_DLFCN */
485    {
486#if TCL_DYLD_USE_NSMODULE || defined(TCL_LOAD_FROM_MEMORY)
487	Tcl_DyldModuleHandle *modulePtr = dyldLoadHandle->modulePtr;
488
489	while (modulePtr != NULL) {
490	    void *ptr;
491	    bool result;
492
493	    result = NSUnLinkModule(modulePtr->module,
494		    NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);
495	    if (result) {
496		TclLoadDbgMsg("NSUnLinkModule() successful");
497	    } else {
498		TclLoadDbgMsg("NSUnLinkModule() failed");
499	    }
500	    ptr = modulePtr;
501	    modulePtr = modulePtr->nextPtr;
502	    ckfree(ptr);
503	}
504#endif /* TCL_DYLD_USE_NSMODULE */
505    }
506    ckfree((char*) dyldLoadHandle);
507}
508
509/*
510 *----------------------------------------------------------------------
511 *
512 * TclGuessPackageName --
513 *
514 *	If the "load" command is invoked without providing a package name,
515 *	this procedure is invoked to try to figure it out.
516 *
517 * Results:
518 *	Always returns 0 to indicate that we couldn't figure out a package
519 *	name; generic code will then try to guess the package from the file
520 *	name. A return value of 1 would have meant that we figured out the
521 *	package name and put it in bufPtr.
522 *
523 * Side effects:
524 *	None.
525 *
526 *----------------------------------------------------------------------
527 */
528
529int
530TclGuessPackageName(
531    CONST char *fileName,	/* Name of file containing package (already
532				 * translated to local form if needed). */
533    Tcl_DString *bufPtr)	/* Initialized empty dstring. Append package
534				 * name to this if possible. */
535{
536    return 0;
537}
538
539#ifdef TCL_LOAD_FROM_MEMORY
540/*
541 *----------------------------------------------------------------------
542 *
543 * TclpLoadMemoryGetBuffer --
544 *
545 *	Allocate a buffer that can be used with TclpLoadMemory() below.
546 *
547 * Results:
548 *	Pointer to allocated buffer or NULL if an error occurs.
549 *
550 * Side effects:
551 *	Buffer is allocated.
552 *
553 *----------------------------------------------------------------------
554 */
555
556MODULE_SCOPE void *
557TclpLoadMemoryGetBuffer(
558    Tcl_Interp *interp,		/* Used for error reporting. */
559    int size)			/* Size of desired buffer. */
560{
561    void *buffer = NULL;
562
563    /*
564     * NSCreateObjectFileImageFromMemory is available but always fails
565     * prior to Darwin 7.
566     */
567    if (tclMacOSXDarwinRelease >= 7) {
568	/*
569	 * We must allocate the buffer using vm_allocate, because
570	 * NSCreateObjectFileImageFromMemory will dispose of it using
571	 * vm_deallocate.
572	 */
573
574	if (vm_allocate(mach_task_self(), (vm_address_t *) &buffer, size, 1)) {
575	    buffer = NULL;
576	}
577    }
578    return buffer;
579}
580
581/*
582 *----------------------------------------------------------------------
583 *
584 * TclpLoadMemory --
585 *
586 *	Dynamically loads binary code file from memory and returns a handle to
587 *	the new code.
588 *
589 * Results:
590 *	A standard Tcl completion code. If an error occurs, an error message
591 *	is left in the interpreter's result.
592 *
593 * Side effects:
594 *	New code is loaded from memory.
595 *
596 *----------------------------------------------------------------------
597 */
598
599MODULE_SCOPE int
600TclpLoadMemory(
601    Tcl_Interp *interp,		/* Used for error reporting. */
602    void *buffer,		/* Buffer containing the desired code
603				 * (allocated with TclpLoadMemoryGetBuffer). */
604    int size,			/* Allocation size of buffer. */
605    int codeSize,		/* Size of code data read into buffer or -1 if
606				 * an error occurred and the buffer should
607				 * just be freed. */
608    Tcl_LoadHandle *loadHandle, /* Filled with token for dynamically loaded
609				 * file which will be passed back to
610				 * (*unloadProcPtr)() to unload the file. */
611    Tcl_FSUnloadFileProc **unloadProcPtr)
612				/* Filled with address of Tcl_FSUnloadFileProc
613				 * function which should be used for this
614				 * file. */
615{
616    Tcl_DyldLoadHandle *dyldLoadHandle;
617    NSObjectFileImage dyldObjFileImage = NULL;
618    Tcl_DyldModuleHandle *modulePtr;
619    NSModule module;
620    const char *objFileImageErrMsg = NULL;
621
622    /*
623     * Try to create an object file image that we can load from.
624     */
625
626    if (codeSize >= 0) {
627	NSObjectFileImageReturnCode err = NSObjectFileImageSuccess;
628	const struct fat_header *fh = buffer;
629	uint32_t ms = 0;
630#ifndef __LP64__
631	const struct mach_header *mh = NULL;
632	#define mh_size  sizeof(struct mach_header)
633	#define mh_magic MH_MAGIC
634	#define arch_abi 0
635#else
636	const struct mach_header_64 *mh = NULL;
637	#define mh_size  sizeof(struct mach_header_64)
638	#define mh_magic MH_MAGIC_64
639	#define arch_abi CPU_ARCH_ABI64
640#endif
641
642	if ((size_t) codeSize >= sizeof(struct fat_header)
643		&& fh->magic == OSSwapHostToBigInt32(FAT_MAGIC)) {
644	    uint32_t fh_nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
645
646	    /*
647	     * Fat binary, try to find mach_header for our architecture
648	     */
649
650	    TclLoadDbgMsg("Fat binary, %d archs", fh_nfat_arch);
651	    if ((size_t) codeSize >= sizeof(struct fat_header) +
652		    fh_nfat_arch * sizeof(struct fat_arch)) {
653		void *fatarchs = (char*)buffer + sizeof(struct fat_header);
654		const NXArchInfo *arch = NXGetLocalArchInfo();
655		struct fat_arch *fa;
656
657		if (fh->magic != FAT_MAGIC) {
658		    swap_fat_arch(fatarchs, fh_nfat_arch, arch->byteorder);
659		}
660		fa = NXFindBestFatArch(arch->cputype | arch_abi,
661			arch->cpusubtype, fatarchs, fh_nfat_arch);
662		if (fa) {
663		    TclLoadDbgMsg("NXFindBestFatArch() successful: "
664			    "local cputype %d subtype %d, "
665			    "fat cputype %d subtype %d",
666			    arch->cputype | arch_abi, arch->cpusubtype,
667			    fa->cputype, fa->cpusubtype);
668		    mh = (void*)((char*)buffer + fa->offset);
669		    ms = fa->size;
670		} else {
671		    TclLoadDbgMsg("NXFindBestFatArch() failed");
672		    err = NSObjectFileImageInappropriateFile;
673		}
674		if (fh->magic != FAT_MAGIC) {
675		    swap_fat_arch(fatarchs, fh_nfat_arch, arch->byteorder);
676		}
677	    } else {
678		TclLoadDbgMsg("Fat binary header failure");
679		err = NSObjectFileImageInappropriateFile;
680	    }
681	} else {
682	    /*
683	     * Thin binary
684	     */
685
686	    TclLoadDbgMsg("Thin binary");
687	    mh = buffer;
688	    ms = codeSize;
689	}
690	if (ms && !(ms >= mh_size && mh->magic == mh_magic &&
691		 mh->filetype == MH_BUNDLE)) {
692	    TclLoadDbgMsg("Inappropriate file: magic %x filetype %d",
693		    mh->magic, mh->filetype);
694	    err = NSObjectFileImageInappropriateFile;
695	}
696	if (err == NSObjectFileImageSuccess) {
697	    err = NSCreateObjectFileImageFromMemory(buffer, codeSize,
698		    &dyldObjFileImage);
699	    if (err == NSObjectFileImageSuccess) {
700		TclLoadDbgMsg("NSCreateObjectFileImageFromMemory() "
701			"successful");
702	    } else {
703		objFileImageErrMsg = DyldOFIErrorMsg(err);
704		TclLoadDbgMsg("NSCreateObjectFileImageFromMemory() failed: %s",
705			objFileImageErrMsg);
706	    }
707	} else {
708	    objFileImageErrMsg = DyldOFIErrorMsg(err);
709	}
710    }
711
712    /*
713     * If it went wrong (or we were asked to just deallocate), get rid of the
714     * memory block and create an error message.
715     */
716
717    if (dyldObjFileImage == NULL) {
718	vm_deallocate(mach_task_self(), (vm_address_t) buffer, size);
719	if (objFileImageErrMsg != NULL) {
720	    Tcl_AppendResult(interp, "NSCreateObjectFileImageFromMemory() "
721		    "error: ", objFileImageErrMsg, NULL);
722	}
723	return TCL_ERROR;
724    }
725
726    /*
727     * Extract the module we want from the image of the object file.
728     */
729
730    module = NSLinkModule(dyldObjFileImage, "[Memory Based Bundle]",
731	    NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_RETURN_ON_ERROR);
732    NSDestroyObjectFileImage(dyldObjFileImage);
733    if (module) {
734	TclLoadDbgMsg("NSLinkModule() successful");
735    } else {
736	NSLinkEditErrors editError;
737	int errorNumber;
738	const char *errorName, *errMsg;
739
740	NSLinkEditError(&editError, &errorNumber, &errorName, &errMsg);
741	TclLoadDbgMsg("NSLinkModule() failed: %s", errMsg);
742	Tcl_AppendResult(interp, errMsg, NULL);
743	return TCL_ERROR;
744    }
745
746    /*
747     * Stash the module reference within the load handle we create and return.
748     */
749
750    modulePtr = (Tcl_DyldModuleHandle *) ckalloc(sizeof(Tcl_DyldModuleHandle));
751    modulePtr->module = module;
752    modulePtr->nextPtr = NULL;
753    dyldLoadHandle = (Tcl_DyldLoadHandle *)
754	    ckalloc(sizeof(Tcl_DyldLoadHandle));
755#if TCL_DYLD_USE_DLFCN
756    dyldLoadHandle->dlHandle = NULL;
757#endif
758    dyldLoadHandle->dyldLibHeader = NULL;
759    dyldLoadHandle->modulePtr = modulePtr;
760    *loadHandle = (Tcl_LoadHandle) dyldLoadHandle;
761    *unloadProcPtr = &TclpUnloadFile;
762    return TCL_OK;
763}
764#endif /* TCL_LOAD_FROM_MEMORY */
765
766/*
767 * Local Variables:
768 * mode: c
769 * c-basic-offset: 4
770 * fill-column: 79
771 * End:
772 */
773