Skip to content

Commit 3418f34

Browse files
committed
btf: move vmlinux search logic into findVMLinux()
loadKernelSpec() gets short-circuited in the first statement on most kernels, resulting in the vmlinux search code being rarely exercised successfully in CI. Split the search logic out into its own function to make it testable in isolation. Although virtme VMs don't have images at any of these paths, running the test suite on a host with a full distro does result in a proper test execution. Signed-off-by: Timo Beckers <[email protected]>
1 parent 0e6f2cf commit 3418f34

File tree

4 files changed

+62
-18
lines changed

4 files changed

+62
-18
lines changed

internal/btf/btf.go

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ func LoadKernelSpec() (*Spec, error) {
312312
return kernelBTF.Spec, err
313313
}
314314

315+
// loadKernelSpec attempts to load the raw vmlinux BTF blob at
316+
// /sys/kernel/btf/vmlinux and falls back to scanning the file system
317+
// for vmlinux ELFs.
315318
func loadKernelSpec() (*Spec, error) {
316319
fh, err := os.Open("/sys/kernel/btf/vmlinux")
317320
if err == nil {
@@ -320,13 +323,21 @@ func loadKernelSpec() (*Spec, error) {
320323
return loadRawSpec(fh, internal.NativeEndian, nil, nil)
321324
}
322325

323-
var uname unix.Utsname
324-
if err := unix.Uname(&uname); err != nil {
325-
return nil, fmt.Errorf("uname failed: %w", err)
326+
file, err := findVMLinux()
327+
if err != nil {
328+
return nil, err
326329
}
330+
defer file.Close()
331+
332+
return loadSpecFromELF(file)
333+
}
327334

328-
end := bytes.IndexByte(uname.Release[:], 0)
329-
release := string(uname.Release[:end])
335+
// findVMLinux scans multiple well-known paths for vmlinux kernel images.
336+
func findVMLinux() (*internal.SafeELFFile, error) {
337+
release, err := internal.KernelRelease()
338+
if err != nil {
339+
return nil, err
340+
}
330341

331342
// use same list of locations as libbpf
332343
// https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122
@@ -341,24 +352,14 @@ func loadKernelSpec() (*Spec, error) {
341352
}
342353

343354
for _, loc := range locations {
344-
path := fmt.Sprintf(loc, release)
345-
346-
fh, err := os.Open(path)
355+
fh, err := os.Open(fmt.Sprintf(loc, release))
347356
if err != nil {
348357
continue
349358
}
350-
defer fh.Close()
351-
352-
file, err := internal.NewSafeELFFile(fh)
353-
if err != nil {
354-
return nil, err
355-
}
356-
defer file.Close()
357-
358-
return loadSpecFromELF(file)
359+
return internal.NewSafeELFFile(fh)
359360
}
360361

361-
return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported)
362+
return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported)
362363
}
363364

364365
// parseBTFHeader parses the header of the .BTF section.

internal/btf/btf_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,23 @@ func TestParseCurrentKernelBTF(t *testing.T) {
186186
}
187187
}
188188

189+
func TestFindVMLinux(t *testing.T) {
190+
file, err := findVMLinux()
191+
testutils.SkipIfNotSupported(t, err)
192+
if err != nil {
193+
t.Fatal("Can't find vmlinux:", err)
194+
}
195+
196+
spec, err := loadSpecFromELF(file)
197+
if err != nil {
198+
t.Fatal("Can't load BTF:", err)
199+
}
200+
201+
if len(spec.namedTypes) == 0 {
202+
t.Fatal("Empty kernel BTF")
203+
}
204+
}
205+
189206
func TestLoadSpecFromElf(t *testing.T) {
190207
testutils.Files(t, testutils.Glob(t, "../../testdata/loader-e*.elf"), func(t *testing.T, file string) {
191208
spec := parseELFBTF(t, file)

internal/version.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package internal
33
import (
44
"fmt"
55
"sync"
6+
7+
"github.com/cilium/ebpf/internal/unix"
68
)
79

810
const (
@@ -105,3 +107,16 @@ func detectKernelVersion() (Version, error) {
105107
}
106108
return NewVersionFromCode(vc), nil
107109
}
110+
111+
// KernelRelease returns the release string of the running kernel.
112+
// Its format depends on the Linux distribution and corresponds to directory
113+
// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and
114+
// 4.19.0-16-amd64.
115+
func KernelRelease() (string, error) {
116+
var uname unix.Utsname
117+
if err := unix.Uname(&uname); err != nil {
118+
return "", fmt.Errorf("uname failed: %w", err)
119+
}
120+
121+
return unix.ByteSliceToString(uname.Release[:]), nil
122+
}

internal/version_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,14 @@ func TestVersionFromCode(t *testing.T) {
8484
})
8585
}
8686
}
87+
88+
func TestKernelRelease(t *testing.T) {
89+
r, err := KernelRelease()
90+
if err != nil {
91+
t.Fatal(err)
92+
}
93+
94+
if r == "" {
95+
t.Fatal("unexpected empty kernel release")
96+
}
97+
}

0 commit comments

Comments
 (0)