123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- package link
- import (
- "errors"
- "fmt"
- "os"
- "unsafe"
- "github.com/cilium/ebpf"
- "github.com/cilium/ebpf/asm"
- "github.com/cilium/ebpf/internal"
- "github.com/cilium/ebpf/internal/sys"
- "github.com/cilium/ebpf/internal/unix"
- )
- // KprobeMultiOptions defines additional parameters that will be used
- // when opening a KprobeMulti Link.
- type KprobeMultiOptions struct {
- // Symbols takes a list of kernel symbol names to attach an ebpf program to.
- //
- // Mutually exclusive with Addresses.
- Symbols []string
- // Addresses takes a list of kernel symbol addresses in case they can not
- // be referred to by name.
- //
- // Note that only start addresses can be specified, since the fprobe API
- // limits the attach point to the function entry or return.
- //
- // Mutually exclusive with Symbols.
- Addresses []uintptr
- // Cookies specifies arbitrary values that can be fetched from an eBPF
- // program via `bpf_get_attach_cookie()`.
- //
- // If set, its length should be equal to the length of Symbols or Addresses.
- // Each Cookie is assigned to the Symbol or Address specified at the
- // corresponding slice index.
- Cookies []uint64
- }
- // KprobeMulti attaches the given eBPF program to the entry point of a given set
- // of kernel symbols.
- //
- // The difference with Kprobe() is that multi-kprobe accomplishes this in a
- // single system call, making it significantly faster than attaching many
- // probes one at a time.
- //
- // Requires at least Linux 5.18.
- func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
- return kprobeMulti(prog, opts, 0)
- }
- // KretprobeMulti attaches the given eBPF program to the return point of a given
- // set of kernel symbols.
- //
- // The difference with Kretprobe() is that multi-kprobe accomplishes this in a
- // single system call, making it significantly faster than attaching many
- // probes one at a time.
- //
- // Requires at least Linux 5.18.
- func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
- return kprobeMulti(prog, opts, unix.BPF_F_KPROBE_MULTI_RETURN)
- }
- func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) {
- if prog == nil {
- return nil, errors.New("cannot attach a nil program")
- }
- syms := uint32(len(opts.Symbols))
- addrs := uint32(len(opts.Addresses))
- cookies := uint32(len(opts.Cookies))
- if syms == 0 && addrs == 0 {
- return nil, fmt.Errorf("one of Symbols or Addresses is required: %w", errInvalidInput)
- }
- if syms != 0 && addrs != 0 {
- return nil, fmt.Errorf("Symbols and Addresses are mutually exclusive: %w", errInvalidInput)
- }
- if cookies > 0 && cookies != syms && cookies != addrs {
- return nil, fmt.Errorf("Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput)
- }
- if err := haveBPFLinkKprobeMulti(); err != nil {
- return nil, err
- }
- attr := &sys.LinkCreateKprobeMultiAttr{
- ProgFd: uint32(prog.FD()),
- AttachType: sys.BPF_TRACE_KPROBE_MULTI,
- KprobeMultiFlags: flags,
- }
- switch {
- case syms != 0:
- attr.Count = syms
- attr.Syms = sys.NewStringSlicePointer(opts.Symbols)
- case addrs != 0:
- attr.Count = addrs
- attr.Addrs = sys.NewPointer(unsafe.Pointer(&opts.Addresses[0]))
- }
- if cookies != 0 {
- attr.Cookies = sys.NewPointer(unsafe.Pointer(&opts.Cookies[0]))
- }
- fd, err := sys.LinkCreateKprobeMulti(attr)
- if errors.Is(err, unix.ESRCH) {
- return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist)
- }
- if errors.Is(err, unix.EINVAL) {
- return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not AttachTraceKprobeMulti?)", err)
- }
- if err != nil {
- return nil, err
- }
- return &kprobeMultiLink{RawLink{fd, ""}}, nil
- }
- type kprobeMultiLink struct {
- RawLink
- }
- var _ Link = (*kprobeMultiLink)(nil)
- func (kml *kprobeMultiLink) Update(prog *ebpf.Program) error {
- return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported)
- }
- func (kml *kprobeMultiLink) Pin(string) error {
- return fmt.Errorf("pin kprobe_multi: %w", ErrNotSupported)
- }
- func (kml *kprobeMultiLink) Unpin() error {
- return fmt.Errorf("unpin kprobe_multi: %w", ErrNotSupported)
- }
- var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5.18", func() error {
- prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
- Name: "probe_kpm_link",
- Type: ebpf.Kprobe,
- Instructions: asm.Instructions{
- asm.Mov.Imm(asm.R0, 0),
- asm.Return(),
- },
- AttachType: ebpf.AttachTraceKprobeMulti,
- License: "MIT",
- })
- if errors.Is(err, unix.E2BIG) {
- // Kernel doesn't support AttachType field.
- return internal.ErrNotSupported
- }
- if err != nil {
- return err
- }
- defer prog.Close()
- fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
- ProgFd: uint32(prog.FD()),
- AttachType: sys.BPF_TRACE_KPROBE_MULTI,
- Count: 1,
- Syms: sys.NewStringSlicePointer([]string{"vprintk"}),
- })
- switch {
- case errors.Is(err, unix.EINVAL):
- return internal.ErrNotSupported
- // If CONFIG_FPROBE isn't set.
- case errors.Is(err, unix.EOPNOTSUPP):
- return internal.ErrNotSupported
- case err != nil:
- return err
- }
- fd.Close()
- return nil
- })
|