Skip to content

Commit 81a6b00

Browse files
committed
Create .so symlinks for driver libraries in container
Signed-off-by: Evan Lezar <[email protected]>
1 parent f55ef6a commit 81a6b00

File tree

7 files changed

+161
-2
lines changed

7 files changed

+161
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@
236236

237237
## v1.15.0
238238

239+
* Add a hook to create `.so` symlinks for driver libraries in a container.
239240
* Remove `nvidia-container-runtime` and `nvidia-docker2` packages.
240241
* Use `XDG_DATA_DIRS` environment variable when locating config files such as graphics config files.
241242
* Add support for v0.7.0 Container Device Interface (CDI) specification.

cmd/nvidia-ctk/info/info.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
package info
1818

1919
import (
20+
"context"
21+
"debug/elf"
22+
"fmt"
23+
2024
"github.com/urfave/cli/v3"
2125

2226
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
@@ -40,7 +44,27 @@ func (m command) build() *cli.Command {
4044
info := cli.Command{
4145
Name: "info",
4246
Usage: "Provide information about the system",
47+
Action: func(ctx context.Context, cmd *cli.Command) error {
48+
return run()
49+
},
4350
}
4451

4552
return &info
4653
}
54+
55+
func run() error {
56+
filename := "/usr/lib/x86_64-linux-gnu/libcuda.so.570.133.20"
57+
58+
lib, err := elf.Open(filename)
59+
if err != nil {
60+
return err
61+
}
62+
defer lib.Close()
63+
64+
sonames, err := lib.DynString(elf.DT_SONAME)
65+
if err != nil {
66+
return err
67+
}
68+
fmt.Printf("sonames=%v\n", sonames)
69+
return nil
70+
}

internal/discover/symlinks.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,22 @@
1717
package discover
1818

1919
import (
20+
"debug/elf"
2021
"fmt"
2122
"path/filepath"
23+
"strings"
2224
)
2325

26+
// TODO: move to the symlinks package
27+
type Symlink struct {
28+
target string
29+
link string
30+
}
31+
32+
func (s *Symlink) String() string {
33+
return fmt.Sprintf("%s::%s", s.target, s.link)
34+
}
35+
2436
type additionalSymlinks struct {
2537
Discover
2638
version string
@@ -60,7 +72,15 @@ func (d *additionalSymlinks) Hooks() ([]Hook, error) {
6072
}
6173
processedPaths[mount.Path] = true
6274

63-
for _, link := range d.getLinksForMount(mount.Path) {
75+
linksForMount := d.getLinksForMount(mount.Path)
76+
soSymlinks, err := d.getDotSoSymlinks(mount.HostPath)
77+
if err != nil {
78+
// TODO: Log this error.
79+
soSymlinks = nil
80+
}
81+
linksForMount = append(linksForMount, soSymlinks...)
82+
83+
for _, link := range linksForMount {
6484
if processedLinks[link] {
6585
continue
6686
}
@@ -110,3 +130,47 @@ func (d additionalSymlinks) isDriverLibrary(libraryName string, filename string)
110130
match, _ := filepath.Match(pattern, filename)
111131
return match
112132
}
133+
134+
func (d *additionalSymlinks) getDotSoSymlinks(libraryPath string) ([]string, error) {
135+
libraryName := filepath.Base(libraryPath)
136+
if !d.isDriverLibrary("*", libraryName) {
137+
return nil, nil
138+
}
139+
140+
var soSymlinks []string
141+
lib, err := elf.Open(libraryPath)
142+
if err != nil {
143+
return nil, err
144+
}
145+
defer lib.Close()
146+
147+
sonames, err := lib.DynString(elf.DT_SONAME)
148+
if err != nil {
149+
return nil, err
150+
}
151+
152+
var sonameLinkPath string
153+
for _, soname := range sonames {
154+
linkPath := filepath.Join(filepath.Dir(libraryPath), soname)
155+
if sonameLinkPath == "" {
156+
sonameLinkPath = linkPath
157+
}
158+
s := Symlink{
159+
target: libraryName,
160+
link: linkPath,
161+
}
162+
soSymlinks = append(soSymlinks, s.String())
163+
}
164+
165+
if sonameLinkPath != "" {
166+
sonameLinkPathExt := filepath.Ext(sonameLinkPath)
167+
soLinkPath := strings.TrimSuffix(sonameLinkPath, sonameLinkPathExt)
168+
s := Symlink{
169+
target: filepath.Base(sonameLinkPath),
170+
link: soLinkPath,
171+
}
172+
soSymlinks = append(soSymlinks, s.String())
173+
}
174+
175+
return soSymlinks, nil
176+
}

internal/lookup/root/options.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,9 @@ func WithConfigSearchPaths(paths ...string) Option {
4343
d.configSearchPaths = paths
4444
}
4545
}
46+
47+
func WithVersion(version string) Option {
48+
return func(d *Driver) {
49+
d.version = version
50+
}
51+
}

internal/lookup/root/root.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,30 @@
1717
package root
1818

1919
import (
20+
"fmt"
2021
"os"
2122
"path/filepath"
2223
"strings"
24+
"sync"
2325

2426
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
2527
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
2628
)
2729

2830
// Driver represents a filesystem in which a set of drivers or devices is defined.
2931
type Driver struct {
32+
sync.Mutex
3033
logger logger.Interface
3134
// Root represents the root from the perspective of the driver libraries and binaries.
3235
Root string
3336
// librarySearchPaths specifies explicit search paths for discovering libraries.
3437
librarySearchPaths []string
3538
// configSearchPaths specified explicit search paths for discovering driver config files.
3639
configSearchPaths []string
40+
// version stores the driver version. This can be specified at construction or cached on subsequent calls.
41+
version string
42+
// libraryRoot stores the absolute path where the driver libraries (libcuda.so.<VERSION>) can be found.
43+
libraryRoot string
3744
}
3845

3946
// New creates a new Driver root using the specified options.
@@ -103,6 +110,62 @@ func (r *Driver) configSearchOptions() []lookup.Option {
103110
}
104111
}
105112

113+
// Version returns the driver version as a string.
114+
func (r *Driver) Version() (string, error) {
115+
r.Lock()
116+
defer r.Unlock()
117+
if r.version != "" {
118+
return r.version, nil
119+
}
120+
121+
libcudaPath, err := r.libcudaPath()
122+
if err != nil {
123+
return "", fmt.Errorf("failed to locate libcuda.so: %v", err)
124+
}
125+
126+
version := strings.TrimPrefix(filepath.Base(libcudaPath), "libcuda.so.")
127+
if version == "" {
128+
return "", fmt.Errorf("failed to determine libcuda.so version from path: %q", libcudaPath)
129+
}
130+
131+
r.version = version
132+
return r.version, nil
133+
}
134+
135+
// LibraryRoot returns the folder in which the driver libraries can be found.
136+
func (r *Driver) LibraryRoot() (string, error) {
137+
r.Lock()
138+
defer r.Unlock()
139+
if r.libraryRoot != "" {
140+
return r.libraryRoot, nil
141+
}
142+
143+
libcudaPath, err := r.libcudaPath()
144+
if err != nil {
145+
return "", fmt.Errorf("failed to locate libcuda.so: %v", err)
146+
}
147+
148+
r.libraryRoot = filepath.Dir(libcudaPath)
149+
return r.libraryRoot, nil
150+
}
151+
152+
// libcudaPath returns the path to libcuda.so.*.* in the driver root.
153+
func (r *Driver) libcudaPath() (string, error) {
154+
pattern := "libcuda.so.*.*"
155+
156+
locator := r.Libraries()
157+
paths, err := locator.Locate(pattern)
158+
if err != nil {
159+
return "", fmt.Errorf("failed to locate %v: %v", pattern, err)
160+
}
161+
162+
libcudaPath := paths[0]
163+
if len(paths) > 1 {
164+
r.logger.Warningf("Selecting %v out of multiple libcuda.so paths.", libcudaPath, paths)
165+
}
166+
return libcudaPath, nil
167+
}
168+
106169
// normalizeSearchPaths takes a list of paths and normalized these.
107170
// Each of the elements in the list is expanded if it is a path list and the
108171
// resultant list is returned.

internal/runtime/runtime_factory_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ func TestFactoryMethod(t *testing.T) {
6767
logger, _ := testlog.NewNullLogger()
6868
driver := root.New(
6969
root.WithDriverRoot("/nvidia/driver/root"),
70+
root.WithVersion("999.88.77"),
7071
)
7172

7273
testCases := []struct {

pkg/nvcdi/driver-nvml.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import (
3232
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
3333
)
3434

35-
// NewDriverDiscoverer creates a discoverer for the libraries and binaries associated with a driver installation.
35+
// newDriverDiscoverer creates a discoverer for the libraries and binaries associated with a driver installation.
3636
// The supplied NVML Library is used to query the expected driver version.
3737
func (l *nvmllib) NewDriverDiscoverer() (discover.Discover, error) {
3838
if r := l.nvmllib.Init(); r != nvml.SUCCESS {

0 commit comments

Comments
 (0)