fix(cdi): Include 32-bit libraries in discovery - #2035
Open
ehfd wants to merge 3 commits into
Open
Conversation
Signed-off-by: Eli Bosley <eli@bosley.dev>
Signed-off-by: Eli Bosley <eli@bosley.dev>
|
Thank you! I apologize for missing the earlier comments on my PR. |
Contributor
Author
|
@elibosley You're the author in the commits, so it's just a carry-over. I'll handle the rest! Thank you for your contribution! |
|
Thanks for the contribution. We will review this PR soon. |
Contributor
Author
|
@henry118 Just a cordial reminder. I just would like this before v1.20.1 to blend with the other PRs. |
Signed-off-by: Seungmin Kim <8457324+ehfd@users.noreply.github.com>
Contributor
Author
|
@henry118 I reduced the code footprint dramatically while doing the same work. FYI. |
henry118
reviewed
Sep 8, 2026
henry118
left a comment
Member
There was a problem hiding this comment.
@ehfd Thanks for bearing with me. A few thoughts on the general approach:
- I still think we want a flag here to gate this feature. Unconditionally discovering and mounting the full 32-bit lib set for every container is probably unnecessary for most use cases;
- IMO we can skip the 32-bit injection for alpine entirely. This feels like solving for a use case we would never support (32-bit containers), and the host is always assumed 64-bit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Superseding and takeover of #1968.
Closes #1968
Reviewer: @henry118 @cdesiniotis
Continues #1968 by @elibosley, whose two commits are included here unmodified. The third commit addresses @henry118's review: instead of gating compat32 discovery behind an opt-in flag, the container-side hook that creates the unsafe condition is made architecture-aware, so the 32-bit stack is exposed only where it can actually be used. Related to #563.
Background
ldcache.List()returns the 32-bit and 64-bit library sets separately, and the CDI lookup path discarded the 32-bit one. The default library locator also stopped at the first source that produced a match, so the linker cache could not contribute libraries from a compat32 directory once a library had been found in a predefined native path.On a multilib host the generated spec therefore exposes the 64-bit driver stack while leaving a 32-bit application — a Steam or Proton title, a 32-bit CUDA or VDPAU consumer — without the matching vendor libraries, even though ELF32 driver libraries are installed and catalogued in the host's linker cache. Such applications fall back to software rendering or fail to start.
Why this is not behind an opt-in flag
The review on #1968 asked for an opt-in flag, mirroring
--compat32inlibnvidia-container, because on musl containers the 32-bit and 64-bit directories are appended to a single/etc/ld-musl-<arch>.pathwith no architecture tagging: musl's dynamic linker loads the first file matching the requested name and fails instead of continuing the search, so a wrong-arch match breaks the workload.That failure is real and is reproduced below. It is also entirely a property of the container, decided in our own
update-ldcachehook, at a point where the architecture of every injected library is directly observable:ld.so.cacheentries are tagged with the architecture of the library they refer to, andld.sorejects a wrong-class file and continues searching. This is what makes 32-bit support work at all, and it is why the fix belongs on the musl side only.--compat32in the legacy path is not purely a gate.nvc_container.cresolves the container's ownlibs32_dirfrom its rootfs (/usr/lib/i386-linux-gnu,/usr/lib32, or/usr/lib) and mounts the 32-bit libraries there. CDI mounts host paths as-is and has no equivalent routing step, so the container-side hook is the only place where this can be resolved..runinstaller asks whether to install the 32-bit compatibility libraries, and Debian and Ubuntu ship them as a separate:i386package. A host without it produces the same spec as today.internal/ldconfigtherefore classifies each injected directory by the ELF class of the libraries it contains (debug/elf) and:/etc/ld-musl-<native>.path;/etc/ld-musl-i386.path(armhfon arm64), and only when a dynamic linker for that architecture is present in the container. In practice no such linker exists, so those directories are simply not searched;00-nvcr-*.confdrop-in;/lib:/usr/local/lib:/usr/libsearch path when it creates the file. Alpine ships no.pathfile, so the hook creates one, and creating it replaces the default search path./usr/local/libis silently dropped today; this is a pre-existing bug in the same function.Classification reads the ELF header rather than matching path names such as
i386-linux-gnuorlib32, so it also holds for hosts that do not follow those conventions.The
.pathfile is split on colons and newlines, exactly as musl's dynamic linker splits it, so the hook sees the same entries the linker will.isMuslnow also detects the musl dynamic linker, falling back to the/etc/alpine-releasecheck it used before.Verification
Bare-metal x86_64, Ubuntu 26.04, 2x Tesla P100-SXM2-16GB, driver 580.178.04,
libnvidia-gl-580:i386installed, Docker 29.7.2. Specs generated withnvidia-ctk cdi generate --mode=nvmlinto/etc/cdiunder distinct vendors and requested withdocker run --runtime=runc --device <vendor>/gpu=all.Discovery. The generated spec grows from 58 to 83
hostPathentries: 25 ELF32 driver libraries under/usr/lib/i386-linux-gnu, their soname symlinks, and--folder /usr/lib/i386-linux-gnu{,/vdpau}for theupdate-ldcachehook.The musl failure, reproduced. With the 32-bit libraries in the spec and the unmodified hook, in an Alpine container whose image ships a
.pathfile:With this PR, same image and spec:
/etc/ld-musl-x86_64.pathin a stock Alpine container contains the native driver directories and no i386 entry, and no/etc/ld-musl-i386.pathis created. In the image above, which ships a.pathfile, the native driver directories are prepended and the existing entries keep their order. An image that carries a 32-bit musl dynamic linker gets the 32-bit directories in/etc/ld-musl-i386.pathand nothing else changes.glibc containers. In
ubuntu:24.04andfedora,nvidia-smi -Lis unchanged, the ldcache carries both architectures, the00-nvcr-*.confdrop-in lists the 32-bit directories, and the 32-bit driver libraries resolve against each other (libX11/libXextare absent because the image has no i386 X11 packages):32-bit workloads. In multilib images (
ubuntu:26.04anddebian:trixiewithlibc6:i386,libvulkan1{,:i386},libegl1{,:i386},libx11-6{,:i386}andlibxext6{,:i386}), a 32-bit process enumerates both P100s through Vulkan (vkCreateInstance,vkEnumeratePhysicalDevices,vkGetPhysicalDeviceProperties, served by the injected 32-bitlibGLX_nvidia.so.0ICD) and initialises the CUDA driver API (cuInitsucceeds and reports both devices through the 32-bitlibcuda.so.1). Onmainthe same programs fail withVK_ERROR_INCOMPATIBLE_DRIVERand a faileddlopen, while their 64-bit builds pass either way. The ICDdlopens glvnd'slibEGL.so.1and the X11 client libraries of its own architecture, so the image has to provide those; without them it fails identically onmain.Unit tests cover the locator merge and the ldcache split (from #1968) and the ELF class split, path-file ordering, default-search-path preservation and musl detection. Tests for the touched packages pass, and
golangci-lint run ./...,go vet ./...andgofmtare clean.Notes for reviewers
NVIDIA_CTK_LIBCUDA_DIRbecomes/usr/lib/x86_64-linux-gnu:/usr/lib/i386-linux-gnuon multilib hosts. The variable already carries a list, and the native directory stays first, but any consumer taking the last entry would now get the 32-bit one.FirstwithMergeinNewLibraryLocatorwidens every lookup that uses it, not only 32-bit ones: the ldcache is now consulted even when a predefined search path already matched. Deduplication and native-first ordering keep version inference, which uses the first result, unchanged.nvidia-ctkwith--library-search-path— take the early return inNewLibraryLocatorand are unaffected by the discovery change. Exposing the 32-bit stack there means passing a second search path for the driver'slib32output; the hook change in this PR classifies those directories correctly, since it does not rely on path naming..pathfile only applies to directories that are not searched already, so a driver directory that is also a system directory keeps its position.