summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordas <das>2005-05-24 04:19:32 (GMT)
committerdas <das>2005-05-24 04:19:32 (GMT)
commitfca40523b43d3d77abecf7ffcd91efb66fdc7893 (patch)
tree81f89c2d95db81e9ecaaddcc33263bcf0f0ac7b1
parent753360929f8d1c7c1184864fe3cd7e5bb9acf497 (diff)
downloadtcl-fca40523b43d3d77abecf7ffcd91efb66fdc7893.zip
tcl-fca40523b43d3d77abecf7ffcd91efb66fdc7893.tar.gz
tcl-fca40523b43d3d77abecf7ffcd91efb66fdc7893.tar.bz2
* 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
-rw-r--r--generic/tclIOUtil.c52
-rw-r--r--generic/tclInt.h11
-rw-r--r--unix/tclLoadDyld.c322
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 <mach-o/dyld.h>
+#include <mach/mach.h>
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