Skip to contentSkip to conversation

perf/ebpf lib + llvm/clang building infrastructure

GIT PULL New Arnaldo Carvalho de Melo 2 participants 26 messages Aug 7, 2015 – Aug 8, 2015

Subsystems (3)
lore
Share & tools
lore

Arnaldo Carvalho de Melo

author maintainer
#0 lore raw

Hi Ingo,

This is up to a test that checks that the infrastructure for getting
a C source file and build it to a form that can then be used to upload it to the kernel is working.

Wang has a tree where we go all the way to passing a .c file as an
--event, but I thought about breaking the patchkit up to what is settled and the "perf test -v LLVM" point looks like a good cutoff point for demoing/testing what is in this patchkit.

It shouldn't affect people without the environment needed, as well it
should check the environment for sanity and provide info about how to get it working, be it in the changeset logs, the docs and on tool output messages.o

If you find anything in it that needs fixing, please let us know if you
think we should fix it before merging it to this point or if something can be fixed in follow up patches.

Wang's repo, rebased, almost builds, he should be refreshing this soon,
fixing those trailing whitespace issues, that is stopping the rebase here:

[acme@zoo linux]$ git remote -v | grep wang | head -1
wangnan.github https://github.com/WangNan0/linux.git (fetch)
[acme@zoo linux]$ git checkout -b ebpf-test wangnan.github/ebpf
Branch ebpf-test set up to track remote branch ebpf from wangnan.github. Switched to a new branch 'ebpf-test'
[acme@zoo linux]$ git rebase perf/ebpf
First, rewinding head to replay your work on top of it...
Applying: perf tests: Add LLVM test for eBPF on-the-fly compiling
Using index info to reconstruct a base tree...
M tools/perf/tests/Build
M tools/perf/tests/builtin-test.c
M tools/perf/tests/tests.h
M tools/perf/util/llvm-utils.c
M tools/perf/util/llvm-utils.h
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
Applying: perf tools: Make perf depend on libbpf
Using index info to reconstruct a base tree...
M tools/perf/MANIFEST
Falling back to patching base and 3-way merge...
Auto-merging tools/perf/MANIFEST
Applying: perf ebpf: Add the libbpf glue
Applying: perf tools: Enable passing bpf object file to --event
Applying: perf tools: Parse probe points of eBPF programs during preparation
Applying: perf probe: Attach trace_probe_event with perf_probe_event
Applying: perf record: Probe at kprobe points
Applying: perf record: Load all eBPF object into kernel
Applying: perf tools: Add bpf_fd field to evsel and config it
Applying: perf tools: Attach eBPF program to perf event
Applying: perf tools: Suppress probing messages when probing by BPF loading
Applying: perf record: Add clang options for compiling BPF scripts
Applying: perf tools: Infrastructure for compiling scriptlets when passing '.c' to --event
Applying: perf tests: Enforce LLVM test for BPF test
Applying: perf test: Enable 'perf test' run as test targets tools/perf/tests/builtin-test.c:279: trailing whitespace.
+ /*
[acme@zoo linux]$

After that whitespace thing is fixed, you'll get to a point where 'perf
test -v BPF' will show you all the steps involved in, from a .c file do all the build process and loading, attaching to a kprobe, etc that we discussed, but those extra patches need more working before I can submit to you.

Otherwise, please consider pulling,

- Arnaldo

The following changes since commit 1354ac6ad84395660f551d0614a6ca39e5bfe8e3:

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core (2015-08-07 09:11:30 +0200)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-ebpf-for-mingo

for you to fetch changes up to 9bc898c7019383b6aa2ae6cb2928c4ca926449f0:

perf tests: Add LLVM test for eBPF on-the-fly compiling (2015-08-07 10:57:24 -0300)

----------------------------------------------------------------
perf/ebpf library + llvm/clang infrastructure

Infrastructure:

- library for interfacing with the kernel eBPF infrastructure, with tools/perf/ targeted as a first user (Wang Nan)

- llvm/clang infrastructure for building BPF object files from C source code (Wang Nan)

----------------------------------------------------------------
Wang Nan (24):
bpf tools: Introduce 'bpf' library and add bpf feature check bpf tools: Allow caller to set printing function bpf tools: Open eBPF object file and do basic validation bpf tools: Read eBPF object from buffer
bpf tools: Check endianness and make libbpf fail early bpf tools: Iterate over ELF sections to collect information bpf tools: Collect version and license from ELF sections bpf tools: Collect map definitions from 'maps' section bpf tools: Collect symbol table from SHT_SYMTAB section bpf tools: Collect eBPF programs from their own sections bpf tools: Collect relocation sections from SHT_REL sections bpf tools: Record map accessing instructions for each program bpf tools: Add bpf.c/h for common bpf operations bpf tools: Create eBPF maps defined in an object file bpf tools: Relocate eBPF programs
bpf tools: Introduce bpf_load_program() to bpf.c bpf tools: Load eBPF programs in object files into kernel bpf tools: Introduce accessors for struct bpf_program bpf tools: Link all bpf objects onto a list
perf tools: Introduce llvm config options
perf tools: Call clang to compile C source to object code perf tools: Auto detecting kernel build directory perf tools: Auto detecting kernel include options perf tests: Add LLVM test for eBPF on-the-fly compiling

 tools/build/feature/Makefile    |    6 +-
 tools/build/feature/test-bpf.c  |   18 +
 tools/lib/bpf/.gitignore        |    2 +
 tools/lib/bpf/Build             |    1 +
 tools/lib/bpf/Makefile          |  195 ++++++++
 tools/lib/bpf/bpf.c             |   85 ++++
 tools/lib/bpf/bpf.h             |   23 +
 tools/lib/bpf/libbpf.c          | 1037 +++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h          |   81 +++
 tools/perf/MANIFEST             |    1 +
 tools/perf/tests/Build          |    1 +
 tools/perf/tests/builtin-test.c |    4 +
 tools/perf/tests/llvm.c         |   98 ++++
 tools/perf/tests/tests.h        |    1 +
 tools/perf/util/Build           |    1 +
 tools/perf/util/config.c        |    4 +
 tools/perf/util/llvm-utils.c    |  408 +++++++++++++++
 tools/perf/util/llvm-utils.h    |   49 ++
 18 files changed, 2014 insertions(+), 1 deletion(-)
 create mode 100644 tools/build/feature/test-bpf.c
 create mode 100644 tools/lib/bpf/.gitignore
 create mode 100644 tools/lib/bpf/Build
 create mode 100644 tools/lib/bpf/Makefile
 create mode 100644 tools/lib/bpf/bpf.c
 create mode 100644 tools/lib/bpf/bpf.h
 create mode 100644 tools/lib/bpf/libbpf.c
 create mode 100644 tools/lib/bpf/libbpf.h
 create mode 100644 tools/perf/tests/llvm.c
 create mode 100644 tools/perf/util/llvm-utils.c
 create mode 100644 tools/perf/util/llvm-utils.h
author maintainer
#1 lore raw

PATCH 1/24 bpf tools: Introduce 'bpf' library and add bpf feature check

From: Wang Nan <[email protected]>

This is the first patch of libbpf. The goal of libbpf is to create a standard way for accessing eBPF object files. This patch creates 'Makefile' and 'Build' for it, allows 'make' to build libbpf.a and libbpf.so, 'make install' to put them into proper directories. Most part of Makefile is borrowed from traceevent.

Before building, it checks the existence of libelf in Makefile, and deny to build if not found. Instead of throwing an error if libelf not found, the error raises in a phony target "elfdep". This design is to ensure 'make clean' still workable even if libelf is not found.

Because libbpf requires 'kern_version' field set for 'union bpf_attr' (bpfdep" is used for that dependency), Kernel BPF API is also checked by intruducing a new feature check 'bpf' into tools/build/feature, which checks the existence and version of linux/bpf.h. When building libbpf, it searches that file from include/uapi/linux in kernel source tree (controlled by FEATURE_CHECK_CFLAGS-bpf). Since it searches kernel source tree it reside, installing of newest kernel headers is not required, except we are trying to port these files to an old kernel.

To avoid checking that file when perf building, the newly introduced 'bpf' feature check doesn't added into FEATURE_TESTS and FEATURE_DISPLAY by default in tools/build/Makefile.feature, but added into libbpf's specific.

Cc Brendan Gregg <[email protected]>
Cc Daniel Borkmann <[email protected]>
Cc David Ahern <[email protected]>
Cc He Kuang <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Kaixu Xia <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Zefan Li <[email protected]>

Bcc: [email protected]

---
 tools/build/feature/Makefile   |   6 +-
 tools/build/feature/test-bpf.c |  18 ++++
 tools/lib/bpf/.gitignore       |   2 +
 tools/lib/bpf/Build            |   1 +
 tools/lib/bpf/Makefile         | 195 +++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.c         |  14 +++
 tools/lib/bpf/libbpf.h         |  11 +++
 7 files changed, 246 insertions(+), 1 deletion(-)
 create mode 100644 tools/build/feature/test-bpf.c
 create mode 100644 tools/lib/bpf/.gitignore
 create mode 100644 tools/lib/bpf/Build
 create mode 100644 tools/lib/bpf/Makefile
 create mode 100644 tools/lib/bpf/libbpf.c
 create mode 100644 tools/lib/bpf/libbpf.h
tools/build/feature/Makefile +5 -1
index 463ed8f2a267..1c0d69f44552 100644
@@ -33,7 +33,8 @@ FILES=					\
 	test-compile-32.bin		\
 	test-compile-x32.bin		\
 	test-zlib.bin			\
-	test-lzma.bin
+	test-lzma.bin			\
+	test-bpf.bin
 
 CC := $(CROSS_COMPILE)gcc -MD
 PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -156,6 +157,9 @@ test-zlib.bin:
 test-lzma.bin:
 	$(BUILD) -llzma
 
+test-bpf.bin:
+	$(BUILD)
+
 -include *.d
 
 ###############################
tools/build/feature/test-bpf.c new file +18 -0
new file mode 100644
index 000000000000..062bac811af9
@@ -0,0 +1,18 @@
+#include <linux/bpf.h>
+
+int main(void)
+{
+	union bpf_attr attr;
+
+	attr.prog_type = BPF_PROG_TYPE_KPROBE;
+	attr.insn_cnt = 0;
+	attr.insns = 0;
+	attr.license = 0;
+	attr.log_buf = 0;
+	attr.log_size = 0;
+	attr.log_level = 0;
+	attr.kern_version = 0;
+
+	attr = attr;
+	return 0;
+}
tools/lib/bpf/.gitignore new file +2 -0
new file mode 100644
index 000000000000..812aeedaea38
@@ -0,0 +1,2 @@
+libbpf_version.h
+FEATURE-DUMP
tools/lib/bpf/Build new file +1 -0
new file mode 100644
index 000000000000..a316484233c9
@@ -0,0 +1 @@
+libbpf-y := libbpf.o
tools/lib/bpf/Makefile new file +195 -0

195 lines not on this page · raw message

tools/lib/bpf/libbpf.c new file +14 -0
new file mode 100644
index 000000000000..c08d6bca1c22
@@ -0,0 +1,14 @@
+/*
+ * Common eBPF ELF object loading operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <linux/bpf.h>
+
+#include "libbpf.h"
tools/lib/bpf/libbpf.h new file +11 -0
new file mode 100644
index 000000000000..a6f46d908097
@@ -0,0 +1,11 @@
+/*
+ * Common eBPF ELF object loading operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+#ifndef __BPF_LIBBPF_H
+#define __BPF_LIBBPF_H
+
+#endif
Signature · 1 line
2.1.0
author maintainer
#2 lore raw

PATCH 2/24 bpf tools: Allow caller to set printing function

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

By libbpf_set_print(), users of libbpf are allowed to register he/she own debug, info and warning printing functions. Libbpf will use those functions to print messages. If not provided, default info and warning printing functions are fprintf(stderr, ...); default debug printing is NULL.

This API is designed to be used by perf, enables it to register its own logging functions to make all logs uniform, instead of separated logging level control.

---
 tools/lib/bpf/libbpf.c | 40 ++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h | 12 ++++++++++++
 2 files changed, 52 insertions(+)
tools/lib/bpf/libbpf.c +40 -0
index c08d6bca1c22..6f0c13a1fb3c 100644
@@ -7,8 +7,48 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
 #include <unistd.h>
 #include <asm/unistd.h>
 #include <linux/bpf.h>
 
 #include "libbpf.h"
+
+#define __printf(a, b)	__attribute__((format(printf, a, b)))
+
+__printf(1, 2)
+static int __base_pr(const char *format, ...)
+{
+	va_list args;
+	int err;
+
+	va_start(args, format);
+	err = vfprintf(stderr, format, args);
+	va_end(args);
+	return err;
+}
+
+static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr;
+static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr;
+static __printf(1, 2) libbpf_print_fn_t __pr_debug;
+
+#define __pr(func, fmt, ...)	\
+do {				\
+	if ((func))		\
+		(func)("libbpf: " fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define pr_warning(fmt, ...)	__pr(__pr_warning, fmt, ##__VA_ARGS__)
+#define pr_info(fmt, ...)	__pr(__pr_info, fmt, ##__VA_ARGS__)
+#define pr_debug(fmt, ...)	__pr(__pr_debug, fmt, ##__VA_ARGS__)
+
+void libbpf_set_print(libbpf_print_fn_t warn,
+		      libbpf_print_fn_t info,
+		      libbpf_print_fn_t debug)
+{
+	__pr_warning = warn;
+	__pr_info = info;
+	__pr_debug = debug;
+}
tools/lib/bpf/libbpf.h +12 -0
index a6f46d908097..8d1eebafa958 100644
@@ -8,4 +8,16 @@
 #ifndef __BPF_LIBBPF_H
 #define __BPF_LIBBPF_H
 
+/*
+ * In include/linux/compiler-gcc.h, __printf is defined. However
+ * it should be better if libbpf.h doesn't depend on Linux header file.
+ * So instead of __printf, here we use gcc attribute directly.
+ */
+typedef int (*libbpf_print_fn_t)(const char *, ...)
+	__attribute__((format(printf, 1, 2)));
+
+void libbpf_set_print(libbpf_print_fn_t warn,
+		      libbpf_print_fn_t info,
+		      libbpf_print_fn_t debug);
+
 #endif
Signature · 1 line
2.1.0
author maintainer
#3 lore raw

PATCH 3/24 bpf tools: Open eBPF object file and do basic validation

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch defines basic interface of libbpf. 'struct bpf_object' will be the handler of each object file. Its internal structure is hide to user. eBPF object files are compiled by LLVM as ELF format. In this patch, libelf is used to open those files, read EHDR and do basic validation according to e_type and e_machine.

All elf related staffs are grouped together and reside in efile field of 'struct bpf_object'. bpf_object__elf_finish() is introduced to clear it.

After all eBPF programs in an object file are loaded, related ELF information is useless. Close the object file and free those memory.

The zfree() and zclose() functions are introduced to ensure setting NULL pointers and negative file descriptors after resources are released.

---
 tools/lib/bpf/libbpf.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h |   8 +++
 2 files changed, 166 insertions(+)
tools/lib/bpf/libbpf.c +158 -0
index 6f0c13a1fb3c..9e446081fb1c 100644
@@ -11,8 +11,12 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
 #include <asm/unistd.h>
 #include <linux/bpf.h>
+#include <libelf.h>
+#include <gelf.h>
 
 #include "libbpf.h"
 
@@ -52,3 +56,157 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 	__pr_info = info;
 	__pr_debug = debug;
 }
+
+/* Copied from tools/perf/util/util.h */
+#ifndef zfree
+# define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
+#endif
+
+#ifndef zclose
+# define zclose(fd) ({			\
+	int ___err = 0;			\
+	if ((fd) >= 0)			\
+		___err = close((fd));	\
+	fd = -1;			\
+	___err; })
+#endif
+
+#ifdef HAVE_LIBELF_MMAP_SUPPORT
+# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ_MMAP
+#else
+# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
+#endif
+
+struct bpf_object {
+	/*
+	 * Information when doing elf related work. Only valid if fd
+	 * is valid.
+	 */
+	struct {
+		int fd;
+		Elf *elf;
+		GElf_Ehdr ehdr;
+	} efile;
+	char path[];
+};
+#define obj_elf_valid(o)	((o)->efile.elf)
+
+static struct bpf_object *bpf_object__new(const char *path)
+{
+	struct bpf_object *obj;
+
+	obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
+	if (!obj) {
+		pr_warning("alloc memory failed for %s\n", path);
+		return NULL;
+	}
+
+	strcpy(obj->path, path);
+	obj->efile.fd = -1;
+	return obj;
+}
+
+static void bpf_object__elf_finish(struct bpf_object *obj)
+{
+	if (!obj_elf_valid(obj))
+		return;
+
+	if (obj->efile.elf) {
+		elf_end(obj->efile.elf);
+		obj->efile.elf = NULL;
+	}
+	zclose(obj->efile.fd);
+}
+
+static int bpf_object__elf_init(struct bpf_object *obj)
+{
+	int err = 0;
+	GElf_Ehdr *ep;
+
+	if (obj_elf_valid(obj)) {
+		pr_warning("elf init: internal error\n");
+		return -EEXIST;
+	}
+
+	obj->efile.fd = open(obj->path, O_RDONLY);
+	if (obj->efile.fd < 0) {
+		pr_warning("failed to open %s: %s\n", obj->path,
+				strerror(errno));
+		return -errno;
+	}
+
+	obj->efile.elf = elf_begin(obj->efile.fd,
+				 LIBBPF_ELF_C_READ_MMAP,
+				 NULL);
+	if (!obj->efile.elf) {
+		pr_warning("failed to open %s as ELF file\n",
+				obj->path);
+		err = -EINVAL;
+		goto errout;
+	}
+
+	if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
+		pr_warning("failed to get EHDR from %s\n",
+				obj->path);
+		err = -EINVAL;
+		goto errout;
+	}
+	ep = &obj->efile.ehdr;
+
+	if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) {
+		pr_warning("%s is not an eBPF object file\n",
+			obj->path);
+		err = -EINVAL;
+		goto errout;
+	}
+
+	return 0;
+errout:
+	bpf_object__elf_finish(obj);
+	return err;
+}
+
+static struct bpf_object *
+__bpf_object__open(const char *path)
+{
+	struct bpf_object *obj;
+
+	if (elf_version(EV_CURRENT) == EV_NONE) {
+		pr_warning("failed to init libelf for %s\n", path);
+		return NULL;
+	}
+
+	obj = bpf_object__new(path);
+	if (!obj)
+		return NULL;
+
+	if (bpf_object__elf_init(obj))
+		goto out;
+
+	bpf_object__elf_finish(obj);
+	return obj;
+out:
+	bpf_object__close(obj);
+	return NULL;
+}
+
+struct bpf_object *bpf_object__open(const char *path)
+{
+	/* param validation */
+	if (!path)
+		return NULL;
+
+	pr_debug("loading %s\n", path);
+
+	return __bpf_object__open(path);
+}
+
+void bpf_object__close(struct bpf_object *obj)
+{
+	if (!obj)
+		return;
+
+	bpf_object__elf_finish(obj);
+
+	free(obj);
+}
tools/lib/bpf/libbpf.h +8 -0
index 8d1eebafa958..ec3301ceeb85 100644
@@ -8,6 +8,8 @@
 #ifndef __BPF_LIBBPF_H
 #define __BPF_LIBBPF_H
 
+#include <stdio.h>
+
 /*
  * In include/linux/compiler-gcc.h, __printf is defined. However
  * it should be better if libbpf.h doesn't depend on Linux header file.
@@ -20,4 +22,10 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 		      libbpf_print_fn_t info,
 		      libbpf_print_fn_t debug);
 
+/* Hide internal to user */
+struct bpf_object;
+
+struct bpf_object *bpf_object__open(const char *path);
+void bpf_object__close(struct bpf_object *object);
+
 #endif
Signature · 1 line
2.1.0
author maintainer
#4 lore raw

PATCH 4/24 bpf tools: Read eBPF object from buffer

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

To support dynamic compiling, this patch allows caller to pass a in-memory buffer to libbpf by bpf_object__open_buffer(). libbpf calls elf_memory() to open it as ELF object file.

Because __bpf_object__open() collects all required data and won't need that buffer anymore, libbpf uses that buffer directly instead of clone a new buffer. Caller of libbpf can free that buffer or use it do other things after bpf_object__open_buffer() return.

---
 tools/lib/bpf/libbpf.c | 62 ++++++++++++++++++++++++++++++++++++++++----------
 tools/lib/bpf/libbpf.h |  2 ++
 2 files changed, 52 insertions(+), 12 deletions(-)
tools/lib/bpf/libbpf.c +50 -12
index 9e446081fb1c..36dfbc1dfa77 100644
@@ -84,6 +84,8 @@ struct bpf_object {
 	 */
 	struct {
 		int fd;
+		void *obj_buf;
+		size_t obj_buf_sz;
 		Elf *elf;
 		GElf_Ehdr ehdr;
 	} efile;
@@ -91,7 +93,9 @@ struct bpf_object {
 };
 #define obj_elf_valid(o)	((o)->efile.elf)
 
-static struct bpf_object *bpf_object__new(const char *path)
+static struct bpf_object *bpf_object__new(const char *path,
+					  void *obj_buf,
+					  size_t obj_buf_sz)
 {
 	struct bpf_object *obj;
 
@@ -103,6 +107,16 @@ static struct bpf_object *bpf_object__new(const char *path)
 
 	strcpy(obj->path, path);
 	obj->efile.fd = -1;
+
+	/*
+	 * Caller of this function should also calls
+	 * bpf_object__elf_finish() after data collection to return
+	 * obj_buf to user. If not, we should duplicate the buffer to
+	 * avoid user freeing them before elf finish.
+	 */
+	obj->efile.obj_buf = obj_buf;
+	obj->efile.obj_buf_sz = obj_buf_sz;
+
 	return obj;
 }
 
@@ -116,6 +130,8 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
 		obj->efile.elf = NULL;
 	}
 	zclose(obj->efile.fd);
+	obj->efile.obj_buf = NULL;
+	obj->efile.obj_buf_sz = 0;
 }
 
 static int bpf_object__elf_init(struct bpf_object *obj)
@@ -128,16 +144,26 @@ static int bpf_object__elf_init(struct bpf_object *obj)
 		return -EEXIST;
 	}
 
-	obj->efile.fd = open(obj->path, O_RDONLY);
-	if (obj->efile.fd < 0) {
-		pr_warning("failed to open %s: %s\n", obj->path,
-				strerror(errno));
-		return -errno;
+	if (obj->efile.obj_buf_sz > 0) {
+		/*
+		 * obj_buf should have been validated by
+		 * bpf_object__open_buffer().
+		 */
+		obj->efile.elf = elf_memory(obj->efile.obj_buf,
+					    obj->efile.obj_buf_sz);
+	} else {
+		obj->efile.fd = open(obj->path, O_RDONLY);
+		if (obj->efile.fd < 0) {
+			pr_warning("failed to open %s: %s\n", obj->path,
+					strerror(errno));
+			return -errno;
+		}
+
+		obj->efile.elf = elf_begin(obj->efile.fd,
+				LIBBPF_ELF_C_READ_MMAP,
+				NULL);
 	}
 
-	obj->efile.elf = elf_begin(obj->efile.fd,
-				 LIBBPF_ELF_C_READ_MMAP,
-				 NULL);
 	if (!obj->efile.elf) {
 		pr_warning("failed to open %s as ELF file\n",
 				obj->path);
@@ -167,7 +193,7 @@ errout:
 }
 
 static struct bpf_object *
-__bpf_object__open(const char *path)
+__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 {
 	struct bpf_object *obj;
 
@@ -176,7 +202,7 @@ __bpf_object__open(const char *path)
 		return NULL;
 	}
 
-	obj = bpf_object__new(path);
+	obj = bpf_object__new(path, obj_buf, obj_buf_sz);
 	if (!obj)
 		return NULL;
 
@@ -198,7 +224,19 @@ struct bpf_object *bpf_object__open(const char *path)
 
 	pr_debug("loading %s\n", path);
 
-	return __bpf_object__open(path);
+	return __bpf_object__open(path, NULL, 0);
+}
+
+struct bpf_object *bpf_object__open_buffer(void *obj_buf,
+					   size_t obj_buf_sz)
+{
+	/* param validation */
+	if (!obj_buf || obj_buf_sz <= 0)
+		return NULL;
+
+	pr_debug("loading object from buffer\n");
+
+	return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz);
 }
 
 void bpf_object__close(struct bpf_object *obj)
tools/lib/bpf/libbpf.h +2 -0
index ec3301ceeb85..dc966dd9fd75 100644
@@ -26,6 +26,8 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 struct bpf_object;
 
 struct bpf_object *bpf_object__open(const char *path);
+struct bpf_object *bpf_object__open_buffer(void *obj_buf,
+					   size_t obj_buf_sz);
 void bpf_object__close(struct bpf_object *object);
 
 #endif
Signature · 1 line
2.1.0
author maintainer
#5 lore raw

PATCH 5/24 bpf tools: Check endianness and make libbpf fail early

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

Check endianness according to EHDR. Code is taken from tools/perf/util/symbol-elf.c.

Libbpf doesn't magically convert missmatched endianness. Even if we swap eBPF instructions to correct byte order, we are unable to deal with endianness in code logical generated by LLVM.

Therefore, libbpf should simply reject missmatched ELF object, and let LLVM to create good code.

---
 tools/lib/bpf/libbpf.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
tools/lib/bpf/libbpf.c +30 -0
index 36dfbc1dfa77..15b3e82b22f6 100644
@@ -192,6 +192,34 @@ errout:
 	return err;
 }
 
+static int
+bpf_object__check_endianness(struct bpf_object *obj)
+{
+	static unsigned int const endian = 1;
+
+	switch (obj->efile.ehdr.e_ident[EI_DATA]) {
+	case ELFDATA2LSB:
+		/* We are big endian, BPF obj is little endian. */
+		if (*(unsigned char const *)&endian != 1)
+			goto mismatch;
+		break;
+
+	case ELFDATA2MSB:
+		/* We are little endian, BPF obj is big endian. */
+		if (*(unsigned char const *)&endian != 0)
+			goto mismatch;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+
+mismatch:
+	pr_warning("Error: endianness mismatch.\n");
+	return -EINVAL;
+}
+
 static struct bpf_object *
 __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 {
@@ -208,6 +236,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 
 	if (bpf_object__elf_init(obj))
 		goto out;
+	if (bpf_object__check_endianness(obj))
+		goto out;
 
 	bpf_object__elf_finish(obj);
 	return obj;
Signature · 1 line
2.1.0
author maintainer
#6 lore raw

PATCH 6/24 bpf tools: Iterate over ELF sections to collect information

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

bpf_obj_elf_collect() is introduced to iterate over each elf sections to collection information in eBPF object files. This function will futher enhanced to collect license, kernel version, programs, configs and map information.

---
 tools/lib/bpf/libbpf.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
tools/lib/bpf/libbpf.c +53 -0
index 15b3e82b22f6..d8d6eb580188 100644
@@ -220,6 +220,57 @@ mismatch:
 	return -EINVAL;
 }
 
+static int bpf_object__elf_collect(struct bpf_object *obj)
+{
+	Elf *elf = obj->efile.elf;
+	GElf_Ehdr *ep = &obj->efile.ehdr;
+	Elf_Scn *scn = NULL;
+	int idx = 0, err = 0;
+
+	/* Elf is corrupted/truncated, avoid calling elf_strptr. */
+	if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
+		pr_warning("failed to get e_shstrndx from %s\n",
+			   obj->path);
+		return -EINVAL;
+	}
+
+	while ((scn = elf_nextscn(elf, scn)) != NULL) {
+		char *name;
+		GElf_Shdr sh;
+		Elf_Data *data;
+
+		idx++;
+		if (gelf_getshdr(scn, &sh) != &sh) {
+			pr_warning("failed to get section header from %s\n",
+				   obj->path);
+			err = -EINVAL;
+			goto out;
+		}
+
+		name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
+		if (!name) {
+			pr_warning("failed to get section name from %s\n",
+				   obj->path);
+			err = -EINVAL;
+			goto out;
+		}
+
+		data = elf_getdata(scn, 0);
+		if (!data) {
+			pr_warning("failed to get section data from %s(%s)\n",
+				   name, obj->path);
+			err = -EINVAL;
+			goto out;
+		}
+		pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n",
+			 name, (unsigned long)data->d_size,
+			 (int)sh.sh_link, (unsigned long)sh.sh_flags,
+			 (int)sh.sh_type);
+	}
+out:
+	return err;
+}
+
 static struct bpf_object *
 __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 {
@@ -238,6 +289,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 		goto out;
 	if (bpf_object__check_endianness(obj))
 		goto out;
+	if (bpf_object__elf_collect(obj))
+		goto out;
 
 	bpf_object__elf_finish(obj);
 	return obj;
Signature · 1 line
2.1.0
author maintainer
#7 lore raw

PATCH 7/24 bpf tools: Collect version and license from ELF sections

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

Expand bpf_obj_elf_collect() to collect license and kernel version information in eBPF object file. eBPF object file should have a section named 'license', which contains a string. It should also have a section named 'version', contains a u32 LINUX_VERSION_CODE.

bpf_obj_validate() is introduced to validate object file after loaded. Currently it only check existence of 'version' section.

---
 tools/lib/bpf/libbpf.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
tools/lib/bpf/libbpf.c +53 -0
index d8d6eb580188..95c8d8e5c350 100644
@@ -14,6 +14,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <asm/unistd.h>
+#include <linux/kernel.h>
 #include <linux/bpf.h>
 #include <libelf.h>
 #include <gelf.h>
@@ -78,6 +79,8 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 #endif
 
 struct bpf_object {
+	char license[64];
+	u32 kern_version;
 	/*
 	 * Information when doing elf related work. Only valid if fd
 	 * is valid.
@@ -220,6 +223,33 @@ mismatch:
 	return -EINVAL;
 }
 
+static int
+bpf_object__init_license(struct bpf_object *obj,
+			 void *data, size_t size)
+{
+	memcpy(obj->license, data,
+	       min(size, sizeof(obj->license) - 1));
+	pr_debug("license of %s is %s\n", obj->path, obj->license);
+	return 0;
+}
+
+static int
+bpf_object__init_kversion(struct bpf_object *obj,
+			  void *data, size_t size)
+{
+	u32 kver;
+
+	if (size != sizeof(kver)) {
+		pr_warning("invalid kver section in %s\n", obj->path);
+		return -EINVAL;
+	}
+	memcpy(&kver, data, sizeof(kver));
+	obj->kern_version = kver;
+	pr_debug("kernel version of %s is %x\n", obj->path,
+		 obj->kern_version);
+	return 0;
+}
+
 static int bpf_object__elf_collect(struct bpf_object *obj)
 {
 	Elf *elf = obj->efile.elf;
@@ -266,11 +296,32 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 			 name, (unsigned long)data->d_size,
 			 (int)sh.sh_link, (unsigned long)sh.sh_flags,
 			 (int)sh.sh_type);
+
+		if (strcmp(name, "license") == 0)
+			err = bpf_object__init_license(obj,
+						       data->d_buf,
+						       data->d_size);
+		else if (strcmp(name, "version") == 0)
+			err = bpf_object__init_kversion(obj,
+							data->d_buf,
+							data->d_size);
+		if (err)
+			goto out;
 	}
 out:
 	return err;
 }
 
+static int bpf_object__validate(struct bpf_object *obj)
+{
+	if (obj->kern_version == 0) {
+		pr_warning("%s doesn't provide kernel version\n",
+			   obj->path);
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static struct bpf_object *
 __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 {
@@ -291,6 +342,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 		goto out;
 	if (bpf_object__elf_collect(obj))
 		goto out;
+	if (bpf_object__validate(obj))
+		goto out;
 
 	bpf_object__elf_finish(obj);
 	return obj;
Signature · 1 line
2.1.0
author maintainer
#8 lore raw

PATCH 8/24 bpf tools: Collect map definitions from 'maps' section

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

If maps are used by eBPF programs, corresponding object file(s) should contain a section named 'map'. Which contains map definitions. This patch copies the data of the whole section. Map data parsing should be acted just before map loading.

---
 tools/lib/bpf/libbpf.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)
tools/lib/bpf/libbpf.c +29 -0
index 95c8d8e5c350..87f5054a9efb 100644
@@ -81,6 +81,9 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 struct bpf_object {
 	char license[64];
 	u32 kern_version;
+	void *maps_buf;
+	size_t maps_buf_sz;
+
 	/*
 	 * Information when doing elf related work. Only valid if fd
 	 * is valid.
@@ -250,6 +253,28 @@ bpf_object__init_kversion(struct bpf_object *obj,
 	return 0;
 }
 
+static int
+bpf_object__init_maps(struct bpf_object *obj, void *data,
+		      size_t size)
+{
+	if (size == 0) {
+		pr_debug("%s doesn't need map definition\n",
+			 obj->path);
+		return 0;
+	}
+
+	obj->maps_buf = malloc(size);
+	if (!obj->maps_buf) {
+		pr_warning("malloc maps failed: %s\n", obj->path);
+		return -ENOMEM;
+	}
+
+	obj->maps_buf_sz = size;
+	memcpy(obj->maps_buf, data, size);
+	pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size);
+	return 0;
+}
+
 static int bpf_object__elf_collect(struct bpf_object *obj)
 {
 	Elf *elf = obj->efile.elf;
@@ -305,6 +330,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 			err = bpf_object__init_kversion(obj,
 							data->d_buf,
 							data->d_size);
+		else if (strcmp(name, "maps") == 0)
+			err = bpf_object__init_maps(obj, data->d_buf,
+						    data->d_size);
 		if (err)
 			goto out;
 	}
@@ -382,5 +410,6 @@ void bpf_object__close(struct bpf_object *obj)
 
 	bpf_object__elf_finish(obj);
 
+	zfree(&obj->maps_buf);
 	free(obj);
 }
Signature · 1 line
2.1.0
author maintainer
#9 lore raw

PATCH 9/24 bpf tools: Collect symbol table from SHT_SYMTAB section

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch collects symbols section. This section is useful when linking BPF maps.

What 'bpf_map_xxx()' functions actually require are map's file descriptors (and the internal verifier converts fds into pointers to 'struct bpf_map'), which we don't know when compiling. Therefore, we should make compiler generate a 'ldr_64 r1, <imm>' instruction, and fill the 'imm' field with the actual file descriptor when loading in libbpf.

BPF programs should be written in this way:

struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(unsigned long),
.value_size = sizeof(unsigned long),
.max_entries = 1000000,
};

SEC("my_func=sys_write")
int my_func(void *ctx)

 {
     ...
     bpf_map_update_elem(&my_map, &key, &value, BPF_ANY);
     ...

}

Compiler should convert '&my_map' into a 'ldr_64, r1, <imm>' instruction, where imm should be the address of 'my_map'. According to the address, libbpf knows which map it actually referenced, and then fills the imm field with the 'fd' of that map created by it.

However, since we never really 'link' the object file, the imm field is only a record in relocation section. Therefore libbpf should do the relocation:

1. In relocation section (type == SHT_REL), positions of each such 'ldr_64' instruction are recorded with a reference of an entry in symbol table (SHT_SYMTAB);

2. From records in symbol table we can find the indics of map variables.

Libbpf first record SHT_SYMTAB and positions of each instruction which required bu such operation. Then create file descriptor. Finally, after map creation complete, replace the imm field.

This is the first patch of BPF map related stuff. It records SHT_SYMTAB into object's efile field for further use.

---
 tools/lib/bpf/libbpf.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
tools/lib/bpf/libbpf.c +10 -0
index 87f5054a9efb..9b016c086867 100644
@@ -94,6 +94,7 @@ struct bpf_object {
 		size_t obj_buf_sz;
 		Elf *elf;
 		GElf_Ehdr ehdr;
+		Elf_Data *symbols;
 	} efile;
 	char path[];
 };
@@ -135,6 +136,7 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
 		elf_end(obj->efile.elf);
 		obj->efile.elf = NULL;
 	}
+	obj->efile.symbols = NULL;
 	zclose(obj->efile.fd);
 	obj->efile.obj_buf = NULL;
 	obj->efile.obj_buf_sz = 0;
@@ -333,6 +335,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 		else if (strcmp(name, "maps") == 0)
 			err = bpf_object__init_maps(obj, data->d_buf,
 						    data->d_size);
+		else if (sh.sh_type == SHT_SYMTAB) {
+			if (obj->efile.symbols) {
+				pr_warning("bpf: multiple SYMTAB in %s\n",
+					   obj->path);
+				err = -EEXIST;
+			} else
+				obj->efile.symbols = data;
+		}
 		if (err)
 			goto out;
 	}
Signature · 1 line
2.1.0
author maintainer
#10 lore raw

PATCH 10/24 bpf tools: Collect eBPF programs from their own sections

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch collects all programs in an object file into an array of 'struct bpf_program' for further processing. That structure is for representing each eBPF program. 'bpf_prog' should be a better name, but it has been used by linux/filter.h. Although it is a kernel space name, I still prefer to call it 'bpf_program' to prevent possible confusion.

bpf_object__add_program() creates a new 'struct bpf_program' object. It first init a variable in stack using bpf_program__init(), then if success, enlarges obj->programs array and copy the new object in.

Cc Brendan Gregg <[email protected]>
Cc Daniel Borkmann <[email protected]>
Cc David Ahern <[email protected]>
Cc He Kuang <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Kaixu Xia <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Zefan Li <[email protected]>
[ Made bpf_object__add_program() propagate the error (-EINVAL or -ENOMEM) ]
---
 tools/lib/bpf/libbpf.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)
tools/lib/bpf/libbpf.c +114 -0
index 9b016c086867..102156f322b6 100644
@@ -78,12 +78,27 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
 #endif
 
+/*
+ * bpf_prog should be a better name but it has been used in
+ * linux/filter.h.
+ */
+struct bpf_program {
+	/* Index in elf obj file, for relocation use. */
+	int idx;
+	char *section_name;
+	struct bpf_insn *insns;
+	size_t insns_cnt;
+};
+
 struct bpf_object {
 	char license[64];
 	u32 kern_version;
 	void *maps_buf;
 	size_t maps_buf_sz;
 
+	struct bpf_program *programs;
+	size_t nr_programs;
+
 	/*
 	 * Information when doing elf related work. Only valid if fd
 	 * is valid.
@@ -100,6 +115,85 @@ struct bpf_object {
 };
 #define obj_elf_valid(o)	((o)->efile.elf)
 
+static void bpf_program__exit(struct bpf_program *prog)
+{
+	if (!prog)
+		return;
+
+	zfree(&prog->section_name);
+	zfree(&prog->insns);
+	prog->insns_cnt = 0;
+	prog->idx = -1;
+}
+
+static int
+bpf_program__init(void *data, size_t size, char *name, int idx,
+		    struct bpf_program *prog)
+{
+	if (size < sizeof(struct bpf_insn)) {
+		pr_warning("corrupted section '%s'\n", name);
+		return -EINVAL;
+	}
+
+	bzero(prog, sizeof(*prog));
+
+	prog->section_name = strdup(name);
+	if (!prog->section_name) {
+		pr_warning("failed to alloc name for prog %s\n",
+			   name);
+		goto errout;
+	}
+
+	prog->insns = malloc(size);
+	if (!prog->insns) {
+		pr_warning("failed to alloc insns for %s\n", name);
+		goto errout;
+	}
+	prog->insns_cnt = size / sizeof(struct bpf_insn);
+	memcpy(prog->insns, data,
+	       prog->insns_cnt * sizeof(struct bpf_insn));
+	prog->idx = idx;
+
+	return 0;
+errout:
+	bpf_program__exit(prog);
+	return -ENOMEM;
+}
+
+static int
+bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
+			char *name, int idx)
+{
+	struct bpf_program prog, *progs;
+	int nr_progs, err;
+
+	err = bpf_program__init(data, size, name, idx, &prog);
+	if (err)
+		return err;
+
+	progs = obj->programs;
+	nr_progs = obj->nr_programs;
+
+	progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1));
+	if (!progs) {
+		/*
+		 * In this case the original obj->programs
+		 * is still valid, so don't need special treat for
+		 * bpf_close_object().
+		 */
+		pr_warning("failed to alloc a new program '%s'\n",
+			   name);
+		bpf_program__exit(&prog);
+		return -ENOMEM;
+	}
+
+	pr_debug("found program %s\n", prog.section_name);
+	obj->programs = progs;
+	obj->nr_programs = nr_progs + 1;
+	progs[nr_progs] = prog;
+	return 0;
+}
+
 static struct bpf_object *bpf_object__new(const char *path,
 					  void *obj_buf,
 					  size_t obj_buf_sz)
@@ -342,6 +436,17 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 				err = -EEXIST;
 			} else
 				obj->efile.symbols = data;
+		} else if ((sh.sh_type == SHT_PROGBITS) &&
+			   (sh.sh_flags & SHF_EXECINSTR) &&
+			   (data->d_size > 0)) {
+			err = bpf_object__add_program(obj, data->d_buf,
+						      data->d_size, name, idx);
+			if (err) {
+				char errmsg[128];
+				strerror_r(-err, errmsg, sizeof(errmsg));
+				pr_warning("failed to alloc program %s (%s): %s",
+					   name, obj->path, errmsg);
+			}
 		}
 		if (err)
 			goto out;
@@ -415,11 +520,20 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
 
 void bpf_object__close(struct bpf_object *obj)
 {
+	size_t i;
+
 	if (!obj)
 		return;
 
 	bpf_object__elf_finish(obj);
 
 	zfree(&obj->maps_buf);
+
+	if (obj->programs && obj->nr_programs) {
+		for (i = 0; i < obj->nr_programs; i++)
+			bpf_program__exit(&obj->programs[i]);
+	}
+	zfree(&obj->programs);
+
 	free(obj);
 }
Signature · 1 line
2.1.0
author maintainer
#11 lore raw

PATCH 11/24 bpf tools: Collect relocation sections from SHT_REL sections

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch collects relocation sections into 'struct object'. Such sections are used for connecting maps to bpf programs. 'reloc' field in 'struct bpf_object' is introduced for storing such information.

This patch simply store the data into 'reloc' field. Following patch will parse them to know the exact instructions which are needed to be relocated.

Note that the collected data will be invalid after ELF object file is closed.

This is the second patch related to map relocation. The first one is 'bpf tools: Collect symbol table from SHT_SYMTAB section'. The principle of map relocation is described in its commit message.

---
 tools/lib/bpf/libbpf.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
tools/lib/bpf/libbpf.c +26 -0
index 102156f322b6..e8088f8214d1 100644
@@ -110,6 +110,11 @@ struct bpf_object {
 		Elf *elf;
 		GElf_Ehdr ehdr;
 		Elf_Data *symbols;
+		struct {
+			GElf_Shdr shdr;
+			Elf_Data *data;
+		} *reloc;
+		int nr_reloc;
 	} efile;
 	char path[];
 };
@@ -231,6 +236,9 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
 		obj->efile.elf = NULL;
 	}
 	obj->efile.symbols = NULL;
+
+	zfree(&obj->efile.reloc);
+	obj->efile.nr_reloc = 0;
 	zclose(obj->efile.fd);
 	obj->efile.obj_buf = NULL;
 	obj->efile.obj_buf_sz = 0;
@@ -447,6 +455,24 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 				pr_warning("failed to alloc program %s (%s): %s",
 					   name, obj->path, errmsg);
 			}
+		} else if (sh.sh_type == SHT_REL) {
+			void *reloc = obj->efile.reloc;
+			int nr_reloc = obj->efile.nr_reloc + 1;
+
+			reloc = realloc(reloc,
+					sizeof(*obj->efile.reloc) * nr_reloc);
+			if (!reloc) {
+				pr_warning("realloc failed\n");
+				err = -ENOMEM;
+			} else {
+				int n = nr_reloc - 1;
+
+				obj->efile.reloc = reloc;
+				obj->efile.nr_reloc = nr_reloc;
+
+				obj->efile.reloc[n].shdr = sh;
+				obj->efile.reloc[n].data = data;
+			}
 		}
 		if (err)
 			goto out;
Signature · 1 line
2.1.0
author maintainer
#12 lore raw

PATCH 12/24 bpf tools: Record map accessing instructions for each program

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch records the indices of instructions which are needed to be relocated. That information is saved in the 'reloc_desc' field in 'struct bpf_program'. In the loading phase (this patch takes effect in the opening phase), the collected instructions will be replaced by map loading instructions.

Since we are going to close the ELF file and clear all data at the end of the 'opening' phase, the ELF information will no longer be valid in the 'loading' phase. We have to locate the instructions before maps are loaded, instead of directly modifying the instruction.

'struct bpf_map_def' is introduced in this patch to let us know how many maps are defined in the object.

This is the third part of map relocation. The principle of map relocation is described in commit message of 'bpf tools: Collect symbol table from SHT_SYMTAB section'.

---
 tools/lib/bpf/libbpf.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h |  13 ++++++
 2 files changed, 137 insertions(+)
tools/lib/bpf/libbpf.c +124 -0
index e8088f8214d1..ac69ae3bd628 100644
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
+#include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -88,6 +89,12 @@ struct bpf_program {
 	char *section_name;
 	struct bpf_insn *insns;
 	size_t insns_cnt;
+
+	struct {
+		int insn_idx;
+		int map_idx;
+	} *reloc_desc;
+	int nr_reloc;
 };
 
 struct bpf_object {
@@ -127,6 +134,9 @@ static void bpf_program__exit(struct bpf_program *prog)
 
 	zfree(&prog->section_name);
 	zfree(&prog->insns);
+	zfree(&prog->reloc_desc);
+
+	prog->nr_reloc = 0;
 	prog->insns_cnt = 0;
 	prog->idx = -1;
 }
@@ -481,6 +491,118 @@ out:
 	return err;
 }
 
+static struct bpf_program *
+bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
+{
+	struct bpf_program *prog;
+	size_t i;
+
+	for (i = 0; i < obj->nr_programs; i++) {
+		prog = &obj->programs[i];
+		if (prog->idx == idx)
+			return prog;
+	}
+	return NULL;
+}
+
+static int
+bpf_program__collect_reloc(struct bpf_program *prog,
+			   size_t nr_maps, GElf_Shdr *shdr,
+			   Elf_Data *data, Elf_Data *symbols)
+{
+	int i, nrels;
+
+	pr_debug("collecting relocating info for: '%s'\n",
+		 prog->section_name);
+	nrels = shdr->sh_size / shdr->sh_entsize;
+
+	prog->reloc_desc = malloc(sizeof(*prog->reloc_desc) * nrels);
+	if (!prog->reloc_desc) {
+		pr_warning("failed to alloc memory in relocation\n");
+		return -ENOMEM;
+	}
+	prog->nr_reloc = nrels;
+
+	for (i = 0; i < nrels; i++) {
+		GElf_Sym sym;
+		GElf_Rel rel;
+		unsigned int insn_idx;
+		struct bpf_insn *insns = prog->insns;
+		size_t map_idx;
+
+		if (!gelf_getrel(data, i, &rel)) {
+			pr_warning("relocation: failed to get %d reloc\n", i);
+			return -EINVAL;
+		}
+
+		insn_idx = rel.r_offset / sizeof(struct bpf_insn);
+		pr_debug("relocation: insn_idx=%u\n", insn_idx);
+
+		if (!gelf_getsym(symbols,
+				 GELF_R_SYM(rel.r_info),
+				 &sym)) {
+			pr_warning("relocation: symbol %"PRIx64" not found\n",
+				   GELF_R_SYM(rel.r_info));
+			return -EINVAL;
+		}
+
+		if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
+			pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
+				   insn_idx, insns[insn_idx].code);
+			return -EINVAL;
+		}
+
+		map_idx = sym.st_value / sizeof(struct bpf_map_def);
+		if (map_idx >= nr_maps) {
+			pr_warning("bpf relocation: map_idx %d large than %d\n",
+				   (int)map_idx, (int)nr_maps - 1);
+			return -EINVAL;
+		}
+
+		prog->reloc_desc[i].insn_idx = insn_idx;
+		prog->reloc_desc[i].map_idx = map_idx;
+	}
+	return 0;
+}
+
+static int bpf_object__collect_reloc(struct bpf_object *obj)
+{
+	int i, err;
+
+	if (!obj_elf_valid(obj)) {
+		pr_warning("Internal error: elf object is closed\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < obj->efile.nr_reloc; i++) {
+		GElf_Shdr *shdr = &obj->efile.reloc[i].shdr;
+		Elf_Data *data = obj->efile.reloc[i].data;
+		int idx = shdr->sh_info;
+		struct bpf_program *prog;
+		size_t nr_maps = obj->maps_buf_sz /
+				 sizeof(struct bpf_map_def);
+
+		if (shdr->sh_type != SHT_REL) {
+			pr_warning("internal error at %d\n", __LINE__);
+			return -EINVAL;
+		}
+
+		prog = bpf_object__find_prog_by_idx(obj, idx);
+		if (!prog) {
+			pr_warning("relocation failed: no %d section\n",
+				   idx);
+			return -ENOENT;
+		}
+
+		err = bpf_program__collect_reloc(prog, nr_maps,
+						 shdr, data,
+						 obj->efile.symbols);
+		if (err)
+			return -EINVAL;
+	}
+	return 0;
+}
+
 static int bpf_object__validate(struct bpf_object *obj)
 {
 	if (obj->kern_version == 0) {
@@ -511,6 +633,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 		goto out;
 	if (bpf_object__elf_collect(obj))
 		goto out;
+	if (bpf_object__collect_reloc(obj))
+		goto out;
 	if (bpf_object__validate(obj))
 		goto out;
 
tools/lib/bpf/libbpf.h +13 -0
index dc966dd9fd75..6e75acdb9112 100644
@@ -30,4 +30,17 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
 					   size_t obj_buf_sz);
 void bpf_object__close(struct bpf_object *object);
 
+/*
+ * We don't need __attribute__((packed)) now since it is
+ * unnecessary for 'bpf_map_def' because they are all aligned.
+ * In addition, using it will trigger -Wpacked warning message,
+ * and will be treated as an error due to -Werror.
+ */
+struct bpf_map_def {
+	unsigned int type;
+	unsigned int key_size;
+	unsigned int value_size;
+	unsigned int max_entries;
+};
+
 #endif
Signature · 1 line
2.1.0
author maintainer
#13 lore raw

PATCH 13/24 bpf tools: Add bpf.c/h for common bpf operations

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch introduces bpf.c and bpf.h, which hold common functions issuing bpf syscall. The goal of these two files is to hide syscall completely from user. Note that bpf.c and bpf.h deal with kernel interface only. Things like structure of 'map' section in the ELF object is not cared by of bpf.[ch].

We first introduce bpf_create_map().

Note that, since functions in bpf.[ch] are wrapper of sys_bpf, they don't use OO style naming.

---
 tools/lib/bpf/Build |  2 +-
 tools/lib/bpf/bpf.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/bpf.h | 16 ++++++++++++++++
 3 files changed, 68 insertions(+), 1 deletion(-)
 create mode 100644 tools/lib/bpf/bpf.c
 create mode 100644 tools/lib/bpf/bpf.h
tools/lib/bpf/Build +1 -1
index a316484233c9..d8749756352d 100644
@@ -1 +1 @@
-libbpf-y := libbpf.o
+libbpf-y := libbpf.o bpf.o
tools/lib/bpf/bpf.c new file +51 -0

51 lines not on this page · raw message

tools/lib/bpf/bpf.h new file +16 -0
new file mode 100644
index 000000000000..28f79426834d
@@ -0,0 +1,16 @@
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+#ifndef __BPF_BPF_H
+#define __BPF_BPF_H
+
+#include <linux/bpf.h>
+
+int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
+		   int max_entries);
+
+#endif
Signature · 1 line
2.1.0
author maintainer
#14 lore raw

PATCH 14/24 bpf tools: Create eBPF maps defined in an object file

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch creates maps based on 'map' section in object file using bpf_create_map(), and stores the fds into an array in 'struct bpf_object'.

Previous patches parse ELF object file and collects required data, but doesn't play with the kernel. They belong to the 'opening' phase. This patch is the first patch in 'loading' phase. The 'loaded' field is introduced in 'struct bpf_object' to avoid loading an object twice, because the loading phase clears resources collected during the opening which becomes useless after loading. In this patch, maps_buf is cleared.

---
 tools/lib/bpf/libbpf.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h |   4 ++
 2 files changed, 106 insertions(+)
tools/lib/bpf/libbpf.c +102 -0
index ac69ae3bd628..54b48de3df78 100644
@@ -21,6 +21,7 @@
 #include <gelf.h>
 
 #include "libbpf.h"
+#include "bpf.h"
 
 #define __printf(a, b)	__attribute__((format(printf, a, b)))
 
@@ -105,6 +106,13 @@ struct bpf_object {
 
 	struct bpf_program *programs;
 	size_t nr_programs;
+	int *map_fds;
+	/*
+	 * This field is required because maps_buf will be freed and
+	 * maps_buf_sz will be set to 0 after loaded.
+	 */
+	size_t nr_map_fds;
+	bool loaded;
 
 	/*
 	 * Information when doing elf related work. Only valid if fd
@@ -233,6 +241,7 @@ static struct bpf_object *bpf_object__new(const char *path,
 	obj->efile.obj_buf = obj_buf;
 	obj->efile.obj_buf_sz = obj_buf_sz;
 
+	obj->loaded = false;
 	return obj;
 }
 
@@ -565,6 +574,62 @@ bpf_program__collect_reloc(struct bpf_program *prog,
 	return 0;
 }
 
+static int
+bpf_object__create_maps(struct bpf_object *obj)
+{
+	unsigned int i;
+	size_t nr_maps;
+	int *pfd;
+
+	nr_maps = obj->maps_buf_sz / sizeof(struct bpf_map_def);
+	if (!obj->maps_buf || !nr_maps) {
+		pr_debug("don't need create maps for %s\n",
+			 obj->path);
+		return 0;
+	}
+
+	obj->map_fds = malloc(sizeof(int) * nr_maps);
+	if (!obj->map_fds) {
+		pr_warning("realloc perf_bpf_map_fds failed\n");
+		return -ENOMEM;
+	}
+	obj->nr_map_fds = nr_maps;
+
+	/* fill all fd with -1 */
+	memset(obj->map_fds, -1, sizeof(int) * nr_maps);
+
+	pfd = obj->map_fds;
+	for (i = 0; i < nr_maps; i++) {
+		struct bpf_map_def def;
+
+		def = *(struct bpf_map_def *)(obj->maps_buf +
+				i * sizeof(struct bpf_map_def));
+
+		*pfd = bpf_create_map(def.type,
+				      def.key_size,
+				      def.value_size,
+				      def.max_entries);
+		if (*pfd < 0) {
+			size_t j;
+			int err = *pfd;
+
+			pr_warning("failed to create map: %s\n",
+				   strerror(errno));
+			for (j = 0; j < i; j++)
+				zclose(obj->map_fds[j]);
+			obj->nr_map_fds = 0;
+			zfree(&obj->map_fds);
+			return err;
+		}
+		pr_debug("create map: fd=%d\n", *pfd);
+		pfd++;
+	}
+
+	zfree(&obj->maps_buf);
+	obj->maps_buf_sz = 0;
+	return 0;
+}
+
 static int bpf_object__collect_reloc(struct bpf_object *obj)
 {
 	int i, err;
@@ -668,6 +733,42 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
 	return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz);
 }
 
+int bpf_object__unload(struct bpf_object *obj)
+{
+	size_t i;
+
+	if (!obj)
+		return -EINVAL;
+
+	for (i = 0; i < obj->nr_map_fds; i++)
+		zclose(obj->map_fds[i]);
+	zfree(&obj->map_fds);
+	obj->nr_map_fds = 0;
+
+	return 0;
+}
+
+int bpf_object__load(struct bpf_object *obj)
+{
+	if (!obj)
+		return -EINVAL;
+
+	if (obj->loaded) {
+		pr_warning("object should not be loaded twice\n");
+		return -EINVAL;
+	}
+
+	obj->loaded = true;
+	if (bpf_object__create_maps(obj))
+		goto out;
+
+	return 0;
+out:
+	bpf_object__unload(obj);
+	pr_warning("failed to load object '%s'\n", obj->path);
+	return -EINVAL;
+}
+
 void bpf_object__close(struct bpf_object *obj)
 {
 	size_t i;
@@ -676,6 +777,7 @@ void bpf_object__close(struct bpf_object *obj)
 		return;
 
 	bpf_object__elf_finish(obj);
+	bpf_object__unload(obj);
 
 	zfree(&obj->maps_buf);
 
tools/lib/bpf/libbpf.h +4 -0
index 6e75acdb9112..3e6960075835 100644
@@ -30,6 +30,10 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
 					   size_t obj_buf_sz);
 void bpf_object__close(struct bpf_object *object);
 
+/* Load/unload object into/from kernel */
+int bpf_object__load(struct bpf_object *obj);
+int bpf_object__unload(struct bpf_object *obj);
+
 /*
  * We don't need __attribute__((packed)) now since it is
  * unnecessary for 'bpf_map_def' because they are all aligned.
Signature · 1 line
2.1.0
author maintainer
#15 lore raw

PATCH 15/24 bpf tools: Relocate eBPF programs

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

If an eBPF program accesses a map, LLVM generates a load instruction which loads an absolute address into a register, like this:

ld_64 r1, <MCOperand Expr:(mymap)>
...
call 2

That ld_64 instruction will be recorded in relocation section. To enable the usage of that map, relocation must be done by replacing the immediate value by real map file descriptor so it can be found by eBPF map functions.

This patch to the relocation work based on information collected by patches:

'bpf tools: Collect symbol table from SHT_SYMTAB section', 'bpf tools: Collect relocation sections from SHT_REL sections' and
'bpf tools: Record map accessing instructions for each program'.

For each instruction which needs relocation, it inject corresponding file descriptor to imm field. As a part of protocol, src_reg is set to BPF_PSEUDO_MAP_FD to notify kernel this is a map loading instruction.

This is the final part of map relocation patch. The principle of map relocation is described in commit message of 'bpf tools: Collect symbol table from SHT_SYMTAB section'.

---
 tools/lib/bpf/libbpf.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)
tools/lib/bpf/libbpf.c +52 -0
index 54b48de3df78..94f966008887 100644
@@ -630,6 +630,56 @@ bpf_object__create_maps(struct bpf_object *obj)
 	return 0;
 }
 
+static int
+bpf_program__relocate(struct bpf_program *prog, int *map_fds)
+{
+	int i;
+
+	if (!prog || !prog->reloc_desc)
+		return 0;
+
+	for (i = 0; i < prog->nr_reloc; i++) {
+		int insn_idx, map_idx;
+		struct bpf_insn *insns = prog->insns;
+
+		insn_idx = prog->reloc_desc[i].insn_idx;
+		map_idx = prog->reloc_desc[i].map_idx;
+
+		if (insn_idx >= (int)prog->insns_cnt) {
+			pr_warning("relocation out of range: '%s'\n",
+				   prog->section_name);
+			return -ERANGE;
+		}
+		insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
+		insns[insn_idx].imm = map_fds[map_idx];
+	}
+
+	zfree(&prog->reloc_desc);
+	prog->nr_reloc = 0;
+	return 0;
+}
+
+
+static int
+bpf_object__relocate(struct bpf_object *obj)
+{
+	struct bpf_program *prog;
+	size_t i;
+	int err;
+
+	for (i = 0; i < obj->nr_programs; i++) {
+		prog = &obj->programs[i];
+
+		err = bpf_program__relocate(prog, obj->map_fds);
+		if (err) {
+			pr_warning("failed to relocate '%s'\n",
+				   prog->section_name);
+			return err;
+		}
+	}
+	return 0;
+}
+
 static int bpf_object__collect_reloc(struct bpf_object *obj)
 {
 	int i, err;
@@ -761,6 +811,8 @@ int bpf_object__load(struct bpf_object *obj)
 	obj->loaded = true;
 	if (bpf_object__create_maps(obj))
 		goto out;
+	if (bpf_object__relocate(obj))
+		goto out;
 
 	return 0;
 out:
Signature · 1 line
2.1.0
author maintainer
#16 lore raw

PATCH 16/24 bpf tools: Introduce bpf_load_program() to bpf.c

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

bpf_load_program() can be used to load bpf program into kernel. To make loading faster, first try to load without logbuf. Try again with logbuf if the first try failed.

---
 tools/lib/bpf/bpf.c | 34 ++++++++++++++++++++++++++++++++++
 tools/lib/bpf/bpf.h |  7 +++++++
 2 files changed, 41 insertions(+)
tools/lib/bpf/bpf.c +34 -0
index 208de7c31e60..a6331050ab79 100644
@@ -29,6 +29,11 @@
 # endif
 #endif
 
+static __u64 ptr_to_u64(void *ptr)
+{
+	return (__u64) (unsigned long) ptr;
+}
+
 static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
 		   unsigned int size)
 {
@@ -49,3 +54,32 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size,
 
 	return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
 }
+
+int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
+		     size_t insns_cnt, char *license,
+		     u32 kern_version, char *log_buf, size_t log_buf_sz)
+{
+	int fd;
+	union bpf_attr attr;
+
+	bzero(&attr, sizeof(attr));
+	attr.prog_type = type;
+	attr.insn_cnt = (__u32)insns_cnt;
+	attr.insns = ptr_to_u64(insns);
+	attr.license = ptr_to_u64(license);
+	attr.log_buf = ptr_to_u64(NULL);
+	attr.log_size = 0;
+	attr.log_level = 0;
+	attr.kern_version = kern_version;
+
+	fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+	if (fd >= 0 || !log_buf || !log_buf_sz)
+		return fd;
+
+	/* Try again with log */
+	attr.log_buf = ptr_to_u64(log_buf);
+	attr.log_size = log_buf_sz;
+	attr.log_level = 1;
+	log_buf[0] = 0;
+	return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+}
tools/lib/bpf/bpf.h +7 -0
index 28f79426834d..854b7361b784 100644
@@ -13,4 +13,11 @@
 int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
 		   int max_entries);
 
+/* Recommend log buffer size */
+#define BPF_LOG_BUF_SIZE 65536
+int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
+		     size_t insns_cnt, char *license,
+		     u32 kern_version, char *log_buf,
+		     size_t log_buf_sz);
+
 #endif
Signature · 1 line
2.1.0
author maintainer
#17 lore raw

PATCH 17/24 bpf tools: Load eBPF programs in object files into kernel

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch utilizes previous introduced bpf_load_program to load programs in the ELF file into kernel. Result is stored in 'fd' field in 'struct bpf_program'.

During loading, it allocs a log buffer and free it before return. Note that that buffer is not passed to bpf_load_program() if the first loading try is successful. Doesn't use a statically allocated log buffer to avoid potention multi-thread problem.

Instructions collected during opening is cleared after loading.

load_program() is created for loading a 'struct bpf_insn' array into kernel, bpf_program__load() calls it. By this design we have a function loads instructions into kernel. It will be used by further patches, which creates different instances from a program and load them into kernel.

---
 tools/lib/bpf/libbpf.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)
tools/lib/bpf/libbpf.c +90 -0
index 94f966008887..38447b7d6e73 100644
@@ -96,6 +96,8 @@ struct bpf_program {
 		int map_idx;
 	} *reloc_desc;
 	int nr_reloc;
+
+	int fd;
 };
 
 struct bpf_object {
@@ -135,11 +137,20 @@ struct bpf_object {
 };
 #define obj_elf_valid(o)	((o)->efile.elf)
 
+static void bpf_program__unload(struct bpf_program *prog)
+{
+	if (!prog)
+		return;
+
+	zclose(prog->fd);
+}
+
 static void bpf_program__exit(struct bpf_program *prog)
 {
 	if (!prog)
 		return;
 
+	bpf_program__unload(prog);
 	zfree(&prog->section_name);
 	zfree(&prog->insns);
 	zfree(&prog->reloc_desc);
@@ -176,6 +187,7 @@ bpf_program__init(void *data, size_t size, char *name, int idx,
 	memcpy(prog->insns, data,
 	       prog->insns_cnt * sizeof(struct bpf_insn));
 	prog->idx = idx;
+	prog->fd = -1;
 
 	return 0;
 errout:
@@ -718,6 +730,79 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
 	return 0;
 }
 
+static int
+load_program(struct bpf_insn *insns, int insns_cnt,
+	     char *license, u32 kern_version, int *pfd)
+{
+	int ret;
+	char *log_buf;
+
+	if (!insns || !insns_cnt)
+		return -EINVAL;
+
+	log_buf = malloc(BPF_LOG_BUF_SIZE);
+	if (!log_buf)
+		pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
+
+	ret = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
+			       insns_cnt, license, kern_version,
+			       log_buf, BPF_LOG_BUF_SIZE);
+
+	if (ret >= 0) {
+		*pfd = ret;
+		ret = 0;
+		goto out;
+	}
+
+	ret = -EINVAL;
+	pr_warning("load bpf program failed: %s\n", strerror(errno));
+
+	if (log_buf) {
+		pr_warning("-- BEGIN DUMP LOG ---\n");
+		pr_warning("\n%s\n", log_buf);
+		pr_warning("-- END LOG --\n");
+	}
+
+out:
+	free(log_buf);
+	return ret;
+}
+
+static int
+bpf_program__load(struct bpf_program *prog,
+		  char *license, u32 kern_version)
+{
+	int err, fd;
+
+	err = load_program(prog->insns, prog->insns_cnt,
+			   license, kern_version, &fd);
+	if (!err)
+		prog->fd = fd;
+
+	if (err)
+		pr_warning("failed to load program '%s'\n",
+			   prog->section_name);
+	zfree(&prog->insns);
+	prog->insns_cnt = 0;
+	return err;
+}
+
+static int
+bpf_object__load_progs(struct bpf_object *obj)
+{
+	size_t i;
+	int err;
+
+	for (i = 0; i < obj->nr_programs; i++) {
+		err = bpf_program__load(&obj->programs[i],
+					obj->license,
+					obj->kern_version);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
 static int bpf_object__validate(struct bpf_object *obj)
 {
 	if (obj->kern_version == 0) {
@@ -795,6 +880,9 @@ int bpf_object__unload(struct bpf_object *obj)
 	zfree(&obj->map_fds);
 	obj->nr_map_fds = 0;
 
+	for (i = 0; i < obj->nr_programs; i++)
+		bpf_program__unload(&obj->programs[i]);
+
 	return 0;
 }
 
@@ -813,6 +901,8 @@ int bpf_object__load(struct bpf_object *obj)
 		goto out;
 	if (bpf_object__relocate(obj))
 		goto out;
+	if (bpf_object__load_progs(obj))
+		goto out;
 
 	return 0;
 out:
Signature · 1 line
2.1.0
author maintainer
#18 lore raw

PATCH 18/24 bpf tools: Introduce accessors for struct bpf_program

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch introduces accessors for user of libbpf to retrieve section name and fd of a opened/loaded eBPF program. 'struct bpf_prog_handler' is used for that purpose. Accessors of programs section name and file descriptor are provided. Set/get private data are also impelmented.

---
 tools/lib/bpf/libbpf.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h | 24 +++++++++++++++++
 2 files changed, 96 insertions(+)
tools/lib/bpf/libbpf.c +72 -0
index 38447b7d6e73..ae1c5cbd8158 100644
@@ -98,6 +98,10 @@ struct bpf_program {
 	int nr_reloc;
 
 	int fd;
+
+	struct bpf_object *obj;
+	void *priv;
+	bpf_program_clear_priv_t clear_priv;
 };
 
 struct bpf_object {
@@ -150,6 +154,12 @@ static void bpf_program__exit(struct bpf_program *prog)
 	if (!prog)
 		return;
 
+	if (prog->clear_priv)
+		prog->clear_priv(prog, prog->priv);
+
+	prog->priv = NULL;
+	prog->clear_priv = NULL;
+
 	bpf_program__unload(prog);
 	zfree(&prog->section_name);
 	zfree(&prog->insns);
@@ -225,6 +235,7 @@ bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
 	pr_debug("found program %s\n", prog.section_name);
 	obj->programs = progs;
 	obj->nr_programs = nr_progs + 1;
+	prog.obj = obj;
 	progs[nr_progs] = prog;
 	return 0;
 }
@@ -931,3 +942,64 @@ void bpf_object__close(struct bpf_object *obj)
 
 	free(obj);
 }
+
+struct bpf_program *
+bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
+{
+	size_t idx;
+
+	if (!obj->programs)
+		return NULL;
+	/* First handler */
+	if (prev == NULL)
+		return &obj->programs[0];
+
+	if (prev->obj != obj) {
+		pr_warning("error: program handler doesn't match object\n");
+		return NULL;
+	}
+
+	idx = (prev - obj->programs) + 1;
+	if (idx >= obj->nr_programs)
+		return NULL;
+	return &obj->programs[idx];
+}
+
+int bpf_program__set_private(struct bpf_program *prog,
+			     void *priv,
+			     bpf_program_clear_priv_t clear_priv)
+{
+	if (prog->priv && prog->clear_priv)
+		prog->clear_priv(prog, prog->priv);
+
+	prog->priv = priv;
+	prog->clear_priv = clear_priv;
+	return 0;
+}
+
+int bpf_program__get_private(struct bpf_program *prog, void **ppriv)
+{
+	*ppriv = prog->priv;
+	return 0;
+}
+
+const char *bpf_program__title(struct bpf_program *prog, bool dup)
+{
+	const char *title;
+
+	title = prog->section_name;
+	if (dup) {
+		title = strdup(title);
+		if (!title) {
+			pr_warning("failed to strdup program title\n");
+			return NULL;
+		}
+	}
+
+	return title;
+}
+
+int bpf_program__fd(struct bpf_program *prog)
+{
+	return prog->fd;
+}
tools/lib/bpf/libbpf.h +24 -0
index 3e6960075835..657e497bd586 100644
@@ -9,6 +9,7 @@
 #define __BPF_LIBBPF_H
 
 #include <stdio.h>
+#include <stdbool.h>
 
 /*
  * In include/linux/compiler-gcc.h, __printf is defined. However
@@ -34,6 +35,29 @@ void bpf_object__close(struct bpf_object *object);
 int bpf_object__load(struct bpf_object *obj);
 int bpf_object__unload(struct bpf_object *obj);
 
+/* Accessors of bpf_program. */
+struct bpf_program;
+struct bpf_program *bpf_program__next(struct bpf_program *prog,
+				      struct bpf_object *obj);
+
+#define bpf_object__for_each_program(pos, obj)		\
+	for ((pos) = bpf_program__next(NULL, (obj));	\
+	     (pos) != NULL;				\
+	     (pos) = bpf_program__next((pos), (obj)))
+
+typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
+					 void *);
+
+int bpf_program__set_private(struct bpf_program *prog, void *priv,
+			     bpf_program_clear_priv_t clear_priv);
+
+int bpf_program__get_private(struct bpf_program *prog,
+			     void **ppriv);
+
+const char *bpf_program__title(struct bpf_program *prog, bool dup);
+
+int bpf_program__fd(struct bpf_program *prog);
+
 /*
  * We don't need __attribute__((packed)) now since it is
  * unnecessary for 'bpf_map_def' because they are all aligned.
Signature · 1 line
2.1.0
author maintainer
#19 lore raw

PATCH 19/24 bpf tools: Link all bpf objects onto a list

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

To allow enumeration of all bpf_objects, keep them in a list (hidden to caller). bpf_object__for_each_safe() is introduced to do this iteration. It is safe even user close the object during iteration.

---
 tools/lib/bpf/libbpf.c | 32 ++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h |  7 +++++++
 2 files changed, 39 insertions(+)
tools/lib/bpf/libbpf.c +32 -0
index ae1c5cbd8158..4fa4bc4505f5 100644
@@ -17,6 +17,7 @@
 #include <asm/unistd.h>
 #include <linux/kernel.h>
 #include <linux/bpf.h>
+#include <linux/list.h>
 #include <libelf.h>
 #include <gelf.h>
 
@@ -104,6 +105,8 @@ struct bpf_program {
 	bpf_program_clear_priv_t clear_priv;
 };
 
+static LIST_HEAD(bpf_objects_list);
+
 struct bpf_object {
 	char license[64];
 	u32 kern_version;
@@ -137,6 +140,12 @@ struct bpf_object {
 		} *reloc;
 		int nr_reloc;
 	} efile;
+	/*
+	 * All loaded bpf_object is linked in a list, which is
+	 * hidden to caller. bpf_objects__<func> handlers deal with
+	 * all objects.
+	 */
+	struct list_head list;
 	char path[];
 };
 #define obj_elf_valid(o)	((o)->efile.elf)
@@ -265,6 +274,9 @@ static struct bpf_object *bpf_object__new(const char *path,
 	obj->efile.obj_buf_sz = obj_buf_sz;
 
 	obj->loaded = false;
+
+	INIT_LIST_HEAD(&obj->list);
+	list_add(&obj->list, &bpf_objects_list);
 	return obj;
 }
 
@@ -940,9 +952,29 @@ void bpf_object__close(struct bpf_object *obj)
 	}
 	zfree(&obj->programs);
 
+	list_del(&obj->list);
 	free(obj);
 }
 
+struct bpf_object *
+bpf_object__next(struct bpf_object *prev)
+{
+	struct bpf_object *next;
+
+	if (!prev)
+		next = list_first_entry(&bpf_objects_list,
+					struct bpf_object,
+					list);
+	else
+		next = list_next_entry(prev, list);
+
+	/* Empty list is noticed here so don't need checking on entry. */
+	if (&next->list == &bpf_objects_list)
+		return NULL;
+
+	return next;
+}
+
 struct bpf_program *
 bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
 {
tools/lib/bpf/libbpf.h +7 -0
index 657e497bd586..ea8adc206b62 100644
@@ -35,6 +35,13 @@ void bpf_object__close(struct bpf_object *object);
 int bpf_object__load(struct bpf_object *obj);
 int bpf_object__unload(struct bpf_object *obj);
 
+struct bpf_object *bpf_object__next(struct bpf_object *prev);
+#define bpf_object__for_each_safe(pos, tmp)			\
+	for ((pos) = bpf_object__next(NULL),		\
+		(tmp) = bpf_object__next(pos);		\
+	     (pos) != NULL;				\
+	     (pos) = (tmp), (tmp) = bpf_object__next(tmp))
+
 /* Accessors of bpf_program. */
 struct bpf_program;
 struct bpf_program *bpf_program__next(struct bpf_program *prog,
Signature · 1 line
2.1.0
author maintainer
#20 lore raw

PATCH 20/24 perf tools: Introduce llvm config options

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch introduces [llvm] config section with 5 options. Following patches will use then to config llvm dynamica compiling.

'llvm-utils.[ch]' is introduced in this patch for holding all llvm/clang related stuffs.

Example:

[llvm]
# Path to clang. If omit, search it from $PATH. clang-path = "/path/to/clang"

# Cmdline template. Following line shows its default value. # Environment variable is used to passing options.
#
# *NOTE*: -D__KERNEL__ MUST appears before $CLANG_OPTIONS, # so user have a chance to use -U__KERNEL__ in $CLANG_OPTIONS # to cancel it.
clang-bpf-cmd-template = "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS \
$KERNEL_INC_OPTIONS -Wno-unused-value \
-Wno-pointer-sign -working-directory \
$WORKING_DIR -c $CLANG_SOURCE -target \
bpf -O2 -o -"

# Options passed to clang, will be passed to cmdline by # $CLANG_OPTIONS.
clang-opt = "-Wno-unused-value -Wno-pointer-sign"

# kbuild directory. If not set, use /lib/modules/uname -r/build. # If set to "" deliberately, skip kernel header auto-detector. kbuild-dir = "/path/to/kernel/build"

# Options passed to 'make' when detecting kernel header options. kbuild-opts = "ARCH=x86_64"

---
 tools/perf/util/Build        |  1 +
 tools/perf/util/config.c     |  4 ++++
 tools/perf/util/llvm-utils.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/llvm-utils.h | 36 +++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+)
 create mode 100644 tools/perf/util/llvm-utils.c
 create mode 100644 tools/perf/util/llvm-utils.h
tools/perf/util/Build +1 -0
index a1e5168dc1fb..2ee81d74cf45 100644
@@ -14,6 +14,7 @@ libperf-y += find_next_bit.o
 libperf-y += help.o
 libperf-y += kallsyms.o
 libperf-y += levenshtein.o
+libperf-y += llvm-utils.o
 libperf-y += parse-options.o
 libperf-y += parse-events.o
 libperf-y += path.o
tools/perf/util/config.c +4 -0
index e18f653cd7db..2e452ac1353d 100644
@@ -12,6 +12,7 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "util/hist.h"  /* perf_hist_config */
+#include "util/llvm-utils.h"   /* perf_llvm_config */
 
 #define MAXNAME (256)
 
@@ -408,6 +409,9 @@ int perf_default_config(const char *var, const char *value,
 	if (!prefixcmp(var, "call-graph."))
 		return perf_callchain_config(var, value);
 
+	if (!prefixcmp(var, "llvm."))
+		return perf_llvm_config(var, value);
+
 	/* Add other config variables here. */
 	return 0;
 }
tools/perf/util/llvm-utils.c new file +45 -0

45 lines not on this page · raw message

tools/perf/util/llvm-utils.h new file +36 -0
new file mode 100644
index 000000000000..504b799687fb
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __LLVM_UTILS_H
+#define __LLVM_UTILS_H
+
+#include "debug.h"
+
+struct llvm_param {
+	/* Path of clang executable */
+	const char *clang_path;
+	/*
+	 * Template of clang bpf compiling. 5 env variables
+	 * can be used:
+	 *   $CLANG_EXEC:		Path to clang.
+	 *   $CLANG_OPTIONS:		Extra options to clang.
+	 *   $KERNEL_INC_OPTIONS:	Kernel include directories.
+	 *   $WORKING_DIR:		Kernel source directory.
+	 *   $CLANG_SOURCE:		Source file to be compiled.
+	 */
+	const char *clang_bpf_cmd_template;
+	/* Will be filled in $CLANG_OPTIONS */
+	const char *clang_opt;
+	/* Where to find kbuild system */
+	const char *kbuild_dir;
+	/*
+	 * Arguments passed to make, like 'ARCH=arm' if doing cross
+	 * compiling. Should not be used for dynamic compiling.
+	 */
+	const char *kbuild_opts;
+};
+
+extern struct llvm_param llvm_param;
+extern int perf_llvm_config(const char *var, const char *value);
+#endif
Signature · 1 line
2.1.0
author maintainer
#21 lore raw

PATCH 21/24 perf tools: Call clang to compile C source to object code

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This is the core patch for supporting eBPF on-the-fly compiling, does the following work:

1. Search clang compiler using search_program().

2. Run command template defined in llvm-bpf-cmd-template option in [llvm] config section using read_from_pipe(). Patch of clang and source code path is injected into shell command using environment variable using force_set_env().

Commiter notice:

When building with DEBUG=1 we get a compiler error that gets fixed with the same approach described in commit b236512280fb:

perf kmem: Fix compiler warning about may be accessing uninitialized variable

The last argument to strtok_r doesn't need to be initialized, its just a placeholder to make this routine reentrant, but gcc doesn't know about that and complains, breaking the build, fix it by setting it to NULL.

---
 tools/perf/util/llvm-utils.c | 226 +++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/llvm-utils.h |   3 +
 2 files changed, 229 insertions(+)
tools/perf/util/llvm-utils.c +226 -0
index 472e8cd69361..5ae11249baad 100644
@@ -43,3 +43,229 @@ int perf_llvm_config(const char *var, const char *value)
 		return -1;
 	return 0;
 }
+
+static int
+search_program(const char *def, const char *name,
+	       char *output)
+{
+	char *env, *path, *tmp = NULL;
+	char buf[PATH_MAX];
+	int ret;
+
+	output[0] = '\0';
+	if (def && def[0] != '\0') {
+		if (def[0] == '/') {
+			if (access(def, F_OK) == 0) {
+				strlcpy(output, def, PATH_MAX);
+				return 0;
+			}
+		} else if (def[0] != '\0')
+			name = def;
+	}
+
+	env = getenv("PATH");
+	if (!env)
+		return -1;
+	env = strdup(env);
+	if (!env)
+		return -1;
+
+	ret = -ENOENT;
+	path = strtok_r(env, ":",  &tmp);
+	while (path) {
+		scnprintf(buf, sizeof(buf), "%s/%s", path, name);
+		if (access(buf, F_OK) == 0) {
+			strlcpy(output, buf, PATH_MAX);
+			ret = 0;
+			break;
+		}
+		path = strtok_r(NULL, ":", &tmp);
+	}
+
+	free(env);
+	return ret;
+}
+
+#define READ_SIZE	4096
+static int
+read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz)
+{
+	int err = 0;
+	void *buf = NULL;
+	FILE *file = NULL;
+	size_t read_sz = 0, buf_sz = 0;
+
+	file = popen(cmd, "r");
+	if (!file) {
+		pr_err("ERROR: unable to popen cmd: %s\n",
+		       strerror(errno));
+		return -EINVAL;
+	}
+
+	while (!feof(file) && !ferror(file)) {
+		/*
+		 * Make buf_sz always have obe byte extra space so we
+		 * can put '\0' there.
+		 */
+		if (buf_sz - read_sz < READ_SIZE + 1) {
+			void *new_buf;
+
+			buf_sz = read_sz + READ_SIZE + 1;
+			new_buf = realloc(buf, buf_sz);
+
+			if (!new_buf) {
+				pr_err("ERROR: failed to realloc memory\n");
+				err = -ENOMEM;
+				goto errout;
+			}
+
+			buf = new_buf;
+		}
+		read_sz += fread(buf + read_sz, 1, READ_SIZE, file);
+	}
+
+	if (buf_sz - read_sz < 1) {
+		pr_err("ERROR: internal error\n");
+		err = -EINVAL;
+		goto errout;
+	}
+
+	if (ferror(file)) {
+		pr_err("ERROR: error occurred when reading from pipe: %s\n",
+		       strerror(errno));
+		err = -EIO;
+		goto errout;
+	}
+
+	err = WEXITSTATUS(pclose(file));
+	file = NULL;
+	if (err) {
+		err = -EINVAL;
+		goto errout;
+	}
+
+	/*
+	 * If buf is string, give it terminal '\0' to make our life
+	 * easier. If buf is not string, that '\0' is out of space
+	 * indicated by read_sz so caller won't even notice it.
+	 */
+	((char *)buf)[read_sz] = '\0';
+
+	if (!p_buf)
+		free(buf);
+	else
+		*p_buf = buf;
+
+	if (p_read_sz)
+		*p_read_sz = read_sz;
+	return 0;
+
+errout:
+	if (file)
+		pclose(file);
+	free(buf);
+	if (p_buf)
+		*p_buf = NULL;
+	if (p_read_sz)
+		*p_read_sz = 0;
+	return err;
+}
+
+static inline void
+force_set_env(const char *var, const char *value)
+{
+	if (value) {
+		setenv(var, value, 1);
+		pr_debug("set env: %s=%s\n", var, value);
+	} else {
+		unsetenv(var);
+		pr_debug("unset env: %s\n", var);
+	}
+}
+
+static void
+version_notice(void)
+{
+	pr_err(
+"     \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n"
+"     \tYou may want to try git trunk:\n"
+"     \t\tgit clone http://llvm.org/git/llvm.git\n"
+"     \t\t     and\n"
+"     \t\tgit clone http://llvm.org/git/clang.git\n\n"
+"     \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n"
+"     \tdebian/ubuntu:\n"
+"     \t\thttp://llvm.org/apt\n\n"
+"     \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n"
+"     \toption in [llvm] section of ~/.perfconfig to:\n\n"
+"     \t  \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \\\n"
+"     \t     -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n"
+"     \t     -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n"
+"     \t(Replace /path/to/llc with path to your llc)\n\n"
+);
+}
+
+int llvm__compile_bpf(const char *path, void **p_obj_buf,
+		      size_t *p_obj_buf_sz)
+{
+	int err;
+	char clang_path[PATH_MAX];
+	const char *clang_opt = llvm_param.clang_opt;
+	const char *template = llvm_param.clang_bpf_cmd_template;
+	void *obj_buf = NULL;
+	size_t obj_buf_sz;
+
+	if (!template)
+		template = CLANG_BPF_CMD_DEFAULT_TEMPLATE;
+
+	err = search_program(llvm_param.clang_path,
+			     "clang", clang_path);
+	if (err) {
+		pr_err(
+"ERROR:\tunable to find clang.\n"
+"Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n"
+"     \tand 'clang-path' option in [llvm] section of ~/.perfconfig.\n");
+		version_notice();
+		return -ENOENT;
+	}
+
+	force_set_env("CLANG_EXEC", clang_path);
+	force_set_env("CLANG_OPTIONS", clang_opt);
+	force_set_env("KERNEL_INC_OPTIONS", NULL);
+	force_set_env("WORKING_DIR", ".");
+
+	/*
+	 * Since we may reset clang's working dir, path of source file
+	 * should be transferred into absolute path, except we want
+	 * stdin to be source file (testing).
+	 */
+	force_set_env("CLANG_SOURCE",
+		      (path[0] == '-') ? path :
+		      make_nonrelative_path(path));
+
+	pr_debug("llvm compiling command template: %s\n", template);
+	err = read_from_pipe(template, &obj_buf, &obj_buf_sz);
+	if (err) {
+		pr_err("ERROR:\tunable to compile %s\n", path);
+		pr_err("Hint:\tCheck error message shown above.\n");
+		pr_err("Hint:\tYou can also pre-compile it into .o using:\n");
+		pr_err("     \t\tclang -target bpf -O2 -c %s\n", path);
+		pr_err("     \twith proper -I and -D options.\n");
+		goto errout;
+	}
+
+	if (!p_obj_buf)
+		free(obj_buf);
+	else
+		*p_obj_buf = obj_buf;
+
+	if (p_obj_buf_sz)
+		*p_obj_buf_sz = obj_buf_sz;
+	return 0;
+errout:
+	free(obj_buf);
+	if (p_obj_buf)
+		*p_obj_buf = NULL;
+	if (p_obj_buf_sz)
+		*p_obj_buf_sz = 0;
+	return err;
+}
tools/perf/util/llvm-utils.h +3 -0
index 504b799687fb..d23adbca120b 100644
@@ -33,4 +33,7 @@ struct llvm_param {
 
 extern struct llvm_param llvm_param;
 extern int perf_llvm_config(const char *var, const char *value);
+
+extern int llvm__compile_bpf(const char *path, void **p_obj_buf,
+			     size_t *p_obj_buf_sz);
 #endif
Signature · 1 line
2.1.0
author maintainer
#22 lore raw

PATCH 22/24 perf tools: Auto detecting kernel build directory

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

This patch detects kernel build directory by checking the existence of include/generated/autoconf.h.

clang working directory is changed to kbuild directory if it is found, to help user use relative include path. Following patch will detect kernel include directory, which contains relative include patch so this workdir changing is needed.

Users are allowed to set 'kbuild-dir = ""' manually to disable this checking.

---
 tools/perf/util/llvm-utils.c | 80 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 79 insertions(+), 1 deletion(-)
tools/perf/util/llvm-utils.c +79 -1
index 5ae11249baad..6bfcb2dd3161 100644
@@ -4,6 +4,7 @@
  */
 
 #include <stdio.h>
+#include <sys/utsname.h>
 #include "util.h"
 #include "debug.h"
 #include "llvm-utils.h"
@@ -204,6 +205,74 @@ version_notice(void)
 );
 }
 
+static int detect_kbuild_dir(char **kbuild_dir)
+{
+	const char *test_dir = llvm_param.kbuild_dir;
+	const char *prefix_dir = "";
+	const char *suffix_dir = "";
+
+	char *autoconf_path;
+	struct utsname utsname;
+
+	int err;
+
+	if (!test_dir) {
+		err = uname(&utsname);
+		if (err) {
+			pr_warning("uname failed: %s\n", strerror(errno));
+			return -EINVAL;
+		}
+
+		test_dir = utsname.release;
+		prefix_dir = "/lib/modules/";
+		suffix_dir = "/build";
+	}
+
+	err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h",
+		       prefix_dir, test_dir, suffix_dir);
+	if (err < 0)
+		return -ENOMEM;
+
+	if (access(autoconf_path, R_OK) == 0) {
+		free(autoconf_path);
+
+		err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir,
+			       suffix_dir);
+		if (err < 0)
+			return -ENOMEM;
+		return 0;
+	}
+	free(autoconf_path);
+	return -ENOENT;
+}
+
+static inline void
+get_kbuild_opts(char **kbuild_dir)
+{
+	int err;
+
+	if (!kbuild_dir)
+		return;
+
+	*kbuild_dir = NULL;
+
+	if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
+		pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
+		pr_debug("Skip kbuild options detection.\n");
+		return;
+	}
+
+	err = detect_kbuild_dir(kbuild_dir);
+	if (err) {
+		pr_warning(
+"WARNING:\tunable to get correct kernel building directory.\n"
+"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n"
+"     \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n"
+"     \tdetection.\n\n");
+		return;
+	}
+}
+
 int llvm__compile_bpf(const char *path, void **p_obj_buf,
 		      size_t *p_obj_buf_sz)
 {
@@ -211,6 +280,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 	char clang_path[PATH_MAX];
 	const char *clang_opt = llvm_param.clang_opt;
 	const char *template = llvm_param.clang_bpf_cmd_template;
+	char *kbuild_dir = NULL;
 	void *obj_buf = NULL;
 	size_t obj_buf_sz;
 
@@ -228,10 +298,16 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 		return -ENOENT;
 	}
 
+	/*
+	 * This is an optional work. Even it fail we can continue our
+	 * work. Needn't to check error return.
+	 */
+	get_kbuild_opts(&kbuild_dir);
+
 	force_set_env("CLANG_EXEC", clang_path);
 	force_set_env("CLANG_OPTIONS", clang_opt);
 	force_set_env("KERNEL_INC_OPTIONS", NULL);
-	force_set_env("WORKING_DIR", ".");
+	force_set_env("WORKING_DIR", kbuild_dir ? : ".");
 
 	/*
 	 * Since we may reset clang's working dir, path of source file
@@ -253,6 +329,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 		goto errout;
 	}
 
+	free(kbuild_dir);
 	if (!p_obj_buf)
 		free(obj_buf);
 	else
@@ -262,6 +339,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 		*p_obj_buf_sz = obj_buf_sz;
 	return 0;
 errout:
+	free(kbuild_dir);
 	free(obj_buf);
 	if (p_obj_buf)
 		*p_obj_buf = NULL;
Signature · 1 line
2.1.0
author maintainer
#23 lore raw

PATCH 23/24 perf tools: Auto detecting kernel include options

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

To help user find correct kernel include options, this patch extracts them from kbuild system by an embedded script kinc_fetch_script, which creates a temporary directory, generates Makefile and an empty dummy.o then use the Makefile to fetch $(NOSTDINC_FLAGS), $(LINUXINCLUDE) and $(EXTRA_CFLAGS) options. The result is passed to compiler script using 'KERNEL_INC_OPTIONS' environment variable.

Because options from kbuild contains relative path like 'Iinclude/generated/uapi', the work directory must be changed. This is done by previous patch.

---
 tools/perf/util/llvm-utils.c | 60 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 55 insertions(+), 5 deletions(-)
tools/perf/util/llvm-utils.c +55 -5
index 6bfcb2dd3161..5887bb8c1243 100644
@@ -246,15 +246,42 @@ static int detect_kbuild_dir(char **kbuild_dir)
 	return -ENOENT;
 }
 
+static const char *kinc_fetch_script =
+"#!/usr/bin/env sh\n"
+"if ! test -d \"$KBUILD_DIR\"\n"
+"then\n"
+"	exit -1\n"
+"fi\n"
+"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n"
+"then\n"
+"	exit -1\n"
+"fi\n"
+"TMPDIR=`mktemp -d`\n"
+"if test -z \"$TMPDIR\"\n"
+"then\n"
+"    exit -1\n"
+"fi\n"
+"cat << EOF > $TMPDIR/Makefile\n"
+"obj-y := dummy.o\n"
+"\\$(obj)/%.o: \\$(src)/%.c\n"
+"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n"
+"EOF\n"
+"touch $TMPDIR/dummy.c\n"
+"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n"
+"RET=$?\n"
+"rm -rf $TMPDIR\n"
+"exit $RET\n";
+
 static inline void
-get_kbuild_opts(char **kbuild_dir)
+get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
 {
 	int err;
 
-	if (!kbuild_dir)
+	if (!kbuild_dir || !kbuild_include_opts)
 		return;
 
 	*kbuild_dir = NULL;
+	*kbuild_include_opts = NULL;
 
 	if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
 		pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
@@ -271,6 +298,27 @@ get_kbuild_opts(char **kbuild_dir)
 "     \tdetection.\n\n");
 		return;
 	}
+
+	pr_debug("Kernel build dir is set to %s\n", *kbuild_dir);
+	force_set_env("KBUILD_DIR", *kbuild_dir);
+	force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts);
+	err = read_from_pipe(kinc_fetch_script,
+			     (void **)kbuild_include_opts,
+			     NULL);
+	if (err) {
+		pr_warning(
+"WARNING:\tunable to get kernel include directories from '%s'\n"
+"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n"
+"     \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n"
+"     \toption in [llvm] to \"\" to suppress this detection.\n\n",
+			*kbuild_dir);
+
+		free(*kbuild_dir);
+		*kbuild_dir = NULL;
+		return;
+	}
+
+	pr_debug("include option is set to %s\n", *kbuild_include_opts);
 }
 
 int llvm__compile_bpf(const char *path, void **p_obj_buf,
@@ -280,7 +328,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 	char clang_path[PATH_MAX];
 	const char *clang_opt = llvm_param.clang_opt;
 	const char *template = llvm_param.clang_bpf_cmd_template;
-	char *kbuild_dir = NULL;
+	char *kbuild_dir = NULL, *kbuild_include_opts = NULL;
 	void *obj_buf = NULL;
 	size_t obj_buf_sz;
 
@@ -302,11 +350,11 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 	 * This is an optional work. Even it fail we can continue our
 	 * work. Needn't to check error return.
 	 */
-	get_kbuild_opts(&kbuild_dir);
+	get_kbuild_opts(&kbuild_dir, &kbuild_include_opts);
 
 	force_set_env("CLANG_EXEC", clang_path);
 	force_set_env("CLANG_OPTIONS", clang_opt);
-	force_set_env("KERNEL_INC_OPTIONS", NULL);
+	force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts);
 	force_set_env("WORKING_DIR", kbuild_dir ? : ".");
 
 	/*
@@ -330,6 +378,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 	}
 
 	free(kbuild_dir);
+	free(kbuild_include_opts);
 	if (!p_obj_buf)
 		free(obj_buf);
 	else
@@ -340,6 +389,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 	return 0;
 errout:
 	free(kbuild_dir);
+	free(kbuild_include_opts);
 	free(obj_buf);
 	if (p_obj_buf)
 		*p_obj_buf = NULL;
Signature · 1 line
2.1.0
author maintainer
#24 lore raw

PATCH 24/24 perf tests: Add LLVM test for eBPF on-the-fly compiling

In reply to Arnaldo Carvalho de Melo

From: Wang Nan <[email protected]>

Previous patches introduce llvm__compile_bpf() to compile source file to eBPF object. This patch adds testcase to test it. It also tests libbpf by opening generated object after applying next patch which introduces HAVE_LIBBPF_SUPPORT option.

Since llvm__compile_bpf() prints long messages which users who don't explicitly test llvm doesn't care, this patch set verbose to -1 to suppress all debug, warning and error message, and hint user use 'perf test -v' to see the full output.

For the same reason, if clang is not found in PATH and there's no [llvm] section in .perfconfig, skip this test.

Cc Brendan Gregg <[email protected]>
Cc Daniel Borkmann <[email protected]>
Cc David Ahern <[email protected]>
Cc He Kuang <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Kaixu Xia <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Zefan Li <[email protected]>
[ Add tools/lib/bpf/ to tools/perf/MANIFEST, so that the tarball targets build ]
---
 tools/perf/MANIFEST             |  1 +
 tools/perf/tests/Build          |  1 +
 tools/perf/tests/builtin-test.c |  4 ++
 tools/perf/tests/llvm.c         | 98 +++++++++++++++++++++++++++++++++++++++++
 tools/perf/tests/tests.h        |  1 +
 tools/perf/util/llvm-utils.c    |  9 ++++
 tools/perf/util/llvm-utils.h    | 10 +++++
 7 files changed, 124 insertions(+)
 create mode 100644 tools/perf/tests/llvm.c
tools/perf/MANIFEST +1 -0
index d01a0aad5a01..f31f15a5f873 100644
@@ -18,6 +18,7 @@ tools/arch/x86/include/asm/atomic.h
 tools/arch/x86/include/asm/rmwcc.h
 tools/lib/traceevent
 tools/lib/api
+tools/lib/bpf
 tools/lib/hweight.c
 tools/lib/rbtree.c
 tools/lib/symbol/kallsyms.c
tools/perf/tests/Build +1 -0
index d20d6e6ab65b..c1518bdd0f1b 100644
@@ -32,6 +32,7 @@ perf-y += sample-parsing.o
 perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
+perf-y += llvm.o
 
 perf-$(CONFIG_X86) += perf-time-to-tsc.o
 
tools/perf/tests/builtin-test.c +4 -0
index c1dde733c3a6..136cd934be66 100644
@@ -175,6 +175,10 @@ static struct test {
 		.func = test__thread_map,
 	},
 	{
+		.desc = "Test LLVM searching and compiling",
+		.func = test__llvm,
+	},
+	{
 		.func = NULL,
 	},
 };
tools/perf/tests/llvm.c new file +98 -0

98 lines not on this page · raw message

tools/perf/tests/tests.h +1 -0
index ebb47d96bc0b..bf113a247987 100644
@@ -62,6 +62,7 @@ int test__fdarray__filter(void);
 int test__fdarray__add(void);
 int test__kmod_path__parse(void);
 int test__thread_map(void);
+int test__llvm(void);
 
 #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
tools/perf/util/llvm-utils.c +9 -0
index 5887bb8c1243..4f6a4780bd5f 100644
@@ -22,6 +22,7 @@ struct llvm_param llvm_param = {
 	.clang_opt = NULL,
 	.kbuild_dir = NULL,
 	.kbuild_opts = NULL,
+	.user_set_param = false,
 };
 
 int perf_llvm_config(const char *var, const char *value)
@@ -42,6 +43,7 @@ int perf_llvm_config(const char *var, const char *value)
 		llvm_param.kbuild_opts = strdup(value);
 	else
 		return -1;
+	llvm_param.user_set_param = true;
 	return 0;
 }
 
@@ -397,3 +399,10 @@ errout:
 		*p_obj_buf_sz = 0;
 	return err;
 }
+
+int llvm__search_clang(void)
+{
+	char clang_path[PATH_MAX];
+
+	return search_program(llvm_param.clang_path, "clang", clang_path);
+}
tools/perf/util/llvm-utils.h +10 -0
index d23adbca120b..5b3cf1c229e2 100644
@@ -29,6 +29,13 @@ struct llvm_param {
 	 * compiling. Should not be used for dynamic compiling.
 	 */
 	const char *kbuild_opts;
+	/*
+	 * Default is false. If one of the above fields is set by user
+	 * explicitly then user_set_llvm is set to true. This is used
+	 * for perf test. If user doesn't set anything in .perfconfig
+	 * and clang is not found, don't trigger llvm test.
+	 */
+	bool user_set_param;
 };
 
 extern struct llvm_param llvm_param;
@@ -36,4 +43,7 @@ extern int perf_llvm_config(const char *var, const char *value);
 
 extern int llvm__compile_bpf(const char *path, void **p_obj_buf,
 			     size_t *p_obj_buf_sz);
+
+/* This function is for test__llvm() use only */
+extern int llvm__search_clang(void);
 #endif
Signature · 1 line
2.1.0
#25 lore raw
In reply to Arnaldo Carvalho de Melo
Quoted 10 lines · Arnaldo Carvalho de Melo

Hi Ingo,

This is up to a test that checks that the infrastructure for getting
a C source file and build it to a form that can then be used to upload it to the kernel is working.

Wang has a tree where we go all the way to passing a .c file as an
--event, but I thought about breaking the patchkit up to what is settled and the "perf test -v LLVM" point looks like a good cutoff point for demoing/testing what is in this patchkit.

Fair enough!

Quoted 132 lines

It shouldn't affect people without the environment needed, as well it
should check the environment for sanity and provide info about how to get it working, be it in the changeset logs, the docs and on tool output messages.o

If you find anything in it that needs fixing, please let us know if you
think we should fix it before merging it to this point or if something can be fixed in follow up patches.

Wang's repo, rebased, almost builds, he should be refreshing this soon,
fixing those trailing whitespace issues, that is stopping the rebase here:

[acme@zoo linux]$ git remote -v | grep wang | head -1
wangnan.github https://github.com/WangNan0/linux.git (fetch)
[acme@zoo linux]$ git checkout -b ebpf-test wangnan.github/ebpf
Branch ebpf-test set up to track remote branch ebpf from wangnan.github. Switched to a new branch 'ebpf-test'
[acme@zoo linux]$ git rebase perf/ebpf
First, rewinding head to replay your work on top of it...
Applying: perf tests: Add LLVM test for eBPF on-the-fly compiling
Using index info to reconstruct a base tree...
M tools/perf/tests/Build
M tools/perf/tests/builtin-test.c
M tools/perf/tests/tests.h
M tools/perf/util/llvm-utils.c
M tools/perf/util/llvm-utils.h
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
Applying: perf tools: Make perf depend on libbpf
Using index info to reconstruct a base tree...
M tools/perf/MANIFEST
Falling back to patching base and 3-way merge...
Auto-merging tools/perf/MANIFEST
Applying: perf ebpf: Add the libbpf glue
Applying: perf tools: Enable passing bpf object file to --event
Applying: perf tools: Parse probe points of eBPF programs during preparation
Applying: perf probe: Attach trace_probe_event with perf_probe_event
Applying: perf record: Probe at kprobe points
Applying: perf record: Load all eBPF object into kernel
Applying: perf tools: Add bpf_fd field to evsel and config it
Applying: perf tools: Attach eBPF program to perf event
Applying: perf tools: Suppress probing messages when probing by BPF loading
Applying: perf record: Add clang options for compiling BPF scripts
Applying: perf tools: Infrastructure for compiling scriptlets when passing '.c' to --event
Applying: perf tests: Enforce LLVM test for BPF test
Applying: perf test: Enable 'perf test' run as test targets tools/perf/tests/builtin-test.c:279: trailing whitespace.
+ /*
[acme@zoo linux]$

After that whitespace thing is fixed, you'll get to a point where 'perf
test -v BPF' will show you all the steps involved in, from a .c file do all the build process and loading, attaching to a kprobe, etc that we discussed, but those extra patches need more working before I can submit to you.

Otherwise, please consider pulling,

- Arnaldo

The following changes since commit 1354ac6ad84395660f551d0614a6ca39e5bfe8e3:

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core (2015-08-07 09:11:30 +0200)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-ebpf-for-mingo

for you to fetch changes up to 9bc898c7019383b6aa2ae6cb2928c4ca926449f0:

perf tests: Add LLVM test for eBPF on-the-fly compiling (2015-08-07 10:57:24 -0300)

----------------------------------------------------------------
perf/ebpf library + llvm/clang infrastructure

Infrastructure:

- library for interfacing with the kernel eBPF infrastructure, with tools/perf/ targeted as a first user (Wang Nan)

- llvm/clang infrastructure for building BPF object files from C source code (Wang Nan)

----------------------------------------------------------------
Wang Nan (24):
bpf tools: Introduce 'bpf' library and add bpf feature check bpf tools: Allow caller to set printing function bpf tools: Open eBPF object file and do basic validation bpf tools: Read eBPF object from buffer
bpf tools: Check endianness and make libbpf fail early bpf tools: Iterate over ELF sections to collect information bpf tools: Collect version and license from ELF sections bpf tools: Collect map definitions from 'maps' section bpf tools: Collect symbol table from SHT_SYMTAB section bpf tools: Collect eBPF programs from their own sections bpf tools: Collect relocation sections from SHT_REL sections bpf tools: Record map accessing instructions for each program bpf tools: Add bpf.c/h for common bpf operations bpf tools: Create eBPF maps defined in an object file bpf tools: Relocate eBPF programs
bpf tools: Introduce bpf_load_program() to bpf.c bpf tools: Load eBPF programs in object files into kernel bpf tools: Introduce accessors for struct bpf_program bpf tools: Link all bpf objects onto a list
perf tools: Introduce llvm config options
perf tools: Call clang to compile C source to object code perf tools: Auto detecting kernel build directory perf tools: Auto detecting kernel include options perf tests: Add LLVM test for eBPF on-the-fly compiling

 tools/build/feature/Makefile    |    6 +-
 tools/build/feature/test-bpf.c  |   18 +
 tools/lib/bpf/.gitignore        |    2 +
 tools/lib/bpf/Build             |    1 +
 tools/lib/bpf/Makefile          |  195 ++++++++
 tools/lib/bpf/bpf.c             |   85 ++++
 tools/lib/bpf/bpf.h             |   23 +
 tools/lib/bpf/libbpf.c          | 1037 +++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h          |   81 +++
 tools/perf/MANIFEST             |    1 +
 tools/perf/tests/Build          |    1 +
 tools/perf/tests/builtin-test.c |    4 +
 tools/perf/tests/llvm.c         |   98 ++++
 tools/perf/tests/tests.h        |    1 +
 tools/perf/util/Build           |    1 +
 tools/perf/util/config.c        |    4 +
 tools/perf/util/llvm-utils.c    |  408 +++++++++++++++
 tools/perf/util/llvm-utils.h    |   49 ++
 18 files changed, 2014 insertions(+), 1 deletion(-)
 create mode 100644 tools/build/feature/test-bpf.c
 create mode 100644 tools/lib/bpf/.gitignore
 create mode 100644 tools/lib/bpf/Build
Quoted 8 lines
 create mode 100644 tools/lib/bpf/Makefile
 create mode 100644 tools/lib/bpf/bpf.c
 create mode 100644 tools/lib/bpf/bpf.h
 create mode 100644 tools/lib/bpf/libbpf.c
 create mode 100644 tools/lib/bpf/libbpf.h
 create mode 100644 tools/perf/tests/llvm.c
 create mode 100644 tools/perf/util/llvm-utils.c
 create mode 100644 tools/perf/util/llvm-utils.h

Pulled, thanks a lot Arnaldo!

Ingo