xref: /third_party/python/Python/importdl.c (revision 7db96d56)
1
2/* Support for dynamic loading of extension modules */
3
4#include "Python.h"
5#include "pycore_call.h"
6#include "pycore_pystate.h"
7#include "pycore_runtime.h"
8
9/* ./configure sets HAVE_DYNAMIC_LOADING if dynamic loading of modules is
10   supported on this platform. configure will then compile and link in one
11   of the dynload_*.c files, as appropriate. We will call a function in
12   those modules to get a function pointer to the module's init function.
13*/
14#ifdef HAVE_DYNAMIC_LOADING
15
16#include "importdl.h"
17
18#ifdef MS_WINDOWS
19extern dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
20                                                     const char *shortname,
21                                                     PyObject *pathname,
22                                                     FILE *fp);
23#else
24extern dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix,
25                                              const char *shortname,
26                                              const char *pathname, FILE *fp);
27#endif
28
29static const char * const ascii_only_prefix = "PyInit";
30static const char * const nonascii_prefix = "PyInitU";
31
32/* Get the variable part of a module's export symbol name.
33 * Returns a bytes instance. For non-ASCII-named modules, the name is
34 * encoded as per PEP 489.
35 * The hook_prefix pointer is set to either ascii_only_prefix or
36 * nonascii_prefix, as appropriate.
37 */
38static PyObject *
39get_encoded_name(PyObject *name, const char **hook_prefix) {
40    PyObject *tmp;
41    PyObject *encoded = NULL;
42    PyObject *modname = NULL;
43    Py_ssize_t name_len, lastdot;
44
45    /* Get the short name (substring after last dot) */
46    name_len = PyUnicode_GetLength(name);
47    if (name_len < 0) {
48        return NULL;
49    }
50    lastdot = PyUnicode_FindChar(name, '.', 0, name_len, -1);
51    if (lastdot < -1) {
52        return NULL;
53    } else if (lastdot >= 0) {
54        tmp = PyUnicode_Substring(name, lastdot + 1, name_len);
55        if (tmp == NULL)
56            return NULL;
57        name = tmp;
58        /* "name" now holds a new reference to the substring */
59    } else {
60        Py_INCREF(name);
61    }
62
63    /* Encode to ASCII or Punycode, as needed */
64    encoded = PyUnicode_AsEncodedString(name, "ascii", NULL);
65    if (encoded != NULL) {
66        *hook_prefix = ascii_only_prefix;
67    } else {
68        if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
69            PyErr_Clear();
70            encoded = PyUnicode_AsEncodedString(name, "punycode", NULL);
71            if (encoded == NULL) {
72                goto error;
73            }
74            *hook_prefix = nonascii_prefix;
75        } else {
76            goto error;
77        }
78    }
79
80    /* Replace '-' by '_' */
81    modname = _PyObject_CallMethod(encoded, &_Py_ID(replace), "cc", '-', '_');
82    if (modname == NULL)
83        goto error;
84
85    Py_DECREF(name);
86    Py_DECREF(encoded);
87    return modname;
88error:
89    Py_DECREF(name);
90    Py_XDECREF(encoded);
91    return NULL;
92}
93
94PyObject *
95_PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp)
96{
97#ifndef MS_WINDOWS
98    PyObject *pathbytes = NULL;
99#endif
100    PyObject *name_unicode = NULL, *name = NULL, *path = NULL, *m = NULL;
101    const char *name_buf, *hook_prefix;
102    const char *oldcontext;
103    dl_funcptr exportfunc;
104    PyModuleDef *def;
105    PyModInitFunction p0;
106
107    name_unicode = PyObject_GetAttrString(spec, "name");
108    if (name_unicode == NULL) {
109        return NULL;
110    }
111    if (!PyUnicode_Check(name_unicode)) {
112        PyErr_SetString(PyExc_TypeError,
113                        "spec.name must be a string");
114        goto error;
115    }
116
117    name = get_encoded_name(name_unicode, &hook_prefix);
118    if (name == NULL) {
119        goto error;
120    }
121    name_buf = PyBytes_AS_STRING(name);
122
123    path = PyObject_GetAttrString(spec, "origin");
124    if (path == NULL)
125        goto error;
126
127    if (PySys_Audit("import", "OOOOO", name_unicode, path,
128                    Py_None, Py_None, Py_None) < 0) {
129        goto error;
130    }
131
132#ifdef MS_WINDOWS
133    exportfunc = _PyImport_FindSharedFuncptrWindows(hook_prefix, name_buf,
134                                                    path, fp);
135#else
136    pathbytes = PyUnicode_EncodeFSDefault(path);
137    if (pathbytes == NULL)
138        goto error;
139    exportfunc = _PyImport_FindSharedFuncptr(hook_prefix, name_buf,
140                                             PyBytes_AS_STRING(pathbytes),
141                                             fp);
142    Py_DECREF(pathbytes);
143#endif
144
145    if (exportfunc == NULL) {
146        if (!PyErr_Occurred()) {
147            PyObject *msg;
148            msg = PyUnicode_FromFormat(
149                "dynamic module does not define "
150                "module export function (%s_%s)",
151                hook_prefix, name_buf);
152            if (msg == NULL)
153                goto error;
154            PyErr_SetImportError(msg, name_unicode, path);
155            Py_DECREF(msg);
156        }
157        goto error;
158    }
159
160    p0 = (PyModInitFunction)exportfunc;
161
162    /* Package context is needed for single-phase init */
163    oldcontext = _Py_PackageContext;
164    _Py_PackageContext = PyUnicode_AsUTF8(name_unicode);
165    if (_Py_PackageContext == NULL) {
166        _Py_PackageContext = oldcontext;
167        goto error;
168    }
169    m = _PyImport_InitFunc_TrampolineCall(p0);
170    _Py_PackageContext = oldcontext;
171
172    if (m == NULL) {
173        if (!PyErr_Occurred()) {
174            PyErr_Format(
175                PyExc_SystemError,
176                "initialization of %s failed without raising an exception",
177                name_buf);
178        }
179        goto error;
180    } else if (PyErr_Occurred()) {
181        PyErr_Clear();
182        PyErr_Format(
183            PyExc_SystemError,
184            "initialization of %s raised unreported exception",
185            name_buf);
186        m = NULL;
187        goto error;
188    }
189    if (Py_IS_TYPE(m, NULL)) {
190        /* This can happen when a PyModuleDef is returned without calling
191         * PyModuleDef_Init on it
192         */
193        PyErr_Format(PyExc_SystemError,
194                     "init function of %s returned uninitialized object",
195                     name_buf);
196        m = NULL; /* prevent segfault in DECREF */
197        goto error;
198    }
199    if (PyObject_TypeCheck(m, &PyModuleDef_Type)) {
200        Py_DECREF(name_unicode);
201        Py_DECREF(name);
202        Py_DECREF(path);
203        return PyModule_FromDefAndSpec((PyModuleDef*)m, spec);
204    }
205
206    /* Fall back to single-phase init mechanism */
207
208    if (hook_prefix == nonascii_prefix) {
209        /* don't allow legacy init for non-ASCII module names */
210        PyErr_Format(
211            PyExc_SystemError,
212            "initialization of %s did not return PyModuleDef",
213            name_buf);
214        goto error;
215    }
216
217    /* Remember pointer to module init function. */
218    def = PyModule_GetDef(m);
219    if (def == NULL) {
220        PyErr_Format(PyExc_SystemError,
221                     "initialization of %s did not return an extension "
222                     "module", name_buf);
223        goto error;
224    }
225    def->m_base.m_init = p0;
226
227    /* Remember the filename as the __file__ attribute */
228    if (PyModule_AddObjectRef(m, "__file__", path) < 0) {
229        PyErr_Clear(); /* Not important enough to report */
230    }
231
232    PyObject *modules = PyImport_GetModuleDict();
233    if (_PyImport_FixupExtensionObject(m, name_unicode, path, modules) < 0)
234        goto error;
235
236    Py_DECREF(name_unicode);
237    Py_DECREF(name);
238    Py_DECREF(path);
239
240    return m;
241
242error:
243    Py_DECREF(name_unicode);
244    Py_XDECREF(name);
245    Py_XDECREF(path);
246    Py_XDECREF(m);
247    return NULL;
248}
249
250#endif /* HAVE_DYNAMIC_LOADING */
251