Skip to content

Commit

Permalink
Makefile: improvements (#28)
Browse files Browse the repository at this point in the history
* Move selftest/libbpf-module to libbpf
* Fix: small fix after uprobe support

    Closes: #33

    $ make libbpfgo-static-test

    ./libbpfgo.go:790:15: errptrError format %s has arg pid of wrong type int
    ./libbpfgo.go:802:14: Sprintf format %s has arg offset of wrong type uint32

* Readme.md: small rephrasing
* Update Readme.md with build instructions and minor fixes
* Makefile: improvements for the entire tree

    Currently you will find the following GNU Makefile rules:

    | Makefile Rule            | Description                       |
    |--------------------------|-----------------------------------|
    | all                      | builds libbpfgo (dynamic)         |
    | clean                    | cleans entire tree                |
    | selftest                 | builds all selftests (static)     |
    | selftest-run             | runs all selftests (static)       |

    * libbpf dynamically linked (libbpf from OS)

    | Makefile Rule            | Description                       |
    |--------------------------|-----------------------------------|
    | libbpfgo-dynamic         | builds dynamic libbpfgo (libbpf)  |
    | libbpfgo-dynamic-test    | 'go test' with dynamic libbpfgo   |
    | selftest-dynamic         | build tests with dynamic libbpfgo |
    | selftest-dynamic-run     | run tests using dynamic libbpfgo  |

    * statically compiled (libbpf submodule)

    | Makefile Rule            | Description                       |
    |--------------------------|-----------------------------------|
    | libbpfgo-static          | builds static libbpfgo (libbpf)   |
    | libbpfgo-static-test     | 'go test' with static libbpfgo    |
    | selftest-static          | build tests with static libbpfgo  |
    | selftest-static-run      | run tests using static libbpfgo   |

    * examples

    $ make libbpfgo-static => libbpfgo statically linked with libbpf
    $ make -C selftest/perfbuffers => single selftest build (static libbpf)
    $ make -C selftest/perfbuffers run-dynamic => single selftest run (dynamic libbpf)
    $ make selftest-static-run => will build & run all static selftests

    > Note 01: dynamic builds need your OS to have a *recent enough* libbpf
    package (and its headers) installed. Sometimes, recent features might
    require the use of backported OS packages in order for your OS to
    contain latest *libbpf* features (sometimes required by libbpfgo).

    > Note 02: static builds need `git submodule init` first. Make sure to
    sync the *libbpf* git submodule before trying to statically compile or
    test the *libbpfgo* repository.

* selftest/map-update: fixes for the new tree structure
* Makefile: better info on errors
* Makefile: turn static builds into full static

    All static binaries (and selftests) were being built statically against
    libbpf only. This change makes them full static binaries.

* Makefile: un-phony vmlinux.h
  • Loading branch information
Rafael David Tinoco authored Jul 12, 2021
1 parent 4928d36 commit a683635
Show file tree
Hide file tree
Showing 41 changed files with 675 additions and 407 deletions.
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
selftest/dist
output*
selftest/*/main
selftest/*/*.o
selftest/*/*-static
selftest/*/*-dynamic
selftest/uprobe/ctest
selftest/uprobe/gotest
7 changes: 3 additions & 4 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

[submodule "selftest/libbpf-module"]
path = selftest/libbpf-module
url = https://github.com/libbpf/libbpf
[submodule "libbpf"]
path = libbpf
url = https://github.com/libbpf/libbpf.git
176 changes: 154 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,29 +1,161 @@
TARGET_BPF := test/test.bpf.o
VMLINUX_H = test/vmlinux.h
BASEDIR = $(abspath ./)

GO_SRC := $(shell find . -type f -name '*.go')
BPF_SRC := $(shell find . -type f -name '*.bpf.c')
PWD := $(shell pwd)
OUTPUT = ./output
SELFTEST = ./selftest

LIBBPF_HEADERS := /usr/include/bpf
LIBBPF := "-lbpf"
CC = gcc
CLANG = clang

.PHONY: all
all: test
ARCH := $(shell uname -m)
ARCH := $(subst x86_64,amd64,$(ARCH))

$(VMLINUX_H):
bpftool btf dump file /sys/kernel/btf/vmlinux format c > test/vmlinux.h
BTFFILE = /sys/kernel/btf/vmlinux
BPFTOOL = $(shell which bpftool || /bin/false)
VMLINUXH = $(OUTPUT)/vmlinux.h

go_env := CC=gcc CGO_CFLAGS="-I $(LIBBPF_HEADERS)" CGO_LDFLAGS="$(LIBBPF)"
.PHONY: test
test: $(TARGET_BPF) $(GO_SRC)
$(go_env) go test -ldflags '-extldflags "-static"' .
# libbpf

$(TARGET_BPF): $(BPF_SRC) $(VMLINUX_H)
clang \
-g -O2 -c -target bpf \
-o $@ $<
LIBBPF_SRC = $(abspath ./libbpf/src)
LIBBPF_OBJ = $(abspath ./$(OUTPUT)/libbpf.a)
LIBBPF_OBJDIR = $(abspath ./$(OUTPUT)/libbpf)
LIBBPF_DESTDIR = $(abspath ./$(OUTPUT))

.PHONY: clean
clean:
rm $(TARGET_BPF) $(VMLINUX_H)
CFLAGS = -g -O2 -Wall -fpie
LDFLAGS =

# golang

CGO_CFLAGS_STATIC = "-I$(abspath $(OUTPUT))"
CGO_LDFLAGS_STATIC = "-lelf -lz $(LIBBPF_OBJ)"
CGO_EXTLDFLAGS_STATIC = '-w -extldflags "-static"'

CGO_CFGLAGS_DYN = "-I. -I/usr/include/"
CGO_LDFLAGS_DYN = "-lelf -lz -lbpf"

# default == shared lib from OS package

all: libbpfgo-dynamic
test: libbpfgo-dynamic-test

# libbpfgo test object

libbpfgo-test-bpf-static: libbpfgo-static # needed for serialization
$(MAKE) -C $(SELFTEST)/build

libbpfgo-test-bpf-dynamic: libbpfgo-dynamic # needed for serialization
$(MAKE) -C $(SELFTEST)/build

libbpfgo-test-bpf-clean:
$(MAKE) -C $(SELFTEST)/build clean

# libbpf: shared

libbpfgo-dynamic: $(OUTPUT)/libbpf
CC=$(CLANG) \
CGO_CFLAGS=$(CGO_CFLAGS_DYN) \
CGO_LDFLAGS=$(CGO_LDFLAGS_DYN) \
go build .

libbpfgo-dynamic-test: libbpfgo-test-bpf-dynamic
CC=$(CLANG) \
CGO_CFLAGS=$(CGO_CFLAGS_DYN) \
CGO_LDFLAGS=$(CGO_LDFLAGS_DYN) \
sudo -E go test .

# libbpf: static

libbpfgo-static: $(VMLINUXH) | $(LIBBPF_OBJ)
CC=$(CLANG) \
CGO_CFLAGS=$(CGO_CFLAGS_STATIC) \
CGO_LDFLAGS=$(CGO_LDFLAGS_STATIC) \
GOOS=linux GOARCH=$(ARCH) \
go build \
-tags netgo -ldflags $(CGO_EXTLDFLAGS_STATIC) \
.

libbpfgo-static-test: libbpfgo-test-bpf-static
CC=$(CLANG) \
CGO_CFLAGS=$(CGO_CFLAGS_STATIC) \
CGO_LDFLAGS=$(CGO_LDFLAGS_STATIC) \
GOOS=linux GOARCH=$(ARCH) \
sudo -E -- go test \
-tags netgo -ldflags $(CGO_EXTLDFLAGS_STATIC) \
.

# vmlinux header file

.PHONY: vmlinuxh
vmlinuxh: $(VMLINUXH)

$(VMLINUXH): $(OUTPUT)
@if [ ! -f $(BTFFILE) ]; then \
echo "ERROR: kernel does not seem to support BTF"; \
exit 1; \
fi
@if [ ! -f $(VMLINUXH) ]; then \
echo "INFO: generating $(VMLINUXH) from $(BTFFILE)"; \
$(BPFTOOL) btf dump file $(BTFFILE) format c > $(VMLINUXH); \
fi

# static libbpf generation for the git submodule

$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch]) | $(OUTPUT)/libbpf
CC="$(CC)" CFLAGS="$(CFLAGS)" LD_FLAGS="$(LDFLAGS)" \
$(MAKE) -C $(LIBBPF_SRC) \
BUILD_STATIC_ONLY=1 \
OBJDIR=$(LIBBPF_OBJDIR) \
DESTDIR=$(LIBBPF_DESTDIR) \
INCLUDEDIR= LIBDIR= UAPIDIR= install

# selftests

SELFTESTS = $(shell find $(SELFTEST) -mindepth 1 -maxdepth 1 -type d ! -name 'common' ! -name 'build')

define FOREACH
SELFTESTERR=0; \
for DIR in $(SELFTESTS); do \
echo "INFO: entering $$DIR..."; \
$(MAKE) -j8 -C $$DIR $(1) || SELFTESTERR=1; \
done; \
if [ $$SELFTESTERR -eq 1 ]; then \
exit 1; \
fi
endef

.PHONY: selftest
.PHONY: selftest-static
.PHONY: selftest-dynamic
.PHONY: selftest-run
.PHONY: selftest-static-run
.PHONY: selftest-dynamic-run
.PHONY: selftest-clean

selftest: selftest-static

selftest-static:
$(call FOREACH, main-static)
selftest-dynamic:
$(call FOREACH, main-dynamic)

selftest-run: selftest-static-run

selftest-static-run:
$(call FOREACH, run-static)
selftest-dynamic-run:
$(call FOREACH, run-dynamic)

selftest-clean:
$(call FOREACH, clean)

# output

$(OUTPUT):
mkdir -p $(OUTPUT)

$(OUTPUT)/libbpf:
mkdir -p $(OUTPUT)/libbpf

# cleanup

clean: selftest-clean libbpfgo-test-bpf-clean
rm -rf $(OUTPUT)
69 changes: 54 additions & 15 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,61 @@

<img src="docs/images/aqua-tux.png" width="150" height="auto">

___
----

libbpfgo is a Go library for working with Linux's [eBPF](https://ebpf.io/). It was created for [Tracee](https://github.com/aquasecurity/tracee), our open source Runtime Security and eBPF tracing tools written in Go. If you are interested in eBPF and it's applications, check out Tracee on Github: [https://github.com/aquasecurity/tracee](https://github.com/aquasecurity/tracee).
libbpfgo is a Go library for Linux's [eBPF](https://ebpf.io/) project. It was created for [Tracee](https://github.com/aquasecurity/tracee), our open source Runtime Security, and eBPF tracing tool, written in Go. If you are interested in eBPF and its applications, check out Tracee at Github: [https://github.com/aquasecurity/tracee](https://github.com/aquasecurity/tracee).

libbpfgo is built around libbpf - the standard library for interacting with eBPF from userspace, which is a C library maintained in Linux upstream. We have created libbpfgo as a thin Go wrapper around libbpf.
libbpfgo is built around [libbpf](https://github.com/libbpf/libbpf) - the standard library for interacting with eBPF programs from userspace - which is a C library maintained in Linux upstream. We have created libbpfgo as a thin Go wrapper around the libbpf project.

## Installing

libbpfgo is using CGO to interop with libbpf and will expect to be linked with libbpf at run or link time. Simply importing libbpfgo is not enough to get started, and you will need to fulfill the required dependency in one of the following ways:
libbpfgo uses CGO to interop with libbpf and will expect to be linked with libbpf at run or link time. Simply importing libbpfgo is not enough to get started, and you will need to fulfill the required dependency in one of the following ways:

1. Install the libbpf as a shared object in the system. Libbpf may already be packaged for you distribution, if not, you can build and install from source. More info [here](https://github.com/libbpf/libbpf).
1. Embed libbpf into your Go project as a vendored dependency. This means that the libbpf code is statically linked into the resulting binary, and there are no runtime dependencies. [Tracee](https://github.com/aquasecurity/tracee) takes this approach and you can take example from it's [Makefile](https://github.com/aquasecurity/tracee/blob/f8df7da6a27f729610992b6bd52e89d510fcf384/tracee-ebpf/Makefile#L62).
1. Install libbpf as a shared object in the system. Libbpf may already be packaged for your distribution and, if not, you can build and install from source. More info [here](https://github.com/libbpf/libbpf).
1. Embed libbpf into your Go project as a vendored dependency. This means that the libbpf code is statically linked into the resulting binary, and there are no runtime dependencies. [Tracee](https://github.com/aquasecurity/tracee) takes this approach.

In the next sesssion you will find different ways to build libbpfgo.

## Building

Currently you will find the following GNU Makefile rules:

| Makefile Rule | Description |
|--------------------------|-----------------------------------|
| all | builds libbpfgo (dynamic) |
| clean | cleans entire tree |
| selftest | builds all selftests (static) |
| selftest-run | runs all selftests (static) |

* libbpf dynamically linked (libbpf from OS)

| Makefile Rule | Description |
|--------------------------|-----------------------------------|
| libbpfgo-dynamic | builds dynamic libbpfgo (libbpf) |
| libbpfgo-dynamic-test | 'go test' with dynamic libbpfgo |
| selftest-dynamic | build tests with dynamic libbpfgo |
| selftest-dynamic-run | run tests using dynamic libbpfgo |

* statically compiled (libbpf submodule)

| Makefile Rule | Description |
|--------------------------|-----------------------------------|
| libbpfgo-static | builds static libbpfgo (libbpf) |
| libbpfgo-static-test | 'go test' with static libbpfgo |
| selftest-static | build tests with static libbpfgo |
| selftest-static-run | run tests using static libbpfgo |

* examples

```
$ make libbpfgo-static => libbpfgo statically linked with libbpf
$ make -C selftest/perfbuffers => single selftest build (static libbpf)
$ make -C selftest/perfbuffers run-dynamic => single selftest run (dynamic libbpf)
$ make selftest-static-run => will build & run all static selftests
```

> Note 01: dynamic builds need your OS to have a *recent enough* libbpf package (and its headers) installed. Sometimes, recent features might require the use of backported OS packages in order for your OS to contain latest *libbpf* features (sometimes required by libbpfgo).
> Note 02: static builds need `git submodule init` first. Make sure to sync the *libbpf* git submodule before trying to statically compile or test the *libbpfgo* repository.
## Concepts

Expand Down Expand Up @@ -47,15 +90,11 @@ rb.Start()
e := <-eventsChannel
```

Please check our github milestones for an idea of the project roadmap. The general goal is to fully implement/expose libbpf's API in Go as seamlessly as possible.

Please check our github milestones for an idea of the project roadmap. The general goal is to fully implement/expose libbpf's API in Go as seamlessly as possible.

## Learn more

- Blost post on [how to Build eBPF Programs with libbpfgo](https://blog.aquasec.com/libbpf-ebpf-programs)

- The [selftests](./selftest) are small programs that use libbpfgo to verify functionality, they're good examples to look at for usage.

- [tracee-ebpf](https://github.com/aquasecurity/tracee/tree/main/tracee-ebpf) is a robust consumer of this package.

- Feel free to ask questions by creating a new [Discussion](https://github.com/aquasecurity/libbpfgo/discussions) and we'd love to help.
- [How to Build eBPF Programs with libbpfgo](https://blog.aquasec.com/libbpf-ebpf-programs).
- [selftests](./selftest) are small program using libbpfgo and might be good usage examples.
- [tracee-ebpf](https://github.com/aquasecurity/tracee/tree/main/tracee-ebpf) is a robust consumer of this project.
- Feel free to ask questions by creating a new [Discussion](https://github.com/aquasecurity/libbpfgo/discussions), we'd love to help.
4 changes: 2 additions & 2 deletions libbpfgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ func doAttachUprobe(prog *BPFProg, isUretprobe bool, pid int, path string, offse
link := C.bpf_program__attach_uprobe(prog.prog, retCBool, pidCint, pathCString, offsetCsizet)
C.free(unsafe.Pointer(pathCString))
if C.IS_ERR_OR_NULL(unsafe.Pointer(link)) {
return nil, errptrError(unsafe.Pointer(link), "failed to attach u(ret)probe to program %s:%d with pid %s, ", path, offset, pid)
return nil, errptrError(unsafe.Pointer(link), "failed to attach u(ret)probe to program %s:%d with pid %d, ", path, offset, pid)
}

upType := Uprobe
Expand All @@ -813,7 +813,7 @@ func doAttachUprobe(prog *BPFProg, isUretprobe bool, pid int, path string, offse
link: link,
prog: prog,
linkType: upType,
eventName: fmt.Sprintf("%s:%d:%s", path, pid, offset),
eventName: fmt.Sprintf("%s:%d:%d", path, pid, offset),
}
return bpfLink, nil
}
Expand Down
4 changes: 2 additions & 2 deletions libbpfgo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (

func Test_LoadAndAttach(t *testing.T) {
// load non exisiting file, should fail
module, err := NewModuleFromFile("test/foo.bpf.o")
module, err := NewModuleFromFile("foo.bpf.o")
if err == nil {
t.Errorf("NewModuleFromFile returned nil error on non-existing file")
}

module, err = NewModuleFromFile("test/test.bpf.o")
module, err = NewModuleFromFile("selftest/build/libbpfgo_test.bpf.o")
if err != nil {
t.Fatalf("NewModuleFromFile failed: %v", err)
}
Expand Down
4 changes: 0 additions & 4 deletions selftest/.gitignore

This file was deleted.

32 changes: 32 additions & 0 deletions selftest/build/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
BASEDIR = $(abspath ../../)

OUTPUT = ../../output

CC = gcc
CLANG = clang

CFLAGS = -g -O2 -Wall -fpie
LDFLAGS =

TEST = libbpfgo_test

all: $(TEST).bpf.o

## main tree dependency

outputdir:
$(MAKE) -C $(BASEDIR) outputdir

vmlinuxh: outputdir
$(MAKE) -C $(BASEDIR) vmlinuxh

## test bpf dependency

$(TEST).bpf.o: $(TEST).bpf.c
$(MAKE) -C $(BASEDIR) vmlinuxh
$(CLANG) $(CFLAGS) -target bpf -I$(OUTPUT) -c $< -o $@

## clean

clean:
rm -f *.o
2 changes: 1 addition & 1 deletion test/test.bpf.c → selftest/build/libbpfgo_test.bpf.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//+build ignore
#include "vmlinux.h"
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

Expand Down
Loading

0 comments on commit a683635

Please sign in to comment.