From e02d5c092f38ab4e0ff284b111162f14169a351e Mon Sep 17 00:00:00 2001 From: Arkadiusz Grzelka Date: Fri, 23 Oct 2026 08:57:32 +0200 Subject: [PATCH] Read the J-Link version from the library's real file name SEGGER's Linux package links /opt/SEGGER/JLink to JLink_V950/. When the library is loaded through the link (LD_LIBRARY_PATH=/opt/SEGGER/JLink or an explicit dllpath), its path has no JLink_V part, so dll_version() returns None and load() copies the 29 MB library into /tmp on every Library(). On Linux, unload() never dlcloses the copy, so deleted copies stay mapped and a process creating a JLink per probe scan fills /tmp. dll_version() now falls back to the file the path resolves to, libjlinkarm.so.9.50.0, and reads the version from its name. Signed-off-by: Arkadiusz Grzelka --- pylink/library.py | 4 ++++ tests/unit/test_library.py | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/pylink/library.py b/pylink/library.py index de1c1bc..6b4f27e 100644 --- a/pylink/library.py +++ b/pylink/library.py @@ -521,6 +521,10 @@ def dll_version(self): if match: num_part = match.group(1) return float(f"{num_part[0]}.{num_part[1:]}") + + match = re.search(r"libjlinkarm(?:_x86)?\.so\.(\d+)\.(\d+)", os.path.realpath(self._path)) + if match: + return float(f"{match.group(1)}.{match.group(2)}") return None diff --git a/tests/unit/test_library.py b/tests/unit/test_library.py index 81c0f37..26764df 100644 --- a/tests/unit/test_library.py +++ b/tests/unit/test_library.py @@ -94,6 +94,7 @@ def join(*args): return s mock_os.path.join.side_effect = join + mock_os.path.realpath.side_effect = lambda f: f def listdir(f): """List the files and directories within the directory.""" @@ -1137,6 +1138,30 @@ def test_dll_version_mac(self, _mock_load_library): lib = library.Library(path) self.assertEqual(lib.dll_version(), 7.96) + @mock.patch('os.path.realpath', new=lambda f: f) + @mock.patch('ctypes.cdll.LoadLibrary') + def test_dll_version_linux_versioned_file_name(self, _mock_load_library): + path = "/opt/SEGGER/JLink/libjlinkarm.so.9.50.0" + lib = library.Library(path) + self.assertEqual(lib.dll_version(), 9.50) + + @mock.patch('sys.platform', new='linux') + @mock.patch('os.path.realpath') + @mock.patch('pylink.library.open') + @mock.patch('tempfile.NamedTemporaryFile') + @mock.patch('ctypes.cdll.LoadLibrary') + def test_dll_version_linux_versioned_file_name_symlink(self, mock_load_library, mock_temp_file, + _mock_open, mock_realpath): + path = "/opt/SEGGER/JLink/libjlinkarm.so.9" + real = "/opt/SEGGER/JLink/libjlinkarm.so.9.50.0" + mock_realpath.side_effect = lambda f: real if f == path else f + + lib = library.Library(path) + + self.assertEqual(lib.dll_version(), 9.50) + mock_temp_file.assert_not_called() + mock_load_library.assert_called_once_with(path) + if __name__ == '__main__': unittest.main()