Attachment "load_from_memory-HEAD.diff" to
ticket [1202209fff]
added by
das
2005-05-15 12:38:12.
Index: generic/tclIOUtil.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIOUtil.c,v
retrieving revision 1.118
diff -u -p -r1.118 tclIOUtil.c
--- generic/tclIOUtil.c 10 May 2005 18:34:40 -0000 1.118
+++ generic/tclIOUtil.c 15 May 2005 03:09:52 -0000
@@ -3065,6 +3065,58 @@ TclLoadFile(interp, pathPtr, symc, symbo
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) {
+ int i;
+ if (*handlePtr == NULL) {
+ break;
+ }
+ for (i = 0;i < symc;i++) {
+ if (symbols[i] != NULL) {
+ *procPtrs[i] = TclpFindSymbol(interp, *handlePtr,
+ symbols[i]);
+ }
+ }
+ *clientDataPtr = (ClientData)*handlePtr;
+ 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.
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.229
diff -u -p -r1.229 tclInt.h
--- generic/tclInt.h 10 May 2005 18:34:42 -0000 1.229
+++ generic/tclInt.h 15 May 2005 03:09:52 -0000
@@ -2075,6 +2075,14 @@ MODULE_SCOPE int TclpDlopen _ANSI_ARGS_(
Tcl_FSUnloadFileProc **unloadProcPtr));
MODULE_SCOPE int TclpUtime _ANSI_ARGS_((Tcl_Obj *pathPtr,
struct utimbuf *tval));
+#ifdef TCL_LOAD_FROM_MEMORY
+MODULE_SCOPE void* TclpLoadMemoryGetBuffer _ANSI_ARGS_((
+ Tcl_Interp *interp, int size));
+MODULE_SCOPE int TclpLoadMemory _ANSI_ARGS_((Tcl_Interp *interp,
+ void *buffer, int size, int codeSize,
+ Tcl_LoadHandle *loadHandle,
+ Tcl_FSUnloadFileProc **unloadProcPtr));
+#endif
/*
*----------------------------------------------------------------
Index: unix/configure
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/configure,v
retrieving revision 1.145
diff -u -p -r1.145 configure
--- unix/configure 14 May 2005 20:46:46 -0000 1.145
+++ unix/configure 15 May 2005 03:09:53 -0000
@@ -8270,6 +8270,11 @@ cat >>confdefs.h <<\_ACEOF
#define MODULE_SCOPE __private_extern__
_ACEOF
+
+cat >>confdefs.h <<\_ACEOF
+#define TCL_LOAD_FROM_MEMORY 1
+_ACEOF
+
# prior to Darwin 7, realpath is not threadsafe, so don't
# use it when threads are enabled, c.f. bug # 711232:
echo "$as_me:$LINENO: checking for realpath" >&5
Index: unix/tcl.m4
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tcl.m4,v
retrieving revision 1.144
diff -u -p -r1.144 tcl.m4
--- unix/tcl.m4 14 May 2005 20:46:48 -0000 1.144
+++ unix/tcl.m4 15 May 2005 03:09:53 -0000
@@ -1439,6 +1439,7 @@ dnl AC_CHECK_TOOL(AR, ar)
AC_DEFINE(TCL_DEFAULT_ENCODING,"utf-8",
[Are we to override what our default encoding is?])
AC_DEFINE(MODULE_SCOPE, __private_extern__, [Linker support for module scope symbols])
+ AC_DEFINE(TCL_LOAD_FROM_MEMORY, 1, [Can this platform load code from memory?])
# prior to Darwin 7, realpath is not threadsafe, so don't
# use it when threads are enabled, c.f. bug # 711232:
AC_CHECK_FUNC(realpath)
Index: unix/tclLoadDyld.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclLoadDyld.c,v
retrieving revision 1.16
diff -u -p -r1.16 tclLoadDyld.c
--- unix/tclLoadDyld.c 14 May 2005 20:46:48 -0000 1.16
+++ unix/tclLoadDyld.c 15 May 2005 03:09:53 -0000
@@ -16,6 +16,7 @@
#include "tclInt.h"
#include <mach-o/dyld.h>
+#include <mach/mach.h>
typedef struct Tcl_DyldModuleHandle {
struct Tcl_DyldModuleHandle *nextModuleHandle;
@@ -27,6 +28,34 @@ typedef struct Tcl_DyldLoadHandle {
Tcl_DyldModuleHandle *firstModuleHandle;
} Tcl_DyldLoadHandle;
+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;
+}
+
/*
*----------------------------------------------------------------------
*
@@ -45,7 +74,7 @@ typedef struct Tcl_DyldLoadHandle {
*----------------------------------------------------------------------
*/
-int
+MODULE_SCOPE int
TclpDlopen(interp, pathPtr, loadHandle, unloadProcPtr)
Tcl_Interp *interp; /* Used for error reporting. */
Tcl_Obj *pathPtr; /* Name of the file containing the desired
@@ -60,6 +89,8 @@ TclpDlopen(interp, pathPtr, loadHandle,
{
Tcl_DyldLoadHandle *dyldLoadHandle;
CONST struct mach_header *dyld_lib;
+ NSObjectFileImage dyld_ofi = NULL;
+ Tcl_DyldModuleHandle *dyldModuleHandle = NULL;
CONST char *native;
/*
@@ -73,32 +104,66 @@ TclpDlopen(interp, pathPtr, loadHandle,
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;
@@ -119,7 +184,7 @@ TclpDlopen(interp, pathPtr, loadHandle,
*
*----------------------------------------------------------------------
*/
-Tcl_PackageInitProc*
+MODULE_SCOPE Tcl_PackageInitProc*
TclpFindSymbol(interp, loadHandle, symbol)
Tcl_Interp *interp;
Tcl_LoadHandle loadHandle;
@@ -133,28 +198,47 @@ TclpFindSymbol(interp, loadHandle, symbo
/*
* 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);
@@ -176,12 +260,13 @@ TclpFindSymbol(interp, loadHandle, symbo
*
* 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.
*
*----------------------------------------------------------------------
*/
-void
+MODULE_SCOPE void
TclpUnloadFile(loadHandle)
Tcl_LoadHandle loadHandle; /* loadHandle returned by a previous call
* to TclpDlopen(). The loadHandle is
@@ -193,7 +278,8 @@ 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);
@@ -221,7 +307,7 @@ TclpUnloadFile(loadHandle)
*----------------------------------------------------------------------
*/
-int
+MODULE_SCOPE int
TclGuessPackageName(fileName, bufPtr)
CONST char *fileName; /* Name of file containing package (already
* translated to local form if needed). */
@@ -230,3 +316,133 @@ 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+MODULE_SCOPE void*
+TclpLoadMemoryGetBuffer(interp, size)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ int size; /* Size of desired buffer */
+{
+ static int haveLoadMemory = -1;
+ void * buffer = NULL;
+
+ if (haveLoadMemory < 0) {
+ /* NSCreateObjectFileImageFromMemory is available but always
+ * fails prior to Darwin 7 */
+ struct utsname name;
+ haveLoadMemory = 0;
+ if (!uname(&name)) {
+ long release = strtol(name.release, NULL, 10);
+ haveLoadMemory = (release >= 7);
+ }
+ }
+ if (haveLoadMemory) {
+ /* 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+MODULE_SCOPE 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