netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls
@ 2019-12-19 14:29 Toke Høiland-Jørgensen
  2019-12-19 14:29 ` [PATCH RFC bpf-next 1/3] libbpf: Add new bpf_object__load2() using new-style opts Toke Høiland-Jørgensen
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Toke Høiland-Jørgensen @ 2019-12-19 14:29 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Alexei Starovoitov, Martin KaFai Lau, Song Liu, Yonghong Song,
	Jesper Dangaard Brouer, Andrii Nakryiko, David Miller, netdev,
	bpf

This series adds support for resolving function calls to functions marked as
'extern' in eBPF source files, by resolving the function call targets at load
time. For now, this only works by static linking (i.e., copying over the
instructions from the function target. Once the kernel support for dynamic
linking lands, support can be added for having a function target be an already
loaded program fd instead of a bpf object.

The API I'm proposing for this is that the caller specifies an explicit mapping
between extern function names and function names in the target object file.
This is to support the XDP multi-prog case, where the dispatcher program may not
necessarily have control over function names in the target programs, so simple
function name resolution can't be used.

I'm sending this series as an RFC because it's still a bit rough around the
edges: There are several places where I'm handling things in a way I'm pretty
sure is not the right way. And while this works for the simple programs added to
the selftest in patch 3, it fails with more complicated target programs.

My problem is that I don't really know what the right thing to do is for these
things, so I've marked them with FIXME comments in the code, in the hope that
someone more knowledgeable can suggest fixes.

Other regular RFC comments are welcome as well, of course; the API in particular
could use a second set of eyes or two :)

---

Toke Høiland-Jørgensen (3):
      libbpf: Add new bpf_object__load2() using new-style opts
      libbpf: Handle function externs and support static linking
      selftests/bpf: Add selftest for XDP multiprogs


 tools/lib/bpf/btf.c                                |   10 +
 tools/lib/bpf/libbpf.c                             |  299 ++++++++++++++++----
 tools/lib/bpf/libbpf.h                             |   28 ++
 tools/lib/bpf/libbpf.map                           |    1 
 .../selftests/bpf/prog_tests/xdp_multiprog.c       |   52 +++
 tools/testing/selftests/bpf/progs/xdp_drop.c       |   13 +
 tools/testing/selftests/bpf/progs/xdp_multiprog.c  |   26 ++
 7 files changed, 366 insertions(+), 63 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_multiprog.c
 create mode 100644 tools/testing/selftests/bpf/progs/xdp_drop.c
 create mode 100644 tools/testing/selftests/bpf/progs/xdp_multiprog.c


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH RFC bpf-next 1/3] libbpf: Add new bpf_object__load2() using new-style opts
  2019-12-19 14:29 [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Toke Høiland-Jørgensen
@ 2019-12-19 14:29 ` Toke Høiland-Jørgensen
  2019-12-19 23:50   ` Andrii Nakryiko
  2019-12-19 14:29 ` [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking Toke Høiland-Jørgensen
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 13+ messages in thread
From: Toke Høiland-Jørgensen @ 2019-12-19 14:29 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Alexei Starovoitov, Martin KaFai Lau, Song Liu, Yonghong Song,
	Jesper Dangaard Brouer, Andrii Nakryiko, David Miller, netdev,
	bpf

From: Toke Høiland-Jørgensen <toke@redhat.com>

Since we introduced DECLARE_LIBBPF_OPTS and related macros for declaring
function options, that is now the preferred way to extend APIs. Introduce a
variant of the bpf_object__load() function that uses this function, and
deprecate the _xattr variant. Since all the good function names were taken,
the new function is unimaginatively called bpf_object__load2().

Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
---
 tools/lib/bpf/libbpf.c   |   31 ++++++++++++++++++-------------
 tools/lib/bpf/libbpf.h   |   13 +++++++++++++
 tools/lib/bpf/libbpf.map |    1 +
 3 files changed, 32 insertions(+), 13 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index febbaac3daf4..266b725e444b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -4844,15 +4844,12 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
 	return 0;
 }
 
-int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
+int bpf_object__load2(struct bpf_object *obj,
+		      const struct bpf_object_load_opts *opts)
 {
-	struct bpf_object *obj;
 	int err, i;
 
-	if (!attr)
-		return -EINVAL;
-	obj = attr->obj;
-	if (!obj)
+	if (!obj || !OPTS_VALID(opts, bpf_object_load_opts))
 		return -EINVAL;
 
 	if (obj->loaded) {
@@ -4867,8 +4864,10 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 	err = err ? : bpf_object__sanitize_and_load_btf(obj);
 	err = err ? : bpf_object__sanitize_maps(obj);
 	err = err ? : bpf_object__create_maps(obj);
-	err = err ? : bpf_object__relocate(obj, attr->target_btf_path);
-	err = err ? : bpf_object__load_progs(obj, attr->log_level);
+	err = err ? : bpf_object__relocate(obj,
+					   OPTS_GET(opts, target_btf_path, NULL));
+	err = err ? : bpf_object__load_progs(obj,
+					     OPTS_GET(opts, log_level, 0));
 	if (err)
 		goto out;
 
@@ -4884,13 +4883,19 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 	return err;
 }
 
-int bpf_object__load(struct bpf_object *obj)
+int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 {
-	struct bpf_object_load_attr attr = {
-		.obj = obj,
-	};
+	DECLARE_LIBBPF_OPTS(bpf_object_load_opts, opts,
+	    .log_level = attr->log_level,
+	    .target_btf_path = attr->target_btf_path,
+	);
+
+	return bpf_object__load2(attr->obj, &opts);
+}
 
-	return bpf_object__load_xattr(&attr);
+int bpf_object__load(struct bpf_object *obj)
+{
+	return bpf_object__load2(obj, NULL);
 }
 
 static int make_parent_dir(const char *path)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index fe592ef48f1b..ce86277d7445 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -132,8 +132,21 @@ struct bpf_object_load_attr {
 	const char *target_btf_path;
 };
 
+struct bpf_object_load_opts {
+	/* size of this struct, for forward/backward compatiblity */
+	size_t sz;
+	/* log level on load */
+	int log_level;
+	/* BTF path (for CO-RE relocations) */
+	const char *target_btf_path;
+};
+#define bpf_object_load_opts__last_field target_btf_path
+
 /* Load/unload object into/from kernel */
 LIBBPF_API int bpf_object__load(struct bpf_object *obj);
+LIBBPF_API int bpf_object__load2(struct bpf_object *obj,
+				 const struct bpf_object_load_opts *opts);
+/* deprecated, use bpf_object__load2() instead */
 LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr);
 LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
 
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index e3a471f38a71..d6cb860763d1 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -217,6 +217,7 @@ LIBBPF_0.0.7 {
 		bpf_object__attach_skeleton;
 		bpf_object__destroy_skeleton;
 		bpf_object__detach_skeleton;
+		bpf_object__load2;
 		bpf_object__load_skeleton;
 		bpf_object__open_skeleton;
 		bpf_program__attach;


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking
  2019-12-19 14:29 [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Toke Høiland-Jørgensen
  2019-12-19 14:29 ` [PATCH RFC bpf-next 1/3] libbpf: Add new bpf_object__load2() using new-style opts Toke Høiland-Jørgensen
@ 2019-12-19 14:29 ` Toke Høiland-Jørgensen
  2019-12-19 16:24   ` Yonghong Song
  2019-12-20  0:02   ` Andrii Nakryiko
  2019-12-19 14:29 ` [PATCH RFC bpf-next 3/3] selftests/bpf: Add selftest for XDP multiprogs Toke Høiland-Jørgensen
  2019-12-20 20:30 ` [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Alexei Starovoitov
  3 siblings, 2 replies; 13+ messages in thread
From: Toke Høiland-Jørgensen @ 2019-12-19 14:29 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Alexei Starovoitov, Martin KaFai Lau, Song Liu, Yonghong Song,
	Jesper Dangaard Brouer, Andrii Nakryiko, David Miller, netdev,
	bpf

From: Toke Høiland-Jørgensen <toke@redhat.com>

This adds support for resolving function externs to libbpf, with a new API
to resolve external function calls by static linking at load-time. The API
for this requires the caller to supply the object files containing the
target functions, and to specify an explicit mapping between extern
function names in the calling program, and function names in the target
object file. This is to support the XDP multi-prog case, where the
dispatcher program may not necessarily have control over function names in
the target programs, so simple function name resolution can't be used.

The target object files must be loaded into the kernel before the calling
program, to ensure all relocations are done on the target functions, so we
can just copy over the instructions.

Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
---
 tools/lib/bpf/btf.c    |   10 +-
 tools/lib/bpf/libbpf.c |  268 +++++++++++++++++++++++++++++++++++++++---------
 tools/lib/bpf/libbpf.h |   17 +++
 3 files changed, 244 insertions(+), 51 deletions(-)

diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 5f04f56e1eb6..2740d4a6b2eb 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -246,6 +246,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
 			size = t->size;
 			goto done;
 		case BTF_KIND_PTR:
+		case BTF_KIND_FUNC_PROTO:
 			size = sizeof(void *);
 			goto done;
 		case BTF_KIND_TYPEDEF:
@@ -288,6 +289,7 @@ int btf__align_of(const struct btf *btf, __u32 id)
 	case BTF_KIND_ENUM:
 		return min(sizeof(void *), t->size);
 	case BTF_KIND_PTR:
+	case BTF_KIND_FUNC_PROTO:
 		return sizeof(void *);
 	case BTF_KIND_TYPEDEF:
 	case BTF_KIND_VOLATILE:
@@ -640,12 +642,16 @@ int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
 		 */
 		if (btf_is_datasec(t)) {
 			err = btf_fixup_datasec(obj, btf, t);
-			if (err)
+			/* FIXME: With function externs we can get a BTF DATASEC
+			 * entry for .extern, but the section doesn't exist; so
+			 * make ENOENT non-fatal
+			 */
+			if (err && err != -ENOENT)
 				break;
 		}
 	}
 
-	return err;
+	return err == -ENOENT ? err : 0;
 }
 
 int btf__load(struct btf *btf)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 266b725e444b..b2c0a2f927e7 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -172,13 +172,17 @@ enum reloc_type {
 	RELO_CALL,
 	RELO_DATA,
 	RELO_EXTERN,
+	RELO_EXTERN_CALL,
 };
 
+struct extern_desc;
+
 struct reloc_desc {
 	enum reloc_type type;
 	int insn_idx;
 	int map_idx;
 	int sym_off;
+	struct extern_desc *ext;
 };
 
 /*
@@ -274,6 +278,7 @@ enum extern_type {
 	EXT_INT,
 	EXT_TRISTATE,
 	EXT_CHAR_ARR,
+	EXT_FUNC
 };
 
 struct extern_desc {
@@ -287,6 +292,7 @@ struct extern_desc {
 	bool is_signed;
 	bool is_weak;
 	bool is_set;
+	struct bpf_program *tgt_prog;
 };
 
 static LIST_HEAD(bpf_objects_list);
@@ -305,6 +311,7 @@ struct bpf_object {
 	char *kconfig;
 	struct extern_desc *externs;
 	int nr_extern;
+	int nr_data_extern;
 	int kconfig_map_idx;
 
 	bool loaded;
@@ -1041,6 +1048,7 @@ static int set_ext_value_tri(struct extern_desc *ext, void *ext_val,
 	case EXT_UNKNOWN:
 	case EXT_INT:
 	case EXT_CHAR_ARR:
+	case EXT_FUNC:
 	default:
 		pr_warn("extern %s=%c should be bool, tristate, or char\n",
 			ext->name, value);
@@ -1281,7 +1289,7 @@ static int bpf_object__init_kconfig_map(struct bpf_object *obj)
 	size_t map_sz;
 	int err;
 
-	if (obj->nr_extern == 0)
+	if (obj->nr_data_extern == 0)
 		return 0;
 
 	last_ext = &obj->externs[obj->nr_extern - 1];
@@ -1822,29 +1830,51 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
 	struct btf_type *t;
 	int i, j, vlen;
 
-	if (!obj->btf || (has_func && has_datasec))
+	if (!obj->btf)
 		return;
-
 	for (i = 1; i <= btf__get_nr_types(btf); i++) {
 		t = (struct btf_type *)btf__type_by_id(btf, i);
 
-		if (!has_datasec && btf_is_var(t)) {
-			/* replace VAR with INT */
-			t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
-			/*
-			 * using size = 1 is the safest choice, 4 will be too
-			 * big and cause kernel BTF validation failure if
-			 * original variable took less than 4 bytes
+		if (btf_is_var(t)) {
+			struct btf_type *var_t;
+
+			var_t = (struct btf_type *)btf__type_by_id(btf,
+								   t->type);
+
+			/* FIXME: The kernel doesn't understand func_proto with
+			 * BTF_VAR_GLOBAL_EXTERN linkage, so we just replace
+			 * them with INTs here. What's the right thing to do?
 			 */
-			t->size = 1;
-			*(int *)(t + 1) = BTF_INT_ENC(0, 0, 8);
-		} else if (!has_datasec && btf_is_datasec(t)) {
+			if (!has_datasec ||
+			    (btf_kind(var_t) == BTF_KIND_FUNC_PROTO &&
+			     btf_var(t)->linkage == BTF_VAR_GLOBAL_EXTERN)) {
+				/* replace VAR with INT */
+				t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
+				/*
+				 * using size = 1 is the safest choice, 4 will
+				 * be too big and cause kernel BTF validation
+				 * failure if original variable took less than 4
+				 * bytes
+				 */
+				t->size = 1;
+				*(int *)(t + 1) = BTF_INT_ENC(0, 0, 8);
+			}
+		} else if (btf_is_datasec(t)) {
 			/* replace DATASEC with STRUCT */
 			const struct btf_var_secinfo *v = btf_var_secinfos(t);
 			struct btf_member *m = btf_members(t);
 			struct btf_type *vt;
+			size_t tot_size = 0;
 			char *name;
 
+			/* FIXME: The .extern datasec can be 0-sized when there
+			 * are only function signatures but no variables marked
+			 * as extern. Kernel doesn't understand this, so we need
+			 * to get rid of those.
+			 */
+			if (has_datasec && t->size > 0)
+				continue;
+
 			name = (char *)btf__name_by_offset(btf, t->name_off);
 			while (*name) {
 				if (*name == '.')
@@ -1861,7 +1891,10 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
 				/* preserve variable name as member name */
 				vt = (void *)btf__type_by_id(btf, v->type);
 				m->name_off = vt->name_off;
+				tot_size += vt->size;
 			}
+			if (t->size < tot_size)
+				t->size = tot_size;
 		} else if (!has_func && btf_is_func_proto(t)) {
 			/* replace FUNC_PROTO with ENUM */
 			vlen = btf_vlen(t);
@@ -2205,6 +2238,8 @@ static enum extern_type find_extern_type(const struct btf *btf, int id,
 		if (find_extern_type(btf, btf_array(t)->type, NULL) != EXT_CHAR)
 			return EXT_UNKNOWN;
 		return EXT_CHAR_ARR;
+	case BTF_KIND_FUNC_PROTO:
+		return EXT_FUNC;
 	default:
 		return EXT_UNKNOWN;
 	}
@@ -2215,6 +2250,10 @@ static int cmp_externs(const void *_a, const void *_b)
 	const struct extern_desc *a = _a;
 	const struct extern_desc *b = _b;
 
+	/* make sure function externs are at the end */
+	if (a->type != b->type)
+		return a->type == EXT_FUNC ? -1 : 1;
+
 	/* descending order by alignment requirements */
 	if (a->align != b->align)
 		return a->align > b->align ? -1 : 1;
@@ -2295,10 +2334,13 @@ static int bpf_object__collect_externs(struct bpf_object *obj)
 			pr_warn("extern '%s' type is unsupported\n", ext_name);
 			return -ENOTSUP;
 		}
+
+		if (ext->type != EXT_FUNC)
+			obj->nr_data_extern++;
 	}
 	pr_debug("collected %d externs total\n", obj->nr_extern);
 
-	if (!obj->nr_extern)
+	if (!obj->nr_data_extern)
 		return 0;
 
 	/* sort externs by (alignment, size, name) and calculate their offsets
@@ -2422,22 +2464,56 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
 	enum libbpf_map_type type;
 	struct bpf_map *map;
 
+	if (sym_is_extern(sym)) {
+		int sym_idx = GELF_R_SYM(rel->r_info);
+		int i, n = obj->nr_extern;
+		struct extern_desc *ext;
+
+		for (i = 0; i < n; i++) {
+			ext = &obj->externs[i];
+			if (ext->sym_idx == sym_idx)
+				break;
+		}
+		if (i >= n) {
+			pr_warn("extern relo failed to find extern for sym %d\n",
+				sym_idx);
+			return -LIBBPF_ERRNO__RELOC;
+		}
+		pr_debug("found extern #%d '%s' (sym %d, off %u) for insn %u\n",
+			 i, ext->name, ext->sym_idx, ext->data_off, insn_idx);
+		reloc_desc->insn_idx = insn_idx;
+		reloc_desc->sym_off = ext->data_off;
+		reloc_desc->ext = ext;
+
+		if (insn->code == (BPF_JMP | BPF_CALL)) {
+			if (insn->src_reg != BPF_PSEUDO_CALL) {
+				pr_warn("incorrect bpf_call opcode\n");
+				return -LIBBPF_ERRNO__RELOC;
+			}
+			obj->has_pseudo_calls = true;
+			reloc_desc->type = RELO_EXTERN_CALL;
+		} else {
+			reloc_desc->type = RELO_EXTERN;
+		}
+		return 0;
+	}
+
 	/* sub-program call relocation */
 	if (insn->code == (BPF_JMP | BPF_CALL)) {
 		if (insn->src_reg != BPF_PSEUDO_CALL) {
 			pr_warn("incorrect bpf_call opcode\n");
 			return -LIBBPF_ERRNO__RELOC;
 		}
-		/* text_shndx can be 0, if no default "main" program exists */
-		if (!shdr_idx || shdr_idx != obj->efile.text_shndx) {
-			pr_warn("bad call relo against section %u\n", shdr_idx);
-			return -LIBBPF_ERRNO__RELOC;
-		}
 		if (sym->st_value % 8) {
 			pr_warn("bad call relo offset: %zu\n",
 				(size_t)sym->st_value);
 			return -LIBBPF_ERRNO__RELOC;
 		}
+		/* text_shndx can be 0, if no default "main" program exists */
+		if (!shdr_idx || shdr_idx != obj->efile.text_shndx) {
+			pr_warn("bad call relo against section %u\n", shdr_idx);
+			return -LIBBPF_ERRNO__RELOC;
+		}
 		reloc_desc->type = RELO_CALL;
 		reloc_desc->insn_idx = insn_idx;
 		reloc_desc->sym_off = sym->st_value;
@@ -2451,28 +2527,6 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
 		return -LIBBPF_ERRNO__RELOC;
 	}
 
-	if (sym_is_extern(sym)) {
-		int sym_idx = GELF_R_SYM(rel->r_info);
-		int i, n = obj->nr_extern;
-		struct extern_desc *ext;
-
-		for (i = 0; i < n; i++) {
-			ext = &obj->externs[i];
-			if (ext->sym_idx == sym_idx)
-				break;
-		}
-		if (i >= n) {
-			pr_warn("extern relo failed to find extern for sym %d\n",
-				sym_idx);
-			return -LIBBPF_ERRNO__RELOC;
-		}
-		pr_debug("found extern #%d '%s' (sym %d, off %u) for insn %u\n",
-			 i, ext->name, ext->sym_idx, ext->data_off, insn_idx);
-		reloc_desc->type = RELO_EXTERN;
-		reloc_desc->insn_idx = insn_idx;
-		reloc_desc->sym_off = ext->data_off;
-		return 0;
-	}
 
 	if (!shdr_idx || shdr_idx >= SHN_LORESERVE) {
 		pr_warn("invalid relo for \'%s\' in special section 0x%x; forgot to initialize global var?..\n",
@@ -4268,6 +4322,46 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
 	return 0;
 }
 
+static int
+bpf_program__reloc_ext_call(struct bpf_program *prog, struct bpf_object *obj,
+			    struct reloc_desc *relo)
+{
+	struct bpf_program *tgt_prog = relo->ext->tgt_prog;
+	struct bpf_insn *insn, *new_insn;
+	size_t new_cnt, old_cnt;
+	int err;
+
+	new_cnt = prog->insns_cnt + tgt_prog->insns_cnt;
+	old_cnt = prog->insns_cnt;
+	new_insn = reallocarray(prog->insns, new_cnt, sizeof(*insn));
+	if (!new_insn) {
+		pr_warn("oom in prog realloc\n");
+		return -ENOMEM;
+	}
+	prog->insns = new_insn;
+
+	/* FIXME: Is this right? Line info and function names seem off when
+	 * dumped from kernel. Also, type info needs resolving across both
+	 * objects; fails with 'invalid type id' for anything but the simplest
+	 * programs as-is.
+	 */
+	err = bpf_program_reloc_btf_ext(prog, tgt_prog->obj,
+					tgt_prog->section_name,
+					prog->insns_cnt);
+	if (err)
+		return err;
+
+	memcpy(new_insn + prog->insns_cnt, tgt_prog->insns,
+	       tgt_prog->insns_cnt * sizeof(*insn));
+	prog->insns_cnt = new_cnt;
+	pr_debug("added %zd insn from %s to prog %s\n",
+		 tgt_prog->insns_cnt, tgt_prog->section_name,
+		 prog->section_name);
+	insn = &prog->insns[relo->insn_idx];
+	insn->imm += relo->sym_off / 8 + old_cnt - relo->insn_idx;
+	return 0;
+}
+
 static int
 bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
 {
@@ -4311,6 +4405,11 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
 			insn[0].imm = obj->maps[obj->kconfig_map_idx].fd;
 			insn[1].imm = relo->sym_off;
 			break;
+		case RELO_EXTERN_CALL:
+			err = bpf_program__reloc_ext_call(prog, obj, relo);
+			if (err)
+				return err;
+			break;
 		case RELO_CALL:
 			err = bpf_program__reloc_text(prog, obj, relo);
 			if (err)
@@ -4565,8 +4664,6 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
 out:
 	if (err)
 		pr_warn("failed to load program '%s'\n", prog->section_name);
-	zfree(&prog->insns);
-	prog->insns_cnt = 0;
 	return err;
 }
 
@@ -4776,20 +4873,77 @@ static int bpf_object__sanitize_maps(struct bpf_object *obj)
 	return 0;
 }
 
+static int bpf_object__resolve_extern_call(struct extern_desc *ext,
+					   const struct bpf_extern_calls *ext_calls)
+{
+	struct bpf_extern_call_tgt *tgt = NULL;
+	struct bpf_program *tgt_prog;
+	int i;
+
+	for (i = 0; i < ext_calls->num_tgts; i++) {
+		if (!strcmp(ext_calls->tgts[i].name, ext->name)) {
+			tgt = &ext_calls->tgts[i];
+			break;
+		}
+	}
+
+	if (!tgt) {
+		pr_warn("Couldn't find external call target for extern %s\n",
+			ext->name);
+		return -ESRCH;
+	}
+
+	/* dynamic linking with in-kernel objects not implemented yet */
+	if (tgt->tgt_fd)
+		return -EINVAL;
+
+	if (!tgt->tgt_obj) {
+		pr_warn("Extern call target %s missing object\n", tgt->name);
+		return -EINVAL;
+	}
+
+	if (!tgt->tgt_obj->loaded) {
+		pr_warn("External call target %s must be loaded first\n",
+			tgt->name);
+		return -EINVAL;
+	}
+
+	if (!tgt->tgt_obj->btf_ext) {
+		pr_warn("External call target %s is missing BTF\n",
+			tgt->name);
+		return -EINVAL;
+	}
+
+	tgt_prog = bpf_object__find_program_by_name(tgt->tgt_obj,
+						    tgt->tgt_prog_name);
+	if (!tgt_prog) {
+		pr_warn("Couldn't find target prog name %s for extern %s\n",
+			tgt->tgt_prog_name, tgt->name);
+		return -ESRCH;
+	}
+
+	/* FIXME: Compare call signature BTF between target and call site. */
+
+	ext->tgt_prog = tgt_prog;
+	ext->is_set = true;
+	return 0;
+}
+
 static int bpf_object__resolve_externs(struct bpf_object *obj,
-				       const char *extra_kconfig)
+				       const char *extra_kconfig,
+				       const struct bpf_extern_calls *ext_calls)
 {
 	bool need_config = false;
 	struct extern_desc *ext;
 	int err, i;
 	void *data;
 
-	if (obj->nr_extern == 0)
-		return 0;
+	if (obj->nr_data_extern == 0)
+		goto calls;
 
 	data = obj->maps[obj->kconfig_map_idx].mmaped;
 
-	for (i = 0; i < obj->nr_extern; i++) {
+	for (i = 0; i < obj->nr_data_extern; i++) {
 		ext = &obj->externs[i];
 
 		if (strcmp(ext->name, "LINUX_KERNEL_VERSION") == 0) {
@@ -4829,6 +4983,23 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
 		if (err)
 			return -EINVAL;
 	}
+
+calls:
+	if (obj->nr_data_extern < obj->nr_extern) {
+		if (!ext_calls) {
+			pr_warn("Found %d external calls, but not config for resolving them\n",
+				obj->nr_extern - obj->nr_data_extern);
+			return -EINVAL;
+		}
+
+		for (i = obj->nr_data_extern; i < obj->nr_extern; i++) {
+			err = bpf_object__resolve_extern_call(&obj->externs[i],
+							      ext_calls);
+			if (err)
+				return err;
+		}
+	}
+
 	for (i = 0; i < obj->nr_extern; i++) {
 		ext = &obj->externs[i];
 
@@ -4860,7 +5031,8 @@ int bpf_object__load2(struct bpf_object *obj,
 	obj->loaded = true;
 
 	err = bpf_object__probe_caps(obj);
-	err = err ? : bpf_object__resolve_externs(obj, obj->kconfig);
+	err = err ? : bpf_object__resolve_externs(obj, obj->kconfig,
+					   OPTS_GET(opts, ext_calls, NULL));
 	err = err ? : bpf_object__sanitize_and_load_btf(obj);
 	err = err ? : bpf_object__sanitize_maps(obj);
 	err = err ? : bpf_object__create_maps(obj);
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index ce86277d7445..99cc4bf36486 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -132,6 +132,19 @@ struct bpf_object_load_attr {
 	const char *target_btf_path;
 };
 
+struct bpf_extern_call_tgt {
+	const char *name;
+	const char *tgt_prog_name;
+	/* set one of tgt_obj or tgt_fd */
+	struct bpf_object *tgt_obj;
+	int tgt_fd;
+};
+
+struct bpf_extern_calls {
+	size_t num_tgts;
+	struct bpf_extern_call_tgt *tgts;
+};
+
 struct bpf_object_load_opts {
 	/* size of this struct, for forward/backward compatiblity */
 	size_t sz;
@@ -139,8 +152,10 @@ struct bpf_object_load_opts {
 	int log_level;
 	/* BTF path (for CO-RE relocations) */
 	const char *target_btf_path;
+	/* Descriptions for resolving bpf extern call targets */
+	const struct bpf_extern_calls *ext_calls;
 };
-#define bpf_object_load_opts__last_field target_btf_path
+#define bpf_object_load_opts__last_field ext_calls
 
 /* Load/unload object into/from kernel */
 LIBBPF_API int bpf_object__load(struct bpf_object *obj);


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC bpf-next 3/3] selftests/bpf: Add selftest for XDP multiprogs
  2019-12-19 14:29 [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Toke Høiland-Jørgensen
  2019-12-19 14:29 ` [PATCH RFC bpf-next 1/3] libbpf: Add new bpf_object__load2() using new-style opts Toke Høiland-Jørgensen
  2019-12-19 14:29 ` [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking Toke Høiland-Jørgensen
@ 2019-12-19 14:29 ` Toke Høiland-Jørgensen
  2019-12-20 20:30 ` [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Alexei Starovoitov
  3 siblings, 0 replies; 13+ messages in thread
From: Toke Høiland-Jørgensen @ 2019-12-19 14:29 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Alexei Starovoitov, Martin KaFai Lau, Song Liu, Yonghong Song,
	Jesper Dangaard Brouer, Andrii Nakryiko, David Miller, netdev,
	bpf

From: Toke Høiland-Jørgensen <toke@redhat.com>

This adds a simple selftest that combines two XDP programs through a third
dispatcher, exercising the libbpf function externals handling.

Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
---
 .../selftests/bpf/prog_tests/xdp_multiprog.c       |   52 ++++++++++++++++++++
 tools/testing/selftests/bpf/progs/xdp_drop.c       |   13 +++++
 tools/testing/selftests/bpf/progs/xdp_multiprog.c  |   26 ++++++++++
 3 files changed, 91 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_multiprog.c
 create mode 100644 tools/testing/selftests/bpf/progs/xdp_drop.c
 create mode 100644 tools/testing/selftests/bpf/progs/xdp_multiprog.c

diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_multiprog.c b/tools/testing/selftests/bpf/prog_tests/xdp_multiprog.c
new file mode 100644
index 000000000000..40a743437222
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_multiprog.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+
+void test_xdp_multiprog(void)
+{
+	const char *file_dispatcher = "./xdp_multiprog.o";
+	const char *file_drop = "./xdp_drop.o";
+	const char *file_pass = "./xdp_dummy.o";
+	struct bpf_object *obj, *obj_drop, *obj_pass;
+	int err;
+
+
+	obj = bpf_object__open_file(file_dispatcher, NULL);
+	err = libbpf_get_error(obj);
+	if (CHECK_FAIL(err))
+		return;
+
+	obj_drop = bpf_object__open_file(file_drop, NULL);
+	err = libbpf_get_error(obj_drop);
+	if (CHECK_FAIL(err))
+		goto out_obj;
+
+	obj_pass = bpf_object__open_file(file_pass, NULL);
+	err = libbpf_get_error(obj_pass);
+	if (CHECK_FAIL(err))
+		goto out_drop;
+
+        err = bpf_object__load(obj_drop);
+        err = err ?: bpf_object__load(obj_pass);
+
+        if (CHECK_FAIL(err))
+                goto out;
+
+	struct bpf_extern_call_tgt tgts[] =
+		{
+		 {.name = "prog1", .tgt_prog_name = "xdp_dummy_prog", .tgt_obj = obj_pass},
+		 {.name = "prog2", .tgt_prog_name = "xdp_drop_prog", .tgt_obj = obj_drop},
+		};
+	struct bpf_extern_calls calls = {.num_tgts = 2, .tgts = tgts };
+
+	DECLARE_LIBBPF_OPTS(bpf_object_load_opts, load_opts,
+			    .ext_calls = &calls);
+
+	err = bpf_object__load2(obj, &load_opts);
+        CHECK_FAIL(err);
+out:
+	bpf_object__close(obj_pass);
+out_drop:
+	bpf_object__close(obj_drop);
+out_obj:
+	bpf_object__close(obj);
+}
diff --git a/tools/testing/selftests/bpf/progs/xdp_drop.c b/tools/testing/selftests/bpf/progs/xdp_drop.c
new file mode 100644
index 000000000000..10e415e49564
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/xdp_drop.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define KBUILD_MODNAME "xdp_drop"
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+SEC("xdp_drop")
+int xdp_drop_prog(struct xdp_md *ctx)
+{
+	return XDP_DROP;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/xdp_multiprog.c b/tools/testing/selftests/bpf/progs/xdp_multiprog.c
new file mode 100644
index 000000000000..ef5ba8172038
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/xdp_multiprog.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define KBUILD_MODNAME "xdp_multiprog"
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+extern int prog1(struct xdp_md *ctx);
+extern int prog2(struct xdp_md *ctx);
+
+SEC("xdp_test")
+int xdp_main(struct xdp_md *ctx)
+{
+        int ret;
+
+        ret = prog1(ctx);
+        if (ret != XDP_PASS)
+                goto out;
+
+        ret = prog2(ctx);
+        if (ret != XDP_DROP)
+                goto out;
+out:
+        return ret;
+}
+
+char _license[] SEC("license") = "GPL";


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking
  2019-12-19 14:29 ` [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking Toke Høiland-Jørgensen
@ 2019-12-19 16:24   ` Yonghong Song
  2019-12-19 16:59     ` Toke Høiland-Jørgensen
  2019-12-20  0:02   ` Andrii Nakryiko
  1 sibling, 1 reply; 13+ messages in thread
From: Yonghong Song @ 2019-12-19 16:24 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen, Daniel Borkmann
  Cc: Alexei Starovoitov, Martin Lau, Song Liu, Jesper Dangaard Brouer,
	Andrii Nakryiko, David Miller, netdev, bpf



On 12/19/19 6:29 AM, Toke Høiland-Jørgensen wrote:
> From: Toke Høiland-Jørgensen <toke@redhat.com>
> 
> This adds support for resolving function externs to libbpf, with a new API
> to resolve external function calls by static linking at load-time. The API
> for this requires the caller to supply the object files containing the
> target functions, and to specify an explicit mapping between extern
> function names in the calling program, and function names in the target
> object file. This is to support the XDP multi-prog case, where the
> dispatcher program may not necessarily have control over function names in
> the target programs, so simple function name resolution can't be used.
> 
> The target object files must be loaded into the kernel before the calling
> program, to ensure all relocations are done on the target functions, so we
> can just copy over the instructions.
> 
> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
> ---
>   tools/lib/bpf/btf.c    |   10 +-
>   tools/lib/bpf/libbpf.c |  268 +++++++++++++++++++++++++++++++++++++++---------
>   tools/lib/bpf/libbpf.h |   17 +++
>   3 files changed, 244 insertions(+), 51 deletions(-)
> 
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index 5f04f56e1eb6..2740d4a6b2eb 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
> @@ -246,6 +246,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
>   			size = t->size;
>   			goto done;
>   		case BTF_KIND_PTR:
> +		case BTF_KIND_FUNC_PROTO:
>   			size = sizeof(void *);
>   			goto done;
>   		case BTF_KIND_TYPEDEF:
> @@ -288,6 +289,7 @@ int btf__align_of(const struct btf *btf, __u32 id)
>   	case BTF_KIND_ENUM:
>   		return min(sizeof(void *), t->size);
>   	case BTF_KIND_PTR:
> +	case BTF_KIND_FUNC_PROTO:
>   		return sizeof(void *);
>   	case BTF_KIND_TYPEDEF:
>   	case BTF_KIND_VOLATILE:
> @@ -640,12 +642,16 @@ int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
>   		 */
>   		if (btf_is_datasec(t)) {
>   			err = btf_fixup_datasec(obj, btf, t);
> -			if (err)
> +			/* FIXME: With function externs we can get a BTF DATASEC
> +			 * entry for .extern, but the section doesn't exist; so
> +			 * make ENOENT non-fatal
> +			 */
> +			if (err && err != -ENOENT)
>   				break;
>   		}
>   	}
>   
> -	return err;
> +	return err == -ENOENT ? err : 0;
>   }
>   
>   int btf__load(struct btf *btf)
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 266b725e444b..b2c0a2f927e7 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -172,13 +172,17 @@ enum reloc_type {
>   	RELO_CALL,
>   	RELO_DATA,
>   	RELO_EXTERN,
> +	RELO_EXTERN_CALL,
>   };
>   
> +struct extern_desc;
> +
>   struct reloc_desc {
>   	enum reloc_type type;
>   	int insn_idx;
>   	int map_idx;
>   	int sym_off;
> +	struct extern_desc *ext;
>   };
>   
>   /*
> @@ -274,6 +278,7 @@ enum extern_type {
>   	EXT_INT,
>   	EXT_TRISTATE,
>   	EXT_CHAR_ARR,
> +	EXT_FUNC
>   };
>   
>   struct extern_desc {
> @@ -287,6 +292,7 @@ struct extern_desc {
>   	bool is_signed;
>   	bool is_weak;
>   	bool is_set;
> +	struct bpf_program *tgt_prog;
>   };
>   
>   static LIST_HEAD(bpf_objects_list);
> @@ -305,6 +311,7 @@ struct bpf_object {
>   	char *kconfig;
>   	struct extern_desc *externs;
>   	int nr_extern;
> +	int nr_data_extern;
>   	int kconfig_map_idx;
>   
>   	bool loaded;
> @@ -1041,6 +1048,7 @@ static int set_ext_value_tri(struct extern_desc *ext, void *ext_val,
>   	case EXT_UNKNOWN:
>   	case EXT_INT:
>   	case EXT_CHAR_ARR:
> +	case EXT_FUNC:
>   	default:
>   		pr_warn("extern %s=%c should be bool, tristate, or char\n",
>   			ext->name, value);
> @@ -1281,7 +1289,7 @@ static int bpf_object__init_kconfig_map(struct bpf_object *obj)
>   	size_t map_sz;
>   	int err;
>   
> -	if (obj->nr_extern == 0)
> +	if (obj->nr_data_extern == 0)
>   		return 0;
>   
>   	last_ext = &obj->externs[obj->nr_extern - 1];
> @@ -1822,29 +1830,51 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
>   	struct btf_type *t;
>   	int i, j, vlen;
>   
> -	if (!obj->btf || (has_func && has_datasec))
> +	if (!obj->btf)
>   		return;
> -
>   	for (i = 1; i <= btf__get_nr_types(btf); i++) {
>   		t = (struct btf_type *)btf__type_by_id(btf, i);
>   
> -		if (!has_datasec && btf_is_var(t)) {
> -			/* replace VAR with INT */
> -			t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
> -			/*
> -			 * using size = 1 is the safest choice, 4 will be too
> -			 * big and cause kernel BTF validation failure if
> -			 * original variable took less than 4 bytes
> +		if (btf_is_var(t)) {
> +			struct btf_type *var_t;
> +
> +			var_t = (struct btf_type *)btf__type_by_id(btf,
> +								   t->type);
> +
> +			/* FIXME: The kernel doesn't understand func_proto with
> +			 * BTF_VAR_GLOBAL_EXTERN linkage, so we just replace
> +			 * them with INTs here. What's the right thing to do?
>   			 */
> -			t->size = 1;
> -			*(int *)(t + 1) = BTF_INT_ENC(0, 0, 8);
> -		} else if (!has_datasec && btf_is_datasec(t)) {
> +			if (!has_datasec ||
> +			    (btf_kind(var_t) == BTF_KIND_FUNC_PROTO &&
> +			     btf_var(t)->linkage == BTF_VAR_GLOBAL_EXTERN)) {

You are the first user to use extern function encoding in BTF! Thanks!

Recently, we have discussion with Alexei and felt that putting extern 
function into datasec/var is not pretty. So we have the following llvm patch
    https://reviews.llvm.org/D71638
to put extern function as a BTF_KIND_FUNC, i.e.,
    BTF_KIND_FUNC
         .info (lower 2 bits) -> FUNC_STATIC, FUNC_GLOBAL, FUNC_EXTERN
         .type -> BTF_KIND_FUNC_PROTO

Alexei is working on kernel side to ensure this is handled properly 
before llvm patch can be merged.

Just let you know for the future potential BTF interface change.

> +				/* replace VAR with INT */
> +				t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
> +				/*
> +				 * using size = 1 is the safest choice, 4 will
> +				 * be too big and cause kernel BTF validation
> +				 * failure if original variable took less than 4
> +				 * bytes
> +				 */
> +				t->size = 1;
> +				*(int *)(t + 1) = BTF_INT_ENC(0, 0, 8);
> +			}
> +		} else if (btf_is_datasec(t)) {
>   			/* replace DATASEC with STRUCT */
>   			const struct btf_var_secinfo *v = btf_var_secinfos(t);
>   			struct btf_member *m = btf_members(t);
>   			struct btf_type *vt;
> +			size_t tot_size = 0;
>   			char *name;
>   
> +			/* FIXME: The .extern datasec can be 0-sized when there
> +			 * are only function signatures but no variables marked
> +			 * as extern. Kernel doesn't understand this, so we need
> +			 * to get rid of those.
> +			 */
> +			if (has_datasec && t->size > 0)
> +				continue;
> +
>   			name = (char *)btf__name_by_offset(btf, t->name_off);
>   			while (*name) {
>   				if (*name == '.')
> @@ -1861,7 +1891,10 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
>   				/* preserve variable name as member name */
>   				vt = (void *)btf__type_by_id(btf, v->type);
>   				m->name_off = vt->name_off;
> +				tot_size += vt->size;
>   			}
> +			if (t->size < tot_size)
> +				t->size = tot_size;
>   		} else if (!has_func && btf_is_func_proto(t)) {
>   			/* replace FUNC_PROTO with ENUM */
>   			vlen = btf_vlen(t);
> @@ -2205,6 +2238,8 @@ static enum extern_type find_extern_type(const struct btf *btf, int id,
>   		if (find_extern_type(btf, btf_array(t)->type, NULL) != EXT_CHAR)
>   			return EXT_UNKNOWN;
>   		return EXT_CHAR_ARR;
> +	case BTF_KIND_FUNC_PROTO:
> +		return EXT_FUNC;
>   	default:
>   		return EXT_UNKNOWN;
>   	}
> @@ -2215,6 +2250,10 @@ static int cmp_externs(const void *_a, const void *_b)
[...]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking
  2019-12-19 16:24   ` Yonghong Song
@ 2019-12-19 16:59     ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 13+ messages in thread
From: Toke Høiland-Jørgensen @ 2019-12-19 16:59 UTC (permalink / raw)
  To: Yonghong Song, Daniel Borkmann
  Cc: Alexei Starovoitov, Martin Lau, Song Liu, Jesper Dangaard Brouer,
	Andrii Nakryiko, David Miller, netdev, bpf

Yonghong Song <yhs@fb.com> writes:

> On 12/19/19 6:29 AM, Toke Høiland-Jørgensen wrote:
>> From: Toke Høiland-Jørgensen <toke@redhat.com>
>> 
>> This adds support for resolving function externs to libbpf, with a new API
>> to resolve external function calls by static linking at load-time. The API
>> for this requires the caller to supply the object files containing the
>> target functions, and to specify an explicit mapping between extern
>> function names in the calling program, and function names in the target
>> object file. This is to support the XDP multi-prog case, where the
>> dispatcher program may not necessarily have control over function names in
>> the target programs, so simple function name resolution can't be used.
>> 
>> The target object files must be loaded into the kernel before the calling
>> program, to ensure all relocations are done on the target functions, so we
>> can just copy over the instructions.
>> 
>> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
>> ---
>>   tools/lib/bpf/btf.c    |   10 +-
>>   tools/lib/bpf/libbpf.c |  268 +++++++++++++++++++++++++++++++++++++++---------
>>   tools/lib/bpf/libbpf.h |   17 +++
>>   3 files changed, 244 insertions(+), 51 deletions(-)
>> 
>> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
>> index 5f04f56e1eb6..2740d4a6b2eb 100644
>> --- a/tools/lib/bpf/btf.c
>> +++ b/tools/lib/bpf/btf.c
>> @@ -246,6 +246,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
>>   			size = t->size;
>>   			goto done;
>>   		case BTF_KIND_PTR:
>> +		case BTF_KIND_FUNC_PROTO:
>>   			size = sizeof(void *);
>>   			goto done;
>>   		case BTF_KIND_TYPEDEF:
>> @@ -288,6 +289,7 @@ int btf__align_of(const struct btf *btf, __u32 id)
>>   	case BTF_KIND_ENUM:
>>   		return min(sizeof(void *), t->size);
>>   	case BTF_KIND_PTR:
>> +	case BTF_KIND_FUNC_PROTO:
>>   		return sizeof(void *);
>>   	case BTF_KIND_TYPEDEF:
>>   	case BTF_KIND_VOLATILE:
>> @@ -640,12 +642,16 @@ int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
>>   		 */
>>   		if (btf_is_datasec(t)) {
>>   			err = btf_fixup_datasec(obj, btf, t);
>> -			if (err)
>> +			/* FIXME: With function externs we can get a BTF DATASEC
>> +			 * entry for .extern, but the section doesn't exist; so
>> +			 * make ENOENT non-fatal
>> +			 */
>> +			if (err && err != -ENOENT)
>>   				break;
>>   		}
>>   	}
>>   
>> -	return err;
>> +	return err == -ENOENT ? err : 0;
>>   }
>>   
>>   int btf__load(struct btf *btf)
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 266b725e444b..b2c0a2f927e7 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -172,13 +172,17 @@ enum reloc_type {
>>   	RELO_CALL,
>>   	RELO_DATA,
>>   	RELO_EXTERN,
>> +	RELO_EXTERN_CALL,
>>   };
>>   
>> +struct extern_desc;
>> +
>>   struct reloc_desc {
>>   	enum reloc_type type;
>>   	int insn_idx;
>>   	int map_idx;
>>   	int sym_off;
>> +	struct extern_desc *ext;
>>   };
>>   
>>   /*
>> @@ -274,6 +278,7 @@ enum extern_type {
>>   	EXT_INT,
>>   	EXT_TRISTATE,
>>   	EXT_CHAR_ARR,
>> +	EXT_FUNC
>>   };
>>   
>>   struct extern_desc {
>> @@ -287,6 +292,7 @@ struct extern_desc {
>>   	bool is_signed;
>>   	bool is_weak;
>>   	bool is_set;
>> +	struct bpf_program *tgt_prog;
>>   };
>>   
>>   static LIST_HEAD(bpf_objects_list);
>> @@ -305,6 +311,7 @@ struct bpf_object {
>>   	char *kconfig;
>>   	struct extern_desc *externs;
>>   	int nr_extern;
>> +	int nr_data_extern;
>>   	int kconfig_map_idx;
>>   
>>   	bool loaded;
>> @@ -1041,6 +1048,7 @@ static int set_ext_value_tri(struct extern_desc *ext, void *ext_val,
>>   	case EXT_UNKNOWN:
>>   	case EXT_INT:
>>   	case EXT_CHAR_ARR:
>> +	case EXT_FUNC:
>>   	default:
>>   		pr_warn("extern %s=%c should be bool, tristate, or char\n",
>>   			ext->name, value);
>> @@ -1281,7 +1289,7 @@ static int bpf_object__init_kconfig_map(struct bpf_object *obj)
>>   	size_t map_sz;
>>   	int err;
>>   
>> -	if (obj->nr_extern == 0)
>> +	if (obj->nr_data_extern == 0)
>>   		return 0;
>>   
>>   	last_ext = &obj->externs[obj->nr_extern - 1];
>> @@ -1822,29 +1830,51 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
>>   	struct btf_type *t;
>>   	int i, j, vlen;
>>   
>> -	if (!obj->btf || (has_func && has_datasec))
>> +	if (!obj->btf)
>>   		return;
>> -
>>   	for (i = 1; i <= btf__get_nr_types(btf); i++) {
>>   		t = (struct btf_type *)btf__type_by_id(btf, i);
>>   
>> -		if (!has_datasec && btf_is_var(t)) {
>> -			/* replace VAR with INT */
>> -			t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
>> -			/*
>> -			 * using size = 1 is the safest choice, 4 will be too
>> -			 * big and cause kernel BTF validation failure if
>> -			 * original variable took less than 4 bytes
>> +		if (btf_is_var(t)) {
>> +			struct btf_type *var_t;
>> +
>> +			var_t = (struct btf_type *)btf__type_by_id(btf,
>> +								   t->type);
>> +
>> +			/* FIXME: The kernel doesn't understand func_proto with
>> +			 * BTF_VAR_GLOBAL_EXTERN linkage, so we just replace
>> +			 * them with INTs here. What's the right thing to do?
>>   			 */
>> -			t->size = 1;
>> -			*(int *)(t + 1) = BTF_INT_ENC(0, 0, 8);
>> -		} else if (!has_datasec && btf_is_datasec(t)) {
>> +			if (!has_datasec ||
>> +			    (btf_kind(var_t) == BTF_KIND_FUNC_PROTO &&
>> +			     btf_var(t)->linkage == BTF_VAR_GLOBAL_EXTERN)) {
>
> You are the first user to use extern function encoding in BTF! Thanks!

Haha, you're welcome!

And yeah, I realise this is pretty bleeding edge stuff, and as you can
probably tell I'm sort of fumbling my way forward here ;)

> Recently, we have discussion with Alexei and felt that putting extern 
> function into datasec/var is not pretty. So we have the following llvm patch
>     https://reviews.llvm.org/D71638
> to put extern function as a BTF_KIND_FUNC, i.e.,
>     BTF_KIND_FUNC
>          .info (lower 2 bits) -> FUNC_STATIC, FUNC_GLOBAL, FUNC_EXTERN
>          .type -> BTF_KIND_FUNC_PROTO
>
> Alexei is working on kernel side to ensure this is handled properly 
> before llvm patch can be merged.
>
> Just let you know for the future potential BTF interface change.

OK, thanks for the head's up. And yeah, I agree this sounds like an
improvement; I was a little puzzled by the datasec/var thing, and a lot
of the weird hacks I had to do were related to working around that. So
if this is just going to go away, that's great!

If you guys can look the rest of the patch over and give me some
pointers on the other FIXME items (and the API), that would be great! No
great rush, though; I'm leaving for the holidays tomorrow, so won't have
any more time to work on this before the new year. I guess I'll and see
how far along the kernel/llvm changes are, and deal with any other
feedback you guys have by then. :)

-Toke


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 1/3] libbpf: Add new bpf_object__load2() using new-style opts
  2019-12-19 14:29 ` [PATCH RFC bpf-next 1/3] libbpf: Add new bpf_object__load2() using new-style opts Toke Høiland-Jørgensen
@ 2019-12-19 23:50   ` Andrii Nakryiko
  2019-12-20 10:50     ` Toke Høiland-Jørgensen
  0 siblings, 1 reply; 13+ messages in thread
From: Andrii Nakryiko @ 2019-12-19 23:50 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen
  Cc: Daniel Borkmann, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jesper Dangaard Brouer, David Miller, Networking,
	bpf

On Thu, Dec 19, 2019 at 6:29 AM Toke Høiland-Jørgensen <toke@redhat.com> wrote:
>
> From: Toke Høiland-Jørgensen <toke@redhat.com>
>
> Since we introduced DECLARE_LIBBPF_OPTS and related macros for declaring
> function options, that is now the preferred way to extend APIs. Introduce a
> variant of the bpf_object__load() function that uses this function, and
> deprecate the _xattr variant. Since all the good function names were taken,
> the new function is unimaginatively called bpf_object__load2().
>
> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
> ---

I've been thinking about options for load quite a bit lately, and I'm
leaning towards an opinion, that bpf_object__load() shouldn't take any
options, and all the various per-bpf_object options have to be
specified in bpf_object_open_opts and stored, if necessary for
load/attach phase. So I'd rather move target_btf_path and log_level to
open_opts instead.

For cases where we need to tune load behavior/options per individual
BPF program, bpf_object_load_opts don't work either way, and we'll
have to add some getters/setters for bpf_program objects, anyways. So
I think it's overall cleaner to specify everything per-bpf_object at
"creation time" (which is open), and keep load()/attach() option-less.

>  tools/lib/bpf/libbpf.c   |   31 ++++++++++++++++++-------------
>  tools/lib/bpf/libbpf.h   |   13 +++++++++++++
>  tools/lib/bpf/libbpf.map |    1 +
>  3 files changed, 32 insertions(+), 13 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index febbaac3daf4..266b725e444b 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -4844,15 +4844,12 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
>         return 0;
>  }
>
> -int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
> +int bpf_object__load2(struct bpf_object *obj,
> +                     const struct bpf_object_load_opts *opts)
>  {
> -       struct bpf_object *obj;
>         int err, i;
>
> -       if (!attr)
> -               return -EINVAL;
> -       obj = attr->obj;
> -       if (!obj)
> +       if (!obj || !OPTS_VALID(opts, bpf_object_load_opts))
>                 return -EINVAL;
>
>         if (obj->loaded) {
> @@ -4867,8 +4864,10 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
>         err = err ? : bpf_object__sanitize_and_load_btf(obj);
>         err = err ? : bpf_object__sanitize_maps(obj);
>         err = err ? : bpf_object__create_maps(obj);
> -       err = err ? : bpf_object__relocate(obj, attr->target_btf_path);
> -       err = err ? : bpf_object__load_progs(obj, attr->log_level);
> +       err = err ? : bpf_object__relocate(obj,
> +                                          OPTS_GET(opts, target_btf_path, NULL));
> +       err = err ? : bpf_object__load_progs(obj,
> +                                            OPTS_GET(opts, log_level, 0));
>         if (err)
>                 goto out;
>
> @@ -4884,13 +4883,19 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
>         return err;
>  }
>
> -int bpf_object__load(struct bpf_object *obj)
> +int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
>  {
> -       struct bpf_object_load_attr attr = {
> -               .obj = obj,
> -       };
> +       DECLARE_LIBBPF_OPTS(bpf_object_load_opts, opts,
> +           .log_level = attr->log_level,
> +           .target_btf_path = attr->target_btf_path,
> +       );
> +
> +       return bpf_object__load2(attr->obj, &opts);
> +}
>
> -       return bpf_object__load_xattr(&attr);
> +int bpf_object__load(struct bpf_object *obj)
> +{
> +       return bpf_object__load2(obj, NULL);
>  }
>
>  static int make_parent_dir(const char *path)
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index fe592ef48f1b..ce86277d7445 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -132,8 +132,21 @@ struct bpf_object_load_attr {
>         const char *target_btf_path;
>  };
>
> +struct bpf_object_load_opts {
> +       /* size of this struct, for forward/backward compatiblity */
> +       size_t sz;
> +       /* log level on load */
> +       int log_level;
> +       /* BTF path (for CO-RE relocations) */
> +       const char *target_btf_path;
> +};
> +#define bpf_object_load_opts__last_field target_btf_path
> +
>  /* Load/unload object into/from kernel */
>  LIBBPF_API int bpf_object__load(struct bpf_object *obj);
> +LIBBPF_API int bpf_object__load2(struct bpf_object *obj,
> +                                const struct bpf_object_load_opts *opts);
> +/* deprecated, use bpf_object__load2() instead */
>  LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr);
>  LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
>
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index e3a471f38a71..d6cb860763d1 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -217,6 +217,7 @@ LIBBPF_0.0.7 {
>                 bpf_object__attach_skeleton;
>                 bpf_object__destroy_skeleton;
>                 bpf_object__detach_skeleton;
> +               bpf_object__load2;
>                 bpf_object__load_skeleton;
>                 bpf_object__open_skeleton;
>                 bpf_program__attach;
>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking
  2019-12-19 14:29 ` [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking Toke Høiland-Jørgensen
  2019-12-19 16:24   ` Yonghong Song
@ 2019-12-20  0:02   ` Andrii Nakryiko
  2019-12-20 10:47     ` Toke Høiland-Jørgensen
  1 sibling, 1 reply; 13+ messages in thread
From: Andrii Nakryiko @ 2019-12-20  0:02 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen
  Cc: Daniel Borkmann, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jesper Dangaard Brouer, David Miller, Networking,
	bpf

On Thu, Dec 19, 2019 at 6:29 AM Toke Høiland-Jørgensen <toke@redhat.com> wrote:
>
> From: Toke Høiland-Jørgensen <toke@redhat.com>
>
> This adds support for resolving function externs to libbpf, with a new API
> to resolve external function calls by static linking at load-time. The API
> for this requires the caller to supply the object files containing the
> target functions, and to specify an explicit mapping between extern
> function names in the calling program, and function names in the target
> object file. This is to support the XDP multi-prog case, where the
> dispatcher program may not necessarily have control over function names in
> the target programs, so simple function name resolution can't be used.
>
> The target object files must be loaded into the kernel before the calling
> program, to ensure all relocations are done on the target functions, so we
> can just copy over the instructions.
>
> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
> ---

A bunch of this code will change after you update to latest Clang with
proper type info for extern functions. E.g., there shouldn't be any
size/alignment for BTF_KIND_FUNC_PROTO, it's illegal. But that
Yonghong already mentioned.

As for the overall approach. I think doing static linking outside of
bpf_object opening/loading is cleaner approach. If we introduce
bpf_linker concept/object and have someting like
bpf_linked__new(options) + a sequence of
bpf_linker__add_object(bpf_object) + final bpf_linker__link(), which
will produce usable bpf_object, as if bpf_object__open() was just
called, it will be better and will allow quite a lot of flexibility in
how we do things, without cluttering bpf_object API itself.
Additionally, we can even have bpf_linker__write_file() to emit a
final ELF file with statically linked object, which can then be loaded
through bpf_object__open_file (we can do the same for in-memory
buffer, of course). You can imagine LLC some day using libbpf to do
actual linking of BPF .o files into a final BPF executable/object
file, just like you expect it to do for non-BPF object files. WDYT?

Additionally, and seems you already realized that as well (judging by
FIXMEs), we'll need to merge those individual objects' BTFs and
deduplicate them, so that they form coherent set of types. Adjusting
line info/func info is mandatory as well.

Another thing we should think through is sharing maps. With
BTF-defined maps, it should be pretty easy to have declaration vs
definiton of maps. E.g.,

prog_a.c:

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 123);
    ... and so on, complete definition
} my_map SEC(".maps");

prog_b.c:

extern struct {
    ... here we can discuss which pieces are necessary/allowed,
potentially all (and they all should match, of course) ...
} my_map SEC(".maps");

prog_b.c won't create a new map, it will just use my_map from prog_a.c.

I might be missing something else as well, but those are the top things, IMO.

I hope this is helpful.

>  tools/lib/bpf/btf.c    |   10 +-
>  tools/lib/bpf/libbpf.c |  268 +++++++++++++++++++++++++++++++++++++++---------
>  tools/lib/bpf/libbpf.h |   17 +++
>  3 files changed, 244 insertions(+), 51 deletions(-)
>

[...]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking
  2019-12-20  0:02   ` Andrii Nakryiko
@ 2019-12-20 10:47     ` Toke Høiland-Jørgensen
  2019-12-20 17:28       ` Andrii Nakryiko
  0 siblings, 1 reply; 13+ messages in thread
From: Toke Høiland-Jørgensen @ 2019-12-20 10:47 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Daniel Borkmann, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jesper Dangaard Brouer, David Miller, Networking,
	bpf

Andrii Nakryiko <andrii.nakryiko@gmail.com> writes:

> On Thu, Dec 19, 2019 at 6:29 AM Toke Høiland-Jørgensen <toke@redhat.com> wrote:
>>
>> From: Toke Høiland-Jørgensen <toke@redhat.com>
>>
>> This adds support for resolving function externs to libbpf, with a new API
>> to resolve external function calls by static linking at load-time. The API
>> for this requires the caller to supply the object files containing the
>> target functions, and to specify an explicit mapping between extern
>> function names in the calling program, and function names in the target
>> object file. This is to support the XDP multi-prog case, where the
>> dispatcher program may not necessarily have control over function names in
>> the target programs, so simple function name resolution can't be used.
>>
>> The target object files must be loaded into the kernel before the calling
>> program, to ensure all relocations are done on the target functions, so we
>> can just copy over the instructions.
>>
>> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
>> ---
>
> A bunch of this code will change after you update to latest Clang with
> proper type info for extern functions. E.g., there shouldn't be any
> size/alignment for BTF_KIND_FUNC_PROTO, it's illegal. But that
> Yonghong already mentioned.

Yup, that fix should be helpful.

> As for the overall approach. I think doing static linking outside of
> bpf_object opening/loading is cleaner approach. If we introduce
> bpf_linker concept/object and have someting like
> bpf_linked__new(options) + a sequence of
> bpf_linker__add_object(bpf_object) + final bpf_linker__link(), which
> will produce usable bpf_object, as if bpf_object__open() was just
> called, it will be better and will allow quite a lot of flexibility in
> how we do things, without cluttering bpf_object API itself.

Hmm, that's not a bad idea, actually. To me it would make more sense
with an API like:

linker = bpf_linker__new(bpf_prog, opts); // start linking of bpf_prog
bpf_linker__resolve_func_static(linker, "func1", other_obj, "tgt_funcname");
bpf_linker__resolve_func_dynamic(linker, "func1", prog_fd);

new_obj = bpf_linker__finish();

I'll look into that when I pick this up again after the holidays :)

> Additionally, we can even have bpf_linker__write_file() to emit a
> final ELF file with statically linked object, which can then be loaded
> through bpf_object__open_file (we can do the same for in-memory
> buffer, of course). You can imagine LLC some day using libbpf to do
> actual linking of BPF .o files into a final BPF executable/object
> file, just like you expect it to do for non-BPF object files. WDYT?

Hmm, yeah, I don't see why we shouldn't be able to get there in the
future. Don't really have an opinion on whether it would be useful for
LLC to pull in the libbpf linker functions, though; maybe? :)

> Additionally, and seems you already realized that as well (judging by
> FIXMEs), we'll need to merge those individual objects' BTFs and
> deduplicate them, so that they form coherent set of types.

Yes, will have to look into this; any reason the existing de-duplication
code can't be reused here? I.e., could we just copy over all the BTF
info from the target object, and then run the de-duplication logic to
narrow it back down to one coherent set? Or would something different be
needed?

> Adjusting line info/func info is mandatory as well.

Yes, seems just copying it was not enough; will happily admit I was just
cargo-culting that bit ;) Guess I'll need to go figure out how line/func
info is actually supposed to work...

> Another thing we should think through is sharing maps. With
> BTF-defined maps, it should be pretty easy to have declaration vs
> definiton of maps. E.g.,
>
> prog_a.c:
>
> struct {
>     __uint(type, BPF_MAP_TYPE_ARRAY);
>     __uint(max_entries, 123);
>     ... and so on, complete definition
> } my_map SEC(".maps");
>
> prog_b.c:
>
> extern struct {
>     ... here we can discuss which pieces are necessary/allowed,
> potentially all (and they all should match, of course) ...
> } my_map SEC(".maps");
>
> prog_b.c won't create a new map, it will just use my_map from
> prog_a.c.

Ah, yes, that could be interesting. I guess we could use the same
"should I re-use" logic as we're doing for pinning map reuse (and
augment that to consider BTF as well in the process).

Is the existing llvm support sufficient to just mark a map struct as
'extern', or would something new be needed? Would it be enough to just
augment the bpf_object__init_user_btf_maps() to look for extern symbols?

> I might be missing something else as well, but those are the top things, IMO.

Right; let's see if that is not enough to at least get to an MVP for
linking. We can always improve things later :)

> I hope this is helpful.

Certainly! Thanks for the feedback!

-Toke


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 1/3] libbpf: Add new bpf_object__load2() using new-style opts
  2019-12-19 23:50   ` Andrii Nakryiko
@ 2019-12-20 10:50     ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 13+ messages in thread
From: Toke Høiland-Jørgensen @ 2019-12-20 10:50 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Daniel Borkmann, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jesper Dangaard Brouer, David Miller, Networking,
	bpf

Andrii Nakryiko <andrii.nakryiko@gmail.com> writes:

> On Thu, Dec 19, 2019 at 6:29 AM Toke Høiland-Jørgensen <toke@redhat.com> wrote:
>>
>> From: Toke Høiland-Jørgensen <toke@redhat.com>
>>
>> Since we introduced DECLARE_LIBBPF_OPTS and related macros for declaring
>> function options, that is now the preferred way to extend APIs. Introduce a
>> variant of the bpf_object__load() function that uses this function, and
>> deprecate the _xattr variant. Since all the good function names were taken,
>> the new function is unimaginatively called bpf_object__load2().
>>
>> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
>> ---
>
> I've been thinking about options for load quite a bit lately, and I'm
> leaning towards an opinion, that bpf_object__load() shouldn't take any
> options, and all the various per-bpf_object options have to be
> specified in bpf_object_open_opts and stored, if necessary for
> load/attach phase. So I'd rather move target_btf_path and log_level to
> open_opts instead.

Hmm, yeah, don't really object to that. I do think the 'log_level' is a
bit of an odd parameter in any case, though. If I turn on verbose
logging using the log_level parameter, that won't affect the logging of
libbpf itself, which was certainly surprising to me when I first
discovered it. So maybe rename it when adding it as an open option
("verbose_verifier" or something along those lines?).

Anyhow, given your idea with having a separate bpf_linker__() type, this
is not really needed for linking in any case, so I'll just drop this
patch for now...

-Toke


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking
  2019-12-20 10:47     ` Toke Høiland-Jørgensen
@ 2019-12-20 17:28       ` Andrii Nakryiko
  0 siblings, 0 replies; 13+ messages in thread
From: Andrii Nakryiko @ 2019-12-20 17:28 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen
  Cc: Daniel Borkmann, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jesper Dangaard Brouer, David Miller, Networking,
	bpf

On Fri, Dec 20, 2019 at 2:47 AM Toke Høiland-Jørgensen <toke@redhat.com> wrote:
>
> Andrii Nakryiko <andrii.nakryiko@gmail.com> writes:
>
> > On Thu, Dec 19, 2019 at 6:29 AM Toke Høiland-Jørgensen <toke@redhat.com> wrote:
> >>
> >> From: Toke Høiland-Jørgensen <toke@redhat.com>
> >>
> >> This adds support for resolving function externs to libbpf, with a new API
> >> to resolve external function calls by static linking at load-time. The API
> >> for this requires the caller to supply the object files containing the
> >> target functions, and to specify an explicit mapping between extern
> >> function names in the calling program, and function names in the target
> >> object file. This is to support the XDP multi-prog case, where the
> >> dispatcher program may not necessarily have control over function names in
> >> the target programs, so simple function name resolution can't be used.
> >>
> >> The target object files must be loaded into the kernel before the calling
> >> program, to ensure all relocations are done on the target functions, so we
> >> can just copy over the instructions.
> >>
> >> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
> >> ---
> >
> > A bunch of this code will change after you update to latest Clang with
> > proper type info for extern functions. E.g., there shouldn't be any
> > size/alignment for BTF_KIND_FUNC_PROTO, it's illegal. But that
> > Yonghong already mentioned.
>
> Yup, that fix should be helpful.
>
> > As for the overall approach. I think doing static linking outside of
> > bpf_object opening/loading is cleaner approach. If we introduce
> > bpf_linker concept/object and have someting like
> > bpf_linked__new(options) + a sequence of
> > bpf_linker__add_object(bpf_object) + final bpf_linker__link(), which
> > will produce usable bpf_object, as if bpf_object__open() was just
> > called, it will be better and will allow quite a lot of flexibility in
> > how we do things, without cluttering bpf_object API itself.
>
> Hmm, that's not a bad idea, actually. To me it would make more sense
> with an API like:
>
> linker = bpf_linker__new(bpf_prog, opts); // start linking of bpf_prog

what's bpf_prog her? is it bpf_object or path to some .o file? Also,
for static linking that could be multiple .o files, which will be
merged, so we need to accomodate that.

> bpf_linker__resolve_func_static(linker, "func1", other_obj, "tgt_funcname");
> bpf_linker__resolve_func_dynamic(linker, "func1", prog_fd);

It seems like static and dynamic linking are a bit different, though,
so I wouldn't put both of them into bpf_linker. By analogy with
userspace code, there is llc, a static linker, and ld.so, dynamic
linker. They have very different sets of responsibilities and they are
different programs completely.

Static linker puts together inter-connected individual object files
into a single executable (or object file). The end result looks like a
single coherent object file/executable, is if it was all compiled from
single .c file.

Dynamic linker, though, takes already complete single executable, and
then resolves external references to libraries. In BPF case, those
libraries are some other bpf_objects (or maybe even kernel).

So what I'm saying, is that it seems cleaner to have bpf_linker, which
will do static linking only and produce final bpf_object. And then
bpf_object__load() will actually do dynamic linking resolution, based
on extern relocations.

But can you also elaborate why we need this mapping from symbol name
in one object into a completely different name in another object? It
seems to me that for static linking, those should unconditionally
match. Even for dynamic linking, I think we should ensure names match,
otherwise it becomes quite confusing. Do you think ensuring consistent
naming is going to be a big problem?


>
> new_obj = bpf_linker__finish();
>
> I'll look into that when I pick this up again after the holidays :)
>
> > Additionally, we can even have bpf_linker__write_file() to emit a
> > final ELF file with statically linked object, which can then be loaded
> > through bpf_object__open_file (we can do the same for in-memory
> > buffer, of course). You can imagine LLC some day using libbpf to do
> > actual linking of BPF .o files into a final BPF executable/object
> > file, just like you expect it to do for non-BPF object files. WDYT?
>
> Hmm, yeah, I don't see why we shouldn't be able to get there in the
> future. Don't really have an opinion on whether it would be useful for
> LLC to pull in the libbpf linker functions, though; maybe? :)
>
> > Additionally, and seems you already realized that as well (judging by
> > FIXMEs), we'll need to merge those individual objects' BTFs and
> > deduplicate them, so that they form coherent set of types.
>
> Yes, will have to look into this; any reason the existing de-duplication
> code can't be reused here? I.e., could we just copy over all the BTF
> info from the target object, and then run the de-duplication logic to
> narrow it back down to one coherent set? Or would something different be
> needed?
>

The algorithm itself will work, but there is a bunch of APIs and
plumbing to be added to support this sort of incremental
deduplication.

> > Adjusting line info/func info is mandatory as well.
>
> Yes, seems just copying it was not enough; will happily admit I was just
> cargo-culting that bit ;) Guess I'll need to go figure out how line/func
> info is actually supposed to work...
>
> > Another thing we should think through is sharing maps. With
> > BTF-defined maps, it should be pretty easy to have declaration vs
> > definiton of maps. E.g.,
> >
> > prog_a.c:
> >
> > struct {
> >     __uint(type, BPF_MAP_TYPE_ARRAY);
> >     __uint(max_entries, 123);
> >     ... and so on, complete definition
> > } my_map SEC(".maps");
> >
> > prog_b.c:
> >
> > extern struct {
> >     ... here we can discuss which pieces are necessary/allowed,
> > potentially all (and they all should match, of course) ...
> > } my_map SEC(".maps");
> >
> > prog_b.c won't create a new map, it will just use my_map from
> > prog_a.c.
>
> Ah, yes, that could be interesting. I guess we could use the same
> "should I re-use" logic as we're doing for pinning map reuse (and
> augment that to consider BTF as well in the process).
>
> Is the existing llvm support sufficient to just mark a map struct as
> 'extern', or would something new be needed? Would it be enough to just
> augment the bpf_object__init_user_btf_maps() to look for extern symbols?

should be, yeah. We have type information for externs, so that should
capture all the info.
>
> > I might be missing something else as well, but those are the top things, IMO.
>
> Right; let's see if that is not enough to at least get to an MVP for
> linking. We can always improve things later :)
>
> > I hope this is helpful.
>
> Certainly! Thanks for the feedback!

cool, glad it helped

>
> -Toke
>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls
  2019-12-19 14:29 [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Toke Høiland-Jørgensen
                   ` (2 preceding siblings ...)
  2019-12-19 14:29 ` [PATCH RFC bpf-next 3/3] selftests/bpf: Add selftest for XDP multiprogs Toke Høiland-Jørgensen
@ 2019-12-20 20:30 ` Alexei Starovoitov
  2019-12-21 16:24   ` Toke Høiland-Jørgensen
  3 siblings, 1 reply; 13+ messages in thread
From: Alexei Starovoitov @ 2019-12-20 20:30 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen
  Cc: Daniel Borkmann, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jesper Dangaard Brouer, Andrii Nakryiko,
	David Miller, netdev, bpf

On Thu, Dec 19, 2019 at 03:29:30PM +0100, Toke Høiland-Jørgensen wrote:
> This series adds support for resolving function calls to functions marked as
> 'extern' in eBPF source files, by resolving the function call targets at load
> time. For now, this only works by static linking (i.e., copying over the
> instructions from the function target. Once the kernel support for dynamic
> linking lands, support can be added for having a function target be an already
> loaded program fd instead of a bpf object.
> 
> The API I'm proposing for this is that the caller specifies an explicit mapping
> between extern function names and function names in the target object file.
> This is to support the XDP multi-prog case, where the dispatcher program may not
> necessarily have control over function names in the target programs, so simple
> function name resolution can't be used.

I think simple name resolution should be the default behavior for both static
and dynamic linking. That's the part where I think we must not reinvent the wheel.
When one .c has
extern int prog1(struct xdp_md *ctx);
another .c should have:
int prog1(struct xdp_md *ctx) {...}
Both static and dynamic linking should link these two .c together without any
extra steps from the user. It's expected behavior that any C user assumes and
it should 'just work'.

Where we need to be creative is how plug two xdp firewalls with arbitrary
program names (including the same names) into common roolet.

One firewall can be:
noinline int foo(struct xdp_md *ctx)
{ // some logic
}
SEC("xdp")
int xdp_prog1(struct xdp_md *ctx)
{
       return foo(ctx);
}

And another firewall:
noinline int foo(struct xdp_md *ctx)
{ // some other logic
}
SEC("xdp")
int xdp_prog2(struct xdp_md *ctx)
{
       return foo(ctx);
}

Both xdp programs (with multiple functions) need to be connected into:

__weak noinline int dummy1(struct xdp_md *ctx) { return XDP_PASS; }
__weak noinline int dummy2(struct xdp_md *ctx) { return XDP_PASS; }

SEC("xdp")
int rootlet(struct xdp_md *ctx)
{
        int ret;

        ret = dummy1(ctx);
        if (ret != XDP_PASS)
                goto out;

        ret = dummy2(ctx);
        if (ret != XDP_DROP)
                goto out;
out:
        return ret;
}

where xdp_prog1() from 1st firewall needs to replace dummy1()
and xdp_prog2() from 2nd firewall needs to replaced dummy2().
Or the other way around depending on the order of installation.

At the kernel level the API is actually simple. It's the pair of
target_prog_fd + btf_id I described earlier in "static/dynamic linking" thread.
Where target_prog_fd is FD of loaded into kernel rootlet and
btf_id is BTF id of dummy1 or dummy2.

When 1st firewall is being loaded libbpf needs to pass target_prog_fd+btf_id
along with xdp_prog1() into the kernel, so that the verifier can do
appropriate checking and refcnting.

Note that the kernel and every program have their own BTF id space.
Their own BTF ids == their own names.
Loading two programs with exactly the same name is ok today and in the future.
Linking into other program name space is where we need to agree on naming first.

The static linking of two .o should follow familiar user space linker logic.
Proposed bpf_linker__..("first.o") and bpf_linker__..("second.o") should work.
Meaning that "extern int foo()" in "second.o" will get resolved with "int foo()"
from "first.o".
Dynamic linking is when "first.o" with "int foo()" was already loaded into
the kernel and "second.o" is loaded after. In such case its "extern int foo()"
will be resolved dynamically from previously loaded program.
The user space analogy of this behavior is glibc.
"first.o" is glibc.so that supplies memcpy() and friends.
"second.o" is some a.out that used "extern int memcpy()".

For XDP rootlet case already loaded weak function dummy[12]() need to
be replaced later by xdp_prog[12](). It's like replacing memcpy() in glibc.so.
I think the user space doesn't have such concepts. I was simply calling it
dynamic linking too, but it's not quite accurate. It's dynamically replacing
already loaded functions. Let's call it "dynamic re-linking" ?

As far as libbpf api for dynamic linking, so far I didn't need to add new stuff.
I'm trying to piggy back on fexit/fentry approach.

I think to prototype re-linking without kernel support. We can do static re-linking.
I think the best approach is to stick with name based resolution. libxdp can do:
- add alias("dummy1") to xdp_prog1() in first_firewall.o
- rename foo() in first_firewall.o into unique_foo().
- add alias("dummy2") to xdp_prog2() in second_firewall.o
- rename foo() in second_firewall.o into very_unique_foo().
- use standard static linking of first_firewall.o + second_firewall.o + rootlet.o

The static re-linking is more work than dynamic re-linking because it needs to
operate in a single name space of final .o. Whereas dynamic re-linking has
individual name space for every loaded into kernel program.
I'm hoping to share a prototype of dynamic re-linking soon.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls
  2019-12-20 20:30 ` [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Alexei Starovoitov
@ 2019-12-21 16:24   ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 13+ messages in thread
From: Toke Høiland-Jørgensen @ 2019-12-21 16:24 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Daniel Borkmann, Alexei Starovoitov, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jesper Dangaard Brouer, Andrii Nakryiko,
	David Miller, netdev, bpf

Alexei Starovoitov <alexei.starovoitov@gmail.com> writes:

> On Thu, Dec 19, 2019 at 03:29:30PM +0100, Toke Høiland-Jørgensen wrote:
>> This series adds support for resolving function calls to functions marked as
>> 'extern' in eBPF source files, by resolving the function call targets at load
>> time. For now, this only works by static linking (i.e., copying over the
>> instructions from the function target. Once the kernel support for dynamic
>> linking lands, support can be added for having a function target be an already
>> loaded program fd instead of a bpf object.
>> 
>> The API I'm proposing for this is that the caller specifies an explicit mapping
>> between extern function names and function names in the target object file.
>> This is to support the XDP multi-prog case, where the dispatcher program may not
>> necessarily have control over function names in the target programs, so simple
>> function name resolution can't be used.
>
> I think simple name resolution should be the default behavior for both static
> and dynamic linking. That's the part where I think we must not reinvent the wheel.
> When one .c has
> extern int prog1(struct xdp_md *ctx);
> another .c should have:
> int prog1(struct xdp_md *ctx) {...}
> Both static and dynamic linking should link these two .c together without any
> extra steps from the user. It's expected behavior that any C user assumes and
> it should 'just work'.

Sure, absolutely, when we can, we should just auto-resolve function
signatures and names...

> Where we need to be creative is how plug two xdp firewalls with arbitrary
> program names (including the same names) into common roolet.

...however, the "same name" issue is why I started down the path of
specifying links explicitly. I figure it will be somewhat common to have
to link in two independent XDP programs that both picked the same
function name (such as "xdp_main").

> One firewall can be:
> noinline int foo(struct xdp_md *ctx)
> { // some logic
> }
> SEC("xdp")
> int xdp_prog1(struct xdp_md *ctx)
> {
>        return foo(ctx);
> }
>
> And another firewall:
> noinline int foo(struct xdp_md *ctx)
> { // some other logic
> }
> SEC("xdp")
> int xdp_prog2(struct xdp_md *ctx)
> {
>        return foo(ctx);
> }
>
> Both xdp programs (with multiple functions) need to be connected into:
>
> __weak noinline int dummy1(struct xdp_md *ctx) { return XDP_PASS; }
> __weak noinline int dummy2(struct xdp_md *ctx) { return XDP_PASS; }
>
> SEC("xdp")
> int rootlet(struct xdp_md *ctx)
> {
>         int ret;
>
>         ret = dummy1(ctx);
>         if (ret != XDP_PASS)
>                 goto out;
>
>         ret = dummy2(ctx);
>         if (ret != XDP_DROP)
>                 goto out;
> out:
>         return ret;
> }
>
> where xdp_prog1() from 1st firewall needs to replace dummy1()
> and xdp_prog2() from 2nd firewall needs to replaced dummy2().
> Or the other way around depending on the order of installation.
>
> At the kernel level the API is actually simple. It's the pair of
> target_prog_fd + btf_id I described earlier in "static/dynamic linking" thread.
> Where target_prog_fd is FD of loaded into kernel rootlet and
> btf_id is BTF id of dummy1 or dummy2.

Ah, right; I was thinking it would need a name, but I agree that btf_id
is better.

> When 1st firewall is being loaded libbpf needs to pass target_prog_fd+btf_id
> along with xdp_prog1() into the kernel, so that the verifier can do
> appropriate checking and refcnting.
>
> Note that the kernel and every program have their own BTF id space.
> Their own BTF ids == their own names.
> Loading two programs with exactly the same name is ok today and in the future.
> Linking into other program name space is where we need to agree on naming first.
>
> The static linking of two .o should follow familiar user space linker logic.
> Proposed bpf_linker__..("first.o") and bpf_linker__..("second.o") should work.
> Meaning that "extern int foo()" in "second.o" will get resolved with "int foo()"
> from "first.o".
> Dynamic linking is when "first.o" with "int foo()" was already loaded into
> the kernel and "second.o" is loaded after. In such case its "extern int foo()"
> will be resolved dynamically from previously loaded program.
> The user space analogy of this behavior is glibc.
> "first.o" is glibc.so that supplies memcpy() and friends.
> "second.o" is some a.out that used "extern int memcpy()".

Right, this makes sense. Are you proposing that the kernel does this
without any intervention from libbpf when the BTF indicates it has an
extern KIND_FUNC_PROTO? What about overriding the names (dynamically
linking against two programs with identical function names)?

> For XDP rootlet case already loaded weak function dummy[12]() need to
> be replaced later by xdp_prog[12](). It's like replacing memcpy() in glibc.so.
> I think the user space doesn't have such concepts. I was simply calling it
> dynamic linking too, but it's not quite accurate. It's dynamically replacing
> already loaded functions. Let's call it "dynamic re-linking" ?

I guess it's kinda akin to LD_PRELOAD? But I'm fine with calling it by a
separate name.

> As far as libbpf api for dynamic linking, so far I didn't need to add new stuff.
> I'm trying to piggy back on fexit/fentry approach.

Cool :)

> I think to prototype re-linking without kernel support. We can do static re-linking.
> I think the best approach is to stick with name based resolution. libxdp can do:
> - add alias("dummy1") to xdp_prog1() in first_firewall.o
> - rename foo() in first_firewall.o into unique_foo().
> - add alias("dummy2") to xdp_prog2() in second_firewall.o
> - rename foo() in second_firewall.o into very_unique_foo().
> - use standard static linking of first_firewall.o + second_firewall.o + rootlet.o

The alias() would be a BTF annotation? Or something else?

> The static re-linking is more work than dynamic re-linking because it needs to
> operate in a single name space of final .o. Whereas dynamic re-linking has
> individual name space for every loaded into kernel program.
> I'm hoping to share a prototype of dynamic re-linking soon.

Excellent! At the rate you're going, you'll have the dynamic re-linking
working before I get static linking done :)

-Toke


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2019-12-21 16:24 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-19 14:29 [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Toke Høiland-Jørgensen
2019-12-19 14:29 ` [PATCH RFC bpf-next 1/3] libbpf: Add new bpf_object__load2() using new-style opts Toke Høiland-Jørgensen
2019-12-19 23:50   ` Andrii Nakryiko
2019-12-20 10:50     ` Toke Høiland-Jørgensen
2019-12-19 14:29 ` [PATCH RFC bpf-next 2/3] libbpf: Handle function externs and support static linking Toke Høiland-Jørgensen
2019-12-19 16:24   ` Yonghong Song
2019-12-19 16:59     ` Toke Høiland-Jørgensen
2019-12-20  0:02   ` Andrii Nakryiko
2019-12-20 10:47     ` Toke Høiland-Jørgensen
2019-12-20 17:28       ` Andrii Nakryiko
2019-12-19 14:29 ` [PATCH RFC bpf-next 3/3] selftests/bpf: Add selftest for XDP multiprogs Toke Høiland-Jørgensen
2019-12-20 20:30 ` [PATCH RFC bpf-next 0/3] libbpf: Add support for extern function calls Alexei Starovoitov
2019-12-21 16:24   ` Toke Høiland-Jørgensen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).