github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/link/kprobe_multi.go (about)

     1  package link
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"unsafe"
     8  
     9  	"github.com/cilium/ebpf"
    10  	"github.com/cilium/ebpf/asm"
    11  	"github.com/cilium/ebpf/internal"
    12  	"github.com/cilium/ebpf/internal/sys"
    13  	"github.com/cilium/ebpf/internal/unix"
    14  )
    15  
    16  // KprobeMultiOptions defines additional parameters that will be used
    17  // when opening a KprobeMulti Link.
    18  type KprobeMultiOptions struct {
    19  	// Symbols takes a list of kernel symbol names to attach an ebpf program to.
    20  	//
    21  	// Mutually exclusive with Addresses.
    22  	Symbols []string
    23  
    24  	// Addresses takes a list of kernel symbol addresses in case they can not
    25  	// be referred to by name.
    26  	//
    27  	// Note that only start addresses can be specified, since the fprobe API
    28  	// limits the attach point to the function entry or return.
    29  	//
    30  	// Mutually exclusive with Symbols.
    31  	Addresses []uintptr
    32  
    33  	// Cookies specifies arbitrary values that can be fetched from an eBPF
    34  	// program via `bpf_get_attach_cookie()`.
    35  	//
    36  	// If set, its length should be equal to the length of Symbols or Addresses.
    37  	// Each Cookie is assigned to the Symbol or Address specified at the
    38  	// corresponding slice index.
    39  	Cookies []uint64
    40  }
    41  
    42  // KprobeMulti attaches the given eBPF program to the entry point of a given set
    43  // of kernel symbols.
    44  //
    45  // The difference with Kprobe() is that multi-kprobe accomplishes this in a
    46  // single system call, making it significantly faster than attaching many
    47  // probes one at a time.
    48  //
    49  // Requires at least Linux 5.18.
    50  func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
    51  	return kprobeMulti(prog, opts, 0)
    52  }
    53  
    54  // KretprobeMulti attaches the given eBPF program to the return point of a given
    55  // set of kernel symbols.
    56  //
    57  // The difference with Kretprobe() is that multi-kprobe accomplishes this in a
    58  // single system call, making it significantly faster than attaching many
    59  // probes one at a time.
    60  //
    61  // Requires at least Linux 5.18.
    62  func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
    63  	return kprobeMulti(prog, opts, unix.BPF_F_KPROBE_MULTI_RETURN)
    64  }
    65  
    66  func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) {
    67  	if prog == nil {
    68  		return nil, errors.New("cannot attach a nil program")
    69  	}
    70  
    71  	syms := uint32(len(opts.Symbols))
    72  	addrs := uint32(len(opts.Addresses))
    73  	cookies := uint32(len(opts.Cookies))
    74  
    75  	if syms == 0 && addrs == 0 {
    76  		return nil, fmt.Errorf("one of Symbols or Addresses is required: %w", errInvalidInput)
    77  	}
    78  	if syms != 0 && addrs != 0 {
    79  		return nil, fmt.Errorf("Symbols and Addresses are mutually exclusive: %w", errInvalidInput)
    80  	}
    81  	if cookies > 0 && cookies != syms && cookies != addrs {
    82  		return nil, fmt.Errorf("Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput)
    83  	}
    84  
    85  	attr := &sys.LinkCreateKprobeMultiAttr{
    86  		ProgFd:           uint32(prog.FD()),
    87  		AttachType:       sys.BPF_TRACE_KPROBE_MULTI,
    88  		KprobeMultiFlags: flags,
    89  	}
    90  
    91  	switch {
    92  	case syms != 0:
    93  		attr.Count = syms
    94  		attr.Syms = sys.NewStringSlicePointer(opts.Symbols)
    95  
    96  	case addrs != 0:
    97  		attr.Count = addrs
    98  		attr.Addrs = sys.NewPointer(unsafe.Pointer(&opts.Addresses[0]))
    99  	}
   100  
   101  	if cookies != 0 {
   102  		attr.Cookies = sys.NewPointer(unsafe.Pointer(&opts.Cookies[0]))
   103  	}
   104  
   105  	fd, err := sys.LinkCreateKprobeMulti(attr)
   106  	if errors.Is(err, unix.ESRCH) {
   107  		return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist)
   108  	}
   109  	if errors.Is(err, unix.EINVAL) {
   110  		return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not AttachTraceKprobeMulti?)", err)
   111  	}
   112  
   113  	if err != nil {
   114  		if haveFeatErr := haveBPFLinkKprobeMulti(); haveFeatErr != nil {
   115  			return nil, haveFeatErr
   116  		}
   117  		return nil, err
   118  	}
   119  
   120  	return &kprobeMultiLink{RawLink{fd, ""}}, nil
   121  }
   122  
   123  type kprobeMultiLink struct {
   124  	RawLink
   125  }
   126  
   127  var _ Link = (*kprobeMultiLink)(nil)
   128  
   129  func (kml *kprobeMultiLink) Update(prog *ebpf.Program) error {
   130  	return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported)
   131  }
   132  
   133  func (kml *kprobeMultiLink) Pin(string) error {
   134  	return fmt.Errorf("pin kprobe_multi: %w", ErrNotSupported)
   135  }
   136  
   137  func (kml *kprobeMultiLink) Unpin() error {
   138  	return fmt.Errorf("unpin kprobe_multi: %w", ErrNotSupported)
   139  }
   140  
   141  var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5.18", func() error {
   142  	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
   143  		Name: "probe_kpm_link",
   144  		Type: ebpf.Kprobe,
   145  		Instructions: asm.Instructions{
   146  			asm.Mov.Imm(asm.R0, 0),
   147  			asm.Return(),
   148  		},
   149  		AttachType: ebpf.AttachTraceKprobeMulti,
   150  		License:    "MIT",
   151  	})
   152  	if errors.Is(err, unix.E2BIG) {
   153  		// Kernel doesn't support AttachType field.
   154  		return internal.ErrNotSupported
   155  	}
   156  	if err != nil {
   157  		return err
   158  	}
   159  	defer prog.Close()
   160  
   161  	fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
   162  		ProgFd:     uint32(prog.FD()),
   163  		AttachType: sys.BPF_TRACE_KPROBE_MULTI,
   164  		Count:      1,
   165  		Syms:       sys.NewStringSlicePointer([]string{"vprintk"}),
   166  	})
   167  	switch {
   168  	case errors.Is(err, unix.EINVAL):
   169  		return internal.ErrNotSupported
   170  	// If CONFIG_FPROBE isn't set.
   171  	case errors.Is(err, unix.EOPNOTSUPP):
   172  		return internal.ErrNotSupported
   173  	case err != nil:
   174  		return err
   175  	}
   176  
   177  	fd.Close()
   178  
   179  	return nil
   180  })