Skip to content

Commit 21ea2b3

Browse files
committed
Fix Linux: dlopen libpython with RTLD_GLOBAL for extension modules
On Linux, Python extension modules (like _socket.so) need to find Python symbols like PyByteArray_Type. When embedding Python in a shared library (NIF), these symbols aren't visible by default. Fix by calling dlopen(libpython, RTLD_GLOBAL) before Py_Initialize to export all Python symbols globally.
1 parent 42d2f10 commit 21ea2b3

1 file changed

Lines changed: 22 additions & 0 deletions

File tree

c_src/py_nif.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
#include <math.h>
4242
#include <unistd.h>
4343
#include <pthread.h>
44+
#ifdef __linux__
45+
#include <dlfcn.h>
46+
#endif
4447

4548
/* ============================================================================
4649
* Timeout support
@@ -462,6 +465,25 @@ static ERL_NIF_TERM nif_py_init(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg
462465
return ATOM_OK;
463466
}
464467

468+
#ifdef __linux__
469+
/* On Linux, we need to load libpython with RTLD_GLOBAL so that Python
470+
* extension modules can find Python symbols when dynamically loaded.
471+
* Without this, modules like _socket.so fail with "undefined symbol: PyByteArray_Type" */
472+
{
473+
char libpython[256];
474+
snprintf(libpython, sizeof(libpython), "libpython%d.%d.so.1.0",
475+
PY_MAJOR_VERSION, PY_MINOR_VERSION);
476+
void *handle = dlopen(libpython, RTLD_NOW | RTLD_GLOBAL);
477+
if (!handle) {
478+
/* Try without .1.0 suffix */
479+
snprintf(libpython, sizeof(libpython), "libpython%d.%d.so",
480+
PY_MAJOR_VERSION, PY_MINOR_VERSION);
481+
handle = dlopen(libpython, RTLD_NOW | RTLD_GLOBAL);
482+
}
483+
/* It's OK if this fails - the symbols might already be global */
484+
}
485+
#endif
486+
465487
/* Initialize Python with thread support */
466488
PyConfig config;
467489
PyConfig_InitPythonConfig(&config);

0 commit comments

Comments
 (0)