From 2f13f99e69c5f985bd255912f115a2afea573d4f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 20 May 2024 15:10:36 -0400 Subject: [PATCH] Handle dlopen(NULL) failure in glibc fallback --- src/pip/_internal/utils/glibc.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/utils/glibc.py b/src/pip/_internal/utils/glibc.py index 81342afa447..e89dda7ebf4 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 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")