[Python-checkins] CVS: python/dist/src/Python dynload_win.c,2.6,2.7

Mark Hammond python-dev@python.org
Thu, 5 Oct 2000 03:54:48 -0700


Update of /cvsroot/python/python/dist/src/Python
In directory slayer.i.sourceforge.net:/tmp/cvs-serv21547

Modified Files:
	dynload_win.c 
Log Message:
Detect conflicting Python DLL on module import under Windows - as per [ Patch #101676 ]


Index: dynload_win.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/dynload_win.c,v
retrieving revision 2.6
retrieving revision 2.7
diff -C2 -r2.6 -r2.7
*** dynload_win.c	2000/09/01 23:29:28	2.6
--- dynload_win.c	2000/10/05 10:54:45	2.7
***************
*** 4,7 ****
--- 4,8 ----
  #include <windows.h>
  #include <direct.h>
+ #include <ctype.h>
  
  #include "Python.h"
***************
*** 20,28 ****
  
  
  dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname,
  				    const char *pathname, FILE *fp)
  {
  	dl_funcptr p;
! 	char funcname[258];
  
  	sprintf(funcname, "init%.200s", shortname);
--- 21,162 ----
  
  
+ #ifdef MS_WIN32
+ 
+ /* Case insensitive string compare, to avoid any dependencies on particular
+    C RTL implementations */
+ 
+ static int strcasecmp (char *string1, char *string2)
+ { 
+ 	int first, second;
+ 
+ 	do {
+ 		first  = tolower(*string1);
+ 		second = tolower(*string2);
+ 		string1++;
+ 		string2++;
+ 	} while (first && first == second);
+ 
+ 	return (first - second);
+ } 
+ 
+ 
+ /* Function to return the name of the "python" DLL that the supplied module
+    directly imports.  Looks through the list of imported modules and
+    returns the first entry that starts with "python" (case sensitive) and
+    is followed by nothing but numbers until the separator (period).
+ 
+    Returns a pointer to the import name, or NULL if no matching name was
+    located.
+ 
+    This function parses through the PE header for the module as loaded in
+    memory by the system loader.  The PE header is accessed as documented by
+    Microsoft in the MSDN PE and COFF specification (2/99), and handles
+    both PE32 and PE32+.  It only worries about the direct import table and
+    not the delay load import table since it's unlikely an extension is
+    going to be delay loading Python (after all, it's already loaded).
+ 
+    If any magic values are not found (e.g., the PE header or optional
+    header magic), then this function simply returns NULL. */
+ 
+ #define DWORD_AT(mem) (*(DWORD *)(mem))
+ #define WORD_AT(mem)  (*(WORD *)(mem))
+ 
+ static char *GetPythonImport (HINSTANCE hModule)
+ {
+ 	unsigned char *dllbase, *import_data, *import_name;
+ 	DWORD pe_offset, opt_offset;
+ 	WORD opt_magic;
+ 	int num_dict_off, import_off;
+ 
+ 	/* Safety check input */
+ 	if (hModule == NULL) {
+ 		return NULL;
+ 	}
+ 
+ 	/* Module instance is also the base load address.  First portion of
+ 	   memory is the MS-DOS loader, which holds the offset to the PE
+ 	   header (from the load base) at 0x3C */
+ 	dllbase = (unsigned char *)hModule;
+ 	pe_offset = DWORD_AT(dllbase + 0x3C);
+ 
+ 	/* The PE signature must be "PE\0\0" */
+ 	if (memcmp(dllbase+pe_offset,"PE\0\0",4)) {
+ 		return NULL;
+ 	}
+ 
+ 	/* Following the PE signature is the standard COFF header (20
+ 	   bytes) and then the optional header.  The optional header starts
+ 	   with a magic value of 0x10B for PE32 or 0x20B for PE32+ (PE32+
+ 	   uses 64-bits for some fields).  It might also be 0x107 for a ROM
+ 	   image, but we don't process that here.
+ 
+ 	   The optional header ends with a data dictionary that directly
+ 	   points to certain types of data, among them the import entries
+ 	   (in the second table entry). Based on the header type, we
+ 	   determine offsets for the data dictionary count and the entry
+ 	   within the dictionary pointing to the imports. */
+ 
+ 	opt_offset = pe_offset + 4 + 20;
+ 	opt_magic = WORD_AT(dllbase+opt_offset);
+ 	if (opt_magic == 0x10B) {
+ 		/* PE32 */
+ 		num_dict_off = 92;
+ 		import_off   = 104;
+ 	} else if (opt_magic == 0x20B) {
+ 		/* PE32+ */
+ 		num_dict_off = 108;
+ 		import_off   = 120;
+ 	} else {
+ 		/* Unsupported */
+ 		return NULL;
+ 	}
+ 
+ 	/* Now if an import table exists, offset to it and walk the list of
+ 	   imports.  The import table is an array (ending when an entry has
+ 	   empty values) of structures (20 bytes each), which contains (at
+ 	   offset 12) a relative address (to the module base) at which a
+ 	   string constant holding the import name is located. */
+ 
+ 	if (DWORD_AT(dllbase + opt_offset + num_dict_off) >= 2) {
+ 		import_data = dllbase + DWORD_AT(dllbase +
+ 						 opt_offset +
+ 						 import_off);
+ 		while (DWORD_AT(import_data)) {
+ 			import_name = dllbase + DWORD_AT(import_data+12);
+ 			if (strlen(import_name) >= 6 &&
+ 			    !strncmp(import_name,"python",6)) {
+ 				char *pch;
+ 
+ 				/* Ensure python prefix is followed only
+ 				   by numbers to the end of the basename */
+ 				pch = import_name + 6;
+ 				while (*pch && *pch != '.') {
+ 					if (*pch >= '0' && *pch <= '9') {
+ 						pch++;
+ 					} else {
+ 						pch = NULL;
+ 						break;
+ 					}
+ 				}
+ 	    
+ 				if (pch) {
+ 					/* Found it - return the name */
+ 					return import_name;
+ 				}
+ 			}
+ 			import_data += 20;
+ 		}
+ 	}
+ 
+ 	return NULL;
+ }
+ #endif /* MS_WIN32 */
+ 
+ 
  dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname,
  				    const char *pathname, FILE *fp)
  {
  	dl_funcptr p;
! 	char funcname[258], *import_python;
  
  	sprintf(funcname, "init%.200s", shortname);
***************
*** 92,95 ****
--- 226,246 ----
  			PyErr_SetString(PyExc_ImportError, errBuf);
  		return NULL;
+ 		} else {
+ 			char buffer[256];
+ 
+ 			sprintf(buffer,"python%d%d.dll",
+ 				PY_MAJOR_VERSION,PY_MINOR_VERSION);
+ 			import_python = GetPythonImport(hDLL);
+ 
+ 			if (import_python &&
+ 			    strcasecmp(buffer,import_python)) {
+ 				sprintf(buffer,
+ 					"Module use of %s conflicts "
+ 					"with this version of Python.",
+ 					import_python);
+ 				PyErr_SetString(PyExc_ImportError,buffer);
+ 				FreeLibrary(hDLL);
+ 				return NULL;
+ 			}
  		}
  		p = GetProcAddress(hDLL, funcname);