github.com/cilium/ebpf@v0.16.0/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) Info() (*Info, error) {
   134  	var info sys.KprobeMultiLinkInfo
   135  	if err := sys.ObjInfo(kml.fd, &info); err != nil {
   136  		return nil, fmt.Errorf("kprobe multi link info: %s", err)
   137  	}
   138  	extra := &KprobeMultiInfo{
   139  		count:  info.Count,
   140  		flags:  info.Flags,
   141  		missed: info.Missed,
   142  	}
   143  
   144  	return &Info{
   145  		info.Type,
   146  		info.Id,
   147  		ebpf.ProgramID(info.ProgId),
   148  		extra,
   149  	}, nil
   150  }
   151  
   152  var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5.18", func() error {
   153  	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
   154  		Name: "probe_kpm_link",
   155  		Type: ebpf.Kprobe,
   156  		Instructions: asm.Instructions{
   157  			asm.Mov.Imm(asm.R0, 0),
   158  			asm.Return(),
   159  		},
   160  		AttachType: ebpf.AttachTraceKprobeMulti,
   161  		License:    "MIT",
   162  	})
   163  	if errors.Is(err, unix.E2BIG) {
   164  		// Kernel doesn't support AttachType field.
   165  		return internal.ErrNotSupported
   166  	}
   167  	if err != nil {
   168  		return err
   169  	}
   170  	defer prog.Close()
   171  
   172  	fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
   173  		ProgFd:     uint32(prog.FD()),
   174  		AttachType: sys.BPF_TRACE_KPROBE_MULTI,
   175  		Count:      1,
   176  		Syms:       sys.NewStringSlicePointer([]string{"vprintk"}),
   177  	})
   178  	switch {
   179  	case errors.Is(err, unix.EINVAL):
   180  		return internal.ErrNotSupported
   181  	// If CONFIG_FPROBE isn't set.
   182  	case errors.Is(err, unix.EOPNOTSUPP):
   183  		return internal.ErrNotSupported
   184  	case err != nil:
   185  		return err
   186  	}
   187  
   188  	fd.Close()
   189  
   190  	return nil
   191  })