From 601bcf82eccfbc15c1ff6cc735aafb2c9dab81a5 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 9 Jul 2024 16:50:58 -0700 Subject: [PATCH] Handle dlopen(NULL) failure in glibc fallback (#12716) Co-authored-by: Pradyun Gedam --- news/12716.bugfix.rst | 1 + src/pip/_internal/utils/glibc.py | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 news/12716.bugfix.rst 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 81342afa447..998868ff2a4 100644 --- a/src/pip/_internal/utils/glibc.py +++ b/src/pip/_internal/utils/glibc.py @@ -40,7 +40,20 @@ def glibc_version_string_ctypes() -> Optional[str]: # 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: @@ -50,7 +63,7 @@ def glibc_version_string_ctypes() -> Optional[str]: # 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")