Detect conflicting Python DLL on module import under Windows - as per [ Patch #101676 ]
This commit is contained in:
parent
85788edca4
commit
1f7838bcc0
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <direct.h>
|
#include <direct.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "importdl.h"
|
#include "importdl.h"
|
||||||
|
@ -19,11 +20,144 @@ const struct filedescr _PyImport_DynLoadFiletab[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#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,
|
dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname,
|
||||||
const char *pathname, FILE *fp)
|
const char *pathname, FILE *fp)
|
||||||
{
|
{
|
||||||
dl_funcptr p;
|
dl_funcptr p;
|
||||||
char funcname[258];
|
char funcname[258], *import_python;
|
||||||
|
|
||||||
sprintf(funcname, "init%.200s", shortname);
|
sprintf(funcname, "init%.200s", shortname);
|
||||||
|
|
||||||
|
@ -91,6 +225,23 @@ dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname,
|
||||||
}
|
}
|
||||||
PyErr_SetString(PyExc_ImportError, errBuf);
|
PyErr_SetString(PyExc_ImportError, errBuf);
|
||||||
return NULL;
|
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);
|
p = GetProcAddress(hDLL, funcname);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue