From fca40523b43d3d77abecf7ffcd91efb66fdc7893 Mon Sep 17 00:00:00 2001 From: das Date: Tue, 24 May 2005 04:19:32 +0000 Subject: * generic/tclIOUtil.c (TclLoadFile): * generic/tclInt.h: * unix/tcl.m4: * unix/tclLoadDyld.c: added support for [load]ing .bundle binaries in addition to .dylib's: .bundle's can be [unload]ed (unlike .dylib's), and can be [load]ed from memory, e.g. directly from VFS without needing to be written out to a temporary location first. [Bug 1202209] * unix/configure: autoconf-2.13 --- generic/tclIOUtil.c | 52 ++++++++- generic/tclInt.h | 11 +- unix/tclLoadDyld.c | 322 +++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 342 insertions(+), 43 deletions(-) diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c index 07ad92c..2647a8b 100644 --- a/generic/tclIOUtil.c +++ b/generic/tclIOUtil.c @@ -17,7 +17,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIOUtil.c,v 1.77.2.20 2004/12/02 18:48:14 vincentdarley Exp $ + * RCS: @(#) $Id: tclIOUtil.c,v 1.77.2.21 2005/05/24 04:19:32 das Exp $ */ #include "tclInt.h" @@ -2785,6 +2785,56 @@ Tcl_FSLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr, return TCL_ERROR; } +#ifdef TCL_LOAD_FROM_MEMORY + /* + * The platform supports loading code from memory, so ask for a + * buffer of the appropriate size, read the file into it and + * load the code from the buffer: + */ + do { + int ret, size; + void *buffer; + Tcl_StatBuf statBuf; + Tcl_Channel data; + + ret = Tcl_FSStat(pathPtr, &statBuf); + if (ret < 0) { + break; + } + size = (int) statBuf.st_size; + /* Tcl_Read takes an int: check that file size isn't wide */ + if (size != (Tcl_WideInt)statBuf.st_size) { + break; + } + data = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0666); + if (!data) { + break; + } + buffer = TclpLoadMemoryGetBuffer(interp, size); + if (!buffer) { + Tcl_Close(interp, data); + break; + } + Tcl_SetChannelOption(interp, data, "-translation", "binary"); + ret = Tcl_Read(data, buffer, size); + Tcl_Close(interp, data); + ret = TclpLoadMemory(interp, buffer, size, ret, handlePtr, unloadProcPtr); + if (ret == TCL_OK) { + if (*handlePtr == NULL) { + break; + } + if (sym1 != NULL) { + *proc1Ptr = TclpFindSymbol(interp, *handlePtr, sym1); + } + if (sym2 != NULL) { + *proc2Ptr = TclpFindSymbol(interp, *handlePtr, sym2); + } + return TCL_OK; + } + } while (0); + Tcl_ResetResult(interp); +#endif + /* * Get a temporary filename to use, first to * copy the file into, and then to load. diff --git a/generic/tclInt.h b/generic/tclInt.h index 300a14c..7f555ec 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclInt.h,v 1.118.2.9 2005/04/07 11:24:13 vasiljevic Exp $ + * RCS: @(#) $Id: tclInt.h,v 1.118.2.10 2005/05/24 04:19:33 das Exp $ */ #ifndef _TCLINT @@ -1789,6 +1789,15 @@ EXTERN int TclpDlopen _ANSI_ARGS_((Tcl_Interp *interp, EXTERN int TclpUtime _ANSI_ARGS_((Tcl_Obj *pathPtr, struct utimbuf *tval)); +#ifdef TCL_LOAD_FROM_MEMORY +EXTERN void* TclpLoadMemoryGetBuffer _ANSI_ARGS_(( + Tcl_Interp *interp, int size)); +EXTERN int TclpLoadMemory _ANSI_ARGS_((Tcl_Interp *interp, + void *buffer, int size, int codeSize, + Tcl_LoadHandle *loadHandle, + Tcl_FSUnloadFileProc **unloadProcPtr)); +#endif + /* *---------------------------------------------------------------- * Command procedures in the generic core: diff --git a/unix/tclLoadDyld.c b/unix/tclLoadDyld.c index 1bace2e..4a42b51 100644 --- a/unix/tclLoadDyld.c +++ b/unix/tclLoadDyld.c @@ -11,12 +11,13 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclLoadDyld.c,v 1.14.2.1 2005/05/14 20:52:33 das Exp $ + * RCS: @(#) $Id: tclLoadDyld.c,v 1.14.2.2 2005/05/24 04:19:33 das Exp $ */ #include "tclInt.h" #include "tclPort.h" #include +#include typedef struct Tcl_DyldModuleHandle { struct Tcl_DyldModuleHandle *nextModuleHandle; @@ -28,6 +29,59 @@ typedef struct Tcl_DyldLoadHandle { Tcl_DyldModuleHandle *firstModuleHandle; } Tcl_DyldLoadHandle; +#ifdef TCL_LOAD_FROM_MEMORY +typedef struct ThreadSpecificData { + int haveLoadMemory; +} ThreadSpecificData; + +static Tcl_ThreadDataKey dataKey; +#endif + +/* + *---------------------------------------------------------------------- + * + * DyldOFIErrorMsg -- + * + * Converts a numerical NSObjectFileImage error into an + * error message string. + * + * Results: + * Error message string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static CONST char* DyldOFIErrorMsg(int err) { + CONST char *ofi_msg = NULL; + + if (err != NSObjectFileImageSuccess) { + switch(err) { + case NSObjectFileImageFailure: + ofi_msg = "object file setup failure"; + break; + case NSObjectFileImageInappropriateFile: + ofi_msg = "not a Mach-O MH_BUNDLE file"; + break; + case NSObjectFileImageArch: + ofi_msg = "no object for this architecture"; + break; + case NSObjectFileImageFormat: + ofi_msg = "bad object file format"; + break; + case NSObjectFileImageAccess: + ofi_msg = "can't read object file"; + break; + default: + ofi_msg = "unknown error"; + break; + } + } + return ofi_msg; +} + /* *---------------------------------------------------------------------- * @@ -61,6 +115,8 @@ TclpDlopen(interp, pathPtr, loadHandle, unloadProcPtr) { Tcl_DyldLoadHandle *dyldLoadHandle; CONST struct mach_header *dyld_lib; + NSObjectFileImage dyld_ofi = NULL; + Tcl_DyldModuleHandle *dyldModuleHandle = NULL; CONST char *native; /* @@ -74,32 +130,66 @@ TclpDlopen(interp, pathPtr, loadHandle, unloadProcPtr) NSADDIMAGE_OPTION_RETURN_ON_ERROR); if (!dyld_lib) { - /* - * Let the OS loader examine the binary search path for - * whatever string the user gave us which hopefully refers - * to a file on the binary path - */ - Tcl_DString ds; - char *fileName = Tcl_GetString(pathPtr); - native = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds); - dyld_lib = NSAddImage(native, - NSADDIMAGE_OPTION_WITH_SEARCHING | - NSADDIMAGE_OPTION_RETURN_ON_ERROR); - Tcl_DStringFree(&ds); - } - - if (!dyld_lib) { NSLinkEditErrors editError; - CONST char *name, *msg; + CONST char *name, *msg, *ofi_msg = NULL; + NSLinkEditError(&editError, &errno, &name, &msg); - Tcl_AppendResult(interp, msg, (char *) NULL); - return TCL_ERROR; + if (editError == NSLinkEditFileAccessError) { + /* The requested file was not found: + * let the OS loader examine the binary search path for + * whatever string the user gave us which hopefully refers + * to a file on the binary path + */ + Tcl_DString ds; + char *fileName = Tcl_GetString(pathPtr); + CONST char *native = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds); + dyld_lib = NSAddImage(native, + NSADDIMAGE_OPTION_WITH_SEARCHING | + NSADDIMAGE_OPTION_RETURN_ON_ERROR); + Tcl_DStringFree(&ds); + if (!dyld_lib) { + NSLinkEditError(&editError, &errno, &name, &msg); + } + } else if ((editError == NSLinkEditFileFormatError && errno == EBADMACHO)) { + /* The requested file was found but was not of type MH_DYLIB, + * attempt to load it as a MH_BUNDLE: */ + NSObjectFileImageReturnCode err; + err = NSCreateObjectFileImageFromFile(native, &dyld_ofi); + ofi_msg = DyldOFIErrorMsg(err); + } + if (!dyld_lib && !dyld_ofi) { + Tcl_AppendResult(interp, msg, (char *) NULL); + if (ofi_msg) { + Tcl_AppendResult(interp, "NSCreateObjectFileImageFromFile() error: ", + ofi_msg, (char *) NULL); + } + return TCL_ERROR; + } } + if (dyld_ofi) { + NSModule module; + module = NSLinkModule(dyld_ofi, native, NSLINKMODULE_OPTION_BINDNOW | + NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSDestroyObjectFileImage(dyld_ofi); + if (module) { + dyldModuleHandle = (Tcl_DyldModuleHandle *) + ckalloc(sizeof(Tcl_DyldModuleHandle)); + if (!dyldModuleHandle) return TCL_ERROR; + dyldModuleHandle->module = module; + dyldModuleHandle->nextModuleHandle = NULL; + } else { + NSLinkEditErrors editError; + CONST char *name, *msg; + NSLinkEditError(&editError, &errno, &name, &msg); + Tcl_AppendResult(interp, msg, (char *) NULL); + return TCL_ERROR; + } + } dyldLoadHandle = (Tcl_DyldLoadHandle *) ckalloc(sizeof(Tcl_DyldLoadHandle)); if (!dyldLoadHandle) return TCL_ERROR; dyldLoadHandle->dyld_lib = dyld_lib; - dyldLoadHandle->firstModuleHandle = NULL; + dyldLoadHandle->firstModuleHandle = dyldModuleHandle; *loadHandle = (Tcl_LoadHandle) dyldLoadHandle; *unloadProcPtr = &TclpUnloadFile; return TCL_OK; @@ -134,35 +224,54 @@ TclpFindSymbol(interp, loadHandle, symbol) /* * dyld adds an underscore to the beginning of symbol names. */ - native = Tcl_UtfToExternalDString(NULL, symbol, -1, &ds); Tcl_DStringInit(&newName); Tcl_DStringAppend(&newName, "_", 1); native = Tcl_DStringAppend(&newName, native, -1); - nsSymbol = NSLookupSymbolInImage(dyldLoadHandle->dyld_lib, native, - NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW | - NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); - if(nsSymbol) { - Tcl_DyldModuleHandle *dyldModuleHandle; - proc = NSAddressOfSymbol(nsSymbol); - dyldModuleHandle = (Tcl_DyldModuleHandle *) ckalloc(sizeof(Tcl_DyldModuleHandle)); - if (dyldModuleHandle) { - dyldModuleHandle->module = NSModuleForSymbol(nsSymbol); - dyldModuleHandle->nextModuleHandle = dyldLoadHandle->firstModuleHandle; - dyldLoadHandle->firstModuleHandle = dyldModuleHandle; - } + if (dyldLoadHandle->dyld_lib) { + nsSymbol = NSLookupSymbolInImage(dyldLoadHandle->dyld_lib, native, + NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW | + NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + if(nsSymbol) { + /* until dyld supports unloading of MY_DYLIB binaries, the + * following is not needed: */ +#ifdef DYLD_SUPPORTS_DYLIB_UNLOADING + NSModule module = NSModuleForSymbol(nsSymbol); + Tcl_DyldModuleHandle *dyldModuleHandle = dyldLoadHandle->firstModuleHandle; + while (dyldModuleHandle) { + if (module == dyldModuleHandle->module) break; + dyldModuleHandle = dyldModuleHandle->nextModuleHandle; + } + if (!dyldModuleHandle) { + dyldModuleHandle = (Tcl_DyldModuleHandle *) + ckalloc(sizeof(Tcl_DyldModuleHandle)); + if (dyldModuleHandle) { + dyldModuleHandle->module = module; + dyldModuleHandle->nextModuleHandle = + dyldLoadHandle->firstModuleHandle; + dyldLoadHandle->firstModuleHandle = dyldModuleHandle; + } + } +#endif /* DYLD_SUPPORTS_DYLIB_UNLOADING */ + } else { + NSLinkEditErrors editError; + CONST char *name, *msg; + NSLinkEditError(&editError, &errno, &name, &msg); + Tcl_AppendResult(interp, msg, (char *) NULL); + } } else { - NSLinkEditErrors editError; - CONST char *name, *msg; - NSLinkEditError(&editError, &errno, &name, &msg); - Tcl_AppendResult(interp, msg, (char *) NULL); + nsSymbol = NSLookupSymbolInModule(dyldLoadHandle->firstModuleHandle->module, + native); + } + if(nsSymbol) { + proc = NSAddressOfSymbol(nsSymbol); } Tcl_DStringFree(&newName); Tcl_DStringFree(&ds); return proc; } - + /* *---------------------------------------------------------------------- * @@ -177,7 +286,8 @@ TclpFindSymbol(interp, loadHandle, symbol) * * Side effects: * Code dissapears from memory. - * Note that this is a no-op on older (OpenStep) versions of dyld. + * Note that dyld currently only supports unloading of binaries of + * type MH_BUNDLE loaded with NSLinkModule() in TclpDlopen() above. * *---------------------------------------------------------------------- */ @@ -194,14 +304,15 @@ TclpUnloadFile(loadHandle) void *ptr; while (dyldModuleHandle) { - NSUnLinkModule(dyldModuleHandle->module, NSUNLINKMODULE_OPTION_NONE); + NSUnLinkModule(dyldModuleHandle->module, + NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); ptr = dyldModuleHandle; dyldModuleHandle = dyldModuleHandle->nextModuleHandle; ckfree(ptr); } ckfree((char*) dyldLoadHandle); } - + /* *---------------------------------------------------------------------- * @@ -231,3 +342,132 @@ TclGuessPackageName(fileName, bufPtr) { return 0; } + +#ifdef TCL_LOAD_FROM_MEMORY +/* + *---------------------------------------------------------------------- + * + * TclpLoadMemoryGetBuffer -- + * + * Allocate a buffer that can be used with TclpLoadMemory() below. + * + * Results: + * Pointer to allocated buffer or NULL if an error occurs. + * + * Side effects: + * Buffer is allocated. + * + *---------------------------------------------------------------------- + */ + +void* +TclpLoadMemoryGetBuffer(interp, size) + Tcl_Interp *interp; /* Used for error reporting. */ + int size; /* Size of desired buffer */ +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + void * buffer = NULL; + + if (!tsdPtr->haveLoadMemory) { + /* NSCreateObjectFileImageFromMemory is available but always + * fails prior to Darwin 7 */ + struct utsname name; + if (!uname(&name)) { + long release = strtol(name.release, NULL, 10); + tsdPtr->haveLoadMemory = (release >= 7) ? 1 : -1; + } + } + if (tsdPtr->haveLoadMemory > 0) { + /* We must allocate the buffer using vm_allocate, because + * NSCreateObjectFileImageFromMemory will dispose of it + * using vm_deallocate. + */ + int err = vm_allocate(mach_task_self(), + (vm_address_t*)&buffer, size, 1); + if (err) { + buffer = NULL; + } + } + return buffer; +} + +/* + *---------------------------------------------------------------------- + * + * TclpLoadMemory -- + * + * Dynamically loads binary code file from memory and returns + * a handle to the new code. + * + * Results: + * A standard Tcl completion code. If an error occurs, an error + * message is left in the interpreter's result. + * + * Side effects: + * New code is loaded from memory. + * + *---------------------------------------------------------------------- + */ + +int +TclpLoadMemory(interp, buffer, size, codeSize, loadHandle, unloadProcPtr) + Tcl_Interp *interp; /* Used for error reporting. */ + void *buffer; /* Buffer containing the desired code + * (allocated with TclpLoadMemoryGetBuffer). */ + int size; /* Allocation size of buffer. */ + int codeSize; /* Size of code data read into buffer or -1 if + * an error occurred and the buffer should + * just be freed. */ + Tcl_LoadHandle *loadHandle; /* Filled with token for dynamically loaded + * file which will be passed back to + * (*unloadProcPtr)() to unload the file. */ + Tcl_FSUnloadFileProc **unloadProcPtr; + /* Filled with address of Tcl_FSUnloadFileProc + * function which should be used for + * this file. */ +{ + Tcl_DyldLoadHandle *dyldLoadHandle; + NSObjectFileImage dyld_ofi = NULL; + Tcl_DyldModuleHandle *dyldModuleHandle; + CONST char *ofi_msg = NULL; + + if (codeSize >= 0) { + NSObjectFileImageReturnCode err; + err = NSCreateObjectFileImageFromMemory(buffer, codeSize, &dyld_ofi); + ofi_msg = DyldOFIErrorMsg(err); + } + if (!dyld_ofi) { + vm_deallocate(mach_task_self(), (vm_address_t) buffer, size); + if (ofi_msg) { + Tcl_AppendResult(interp, "NSCreateObjectFileImageFromFile() error: ", + ofi_msg, (char *) NULL); + } + return TCL_ERROR; + } else { + NSModule module; + module = NSLinkModule(dyld_ofi, "[Memory Based Bundle]", + NSLINKMODULE_OPTION_BINDNOW |NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSDestroyObjectFileImage(dyld_ofi); + if (module) { + dyldModuleHandle = (Tcl_DyldModuleHandle *) + ckalloc(sizeof(Tcl_DyldModuleHandle)); + if (!dyldModuleHandle) return TCL_ERROR; + dyldModuleHandle->module = module; + dyldModuleHandle->nextModuleHandle = NULL; + } else { + NSLinkEditErrors editError; + CONST char *name, *msg; + NSLinkEditError(&editError, &errno, &name, &msg); + Tcl_AppendResult(interp, msg, (char *) NULL); + return TCL_ERROR; + } + } + dyldLoadHandle = (Tcl_DyldLoadHandle *) ckalloc(sizeof(Tcl_DyldLoadHandle)); + if (!dyldLoadHandle) return TCL_ERROR; + dyldLoadHandle->dyld_lib = NULL; + dyldLoadHandle->firstModuleHandle = dyldModuleHandle; + *loadHandle = (Tcl_LoadHandle) dyldLoadHandle; + *unloadProcPtr = &TclpUnloadFile; + return TCL_OK; +} +#endif -- cgit v0.12