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