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