Repository: cilium/ebpf
Pull request #12 (2019-10-03 17:45:55)

use build tags
Signed-off-by: Lehner Florian <dev@der-flo.net>

This PR addresses the issue described in https://github.com/cilium/ebpf/issues/10.

It introduces only one new error variable `errNonLinux`. This error is returned, if unix specific functions are called.

lmb: Hi Florian, thanks for your contribution! Have you tried the idea that I outlined in https://github.com/cilium/ebpf/issues/10#issuecomment-534993145 ?

Splitting the implementation into different files is fairly invasive and difficult to maintain. Hence my idea to make an `internal/unix` package which re-exports the constants / functions we need. That would reduce the changes a lot.
florianl: Hi Lorenz,
thanks for your feedback.

I'll update the PR so it will have an `internal/unix` package as you suggested. So far, I had the feeling, that the different files are less invasive. But this is only a subjective personal view.
florianl: Hi Lorenz,
I've updated the PR according to your suggestions.
And I totally agree, this way, the change is way less invasive.
lmb: I just tried the following:

```
$ GOOS=darwin go build ./...
# github.com/cilium/ebpf
./map.go:558:27: invalid operation: "github.com/pkg/errors".Cause(err) == unix.ENOENT (mismatched types error and int)
./prog.go:119:33: invalid operation: "github.com/pkg/errors".Cause(err) == unix.ENOSPC (mismatched types error and int)
./prog.go:128:34: invalid operation: "github.com/pkg/errors".Cause(nerr) == unix.ENOSPC (mismatched types error and int)
./prog.go:299:28: invalid operation: "github.com/pkg/errors".Cause(err) == unix.EINVAL (mismatched types error and int)
./syscalls.go:192:10: invalid operation: err == unix.EAGAIN (mismatched types error and int)
./syscalls.go:392:7: cannot use errNo (type uintptr) as type error in assignment:
        uintptr does not implement error (missing Error method)
```

I think making the errnos of type syscall.Errno should be enough.

Patch:
diff --git a/internal/unix/types_linux.go b/internal/unix/types_linux.go
new file mode 100644
index 0000000..49c6be5
--- /dev/null
+++ b/internal/unix/types_linux.go
@@ -0,0 +1,118 @@
+// +build linux
+
+package unix
+
+import (
+	"syscall"
+
+	linux "golang.org/x/sys/unix"
+)
+
+const (
+	ENOENT                   = linux.ENOENT
+	EAGAIN                   = linux.EAGAIN
+	ENOSPC                   = linux.ENOSPC
+	EINVAL                   = linux.EINVAL
+	EPOLLIN                  = linux.EPOLLIN
+	BPF_OBJ_NAME_LEN         = linux.BPF_OBJ_NAME_LEN
+	BPF_TAG_SIZE             = linux.BPF_TAG_SIZE
+	SYS_BPF                  = linux.SYS_BPF
+	F_DUPFD_CLOEXEC          = linux.F_DUPFD_CLOEXEC
+	EPOLL_CTL_ADD            = linux.EPOLL_CTL_ADD
+	EPOLL_CLOEXEC            = linux.EPOLL_CLOEXEC
+	O_CLOEXEC                = linux.O_CLOEXEC
+	O_NONBLOCK               = linux.O_NONBLOCK
+	PROT_READ                = linux.PROT_READ
+	PROT_WRITE               = linux.PROT_WRITE
+	MAP_SHARED               = linux.MAP_SHARED
+	PERF_TYPE_SOFTWARE       = linux.PERF_TYPE_SOFTWARE
+	PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT
+	PerfBitWatermark         = linux.PerfBitWatermark
+	PERF_SAMPLE_RAW          = linux.PERF_SAMPLE_RAW
+	PERF_FLAG_FD_CLOEXEC     = linux.PERF_FLAG_FD_CLOEXEC
+)
+
+// Statfs_t is a wrapper
+type Statfs_t = linux.Statfs_t
+
+// Rlimit is a wrapper
+type Rlimit = linux.Rlimit
+
+// Setrlimit is a wrapper
+func Setrlimit(resource int, rlim *Rlimit) (err error) {
+	return linux.Setrlimit(resource, rlim)
+}
+
+// Syscall is a wrapper
+func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
+	return linux.Syscall(trap, a1, a2, a3)
+}
+
+// FcntlInt is a wrapper
+func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
+	return linux.FcntlInt(fd, cmd, arg)
+}
+
+// Statfs is a wrapper
+func Statfs(path string, buf *Statfs_t) (err error) {
+	return linux.Statfs(path, buf)
+}
+
+// Close is a wrapper
+func Close(fd int) (err error) {
+	return linux.Close(fd)
+}
+
+// EpollEvent is a wrapper
+type EpollEvent = linux.EpollEvent
+
+// EpollWait is a wrapper
+func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
+	return linux.EpollWait(epfd, events, msec)
+}
+
+// EpollCtl is a wrapper
+func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
+	return linux.EpollCtl(epfd, op, fd, event)
+}
+
+// Eventfd is a wrapper
+func Eventfd(initval uint, flags int) (fd int, err error) {
+	return linux.Eventfd(initval, flags)
+}
+
+// Write is a wrapper
+func Write(fd int, p []byte) (n int, err error) {
+	return linux.Write(fd, p)
+}
+
+// EpollCreate1 is a wrapper
+func EpollCreate1(flag int) (fd int, err error) {
+	return linux.EpollCreate1(flag)
+}
+
+// PerfEventMmapPage is a wrapper
+type PerfEventMmapPage linux.PerfEventMmapPage
+
+// SetNonblock is a wrapper
+func SetNonblock(fd int, nonblocking bool) (err error) {
+	return linux.SetNonblock(fd, nonblocking)
+}
+
+// Mmap is a wrapper
+func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
+	return linux.Mmap(fd, offset, length, prot, flags)
+}
+
+// Munmap is a wrapper
+func Munmap(b []byte) (err error) {
+	return linux.Munmap(b)
+}
+
+// PerfEventAttr is a wrapper
+type PerfEventAttr = linux.PerfEventAttr
+
+// PerfEventOpen is a wrapper
+func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
+	return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags)
+}
diff --git a/internal/unix/types_other.go b/internal/unix/types_other.go
new file mode 100644
index 0000000..a327f2a
--- /dev/null
+++ b/internal/unix/types_other.go
@@ -0,0 +1,183 @@
+// +build !linux
+
+package unix
+
+import (
+	"fmt"
+	"runtime"
+	"syscall"
+)
+
+var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
+
+const (
+	ENOENT                   = syscall.ENOENT
+	EAGAIN                   = syscall.EAGAIN
+	ENOSPC                   = syscall.ENOSPC
+	EINVAL                   = syscall.EINVAL
+	BPF_OBJ_NAME_LEN         = 0x10
+	BPF_TAG_SIZE             = 0x8
+	SYS_BPF                  = 321
+	F_DUPFD_CLOEXEC          = 0x406
+	EPOLLIN                  = 0x1
+	EPOLL_CTL_ADD            = 0x1
+	EPOLL_CLOEXEC            = 0x80000
+	O_CLOEXEC                = 0x80000
+	O_NONBLOCK               = 0x800
+	PROT_READ                = 0x1
+	PROT_WRITE               = 0x2
+	MAP_SHARED               = 0x1
+	PERF_TYPE_SOFTWARE       = 0x1
+	PERF_COUNT_SW_BPF_OUTPUT = 0xa
+	PerfBitWatermark         = 0x4000
+	PERF_SAMPLE_RAW          = 0x400
+	PERF_FLAG_FD_CLOEXEC     = 0x8
+)
+
+// Statfs_t is a wrapper
+type Statfs_t struct {
+	Type    int64
+	Bsize   int64
+	Blocks  uint64
+	Bfree   uint64
+	Bavail  uint64
+	Files   uint64
+	Ffree   uint64
+	Fsid    [2]int32
+	Namelen int64
+	Frsize  int64
+	Flags   int64
+	Spare   [4]int64
+}
+
+// Rlimit is a wrapper
+type Rlimit struct {
+	Cur uint64
+	Max uint64
+}
+
+// Setrlimit is a wrapper
+func Setrlimit(resource int, rlim *Rlimit) (err error) {
+	return errNonLinux
+}
+
+// Syscall is a wrapper
+func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
+	return 0, 0, syscall.Errno(1)
+}
+
+// FcntlInt is a wrapper
+func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
+	return -1, errNonLinux
+}
+
+// Statfs is a wrapper
+func Statfs(path string, buf *Statfs_t) error {
+	return errNonLinux
+}
+
+// Close is a wrapper
+func Close(fd int) (err error) {
+	return errNonLinux
+}
+
+// EpollEvent is a wrapper
+type EpollEvent struct {
+	Events uint32
+	Fd     int32
+	Pad    int32
+}
+
+// EpollWait is a wrapper
+func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
+	return 0, errNonLinux
+}
+
+// EpollCtl is a wrapper
+func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
+	return errNonLinux
+}
+
+// Eventfd is a wrapper
+func Eventfd(initval uint, flags int) (fd int, err error) {
+	return 0, errNonLinux
+}
+
+// Write is a wrapper
+func Write(fd int, p []byte) (n int, err error) {
+	return 0, errNonLinux
+}
+
+// EpollCreate1 is a wrapper
+func EpollCreate1(flag int) (fd int, err error) {
+	return 0, errNonLinux
+}
+
+// PerfEventMmapPage is a wrapper
+type PerfEventMmapPage struct {
+	Version        uint32
+	Compat_version uint32
+	Lock           uint32
+	Index          uint32
+	Offset         int64
+	Time_enabled   uint64
+	Time_running   uint64
+	Capabilities   uint64
+	Pmc_width      uint16
+	Time_shift     uint16
+	Time_mult      uint32
+	Time_offset    uint64
+	Time_zero      uint64
+	Size           uint32
+
+	Data_head   uint64
+	Data_tail   uint64
+	Data_offset uint64
+	Data_size   uint64
+	Aux_head    uint64
+	Aux_tail    uint64
+	Aux_offset  uint64
+	Aux_size    uint64
+}
+
+// SetNonblock is a wrapper
+func SetNonblock(fd int, nonblocking bool) (err error) {
+	return errNonLinux
+}
+
+// Mmap is a wrapper
+func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
+	return []byte{}, errNonLinux
+}
+
+// Munmap is a wrapper
+func Munmap(b []byte) (err error) {
+	return errNonLinux
+}
+
+// PerfEventAttr is a wrapper
+type PerfEventAttr struct {
+	Type               uint32
+	Size               uint32
+	Config             uint64
+	Sample             uint64
+	Sample_type        uint64
+	Read_format        uint64
+	Bits               uint64
+	Wakeup             uint32
+	Bp_type            uint32
+	Ext1               uint64
+	Ext2               uint64
+	Branch_sample_type uint64
+	Sample_regs_user   uint64
+	Sample_stack_user  uint32
+	Clockid            int32
+	Sample_regs_intr   uint64
+	Aux_watermark      uint32
+	Sample_max_stack   uint16
+}
+
+// PerfEventOpen is a wrapper
+func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
+	return 0, errNonLinux
+}
diff --git a/map.go b/map.go
index 18d082b..4b2b986 100644
--- a/map.go
+++ b/map.go
@@ -5,9 +5,9 @@ import (
 	"unsafe"
 
 	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/unix"
 
 	"github.com/pkg/errors"
-	"golang.org/x/sys/unix"
 )
 
 // MapSpec defines a Map.
diff --git a/perf/reader.go b/perf/reader.go
index a7eb2e7..3534e99 100644
--- a/perf/reader.go
+++ b/perf/reader.go
@@ -10,9 +10,9 @@ import (
 
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/unix"
 
 	"github.com/pkg/errors"
-	"golang.org/x/sys/unix"
 )
 
 var (
diff --git a/perf/ring.go b/perf/ring.go
index a038d73..ff63b05 100644
--- a/perf/ring.go
+++ b/perf/ring.go
@@ -7,8 +7,9 @@ import (
 	"sync/atomic"
 	"unsafe"
 
+	"github.com/cilium/ebpf/internal/unix"
+
 	"github.com/pkg/errors"
-	"golang.org/x/sys/unix"
 )
 
 // perfEventRing is a page of metadata followed by
diff --git a/prog.go b/prog.go
index d263182..9f020a9 100644
--- a/prog.go
+++ b/prog.go
@@ -11,7 +11,7 @@ import (
 
 	"github.com/cilium/ebpf/asm"
 	"github.com/cilium/ebpf/internal"
-	"golang.org/x/sys/unix"
+	"github.com/cilium/ebpf/internal/unix"
 
 	"github.com/pkg/errors"
 )
diff --git a/syscalls.go b/syscalls.go
index 8b05a4d..011e3af 100644
--- a/syscalls.go
+++ b/syscalls.go
@@ -8,8 +8,9 @@ import (
 	"strings"
 	"unsafe"
 
+	"github.com/cilium/ebpf/internal/unix"
+
 	"github.com/pkg/errors"
-	"golang.org/x/sys/unix"
 )
 
 var errClosedFd = errors.New("use of closed file descriptor")


Test patch:
diff --git a/map_test.go b/map_test.go
index b17f03f..29ce568 100644
--- a/map_test.go
+++ b/map_test.go
@@ -11,9 +11,9 @@ import (
 	"unsafe"
 
 	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/unix"
 
 	"github.com/pkg/errors"
-	"golang.org/x/sys/unix"
 )
 
 func TestMain(m *testing.M) {
diff --git a/perf/reader_test.go b/perf/reader_test.go
index 5cb6b68..2015f0f 100644
--- a/perf/reader_test.go
+++ b/perf/reader_test.go
@@ -12,8 +12,7 @@ import (
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/asm"
 	"github.com/cilium/ebpf/internal"
-
-	"golang.org/x/sys/unix"
+	"github.com/cilium/ebpf/internal/unix"
 )
 
 func TestPerfReader(t *testing.T) {
diff --git a/perf/ring_test.go b/perf/ring_test.go
index 8c4c3b8..60796cb 100644
--- a/perf/ring_test.go
+++ b/perf/ring_test.go
@@ -5,7 +5,7 @@ import (
 	"io"
 	"testing"
 
-	"golang.org/x/sys/unix"
+	"github.com/cilium/ebpf/internal/unix"
 )
 
 func TestRingBufferReader(t *testing.T) {