kprobe_multi.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package link
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "unsafe"
  7. "github.com/cilium/ebpf"
  8. "github.com/cilium/ebpf/asm"
  9. "github.com/cilium/ebpf/internal"
  10. "github.com/cilium/ebpf/internal/sys"
  11. "github.com/cilium/ebpf/internal/unix"
  12. )
  13. // KprobeMultiOptions defines additional parameters that will be used
  14. // when opening a KprobeMulti Link.
  15. type KprobeMultiOptions struct {
  16. // Symbols takes a list of kernel symbol names to attach an ebpf program to.
  17. //
  18. // Mutually exclusive with Addresses.
  19. Symbols []string
  20. // Addresses takes a list of kernel symbol addresses in case they can not
  21. // be referred to by name.
  22. //
  23. // Note that only start addresses can be specified, since the fprobe API
  24. // limits the attach point to the function entry or return.
  25. //
  26. // Mutually exclusive with Symbols.
  27. Addresses []uintptr
  28. // Cookies specifies arbitrary values that can be fetched from an eBPF
  29. // program via `bpf_get_attach_cookie()`.
  30. //
  31. // If set, its length should be equal to the length of Symbols or Addresses.
  32. // Each Cookie is assigned to the Symbol or Address specified at the
  33. // corresponding slice index.
  34. Cookies []uint64
  35. }
  36. // KprobeMulti attaches the given eBPF program to the entry point of a given set
  37. // of kernel symbols.
  38. //
  39. // The difference with Kprobe() is that multi-kprobe accomplishes this in a
  40. // single system call, making it significantly faster than attaching many
  41. // probes one at a time.
  42. //
  43. // Requires at least Linux 5.18.
  44. func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
  45. return kprobeMulti(prog, opts, 0)
  46. }
  47. // KretprobeMulti attaches the given eBPF program to the return point of a given
  48. // set of kernel symbols.
  49. //
  50. // The difference with Kretprobe() is that multi-kprobe accomplishes this in a
  51. // single system call, making it significantly faster than attaching many
  52. // probes one at a time.
  53. //
  54. // Requires at least Linux 5.18.
  55. func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
  56. return kprobeMulti(prog, opts, unix.BPF_F_KPROBE_MULTI_RETURN)
  57. }
  58. func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) {
  59. if prog == nil {
  60. return nil, errors.New("cannot attach a nil program")
  61. }
  62. syms := uint32(len(opts.Symbols))
  63. addrs := uint32(len(opts.Addresses))
  64. cookies := uint32(len(opts.Cookies))
  65. if syms == 0 && addrs == 0 {
  66. return nil, fmt.Errorf("one of Symbols or Addresses is required: %w", errInvalidInput)
  67. }
  68. if syms != 0 && addrs != 0 {
  69. return nil, fmt.Errorf("Symbols and Addresses are mutually exclusive: %w", errInvalidInput)
  70. }
  71. if cookies > 0 && cookies != syms && cookies != addrs {
  72. return nil, fmt.Errorf("Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput)
  73. }
  74. if err := haveBPFLinkKprobeMulti(); err != nil {
  75. return nil, err
  76. }
  77. attr := &sys.LinkCreateKprobeMultiAttr{
  78. ProgFd: uint32(prog.FD()),
  79. AttachType: sys.BPF_TRACE_KPROBE_MULTI,
  80. KprobeMultiFlags: flags,
  81. }
  82. switch {
  83. case syms != 0:
  84. attr.Count = syms
  85. attr.Syms = sys.NewStringSlicePointer(opts.Symbols)
  86. case addrs != 0:
  87. attr.Count = addrs
  88. attr.Addrs = sys.NewPointer(unsafe.Pointer(&opts.Addresses[0]))
  89. }
  90. if cookies != 0 {
  91. attr.Cookies = sys.NewPointer(unsafe.Pointer(&opts.Cookies[0]))
  92. }
  93. fd, err := sys.LinkCreateKprobeMulti(attr)
  94. if errors.Is(err, unix.ESRCH) {
  95. return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist)
  96. }
  97. if errors.Is(err, unix.EINVAL) {
  98. return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not AttachTraceKprobeMulti?)", err)
  99. }
  100. if err != nil {
  101. return nil, err
  102. }
  103. return &kprobeMultiLink{RawLink{fd, ""}}, nil
  104. }
  105. type kprobeMultiLink struct {
  106. RawLink
  107. }
  108. var _ Link = (*kprobeMultiLink)(nil)
  109. func (kml *kprobeMultiLink) Update(prog *ebpf.Program) error {
  110. return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported)
  111. }
  112. func (kml *kprobeMultiLink) Pin(string) error {
  113. return fmt.Errorf("pin kprobe_multi: %w", ErrNotSupported)
  114. }
  115. func (kml *kprobeMultiLink) Unpin() error {
  116. return fmt.Errorf("unpin kprobe_multi: %w", ErrNotSupported)
  117. }
  118. var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5.18", func() error {
  119. prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
  120. Name: "probe_kpm_link",
  121. Type: ebpf.Kprobe,
  122. Instructions: asm.Instructions{
  123. asm.Mov.Imm(asm.R0, 0),
  124. asm.Return(),
  125. },
  126. AttachType: ebpf.AttachTraceKprobeMulti,
  127. License: "MIT",
  128. })
  129. if errors.Is(err, unix.E2BIG) {
  130. // Kernel doesn't support AttachType field.
  131. return internal.ErrNotSupported
  132. }
  133. if err != nil {
  134. return err
  135. }
  136. defer prog.Close()
  137. fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
  138. ProgFd: uint32(prog.FD()),
  139. AttachType: sys.BPF_TRACE_KPROBE_MULTI,
  140. Count: 1,
  141. Syms: sys.NewStringSlicePointer([]string{"vprintk"}),
  142. })
  143. switch {
  144. case errors.Is(err, unix.EINVAL):
  145. return internal.ErrNotSupported
  146. // If CONFIG_FPROBE isn't set.
  147. case errors.Is(err, unix.EOPNOTSUPP):
  148. return internal.ErrNotSupported
  149. case err != nil:
  150. return err
  151. }
  152. fd.Close()
  153. return nil
  154. })