summaryrefslogtreecommitdiff
path: root/vendor/github.com/cilium/ebpf/link/uprobe.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cilium/ebpf/link/uprobe.go')
-rw-r--r--vendor/github.com/cilium/ebpf/link/uprobe.go373
1 files changed, 0 insertions, 373 deletions
diff --git a/vendor/github.com/cilium/ebpf/link/uprobe.go b/vendor/github.com/cilium/ebpf/link/uprobe.go
deleted file mode 100644
index edf925b57..000000000
--- a/vendor/github.com/cilium/ebpf/link/uprobe.go
+++ /dev/null
@@ -1,373 +0,0 @@
-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
-}