diff --git a/news/12716.bugfix.rst b/news/12716.bugfix.rst new file mode 100644 index 00000000000..7af794f7bb3 --- /dev/null +++ b/news/12716.bugfix.rst @@ -0,0 +1 @@ +Avoid dlopen failure for glibc detection in musl builds diff --git a/src/pip/_internal/utils/glibc.py b/src/pip/_internal/utils/glibc.py index 36104244138..ebb53d6fe0c 100644 --- a/src/pip/_internal/utils/glibc.py +++ b/src/pip/_internal/utils/glibc.py @@ -49,7 +49,20 @@ def glibc_version_string_ctypes(): # manpage says, "If filename is NULL, then the returned handle is for the # main program". This way we can let the linker do the work to figure out # which libc our process is actually using. - process_namespace = ctypes.CDLL(None) + # + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can't proceed, so we bail on our attempt. + try: + process_namespace = ctypes.CDLL(None) + except OSError: + return None + try: gnu_get_libc_version = process_namespace.gnu_get_libc_version except AttributeError: @@ -59,7 +72,7 @@ def glibc_version_string_ctypes(): # Call gnu_get_libc_version, which returns a string like "2.5" gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() + version_str: str = gnu_get_libc_version() # py2 / py3 compatibility: if not isinstance(version_str, str): version_str = version_str.decode("ascii")