Home | History | Annotate | Download | only in Python
      1 
      2 /* Support for dynamic loading of extension modules */
      3 
      4 #include "Python.h"
      5 
      6 #ifdef HAVE_DIRECT_H
      7 #include <direct.h>
      8 #endif
      9 #include <ctype.h>
     10 
     11 #include "importdl.h"
     12 #include "patchlevel.h"
     13 #include <windows.h>
     14 
     15 // "activation context" magic - see dl_nt.c...
     16 #if HAVE_SXS
     17 extern ULONG_PTR _Py_ActivateActCtx();
     18 void _Py_DeactivateActCtx(ULONG_PTR cookie);
     19 #endif
     20 
     21 #ifdef _DEBUG
     22 #define PYD_DEBUG_SUFFIX "_d"
     23 #else
     24 #define PYD_DEBUG_SUFFIX ""
     25 #endif
     26 
     27 #ifdef PYD_PLATFORM_TAG
     28 #define PYD_TAGGED_SUFFIX PYD_DEBUG_SUFFIX ".cp" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) "-" PYD_PLATFORM_TAG ".pyd"
     29 #else
     30 #define PYD_TAGGED_SUFFIX PYD_DEBUG_SUFFIX ".cp" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) ".pyd"
     31 #endif
     32 
     33 #define PYD_UNTAGGED_SUFFIX PYD_DEBUG_SUFFIX ".pyd"
     34 
     35 const char *_PyImport_DynLoadFiletab[] = {
     36     PYD_TAGGED_SUFFIX,
     37     PYD_UNTAGGED_SUFFIX,
     38     NULL
     39 };
     40 
     41 /* Case insensitive string compare, to avoid any dependencies on particular
     42    C RTL implementations */
     43 
     44 static int strcasecmp (const char *string1, const char *string2)
     45 {
     46     int first, second;
     47 
     48     do {
     49         first  = tolower(*string1);
     50         second = tolower(*string2);
     51         string1++;
     52         string2++;
     53     } while (first && first == second);
     54 
     55     return (first - second);
     56 }
     57 
     58 
     59 /* Function to return the name of the "python" DLL that the supplied module
     60    directly imports.  Looks through the list of imported modules and
     61    returns the first entry that starts with "python" (case sensitive) and
     62    is followed by nothing but numbers until the separator (period).
     63 
     64    Returns a pointer to the import name, or NULL if no matching name was
     65    located.
     66 
     67    This function parses through the PE header for the module as loaded in
     68    memory by the system loader.  The PE header is accessed as documented by
     69    Microsoft in the MSDN PE and COFF specification (2/99), and handles
     70    both PE32 and PE32+.  It only worries about the direct import table and
     71    not the delay load import table since it's unlikely an extension is
     72    going to be delay loading Python (after all, it's already loaded).
     73 
     74    If any magic values are not found (e.g., the PE header or optional
     75    header magic), then this function simply returns NULL. */
     76 
     77 #define DWORD_AT(mem) (*(DWORD *)(mem))
     78 #define WORD_AT(mem)  (*(WORD *)(mem))
     79 
     80 static char *GetPythonImport (HINSTANCE hModule)
     81 {
     82     unsigned char *dllbase, *import_data, *import_name;
     83     DWORD pe_offset, opt_offset;
     84     WORD opt_magic;
     85     int num_dict_off, import_off;
     86 
     87     /* Safety check input */
     88     if (hModule == NULL) {
     89         return NULL;
     90     }
     91 
     92     /* Module instance is also the base load address.  First portion of
     93        memory is the MS-DOS loader, which holds the offset to the PE
     94        header (from the load base) at 0x3C */
     95     dllbase = (unsigned char *)hModule;
     96     pe_offset = DWORD_AT(dllbase + 0x3C);
     97 
     98     /* The PE signature must be "PE\0\0" */
     99     if (memcmp(dllbase+pe_offset,"PE\0\0",4)) {
    100         return NULL;
    101     }
    102 
    103     /* Following the PE signature is the standard COFF header (20
    104        bytes) and then the optional header.  The optional header starts
    105        with a magic value of 0x10B for PE32 or 0x20B for PE32+ (PE32+
    106        uses 64-bits for some fields).  It might also be 0x107 for a ROM
    107        image, but we don't process that here.
    108 
    109        The optional header ends with a data dictionary that directly
    110        points to certain types of data, among them the import entries
    111        (in the second table entry). Based on the header type, we
    112        determine offsets for the data dictionary count and the entry
    113        within the dictionary pointing to the imports. */
    114 
    115     opt_offset = pe_offset + 4 + 20;
    116     opt_magic = WORD_AT(dllbase+opt_offset);
    117     if (opt_magic == 0x10B) {
    118         /* PE32 */
    119         num_dict_off = 92;
    120         import_off   = 104;
    121     } else if (opt_magic == 0x20B) {
    122         /* PE32+ */
    123         num_dict_off = 108;
    124         import_off   = 120;
    125     } else {
    126         /* Unsupported */
    127         return NULL;
    128     }
    129 
    130     /* Now if an import table exists, offset to it and walk the list of
    131        imports.  The import table is an array (ending when an entry has
    132        empty values) of structures (20 bytes each), which contains (at
    133        offset 12) a relative address (to the module base) at which a
    134        string constant holding the import name is located. */
    135 
    136     if (DWORD_AT(dllbase + opt_offset + num_dict_off) >= 2) {
    137         /* We have at least 2 tables - the import table is the second
    138            one.  But still it may be that the table size is zero */
    139         if (0 == DWORD_AT(dllbase + opt_offset + import_off + sizeof(DWORD)))
    140             return NULL;
    141         import_data = dllbase + DWORD_AT(dllbase +
    142                                          opt_offset +
    143                                          import_off);
    144         while (DWORD_AT(import_data)) {
    145             import_name = dllbase + DWORD_AT(import_data+12);
    146             if (strlen(import_name) >= 6 &&
    147                 !strncmp(import_name,"python",6)) {
    148                 char *pch;
    149 
    150 #ifndef _DEBUG
    151                 /* In a release version, don't claim that python3.dll is
    152                    a Python DLL. */
    153                 if (strcmp(import_name, "python3.dll") == 0) {
    154                     import_data += 20;
    155                     continue;
    156                 }
    157 #endif
    158 
    159                 /* Ensure python prefix is followed only
    160                    by numbers to the end of the basename */
    161                 pch = import_name + 6;
    162 #ifdef _DEBUG
    163                 while (*pch && pch[0] != '_' && pch[1] != 'd' && pch[2] != '.') {
    164 #else
    165                 while (*pch && *pch != '.') {
    166 #endif
    167                     if (*pch >= '0' && *pch <= '9') {
    168                         pch++;
    169                     } else {
    170                         pch = NULL;
    171                         break;
    172                     }
    173                 }
    174 
    175                 if (pch) {
    176                     /* Found it - return the name */
    177                     return import_name;
    178                 }
    179             }
    180             import_data += 20;
    181         }
    182     }
    183 
    184     return NULL;
    185 }
    186 
    187 dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
    188                                               const char *shortname,
    189                                               PyObject *pathname, FILE *fp)
    190 {
    191     dl_funcptr p;
    192     char funcname[258], *import_python;
    193     wchar_t *wpathname;
    194 
    195 #ifndef _DEBUG
    196     _Py_CheckPython3();
    197 #endif
    198 
    199     wpathname = PyUnicode_AsUnicode(pathname);
    200     if (wpathname == NULL)
    201         return NULL;
    202 
    203     PyOS_snprintf(funcname, sizeof(funcname), "%.20s_%.200s", prefix, shortname);
    204 
    205     {
    206         HINSTANCE hDLL = NULL;
    207         unsigned int old_mode;
    208 #if HAVE_SXS
    209         ULONG_PTR cookie = 0;
    210 #endif
    211 
    212         /* Don't display a message box when Python can't load a DLL */
    213         old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
    214 
    215 #if HAVE_SXS
    216         cookie = _Py_ActivateActCtx();
    217 #endif
    218         /* We use LoadLibraryEx so Windows looks for dependent DLLs
    219             in directory of pathname first. */
    220         /* XXX This call doesn't exist in Windows CE */
    221         hDLL = LoadLibraryExW(wpathname, NULL,
    222                               LOAD_WITH_ALTERED_SEARCH_PATH);
    223 #if HAVE_SXS
    224         _Py_DeactivateActCtx(cookie);
    225 #endif
    226 
    227         /* restore old error mode settings */
    228         SetErrorMode(old_mode);
    229 
    230         if (hDLL==NULL){
    231             PyObject *message;
    232             unsigned int errorCode;
    233 
    234             /* Get an error string from Win32 error code */
    235             wchar_t theInfo[256]; /* Pointer to error text
    236                                   from system */
    237             int theLength; /* Length of error text */
    238 
    239             errorCode = GetLastError();
    240 
    241             theLength = FormatMessageW(
    242                 FORMAT_MESSAGE_FROM_SYSTEM |
    243                 FORMAT_MESSAGE_IGNORE_INSERTS, /* flags */
    244                 NULL, /* message source */
    245                 errorCode, /* the message (error) ID */
    246                 MAKELANGID(LANG_NEUTRAL,
    247                            SUBLANG_DEFAULT),
    248                            /* Default language */
    249                 theInfo, /* the buffer */
    250                 sizeof(theInfo) / sizeof(wchar_t), /* size in wchars */
    251                 NULL); /* no additional format args. */
    252 
    253             /* Problem: could not get the error message.
    254                This should not happen if called correctly. */
    255             if (theLength == 0) {
    256                 message = PyUnicode_FromFormat(
    257                     "DLL load failed with error code %d",
    258                     errorCode);
    259             } else {
    260                 /* For some reason a \r\n
    261                    is appended to the text */
    262                 if (theLength >= 2 &&
    263                     theInfo[theLength-2] == '\r' &&
    264                     theInfo[theLength-1] == '\n') {
    265                     theLength -= 2;
    266                     theInfo[theLength] = '\0';
    267                 }
    268                 message = PyUnicode_FromString(
    269                     "DLL load failed: ");
    270 
    271                 PyUnicode_AppendAndDel(&message,
    272                     PyUnicode_FromWideChar(
    273                         theInfo,
    274                         theLength));
    275             }
    276             if (message != NULL) {
    277                 PyObject *shortname_obj = PyUnicode_FromString(shortname);
    278                 PyErr_SetImportError(message, shortname_obj, pathname);
    279                 Py_XDECREF(shortname_obj);
    280                 Py_DECREF(message);
    281             }
    282             return NULL;
    283         } else {
    284             char buffer[256];
    285 
    286             PyOS_snprintf(buffer, sizeof(buffer),
    287 #ifdef _DEBUG
    288                           "python%d%d_d.dll",
    289 #else
    290                           "python%d%d.dll",
    291 #endif
    292                           PY_MAJOR_VERSION,PY_MINOR_VERSION);
    293             import_python = GetPythonImport(hDLL);
    294 
    295             if (import_python &&
    296                 strcasecmp(buffer,import_python)) {
    297                 PyErr_Format(PyExc_ImportError,
    298                              "Module use of %.150s conflicts "
    299                              "with this version of Python.",
    300                              import_python);
    301                 FreeLibrary(hDLL);
    302                 return NULL;
    303             }
    304         }
    305         p = GetProcAddress(hDLL, funcname);
    306     }
    307 
    308     return p;
    309 }
    310