Skip to main content

Python/dynload_shlib.c

Source:

cpython 3.14 @ ab2d84fe1023/Python/dynload_shlib.c

dynload_shlib.c implements dynamic loading of C extension modules (.so/.dylib) on POSIX systems using dlopen. Windows has a separate dynload_win.c.

Map

LinesSymbolRole
1-50_Py_INIT_FUNC_PREFIX"PyInit_" — prefix for the module init function
51-130_PyImport_FindSharedFuncptrdlopen the .so, locate PyInit_<name>
131-200_PyImport_LoadDynamicModuleWithSpecCall init function, wrap result in ModuleSpec
201-260RTLD flags`RTLD_NOW

Reading

_PyImport_FindSharedFuncptr

// CPython: Python/dynload_shlib.c:75 _PyImport_FindSharedFuncptr
dl_funcptr
_PyImport_FindSharedFuncptr(const char *prefix, const char *shortname,
const char *pathname, FILE *fp)
{
void *handle;
char funcname[258];
/* Construct: "PyInit_" + shortname */
PyOS_snprintf(funcname, sizeof(funcname), "%s%s", prefix, shortname);
/* dlopen the shared library */
handle = dlopen(pathname, dlopenflags);
if (handle == NULL) {
const char *error = dlerror();
PyErr_Format(PyExc_ImportError,
"dlopen(%s): %s", pathname, error);
return NULL;
}
/* Locate the init function */
dl_funcptr p = (dl_funcptr)dlsym(handle, funcname);
return p;
}

Init function calling convention

// CPython: Python/dynload_shlib.c:165 _PyImport_LoadDynamicModuleWithSpec
PyObject *
_PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp)
{
/* Build pathname from spec.origin */
const char *pathname = PyUnicode_AsUTF8(spec_origin);
/* Find the init function */
PyModInitFunction p0 = (PyModInitFunction)
_PyImport_FindSharedFuncptr(INIT_FUNCNAME_PREFIX, name_utf8, pathname, fp);
if (p0 == NULL) {
if (!PyErr_Occurred()) {
PyErr_Format(PyExc_ImportError,
"dynamic module does not define module export function"
" (%s)", funcname);
}
return NULL;
}
/* Call PyInit_<name>() — returns PyModuleDef * or PyObject * */
PyObject *mod = p0();
return mod;
}

The init function must be named exactly PyInit_<modulename>. It returns either a PyModuleDef * (multi-phase init, 3.5+) or a fully-initialized PyObject * (single-phase).

RTLD flags

// CPython: Python/dynload_shlib.c:48 dlopenflags
/* Default: RTLD_NOW (resolve symbols immediately) | RTLD_GLOBAL (symbols visible to later loads) */
/* sys.setdlopenflags() lets the user override, e.g. RTLD_LOCAL for isolation */
int dlopenflags = sys_flags.dl_open_flags;

RTLD_GLOBAL is needed when extension A exports symbols used by extension B. RTLD_LOCAL is safer but may cause symbol conflicts.

gopy notes

In gopy there are no dlopen extension modules; all modules are compiled into the Go binary. dynload_shlib.c documents the CPython mechanism for context. The PyInit_ convention is relevant to gopy's FFI layer, where C extension modules can be wrapped by implementing PyInit_<name> in a Go-generated shim that calls back into Go.