Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

import json
import sys

from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL
from cuda.pathfinder._utils.platform_aware import IS_WINDOWS

if IS_WINDOWS:
from cuda.pathfinder._dynamic_libs.load_dl_windows import load_with_system_search
else:
from cuda.pathfinder._dynamic_libs.load_dl_linux import load_with_system_search


def _probe_canary_abs_path(libname: str) -> str | None:
loaded: LoadedDL | None = load_with_system_search(libname)
if loaded is None:
return None
abs_path = loaded.abs_path
if not isinstance(abs_path, str):
return None
return abs_path


def main(argv: list[str] | None = None) -> int:
args = sys.argv[1:] if argv is None else argv
if len(args) != 1:
return 2
print(json.dumps(_probe_canary_abs_path(args[0]))) # noqa: T201
return 0


if __name__ == "__main__":
raise SystemExit(main())
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def _find_lib_dir_using_anchor_point(libname: str, anchor_point: str, linux_lib_
for rel_path in rel_paths:
for dirname in sorted(glob.glob(os.path.join(anchor_point, rel_path))):
if os.path.isdir(dirname):
return dirname
return os.path.normpath(dirname)

return None

Expand Down Expand Up @@ -152,6 +152,57 @@ def _find_dll_using_lib_dir(
return None


def _derive_ctk_root_linux(resolved_lib_path: str) -> str | None:
"""Derive the CTK installation root from a resolved library path on Linux.

Standard system CTK layout: ``$CTK_ROOT/lib64/libfoo.so.XX``
(some installs use ``lib`` instead of ``lib64``).

Returns None if the path doesn't match a recognized layout.
"""
lib_dir = os.path.dirname(resolved_lib_path)
basename = os.path.basename(lib_dir)
if basename in ("lib64", "lib"):
return os.path.dirname(lib_dir)
return None


def _derive_ctk_root_windows(resolved_lib_path: str) -> str | None:
"""Derive the CTK installation root from a resolved library path on Windows.

Handles two CTK layouts:
- CTK 13: ``$CTK_ROOT/bin/x64/foo.dll``
- CTK 12: ``$CTK_ROOT/bin/foo.dll``

Returns None if the path doesn't match a recognized layout.

Uses ``ntpath`` explicitly so the function is testable on any platform.
"""
import ntpath

lib_dir = ntpath.dirname(resolved_lib_path)
basename = ntpath.basename(lib_dir).lower()
if basename == "x64":
parent = ntpath.dirname(lib_dir)
if ntpath.basename(parent).lower() == "bin":
return ntpath.dirname(parent)
elif basename == "bin":
return ntpath.dirname(lib_dir)
return None


def derive_ctk_root(resolved_lib_path: str) -> str | None:
"""Derive the CTK installation root from a resolved library path.

Given the absolute path of a loaded CTK shared library, walk up the
directory tree to find the CTK root. Returns None if the path doesn't
match any recognized CTK directory layout.
"""
if IS_WINDOWS:
return _derive_ctk_root_windows(resolved_lib_path)
return _derive_ctk_root_linux(resolved_lib_path)


class _FindNvidiaDynamicLib:
def __init__(self, libname: str):
self.libname = libname
Expand Down Expand Up @@ -185,6 +236,16 @@ def try_with_conda_prefix(self) -> str | None:
def try_with_cuda_home(self) -> str | None:
return self._find_using_lib_dir(_find_lib_dir_using_cuda_home(self.libname))

def try_via_ctk_root(self, ctk_root: str) -> str | None:
"""Find the library under a derived CTK root directory.

Uses :func:`_find_lib_dir_using_anchor_point` which already knows
about non-standard sub-paths (e.g. ``nvvm/lib64`` for nvvm).
"""
return self._find_using_lib_dir(
_find_lib_dir_using_anchor_point(self.libname, anchor_point=ctk_root, linux_lib_dir="lib64")
)

def _find_using_lib_dir(self, lib_dir: str | None) -> str | None:
if lib_dir is None:
return None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@
# SPDX-License-Identifier: Apache-2.0

import functools
import json
import struct
import subprocess
import sys

from cuda.pathfinder._dynamic_libs.find_nvidia_dynamic_lib import _FindNvidiaDynamicLib
from cuda.pathfinder._dynamic_libs.find_nvidia_dynamic_lib import (
_FindNvidiaDynamicLib,
derive_ctk_root,
)
from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL, load_dependencies
from cuda.pathfinder._utils.platform_aware import IS_WINDOWS

Expand All @@ -22,6 +27,73 @@
load_with_system_search,
)

# Libs that reside on the standard linker path in system CTK installs.
# Used to discover the CTK root when a lib with a non-standard path
# (e.g. nvvm under $CTK_ROOT/nvvm/lib64) can't be found directly.
_CTK_ROOT_CANARY_LIBNAMES = ("cudart",)


def _resolve_system_loaded_abs_path_in_subprocess(libname: str) -> str | None:
"""Resolve a library's system-search absolute path in a child process.

This keeps any side-effects of loading the canary library scoped to the
child process instead of polluting the current process.
"""
cmd = [
sys.executable,
"-m",
"cuda.pathfinder._dynamic_libs.canary_probe_subprocess",
libname,
]
try:
result = subprocess.run( # noqa: S603
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While working on the initial version of pathfinder, using subprocess was frowned upon, for general security reasons I think, so I implemented cuda_pathfinder/tests/spawned_process_runner.py. I was hoping you'd use that here.

The rest looks good to me, although I still need to spend some time carefully looking at the tests.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spawned_process_runner.py came from here (from before we had Cursor):

https://chatgpt.com/share/681914ce-f274-8008-9e9f-4538716b4ed7

There I started with:

I need to run Python code in a separate process, but I cannot use suprocess.run because that uses fork, which is incompatible with my requirements.

So I think spawned_process_runner.py will give us better isolation (and predictability), it's not just the secops concern.

cmd,
check=False,
capture_output=True,
text=True,
timeout=10.0,
)
except (OSError, subprocess.SubprocessError):
return None
if result.returncode != 0:
return None

# Read the final non-empty stdout line in case earlier lines are emitted.
lines = [line for line in result.stdout.splitlines() if line.strip()]
if not lines:
return None
try:
payload = json.loads(lines[-1])
except json.JSONDecodeError:
return None
if isinstance(payload, str):
return payload
return None


def _try_ctk_root_canary(finder: _FindNvidiaDynamicLib) -> str | None:
"""Derive the CTK root from a system-installed canary lib.

For libs like nvvm whose shared object doesn't reside on the standard
linker path, we locate a well-known CTK lib that IS on the linker path
via system search, derive the CTK installation root from its resolved
path, and then look for the target lib relative to that root.

The canary load is performed in a subprocess to avoid introducing loader
state into the current process.
"""
for canary_libname in _CTK_ROOT_CANARY_LIBNAMES:
canary_abs_path = _resolve_system_loaded_abs_path_in_subprocess(canary_libname)
if canary_abs_path is None:
continue
ctk_root = derive_ctk_root(canary_abs_path)
if ctk_root is None:
continue
abs_path: str | None = finder.try_via_ctk_root(ctk_root)
if abs_path is not None:
return abs_path
return None


def _load_lib_no_cache(libname: str) -> LoadedDL:
finder = _FindNvidiaDynamicLib(libname)
Expand Down Expand Up @@ -50,11 +122,21 @@ def _load_lib_no_cache(libname: str) -> LoadedDL:
loaded = load_with_system_search(libname)
if loaded is not None:
return loaded

abs_path = finder.try_with_cuda_home()
if abs_path is None:
finder.raise_not_found_error()
else:
if abs_path is not None:
found_via = "CUDA_HOME"
else:
# Canary probe: if the direct system search and CUDA_HOME both
# failed (e.g. nvvm isn't on the linker path and CUDA_HOME is
# unset), try to discover the CTK root by loading a well-known CTK
# lib in a subprocess, then look for the target lib relative to
# that root.
abs_path = _try_ctk_root_canary(finder)
if abs_path is not None:
found_via = "system-ctk-root"
else:
finder.raise_not_found_error()

return load_with_abs_path(libname, abs_path, found_via)

Expand Down Expand Up @@ -123,6 +205,14 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL:

- If set, use ``CUDA_HOME`` or ``CUDA_PATH`` (in that order).

5. **CTK root canary probe**

- For libraries whose shared object doesn't reside on the standard
linker path (e.g. ``libnvvm.so`` lives under ``$CTK_ROOT/nvvm/lib64``),
attempt to discover the CTK installation root by system-loading a
well-known CTK library (``cudart``) in a subprocess, then derive
the root from its resolved absolute path.

Notes:
The search is performed **per library**. There is currently no mechanism to
guarantee that multiple libraries are all resolved from the same location.
Expand Down
Loading
Loading