1
1
# Copyright 2025 NVIDIA Corporation. All rights reserved.
2
- #
3
2
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
4
3
5
4
import ctypes
6
5
import ctypes .util
7
6
import os
8
- from typing import Optional , Tuple
7
+ from typing import Optional
9
8
10
9
from .load_dl_common import LoadedDL
11
10
12
- _LINUX_CDLL_MODE = os .RTLD_NOW | os .RTLD_GLOBAL
11
+ CDLL_MODE = os .RTLD_NOW | os .RTLD_GLOBAL
13
12
14
- _LIBDL_PATH = ctypes .util .find_library ("dl" ) or "libdl.so.2"
15
- _LIBDL = ctypes .CDLL (_LIBDL_PATH )
16
- _LIBDL .dladdr .argtypes = [ctypes .c_void_p , ctypes .c_void_p ]
17
- _LIBDL .dladdr .restype = ctypes .c_int
13
+ LIBDL_PATH = ctypes .util .find_library ("dl" ) or "libdl.so.2"
14
+ LIBDL = ctypes .CDLL (LIBDL_PATH )
15
+ LIBDL .dladdr .argtypes = [ctypes .c_void_p , ctypes .c_void_p ]
16
+ LIBDL .dladdr .restype = ctypes .c_int
18
17
19
18
20
19
class Dl_info (ctypes .Structure ):
20
+ """Structure used by dladdr to return information about a loaded symbol."""
21
+
21
22
_fields_ = [
22
23
("dli_fname" , ctypes .c_char_p ), # path to .so
23
24
("dli_fbase" , ctypes .c_void_p ),
@@ -27,50 +28,98 @@ class Dl_info(ctypes.Structure):
27
28
28
29
29
30
def abs_path_for_dynamic_library (libname : str , handle : int ) -> Optional [str ]:
30
- """Get the absolute path of a loaded dynamic library on Linux."""
31
+ """Get the absolute path of a loaded dynamic library on Linux.
32
+
33
+ Args:
34
+ libname: The name of the library
35
+ handle: The library handle
36
+
37
+ Returns:
38
+ The absolute path to the library file, or None if no expected symbol is found
39
+
40
+ Raises:
41
+ OSError: If dladdr fails to get information about the symbol
42
+ """
31
43
from .supported_libs import EXPECTED_LIB_SYMBOLS
32
-
44
+
33
45
for symbol_name in EXPECTED_LIB_SYMBOLS [libname ]:
34
46
symbol = getattr (handle , symbol_name , None )
35
47
if symbol is not None :
36
48
break
37
49
else :
38
50
return None
39
-
51
+
40
52
addr = ctypes .cast (symbol , ctypes .c_void_p )
41
53
info = Dl_info ()
42
- if _LIBDL .dladdr (addr , ctypes .byref (info )) == 0 :
54
+ if LIBDL .dladdr (addr , ctypes .byref (info )) == 0 :
43
55
raise OSError (f"dladdr failed for { libname = !r} " )
44
56
return info .dli_fname .decode ()
45
57
46
58
47
- def load_and_report_path (libname : str , soname : str ) -> Tuple [int , str ]:
48
- """Load a dynamic library and return its handle and absolute path."""
49
- handle = ctypes .CDLL (soname , _LINUX_CDLL_MODE )
50
- abs_path = abs_path_for_dynamic_library (libname , handle )
51
- if abs_path is None :
52
- raise RuntimeError (f"No expected symbol for { libname = !r} " )
53
- return handle ._handle , abs_path
59
+ def check_if_already_loaded (libname : str ) -> Optional [LoadedDL ]:
60
+ """Check if the library is already loaded in the process.
54
61
62
+ Args:
63
+ libname: The name of the library to check
55
64
56
- def load_dynamic_library (libname : str , found_path : str ) -> LoadedDL :
57
- """Load a dynamic library from the given path."""
58
- try :
59
- handle = ctypes .CDLL (found_path , _LINUX_CDLL_MODE )
60
- except OSError as e :
61
- raise RuntimeError (f"Failed to dlopen { found_path } : { e } " ) from e
62
- return LoadedDL (handle ._handle , found_path , False )
63
-
65
+ Returns:
66
+ A LoadedDL object if the library is already loaded, None otherwise
64
67
65
- def check_if_already_loaded (libname : str ) -> Optional [LoadedDL ]:
66
- """Check if the library is already loaded in the process."""
68
+ Example:
69
+ >>> loaded = check_if_already_loaded("cudart")
70
+ >>> if loaded is not None:
71
+ ... print(f"Library already loaded from {loaded.abs_path}")
72
+ """
67
73
from .supported_libs import SUPPORTED_LINUX_SONAMES
68
-
74
+
69
75
for soname in SUPPORTED_LINUX_SONAMES .get (libname , ()):
70
76
try :
71
77
handle = ctypes .CDLL (soname , mode = os .RTLD_NOLOAD )
72
78
except OSError :
73
79
continue
74
80
else :
75
81
return LoadedDL (handle ._handle , abs_path_for_dynamic_library (libname , handle ), True )
76
- return None
82
+ return None
83
+
84
+
85
+ def load_with_system_search (libname : str , soname : str ) -> Optional [LoadedDL ]:
86
+ """Try to load a library using system search paths.
87
+
88
+ Args:
89
+ libname: The name of the library to load
90
+ soname: The soname to search for
91
+
92
+ Returns:
93
+ A LoadedDL object if successful, None if the library cannot be loaded
94
+
95
+ Raises:
96
+ RuntimeError: If the library is loaded but no expected symbol is found
97
+ """
98
+ try :
99
+ handle = ctypes .CDLL (soname , CDLL_MODE )
100
+ abs_path = abs_path_for_dynamic_library (libname , handle )
101
+ if abs_path is None :
102
+ raise RuntimeError (f"No expected symbol for { libname = !r} " )
103
+ return LoadedDL (handle ._handle , abs_path , False )
104
+ except OSError :
105
+ return None
106
+
107
+
108
+ def load_with_abs_path (libname : str , found_path : str ) -> LoadedDL :
109
+ """Load a dynamic library from the given path.
110
+
111
+ Args:
112
+ libname: The name of the library to load
113
+ found_path: The absolute path to the library file
114
+
115
+ Returns:
116
+ A LoadedDL object representing the loaded library
117
+
118
+ Raises:
119
+ RuntimeError: If the library cannot be loaded
120
+ """
121
+ try :
122
+ handle = ctypes .CDLL (found_path , CDLL_MODE )
123
+ except OSError as e :
124
+ raise RuntimeError (f"Failed to dlopen { found_path } : { e } " ) from e
125
+ return LoadedDL (handle ._handle , found_path , False )
0 commit comments