diff mbox series

[v2,bpf-next,08/11] bpftool: add `gen object` command to perform BPF static linking

Message ID 20210313193537.1548766-9-andrii@kernel.org (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series BPF static linking | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for bpf-next
netdev/subject_prefix success Link
netdev/cc_maintainers warning 13 maintainers not CCed: yhs@fb.com jagdsh.linux@gmail.com kpsingh@kernel.org kafai@fb.com clang-built-linux@googlegroups.com ast@kernel.org nathan@kernel.org john.fastabend@gmail.com tklauser@distanz.ch yuehaibing@huawei.com songliubraving@fb.com ndesaulniers@google.com quentin@isovalent.com
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch warning WARNING: line length of 82 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link

Commit Message

Andrii Nakryiko March 13, 2021, 7:35 p.m. UTC
Add `bpftool gen object <output-file> <input_file>...` command to statically
link multiple BPF ELF object files into a single output BPF ELF object file.

Similarly to existing '*.o' convention, bpftool is establishing a '*.bpfo'
convention for statically-linked BPF object files. Both .o and .bpfo suffixes
will be stripped out during BPF skeleton generation to infer BPF object name.

This patch also updates bash completions and man page. Man page gets a short
section on `gen object` command, but also updates the skeleton example to show
off workflow for BPF application with two .bpf.c files, compiled individually
with Clang, then resulting object files are linked together with `gen object`,
and then final object file is used to generate usable BPF skeleton. This
should help new users understand realistic workflow w.r.t. compiling
mutli-file BPF application.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 .../bpf/bpftool/Documentation/bpftool-gen.rst | 65 +++++++++++++++----
 tools/bpf/bpftool/bash-completion/bpftool     |  2 +-
 tools/bpf/bpftool/gen.c                       | 49 +++++++++++++-
 3 files changed, 99 insertions(+), 17 deletions(-)

Comments

Quentin Monnet March 15, 2021, 9:24 a.m. UTC | #1
2021-03-13 11:35 UTC-0800 ~ Andrii Nakryiko <andrii@kernel.org>
> Add `bpftool gen object <output-file> <input_file>...` command to statically
> link multiple BPF ELF object files into a single output BPF ELF object file.
> 
> Similarly to existing '*.o' convention, bpftool is establishing a '*.bpfo'
> convention for statically-linked BPF object files. Both .o and .bpfo suffixes
> will be stripped out during BPF skeleton generation to infer BPF object name.
> 
> This patch also updates bash completions and man page. Man page gets a short
> section on `gen object` command, but also updates the skeleton example to show
> off workflow for BPF application with two .bpf.c files, compiled individually
> with Clang, then resulting object files are linked together with `gen object`,
> and then final object file is used to generate usable BPF skeleton. This
> should help new users understand realistic workflow w.r.t. compiling
> mutli-file BPF application.
> 
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> ---
>  .../bpf/bpftool/Documentation/bpftool-gen.rst | 65 +++++++++++++++----
>  tools/bpf/bpftool/bash-completion/bpftool     |  2 +-
>  tools/bpf/bpftool/gen.c                       | 49 +++++++++++++-
>  3 files changed, 99 insertions(+), 17 deletions(-)
> 
> diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> index 84cf0639696f..4cdce187c393 100644
> --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> @@ -14,16 +14,37 @@ SYNOPSIS
>  
>  	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
>  
> -	*COMMAND* := { **skeleton** | **help** }
> +	*COMMAND* := { **object** | **skeleton** | **help** }
>  
>  GEN COMMANDS
>  =============
>  
> +|	**bpftool** **gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
>  |	**bpftool** **gen skeleton** *FILE*
>  |	**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 involed

Typo: "involed"

> +		  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*.
>  

> diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
> index fdffbc64c65c..7ca23c58f2c0 100644
> --- a/tools/bpf/bpftool/bash-completion/bpftool
> +++ b/tools/bpf/bpftool/bash-completion/bpftool
> @@ -981,7 +981,7 @@ _bpftool()
>              ;;
>          gen)
>              case $command in
> -                skeleton)
> +                object|skeleton)
>                      _filedir
>                      ;;
>                  *)

Suggesting the "object" keyword for completing "bpftool gen [tab]"
is missing. It is just a few lines below:

------
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index fdffbc64c65c..223438e86932 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -981,12 +981,12 @@ _bpftool()
             ;;
         gen)
             case $command in
-                skeleton)
+                object|skeleton)
                     _filedir
                     ;;
                 *)
                     [[ $prev == $object ]] && \
-                        COMPREPLY=( $( compgen -W 'skeleton help' -- "$cur" ) )
+                        COMPREPLY=( $( compgen -W 'object skeleton help' -- "$cur" ) )
                     ;;
             esac
             ;;
------

Looks good otherwise. Thanks for the documentation, it's great to
have the example in the man page.

Pending the two nits above are fixed:
Reviewed-by: Quentin Monnet <quentin@isovalent.com>

Quentin
Andrii Nakryiko March 16, 2021, 5:16 a.m. UTC | #2
On Mon, Mar 15, 2021 at 2:24 AM Quentin Monnet <quentin@isovalent.com> wrote:
>
> 2021-03-13 11:35 UTC-0800 ~ Andrii Nakryiko <andrii@kernel.org>
> > Add `bpftool gen object <output-file> <input_file>...` command to statically
> > link multiple BPF ELF object files into a single output BPF ELF object file.
> >
> > Similarly to existing '*.o' convention, bpftool is establishing a '*.bpfo'
> > convention for statically-linked BPF object files. Both .o and .bpfo suffixes
> > will be stripped out during BPF skeleton generation to infer BPF object name.
> >
> > This patch also updates bash completions and man page. Man page gets a short
> > section on `gen object` command, but also updates the skeleton example to show
> > off workflow for BPF application with two .bpf.c files, compiled individually
> > with Clang, then resulting object files are linked together with `gen object`,
> > and then final object file is used to generate usable BPF skeleton. This
> > should help new users understand realistic workflow w.r.t. compiling
> > mutli-file BPF application.
> >
> > Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> > ---
> >  .../bpf/bpftool/Documentation/bpftool-gen.rst | 65 +++++++++++++++----
> >  tools/bpf/bpftool/bash-completion/bpftool     |  2 +-
> >  tools/bpf/bpftool/gen.c                       | 49 +++++++++++++-
> >  3 files changed, 99 insertions(+), 17 deletions(-)
> >
> > diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> > index 84cf0639696f..4cdce187c393 100644
> > --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> > +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> > @@ -14,16 +14,37 @@ SYNOPSIS
> >
> >       *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
> >
> > -     *COMMAND* := { **skeleton** | **help** }
> > +     *COMMAND* := { **object** | **skeleton** | **help** }
> >
> >  GEN COMMANDS
> >  =============
> >
> > +|    **bpftool** **gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
> >  |    **bpftool** **gen skeleton** *FILE*
> >  |    **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 involed
>
> Typo: "involed"
>
> > +               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*.
> >
>
> > diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
> > index fdffbc64c65c..7ca23c58f2c0 100644
> > --- a/tools/bpf/bpftool/bash-completion/bpftool
> > +++ b/tools/bpf/bpftool/bash-completion/bpftool
> > @@ -981,7 +981,7 @@ _bpftool()
> >              ;;
> >          gen)
> >              case $command in
> > -                skeleton)
> > +                object|skeleton)
> >                      _filedir
> >                      ;;
> >                  *)
>
> Suggesting the "object" keyword for completing "bpftool gen [tab]"
> is missing. It is just a few lines below:
>
> ------
> diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
> index fdffbc64c65c..223438e86932 100644
> --- a/tools/bpf/bpftool/bash-completion/bpftool
> +++ b/tools/bpf/bpftool/bash-completion/bpftool
> @@ -981,12 +981,12 @@ _bpftool()
>              ;;
>          gen)
>              case $command in
> -                skeleton)
> +                object|skeleton)
>                      _filedir
>                      ;;
>                  *)
>                      [[ $prev == $object ]] && \
> -                        COMPREPLY=( $( compgen -W 'skeleton help' -- "$cur" ) )
> +                        COMPREPLY=( $( compgen -W 'object skeleton help' -- "$cur" ) )
>                      ;;
>              esac
>              ;;
> ------
>
> Looks good otherwise. Thanks for the documentation, it's great to
> have the example in the man page.
>
> Pending the two nits above are fixed:
> Reviewed-by: Quentin Monnet <quentin@isovalent.com>

Thanks! I'll wait a bit for some more feedback on other patches and
will fix it in the next version (unless my other patches are perfect,
of course ;).

>
> Quentin
diff mbox series

Patch

diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
index 84cf0639696f..4cdce187c393 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
@@ -14,16 +14,37 @@  SYNOPSIS
 
 	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
 
-	*COMMAND* := { **skeleton** | **help** }
+	*COMMAND* := { **object** | **skeleton** | **help** }
 
 GEN COMMANDS
 =============
 
+|	**bpftool** **gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
 |	**bpftool** **gen skeleton** *FILE*
 |	**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 involed
+		  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*.
 
@@ -130,26 +151,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 +175,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 +199,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.bpfo 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.bpfo*.
+
+**$ bpftool gen skeleton example.bpfo**
 
 ::
 
@@ -227,7 +264,7 @@  and global variables.
 
   #endif /* __EXAMPLE_SKEL_H__ */
 
-**$ cat example_user.c**
+**$ cat example.c**
 
 ::
 
@@ -270,7 +307,7 @@  and global variables.
   	return err;
   }
 
-**# ./example_user**
+**# ./example**
 
 ::
 
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index fdffbc64c65c..7ca23c58f2c0 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -981,7 +981,7 @@  _bpftool()
             ;;
         gen)
             case $command in
-                skeleton)
+                object|skeleton)
                     _filedir
                     ;;
                 *)
diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index 4033c46d83e7..123cca7b400b 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -54,7 +54,9 @@  static void get_obj_name(char *name, const char *file)
 	strncpy(name, basename(file), MAX_OBJ_NAME_LEN - 1);
 	name[MAX_OBJ_NAME_LEN - 1] = '\0';
 	if (str_has_suffix(name, ".o"))
-		name[strlen(name) - 2] = '\0';
+		name[strlen(name) - sizeof(".o") + 1] = '\0';
+	else if (str_has_suffix(name, ".bpfo"))
+		name[strlen(name) - sizeof(".bpfo") + 1] = '\0';
 	sanitize_identifier(name);
 }
 
@@ -591,6 +593,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 +642,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\n"
 		"       %1$s %2$s help\n"
 		"\n"
 		"       " HELP_SPEC_OPTIONS "\n"
@@ -610,6 +654,7 @@  static int do_help(int argc, char **argv)
 }
 
 static const struct cmd cmds[] = {
+	{ "object",	do_object },
 	{ "skeleton",	do_skeleton },
 	{ "help",	do_help },
 	{ 0 }