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