From: Andrii Nakryiko <andrii@kernel.org>
To: <bpf@vger.kernel.org>, <netdev@vger.kernel.org>, <ast@fb.com>,
<daniel@iogearbox.net>
Cc: <andrii@kernel.org>, <kernel-team@fb.com>
Subject: [PATCH bpf-next 12/17] libbpf: support extern resolution for BTF-defined maps in .maps section
Date: Wed, 14 Apr 2021 13:01:41 -0700 [thread overview]
Message-ID: <20210414200146.2663044-13-andrii@kernel.org> (raw)
In-Reply-To: <20210414200146.2663044-1-andrii@kernel.org>
Add extra logic to handle map externs (only BTF-defined maps are supported for
linking). Re-use the map parsing logic used during bpf_object__open(). Map
externs are currently restricted to always and only specify map type, key
type and/or size, and value type and/or size. Nothing extra is allowed. If any
of those attributes are mismatched between extern and actual map definition,
linker will report an error.
The original intent was to allow for extern to specify attributes that matters
(to user) to enforce. E.g., if you specify just key information and omit
value, then any value fits. Similarly, it should have been possible to enforce
map_flags, pinning, and any other possible map attribute. Unfortunately, that
means that multiple externs can be only partially overlapping with each other,
which means linker would need to combine their type definitions to end up with
the most restrictive and fullest map definition. This requires an extra amount
of BTF manipulation which at this time was deemed unnecessary and would
require further extending generic BTF writer APIs. So that is left for future
follow ups, if there will be demand for that. But the idea seems intresting
and useful, so I want to document it here.
Otherwise extern maps behave intuitively, just like extern vars and funcs.
Weak definitions are also supported.
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
tools/lib/bpf/linker.c | 167 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 167 insertions(+)
diff --git a/tools/lib/bpf/linker.c b/tools/lib/bpf/linker.c
index 7f9b91760462..9432c125fa43 100644
--- a/tools/lib/bpf/linker.c
+++ b/tools/lib/bpf/linker.c
@@ -1467,6 +1467,169 @@ static bool glob_sym_btf_matches(const char *sym_name, bool exact,
}
}
+static bool map_defs_match(const char *sym_name, bool full_match,
+ const struct btf *main_btf,
+ const struct btf_map_def *main_def,
+ const struct btf_map_def *main_inner_def,
+ const struct btf *extra_btf,
+ const struct btf_map_def *extra_def,
+ const struct btf_map_def *extra_inner_def)
+{
+ const char *reason;
+
+ if (main_def->map_type != extra_def->map_type) {
+ reason = "type";
+ goto mismatch;
+ }
+
+ /* check key type/size match */
+ if (main_def->key_size != extra_def->key_size) {
+ reason = "key_size";
+ goto mismatch;
+ }
+ if (!!main_def->key_type_id != !!extra_def->key_type_id) {
+ reason = "key type";
+ goto mismatch;
+ }
+ if ((main_def->parts & MAP_DEF_KEY_TYPE)
+ && !glob_sym_btf_matches(sym_name, true /*exact*/,
+ main_btf, main_def->key_type_id,
+ extra_btf, extra_def->key_type_id)) {
+ reason = "key type";
+ goto mismatch;
+ }
+
+ /* validate value type/size match */
+ if (main_def->value_size != extra_def->value_size) {
+ reason = "value_size";
+ goto mismatch;
+ }
+ if (!!main_def->value_type_id != !!extra_def->value_type_id) {
+ reason = "value type";
+ goto mismatch;
+ }
+ if ((main_def->parts & MAP_DEF_VALUE_TYPE)
+ && !glob_sym_btf_matches(sym_name, true /*exact*/,
+ main_btf, main_def->value_type_id,
+ extra_btf, extra_def->value_type_id)) {
+ reason = "key type";
+ goto mismatch;
+ }
+
+ /* when having non-extern and extern, we don't compare the rest,
+ * because externs are currently enforced to only specify map type,
+ * key, and value info
+ */
+ if (!full_match)
+ return true;
+
+ if (main_def->max_entries != extra_def->max_entries) {
+ reason = "max_entries";
+ goto mismatch;
+ }
+ if (main_def->map_flags != extra_def->map_flags) {
+ reason = "map_flags";
+ goto mismatch;
+ }
+ if (main_def->numa_node != extra_def->numa_node) {
+ reason = "numa_node";
+ goto mismatch;
+ }
+ if (main_def->pinning != extra_def->pinning) {
+ reason = "pinning";
+ goto mismatch;
+ }
+
+ if ((main_def->parts & MAP_DEF_INNER_MAP) != (extra_def->parts & MAP_DEF_INNER_MAP)) {
+ reason = "inner map";
+ goto mismatch;
+ }
+
+ if (main_def->parts & MAP_DEF_INNER_MAP) {
+ char inner_map_name[128];
+
+ snprintf(inner_map_name, sizeof(inner_map_name), "%s.inner", sym_name);
+
+ return map_defs_match(inner_map_name, true /*full_match*/,
+ main_btf, main_inner_def, NULL,
+ extra_btf, extra_inner_def, NULL);
+ }
+
+ return true;
+
+mismatch:
+ pr_warn("global '%s': map %s mismatch\n", sym_name, reason);
+ return false;
+}
+
+#define MAP_DEF_EXTERN_PARTS (MAP_DEF_MAP_TYPE \
+ | MAP_DEF_KEY_SIZE | MAP_DEF_KEY_TYPE \
+ | MAP_DEF_VALUE_SIZE | MAP_DEF_VALUE_TYPE)
+
+static bool glob_map_defs_match(const char *sym_name,
+ struct bpf_linker *linker, struct glob_sym *glob_sym,
+ struct src_obj *obj, Elf64_Sym *sym, int btf_id)
+{
+ bool sym_is_extern = sym->st_shndx == SHN_UNDEF;
+ struct btf_map_def dst_def = {}, dst_inner_def = {};
+ struct btf_map_def src_def = {}, src_inner_def = {};
+ const struct btf_type *t;
+ int err;
+
+ t = btf__type_by_id(obj->btf, btf_id);
+ if (!btf_is_var(t)) {
+ pr_warn("global '%s': invalid map definition type [%d]\n", sym_name, btf_id);
+ return false;
+ }
+ t = skip_mods_and_typedefs(obj->btf, t->type, NULL);
+
+ err = parse_btf_map_def(sym_name, obj->btf, t, true /*strict*/, &src_def, &src_inner_def);
+ if (err) {
+ pr_warn("global '%s': invalid map definition\n", sym_name);
+ return false;
+ }
+
+ /* We restict extern map defs to only specify map type and key/value
+ * type or size. Inner map definitions are prohibited for now as well.
+ */
+ if (sym_is_extern && (src_def.parts & ~MAP_DEF_EXTERN_PARTS)) {
+ pr_warn("global '%s': extern map can specify only map type and key/value info\n",
+ sym_name);
+ return false;
+ }
+
+ /* re-parse existing map definition */
+ t = btf__type_by_id(linker->btf, glob_sym->btf_id);
+ t = skip_mods_and_typedefs(linker->btf, t->type, NULL);
+ err = parse_btf_map_def(sym_name, linker->btf, t, true /*strict*/, &dst_def, &dst_inner_def);
+ if (err) {
+ /* this should not happen, because we already validated it */
+ pr_warn("global '%s': invalid dst map definition\n", sym_name);
+ return false;
+ }
+
+ if (glob_sym->is_extern != sym_is_extern) {
+ /* extern map def should be a subset of non-extern one */
+ if (sym_is_extern)
+ /* existing map def is the main one */
+ return map_defs_match(sym_name, false /*full_match*/,
+ linker->btf, &dst_def, &dst_inner_def,
+ obj->btf, &src_def, &src_inner_def);
+ else
+ /* new map def is the main one */
+ return map_defs_match(sym_name, false /*full_match*/,
+ obj->btf, &src_def, &src_inner_def,
+ linker->btf, &dst_def, &dst_inner_def);
+ } else {
+ /* map defs should match exactly regardless of extern/extern
+ * or non-extern/non-extern case
+ */
+ return map_defs_match(sym_name, true /*full_match*/,
+ linker->btf, &dst_def, &dst_inner_def,
+ obj->btf, &src_def, &src_inner_def);
+ }
+}
+
static bool glob_syms_match(const char *sym_name,
struct bpf_linker *linker, struct glob_sym *glob_sym,
struct src_obj *obj, Elf64_Sym *sym, size_t sym_idx, int btf_id)
@@ -1488,6 +1651,10 @@ static bool glob_syms_match(const char *sym_name,
return false;
}
+ /* deal with .maps definitions specially */
+ if (glob_sym->sec_id && strcmp(linker->secs[glob_sym->sec_id].sec_name, MAPS_ELF_SEC) == 0)
+ return glob_map_defs_match(sym_name, linker, glob_sym, obj, sym, btf_id);
+
if (!glob_sym_btf_matches(sym_name, true /*exact*/,
linker->btf, glob_sym->btf_id, obj->btf, btf_id))
return false;
--
2.30.2
next prev parent reply other threads:[~2021-04-14 20:02 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-04-14 20:01 [PATCH bpf-next 00/17] BPF static linker: support externs Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 01/17] bpftool: support dumping BTF VAR's "extern" linkage Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 02/17] bpftool: dump more info about DATASEC members Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 03/17] libbpf: suppress compiler warning when using SEC() macro with externs Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 04/17] libbpf: mark BPF subprogs with hidden visibility as static for BPF verifier Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 05/17] libbpf: allow gaps in BPF program sections to support overriden weak functions Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 06/17] libbpf: refactor BTF map definition parsing Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 07/17] libbpf: factor out symtab and relos sanity checks Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 08/17] libbpf: make few internal helpers available outside of libbpf.c Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 09/17] libbpf: extend sanity checking ELF symbols with externs validation Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 10/17] libbpf: tighten BTF type ID rewriting with error checking Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 11/17] libbpf: add linker extern resolution support for functions and global variables Andrii Nakryiko
2021-04-14 20:01 ` Andrii Nakryiko [this message]
2021-04-14 22:00 ` [PATCH bpf-next 12/17] libbpf: support extern resolution for BTF-defined maps in .maps section Alexei Starovoitov
2021-04-14 23:48 ` Andrii Nakryiko
2021-04-15 2:01 ` Alexei Starovoitov
2021-04-15 20:35 ` Andrii Nakryiko
2021-04-15 20:57 ` Alexei Starovoitov
2021-04-14 20:01 ` [PATCH bpf-next 13/17] selftests/bpf: use -O0 instead of -Og in selftests builds Andrii Nakryiko
2021-04-14 22:02 ` Alexei Starovoitov
2021-04-14 22:15 ` David Laight
2021-04-15 0:03 ` Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 14/17] selftests/bpf: omit skeleton generation for multi-linked BPF object files Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 15/17] selftests/bpf: add function linking selftest Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 16/17] selftests/bpf: add global variables " Andrii Nakryiko
2021-04-14 20:01 ` [PATCH bpf-next 17/17] sleftests/bpf: add map " Andrii Nakryiko
2021-04-15 0:21 ` [PATCH bpf-next 00/17] BPF static linker: support externs Andrii Nakryiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210414200146.2663044-13-andrii@kernel.org \
--to=andrii@kernel.org \
--cc=ast@fb.com \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@fb.com \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).