Bundle libioctl_hook.so into the run_sniffer tool.

This makes it more convenient to use the `run_sniffer` tool as it is now
self-contained.

This also removes the use of RELR relocations for `libioctl_hook.so` as
I've found that they don't work on the `nvidia-smi` binary I'm working with.
This improves compatibility in general.

PiperOrigin-RevId: 644493298
This commit is contained in:
Etienne Perot
2024-06-18 13:30:35 -07:00
committed by gVisor bot
parent d4e733ac17
commit 6d021e7eb2
3 changed files with 49 additions and 7 deletions
+13
View File
@@ -19,6 +19,10 @@ cc_binary(
"sniffer_bridge.cc",
"sniffer_bridge.h",
],
# Needed to support being used when LD_PRELOAD'd into binaries that are
# built with a libc library that doesn't support RELR relocations
# (such as nvidia-smi).
features = ["-enable_relr"],
linkshared = True,
deps = [
":ioctl_cc_proto",
@@ -31,6 +35,15 @@ cc_binary(
go_binary(
name = "run_sniffer",
srcs = ["run_sniffer.go"],
embedsrcs = [
# The 'keep' comment is needed to prevent glaze from removing this
# dependency. This is because the `:ioctl_hook` `cc_binary` rule
# output file is 'libioctl_hook.so' (because of `linkshared = True`),
# but glaze assumes that the output file is just `ioctl_hook`, so it
# can't associate the `go:embed` statement in `run_sniffer.go` with
# `:ioctl_hook`'s actual output file.
":ioctl_hook", # keep
],
static = True,
deps = [
"//pkg/log",
-5
View File
@@ -19,7 +19,6 @@ To start, we need to build the shared library and Go binary:
```
make copy TARGETS=//tools/ioctl_sniffer:run_sniffer DESTINATION=bin/
make copy TARGETS=//tools/ioctl_sniffer:ioctl_hook DESTINATION=bin/
```
Once we have the binary, we can hook into any GPU workload by passing the
@@ -46,7 +45,3 @@ Alloc:
...
Unknown:
```
Note that by default, `run_sniffer` assumes the shared library is located in the
same directory. You can specify the path to the library with the optional
`-ld_preload` flag.
+36 -2
View File
@@ -23,9 +23,31 @@ import (
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/tools/ioctl_sniffer/sniffer"
_ "embed" // Necessary to use go:embed.
)
var ldPreloadPath = flag.String("ld_preload", "./libioctl_hook.so", "The path to the LD_PRELOAD library.")
//go:embed libioctl_hook.so
var ioctlHookSharedObject []byte
// createSharedObject creates a temporary directory containing the ioctl hook
// shared object, and returns the path to it. This file will be automatically
// deleted when the program exits.
func createSharedObject() (*os.File, error) {
tmpFile, err := os.CreateTemp(os.TempDir(), "libioctl_hook.*.so")
if err != nil {
return nil, fmt.Errorf("failed to create temporary file: %w", err)
}
// Remove the file from the filesystem but keep a handle open to it
// so that it lasts only as long as the program does.
if err := os.Remove(tmpFile.Name()); err != nil {
return nil, fmt.Errorf("failed to unlink temporary file: %w", err)
}
if _, err := tmpFile.Write(ioctlHookSharedObject); err != nil {
return nil, fmt.Errorf("failed to write to temporary file: %w", err)
}
return tmpFile, nil
}
// Main is our main function.
func Main() error {
@@ -39,6 +61,16 @@ func Main() error {
return fmt.Errorf("failed to init sniffer: %w", err)
}
hookFile, err := createSharedObject()
if err != nil {
return fmt.Errorf("failed to create shared object file: %w", err)
}
defer func() {
if err := hookFile.Close(); err != nil {
log.Warningf("failed to close shared object file: %w", err)
}
}()
// Create a pipe to read the output of the command.
r, w, err := os.Pipe()
if err != nil {
@@ -51,7 +83,9 @@ func Main() error {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(), fmt.Sprintf("LD_PRELOAD=%s", *ldPreloadPath))
// Refer to the hook file by file descriptor here as its named file no
// longer exists.
cmd.Env = append(os.Environ(), fmt.Sprintf("LD_PRELOAD=/proc/%d/fd/%d", os.Getpid(), hookFile.Fd()))
// Run the command and start reading the output.
if err := cmd.Start(); err != nil {