Tcl Source Code

Artifact [31a58dac0b]
Login

Artifact 31a58dac0b5a6639585ca5911c8a1b4e7e9d5aae:

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