Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Python/dynload_win.c
12 views
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
31
const 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
58
static 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
#ifdef Py_ENABLE_SHARED
167
/* Load python3.dll before loading any extension module that might refer
168
to it. That way, we can be sure that always the python3.dll corresponding
169
to this python DLL is loaded, not a python3.dll that might be on the path
170
by chance.
171
Return whether the DLL was found.
172
*/
173
extern HMODULE PyWin_DLLhModule;
174
static int
175
_Py_CheckPython3(void)
176
{
177
static int python3_checked = 0;
178
static HANDLE hPython3;
179
#define MAXPATHLEN 512
180
wchar_t py3path[MAXPATHLEN+1];
181
if (python3_checked) {
182
return hPython3 != NULL;
183
}
184
python3_checked = 1;
185
186
/* If there is a python3.dll next to the python3y.dll,
187
use that DLL */
188
if (PyWin_DLLhModule && GetModuleFileNameW(PyWin_DLLhModule, py3path, MAXPATHLEN)) {
189
wchar_t *p = wcsrchr(py3path, L'\\');
190
if (p) {
191
wcscpy(p + 1, PY3_DLLNAME);
192
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
193
if (hPython3 != NULL) {
194
return 1;
195
}
196
}
197
}
198
199
/* If we can locate python3.dll in our application dir,
200
use that DLL */
201
hPython3 = LoadLibraryExW(PY3_DLLNAME, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
202
if (hPython3 != NULL) {
203
return 1;
204
}
205
206
/* For back-compat, also search {sys.prefix}\DLLs, though
207
that has not been a normal install layout for a while */
208
PyInterpreterState *interp = _PyInterpreterState_GET();
209
PyConfig *config = (PyConfig*)_PyInterpreterState_GetConfig(interp);
210
assert(config->prefix);
211
if (config->prefix) {
212
wcscpy_s(py3path, MAXPATHLEN, config->prefix);
213
if (py3path[0] && _Py_add_relfile(py3path, L"DLLs\\" PY3_DLLNAME, MAXPATHLEN) >= 0) {
214
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
215
}
216
}
217
return hPython3 != NULL;
218
#undef MAXPATHLEN
219
}
220
#endif /* Py_ENABLE_SHARED */
221
222
dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
223
const char *shortname,
224
PyObject *pathname, FILE *fp)
225
{
226
dl_funcptr p;
227
char funcname[258], *import_python;
228
229
#ifdef Py_ENABLE_SHARED
230
_Py_CheckPython3();
231
#endif /* Py_ENABLE_SHARED */
232
233
wchar_t *wpathname = PyUnicode_AsWideCharString(pathname, NULL);
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
#ifdef MS_WINDOWS_DESKTOP
242
unsigned int old_mode;
243
244
/* Don't display a message box when Python can't load a DLL */
245
old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
246
#endif
247
248
/* bpo-36085: We use LoadLibraryEx with restricted search paths
249
to avoid DLL preloading attacks and enable use of the
250
AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to
251
ensure DLLs adjacent to the PYD are preferred. */
252
Py_BEGIN_ALLOW_THREADS
253
hDLL = LoadLibraryExW(wpathname, NULL,
254
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
255
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
256
Py_END_ALLOW_THREADS
257
PyMem_Free(wpathname);
258
259
#ifdef MS_WINDOWS_DESKTOP
260
/* restore old error mode settings */
261
SetErrorMode(old_mode);
262
#endif
263
264
if (hDLL==NULL){
265
PyObject *message;
266
unsigned int errorCode;
267
268
/* Get an error string from Win32 error code */
269
wchar_t theInfo[256]; /* Pointer to error text
270
from system */
271
int theLength; /* Length of error text */
272
273
errorCode = GetLastError();
274
275
theLength = FormatMessageW(
276
FORMAT_MESSAGE_FROM_SYSTEM |
277
FORMAT_MESSAGE_IGNORE_INSERTS, /* flags */
278
NULL, /* message source */
279
errorCode, /* the message (error) ID */
280
MAKELANGID(LANG_NEUTRAL,
281
SUBLANG_DEFAULT),
282
/* Default language */
283
theInfo, /* the buffer */
284
sizeof(theInfo) / sizeof(wchar_t), /* size in wchars */
285
NULL); /* no additional format args. */
286
287
/* Problem: could not get the error message.
288
This should not happen if called correctly. */
289
if (theLength == 0) {
290
message = PyUnicode_FromFormat(
291
"DLL load failed with error code %u while importing %s",
292
errorCode, shortname);
293
} else {
294
/* For some reason a \r\n
295
is appended to the text */
296
if (theLength >= 2 &&
297
theInfo[theLength-2] == '\r' &&
298
theInfo[theLength-1] == '\n') {
299
theLength -= 2;
300
theInfo[theLength] = '\0';
301
}
302
message = PyUnicode_FromFormat(
303
"DLL load failed while importing %s: ", shortname);
304
305
PyUnicode_AppendAndDel(&message,
306
PyUnicode_FromWideChar(
307
theInfo,
308
theLength));
309
}
310
if (message != NULL) {
311
PyObject *shortname_obj = PyUnicode_FromString(shortname);
312
PyErr_SetImportError(message, shortname_obj, pathname);
313
Py_XDECREF(shortname_obj);
314
Py_DECREF(message);
315
}
316
return NULL;
317
} else {
318
char buffer[256];
319
320
PyOS_snprintf(buffer, sizeof(buffer),
321
#ifdef _DEBUG
322
"python%d%d_d.dll",
323
#else
324
"python%d%d.dll",
325
#endif
326
PY_MAJOR_VERSION,PY_MINOR_VERSION);
327
import_python = GetPythonImport(hDLL);
328
329
if (import_python &&
330
_stricmp(buffer,import_python)) {
331
PyErr_Format(PyExc_ImportError,
332
"Module use of %.150s conflicts "
333
"with this version of Python.",
334
import_python);
335
Py_BEGIN_ALLOW_THREADS
336
FreeLibrary(hDLL);
337
Py_END_ALLOW_THREADS
338
return NULL;
339
}
340
}
341
Py_BEGIN_ALLOW_THREADS
342
p = GetProcAddress(hDLL, funcname);
343
Py_END_ALLOW_THREADS
344
}
345
346
return p;
347
}
348
349