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 })