Repository: cilium/ebpf
Pull request #777 (2022-08-31 20:35:35)

iter: Add support to load sleepable iterator program type
Adding support to load sleepable iterator program type,
defined by 'iter.s/...' section.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>


Patch:
diff --git a/btf/btf_types.go b/btf/btf_types.go
index 1ab0917..d64916d 100644
--- a/btf/btf_types.go
+++ b/btf/btf_types.go
@@ -237,7 +237,7 @@ type btfVariable struct {
 
 type btfEnum struct {
 	NameOff uint32
-	Val     int32
+	Val     uint32
 }
 
 type btfParam struct {
diff --git a/btf/format.go b/btf/format.go
index 5769565..207d542 100644
--- a/btf/format.go
+++ b/btf/format.go
@@ -63,39 +63,25 @@ func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error {
 		return fmt.Errorf("need a name for type %s", typ)
 	}
 
-	switch v := skipQualifiers(typ).(type) {
-	case *Enum:
-		fmt.Fprintf(&gf.w, "type %s ", name)
-		switch v.Size {
-		case 1:
-			gf.w.WriteString("int8")
-		case 2:
-			gf.w.WriteString("int16")
-		case 4:
-			gf.w.WriteString("int32")
-		case 8:
-			gf.w.WriteString("int64")
-		default:
-			return fmt.Errorf("%s: invalid enum size %d", typ, v.Size)
-		}
-
-		if len(v.Values) == 0 {
-			return nil
-		}
-
-		gf.w.WriteString("; const ( ")
-		for _, ev := range v.Values {
-			id := gf.enumIdentifier(name, ev.Name)
-			fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value)
-		}
-		gf.w.WriteString(")")
+	typ = skipQualifiers(typ)
+	fmt.Fprintf(&gf.w, "type %s ", name)
+	if err := gf.writeTypeLit(typ, 0); err != nil {
+		return err
+	}
 
+	e, ok := typ.(*Enum)
+	if !ok || len(e.Values) == 0 {
 		return nil
+	}
 
-	default:
-		fmt.Fprintf(&gf.w, "type %s ", name)
-		return gf.writeTypeLit(v, 0)
+	gf.w.WriteString("; const ( ")
+	for _, ev := range e.Values {
+		id := gf.enumIdentifier(name, ev.Name)
+		fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value)
 	}
+	gf.w.WriteString(")")
+
+	return nil
 }
 
 // writeType outputs the name of a named type or a literal describing the type.
@@ -136,7 +122,21 @@ func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
 		gf.writeIntLit(v)
 
 	case *Enum:
-		gf.w.WriteString("int32")
+		if !v.Signed {
+			gf.w.WriteRune('u')
+		}
+		switch v.Size {
+		case 1:
+			gf.w.WriteString("int8")
+		case 2:
+			gf.w.WriteString("int16")
+		case 4:
+			gf.w.WriteString("int32")
+		case 8:
+			gf.w.WriteString("int64")
+		default:
+			err = fmt.Errorf("invalid enum size %d", v.Size)
+		}
 
 	case *Typedef:
 		err = gf.writeType(v.Type, depth)
@@ -199,11 +199,15 @@ func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int)
 			gf.writePadding(n)
 		}
 
-		size, err := Sizeof(m.Type)
+		fieldSize, err := Sizeof(m.Type)
 		if err != nil {
 			return fmt.Errorf("field %d: %w", i, err)
 		}
-		prevOffset = offset + uint32(size)
+
+		prevOffset = offset + uint32(fieldSize)
+		if prevOffset > size {
+			return fmt.Errorf("field %d of size %d exceeds type size %d", i, fieldSize, size)
+		}
 
 		if err := gf.writeStructField(m, depth); err != nil {
 			return fmt.Errorf("field %d: %w", i, err)
diff --git a/btf/types.go b/btf/types.go
index 38ad3dd..b34aaee 100644
--- a/btf/types.go
+++ b/btf/types.go
@@ -273,7 +273,9 @@ type Member struct {
 type Enum struct {
 	Name string
 	// Size of the enum value in bytes.
-	Size   uint32
+	Size uint32
+	// True if the values should be interpreted as signed integers.
+	Signed bool
 	Values []EnumValue
 }
 
@@ -288,7 +290,7 @@ func (e *Enum) TypeName() string { return e.Name }
 // Is is not a valid Type
 type EnumValue struct {
 	Name  string
-	Value int32
+	Value uint64
 }
 
 func (e *Enum) size() uint32    { return e.Size }
@@ -993,17 +995,20 @@ func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTabl
 		case kindEnum:
 			rawvals := raw.data.([]btfEnum)
 			vals := make([]EnumValue, 0, len(rawvals))
+			signed := raw.KindFlag()
 			for i, btfVal := range rawvals {
 				name, err := rawStrings.Lookup(btfVal.NameOff)
 				if err != nil {
 					return nil, fmt.Errorf("get name for enum value %d: %s", i, err)
 				}
-				vals = append(vals, EnumValue{
-					Name:  name,
-					Value: btfVal.Val,
-				})
+				value := uint64(btfVal.Val)
+				if signed {
+					// Sign extend values to 64 bit.
+					value = uint64(int32(btfVal.Val))
+				}
+				vals = append(vals, EnumValue{name, value})
 			}
-			typ = &Enum{name, raw.Size(), vals}
+			typ = &Enum{name, raw.Size(), signed, vals}
 
 		case kindForward:
 			if raw.KindFlag() {
diff --git a/elf_reader.go b/elf_reader.go
index 04d57c5..e0e8fd6 100644
--- a/elf_reader.go
+++ b/elf_reader.go
@@ -1110,6 +1110,7 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
 		{"lsm/", LSM, AttachLSMMac, 0},
 		{"lsm.s/", LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE},
 		{"iter/", Tracing, AttachTraceIter, 0},
+		{"iter.s/", Tracing, AttachTraceIter, unix.BPF_F_SLEEPABLE},
 		{"syscall", Syscall, AttachNone, 0},
 		{"xdp_devmap/", XDP, AttachXDPDevMap, 0},
 		{"xdp_cpumap/", XDP, AttachXDPCPUMap, 0},
diff --git a/features/misc.go b/features/misc.go
index b09d0ca..afe7bf7 100644
--- a/features/misc.go
+++ b/features/misc.go
@@ -84,8 +84,8 @@ func probeMisc(mt miscType) (err error) {
 		err = wrapProbeErrors(err)
 	}()
 
-	mc.Lock()
-	defer mc.Unlock()
+	miscs.Lock()
+	defer miscs.Unlock()
 	err, ok := miscs.miscTypes[mt]
 	if ok {
 		return err
diff --git a/internal/sys/types.go b/internal/sys/types.go
index fd2ec24..9fb6e08 100644
--- a/internal/sys/types.go
+++ b/internal/sys/types.go
@@ -6,14 +6,14 @@ import (
 	"unsafe"
 )
 
-type AdjRoomMode int32
+type AdjRoomMode uint32
 
 const (
 	BPF_ADJ_ROOM_NET AdjRoomMode = 0
 	BPF_ADJ_ROOM_MAC AdjRoomMode = 1
 )
 
-type AttachType int32
+type AttachType uint32
 
 const (
 	BPF_CGROUP_INET_INGRESS            AttachType = 0
@@ -62,7 +62,7 @@ const (
 	__MAX_BPF_ATTACH_TYPE              AttachType = 43
 )
 
-type Cmd int32
+type Cmd uint32
 
 const (
 	BPF_MAP_CREATE                  Cmd = 0
@@ -104,7 +104,7 @@ const (
 	BPF_PROG_BIND_MAP               Cmd = 35
 )
 
-type FunctionId int32
+type FunctionId uint32
 
 const (
 	BPF_FUNC_unspec                         FunctionId = 0
@@ -304,14 +304,14 @@ const (
 	__BPF_FUNC_MAX_ID                       FunctionId = 194
 )
 
-type HdrStartOff int32
+type HdrStartOff uint32
 
 const (
 	BPF_HDR_START_MAC HdrStartOff = 0
 	BPF_HDR_START_NET HdrStartOff = 1
 )
 
-type LinkType int32
+type LinkType uint32
 
 const (
 	BPF_LINK_TYPE_UNSPEC         LinkType = 0
@@ -326,7 +326,7 @@ const (
 	MAX_BPF_LINK_TYPE            LinkType = 9
 )
 
-type MapType int32
+type MapType uint32
 
 const (
 	BPF_MAP_TYPE_UNSPEC                MapType = 0
@@ -362,7 +362,7 @@ const (
 	BPF_MAP_TYPE_BLOOM_FILTER          MapType = 30
 )
 
-type ProgType int32
+type ProgType uint32
 
 const (
 	BPF_PROG_TYPE_UNSPEC                  ProgType = 0
@@ -399,7 +399,7 @@ const (
 	BPF_PROG_TYPE_SYSCALL                 ProgType = 31
 )
 
-type RetCode int32
+type RetCode uint32
 
 const (
 	BPF_OK          RetCode = 0
@@ -408,14 +408,14 @@ const (
 	BPF_LWT_REROUTE RetCode = 128
 )
 
-type SkAction int32
+type SkAction uint32
 
 const (
 	SK_DROP SkAction = 0
 	SK_PASS SkAction = 1
 )
 
-type StackBuildIdStatus int32
+type StackBuildIdStatus uint32
 
 const (
 	BPF_STACK_BUILD_ID_EMPTY StackBuildIdStatus = 0
@@ -423,13 +423,13 @@ const (
 	BPF_STACK_BUILD_ID_IP    StackBuildIdStatus = 2
 )
 
-type StatsType int32
+type StatsType uint32
 
 const (
 	BPF_STATS_RUN_TIME StatsType = 0
 )
 
-type XdpAction int32
+type XdpAction uint32
 
 const (
 	XDP_ABORTED  XdpAction = 0
diff --git a/link/kprobe.go b/link/kprobe.go
index d1574d2..99715f6 100644
--- a/link/kprobe.go
+++ b/link/kprobe.go
@@ -1,7 +1,6 @@
 package link
 
 import (
-	"bytes"
 	"crypto/rand"
 	"errors"
 	"fmt"
@@ -9,7 +8,6 @@ import (
 	"path/filepath"
 	"runtime"
 	"strings"
-	"sync"
 	"syscall"
 	"unsafe"
 
@@ -20,12 +18,6 @@ import (
 
 var (
 	kprobeEventsPath = filepath.Join(tracefsPath, "kprobe_events")
-
-	kprobeRetprobeBit = struct {
-		once  sync.Once
-		value uint64
-		err   error
-	}{}
 )
 
 type probeType uint8
@@ -83,13 +75,6 @@ func (pt probeType) PerfEventType(ret bool) perfEventType {
 	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():
@@ -238,14 +223,17 @@ func pmuKprobe(args probeArgs) (*perfEvent, error) {
 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)
+	et, err := readUint64FromFileOnce("%d\n", "/sys/bus/event_source/devices", typ.String(), "type")
+	if errors.Is(err, os.ErrNotExist) {
+		return nil, fmt.Errorf("%s: %w", typ, ErrNotSupported)
+	}
 	if err != nil {
 		return nil, err
 	}
 
 	var config uint64
 	if args.ret {
-		bit, err := typ.RetprobeBit()
+		bit, err := readUint64FromFileOnce("config:%d\n", "/sys/bus/event_source/devices", typ.String(), "/format/retprobe")
 		if err != nil {
 			return nil, err
 		}
@@ -536,35 +524,6 @@ func probePrefix(ret bool) string {
 	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
diff --git a/link/perf_event.go b/link/perf_event.go
index 0e5bd47..602f45b 100644
--- a/link/perf_event.go
+++ b/link/perf_event.go
@@ -7,8 +7,8 @@ import (
 	"os"
 	"path/filepath"
 	"runtime"
-	"strconv"
 	"strings"
+	"sync"
 	"unsafe"
 
 	"github.com/cilium/ebpf"
@@ -270,7 +270,11 @@ func unsafeStringPtr(str string) (unsafe.Pointer, error) {
 // 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")
+	path, err := sanitizePath(tracefsPath, "events", group, name, "id")
+	if err != nil {
+		return 0, err
+	}
+	tid, err := readUint64FromFile("%d\n", path)
 	if errors.Is(err, os.ErrNotExist) {
 		return 0, fmt.Errorf("trace event %s/%s: %w", group, name, os.ErrNotExist)
 	}
@@ -281,22 +285,6 @@ func getTraceEventID(group, name string) (uint64, error) {
 	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.
@@ -317,23 +305,75 @@ func openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) {
 	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) {
+func sanitizePath(base string, path ...string) (string, 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)
+		return "", fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput)
+	}
+	return p, nil
+}
+
+// readUint64FromFile reads a uint64 from a file.
+//
+// format specifies the contents of the file in fmt.Scanf syntax.
+func readUint64FromFile(format string, path ...string) (uint64, error) {
+	filename := filepath.Join(path...)
+	data, err := os.ReadFile(filename)
+	if err != nil {
+		return 0, fmt.Errorf("reading file %q: %w", filename, err)
 	}
 
-	data, err := os.ReadFile(p)
+	var value uint64
+	n, err := fmt.Fscanf(bytes.NewReader(data), format, &value)
 	if err != nil {
-		return 0, fmt.Errorf("reading file %s: %w", p, err)
+		return 0, fmt.Errorf("parsing file %q: %w", filename, err)
+	}
+	if n != 1 {
+		return 0, fmt.Errorf("parsing file %q: expected 1 item, got %d", filename, n)
+	}
+
+	return value, nil
+}
+
+type uint64FromFileKey struct {
+	format, path string
+}
+
+var uint64FromFileCache = struct {
+	sync.RWMutex
+	values map[uint64FromFileKey]uint64
+}{
+	values: map[uint64FromFileKey]uint64{},
+}
+
+// readUint64FromFileOnce is like readUint64FromFile but memoizes the result.
+func readUint64FromFileOnce(format string, path ...string) (uint64, error) {
+	filename := filepath.Join(path...)
+	key := uint64FromFileKey{format, filename}
+
+	uint64FromFileCache.RLock()
+	if value, ok := uint64FromFileCache.values[key]; ok {
+		uint64FromFileCache.RUnlock()
+		return value, nil
+	}
+	uint64FromFileCache.RUnlock()
+
+	value, err := readUint64FromFile(format, filename)
+	if err != nil {
+		return 0, err
+	}
+
+	uint64FromFileCache.Lock()
+	defer uint64FromFileCache.Unlock()
+
+	if value, ok := uint64FromFileCache.values[key]; ok {
+		// Someone else got here before us, use what is cached.
+		return value, nil
 	}
 
-	et := bytes.TrimSpace(data)
-	return strconv.ParseUint(string(et), 10, 64)
+	uint64FromFileCache.values[key] = value
+	return value, nil
 }
 
 // Probe BPF perf link.
diff --git a/link/uprobe.go b/link/uprobe.go
index 7c99b86..b4ce55f 100644
--- a/link/uprobe.go
+++ b/link/uprobe.go
@@ -7,7 +7,6 @@ import (
 	"os"
 	"path/filepath"
 	"strings"
-	"sync"
 
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/internal"
@@ -16,12 +15,6 @@ import (
 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
@@ -364,10 +357,3 @@ func uprobeToken(args probeArgs) string {
 
 	return po
 }
-
-func uretprobeBit() (uint64, error) {
-	uprobeRetprobeBit.once.Do(func() {
-		uprobeRetprobeBit.value, uprobeRetprobeBit.err = determineRetprobeBit(uprobeType)
-	})
-	return uprobeRetprobeBit.value, uprobeRetprobeBit.err
-}


Test patch:
diff --git a/btf/core_test.go b/btf/core_test.go
index 136da3d..9767578 100644
--- a/btf/core_test.go
+++ b/btf/core_test.go
@@ -209,7 +209,7 @@ func TestCOREFindEnumValue(t *testing.T) {
 		name                    string
 		local, target           Type
 		acc                     coreAccessor
-		localValue, targetValue int32
+		localValue, targetValue uint64
 	}{
 		{"a to b", a, b, coreAccessor{0}, 23, 0},
 		{"b to a", b, a, coreAccessor{1}, 123, 42},
diff --git a/btf/format_test.go b/btf/format_test.go
index 0cbe519..08d606d 100644
--- a/btf/format_test.go
+++ b/btf/format_test.go
@@ -24,8 +24,18 @@ func TestGoTypeDeclaration(t *testing.T) {
 		{&Int{Size: 8}, "type t uint64"},
 		{&Typedef{Name: "frob", Type: &Int{Size: 8}}, "type t uint64"},
 		{&Int{Size: 16}, "type t uint128"},
-		{&Enum{Values: []EnumValue{{"FOO", 32}}, Size: 4}, "type t int32; const ( tFOO t = 32; )"},
-		{&Enum{Values: []EnumValue{{"BAR", 1}}, Size: 1}, "type t int8; const ( tBAR t = 1; )"},
+		{&Enum{Values: []EnumValue{{"FOO", 32}}, Size: 4}, "type t uint32; const ( tFOO t = 32; )"},
+		{&Enum{Values: []EnumValue{{"BAR", 1}}, Size: 1, Signed: true}, "type t int8; const ( tBAR t = 1; )"},
+		{
+			&Struct{
+				Name: "enum literals",
+				Size: 2,
+				Members: []Member{
+					{Name: "enum", Type: &Enum{Values: []EnumValue{{"BAR", 1}}, Size: 2}, Offset: 0},
+				},
+			},
+			"type t struct { enum uint16; }",
+		},
 		{&Array{Nelems: 2, Type: &Int{Size: 1}}, "type t [2]uint8"},
 		{
 			&Union{
@@ -155,12 +165,12 @@ func TestGoTypeDeclarationNamed(t *testing.T) {
 		named  []Type
 		output string
 	}{
-		{e1, []Type{e1}, "type t int32"},
+		{e1, []Type{e1}, "type t uint32"},
 		{s1, []Type{e1, s1}, "type t struct { frob E1; }"},
 		{s2, []Type{e1}, "type t struct { frood struct { frob E1; }; }"},
 		{s2, []Type{e1, s1}, "type t struct { frood S1; }"},
-		{td, nil, "type t int32"},
-		{td, []Type{td}, "type t int32"},
+		{td, nil, "type t uint32"},
+		{td, []Type{td}, "type t uint32"},
 		{arr, []Type{td}, "type t [1]TD"},
 	}
 
@@ -212,6 +222,19 @@ func TestGoTypeDeclarationCycle(t *testing.T) {
 	}
 }
 
+func TestRejectBogusTypes(t *testing.T) {
+	var gf GoFormatter
+	_, err := gf.TypeDeclaration("t", &Struct{
+		Size: 1,
+		Members: []Member{
+			{Name: "foo", Type: &Int{Size: 2}, Offset: 0},
+		},
+	})
+	if err == nil {
+		t.Fatal("TypeDeclaration does not reject bogus struct")
+	}
+}
+
 func mustGoTypeDeclaration(tb testing.TB, typ Type, names map[Type]string, id func(string) string) string {
 	tb.Helper()
 
diff --git a/cmd/bpf2go/test/test_bpfeb.go b/cmd/bpf2go/test/test_bpfeb.go
index d750782..1b285e0 100644
--- a/cmd/bpf2go/test/test_bpfeb.go
+++ b/cmd/bpf2go/test/test_bpfeb.go
@@ -20,7 +20,7 @@ type testBarfoo struct {
 	Boo testE
 }
 
-type testE int32
+type testE uint32
 
 const (
 	testEHOOPY testE = 0
diff --git a/cmd/bpf2go/test/test_bpfel.go b/cmd/bpf2go/test/test_bpfel.go
index 659466c..236d310 100644
--- a/cmd/bpf2go/test/test_bpfel.go
+++ b/cmd/bpf2go/test/test_bpfel.go
@@ -20,7 +20,7 @@ type testBarfoo struct {
 	Boo testE
 }
 
-type testE int32
+type testE uint32
 
 const (
 	testEHOOPY testE = 0
diff --git a/link/kprobe_test.go b/link/kprobe_test.go
index 2cf07ea..a12378e 100644
--- a/link/kprobe_test.go
+++ b/link/kprobe_test.go
@@ -307,19 +307,6 @@ func TestKprobeTraceFSGroup(t *testing.T) {
 	c.Assert(err, qt.Not(qt.IsNil))
 }
 
-func TestDetermineRetprobeBit(t *testing.T) {
-	testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU")
-	c := qt.New(t)
-
-	rpk, err := kretprobeBit()
-	c.Assert(err, qt.IsNil)
-	c.Assert(rpk, qt.Equals, uint64(0))
-
-	rpu, err := uretprobeBit()
-	c.Assert(err, qt.IsNil)
-	c.Assert(rpu, qt.Equals, uint64(0))
-}
-
 func TestKprobeProgramCall(t *testing.T) {
 	m, p := newUpdaterMapProg(t, ebpf.Kprobe)
 
diff --git a/link/perf_event_test.go b/link/perf_event_test.go
index aadaa7a..4395c47 100644
--- a/link/perf_event_test.go
+++ b/link/perf_event_test.go
@@ -2,28 +2,12 @@ package link
 
 import (
 	"errors"
-	"os"
 	"testing"
 
 	"github.com/cilium/ebpf/internal/testutils"
 	qt "github.com/frankban/quicktest"
 )
 
-func TestTraceEventTypePMU(t *testing.T) {
-	// Requires at least 4.17 (e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU")
-	testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU")
-
-	c := qt.New(t)
-
-	et, err := getPMUEventType(kprobeType)
-	c.Assert(err, qt.IsNil)
-	c.Assert(et, qt.Not(qt.Equals), 0)
-
-	et, err = getPMUEventType(uprobeType)
-	c.Assert(err, qt.IsNil)
-	c.Assert(et, qt.Not(qt.Equals), 0)
-}
-
 func TestTraceEventID(t *testing.T) {
 	c := qt.New(t)
 
@@ -32,15 +16,15 @@ func TestTraceEventID(t *testing.T) {
 	c.Assert(eid, qt.Not(qt.Equals), 0)
 }
 
-func TestTraceReadID(t *testing.T) {
-	_, err := uint64FromFile("/base/path/", "../escaped")
+func TestSanitizePath(t *testing.T) {
+	_, err := sanitizePath("/base/path/", "../escaped")
 	if !errors.Is(err, errInvalidInput) {
 		t.Errorf("expected error %s, got: %s", errInvalidInput, err)
 	}
 
-	_, err = uint64FromFile("/base/path/not", "../not/escaped")
-	if !errors.Is(err, os.ErrNotExist) {
-		t.Errorf("expected os.ErrNotExist, got: %s", err)
+	_, err = sanitizePath("/base/path/not", "../not/escaped")
+	if err != nil {
+		t.Errorf("expected no error, got: %s", err)
 	}
 }