Skip to content

fix(nvjitlink-sys): find libnvJitLink under lib/ as well as lib64/ - #1333

Merged
nihalpasham merged 5 commits into
NVIDIA:mainfrom
lucifer1004:nvjitlink-conda-lib
Oct 19, 2026
Merged

nihalpasham merged 5 commits into
NVIDIA:mainfrom
lucifer1004:nvjitlink-conda-lib

Conversation

@lucifer1004

@lucifer1004 lucifer1004 commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

What this adds

nvjitlink-sys now probes <root>/lib/libnvJitLink.so after <root>/lib64/libnvJitLink.so under each CUDA root, so a toolkit laid out with lib/ (conda-forge, and nixpkgs as the in-tree flake joins it) gives the finalizer an exact file to fingerprint and cargo oxide build --materialize-cubin works on such machines. Installer layouts keep lib64/ first and behave as before. The materialization refusal now says which variables to set.

# conda-forge machine: nvJitLink at $CONDA_PREFIX/lib/libnvJitLink.so, no lib64/
export CUDA_HOME=$CONDA_PREFIX
cargo oxide run vecadd --arch sm_120 --materialize-cubin

before: probes $CUDA_HOME/lib64/..., /usr/local/cuda/lib64/..., /opt/cuda/lib64/... (3 misses),
        then dlopen("libnvJitLink.so.13") by name
        -> CUDA materializer discovery failed: the loaded libNVVM or nvJitLink library
           cannot be tied to an exact file; refusing materialization ...          (exit 2)
after:  probes $CUDA_HOME/lib64/..., then $CUDA_HOME/lib/libnvJitLink.so
        -> Output format: materialized cubin ... SUCCESS: All 1024 elements correct!  (exit 0)
// nvjitlink-sys: the candidates probed under each root, lib64 first
fn library_candidates(roots: &[PathBuf]) -> Vec<PathBuf> {
    roots
        .iter()
        .flat_map(|root| ["lib64", "lib"].map(|dir| root.join(dir).join("libnvJitLink.so")))
        .collect()
}

What we fixed

  • open_library joined only lib64/libnvJitLink.so under each root. A root laid out with lib/ fell through to the SONAME branch, which carries no file identity, so the materializer handshake is None and both the cargo oxide helper and the backend refuse materialization.
  • With a system toolkit present and CUDA_HOME at a lib/ root, main silently took the system nvJitLink while libNVVM came from the configured root. Both now come from the configured root.
  • The refusal names LIBNVVM_PATH, LIBNVJITLINK_PATH, CUDA_TOOLKIT_PATH and CUDA_HOME, and calls the SONAME load the usual cause.
Implementation
  • library_candidates(roots) yields <root>/lib64/libnvJitLink.so then <root>/lib/libnvJitLink.so per root. open_library iterates it through the unchanged open_library_path (canonicalize, retained /proc/self/fd load, identity capture), so a lib/ hit is fingerprinted the same way a lib64 hit is, and a missing or unloadable file falls through to the next candidate.
  • LIBNVJITLINK_PATH first and the SONAME fallback last are unchanged. libnvvm-sys is untouched because these layouts keep nvvm/lib64/.
  • Tests: the candidate-order unit test, plus a Linux regression through a small open_library_under_roots seam that loads a probe DSO from <root>/lib/ with no lib64/ and checks the retained identity, lib64-first precedence and the empty-root negative case.
  • Docs: the nvjitlink-sys README, the cuda-host ltoir discovery docs and the cargo oxide doctor hint describe the new order and the unversioned-name limit.

Verification

Checked at a00a3da13 (nightly-2026-10-11, RTX 5090):

  • ✅ cargo test -p nvjitlink-sys (16 passed, 3 ignored toolkit tests, doctest passed), -p cargo-oxide (255), -p cuda-artifact-finalizer (33), -p cuda-host (108); clippy and rustdoc with warnings as errors; fmt; the lock check; all 18 guards.
  • ✅ Fails before, passes after: with main's lib64-only loop, two of the three new tests fail; with this branch all pass.
  • ✅ Mount namespace with /usr/local/cuda-13.4 and -13.3 hidden and CUDA_HOME at a conda-style root (lib/libnvJitLink.so.13.4.92, nvvm/lib64/libnvvm.so, no lib64/): main exits 2 with the refusal; this branch prints Output format: materialized cubin and SUCCESS: All 1024 elements correct!, with the root's lib/ copy as the fingerprinted file. memcheck on the materialized vecadd: 0 errors.
  • ✅ Controls: CUDA_HOME unset gives byte-identical handshakes before and after; a root with both directories still picks lib64/; a broken lib/ candidate falls through to the next root; plain vecadd.ptx identical to main.
  • ✅ Author: conda-forge CUDA 13.2 with CUDA_HOME=$CONDA_PREFIX, main fails with the refusal and this branch materializes and runs on an RTX PRO 6000.

Caveats

  • Only the unversioned libnvJitLink.so is probed under a root. A runtime-only conda env or a pip wheel that ships only libnvJitLink.so.13 still takes the SONAME route and is refused for materialization; LIBNVJITLINK_PATH at the versioned file works and is what the message now says.
  • A configured root whose lib/libnvJitLink.so is older than the toolkit's libNVVM now fails loudly in nvJitLink where main used the system copy by accident.
  • Linux only; Windows discovery is untouched.

nvjitlink-sys probed only `<root>/lib64/libnvJitLink.so` under each CUDA
root. conda environments ship the library under `<root>/lib/` and have no
`lib64/`, so the probe fell through to loading by SONAME. A library found
that way cannot be tied to an exact file, and `--materialize-cubin` then
refused to run with CUDA_HOME set to the environment. libNVVM, under
`<root>/nvvm/lib64/`, was found in the same layout.

Probe `lib64/` then `lib/` under each root, and say in the
materialization error that a library found only by SONAME has no exact
file, and how to point at one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Zihua Wu <13583761+lucifer1004@users.noreply.github.com>
@nihalpasham nihalpasham added the cuda-oxide SIMT programming model: rustc backend, cargo oxide, cuda-device, cuda-host, book label Oct 11, 2026
Hoist the per-root probe loop out of open_library into
open_library_under_roots, which takes the roots as a slice. The
LIBNVJITLINK_PATH override and the SONAME fallback stay in
open_library, so a developer's environment cannot short-circuit a
test of the loop.

Add three Linux tests that build the probe DSO under a temp root: a
lib/-only root is found and its retained identity is the lib/ file,
lib64/ wins when both directories exist, and an empty root reports
both candidates in the tried list. The first and the third fail
against the lib64-only probe that main had.

Name conda-forge and nixpkgs as the lib/ layouts in the crate docs,
list the discovery order there, and record that only the unversioned
libnvJitLink.so is probed under a root.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
…n refusal

The refusal fires whenever either tool has no exact file: after a
SONAME load, but also when the /proc/self/fd load falls back to a
pathname load, when the digest is missing, or when the file changed.
Call the SONAME load the usual cause rather than the only one, and
list CUDA_TOOLKIT_PATH before CUDA_HOME, as the sys crates' own
errors do.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
The nvjitlink-sys README, the cuda-host ltoir module docs and the
cargo oxide doctor hint still said lib64 only. State the
lib64-then-lib order, name conda-forge and nixpkgs as the lib/
layouts, and record that only the unversioned libnvJitLink.so is
probed under a root, with LIBNVJITLINK_PATH as the way to name a
versioned file.

Signed-off-by: nihalpasham <nihalp@nvidia.com>
@nihalpasham
nihalpasham self-requested a review as a code owner October 19, 2026 11:14
@copy-pr-bot

copy-pr-bot Bot commented Oct 19, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@nihalpasham nihalpasham added bug Something isn't working host-apis Host-side runtime APIs (cuda-host, cuda-core, cuda-async) build-related Build scripts, toolchain discovery, or compilation setup cargo-oxide The `cargo oxide` subcommand and build automation labels Oct 19, 2026
@nihalpasham

Copy link
Copy Markdown
Collaborator

/ok to test a00a3da

@nihalpasham

Copy link
Copy Markdown
Collaborator

Thanks, this is the right fix: the probe loop is the one place every consumer reaches nvJitLink through, and lib64 stays first. On top:

  • Merged main so the two files live under cuda-oxide/, and brought the discovery docs in line with the new order (the nvjitlink-sys README, the cuda-host ltoir docs, the cargo oxide doctor hint). They also record the limit: only the unversioned libnvJitLink.so is probed under a root, so a layout that ships only libnvJitLink.so.13 still needs LIBNVJITLINK_PATH.
  • Reworded the refusal to name every discovery variable and to call the SONAME load the usual cause rather than the only one.
  • Added a Linux regression that loads a probe DSO from <root>/lib/ with no lib64/: retained identity, lib64-first precedence and the empty-root negative case.

I reproduced both sides here: with the system toolkit hidden and CUDA_HOME at a conda-style root, main refuses materialization and this branch materializes and runs vecadd on the 5090 with the root's lib/libnvJitLink.so as the fingerprinted file. At a00a3da13 the four crates' tests, clippy, rustdoc, fmt, the 18 guards and all CI checks passed. #1317 is about the ffi examples' headers and stays open.

@nihalpasham
nihalpasham merged commit 4eb5fb1 into NVIDIA:main Oct 19, 2026
62 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working build-related Build scripts, toolchain discovery, or compilation setup cargo-oxide The `cargo oxide` subcommand and build automation cuda-oxide SIMT programming model: rustc backend, cargo oxide, cuda-device, cuda-host, book host-apis Host-side runtime APIs (cuda-host, cuda-core, cuda-async)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants