github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/link/uprobe_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 // UprobeMultiOptions defines additional parameters that will be used 17 // when opening a UprobeMulti Link. 18 type UprobeMultiOptions struct { 19 // Symbol addresses. If set, overrides the addresses eventually parsed from 20 // the executable. Mutually exclusive with UprobeMulti's symbols argument. 21 Addresses []uint64 22 23 // Offsets into functions provided by UprobeMulti's symbols argument. 24 // For example: to set uprobes to main+5 and _start+10, call UprobeMulti 25 // with: 26 // symbols: "main", "_start" 27 // opt.Offsets: 5, 10 28 Offsets []uint64 29 30 // Optional list of associated ref counter offsets. 31 RefCtrOffsets []uint64 32 33 // Optional list of associated BPF cookies. 34 Cookies []uint64 35 36 // Only set the uprobe_multi link on the given process ID, zero PID means 37 // system-wide. 38 PID uint32 39 } 40 41 func (ex *Executable) UprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions) (Link, error) { 42 return ex.uprobeMulti(symbols, prog, opts, 0) 43 } 44 45 func (ex *Executable) UretprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions) (Link, error) { 46 47 // The return probe is not limited for symbols entry, so there's no special 48 // setup for return uprobes (other than the extra flag). The symbols, opts.Offsets 49 // and opts.Addresses arrays follow the same logic as for entry uprobes. 50 return ex.uprobeMulti(symbols, prog, opts, unix.BPF_F_UPROBE_MULTI_RETURN) 51 } 52 53 func (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions, flags uint32) (Link, error) { 54 if prog == nil { 55 return nil, errors.New("cannot attach a nil program") 56 } 57 58 if opts == nil { 59 opts = &UprobeMultiOptions{} 60 } 61 62 addresses, err := ex.addresses(symbols, opts.Addresses, opts.Offsets) 63 if err != nil { 64 return nil, err 65 } 66 67 addrs := len(addresses) 68 cookies := len(opts.Cookies) 69 refCtrOffsets := len(opts.RefCtrOffsets) 70 71 if addrs == 0 { 72 return nil, fmt.Errorf("Addresses are required: %w", errInvalidInput) 73 } 74 if refCtrOffsets > 0 && refCtrOffsets != addrs { 75 return nil, fmt.Errorf("RefCtrOffsets must be exactly Addresses in length: %w", errInvalidInput) 76 } 77 if cookies > 0 && cookies != addrs { 78 return nil, fmt.Errorf("Cookies must be exactly Addresses in length: %w", errInvalidInput) 79 } 80 81 attr := &sys.LinkCreateUprobeMultiAttr{ 82 Path: sys.NewStringPointer(ex.path), 83 ProgFd: uint32(prog.FD()), 84 AttachType: sys.BPF_TRACE_UPROBE_MULTI, 85 UprobeMultiFlags: flags, 86 Count: uint32(addrs), 87 Offsets: sys.NewPointer(unsafe.Pointer(&addresses[0])), 88 Pid: opts.PID, 89 } 90 91 if refCtrOffsets != 0 { 92 attr.RefCtrOffsets = sys.NewPointer(unsafe.Pointer(&opts.RefCtrOffsets[0])) 93 } 94 if cookies != 0 { 95 attr.Cookies = sys.NewPointer(unsafe.Pointer(&opts.Cookies[0])) 96 } 97 98 fd, err := sys.LinkCreateUprobeMulti(attr) 99 if errors.Is(err, unix.ESRCH) { 100 return nil, fmt.Errorf("%w (specified pid not found?)", os.ErrNotExist) 101 } 102 if errors.Is(err, unix.EINVAL) { 103 return nil, fmt.Errorf("%w (missing symbol or prog's AttachType not AttachTraceUprobeMulti?)", err) 104 } 105 106 if err != nil { 107 if haveFeatErr := haveBPFLinkUprobeMulti(); haveFeatErr != nil { 108 return nil, haveFeatErr 109 } 110 return nil, err 111 } 112 113 return &uprobeMultiLink{RawLink{fd, ""}}, nil 114 } 115 116 func (ex *Executable) addresses(symbols []string, addresses, offsets []uint64) ([]uint64, error) { 117 n := len(symbols) 118 if n == 0 { 119 n = len(addresses) 120 } 121 122 if n == 0 { 123 return nil, fmt.Errorf("%w: neither symbols nor addresses given", errInvalidInput) 124 } 125 126 if symbols != nil && len(symbols) != n { 127 return nil, fmt.Errorf("%w: have %d symbols but want %d", errInvalidInput, len(symbols), n) 128 } 129 130 if addresses != nil && len(addresses) != n { 131 return nil, fmt.Errorf("%w: have %d addresses but want %d", errInvalidInput, len(addresses), n) 132 } 133 134 if offsets != nil && len(offsets) != n { 135 return nil, fmt.Errorf("%w: have %d offsets but want %d", errInvalidInput, len(offsets), n) 136 } 137 138 results := make([]uint64, 0, n) 139 for i := 0; i < n; i++ { 140 var sym string 141 if symbols != nil { 142 sym = symbols[i] 143 } 144 145 var addr, off uint64 146 if addresses != nil { 147 addr = addresses[i] 148 } 149 150 if offsets != nil { 151 off = offsets[i] 152 } 153 154 result, err := ex.address(sym, addr, off) 155 if err != nil { 156 return nil, err 157 } 158 159 results = append(results, result) 160 } 161 162 return results, nil 163 } 164 165 type uprobeMultiLink struct { 166 RawLink 167 } 168 169 var _ Link = (*uprobeMultiLink)(nil) 170 171 func (kml *uprobeMultiLink) Update(prog *ebpf.Program) error { 172 return fmt.Errorf("update uprobe_multi: %w", ErrNotSupported) 173 } 174 175 func (kml *uprobeMultiLink) Pin(string) error { 176 return fmt.Errorf("pin uprobe_multi: %w", ErrNotSupported) 177 } 178 179 func (kml *uprobeMultiLink) Unpin() error { 180 return fmt.Errorf("unpin uprobe_multi: %w", ErrNotSupported) 181 } 182 183 var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", "6.6", func() error { 184 prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ 185 Name: "probe_upm_link", 186 Type: ebpf.Kprobe, 187 Instructions: asm.Instructions{ 188 asm.Mov.Imm(asm.R0, 0), 189 asm.Return(), 190 }, 191 AttachType: ebpf.AttachTraceUprobeMulti, 192 License: "MIT", 193 }) 194 if errors.Is(err, unix.E2BIG) { 195 // Kernel doesn't support AttachType field. 196 return internal.ErrNotSupported 197 } 198 if err != nil { 199 return err 200 } 201 defer prog.Close() 202 203 // We try to create uprobe multi link on '/' path which results in 204 // error with -EBADF in case uprobe multi link is supported. 205 fd, err := sys.LinkCreateUprobeMulti(&sys.LinkCreateUprobeMultiAttr{ 206 ProgFd: uint32(prog.FD()), 207 AttachType: sys.BPF_TRACE_UPROBE_MULTI, 208 Path: sys.NewStringPointer("/"), 209 Offsets: sys.NewPointer(unsafe.Pointer(&[]uint64{0})), 210 Count: 1, 211 }) 212 switch { 213 case errors.Is(err, unix.EBADF): 214 return nil 215 case errors.Is(err, unix.EINVAL): 216 return internal.ErrNotSupported 217 case err != nil: 218 return err 219 } 220 221 // should not happen 222 fd.Close() 223 return errors.New("successfully attached uprobe_multi to /, kernel bug?") 224 })