Repository: cilium/ebpf
Pull request #1540 (2024-08-07 07:31:27)

internal: split out linux specific bits into a separate package
internal: move Pin and Unpin to sys

    Pin and Unpin implement library-specific behaviour for BPF object pinning.
    Move it to package sys since that abstracts away BPF things.

    Signed-off-by: Lorenz Bauer <lmb@isovalent.com>

internal: move linux specific code to separate package

    Move some linux specific code out of package internal into package linux. As
    a rule of thumb, code probably belongs in this package if it calls into
    package unix.

    Signed-off-by: Lorenz Bauer <lmb@isovalent.com>

internal/kconfig: move Find to package linux

    Move Find to package linux, since it depends on deriving a file name from
    the current linux kernel version.

    This means that package kconfig only implements parsing of the kconfig
    format.

    Signed-off-by: Lorenz Bauer <lmb@isovalent.com>

examples: add linux build tag to uretprobe, ringbuf, socket_dist

    The two examples call into package unix which is not available on non-Unix
    platforms. Add build tags so that

        GOOS=windows go build ./...

    works without errors.

    Signed-off-by: Lorenz Bauer <lmb@isovalent.com>

program: use unix.SIGUSR1 in tests

    Signed-off-by: Lorenz Bauer <lmb@isovalent.com>

link: use internal/unix in netkit tests

    Signed-off-by: Lorenz Bauer <lmb@isovalent.com>

CI: cross build for Windows instead of Darwin

    CI cross compiles the package and its tests for Darwin to make sure that
    it's possible to edit the source code on other Unix like platforms.

    Up the game a little and cross build for Windows instead of Darwin: this
    flushes out accidental uses of x/sys/unix instead of internal/unix.

    Signed-off-by: Lorenz Bauer <lmb@isovalent.com>



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2919685..b3f7399 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -55,9 +55,9 @@ jobs:
         run: go build -v ./...
         working-directory: ./examples
 
-      - name: Cross build darwin
+      - name: Cross build windows
         env:
-          GOOS: darwin
+          GOOS: windows
         run: |
           go build -v ./...
           go test -c -o /dev/null ./... >/dev/null
diff --git a/btf/kernel.go b/btf/kernel.go
index 8584ebc..e5c54ce 100644
--- a/btf/kernel.go
+++ b/btf/kernel.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/cilium/ebpf/internal"
 	"github.com/cilium/ebpf/internal/kallsyms"
+	"github.com/cilium/ebpf/internal/linux"
 )
 
 var kernelBTF = struct {
@@ -130,7 +131,7 @@ func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) {
 
 // findVMLinux scans multiple well-known paths for vmlinux kernel images.
 func findVMLinux() (*os.File, error) {
-	release, err := internal.KernelRelease()
+	release, err := linux.KernelRelease()
 	if err != nil {
 		return nil, err
 	}
diff --git a/collection.go b/collection.go
index b2cb214..3623105 100644
--- a/collection.go
+++ b/collection.go
@@ -11,6 +11,7 @@ import (
 	"github.com/cilium/ebpf/btf"
 	"github.com/cilium/ebpf/internal"
 	"github.com/cilium/ebpf/internal/kconfig"
+	"github.com/cilium/ebpf/internal/linux"
 	"github.com/cilium/ebpf/internal/sysenc"
 )
 
@@ -619,7 +620,7 @@ func resolveKconfig(m *MapSpec) error {
 				return fmt.Errorf("variable %s must be a 32 bits integer, got %s", n, v.Type)
 			}
 
-			kv, err := internal.KernelVersion()
+			kv, err := linux.KernelVersion()
 			if err != nil {
 				return fmt.Errorf("getting kernel version: %w", err)
 			}
@@ -651,7 +652,7 @@ func resolveKconfig(m *MapSpec) error {
 
 	// We only parse kconfig file if a CONFIG_* variable was found.
 	if len(configs) > 0 {
-		f, err := kconfig.Find()
+		f, err := linux.FindKConfig()
 		if err != nil {
 			return fmt.Errorf("cannot find a kconfig file: %w", err)
 		}
diff --git a/examples/ringbuffer/bpf_bpfeb.go b/examples/ringbuffer/bpf_bpfeb.go
index c9eef6d..3df7da1 100644
--- a/examples/ringbuffer/bpf_bpfeb.go
+++ b/examples/ringbuffer/bpf_bpfeb.go
@@ -1,5 +1,5 @@
 // Code generated by bpf2go; DO NOT EDIT.
-//go:build mips || mips64 || ppc64 || s390x
+//go:build (mips || mips64 || ppc64 || s390x) && linux
 
 package main
 
diff --git a/examples/ringbuffer/bpf_bpfel.go b/examples/ringbuffer/bpf_bpfel.go
index 12a5de6..f3d67de 100644
--- a/examples/ringbuffer/bpf_bpfel.go
+++ b/examples/ringbuffer/bpf_bpfel.go
@@ -1,5 +1,5 @@
 // Code generated by bpf2go; DO NOT EDIT.
-//go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64
+//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64) && linux
 
 package main
 
diff --git a/examples/ringbuffer/main.go b/examples/ringbuffer/main.go
index 5f9c69e..5b5abe1 100644
--- a/examples/ringbuffer/main.go
+++ b/examples/ringbuffer/main.go
@@ -1,3 +1,5 @@
+//go:build linux
+
 package main
 
 import (
@@ -9,13 +11,14 @@ import (
 	"os/signal"
 	"syscall"
 
+	"golang.org/x/sys/unix"
+
 	"github.com/cilium/ebpf/link"
 	"github.com/cilium/ebpf/ringbuf"
 	"github.com/cilium/ebpf/rlimit"
-	"golang.org/x/sys/unix"
 )
 
-//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -type event bpf ringbuffer.c -- -I../headers
+//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -tags linux -type event bpf ringbuffer.c -- -I../headers
 
 func main() {
 	// Name of the kernel function to trace.
diff --git a/examples/uretprobe/bpf_x86_bpfel.go b/examples/uretprobe/bpf_x86_bpfel.go
index 11bd264..309c341 100644
--- a/examples/uretprobe/bpf_x86_bpfel.go
+++ b/examples/uretprobe/bpf_x86_bpfel.go
@@ -1,5 +1,5 @@
 // Code generated by bpf2go; DO NOT EDIT.
-//go:build 386 || amd64
+//go:build (386 || amd64) && linux
 
 package main
 
diff --git a/examples/uretprobe/main.go b/examples/uretprobe/main.go
index ac258f6..30d1ad7 100644
--- a/examples/uretprobe/main.go
+++ b/examples/uretprobe/main.go
@@ -2,7 +2,7 @@
 // The program will be attached to the 'readline' symbol in the binary '/bin/bash' and print out
 // the line which 'readline' functions returns to the caller.
 
-//go:build amd64
+//go:build amd64 && linux
 
 package main
 
@@ -15,13 +15,14 @@ import (
 	"os/signal"
 	"syscall"
 
+	"golang.org/x/sys/unix"
+
 	"github.com/cilium/ebpf/link"
 	"github.com/cilium/ebpf/perf"
 	"github.com/cilium/ebpf/rlimit"
-	"golang.org/x/sys/unix"
 )
 
-//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target amd64 -type event bpf uretprobe.c -- -I../headers
+//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target amd64 -tags linux -type event bpf uretprobe.c -- -I../headers
 
 const (
 	// The path to the ELF binary containing the function to trace.
diff --git a/features/version.go b/features/version.go
index 69e1c39..d54d3ea 100644
--- a/features/version.go
+++ b/features/version.go
@@ -1,6 +1,6 @@
 package features
 
-import "github.com/cilium/ebpf/internal"
+import "github.com/cilium/ebpf/internal/linux"
 
 // LinuxVersionCode returns the version of the currently running kernel
 // as defined in the LINUX_VERSION_CODE compile-time macro. It is represented
@@ -10,7 +10,7 @@ import "github.com/cilium/ebpf/internal"
 // kernel features, always prefer feature probes in this package. Some
 // distributions backport or disable eBPF features.
 func LinuxVersionCode() (uint32, error) {
-	v, err := internal.KernelVersion()
+	v, err := linux.KernelVersion()
 	if err != nil {
 		return 0, err
 	}
diff --git a/internal/kconfig/kconfig.go b/internal/kconfig/kconfig.go
index 1921e4f..c32c066 100644
--- a/internal/kconfig/kconfig.go
+++ b/internal/kconfig/kconfig.go
@@ -1,3 +1,4 @@
+// Package kconfig implements a parser for the format of Linux's .config file.
 package kconfig
 
 import (
@@ -7,7 +8,6 @@ import (
 	"fmt"
 	"io"
 	"math"
-	"os"
 	"strconv"
 	"strings"
 
@@ -15,30 +15,6 @@ import (
 	"github.com/cilium/ebpf/internal"
 )
 
-// Find find a kconfig file on the host.
-// It first reads from /boot/config- of the current running kernel and tries
-// /proc/config.gz if nothing was found in /boot.
-// If none of the file provide a kconfig, it returns an error.
-func Find() (*os.File, error) {
-	kernelRelease, err := internal.KernelRelease()
-	if err != nil {
-		return nil, fmt.Errorf("cannot get kernel release: %w", err)
-	}
-
-	path := "/boot/config-" + kernelRelease
-	f, err := os.Open(path)
-	if err == nil {
-		return f, nil
-	}
-
-	f, err = os.Open("/proc/config.gz")
-	if err == nil {
-		return f, nil
-	}
-
-	return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path)
-}
-
 // Parse parses the kconfig file for which a reader is given.
 // All the CONFIG_* which are in filter and which are set set will be
 // put in the returned map as key with their corresponding value as map value.
diff --git a/internal/auxv.go b/internal/linux/auxv.go
similarity index 98%
rename from internal/auxv.go
rename to internal/linux/auxv.go
index 45fd0d3..98bb5d8 100644
--- a/internal/auxv.go
+++ b/internal/linux/auxv.go
@@ -1,4 +1,4 @@
-package internal
+package linux
 
 import (
 	"errors"
diff --git a/internal/linux/doc.go b/internal/linux/doc.go
new file mode 100644
index 0000000..064e754
--- /dev/null
+++ b/internal/linux/doc.go
@@ -0,0 +1,2 @@
+// Package linux contains OS specific wrappers around package unix.
+package linux
diff --git a/internal/linux/kconfig.go b/internal/linux/kconfig.go
new file mode 100644
index 0000000..1488ecb
--- /dev/null
+++ b/internal/linux/kconfig.go
@@ -0,0 +1,31 @@
+package linux
+
+import (
+	"fmt"
+	"os"
+)
+
+// FindKConfig searches for a kconfig file on the host.
+//
+// It first reads from /boot/config- of the current running kernel and tries
+// /proc/config.gz if nothing was found in /boot.
+// If none of the file provide a kconfig, it returns an error.
+func FindKConfig() (*os.File, error) {
+	kernelRelease, err := KernelRelease()
+	if err != nil {
+		return nil, fmt.Errorf("cannot get kernel release: %w", err)
+	}
+
+	path := "/boot/config-" + kernelRelease
+	f, err := os.Open(path)
+	if err == nil {
+		return f, nil
+	}
+
+	f, err = os.Open("/proc/config.gz")
+	if err == nil {
+		return f, nil
+	}
+
+	return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path)
+}
diff --git a/internal/statfs.go b/internal/linux/statfs.go
similarity index 96%
rename from internal/statfs.go
rename to internal/linux/statfs.go
index 44c02d6..e268c06 100644
--- a/internal/statfs.go
+++ b/internal/linux/statfs.go
@@ -1,4 +1,4 @@
-package internal
+package linux
 
 import (
 	"unsafe"
diff --git a/internal/testdata/auxv32le.bin b/internal/linux/testdata/auxv32le.bin
similarity index 100%
rename from internal/testdata/auxv32le.bin
rename to internal/linux/testdata/auxv32le.bin
diff --git a/internal/testdata/auxv64le.bin b/internal/linux/testdata/auxv64le.bin
similarity index 100%
rename from internal/testdata/auxv64le.bin
rename to internal/linux/testdata/auxv64le.bin
diff --git a/internal/testdata/auxv64le_no_vdso.bin b/internal/linux/testdata/auxv64le_no_vdso.bin
similarity index 100%
rename from internal/testdata/auxv64le_no_vdso.bin
rename to internal/linux/testdata/auxv64le_no_vdso.bin
diff --git a/internal/testdata/vdso.bin b/internal/linux/testdata/vdso.bin
similarity index 100%
rename from internal/testdata/vdso.bin
rename to internal/linux/testdata/vdso.bin
diff --git a/internal/testdata/vdso_multiple_notes.bin b/internal/linux/testdata/vdso_multiple_notes.bin
similarity index 100%
rename from internal/testdata/vdso_multiple_notes.bin
rename to internal/linux/testdata/vdso_multiple_notes.bin
diff --git a/internal/vdso.go b/internal/linux/vdso.go
similarity index 93%
rename from internal/vdso.go
rename to internal/linux/vdso.go
index 1049278..1d8d0ef 100644
--- a/internal/vdso.go
+++ b/internal/linux/vdso.go
@@ -1,4 +1,4 @@
-package internal
+package linux
 
 import (
 	"debug/elf"
@@ -9,6 +9,7 @@ import (
 	"math"
 	"os"
 
+	"github.com/cilium/ebpf/internal"
 	"github.com/cilium/ebpf/internal/unix"
 )
 
@@ -82,7 +83,7 @@ type elfNoteHeader struct {
 // vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in
 // the ELF notes section of the binary provided by the reader.
 func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {
-	hdr, err := NewSafeELFFile(r)
+	hdr, err := internal.NewSafeELFFile(r)
 	if err != nil {
 		return 0, fmt.Errorf("reading vDSO ELF: %w", err)
 	}
@@ -110,7 +111,7 @@ func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {
 			var name string
 			if n.NameSize > 0 {
 				// Read the note name, aligned to 4 bytes.
-				buf := make([]byte, Align(n.NameSize, 4))
+				buf := make([]byte, internal.Align(n.NameSize, 4))
 				if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil {
 					return 0, fmt.Errorf("reading note name: %w", err)
 				}
@@ -132,7 +133,7 @@ func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {
 				}
 
 				// Discard the note descriptor if it exists but we're not interested in it.
-				if _, err := io.CopyN(io.Discard, sr, int64(Align(n.DescSize, 4))); err != nil {
+				if _, err := io.CopyN(io.Discard, sr, int64(internal.Align(n.DescSize, 4))); err != nil {
 					return 0, err
 				}
 			}
diff --git a/internal/linux/version.go b/internal/linux/version.go
new file mode 100644
index 0000000..798dd3f
--- /dev/null
+++ b/internal/linux/version.go
@@ -0,0 +1,34 @@
+package linux
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/unix"
+)
+
+// KernelVersion returns the version of the currently running kernel.
+var KernelVersion = sync.OnceValues(detectKernelVersion)
+
+// detectKernelVersion returns the version of the running kernel.
+func detectKernelVersion() (internal.Version, error) {
+	vc, err := vdsoVersion()
+	if err != nil {
+		return internal.Version{}, err
+	}
+	return internal.NewVersionFromCode(vc), nil
+}
+
+// KernelRelease returns the release string of the running kernel.
+// Its format depends on the Linux distribution and corresponds to directory
+// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and
+// 4.19.0-16-amd64.
+func KernelRelease() (string, error) {
+	var uname unix.Utsname
+	if err := unix.Uname(&uname); err != nil {
+		return "", fmt.Errorf("uname failed: %w", err)
+	}
+
+	return unix.ByteSliceToString(uname.Release[:]), nil
+}
diff --git a/internal/pinning.go b/internal/sys/pinning.go
similarity index 77%
rename from internal/pinning.go
rename to internal/sys/pinning.go
index 01d892f..9a4c6c7 100644
--- a/internal/pinning.go
+++ b/internal/sys/pinning.go
@@ -1,4 +1,4 @@
-package internal
+package sys
 
 import (
 	"errors"
@@ -7,11 +7,11 @@ import (
 	"path/filepath"
 	"runtime"
 
-	"github.com/cilium/ebpf/internal/sys"
+	"github.com/cilium/ebpf/internal/linux"
 	"github.com/cilium/ebpf/internal/unix"
 )
 
-func Pin(currentPath, newPath string, fd *sys.FD) error {
+func Pin(currentPath, newPath string, fd *FD) error {
 	if newPath == "" {
 		return errors.New("given pinning path cannot be empty")
 	}
@@ -19,7 +19,7 @@ func Pin(currentPath, newPath string, fd *sys.FD) error {
 		return nil
 	}
 
-	fsType, err := FSType(filepath.Dir(newPath))
+	fsType, err := linux.FSType(filepath.Dir(newPath))
 	if err != nil {
 		return err
 	}
@@ -30,8 +30,8 @@ func Pin(currentPath, newPath string, fd *sys.FD) error {
 	defer runtime.KeepAlive(fd)
 
 	if currentPath == "" {
-		return sys.ObjPin(&sys.ObjPinAttr{
-			Pathname: sys.NewStringPointer(newPath),
+		return ObjPin(&ObjPinAttr{
+			Pathname: NewStringPointer(newPath),
 			BpfFd:    fd.Uint(),
 		})
 	}
@@ -47,8 +47,8 @@ func Pin(currentPath, newPath string, fd *sys.FD) error {
 		return fmt.Errorf("unable to move pinned object to new path %v: %w", newPath, err)
 	}
 	// Internal state not in sync with the file system so let's fix it.
-	return sys.ObjPin(&sys.ObjPinAttr{
-		Pathname: sys.NewStringPointer(newPath),
+	return ObjPin(&ObjPinAttr{
+		Pathname: NewStringPointer(newPath),
 		BpfFd:    fd.Uint(),
 	})
 }
diff --git a/internal/tracefs/kprobe.go b/internal/tracefs/kprobe.go
index 897740f..800d352 100644
--- a/internal/tracefs/kprobe.go
+++ b/internal/tracefs/kprobe.go
@@ -12,6 +12,7 @@ import (
 	"syscall"
 
 	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/linux"
 	"github.com/cilium/ebpf/internal/unix"
 )
 
@@ -121,7 +122,7 @@ var getTracefsPath = sync.OnceValues(func() (string, error) {
 		// RHEL/CentOS
 		{"/sys/kernel/debug/tracing", unix.DEBUGFS_MAGIC},
 	} {
-		if fsType, err := internal.FSType(p.path); err == nil && fsType == p.fsType {
+		if fsType, err := linux.FSType(p.path); err == nil && fsType == p.fsType {
 			return p.path, nil
 		}
 	}
diff --git a/internal/unix/types_linux.go b/internal/unix/types_linux.go
index c09b8d5..144e608 100644
--- a/internal/unix/types_linux.go
+++ b/internal/unix/types_linux.go
@@ -81,6 +81,7 @@ const (
 	SO_DETACH_BPF             = linux.SO_DETACH_BPF
 	SOL_SOCKET                = linux.SOL_SOCKET
 	SIGPROF                   = linux.SIGPROF
+	SIGUSR1                   = linux.SIGUSR1
 	SIG_BLOCK                 = linux.SIG_BLOCK
 	SIG_UNBLOCK               = linux.SIG_UNBLOCK
 	BPF_FS_MAGIC              = linux.BPF_FS_MAGIC
@@ -88,6 +89,8 @@ const (
 	DEBUGFS_MAGIC             = linux.DEBUGFS_MAGIC
 	BPF_RB_NO_WAKEUP          = linux.BPF_RB_NO_WAKEUP
 	BPF_RB_FORCE_WAKEUP       = linux.BPF_RB_FORCE_WAKEUP
+	AF_UNSPEC                 = linux.AF_UNSPEC
+	IFF_UP                    = linux.IFF_UP
 )
 
 type Statfs_t = linux.Statfs_t
diff --git a/internal/unix/types_other.go b/internal/unix/types_other.go
index 0228818..06cc3a0 100644
--- a/internal/unix/types_other.go
+++ b/internal/unix/types_other.go
@@ -84,6 +84,7 @@ const (
 	SO_DETACH_BPF
 	SOL_SOCKET
 	SIGPROF
+	SIGUSR1
 	SIG_BLOCK
 	SIG_UNBLOCK
 	BPF_FS_MAGIC
@@ -92,6 +93,8 @@ const (
 	BPF_RB_NO_WAKEUP
 	BPF_RB_FORCE_WAKEUP
 	BPF_F_LOCK
+	AF_UNSPEC
+	IFF_UP
 )
 
 type Statfs_t struct {
diff --git a/internal/version.go b/internal/version.go
index acd4650..a230830 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -2,9 +2,6 @@ package internal
 
 import (
 	"fmt"
-	"sync"
-
-	"github.com/cilium/ebpf/internal/unix"
 )
 
 const (
@@ -78,30 +75,3 @@ func (v Version) Kernel() uint32 {
 	// each other when overflowing 8 bits.
 	return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s))
 }
-
-// KernelVersion returns the version of the currently running kernel.
-var KernelVersion = sync.OnceValues(func() (Version, error) {
-	return detectKernelVersion()
-})
-
-// detectKernelVersion returns the version of the running kernel.
-func detectKernelVersion() (Version, error) {
-	vc, err := vdsoVersion()
-	if err != nil {
-		return Version{}, err
-	}
-	return NewVersionFromCode(vc), nil
-}
-
-// KernelRelease returns the release string of the running kernel.
-// Its format depends on the Linux distribution and corresponds to directory
-// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and
-// 4.19.0-16-amd64.
-func KernelRelease() (string, error) {
-	var uname unix.Utsname
-	if err := unix.Uname(&uname); err != nil {
-		return "", fmt.Errorf("uname failed: %w", err)
-	}
-
-	return unix.ByteSliceToString(uname.Release[:]), nil
-}
diff --git a/link/link.go b/link/link.go
index 9c34616..eef834a 100644
--- a/link/link.go
+++ b/link/link.go
@@ -380,7 +380,7 @@ 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.Pin(l.pinnedPath, fileName, l.fd); err != nil {
+	if err := sys.Pin(l.pinnedPath, fileName, l.fd); err != nil {
 		return err
 	}
 	l.pinnedPath = fileName
@@ -389,7 +389,7 @@ func (l *RawLink) Pin(fileName string) error {
 
 // Unpin implements the Link interface.
 func (l *RawLink) Unpin() error {
-	if err := internal.Unpin(l.pinnedPath); err != nil {
+	if err := sys.Unpin(l.pinnedPath); err != nil {
 		return err
 	}
 	l.pinnedPath = ""
diff --git a/map.go b/map.go
index 128767b..906519f 100644
--- a/map.go
+++ b/map.go
@@ -1349,7 +1349,7 @@ func (m *Map) Clone() (*Map, error) {
 // This requires bpffs to be mounted above fileName.
 // See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd
 func (m *Map) Pin(fileName string) error {
-	if err := internal.Pin(m.pinnedPath, fileName, m.fd); err != nil {
+	if err := sys.Pin(m.pinnedPath, fileName, m.fd); err != nil {
 		return err
 	}
 	m.pinnedPath = fileName
@@ -1362,7 +1362,7 @@ func (m *Map) Pin(fileName string) error {
 //
 // Unpinning an unpinned Map returns nil.
 func (m *Map) Unpin() error {
-	if err := internal.Unpin(m.pinnedPath); err != nil {
+	if err := sys.Unpin(m.pinnedPath); err != nil {
 		return err
 	}
 	m.pinnedPath = ""
diff --git a/prog.go b/prog.go
index 1761654..776799e 100644
--- a/prog.go
+++ b/prog.go
@@ -16,6 +16,7 @@ import (
 	"github.com/cilium/ebpf/btf"
 	"github.com/cilium/ebpf/internal"
 	"github.com/cilium/ebpf/internal/kallsyms"
+	"github.com/cilium/ebpf/internal/linux"
 	"github.com/cilium/ebpf/internal/sys"
 	"github.com/cilium/ebpf/internal/sysenc"
 	"github.com/cilium/ebpf/internal/unix"
@@ -253,7 +254,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
 	// Overwrite Kprobe program version if set to zero or the magic version constant.
 	kv := spec.KernelVersion
 	if spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) {
-		v, err := internal.KernelVersion()
+		v, err := linux.KernelVersion()
 		if err != nil {
 			return nil, fmt.Errorf("detecting kernel version: %w", err)
 		}
@@ -590,7 +591,7 @@ func (p *Program) Clone() (*Program, error) {
 // This requires bpffs to be mounted above fileName.
 // See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd
 func (p *Program) Pin(fileName string) error {
-	if err := internal.Pin(p.pinnedPath, fileName, p.fd); err != nil {
+	if err := sys.Pin(p.pinnedPath, fileName, p.fd); err != nil {
 		return err
 	}
 	p.pinnedPath = fileName
@@ -603,7 +604,7 @@ func (p *Program) Pin(fileName string) error {
 //
 // Unpinning an unpinned Program returns nil.
 func (p *Program) Unpin() error {
-	if err := internal.Unpin(p.pinnedPath); err != nil {
+	if err := sys.Unpin(p.pinnedPath); err != nil {
 		return err
 	}
 	p.pinnedPath = ""


Test patch:
diff --git a/elf_reader_test.go b/elf_reader_test.go
index da164b4..54bfdf2 100644
--- a/elf_reader_test.go
+++ b/elf_reader_test.go
@@ -14,6 +14,7 @@ import (
 
 	"github.com/cilium/ebpf/btf"
 	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/linux"
 	"github.com/cilium/ebpf/internal/sys"
 	"github.com/cilium/ebpf/internal/testutils"
 
@@ -604,7 +605,7 @@ func TestKconfigKernelVersion(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	v, err := internal.KernelVersion()
+	v, err := linux.KernelVersion()
 	if err != nil {
 		t.Fatalf("getting kernel version: %s", err)
 	}
diff --git a/example_sock_extract_dist_test.go b/example_sock_extract_dist_test.go
index cbd1254..ad88d24 100644
--- a/example_sock_extract_dist_test.go
+++ b/example_sock_extract_dist_test.go
@@ -1,3 +1,5 @@
+//go:build linux
+
 package ebpf_test
 
 // This code is derived from https://github.com/cloudflare/cloudflare-blog/tree/master/2018-03-ebpf
diff --git a/internal/auxv_test.go b/internal/linux/auxv_test.go
similarity index 94%
rename from internal/auxv_test.go
rename to internal/linux/auxv_test.go
index ac9d043..14ab0eb 100644
--- a/internal/auxv_test.go
+++ b/internal/linux/auxv_test.go
@@ -1,4 +1,4 @@
-package internal
+package linux
 
 import (
 	"encoding/binary"
@@ -8,6 +8,7 @@ import (
 	"testing"
 	"unsafe"
 
+	"github.com/cilium/ebpf/internal"
 	"github.com/cilium/ebpf/internal/unix"
 )
 
@@ -66,7 +67,7 @@ func newAuxFileReader(path string, order binary.ByteOrder, uintptrIs32bits bool)
 
 func newDefaultAuxvFileReader() (auxvPairReader, error) {
 	const uintptrIs32bits = unsafe.Sizeof((uintptr)(0)) == 4
-	return newAuxFileReader("/proc/self/auxv", NativeEndian, uintptrIs32bits)
+	return newAuxFileReader("/proc/self/auxv", internal.NativeEndian, uintptrIs32bits)
 }
 
 func TestAuxvBothSourcesEqual(t *testing.T) {
diff --git a/internal/statfs_test.go b/internal/linux/statfs_test.go
similarity index 95%
rename from internal/statfs_test.go
rename to internal/linux/statfs_test.go
index 0ff4c5d..d1fed82 100644
--- a/internal/statfs_test.go
+++ b/internal/linux/statfs_test.go
@@ -1,4 +1,4 @@
-package internal
+package linux
 
 import (
 	"testing"
diff --git a/internal/vdso_test.go b/internal/linux/vdso_test.go
similarity index 99%
rename from internal/vdso_test.go
rename to internal/linux/vdso_test.go
index bb6f73b..b50c80c 100644
--- a/internal/vdso_test.go
+++ b/internal/linux/vdso_test.go
@@ -1,4 +1,4 @@
-package internal
+package linux
 
 import (
 	"encoding/binary"
diff --git a/internal/linux/version_test.go b/internal/linux/version_test.go
new file mode 100644
index 0000000..8c122dc
--- /dev/null
+++ b/internal/linux/version_test.go
@@ -0,0 +1,23 @@
+package linux
+
+import (
+	"testing"
+
+	"github.com/go-quicktest/qt"
+)
+
+func TestCurrentKernelVersion(t *testing.T) {
+	_, err := KernelVersion()
+	qt.Assert(t, qt.IsNil(err))
+}
+
+func TestKernelRelease(t *testing.T) {
+	r, err := KernelRelease()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if r == "" {
+		t.Fatal("unexpected empty kernel release")
+	}
+}
diff --git a/internal/testutils/feature.go b/internal/testutils/feature.go
index 81a169f..75edcd1 100644
--- a/internal/testutils/feature.go
+++ b/internal/testutils/feature.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/linux"
 )
 
 const (
@@ -113,7 +114,7 @@ func isKernelLessThan(tb testing.TB, minv internal.Version) bool {
 func kernelVersion(tb testing.TB) internal.Version {
 	tb.Helper()
 
-	v, err := internal.KernelVersion()
+	v, err := linux.KernelVersion()
 	if err != nil {
 		tb.Fatal(err)
 	}
diff --git a/internal/version_test.go b/internal/version_test.go
index 6d75ece..dcf4d25 100644
--- a/internal/version_test.go
+++ b/internal/version_test.go
@@ -2,8 +2,6 @@ package internal
 
 import (
 	"testing"
-
-	"github.com/go-quicktest/qt"
 )
 
 func TestVersion(t *testing.T) {
@@ -48,11 +46,6 @@ func TestKernelVersion(t *testing.T) {
 	}
 }
 
-func TestCurrentKernelVersion(t *testing.T) {
-	_, err := KernelVersion()
-	qt.Assert(t, qt.IsNil(err))
-}
-
 func TestVersionFromCode(t *testing.T) {
 	var tests = []struct {
 		name string
@@ -74,14 +67,3 @@ func TestVersionFromCode(t *testing.T) {
 		})
 	}
 }
-
-func TestKernelRelease(t *testing.T) {
-	r, err := KernelRelease()
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if r == "" {
-		t.Fatal("unexpected empty kernel release")
-	}
-}
diff --git a/link/netkit_test.go b/link/netkit_test.go
index 51d190e..3e1acf5 100644
--- a/link/netkit_test.go
+++ b/link/netkit_test.go
@@ -5,12 +5,13 @@ import (
 	"sync/atomic"
 	"testing"
 
-	"github.com/cilium/ebpf"
-	"github.com/cilium/ebpf/internal/testutils"
 	"github.com/go-quicktest/qt"
 	"github.com/jsimonetti/rtnetlink/v2"
 	"github.com/jsimonetti/rtnetlink/v2/driver"
-	"golang.org/x/sys/unix"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal/testutils"
+	"github.com/cilium/ebpf/internal/unix"
 )
 
 func TestAttachNetkit(t *testing.T) {
diff --git a/prog_test.go b/prog_test.go
index dd540e5..b43fcff 100644
--- a/prog_test.go
+++ b/prog_test.go
@@ -269,7 +269,7 @@ func TestProgramTestRunInterrupt(t *testing.T) {
 
 	tid := <-tidChan
 	for {
-		err := unix.Tgkill(tgid, tid, syscall.SIGUSR1)
+		err := unix.Tgkill(tgid, tid, unix.SIGUSR1)
 		if err != nil {
 			t.Fatal("Can't send signal to goroutine thread:", err)
 		}
diff --git a/rlimit/rlimit_test.go b/rlimit/rlimit_test.go
index 9b2cd94..95f1cfc 100644
--- a/rlimit/rlimit_test.go
+++ b/rlimit/rlimit_test.go
@@ -3,7 +3,7 @@ package rlimit
 import (
 	"testing"
 
-	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/linux"
 	"github.com/cilium/ebpf/internal/unix"
 
 	"github.com/go-quicktest/qt"
@@ -20,7 +20,7 @@ func TestRemoveMemlock(t *testing.T) {
 	qt.Assert(t, qt.IsNil(unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &after)))
 
 	// We can't use testutils here due to an import cycle.
-	version, err := internal.KernelVersion()
+	version, err := linux.KernelVersion()
 	qt.Assert(t, qt.IsNil(err))
 
 	if version.Less(unsupportedMemcgAccounting.MinimumVersion) {