1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
package btf
import (
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/linux"
)
var kernelBTF = struct {
sync.RWMutex
kernel *Spec
modules map[string]*Spec
}{
modules: make(map[string]*Spec),
}
// FlushKernelSpec removes any cached kernel type information.
func FlushKernelSpec() {
kernelBTF.Lock()
defer kernelBTF.Unlock()
kernelBTF.kernel = nil
kernelBTF.modules = make(map[string]*Spec)
}
// LoadKernelSpec returns the current kernel's BTF information.
//
// Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system
// for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled.
func LoadKernelSpec() (*Spec, error) {
kernelBTF.RLock()
spec := kernelBTF.kernel
kernelBTF.RUnlock()
if spec == nil {
kernelBTF.Lock()
defer kernelBTF.Unlock()
spec = kernelBTF.kernel
}
if spec != nil {
return spec.Copy(), nil
}
spec, _, err := loadKernelSpec()
if err != nil {
return nil, err
}
kernelBTF.kernel = spec
return spec.Copy(), nil
}
// LoadKernelModuleSpec returns the BTF information for the named kernel module.
//
// Defaults to /sys/kernel/btf/<module>.
// Returns an error wrapping ErrNotSupported if BTF is not enabled.
// Returns an error wrapping fs.ErrNotExist if BTF for the specific module doesn't exist.
func LoadKernelModuleSpec(module string) (*Spec, error) {
kernelBTF.RLock()
spec := kernelBTF.modules[module]
kernelBTF.RUnlock()
if spec != nil {
return spec.Copy(), nil
}
base, err := LoadKernelSpec()
if err != nil {
return nil, fmt.Errorf("load kernel spec: %w", err)
}
kernelBTF.Lock()
defer kernelBTF.Unlock()
if spec = kernelBTF.modules[module]; spec != nil {
return spec.Copy(), nil
}
spec, err = loadKernelModuleSpec(module, base)
if err != nil {
return nil, err
}
kernelBTF.modules[module] = spec
return spec.Copy(), nil
}
func loadKernelSpec() (_ *Spec, fallback bool, _ error) {
fh, err := os.Open("/sys/kernel/btf/vmlinux")
if err == nil {
defer fh.Close()
spec, err := loadRawSpec(fh, internal.NativeEndian, nil)
return spec, false, err
}
file, err := findVMLinux()
if err != nil {
return nil, false, err
}
defer file.Close()
spec, err := LoadSpecFromReader(file)
return spec, true, err
}
func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) {
dir, file := filepath.Split(module)
if dir != "" || filepath.Ext(file) != "" {
return nil, fmt.Errorf("invalid module name %q", module)
}
fh, err := os.Open(filepath.Join("/sys/kernel/btf", module))
if err != nil {
return nil, err
}
defer fh.Close()
return loadRawSpec(fh, internal.NativeEndian, base)
}
// findVMLinux scans multiple well-known paths for vmlinux kernel images.
func findVMLinux() (*os.File, error) {
release, err := linux.KernelRelease()
if err != nil {
return nil, err
}
// use same list of locations as libbpf
// https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122
locations := []string{
"/boot/vmlinux-%s",
"/lib/modules/%s/vmlinux-%[1]s",
"/lib/modules/%s/build/vmlinux",
"/usr/lib/modules/%s/kernel/vmlinux",
"/usr/lib/debug/boot/vmlinux-%s",
"/usr/lib/debug/boot/vmlinux-%s.debug",
"/usr/lib/debug/lib/modules/%s/vmlinux",
}
for _, loc := range locations {
file, err := os.Open(fmt.Sprintf(loc, release))
if errors.Is(err, os.ErrNotExist) {
continue
}
return file, err
}
return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported)
}
|