Commit 6d8b2716 authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'BPF static linking'



Andrii Nakryiko says:

====================

This patch set adds new libbpf APIs and their bpftool integration that allows
to perform static linking of BPF object files. Currently no extern resolution
across object files is performed. This is going to be the focus of the follow
up patches. But, given amount of code and logic necessary to perform just
basic functionality of linking together mostly independent BPF object files,
it was decided to land basic BPF linker code and logic first and extend it
afterwards.

The motivation for BPF static linking is to provide the functionality that is
naturally assumed for user-space development process: ability to structure
application's code without artificial restrictions of having all the code and
data (variables and maps) inside a single source code file.

This enables better engineering practices of splitting code into
well-encapsulated parts. It provides ability to hide internal state from other
parts of the code base through static variables and maps. It is also a first
steps towards having generic reusable BPF libraries.

Please see individual patches (mostly #6 and #7) for more details. Patch #10
passes all test_progs' individual BPF .o files through BPF static linker,
which is supposed to be a no-op operation, so is essentially validating that
BPF static linker doesn't produce corrupted ELF object files. Patch #11 adds
Makefile infra to be able to specify multi-file BPF object files and adds the
first multi-file test to validate correctness.

v3->v4:
  - fix Makefile copy/paste error of diff'ing invalid object files (Alexei);
  - fix uninitialized obj_name variable that could lead to bogus object names
    being used during skeleton generation (kernel-patches CI);
v2->v3:
  - added F(F(F(X))) = F(F(X)) test for all linked BPF object files (Alexei);
  - used reallocarray() more consistently in few places (Alexei);
  - improved bash completions for `gen object` (Quentin);
  - dropped .bpfo extension, but had to add optional `name OBJECT_FILE`
    parameter (path #8) to `gen skeleton` command to specify desired object
    name during skeleton generation;
  - fixed bug of merging DATASECS of special "license" and "version" sections.
    Linker currently strictly validates that all versions and licenses matches
    exactly and keeps only ELF symbols and BTF DATASEC from the very first
    object file with license/version. For all other object files, we ignore
    ELF symbols, but weren't ignoring DATASECs, which caused further problems
    of not being able to find a corresponding ELF symbol, if variable name
    differs between two files (which we test deliberately in multi-file
    linking selftest). The fix is to ignore BTF DATASECS;
v1->v2:
  - extracted `struct strset` to manage unique set of strings both for BTF and
    ELF SYMTAB (patch #4, refactors btf and btf_dedup logic as well) (Alexei);
  - fixed bugs in bpftool gen command; renamed it to `gen object`, added BASH
    completions and extended/updated man page (Quentin).
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents fdc13979 a0964f52
Loading
Loading
Loading
Loading
+59 −19
Original line number Diff line number Diff line
@@ -14,16 +14,37 @@ SYNOPSIS

	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }

	*COMMAND* := { **skeleton** | **help** }
	*COMMAND* := { **object** | **skeleton** | **help** }

GEN COMMANDS
=============

|	**bpftool** **gen skeleton** *FILE*
|	**bpftool** **gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
|	**bpftool** **gen skeleton** *FILE* [**name** *OBJECT_NAME*]
|	**bpftool** **gen help**

DESCRIPTION
===========
	**bpftool gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
		  Statically link (combine) together one or more *INPUT_FILE*'s
		  into a single resulting *OUTPUT_FILE*. All the files involved
		  are BPF ELF object files.

		  The rules of BPF static linking are mostly the same as for
		  user-space object files, but in addition to combining data
		  and instruction sections, .BTF and .BTF.ext (if present in
		  any of the input files) data are combined together. .BTF
		  data is deduplicated, so all the common types across
		  *INPUT_FILE*'s will only be represented once in the resulting
		  BTF information.

		  BPF static linking allows to partition BPF source code into
		  individually compiled files that are then linked into
		  a single resulting BPF object file, which can be used to
		  generated BPF skeleton (with **gen skeleton** command) or
		  passed directly into **libbpf** (using **bpf_object__open()**
		  family of APIs).

	**bpftool gen skeleton** *FILE*
		  Generate BPF skeleton C header file for a given *FILE*.

@@ -75,10 +96,13 @@ DESCRIPTION
		  specific maps, programs, etc.

		  As part of skeleton, few custom functions are generated.
		  Each of them is prefixed with object name, derived from
		  object file name. I.e., if BPF object file name is
		  **example.o**, BPF object name will be **example**. The
		  following custom functions are provided in such case:
		  Each of them is prefixed with object name. Object name can
		  either be derived from object file name, i.e., if BPF object
		  file name is **example.o**, BPF object name will be
		  **example**. Object name can be also specified explicitly
		  through **name** *OBJECT_NAME* parameter. The following
		  custom functions are provided (assuming **example** as
		  the object name):

		  - **example__open** and **example__open_opts**.
		    These functions are used to instantiate skeleton. It
@@ -130,26 +154,19 @@ OPTIONS

EXAMPLES
========
**$ cat example.c**
**$ cat example1.bpf.c**

::

  #include <stdbool.h>
  #include <linux/ptrace.h>
  #include <linux/bpf.h>
  #include "bpf_helpers.h"
  #include <bpf/bpf_helpers.h>

  const volatile int param1 = 42;
  bool global_flag = true;
  struct { int x; } data = {};

  struct {
  	__uint(type, BPF_MAP_TYPE_HASH);
  	__uint(max_entries, 128);
  	__type(key, int);
  	__type(value, long);
  } my_map SEC(".maps");

  SEC("raw_tp/sys_enter")
  int handle_sys_enter(struct pt_regs *ctx)
  {
@@ -161,6 +178,21 @@ EXAMPLES
  	return 0;
  }

**$ cat example2.bpf.c**

::

  #include <linux/ptrace.h>
  #include <linux/bpf.h>
  #include <bpf/bpf_helpers.h>

  struct {
  	__uint(type, BPF_MAP_TYPE_HASH);
  	__uint(max_entries, 128);
  	__type(key, int);
  	__type(value, long);
  } my_map SEC(".maps");

  SEC("raw_tp/sys_exit")
  int handle_sys_exit(struct pt_regs *ctx)
  {
@@ -170,9 +202,17 @@ EXAMPLES
  }

This is example BPF application with two BPF programs and a mix of BPF maps
and global variables.
and global variables. Source code is split across two source code files.

**$ bpftool gen skeleton example.o**
**$ clang -target bpf -g example1.bpf.c -o example1.bpf.o**
**$ clang -target bpf -g example2.bpf.c -o example2.bpf.o**
**$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o**

This set of commands compiles *example1.bpf.c* and *example2.bpf.c*
individually and then statically links respective object files into the final
BPF ELF object file *example.bpf.o*.

**$ bpftool gen skeleton example.bpf.o name example | tee example.skel.h**

::

@@ -227,7 +267,7 @@ and global variables.

  #endif /* __EXAMPLE_SKEL_H__ */

**$ cat example_user.c**
**$ cat example.c**

::

@@ -270,7 +310,7 @@ and global variables.
  	return err;
  }

**# ./example_user**
**# ./example**

::

+15 −2
Original line number Diff line number Diff line
@@ -981,12 +981,25 @@ _bpftool()
            ;;
        gen)
            case $command in
                object)
                    _filedir
                    return 0
                    ;;
                skeleton)
                    case $prev in
                        $command)
                            _filedir
                            return 0
                            ;;
                        *)
                            _bpftool_once_attr 'name'
                            return 0
                            ;;
                    esac
                    ;;
                *)
                    [[ $prev == $object ]] && \
                        COMPREPLY=( $( compgen -W 'skeleton help' -- "$cur" ) )
                        COMPREPLY=( $( compgen -W 'object skeleton help' -- "$cur" ) )
                    ;;
            esac
            ;;
+69 −3
Original line number Diff line number Diff line
@@ -273,7 +273,7 @@ static int do_skeleton(int argc, char **argv)
	char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];
	size_t i, map_cnt = 0, prog_cnt = 0, file_sz, mmap_sz;
	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
	char obj_name[MAX_OBJ_NAME_LEN], *obj_data;
	char obj_name[MAX_OBJ_NAME_LEN] = "", *obj_data;
	struct bpf_object *obj = NULL;
	const char *file, *ident;
	struct bpf_program *prog;
@@ -288,6 +288,28 @@ static int do_skeleton(int argc, char **argv)
	}
	file = GET_ARG();

	while (argc) {
		if (!REQ_ARGS(2))
			return -1;

		if (is_prefix(*argv, "name")) {
			NEXT_ARG();

			if (obj_name[0] != '\0') {
				p_err("object name already specified");
				return -1;
			}

			strncpy(obj_name, *argv, MAX_OBJ_NAME_LEN - 1);
			obj_name[MAX_OBJ_NAME_LEN - 1] = '\0';
		} else {
			p_err("unknown arg %s", *argv);
			return -1;
		}

		NEXT_ARG();
	}

	if (argc) {
		p_err("extra unknown arguments");
		return -1;
@@ -310,6 +332,7 @@ static int do_skeleton(int argc, char **argv)
		p_err("failed to mmap() %s: %s", file, strerror(errno));
		goto out;
	}
	if (obj_name[0] == '\0')
		get_obj_name(obj_name, file);
	opts.object_name = obj_name;
	obj = bpf_object__open_mem(obj_data, file_sz, &opts);
@@ -591,6 +614,47 @@ static int do_skeleton(int argc, char **argv)
	return err;
}

static int do_object(int argc, char **argv)
{
	struct bpf_linker *linker;
	const char *output_file, *file;
	int err = 0;

	if (!REQ_ARGS(2)) {
		usage();
		return -1;
	}

	output_file = GET_ARG();

	linker = bpf_linker__new(output_file, NULL);
	if (!linker) {
		p_err("failed to create BPF linker instance");
		return -1;
	}

	while (argc) {
		file = GET_ARG();

		err = bpf_linker__add_file(linker, file);
		if (err) {
			p_err("failed to link '%s': %s (%d)", file, strerror(err), err);
			goto out;
		}
	}

	err = bpf_linker__finalize(linker);
	if (err) {
		p_err("failed to finalize ELF file: %s (%d)", strerror(err), err);
		goto out;
	}

	err = 0;
out:
	bpf_linker__free(linker);
	return err;
}

static int do_help(int argc, char **argv)
{
	if (json_output) {
@@ -599,7 +663,8 @@ static int do_help(int argc, char **argv)
	}

	fprintf(stderr,
		"Usage: %1$s %2$s skeleton FILE\n"
		"Usage: %1$s %2$s object OUTPUT_FILE INPUT_FILE [INPUT_FILE...]\n"
		"       %1$s %2$s skeleton FILE [name OBJECT_NAME]\n"
		"       %1$s %2$s help\n"
		"\n"
		"       " HELP_SPEC_OPTIONS "\n"
@@ -610,6 +675,7 @@ static int do_help(int argc, char **argv)
}

static const struct cmd cmds[] = {
	{ "object",	do_object },
	{ "skeleton",	do_skeleton },
	{ "help",	do_help },
	{ 0 }
+1 −1
Original line number Diff line number Diff line
libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \
	    netlink.o bpf_prog_linfo.o libbpf_probes.o xsk.o hashmap.o \
	    btf_dump.o ringbuf.o
	    btf_dump.o ringbuf.o strset.o linker.o
+339 −375

File changed.

Preview size limit exceeded, changes collapsed.

Loading