summaryrefslogtreecommitdiff
path: root/vendor/github.com/cilium/ebpf/link
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cilium/ebpf/link')
-rw-r--r--vendor/github.com/cilium/ebpf/link/cgroup.go24
-rw-r--r--vendor/github.com/cilium/ebpf/link/iter.go86
-rw-r--r--vendor/github.com/cilium/ebpf/link/kprobe.go568
-rw-r--r--vendor/github.com/cilium/ebpf/link/link.go251
-rw-r--r--vendor/github.com/cilium/ebpf/link/netns.go28
-rw-r--r--vendor/github.com/cilium/ebpf/link/perf_event.go394
-rw-r--r--vendor/github.com/cilium/ebpf/link/platform.go25
-rw-r--r--vendor/github.com/cilium/ebpf/link/program.go14
-rw-r--r--vendor/github.com/cilium/ebpf/link/raw_tracepoint.go62
-rw-r--r--vendor/github.com/cilium/ebpf/link/socket_filter.go40
-rw-r--r--vendor/github.com/cilium/ebpf/link/syscalls.go108
-rw-r--r--vendor/github.com/cilium/ebpf/link/tracepoint.go77
-rw-r--r--vendor/github.com/cilium/ebpf/link/tracing.go141
-rw-r--r--vendor/github.com/cilium/ebpf/link/uprobe.go373
-rw-r--r--vendor/github.com/cilium/ebpf/link/xdp.go54
15 files changed, 1971 insertions, 274 deletions
diff --git a/vendor/github.com/cilium/ebpf/link/cgroup.go b/vendor/github.com/cilium/ebpf/link/cgroup.go
index 16a943930..003b0638e 100644
--- a/vendor/github.com/cilium/ebpf/link/cgroup.go
+++ b/vendor/github.com/cilium/ebpf/link/cgroup.go
@@ -56,16 +56,6 @@ func AttachCgroup(opts CgroupOptions) (Link, error) {
return cg, nil
}
-// LoadPinnedCgroup loads a pinned cgroup from a bpffs.
-func LoadPinnedCgroup(fileName string) (Link, error) {
- link, err := LoadPinnedRawLink(fileName)
- if err != nil {
- return nil, err
- }
-
- return &linkCgroup{link}, nil
-}
-
type progAttachCgroup struct {
cgroup *os.File
current *ebpf.Program
@@ -147,14 +137,20 @@ func (cg *progAttachCgroup) Pin(string) error {
return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
}
+func (cg *progAttachCgroup) Unpin() error {
+ return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
+}
+
+func (cg *progAttachCgroup) Info() (*Info, error) {
+ return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported)
+}
+
type linkCgroup struct {
- *RawLink
+ RawLink
}
var _ Link = (*linkCgroup)(nil)
-func (cg *linkCgroup) isLink() {}
-
func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) {
link, err := AttachRawLink(RawLinkOptions{
Target: int(cgroup.Fd()),
@@ -165,5 +161,5 @@ func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program)
return nil, err
}
- return &linkCgroup{link}, err
+ return &linkCgroup{*link}, err
}
diff --git a/vendor/github.com/cilium/ebpf/link/iter.go b/vendor/github.com/cilium/ebpf/link/iter.go
index 2b5f2846a..d2b32ef33 100644
--- a/vendor/github.com/cilium/ebpf/link/iter.go
+++ b/vendor/github.com/cilium/ebpf/link/iter.go
@@ -3,8 +3,10 @@ package link
import (
"fmt"
"io"
+ "unsafe"
"github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/internal/sys"
)
type IterOptions struct {
@@ -15,77 +17,69 @@ type IterOptions struct {
// AttachTo requires the kernel to include BTF of itself,
// and it to be compiled with a recent pahole (>= 1.16).
Program *ebpf.Program
+
+ // Map specifies the target map for bpf_map_elem and sockmap iterators.
+ // It may be nil.
+ Map *ebpf.Map
}
// AttachIter attaches a BPF seq_file iterator.
func AttachIter(opts IterOptions) (*Iter, error) {
- link, err := AttachRawLink(RawLinkOptions{
- Program: opts.Program,
- Attach: ebpf.AttachTraceIter,
- })
- if err != nil {
- return nil, fmt.Errorf("can't link iterator: %w", err)
+ if err := haveBPFLink(); err != nil {
+ return nil, err
}
- return &Iter{link}, err
-}
+ progFd := opts.Program.FD()
+ if progFd < 0 {
+ return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
+ }
+
+ var info bpfIterLinkInfoMap
+ if opts.Map != nil {
+ mapFd := opts.Map.FD()
+ if mapFd < 0 {
+ return nil, fmt.Errorf("invalid map: %w", sys.ErrClosedFd)
+ }
+ info.map_fd = uint32(mapFd)
+ }
+
+ attr := sys.LinkCreateIterAttr{
+ ProgFd: uint32(progFd),
+ AttachType: sys.AttachType(ebpf.AttachTraceIter),
+ IterInfo: sys.NewPointer(unsafe.Pointer(&info)),
+ IterInfoLen: uint32(unsafe.Sizeof(info)),
+ }
-// LoadPinnedIter loads a pinned iterator from a bpffs.
-func LoadPinnedIter(fileName string) (*Iter, error) {
- link, err := LoadPinnedRawLink(fileName)
+ fd, err := sys.LinkCreateIter(&attr)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("can't link iterator: %w", err)
}
- return &Iter{link}, err
+ return &Iter{RawLink{fd, ""}}, err
}
// Iter represents an attached bpf_iter.
type Iter struct {
- link *RawLink
-}
-
-var _ Link = (*Iter)(nil)
-
-func (it *Iter) isLink() {}
-
-// FD returns the underlying file descriptor.
-func (it *Iter) FD() int {
- return it.link.FD()
-}
-
-// Close implements Link.
-func (it *Iter) Close() error {
- return it.link.Close()
-}
-
-// Pin implements Link.
-func (it *Iter) Pin(fileName string) error {
- return it.link.Pin(fileName)
-}
-
-// Update implements Link.
-func (it *Iter) Update(new *ebpf.Program) error {
- return it.link.Update(new)
+ RawLink
}
// Open creates a new instance of the iterator.
//
// Reading from the returned reader triggers the BPF program.
func (it *Iter) Open() (io.ReadCloser, error) {
- linkFd, err := it.link.fd.Value()
- if err != nil {
- return nil, err
+ attr := &sys.IterCreateAttr{
+ LinkFd: it.fd.Uint(),
}
- attr := &bpfIterCreateAttr{
- linkFd: linkFd,
- }
-
- fd, err := bpfIterCreate(attr)
+ fd, err := sys.IterCreate(attr)
if err != nil {
return nil, fmt.Errorf("can't create iterator: %w", err)
}
return fd.File("bpf_iter"), nil
}
+
+// union bpf_iter_link_info.map
+type bpfIterLinkInfoMap struct {
+ map_fd uint32
+}
diff --git a/vendor/github.com/cilium/ebpf/link/kprobe.go b/vendor/github.com/cilium/ebpf/link/kprobe.go
new file mode 100644
index 000000000..fdf622a0c
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/kprobe.go
@@ -0,0 +1,568 @@
+package link
+
+import (
+ "bytes"
+ "crypto/rand"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
+ "syscall"
+ "unsafe"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/internal/sys"
+ "github.com/cilium/ebpf/internal/unix"
+)
+
+var (
+ kprobeEventsPath = filepath.Join(tracefsPath, "kprobe_events")
+
+ kprobeRetprobeBit = struct {
+ once sync.Once
+ value uint64
+ err error
+ }{}
+)
+
+type probeType uint8
+
+type probeArgs struct {
+ symbol, group, path string
+ offset, refCtrOffset, cookie uint64
+ pid int
+ ret bool
+}
+
+// KprobeOptions defines additional parameters that will be used
+// when loading Kprobes.
+type KprobeOptions struct {
+ // Arbitrary value that can be fetched from an eBPF program
+ // via `bpf_get_attach_cookie()`.
+ //
+ // Needs kernel 5.15+.
+ Cookie uint64
+ // Offset of the kprobe relative to the traced symbol.
+ // Can be used to insert kprobes at arbitrary offsets in kernel functions,
+ // e.g. in places where functions have been inlined.
+ Offset uint64
+}
+
+const (
+ kprobeType probeType = iota
+ uprobeType
+)
+
+func (pt probeType) String() string {
+ if pt == kprobeType {
+ return "kprobe"
+ }
+ return "uprobe"
+}
+
+func (pt probeType) EventsPath() string {
+ if pt == kprobeType {
+ return kprobeEventsPath
+ }
+ return uprobeEventsPath
+}
+
+func (pt probeType) PerfEventType(ret bool) perfEventType {
+ if pt == kprobeType {
+ if ret {
+ return kretprobeEvent
+ }
+ return kprobeEvent
+ }
+ if ret {
+ return uretprobeEvent
+ }
+ return uprobeEvent
+}
+
+func (pt probeType) RetprobeBit() (uint64, error) {
+ if pt == kprobeType {
+ return kretprobeBit()
+ }
+ return uretprobeBit()
+}
+
+// Kprobe attaches the given eBPF program to a perf event that fires when the
+// given kernel symbol starts executing. See /proc/kallsyms for available
+// symbols. For example, printk():
+//
+// kp, err := Kprobe("printk", prog, nil)
+//
+// Losing the reference to the resulting Link (kp) will close the Kprobe
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {
+ k, err := kprobe(symbol, prog, opts, false)
+ if err != nil {
+ return nil, err
+ }
+
+ lnk, err := attachPerfEvent(k, prog)
+ if err != nil {
+ k.Close()
+ return nil, err
+ }
+
+ return lnk, nil
+}
+
+// Kretprobe attaches the given eBPF program to a perf event that fires right
+// before the given kernel symbol exits, with the function stack left intact.
+// See /proc/kallsyms for available symbols. For example, printk():
+//
+// kp, err := Kretprobe("printk", prog, nil)
+//
+// Losing the reference to the resulting Link (kp) will close the Kretprobe
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+func Kretprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {
+ k, err := kprobe(symbol, prog, opts, true)
+ if err != nil {
+ return nil, err
+ }
+
+ lnk, err := attachPerfEvent(k, prog)
+ if err != nil {
+ k.Close()
+ return nil, err
+ }
+
+ return lnk, nil
+}
+
+// isValidKprobeSymbol implements the equivalent of a regex match
+// against "^[a-zA-Z_][0-9a-zA-Z_.]*$".
+func isValidKprobeSymbol(s string) bool {
+ if len(s) < 1 {
+ return false
+ }
+
+ for i, c := range []byte(s) {
+ switch {
+ case c >= 'a' && c <= 'z':
+ case c >= 'A' && c <= 'Z':
+ case c == '_':
+ case i > 0 && c >= '0' && c <= '9':
+
+ // Allow `.` in symbol name. GCC-compiled kernel may change symbol name
+ // to have a `.isra.$n` suffix, like `udp_send_skb.isra.52`.
+ // See: https://gcc.gnu.org/gcc-10/changes.html
+ case i > 0 && c == '.':
+
+ default:
+ return false
+ }
+ }
+
+ return true
+}
+
+// kprobe opens a perf event on the given symbol and attaches prog to it.
+// If ret is true, create a kretprobe.
+func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (*perfEvent, error) {
+ if symbol == "" {
+ return nil, fmt.Errorf("symbol name cannot be empty: %w", errInvalidInput)
+ }
+ if prog == nil {
+ return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+ }
+ if !isValidKprobeSymbol(symbol) {
+ return nil, fmt.Errorf("symbol '%s' must be a valid symbol in /proc/kallsyms: %w", symbol, errInvalidInput)
+ }
+ if prog.Type() != ebpf.Kprobe {
+ return nil, fmt.Errorf("eBPF program type %s is not a Kprobe: %w", prog.Type(), errInvalidInput)
+ }
+
+ args := probeArgs{
+ pid: perfAllThreads,
+ symbol: symbol,
+ ret: ret,
+ }
+
+ if opts != nil {
+ args.cookie = opts.Cookie
+ args.offset = opts.Offset
+ }
+
+ // Use kprobe PMU if the kernel has it available.
+ tp, err := pmuKprobe(args)
+ if errors.Is(err, os.ErrNotExist) {
+ args.symbol = platformPrefix(symbol)
+ tp, err = pmuKprobe(args)
+ }
+ if err == nil {
+ return tp, nil
+ }
+ if err != nil && !errors.Is(err, ErrNotSupported) {
+ return nil, fmt.Errorf("creating perf_kprobe PMU: %w", err)
+ }
+
+ // Use tracefs if kprobe PMU is missing.
+ args.symbol = symbol
+ tp, err = tracefsKprobe(args)
+ if errors.Is(err, os.ErrNotExist) {
+ args.symbol = platformPrefix(symbol)
+ tp, err = tracefsKprobe(args)
+ }
+ if err != nil {
+ return nil, fmt.Errorf("creating trace event '%s' in tracefs: %w", symbol, err)
+ }
+
+ return tp, nil
+}
+
+// pmuKprobe opens a perf event based on the kprobe PMU.
+// Returns os.ErrNotExist if the given symbol does not exist in the kernel.
+func pmuKprobe(args probeArgs) (*perfEvent, error) {
+ return pmuProbe(kprobeType, args)
+}
+
+// pmuProbe opens a perf event based on a Performance Monitoring Unit.
+//
+// Requires at least a 4.17 kernel.
+// e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU"
+// 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU"
+//
+// Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU
+func pmuProbe(typ probeType, args probeArgs) (*perfEvent, error) {
+ // Getting the PMU type will fail if the kernel doesn't support
+ // the perf_[k,u]probe PMU.
+ et, err := getPMUEventType(typ)
+ if err != nil {
+ return nil, err
+ }
+
+ var config uint64
+ if args.ret {
+ bit, err := typ.RetprobeBit()
+ if err != nil {
+ return nil, err
+ }
+ config |= 1 << bit
+ }
+
+ var (
+ attr unix.PerfEventAttr
+ sp unsafe.Pointer
+ )
+ switch typ {
+ case kprobeType:
+ // Create a pointer to a NUL-terminated string for the kernel.
+ sp, err = unsafeStringPtr(args.symbol)
+ if err != nil {
+ return nil, err
+ }
+
+ attr = unix.PerfEventAttr{
+ // The minimum size required for PMU kprobes is PERF_ATTR_SIZE_VER1,
+ // since it added the config2 (Ext2) field. Use Ext2 as probe_offset.
+ Size: unix.PERF_ATTR_SIZE_VER1,
+ Type: uint32(et), // PMU event type read from sysfs
+ Ext1: uint64(uintptr(sp)), // Kernel symbol to trace
+ Ext2: args.offset, // Kernel symbol offset
+ Config: config, // Retprobe flag
+ }
+ case uprobeType:
+ sp, err = unsafeStringPtr(args.path)
+ if err != nil {
+ return nil, err
+ }
+
+ if args.refCtrOffset != 0 {
+ config |= args.refCtrOffset << uprobeRefCtrOffsetShift
+ }
+
+ attr = unix.PerfEventAttr{
+ // The minimum size required for PMU uprobes is PERF_ATTR_SIZE_VER1,
+ // since it added the config2 (Ext2) field. The Size field controls the
+ // size of the internal buffer the kernel allocates for reading the
+ // perf_event_attr argument from userspace.
+ Size: unix.PERF_ATTR_SIZE_VER1,
+ Type: uint32(et), // PMU event type read from sysfs
+ Ext1: uint64(uintptr(sp)), // Uprobe path
+ Ext2: args.offset, // Uprobe offset
+ Config: config, // RefCtrOffset, Retprobe flag
+ }
+ }
+
+ rawFd, err := unix.PerfEventOpen(&attr, args.pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
+
+ // On some old kernels, kprobe PMU doesn't allow `.` in symbol names and
+ // return -EINVAL. Return ErrNotSupported to allow falling back to tracefs.
+ // https://github.com/torvalds/linux/blob/94710cac0ef4/kernel/trace/trace_kprobe.c#L340-L343
+ if errors.Is(err, unix.EINVAL) && strings.Contains(args.symbol, ".") {
+ return nil, fmt.Errorf("symbol '%s+%#x': older kernels don't accept dots: %w", args.symbol, args.offset, ErrNotSupported)
+ }
+ // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
+ // when trying to create a kretprobe for a missing symbol. Make sure ENOENT
+ // is returned to the caller.
+ if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
+ return nil, fmt.Errorf("symbol '%s+%#x' not found: %w", args.symbol, args.offset, os.ErrNotExist)
+ }
+ // Since commit ab105a4fb894, -EILSEQ is returned when a kprobe sym+offset is resolved
+ // to an invalid insn boundary.
+ if errors.Is(err, syscall.EILSEQ) {
+ return nil, fmt.Errorf("symbol '%s+%#x' not found (bad insn boundary): %w", args.symbol, args.offset, os.ErrNotExist)
+ }
+ // Since at least commit cb9a19fe4aa51, ENOTSUPP is returned
+ // when attempting to set a uprobe on a trap instruction.
+ if errors.Is(err, unix.ENOTSUPP) {
+ return nil, fmt.Errorf("failed setting uprobe on offset %#x (possible trap insn): %w", args.offset, err)
+ }
+ if err != nil {
+ return nil, fmt.Errorf("opening perf event: %w", err)
+ }
+
+ // Ensure the string pointer is not collected before PerfEventOpen returns.
+ runtime.KeepAlive(sp)
+
+ fd, err := sys.NewFD(rawFd)
+ if err != nil {
+ return nil, err
+ }
+
+ // Kernel has perf_[k,u]probe PMU available, initialize perf event.
+ return &perfEvent{
+ typ: typ.PerfEventType(args.ret),
+ name: args.symbol,
+ pmuID: et,
+ cookie: args.cookie,
+ fd: fd,
+ }, nil
+}
+
+// tracefsKprobe creates a Kprobe tracefs entry.
+func tracefsKprobe(args probeArgs) (*perfEvent, error) {
+ return tracefsProbe(kprobeType, args)
+}
+
+// tracefsProbe creates a trace event by writing an entry to <tracefs>/[k,u]probe_events.
+// A new trace event group name is generated on every call to support creating
+// multiple trace events for the same kernel or userspace symbol.
+// Path and offset are only set in the case of uprobe(s) and are used to set
+// the executable/library path on the filesystem and the offset where the probe is inserted.
+// A perf event is then opened on the newly-created trace event and returned to the caller.
+func tracefsProbe(typ probeType, args probeArgs) (_ *perfEvent, err error) {
+ // Generate a random string for each trace event we attempt to create.
+ // This value is used as the 'group' token in tracefs to allow creating
+ // multiple kprobe trace events with the same name.
+ group, err := randomGroup("ebpf")
+ if err != nil {
+ return nil, fmt.Errorf("randomizing group name: %w", err)
+ }
+ args.group = group
+
+ // Before attempting to create a trace event through tracefs,
+ // check if an event with the same group and name already exists.
+ // Kernels 4.x and earlier don't return os.ErrExist on writing a duplicate
+ // entry, so we need to rely on reads for detecting uniqueness.
+ _, err = getTraceEventID(group, args.symbol)
+ if err == nil {
+ return nil, fmt.Errorf("trace event already exists: %s/%s", group, args.symbol)
+ }
+ if err != nil && !errors.Is(err, os.ErrNotExist) {
+ return nil, fmt.Errorf("checking trace event %s/%s: %w", group, args.symbol, err)
+ }
+
+ // Create the [k,u]probe trace event using tracefs.
+ if err := createTraceFSProbeEvent(typ, args); err != nil {
+ return nil, fmt.Errorf("creating probe entry on tracefs: %w", err)
+ }
+ defer func() {
+ if err != nil {
+ // Make sure we clean up the created tracefs event when we return error.
+ // If a livepatch handler is already active on the symbol, the write to
+ // tracefs will succeed, a trace event will show up, but creating the
+ // perf event will fail with EBUSY.
+ _ = closeTraceFSProbeEvent(typ, args.group, args.symbol)
+ }
+ }()
+
+ // Get the newly-created trace event's id.
+ tid, err := getTraceEventID(group, args.symbol)
+ if err != nil {
+ return nil, fmt.Errorf("getting trace event id: %w", err)
+ }
+
+ // Kprobes are ephemeral tracepoints and share the same perf event type.
+ fd, err := openTracepointPerfEvent(tid, args.pid)
+ if err != nil {
+ return nil, err
+ }
+
+ return &perfEvent{
+ typ: typ.PerfEventType(args.ret),
+ group: group,
+ name: args.symbol,
+ tracefsID: tid,
+ cookie: args.cookie,
+ fd: fd,
+ }, nil
+}
+
+// createTraceFSProbeEvent creates a new ephemeral trace event by writing to
+// <tracefs>/[k,u]probe_events. Returns os.ErrNotExist if symbol is not a valid
+// kernel symbol, or if it is not traceable with kprobes. Returns os.ErrExist
+// if a probe with the same group and symbol already exists.
+func createTraceFSProbeEvent(typ probeType, args probeArgs) error {
+ // Open the kprobe_events file in tracefs.
+ f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666)
+ if err != nil {
+ return fmt.Errorf("error opening '%s': %w", typ.EventsPath(), err)
+ }
+ defer f.Close()
+
+ var pe, token string
+ switch typ {
+ case kprobeType:
+ // The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt):
+ // p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
+ // r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
+ // -:[GRP/]EVENT : Clear a probe
+ //
+ // Some examples:
+ // r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy
+ // p:ebpf_5678/p_my_kprobe __x64_sys_execve
+ //
+ // Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the
+ // kernel default to NR_CPUS. This is desired in most eBPF cases since
+ // subsampling or rate limiting logic can be more accurately implemented in
+ // the eBPF program itself.
+ // See Documentation/kprobes.txt for more details.
+ token = kprobeToken(args)
+ pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret), args.group, sanitizeSymbol(args.symbol), token)
+ case uprobeType:
+ // The uprobe_events syntax is as follows:
+ // p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe
+ // r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return probe
+ // -:[GRP/]EVENT : Clear a probe
+ //
+ // Some examples:
+ // r:ebpf_1234/readline /bin/bash:0x12345
+ // p:ebpf_5678/main_mySymbol /bin/mybin:0x12345(0x123)
+ //
+ // See Documentation/trace/uprobetracer.txt for more details.
+ token = uprobeToken(args)
+ pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret), args.group, args.symbol, token)
+ }
+ _, err = f.WriteString(pe)
+ // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
+ // when trying to create a kretprobe for a missing symbol. Make sure ENOENT
+ // is returned to the caller.
+ // EINVAL is also returned on pre-5.2 kernels when the `SYM[+offs]` token
+ // is resolved to an invalid insn boundary.
+ if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
+ return fmt.Errorf("token %s: %w", token, os.ErrNotExist)
+ }
+ // Since commit ab105a4fb894, -EILSEQ is returned when a kprobe sym+offset is resolved
+ // to an invalid insn boundary.
+ if errors.Is(err, syscall.EILSEQ) {
+ return fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist)
+ }
+ // ERANGE is returned when the `SYM[+offs]` token is too big and cannot
+ // be resolved.
+ if errors.Is(err, syscall.ERANGE) {
+ return fmt.Errorf("token %s: offset too big: %w", token, os.ErrNotExist)
+ }
+ if err != nil {
+ return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err)
+ }
+
+ return nil
+}
+
+// closeTraceFSProbeEvent removes the [k,u]probe with the given type, group and symbol
+// from <tracefs>/[k,u]probe_events.
+func closeTraceFSProbeEvent(typ probeType, group, symbol string) error {
+ f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666)
+ if err != nil {
+ return fmt.Errorf("error opening %s: %w", typ.EventsPath(), err)
+ }
+ defer f.Close()
+
+ // See [k,u]probe_events syntax above. The probe type does not need to be specified
+ // for removals.
+ pe := fmt.Sprintf("-:%s/%s", group, sanitizeSymbol(symbol))
+ if _, err = f.WriteString(pe); err != nil {
+ return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err)
+ }
+
+ return nil
+}
+
+// randomGroup generates a pseudorandom string for use as a tracefs group name.
+// Returns an error when the output string would exceed 63 characters (kernel
+// limitation), when rand.Read() fails or when prefix contains characters not
+// allowed by isValidTraceID.
+func randomGroup(prefix string) (string, error) {
+ if !isValidTraceID(prefix) {
+ return "", fmt.Errorf("prefix '%s' must be alphanumeric or underscore: %w", prefix, errInvalidInput)
+ }
+
+ b := make([]byte, 8)
+ if _, err := rand.Read(b); err != nil {
+ return "", fmt.Errorf("reading random bytes: %w", err)
+ }
+
+ group := fmt.Sprintf("%s_%x", prefix, b)
+ if len(group) > 63 {
+ return "", fmt.Errorf("group name '%s' cannot be longer than 63 characters: %w", group, errInvalidInput)
+ }
+
+ return group, nil
+}
+
+func probePrefix(ret bool) string {
+ if ret {
+ return "r"
+ }
+ return "p"
+}
+
+// determineRetprobeBit reads a Performance Monitoring Unit's retprobe bit
+// from /sys/bus/event_source/devices/<pmu>/format/retprobe.
+func determineRetprobeBit(typ probeType) (uint64, error) {
+ p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe")
+
+ data, err := os.ReadFile(p)
+ if err != nil {
+ return 0, err
+ }
+
+ var rp uint64
+ n, err := fmt.Sscanf(string(bytes.TrimSpace(data)), "config:%d", &rp)
+ if err != nil {
+ return 0, fmt.Errorf("parse retprobe bit: %w", err)
+ }
+ if n != 1 {
+ return 0, fmt.Errorf("parse retprobe bit: expected 1 item, got %d", n)
+ }
+
+ return rp, nil
+}
+
+func kretprobeBit() (uint64, error) {
+ kprobeRetprobeBit.once.Do(func() {
+ kprobeRetprobeBit.value, kprobeRetprobeBit.err = determineRetprobeBit(kprobeType)
+ })
+ return kprobeRetprobeBit.value, kprobeRetprobeBit.err
+}
+
+// kprobeToken creates the SYM[+offs] token for the tracefs api.
+func kprobeToken(args probeArgs) string {
+ po := args.symbol
+
+ if args.offset != 0 {
+ po += fmt.Sprintf("+%#x", args.offset)
+ }
+
+ return po
+}
diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go
index 48f1a5529..067d0101a 100644
--- a/vendor/github.com/cilium/ebpf/link/link.go
+++ b/vendor/github.com/cilium/ebpf/link/link.go
@@ -1,11 +1,14 @@
package link
import (
+ "bytes"
+ "encoding/binary"
"fmt"
- "unsafe"
"github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/sys"
)
var ErrNotSupported = internal.ErrNotSupported
@@ -22,19 +25,65 @@ type Link interface {
// May return an error wrapping ErrNotSupported.
Pin(string) error
+ // Undo a previous call to Pin.
+ //
+ // May return an error wrapping ErrNotSupported.
+ Unpin() error
+
// Close frees resources.
//
- // The link will be broken unless it has been pinned. A link
- // may continue past the lifetime of the process if Close is
+ // The link will be broken unless it has been successfully pinned.
+ // A link may continue past the lifetime of the process if Close is
// not called.
Close() error
+ // Info returns metadata on a link.
+ //
+ // May return an error wrapping ErrNotSupported.
+ Info() (*Info, error)
+
// Prevent external users from implementing this interface.
isLink()
}
+// LoadPinnedLink loads a link that was persisted into a bpffs.
+func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
+ raw, err := loadPinnedRawLink(fileName, opts)
+ if err != nil {
+ return nil, err
+ }
+
+ return wrapRawLink(raw)
+}
+
+// wrap a RawLink in a more specific type if possible.
+//
+// The function takes ownership of raw and closes it on error.
+func wrapRawLink(raw *RawLink) (Link, error) {
+ info, err := raw.Info()
+ if err != nil {
+ raw.Close()
+ return nil, err
+ }
+
+ switch info.Type {
+ case RawTracepointType:
+ return &rawTracepoint{*raw}, nil
+ case TracingType:
+ return &tracing{*raw}, nil
+ case CgroupType:
+ return &linkCgroup{*raw}, nil
+ case IterType:
+ return &Iter{*raw}, nil
+ case NetNsType:
+ return &NetNsLink{*raw}, nil
+ default:
+ return raw, nil
+ }
+}
+
// ID uniquely identifies a BPF link.
-type ID uint32
+type ID = sys.LinkID
// RawLinkOptions control the creation of a raw link.
type RawLinkOptions struct {
@@ -44,13 +93,55 @@ type RawLinkOptions struct {
Program *ebpf.Program
// Attach must match the attach type of Program.
Attach ebpf.AttachType
+ // BTF is the BTF of the attachment target.
+ BTF btf.TypeID
+ // Flags control the attach behaviour.
+ Flags uint32
}
-// RawLinkInfo contains metadata on a link.
-type RawLinkInfo struct {
+// Info contains metadata on a link.
+type Info struct {
Type Type
ID ID
Program ebpf.ProgramID
+ extra interface{}
+}
+
+type TracingInfo sys.TracingLinkInfo
+type CgroupInfo sys.CgroupLinkInfo
+type NetNsInfo sys.NetNsLinkInfo
+type XDPInfo sys.XDPLinkInfo
+
+// Tracing returns tracing type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) Tracing() *TracingInfo {
+ e, _ := r.extra.(*TracingInfo)
+ return e
+}
+
+// Cgroup returns cgroup type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) Cgroup() *CgroupInfo {
+ e, _ := r.extra.(*CgroupInfo)
+ return e
+}
+
+// NetNs returns netns type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) NetNs() *NetNsInfo {
+ e, _ := r.extra.(*NetNsInfo)
+ return e
+}
+
+// ExtraNetNs returns XDP type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) XDP() *XDPInfo {
+ e, _ := r.extra.(*XDPInfo)
+ return e
}
// RawLink is the low-level API to bpf_link.
@@ -58,7 +149,8 @@ type RawLinkInfo struct {
// You should consider using the higher level interfaces in this
// package instead.
type RawLink struct {
- fd *internal.FD
+ fd *sys.FD
+ pinnedPath string
}
// AttachRawLink creates a raw link.
@@ -68,66 +160,46 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
}
if opts.Target < 0 {
- return nil, fmt.Errorf("invalid target: %s", internal.ErrClosedFd)
+ return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd)
}
progFd := opts.Program.FD()
if progFd < 0 {
- return nil, fmt.Errorf("invalid program: %s", internal.ErrClosedFd)
+ return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
}
- attr := bpfLinkCreateAttr{
- targetFd: uint32(opts.Target),
- progFd: uint32(progFd),
- attachType: opts.Attach,
+ attr := sys.LinkCreateAttr{
+ TargetFd: uint32(opts.Target),
+ ProgFd: uint32(progFd),
+ AttachType: sys.AttachType(opts.Attach),
+ TargetBtfId: uint32(opts.BTF),
+ Flags: opts.Flags,
}
- fd, err := bpfLinkCreate(&attr)
+ fd, err := sys.LinkCreate(&attr)
if err != nil {
return nil, fmt.Errorf("can't create link: %s", err)
}
- return &RawLink{fd}, nil
-}
-
-// LoadPinnedRawLink loads a persisted link from a bpffs.
-func LoadPinnedRawLink(fileName string) (*RawLink, error) {
- return loadPinnedRawLink(fileName, UnspecifiedType)
+ return &RawLink{fd, ""}, nil
}
-func loadPinnedRawLink(fileName string, typ Type) (*RawLink, error) {
- fd, err := internal.BPFObjGet(fileName)
- if err != nil {
- return nil, fmt.Errorf("load pinned link: %s", err)
- }
-
- link := &RawLink{fd}
- if typ == UnspecifiedType {
- return link, nil
- }
-
- info, err := link.Info()
+func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) {
+ fd, err := sys.ObjGet(&sys.ObjGetAttr{
+ Pathname: sys.NewStringPointer(fileName),
+ FileFlags: opts.Marshal(),
+ })
if err != nil {
- link.Close()
- return nil, fmt.Errorf("get pinned link info: %s", err)
- }
-
- if info.Type != typ {
- link.Close()
- return nil, fmt.Errorf("link type %v doesn't match %v", info.Type, typ)
+ return nil, fmt.Errorf("load pinned link: %w", err)
}
- return link, nil
+ return &RawLink{fd, fileName}, nil
}
func (l *RawLink) isLink() {}
// FD returns the raw file descriptor.
func (l *RawLink) FD() int {
- fd, err := l.fd.Value()
- if err != nil {
- return -1
- }
- return int(fd)
+ return l.fd.Int()
}
// Close breaks the link.
@@ -142,13 +214,23 @@ func (l *RawLink) Close() error {
// Calling Close on a pinned Link will not break the link
// until the pin is removed.
func (l *RawLink) Pin(fileName string) error {
- if err := internal.BPFObjPin(fileName, l.fd); err != nil {
- return fmt.Errorf("can't pin link: %s", err)
+ if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil {
+ return err
}
+ l.pinnedPath = fileName
return nil
}
-// Update implements Link.
+// Unpin implements the Link interface.
+func (l *RawLink) Unpin() error {
+ if err := internal.Unpin(l.pinnedPath); err != nil {
+ return err
+ }
+ l.pinnedPath = ""
+ return nil
+}
+
+// Update implements the Link interface.
func (l *RawLink) Update(new *ebpf.Program) error {
return l.UpdateArgs(RawLinkUpdateOptions{
New: new,
@@ -166,49 +248,66 @@ type RawLinkUpdateOptions struct {
func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error {
newFd := opts.New.FD()
if newFd < 0 {
- return fmt.Errorf("invalid program: %s", internal.ErrClosedFd)
+ return fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
}
var oldFd int
if opts.Old != nil {
oldFd = opts.Old.FD()
if oldFd < 0 {
- return fmt.Errorf("invalid replacement program: %s", internal.ErrClosedFd)
+ return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd)
}
}
- linkFd, err := l.fd.Value()
- if err != nil {
- return fmt.Errorf("can't update link: %s", err)
- }
-
- attr := bpfLinkUpdateAttr{
- linkFd: linkFd,
- newProgFd: uint32(newFd),
- oldProgFd: uint32(oldFd),
- flags: opts.Flags,
+ attr := sys.LinkUpdateAttr{
+ LinkFd: l.fd.Uint(),
+ NewProgFd: uint32(newFd),
+ OldProgFd: uint32(oldFd),
+ Flags: opts.Flags,
}
- return bpfLinkUpdate(&attr)
-}
-
-// struct bpf_link_info
-type bpfLinkInfo struct {
- typ uint32
- id uint32
- prog_id uint32
+ return sys.LinkUpdate(&attr)
}
// Info returns metadata about the link.
-func (l *RawLink) Info() (*RawLinkInfo, error) {
- var info bpfLinkInfo
- err := internal.BPFObjGetInfoByFD(l.fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
- if err != nil {
+func (l *RawLink) Info() (*Info, error) {
+ var info sys.LinkInfo
+
+ if err := sys.ObjInfo(l.fd, &info); err != nil {
return nil, fmt.Errorf("link info: %s", err)
}
- return &RawLinkInfo{
- Type(info.typ),
- ID(info.id),
- ebpf.ProgramID(info.prog_id),
+ var extra interface{}
+ switch info.Type {
+ case CgroupType:
+ extra = &CgroupInfo{}
+ case IterType:
+ // not supported
+ case NetNsType:
+ extra = &NetNsInfo{}
+ case RawTracepointType:
+ // not supported
+ case TracingType:
+ extra = &TracingInfo{}
+ case XDPType:
+ extra = &XDPInfo{}
+ case PerfEventType:
+ // no extra
+ default:
+ return nil, fmt.Errorf("unknown link info type: %d", info.Type)
+ }
+
+ if info.Type != RawTracepointType && info.Type != IterType && info.Type != PerfEventType {
+ buf := bytes.NewReader(info.Extra[:])
+ err := binary.Read(buf, internal.NativeEndian, extra)
+ if err != nil {
+ return nil, fmt.Errorf("can not read extra link info: %w", err)
+ }
+ }
+
+ return &Info{
+ info.Type,
+ info.Id,
+ ebpf.ProgramID(info.ProgId),
+ extra,
}, nil
}
diff --git a/vendor/github.com/cilium/ebpf/link/netns.go b/vendor/github.com/cilium/ebpf/link/netns.go
index 3533ff0fa..344ecced6 100644
--- a/vendor/github.com/cilium/ebpf/link/netns.go
+++ b/vendor/github.com/cilium/ebpf/link/netns.go
@@ -6,14 +6,9 @@ import (
"github.com/cilium/ebpf"
)
-// NetNsInfo contains metadata about a network namespace link.
-type NetNsInfo struct {
- RawLinkInfo
-}
-
// NetNsLink is a program attached to a network namespace.
type NetNsLink struct {
- *RawLink
+ RawLink
}
// AttachNetNs attaches a program to a network namespace.
@@ -37,24 +32,5 @@ func AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) {
return nil, err
}
- return &NetNsLink{link}, nil
-}
-
-// LoadPinnedNetNs loads a network namespace link from bpffs.
-func LoadPinnedNetNs(fileName string) (*NetNsLink, error) {
- link, err := loadPinnedRawLink(fileName, NetNsType)
- if err != nil {
- return nil, err
- }
-
- return &NetNsLink{link}, nil
-}
-
-// Info returns information about the link.
-func (nns *NetNsLink) Info() (*NetNsInfo, error) {
- info, err := nns.RawLink.Info()
- if err != nil {
- return nil, err
- }
- return &NetNsInfo{*info}, nil
+ return &NetNsLink{*link}, nil
}
diff --git a/vendor/github.com/cilium/ebpf/link/perf_event.go b/vendor/github.com/cilium/ebpf/link/perf_event.go
new file mode 100644
index 000000000..0e5bd4791
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/perf_event.go
@@ -0,0 +1,394 @@
+package link
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "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"
+)
+
+// Getting the terminology right is usually the hardest part. For posterity and
+// for staying sane during implementation:
+//
+// - trace event: Representation of a kernel runtime hook. Filesystem entries
+// under <tracefs>/events. Can be tracepoints (static), kprobes or uprobes.
+// Can be instantiated into perf events (see below).
+// - tracepoint: A predetermined hook point in the kernel. Exposed as trace
+// events in (sub)directories under <tracefs>/events. Cannot be closed or
+// removed, they are static.
+// - k(ret)probe: Ephemeral trace events based on entry or exit points of
+// exported kernel symbols. kprobe-based (tracefs) trace events can be
+// created system-wide by writing to the <tracefs>/kprobe_events file, or
+// they can be scoped to the current process by creating PMU perf events.
+// - u(ret)probe: Ephemeral trace events based on user provides ELF binaries
+// and offsets. uprobe-based (tracefs) trace events can be
+// created system-wide by writing to the <tracefs>/uprobe_events file, or
+// they can be scoped to the current process by creating PMU perf events.
+// - perf event: An object instantiated based on an existing trace event or
+// kernel symbol. Referred to by fd in userspace.
+// Exactly one eBPF program can be attached to a perf event. Multiple perf
+// events can be created from a single trace event. Closing a perf event
+// stops any further invocations of the attached eBPF program.
+
+var (
+ tracefsPath = "/sys/kernel/debug/tracing"
+
+ errInvalidInput = errors.New("invalid input")
+)
+
+const (
+ perfAllThreads = -1
+)
+
+type perfEventType uint8
+
+const (
+ tracepointEvent perfEventType = iota
+ kprobeEvent
+ kretprobeEvent
+ uprobeEvent
+ uretprobeEvent
+)
+
+// A perfEvent represents a perf event kernel object. Exactly one eBPF program
+// can be attached to it. It is created based on a tracefs trace event or a
+// Performance Monitoring Unit (PMU).
+type perfEvent struct {
+ // The event type determines the types of programs that can be attached.
+ typ perfEventType
+
+ // Group and name of the tracepoint/kprobe/uprobe.
+ group string
+ name string
+
+ // PMU event ID read from sysfs. Valid IDs are non-zero.
+ pmuID uint64
+ // ID of the trace event read from tracefs. Valid IDs are non-zero.
+ tracefsID uint64
+
+ // User provided arbitrary value.
+ cookie uint64
+
+ // This is the perf event FD.
+ fd *sys.FD
+}
+
+func (pe *perfEvent) Close() error {
+ if err := pe.fd.Close(); err != nil {
+ return fmt.Errorf("closing perf event fd: %w", err)
+ }
+
+ switch pe.typ {
+ case kprobeEvent, kretprobeEvent:
+ // Clean up kprobe tracefs entry.
+ if pe.tracefsID != 0 {
+ return closeTraceFSProbeEvent(kprobeType, pe.group, pe.name)
+ }
+ case uprobeEvent, uretprobeEvent:
+ // Clean up uprobe tracefs entry.
+ if pe.tracefsID != 0 {
+ return closeTraceFSProbeEvent(uprobeType, pe.group, pe.name)
+ }
+ case tracepointEvent:
+ // Tracepoint trace events don't hold any extra resources.
+ return nil
+ }
+
+ return nil
+}
+
+// perfEventLink represents a bpf perf link.
+type perfEventLink struct {
+ RawLink
+ pe *perfEvent
+}
+
+func (pl *perfEventLink) isLink() {}
+
+// Pinning requires the underlying perf event FD to stay open.
+//
+// | PerfEvent FD | BpfLink FD | Works |
+// |--------------|------------|-------|
+// | Open | Open | Yes |
+// | Closed | Open | No |
+// | Open | Closed | No (Pin() -> EINVAL) |
+// | Closed | Closed | No (Pin() -> EINVAL) |
+//
+// There is currently no pretty way to recover the perf event FD
+// when loading a pinned link, so leave as not supported for now.
+func (pl *perfEventLink) Pin(string) error {
+ return fmt.Errorf("perf event link pin: %w", ErrNotSupported)
+}
+
+func (pl *perfEventLink) Unpin() error {
+ return fmt.Errorf("perf event link unpin: %w", ErrNotSupported)
+}
+
+func (pl *perfEventLink) Close() error {
+ if err := pl.pe.Close(); err != nil {
+ return fmt.Errorf("perf event link close: %w", err)
+ }
+ return pl.fd.Close()
+}
+
+func (pl *perfEventLink) Update(prog *ebpf.Program) error {
+ return fmt.Errorf("perf event link update: %w", ErrNotSupported)
+}
+
+// perfEventIoctl implements Link and handles the perf event lifecycle
+// via ioctl().
+type perfEventIoctl struct {
+ *perfEvent
+}
+
+func (pi *perfEventIoctl) isLink() {}
+
+// Since 4.15 (e87c6bc3852b "bpf: permit multiple bpf attachments for a single perf event"),
+// calling PERF_EVENT_IOC_SET_BPF appends the given program to a prog_array
+// owned by the perf event, which means multiple programs can be attached
+// simultaneously.
+//
+// Before 4.15, calling PERF_EVENT_IOC_SET_BPF more than once on a perf event
+// returns EEXIST.
+//
+// Detaching a program from a perf event is currently not possible, so a
+// program replacement mechanism cannot be implemented for perf events.
+func (pi *perfEventIoctl) Update(prog *ebpf.Program) error {
+ return fmt.Errorf("perf event ioctl update: %w", ErrNotSupported)
+}
+
+func (pi *perfEventIoctl) Pin(string) error {
+ return fmt.Errorf("perf event ioctl pin: %w", ErrNotSupported)
+}
+
+func (pi *perfEventIoctl) Unpin() error {
+ return fmt.Errorf("perf event ioctl unpin: %w", ErrNotSupported)
+}
+
+func (pi *perfEventIoctl) Info() (*Info, error) {
+ return nil, fmt.Errorf("perf event ioctl info: %w", ErrNotSupported)
+}
+
+// attach the given eBPF prog to the perf event stored in pe.
+// pe must contain a valid perf event fd.
+// prog's type must match the program type stored in pe.
+func attachPerfEvent(pe *perfEvent, prog *ebpf.Program) (Link, error) {
+ if prog == nil {
+ return nil, errors.New("cannot attach a nil program")
+ }
+ if prog.FD() < 0 {
+ return nil, fmt.Errorf("invalid program: %w", sys.ErrClosedFd)
+ }
+
+ switch pe.typ {
+ case kprobeEvent, kretprobeEvent, uprobeEvent, uretprobeEvent:
+ if t := prog.Type(); t != ebpf.Kprobe {
+ return nil, fmt.Errorf("invalid program type (expected %s): %s", ebpf.Kprobe, t)
+ }
+ case tracepointEvent:
+ if t := prog.Type(); t != ebpf.TracePoint {
+ return nil, fmt.Errorf("invalid program type (expected %s): %s", ebpf.TracePoint, t)
+ }
+ default:
+ return nil, fmt.Errorf("unknown perf event type: %d", pe.typ)
+ }
+
+ if err := haveBPFLinkPerfEvent(); err == nil {
+ return attachPerfEventLink(pe, prog)
+ }
+ return attachPerfEventIoctl(pe, prog)
+}
+
+func attachPerfEventIoctl(pe *perfEvent, prog *ebpf.Program) (*perfEventIoctl, error) {
+ if pe.cookie != 0 {
+ return nil, fmt.Errorf("cookies are not supported: %w", ErrNotSupported)
+ }
+
+ // Assign the eBPF program to the perf event.
+ err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_SET_BPF, prog.FD())
+ if err != nil {
+ return nil, fmt.Errorf("setting perf event bpf program: %w", err)
+ }
+
+ // PERF_EVENT_IOC_ENABLE and _DISABLE ignore their given values.
+ if err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_ENABLE, 0); err != nil {
+ return nil, fmt.Errorf("enable perf event: %s", err)
+ }
+
+ pi := &perfEventIoctl{pe}
+
+ // Close the perf event when its reference is lost to avoid leaking system resources.
+ runtime.SetFinalizer(pi, (*perfEventIoctl).Close)
+ return pi, nil
+}
+
+// Use the bpf api to attach the perf event (BPF_LINK_TYPE_PERF_EVENT, 5.15+).
+//
+// https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e
+func attachPerfEventLink(pe *perfEvent, prog *ebpf.Program) (*perfEventLink, error) {
+ fd, err := sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{
+ ProgFd: uint32(prog.FD()),
+ TargetFd: pe.fd.Uint(),
+ AttachType: sys.BPF_PERF_EVENT,
+ BpfCookie: pe.cookie,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("cannot create bpf perf link: %v", err)
+ }
+
+ pl := &perfEventLink{RawLink{fd: fd}, pe}
+
+ // Close the perf event when its reference is lost to avoid leaking system resources.
+ runtime.SetFinalizer(pl, (*perfEventLink).Close)
+ return pl, nil
+}
+
+// unsafeStringPtr returns an unsafe.Pointer to a NUL-terminated copy of str.
+func unsafeStringPtr(str string) (unsafe.Pointer, error) {
+ p, err := unix.BytePtrFromString(str)
+ if err != nil {
+ return nil, err
+ }
+ return unsafe.Pointer(p), nil
+}
+
+// getTraceEventID reads a trace event's ID from tracefs given its group and name.
+// The kernel requires group and name to be alphanumeric or underscore.
+//
+// name automatically has its invalid symbols converted to underscores so the caller
+// can pass a raw symbol name, e.g. a kernel symbol containing dots.
+func getTraceEventID(group, name string) (uint64, error) {
+ name = sanitizeSymbol(name)
+ tid, err := uint64FromFile(tracefsPath, "events", group, name, "id")
+ if errors.Is(err, os.ErrNotExist) {
+ return 0, fmt.Errorf("trace event %s/%s: %w", group, name, os.ErrNotExist)
+ }
+ if err != nil {
+ return 0, fmt.Errorf("reading trace event ID of %s/%s: %w", group, name, err)
+ }
+
+ return tid, nil
+}
+
+// getPMUEventType reads a Performance Monitoring Unit's type (numeric identifier)
+// from /sys/bus/event_source/devices/<pmu>/type.
+//
+// Returns ErrNotSupported if the pmu type is not supported.
+func getPMUEventType(typ probeType) (uint64, error) {
+ et, err := uint64FromFile("/sys/bus/event_source/devices", typ.String(), "type")
+ if errors.Is(err, os.ErrNotExist) {
+ return 0, fmt.Errorf("pmu type %s: %w", typ, ErrNotSupported)
+ }
+ if err != nil {
+ return 0, fmt.Errorf("reading pmu type %s: %w", typ, err)
+ }
+
+ return et, nil
+}
+
+// openTracepointPerfEvent opens a tracepoint-type perf event. System-wide
+// [k,u]probes created by writing to <tracefs>/[k,u]probe_events are tracepoints
+// behind the scenes, and can be attached to using these perf events.
+func openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) {
+ attr := unix.PerfEventAttr{
+ Type: unix.PERF_TYPE_TRACEPOINT,
+ Config: tid,
+ Sample_type: unix.PERF_SAMPLE_RAW,
+ Sample: 1,
+ Wakeup: 1,
+ }
+
+ fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
+ if err != nil {
+ return nil, fmt.Errorf("opening tracepoint perf event: %w", err)
+ }
+
+ return sys.NewFD(fd)
+}
+
+// uint64FromFile reads a uint64 from a file. All elements of path are sanitized
+// and joined onto base. Returns error if base no longer prefixes the path after
+// joining all components.
+func uint64FromFile(base string, path ...string) (uint64, error) {
+ l := filepath.Join(path...)
+ p := filepath.Join(base, l)
+ if !strings.HasPrefix(p, base) {
+ return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput)
+ }
+
+ data, err := os.ReadFile(p)
+ if err != nil {
+ return 0, fmt.Errorf("reading file %s: %w", p, err)
+ }
+
+ et := bytes.TrimSpace(data)
+ return strconv.ParseUint(string(et), 10, 64)
+}
+
+// Probe BPF perf link.
+//
+// https://elixir.bootlin.com/linux/v5.16.8/source/kernel/bpf/syscall.c#L4307
+// https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e
+var haveBPFLinkPerfEvent = internal.FeatureTest("bpf_link_perf_event", "5.15", func() error {
+ prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+ Name: "probe_bpf_perf_link",
+ Type: ebpf.Kprobe,
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.Return(),
+ },
+ License: "MIT",
+ })
+ if err != nil {
+ return err
+ }
+ defer prog.Close()
+
+ _, err = sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{
+ ProgFd: uint32(prog.FD()),
+ AttachType: sys.BPF_PERF_EVENT,
+ })
+ if errors.Is(err, unix.EINVAL) {
+ return internal.ErrNotSupported
+ }
+ if errors.Is(err, unix.EBADF) {
+ return nil
+ }
+ return err
+})
+
+// isValidTraceID implements the equivalent of a regex match
+// against "^[a-zA-Z_][0-9a-zA-Z_]*$".
+//
+// Trace event groups, names and kernel symbols must adhere to this set
+// of characters. Non-empty, first character must not be a number, all
+// characters must be alphanumeric or underscore.
+func isValidTraceID(s string) bool {
+ if len(s) < 1 {
+ return false
+ }
+ for i, c := range []byte(s) {
+ switch {
+ case c >= 'a' && c <= 'z':
+ case c >= 'A' && c <= 'Z':
+ case c == '_':
+ case i > 0 && c >= '0' && c <= '9':
+
+ default:
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/vendor/github.com/cilium/ebpf/link/platform.go b/vendor/github.com/cilium/ebpf/link/platform.go
new file mode 100644
index 000000000..eb6f7b7a3
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/platform.go
@@ -0,0 +1,25 @@
+package link
+
+import (
+ "fmt"
+ "runtime"
+)
+
+func platformPrefix(symbol string) string {
+
+ prefix := runtime.GOARCH
+
+ // per https://github.com/golang/go/blob/master/src/go/build/syslist.go
+ switch prefix {
+ case "386":
+ prefix = "ia32"
+ case "amd64", "amd64p32":
+ prefix = "x64"
+ case "arm64", "arm64be":
+ prefix = "arm64"
+ default:
+ return symbol
+ }
+
+ return fmt.Sprintf("__%s_%s", prefix, symbol)
+}
diff --git a/vendor/github.com/cilium/ebpf/link/program.go b/vendor/github.com/cilium/ebpf/link/program.go
index 0fe9d37c4..ea3181737 100644
--- a/vendor/github.com/cilium/ebpf/link/program.go
+++ b/vendor/github.com/cilium/ebpf/link/program.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/cilium/ebpf"
- "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/sys"
)
type RawAttachProgramOptions struct {
@@ -34,7 +34,7 @@ func RawAttachProgram(opts RawAttachProgramOptions) error {
replaceFd = uint32(opts.Replace.FD())
}
- attr := internal.BPFProgAttachAttr{
+ attr := sys.ProgAttachAttr{
TargetFd: uint32(opts.Target),
AttachBpfFd: uint32(opts.Program.FD()),
ReplaceBpfFd: replaceFd,
@@ -42,8 +42,8 @@ func RawAttachProgram(opts RawAttachProgramOptions) error {
AttachFlags: uint32(opts.Flags),
}
- if err := internal.BPFProgAttach(&attr); err != nil {
- return fmt.Errorf("can't attach program: %s", err)
+ if err := sys.ProgAttach(&attr); err != nil {
+ return fmt.Errorf("can't attach program: %w", err)
}
return nil
}
@@ -63,13 +63,13 @@ func RawDetachProgram(opts RawDetachProgramOptions) error {
return err
}
- attr := internal.BPFProgDetachAttr{
+ attr := sys.ProgDetachAttr{
TargetFd: uint32(opts.Target),
AttachBpfFd: uint32(opts.Program.FD()),
AttachType: uint32(opts.Attach),
}
- if err := internal.BPFProgDetach(&attr); err != nil {
- return fmt.Errorf("can't detach program: %s", err)
+ if err := sys.ProgDetach(&attr); err != nil {
+ return fmt.Errorf("can't detach program: %w", err)
}
return nil
diff --git a/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
index 65652486f..925e621cb 100644
--- a/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
+++ b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
@@ -1,10 +1,11 @@
package link
import (
+ "errors"
"fmt"
"github.com/cilium/ebpf"
- "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/sys"
)
type RawTracepointOptions struct {
@@ -22,36 +23,65 @@ func AttachRawTracepoint(opts RawTracepointOptions) (Link, error) {
return nil, fmt.Errorf("invalid program type %s, expected RawTracepoint(Writable)", t)
}
if opts.Program.FD() < 0 {
- return nil, fmt.Errorf("invalid program: %w", internal.ErrClosedFd)
+ return nil, fmt.Errorf("invalid program: %w", sys.ErrClosedFd)
}
- fd, err := bpfRawTracepointOpen(&bpfRawTracepointOpenAttr{
- name: internal.NewStringPointer(opts.Name),
- fd: uint32(opts.Program.FD()),
+ fd, err := sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{
+ Name: sys.NewStringPointer(opts.Name),
+ ProgFd: uint32(opts.Program.FD()),
})
if err != nil {
return nil, err
}
- return &progAttachRawTracepoint{fd: fd}, nil
+ err = haveBPFLink()
+ if errors.Is(err, ErrNotSupported) {
+ // Prior to commit 70ed506c3bbc ("bpf: Introduce pinnable bpf_link abstraction")
+ // raw_tracepoints are just a plain fd.
+ return &simpleRawTracepoint{fd}, nil
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ return &rawTracepoint{RawLink{fd: fd}}, nil
}
-type progAttachRawTracepoint struct {
- fd *internal.FD
+type simpleRawTracepoint struct {
+ fd *sys.FD
}
-var _ Link = (*progAttachRawTracepoint)(nil)
+var _ Link = (*simpleRawTracepoint)(nil)
-func (rt *progAttachRawTracepoint) isLink() {}
+func (frt *simpleRawTracepoint) isLink() {}
+
+func (frt *simpleRawTracepoint) Close() error {
+ return frt.fd.Close()
+}
-func (rt *progAttachRawTracepoint) Close() error {
- return rt.fd.Close()
+func (frt *simpleRawTracepoint) Update(_ *ebpf.Program) error {
+ return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported)
}
-func (rt *progAttachRawTracepoint) Update(_ *ebpf.Program) error {
- return fmt.Errorf("can't update raw_tracepoint: %w", ErrNotSupported)
+func (frt *simpleRawTracepoint) Pin(string) error {
+ return fmt.Errorf("pin raw_tracepoint: %w", ErrNotSupported)
}
-func (rt *progAttachRawTracepoint) Pin(_ string) error {
- return fmt.Errorf("can't pin raw_tracepoint: %w", ErrNotSupported)
+func (frt *simpleRawTracepoint) Unpin() error {
+ return fmt.Errorf("unpin raw_tracepoint: %w", ErrNotSupported)
+}
+
+func (frt *simpleRawTracepoint) Info() (*Info, error) {
+ return nil, fmt.Errorf("can't get raw_tracepoint info: %w", ErrNotSupported)
+}
+
+type rawTracepoint struct {
+ RawLink
+}
+
+var _ Link = (*rawTracepoint)(nil)
+
+func (rt *rawTracepoint) Update(_ *ebpf.Program) error {
+ return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported)
}
diff --git a/vendor/github.com/cilium/ebpf/link/socket_filter.go b/vendor/github.com/cilium/ebpf/link/socket_filter.go
new file mode 100644
index 000000000..94f3958cc
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/socket_filter.go
@@ -0,0 +1,40 @@
+package link
+
+import (
+ "syscall"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/internal/unix"
+)
+
+// AttachSocketFilter attaches a SocketFilter BPF program to a socket.
+func AttachSocketFilter(conn syscall.Conn, program *ebpf.Program) error {
+ rawConn, err := conn.SyscallConn()
+ if err != nil {
+ return err
+ }
+ var ssoErr error
+ err = rawConn.Control(func(fd uintptr) {
+ ssoErr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_ATTACH_BPF, program.FD())
+ })
+ if ssoErr != nil {
+ return ssoErr
+ }
+ return err
+}
+
+// DetachSocketFilter detaches a SocketFilter BPF program from a socket.
+func DetachSocketFilter(conn syscall.Conn) error {
+ rawConn, err := conn.SyscallConn()
+ if err != nil {
+ return err
+ }
+ var ssoErr error
+ err = rawConn.Control(func(fd uintptr) {
+ ssoErr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_DETACH_BPF, 0)
+ })
+ if ssoErr != nil {
+ return ssoErr
+ }
+ return err
+}
diff --git a/vendor/github.com/cilium/ebpf/link/syscalls.go b/vendor/github.com/cilium/ebpf/link/syscalls.go
index 19326c8af..a661395b3 100644
--- a/vendor/github.com/cilium/ebpf/link/syscalls.go
+++ b/vendor/github.com/cilium/ebpf/link/syscalls.go
@@ -2,35 +2,33 @@ package link
import (
"errors"
- "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"
)
// Type is the kind of link.
-type Type uint32
+type Type = sys.LinkType
// Valid link types.
-//
-// Equivalent to enum bpf_link_type.
const (
- UnspecifiedType Type = iota
- RawTracepointType
- TracingType
- CgroupType
- IterType
- NetNsType
- XDPType
+ UnspecifiedType = sys.BPF_LINK_TYPE_UNSPEC
+ RawTracepointType = sys.BPF_LINK_TYPE_RAW_TRACEPOINT
+ TracingType = sys.BPF_LINK_TYPE_TRACING
+ CgroupType = sys.BPF_LINK_TYPE_CGROUP
+ IterType = sys.BPF_LINK_TYPE_ITER
+ NetNsType = sys.BPF_LINK_TYPE_NETNS
+ XDPType = sys.BPF_LINK_TYPE_XDP
+ PerfEventType = sys.BPF_LINK_TYPE_PERF_EVENT
)
var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() error {
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
- Type: ebpf.CGroupSKB,
- AttachType: ebpf.AttachCGroupInetIngress,
- License: "MIT",
+ Type: ebpf.CGroupSKB,
+ License: "MIT",
Instructions: asm.Instructions{
asm.Mov.Imm(asm.R0, 0),
asm.Return(),
@@ -69,7 +67,7 @@ var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replace
// We know that we have BPF_PROG_ATTACH since we can load CGroupSKB programs.
// If passing BPF_F_REPLACE gives us EINVAL we know that the feature isn't
// present.
- attr := internal.BPFProgAttachAttr{
+ attr := sys.ProgAttachAttr{
// We rely on this being checked after attachFlags.
TargetFd: ^uint32(0),
AttachBpfFd: uint32(prog.FD()),
@@ -77,7 +75,7 @@ var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replace
AttachFlags: uint32(flagReplace),
}
- err = internal.BPFProgAttach(&attr)
+ err = sys.ProgAttach(&attr)
if errors.Is(err, unix.EINVAL) {
return internal.ErrNotSupported
}
@@ -87,55 +85,14 @@ var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replace
return err
})
-type bpfLinkCreateAttr struct {
- progFd uint32
- targetFd uint32
- attachType ebpf.AttachType
- flags uint32
-}
-
-func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) {
- ptr, err := internal.BPF(internal.BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
- if err != nil {
- return nil, err
- }
- return internal.NewFD(uint32(ptr)), nil
-}
-
-type bpfLinkUpdateAttr struct {
- linkFd uint32
- newProgFd uint32
- flags uint32
- oldProgFd uint32
-}
-
-func bpfLinkUpdate(attr *bpfLinkUpdateAttr) error {
- _, err := internal.BPF(internal.BPF_LINK_UPDATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
- return err
-}
-
var haveBPFLink = internal.FeatureTest("bpf_link", "5.7", func() error {
- prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
- Type: ebpf.CGroupSKB,
- AttachType: ebpf.AttachCGroupInetIngress,
- License: "MIT",
- Instructions: asm.Instructions{
- asm.Mov.Imm(asm.R0, 0),
- asm.Return(),
- },
- })
- if err != nil {
- return internal.ErrNotSupported
- }
- defer prog.Close()
-
- attr := bpfLinkCreateAttr{
+ attr := sys.LinkCreateAttr{
// This is a hopefully invalid file descriptor, which triggers EBADF.
- targetFd: ^uint32(0),
- progFd: uint32(prog.FD()),
- attachType: ebpf.AttachCGroupInetIngress,
+ TargetFd: ^uint32(0),
+ ProgFd: ^uint32(0),
+ AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress),
}
- _, err = bpfLinkCreate(&attr)
+ _, err := sys.LinkCreate(&attr)
if errors.Is(err, unix.EINVAL) {
return internal.ErrNotSupported
}
@@ -144,30 +101,3 @@ var haveBPFLink = internal.FeatureTest("bpf_link", "5.7", func() error {
}
return err
})
-
-type bpfIterCreateAttr struct {
- linkFd uint32
- flags uint32
-}
-
-func bpfIterCreate(attr *bpfIterCreateAttr) (*internal.FD, error) {
- ptr, err := internal.BPF(internal.BPF_ITER_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
- if err == nil {
- return internal.NewFD(uint32(ptr)), nil
- }
- return nil, err
-}
-
-type bpfRawTracepointOpenAttr struct {
- name internal.Pointer
- fd uint32
- _ uint32
-}
-
-func bpfRawTracepointOpen(attr *bpfRawTracepointOpenAttr) (*internal.FD, error) {
- ptr, err := internal.BPF(internal.BPF_RAW_TRACEPOINT_OPEN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
- if err == nil {
- return internal.NewFD(uint32(ptr)), nil
- }
- return nil, err
-}
diff --git a/vendor/github.com/cilium/ebpf/link/tracepoint.go b/vendor/github.com/cilium/ebpf/link/tracepoint.go
new file mode 100644
index 000000000..a59ef9d1c
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/tracepoint.go
@@ -0,0 +1,77 @@
+package link
+
+import (
+ "fmt"
+
+ "github.com/cilium/ebpf"
+)
+
+// TracepointOptions defines additional parameters that will be used
+// when loading Tracepoints.
+type TracepointOptions struct {
+ // Arbitrary value that can be fetched from an eBPF program
+ // via `bpf_get_attach_cookie()`.
+ //
+ // Needs kernel 5.15+.
+ Cookie uint64
+}
+
+// Tracepoint attaches the given eBPF program to the tracepoint with the given
+// group and name. See /sys/kernel/debug/tracing/events to find available
+// tracepoints. The top-level directory is the group, the event's subdirectory
+// is the name. Example:
+//
+// tp, err := Tracepoint("syscalls", "sys_enter_fork", prog, nil)
+//
+// Losing the reference to the resulting Link (tp) will close the Tracepoint
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+//
+// Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is
+// only possible as of kernel 4.14 (commit cf5f5ce).
+func Tracepoint(group, name string, prog *ebpf.Program, opts *TracepointOptions) (Link, error) {
+ if group == "" || name == "" {
+ return nil, fmt.Errorf("group and name cannot be empty: %w", errInvalidInput)
+ }
+ if prog == nil {
+ return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+ }
+ if !isValidTraceID(group) || !isValidTraceID(name) {
+ return nil, fmt.Errorf("group and name '%s/%s' must be alphanumeric or underscore: %w", group, name, errInvalidInput)
+ }
+ if prog.Type() != ebpf.TracePoint {
+ return nil, fmt.Errorf("eBPF program type %s is not a Tracepoint: %w", prog.Type(), errInvalidInput)
+ }
+
+ tid, err := getTraceEventID(group, name)
+ if err != nil {
+ return nil, err
+ }
+
+ fd, err := openTracepointPerfEvent(tid, perfAllThreads)
+ if err != nil {
+ return nil, err
+ }
+
+ var cookie uint64
+ if opts != nil {
+ cookie = opts.Cookie
+ }
+
+ pe := &perfEvent{
+ typ: tracepointEvent,
+ group: group,
+ name: name,
+ tracefsID: tid,
+ cookie: cookie,
+ fd: fd,
+ }
+
+ lnk, err := attachPerfEvent(pe, prog)
+ if err != nil {
+ pe.Close()
+ return nil, err
+ }
+
+ return lnk, nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/tracing.go b/vendor/github.com/cilium/ebpf/link/tracing.go
new file mode 100644
index 000000000..e47e61a3b
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/tracing.go
@@ -0,0 +1,141 @@
+package link
+
+import (
+ "fmt"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/btf"
+ "github.com/cilium/ebpf/internal/sys"
+)
+
+type tracing struct {
+ RawLink
+}
+
+func (f *tracing) Update(new *ebpf.Program) error {
+ return fmt.Errorf("tracing update: %w", ErrNotSupported)
+}
+
+// AttachFreplace attaches the given eBPF program to the function it replaces.
+//
+// The program and name can either be provided at link time, or can be provided
+// at program load time. If they were provided at load time, they should be nil
+// and empty respectively here, as they will be ignored by the kernel.
+// Examples:
+//
+// AttachFreplace(dispatcher, "function", replacement)
+// AttachFreplace(nil, "", replacement)
+func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (Link, error) {
+ if (name == "") != (targetProg == nil) {
+ return nil, fmt.Errorf("must provide both or neither of name and targetProg: %w", errInvalidInput)
+ }
+ if prog == nil {
+ return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+ }
+ if prog.Type() != ebpf.Extension {
+ return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput)
+ }
+
+ var (
+ target int
+ typeID btf.TypeID
+ )
+ if targetProg != nil {
+ btfHandle, err := targetProg.Handle()
+ if err != nil {
+ return nil, err
+ }
+ defer btfHandle.Close()
+
+ spec, err := btfHandle.Spec(nil)
+ if err != nil {
+ return nil, err
+ }
+
+ var function *btf.Func
+ if err := spec.TypeByName(name, &function); err != nil {
+ return nil, err
+ }
+
+ target = targetProg.FD()
+ typeID, err = spec.TypeID(function)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ link, err := AttachRawLink(RawLinkOptions{
+ Target: target,
+ Program: prog,
+ Attach: ebpf.AttachNone,
+ BTF: typeID,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return &tracing{*link}, nil
+}
+
+type TracingOptions struct {
+ // Program must be of type Tracing with attach type
+ // AttachTraceFEntry/AttachTraceFExit/AttachModifyReturn or
+ // AttachTraceRawTp.
+ Program *ebpf.Program
+}
+
+type LSMOptions struct {
+ // Program must be of type LSM with attach type
+ // AttachLSMMac.
+ Program *ebpf.Program
+}
+
+// attachBTFID links all BPF program types (Tracing/LSM) that they attach to a btf_id.
+func attachBTFID(program *ebpf.Program) (Link, error) {
+ if program.FD() < 0 {
+ return nil, fmt.Errorf("invalid program %w", sys.ErrClosedFd)
+ }
+
+ fd, err := sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{
+ ProgFd: uint32(program.FD()),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ raw := RawLink{fd: fd}
+ info, err := raw.Info()
+ if err != nil {
+ raw.Close()
+ return nil, err
+ }
+
+ if info.Type == RawTracepointType {
+ // Sadness upon sadness: a Tracing program with AttachRawTp returns
+ // a raw_tracepoint link. Other types return a tracing link.
+ return &rawTracepoint{raw}, nil
+ }
+
+ return &tracing{RawLink: RawLink{fd: fd}}, nil
+}
+
+// AttachTracing links a tracing (fentry/fexit/fmod_ret) BPF program or
+// a BTF-powered raw tracepoint (tp_btf) BPF Program to a BPF hook defined
+// in kernel modules.
+func AttachTracing(opts TracingOptions) (Link, error) {
+ if t := opts.Program.Type(); t != ebpf.Tracing {
+ return nil, fmt.Errorf("invalid program type %s, expected Tracing", t)
+ }
+
+ return attachBTFID(opts.Program)
+}
+
+// AttachLSM links a Linux security module (LSM) BPF Program to a BPF
+// hook defined in kernel modules.
+func AttachLSM(opts LSMOptions) (Link, error) {
+ if t := opts.Program.Type(); t != ebpf.LSM {
+ return nil, fmt.Errorf("invalid program type %s, expected LSM", t)
+ }
+
+ return attachBTFID(opts.Program)
+}
diff --git a/vendor/github.com/cilium/ebpf/link/uprobe.go b/vendor/github.com/cilium/ebpf/link/uprobe.go
new file mode 100644
index 000000000..edf925b57
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/uprobe.go
@@ -0,0 +1,373 @@
+package link
+
+import (
+ "debug/elf"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/internal"
+)
+
+var (
+ uprobeEventsPath = filepath.Join(tracefsPath, "uprobe_events")
+
+ uprobeRetprobeBit = struct {
+ once sync.Once
+ value uint64
+ err error
+ }{}
+
+ uprobeRefCtrOffsetPMUPath = "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset"
+ // elixir.bootlin.com/linux/v5.15-rc7/source/kernel/events/core.c#L9799
+ uprobeRefCtrOffsetShift = 32
+ haveRefCtrOffsetPMU = internal.FeatureTest("RefCtrOffsetPMU", "4.20", func() error {
+ _, err := os.Stat(uprobeRefCtrOffsetPMUPath)
+ if err != nil {
+ return internal.ErrNotSupported
+ }
+ return nil
+ })
+
+ // ErrNoSymbol indicates that the given symbol was not found
+ // in the ELF symbols table.
+ ErrNoSymbol = errors.New("not found")
+)
+
+// Executable defines an executable program on the filesystem.
+type Executable struct {
+ // Path of the executable on the filesystem.
+ path string
+ // Parsed ELF and dynamic symbols' addresses.
+ addresses map[string]uint64
+}
+
+// UprobeOptions defines additional parameters that will be used
+// when loading Uprobes.
+type UprobeOptions struct {
+ // Symbol address. Must be provided in case of external symbols (shared libs).
+ // If set, overrides the address eventually parsed from the executable.
+ Address uint64
+ // The offset relative to given symbol. Useful when tracing an arbitrary point
+ // inside the frame of given symbol.
+ //
+ // Note: this field changed from being an absolute offset to being relative
+ // to Address.
+ Offset uint64
+ // Only set the uprobe on the given process ID. Useful when tracing
+ // shared library calls or programs that have many running instances.
+ PID int
+ // Automatically manage SDT reference counts (semaphores).
+ //
+ // If this field is set, the Kernel will increment/decrement the
+ // semaphore located in the process memory at the provided address on
+ // probe attach/detach.
+ //
+ // See also:
+ // sourceware.org/systemtap/wiki/UserSpaceProbeImplementation (Semaphore Handling)
+ // github.com/torvalds/linux/commit/1cc33161a83d
+ // github.com/torvalds/linux/commit/a6ca88b241d5
+ RefCtrOffset uint64
+ // Arbitrary value that can be fetched from an eBPF program
+ // via `bpf_get_attach_cookie()`.
+ //
+ // Needs kernel 5.15+.
+ Cookie uint64
+}
+
+// To open a new Executable, use:
+//
+// OpenExecutable("/bin/bash")
+//
+// The returned value can then be used to open Uprobe(s).
+func OpenExecutable(path string) (*Executable, error) {
+ if path == "" {
+ return nil, fmt.Errorf("path cannot be empty")
+ }
+
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, fmt.Errorf("open file '%s': %w", path, err)
+ }
+ defer f.Close()
+
+ se, err := internal.NewSafeELFFile(f)
+ if err != nil {
+ return nil, fmt.Errorf("parse ELF file: %w", err)
+ }
+
+ if se.Type != elf.ET_EXEC && se.Type != elf.ET_DYN {
+ // ELF is not an executable or a shared object.
+ return nil, errors.New("the given file is not an executable or a shared object")
+ }
+
+ ex := Executable{
+ path: path,
+ addresses: make(map[string]uint64),
+ }
+
+ if err := ex.load(se); err != nil {
+ return nil, err
+ }
+
+ return &ex, nil
+}
+
+func (ex *Executable) load(f *internal.SafeELFFile) error {
+ syms, err := f.Symbols()
+ if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
+ return err
+ }
+
+ dynsyms, err := f.DynamicSymbols()
+ if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
+ return err
+ }
+
+ syms = append(syms, dynsyms...)
+
+ for _, s := range syms {
+ if elf.ST_TYPE(s.Info) != elf.STT_FUNC {
+ // Symbol not associated with a function or other executable code.
+ continue
+ }
+
+ address := s.Value
+
+ // Loop over ELF segments.
+ for _, prog := range f.Progs {
+ // Skip uninteresting segments.
+ if prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 {
+ continue
+ }
+
+ if prog.Vaddr <= s.Value && s.Value < (prog.Vaddr+prog.Memsz) {
+ // If the symbol value is contained in the segment, calculate
+ // the symbol offset.
+ //
+ // fn symbol offset = fn symbol VA - .text VA + .text offset
+ //
+ // stackoverflow.com/a/40249502
+ address = s.Value - prog.Vaddr + prog.Off
+ break
+ }
+ }
+
+ ex.addresses[s.Name] = address
+ }
+
+ return nil
+}
+
+// address calculates the address of a symbol in the executable.
+//
+// opts must not be nil.
+func (ex *Executable) address(symbol string, opts *UprobeOptions) (uint64, error) {
+ if opts.Address > 0 {
+ return opts.Address + opts.Offset, nil
+ }
+
+ address, ok := ex.addresses[symbol]
+ if !ok {
+ return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
+ }
+
+ // Symbols with location 0 from section undef are shared library calls and
+ // are relocated before the binary is executed. Dynamic linking is not
+ // implemented by the library, so mark this as unsupported for now.
+ //
+ // Since only offset values are stored and not elf.Symbol, if the value is 0,
+ // assume it's an external symbol.
+ if address == 0 {
+ return 0, fmt.Errorf("cannot resolve %s library call '%s': %w "+
+ "(consider providing UprobeOptions.Address)", ex.path, symbol, ErrNotSupported)
+ }
+
+ return address + opts.Offset, nil
+}
+
+// Uprobe attaches the given eBPF program to a perf event that fires when the
+// given symbol starts executing in the given Executable.
+// For example, /bin/bash::main():
+//
+// ex, _ = OpenExecutable("/bin/bash")
+// ex.Uprobe("main", prog, nil)
+//
+// When using symbols which belongs to shared libraries,
+// an offset must be provided via options:
+//
+// up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
+//
+// Note: Setting the Offset field in the options supersedes the symbol's offset.
+//
+// Losing the reference to the resulting Link (up) will close the Uprobe
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+//
+// Functions provided by shared libraries can currently not be traced and
+// will result in an ErrNotSupported.
+func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
+ u, err := ex.uprobe(symbol, prog, opts, false)
+ if err != nil {
+ return nil, err
+ }
+
+ lnk, err := attachPerfEvent(u, prog)
+ if err != nil {
+ u.Close()
+ return nil, err
+ }
+
+ return lnk, nil
+}
+
+// Uretprobe attaches the given eBPF program to a perf event that fires right
+// before the given symbol exits. For example, /bin/bash::main():
+//
+// ex, _ = OpenExecutable("/bin/bash")
+// ex.Uretprobe("main", prog, nil)
+//
+// When using symbols which belongs to shared libraries,
+// an offset must be provided via options:
+//
+// up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
+//
+// Note: Setting the Offset field in the options supersedes the symbol's offset.
+//
+// Losing the reference to the resulting Link (up) will close the Uprobe
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+//
+// Functions provided by shared libraries can currently not be traced and
+// will result in an ErrNotSupported.
+func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
+ u, err := ex.uprobe(symbol, prog, opts, true)
+ if err != nil {
+ return nil, err
+ }
+
+ lnk, err := attachPerfEvent(u, prog)
+ if err != nil {
+ u.Close()
+ return nil, err
+ }
+
+ return lnk, nil
+}
+
+// uprobe opens a perf event for the given binary/symbol and attaches prog to it.
+// If ret is true, create a uretprobe.
+func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions, ret bool) (*perfEvent, error) {
+ if prog == nil {
+ return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+ }
+ if prog.Type() != ebpf.Kprobe {
+ return nil, fmt.Errorf("eBPF program type %s is not Kprobe: %w", prog.Type(), errInvalidInput)
+ }
+ if opts == nil {
+ opts = &UprobeOptions{}
+ }
+
+ offset, err := ex.address(symbol, opts)
+ if err != nil {
+ return nil, err
+ }
+
+ pid := opts.PID
+ if pid == 0 {
+ pid = perfAllThreads
+ }
+
+ if opts.RefCtrOffset != 0 {
+ if err := haveRefCtrOffsetPMU(); err != nil {
+ return nil, fmt.Errorf("uprobe ref_ctr_offset: %w", err)
+ }
+ }
+
+ args := probeArgs{
+ symbol: symbol,
+ path: ex.path,
+ offset: offset,
+ pid: pid,
+ refCtrOffset: opts.RefCtrOffset,
+ ret: ret,
+ cookie: opts.Cookie,
+ }
+
+ // Use uprobe PMU if the kernel has it available.
+ tp, err := pmuUprobe(args)
+ if err == nil {
+ return tp, nil
+ }
+ if err != nil && !errors.Is(err, ErrNotSupported) {
+ return nil, fmt.Errorf("creating perf_uprobe PMU: %w", err)
+ }
+
+ // Use tracefs if uprobe PMU is missing.
+ args.symbol = sanitizeSymbol(symbol)
+ tp, err = tracefsUprobe(args)
+ if err != nil {
+ return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err)
+ }
+
+ return tp, nil
+}
+
+// pmuUprobe opens a perf event based on the uprobe PMU.
+func pmuUprobe(args probeArgs) (*perfEvent, error) {
+ return pmuProbe(uprobeType, args)
+}
+
+// tracefsUprobe creates a Uprobe tracefs entry.
+func tracefsUprobe(args probeArgs) (*perfEvent, error) {
+ return tracefsProbe(uprobeType, args)
+}
+
+// sanitizeSymbol replaces every invalid character for the tracefs api with an underscore.
+// It is equivalent to calling regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString("_").
+func sanitizeSymbol(s string) string {
+ var b strings.Builder
+ b.Grow(len(s))
+ var skip bool
+ for _, c := range []byte(s) {
+ switch {
+ case c >= 'a' && c <= 'z',
+ c >= 'A' && c <= 'Z',
+ c >= '0' && c <= '9':
+ skip = false
+ b.WriteByte(c)
+
+ default:
+ if !skip {
+ b.WriteByte('_')
+ skip = true
+ }
+ }
+ }
+
+ return b.String()
+}
+
+// uprobeToken creates the PATH:OFFSET(REF_CTR_OFFSET) token for the tracefs api.
+func uprobeToken(args probeArgs) string {
+ po := fmt.Sprintf("%s:%#x", args.path, args.offset)
+
+ if args.refCtrOffset != 0 {
+ // This is not documented in Documentation/trace/uprobetracer.txt.
+ // elixir.bootlin.com/linux/v5.15-rc7/source/kernel/trace/trace.c#L5564
+ po += fmt.Sprintf("(%#x)", args.refCtrOffset)
+ }
+
+ return po
+}
+
+func uretprobeBit() (uint64, error) {
+ uprobeRetprobeBit.once.Do(func() {
+ uprobeRetprobeBit.value, uprobeRetprobeBit.err = determineRetprobeBit(uprobeType)
+ })
+ return uprobeRetprobeBit.value, uprobeRetprobeBit.err
+}
diff --git a/vendor/github.com/cilium/ebpf/link/xdp.go b/vendor/github.com/cilium/ebpf/link/xdp.go
new file mode 100644
index 000000000..aa8dd3a4c
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/xdp.go
@@ -0,0 +1,54 @@
+package link
+
+import (
+ "fmt"
+
+ "github.com/cilium/ebpf"
+)
+
+// XDPAttachFlags represents how XDP program will be attached to interface.
+type XDPAttachFlags uint32
+
+const (
+ // XDPGenericMode (SKB) links XDP BPF program for drivers which do
+ // not yet support native XDP.
+ XDPGenericMode XDPAttachFlags = 1 << (iota + 1)
+ // XDPDriverMode links XDP BPF program into the driver’s receive path.
+ XDPDriverMode
+ // XDPOffloadMode offloads the entire XDP BPF program into hardware.
+ XDPOffloadMode
+)
+
+type XDPOptions struct {
+ // Program must be an XDP BPF program.
+ Program *ebpf.Program
+
+ // Interface is the interface index to attach program to.
+ Interface int
+
+ // Flags is one of XDPAttachFlags (optional).
+ //
+ // Only one XDP mode should be set, without flag defaults
+ // to driver/generic mode (best effort).
+ Flags XDPAttachFlags
+}
+
+// AttachXDP links an XDP BPF program to an XDP hook.
+func AttachXDP(opts XDPOptions) (Link, error) {
+ if t := opts.Program.Type(); t != ebpf.XDP {
+ return nil, fmt.Errorf("invalid program type %s, expected XDP", t)
+ }
+
+ if opts.Interface < 1 {
+ return nil, fmt.Errorf("invalid interface index: %d", opts.Interface)
+ }
+
+ rawLink, err := AttachRawLink(RawLinkOptions{
+ Program: opts.Program,
+ Attach: ebpf.AttachXDP,
+ Target: opts.Interface,
+ Flags: uint32(opts.Flags),
+ })
+
+ return rawLink, err
+}