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  }