cpython/Python/dynload_shlib.c

134 lines
3.5 KiB
C
Raw Normal View History

/* Support for dynamic loading of extension modules */
#include "Python.h"
#include "pycore_interp.h" // _PyInterpreterState.dlopenflags
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "importdl.h"
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__NetBSD__)
#include <sys/param.h>
#if (NetBSD < 199712)
#include <nlist.h>
#include <link.h>
#define dlerror() "error in dynamic linking"
#endif
#endif /* NetBSD */
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#if (defined(__OpenBSD__) || defined(__NetBSD__)) && !defined(__ELF__)
#define LEAD_UNDERSCORE "_"
#else
#define LEAD_UNDERSCORE ""
#endif
/* The .so extension module ABI tag, supplied by the Makefile via
Makefile.pre.in and configure. This is used to discriminate between
incompatible .so files so that extensions for different Python builds can
live in the same directory. E.g. foomodule.cpython-32.so
*/
const char *_PyImport_DynLoadFiletab[] = {
#ifdef __CYGWIN__
".dll",
#else /* !__CYGWIN__ */
"." SOABI ".so",
#ifdef ALT_SOABI
"." ALT_SOABI ".so",
#endif
".abi" PYTHON_ABI_STRING ".so",
".so",
#endif /* __CYGWIN__ */
NULL,
};
static struct {
dev_t dev;
ino_t ino;
void *handle;
} handles[128];
static int nhandles = 0;
dl_funcptr
_PyImport_FindSharedFuncptr(const char *prefix,
const char *shortname,
const char *pathname, FILE *fp)
{
dl_funcptr p;
void *handle;
char funcname[258];
char pathbuf[260];
int dlopenflags=0;
if (strchr(pathname, '/') == NULL) {
/* Prefix bare filename with "./" */
PyOS_snprintf(pathbuf, sizeof(pathbuf), "./%-.255s", pathname);
pathname = pathbuf;
}
PyOS_snprintf(funcname, sizeof(funcname),
LEAD_UNDERSCORE "%.20s_%.200s", prefix, shortname);
if (fp != NULL) {
int i;
struct _Py_stat_struct status;
if (_Py_fstat(fileno(fp), &status) == -1)
2013-07-22 22:08:09 -07:00
return NULL;
for (i = 0; i < nhandles; i++) {
if (status.st_dev == handles[i].dev &&
status.st_ino == handles[i].ino) {
p = (dl_funcptr) dlsym(handles[i].handle,
funcname);
return p;
}
}
if (nhandles < 128) {
handles[nhandles].dev = status.st_dev;
handles[nhandles].ino = status.st_ino;
}
}
dlopenflags = _PyInterpreterState_GET()->dlopenflags;
handle = dlopen(pathname, dlopenflags);
if (handle == NULL) {
PyObject *mod_name;
PyObject *path;
PyObject *error_ob;
const char *error = dlerror();
if (error == NULL)
error = "unknown dlopen() error";
bpo-41894: Fix UnicodeDecodeError while loading native module (GH-22466) When running in a non-UTF-8 locale, if an error occurs while importing a native Python module (say because a dependent share library is missing), the error message string returned may contain non-ASCII code points causing a UnicodeDecodeError. PyUnicode_DecodeFSDefault is used for buffers which may contain filesystem paths. For consistency with os.strerror(), PyUnicode_DecodeLocale is used for buffers which contain system error messages. While the shortname parameter is always encoded in ASCII according to PEP 489, it is left decoded using PyUnicode_FromString to minimize the changes and since it should not affect the decoding (albeit _potentially_ slower). In dynload_hpux, since the error buffer contains a message generated from a static ASCII string and the module filesystem path, PyUnicode_DecodeFSDefault is used instead of PyUnicode_DecodeLocale as is used elsewhere. * bpo-41894: Fix bugs in dynload error msg handling For both dynload_aix and dynload_hpux, properly handle the possibility that decoding strings may return NULL and when such an error happens, properly decrement any previously decoded strings and return early. In addition, in dynload_aix, ensure that we pass the decoded string *object* pathname_ob to PyErr_SetImportError instead of the original pathname buffer. Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> (cherry picked from commit 2d2af320d94afc6561e8f8adf174c9d3fd9065bc) Co-authored-by: Kevin Adler <kadler@us.ibm.com>
2020-10-14 19:25:45 -07:00
error_ob = PyUnicode_DecodeLocale(error, "surrogateescape");
if (error_ob == NULL)
return NULL;
mod_name = PyUnicode_FromString(shortname);
if (mod_name == NULL) {
Py_DECREF(error_ob);
return NULL;
}
bpo-41894: Fix UnicodeDecodeError while loading native module (GH-22466) When running in a non-UTF-8 locale, if an error occurs while importing a native Python module (say because a dependent share library is missing), the error message string returned may contain non-ASCII code points causing a UnicodeDecodeError. PyUnicode_DecodeFSDefault is used for buffers which may contain filesystem paths. For consistency with os.strerror(), PyUnicode_DecodeLocale is used for buffers which contain system error messages. While the shortname parameter is always encoded in ASCII according to PEP 489, it is left decoded using PyUnicode_FromString to minimize the changes and since it should not affect the decoding (albeit _potentially_ slower). In dynload_hpux, since the error buffer contains a message generated from a static ASCII string and the module filesystem path, PyUnicode_DecodeFSDefault is used instead of PyUnicode_DecodeLocale as is used elsewhere. * bpo-41894: Fix bugs in dynload error msg handling For both dynload_aix and dynload_hpux, properly handle the possibility that decoding strings may return NULL and when such an error happens, properly decrement any previously decoded strings and return early. In addition, in dynload_aix, ensure that we pass the decoded string *object* pathname_ob to PyErr_SetImportError instead of the original pathname buffer. Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> (cherry picked from commit 2d2af320d94afc6561e8f8adf174c9d3fd9065bc) Co-authored-by: Kevin Adler <kadler@us.ibm.com>
2020-10-14 19:25:45 -07:00
path = PyUnicode_DecodeFSDefault(pathname);
if (path == NULL) {
Py_DECREF(error_ob);
Py_DECREF(mod_name);
return NULL;
}
PyErr_SetImportError(error_ob, mod_name, path);
Py_DECREF(error_ob);
Py_DECREF(mod_name);
Py_DECREF(path);
return NULL;
}
if (fp != NULL && nhandles < 128)
handles[nhandles++].handle = handle;
p = (dl_funcptr) dlsym(handle, funcname);
return p;
}