All of lore.kernel.org
 help / color / mirror / Atom feed
From: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
To: bpf@vger.kernel.org
Cc: nkapron@google.com, teknoraver@meta.com,
	roberto.sassu@huawei.com, gregkh@linuxfoundation.org,
	paul@paul-moore.com, code@tyhicks.com,
	flaniel@linux.microsoft.com
Subject: [PATCH 09/14] bpf: Collect extern relocations
Date: Thu,  9 Jan 2025 13:43:51 -0800	[thread overview]
Message-ID: <20250109214617.485144-10-bboscaccy@linux.microsoft.com> (raw)
In-Reply-To: <20250109214617.485144-1-bboscaccy@linux.microsoft.com>

This code heavily borrows from bpf_object__collect_externs in
libbpf. Here we walk the symbol table and attempt to determine which
symbols correspond to external relocations, specifically kconfig
options and kernel or module symbols.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 kernel/bpf/syscall.c | 337 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 337 insertions(+)

diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 03ab0bb7bf076..51b14cb9c4ca1 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6245,6 +6245,339 @@ static int elf_collect(struct bpf_obj *obj)
 	return err;
 }
 
+static enum bpf_kcfg_type find_kcfg_type(const struct btf *btf, int id,
+				     bool *is_signed)
+{
+	const struct btf_type *t;
+	const char *name;
+
+	t = skip_mods_and_typedefs(btf, id, NULL);
+	name = btf_str_by_offset(btf, t->name_off);
+
+	if (is_signed)
+		*is_signed = false;
+	switch (btf_kind(t)) {
+	case BTF_KIND_INT: {
+		int enc = btf_int_encoding(t);
+
+		if (enc & BTF_INT_BOOL)
+			return t->size == 1 ? KCFG_BOOL : KCFG_UNKNOWN;
+		if (is_signed)
+			*is_signed = enc & BTF_INT_SIGNED;
+		if (t->size == 1)
+			return KCFG_CHAR;
+		if (t->size < 1 || t->size > 8 || (t->size & (t->size - 1)))
+			return KCFG_UNKNOWN;
+		return KCFG_INT;
+	}
+	case BTF_KIND_ENUM:
+		if (t->size != 4)
+			return KCFG_UNKNOWN;
+		if (strcmp(name, "libbpf_tristate"))
+			return KCFG_UNKNOWN;
+		return KCFG_TRISTATE;
+	case BTF_KIND_ENUM64:
+		if (strcmp(name, "libbpf_tristate"))
+			return KCFG_UNKNOWN;
+		return KCFG_TRISTATE;
+	case BTF_KIND_ARRAY:
+		if (btf_array(t)->nelems == 0)
+			return KCFG_UNKNOWN;
+		if (find_kcfg_type(btf, btf_array(t)->type, NULL) != KCFG_CHAR)
+			return KCFG_UNKNOWN;
+		return KCFG_CHAR_ARR;
+	default:
+		return KCFG_UNKNOWN;
+	}
+}
+
+static int cmp_externs(const void *_a, const void *_b)
+{
+	const struct bpf_extern_desc *a = _a;
+	const struct bpf_extern_desc *b = _b;
+
+	if (a->type != b->type)
+		return a->type < b->type ? -1 : 1;
+
+	if (a->type == EXT_KCFG) {
+		/* descending order by alignment requirements */
+		if (a->kcfg.align != b->kcfg.align)
+			return a->kcfg.align > b->kcfg.align ? -1 : 1;
+		/* ascending order by size, within same alignment class */
+		if (a->kcfg.sz != b->kcfg.sz)
+			return a->kcfg.sz < b->kcfg.sz ? -1 : 1;
+	}
+
+	/* resolve ties by name */
+	return strcmp(a->name, b->name);
+}
+
+static int find_int_btf_id(const struct btf *btf)
+{
+	const struct btf_type *t;
+	int i, n;
+
+	n = btf_type_cnt(btf);
+	for (i = 1; i < n; i++) {
+		t = btf_type_by_id(btf, i);
+
+		if (btf_type_is_int(t) && btf_type_int_bits(t) == 32)
+			return i;
+	}
+	return 0;
+}
+
+static struct bpf_extern_desc *find_extern_by_name(const struct bpf_obj *obj,
+						   const void *name)
+{
+	int i;
+
+	for (i = 0; i < obj->nr_extern; i++) {
+		if (strcmp(obj->externs[i].name, name) == 0)
+			return &obj->externs[i];
+	}
+	return NULL;
+}
+
+static int add_dummy_ksym_var(struct btf *btf)
+{
+	int i, int_btf_id, sec_btf_id, dummy_var_btf_id;
+	const struct btf_var_secinfo *vs;
+	const struct btf_type *sec;
+
+	if (!btf)
+		return 0;
+
+	sec_btf_id = btf_find_by_name_kind(btf, ".ksyms",
+					   BTF_KIND_DATASEC);
+	if (sec_btf_id < 0)
+		return 0;
+
+	sec = btf_type_by_id(btf, sec_btf_id);
+	vs = btf_var_secinfos(sec);
+	for (i = 0; i < btf_vlen(sec); i++, vs++) {
+		const struct btf_type *vt;
+
+		vt = btf_type_by_id(btf, vs->type);
+		if (btf_type_is_func(vt))
+			break;
+	}
+
+	/* No func in ksyms sec.  No need to add dummy var. */
+	if (i == btf_vlen(sec))
+		return 0;
+
+	int_btf_id = find_int_btf_id(btf);
+
+	dummy_var_btf_id = btf_add_var(btf,
+				       sec->name_off,
+				       BTF_VAR_GLOBAL_ALLOCATED,
+				       int_btf_id);
+	if (dummy_var_btf_id < 0)
+		pr_warn("cannot create a dummy_ksym var\n");
+
+	return dummy_var_btf_id;
+}
+
+static int collect_externs(struct bpf_obj *obj)
+{
+	int i, n, off, dummy_var_btf_id;
+	Elf_Shdr *symsec = &obj->sechdrs[obj->index.sym];
+	Elf_Sym *sym = (void *)obj->hdr + symsec->sh_offset;
+	const char *ext_name;
+	const char *sec_name;
+	struct bpf_extern_desc *ext;
+	const struct btf_type *t;
+	size_t ext_essent_len;
+	struct btf_type *sec, *kcfg_sec = NULL, *ksym_sec = NULL;
+	int size;
+	int int_btf_id;
+	const struct btf_type *dummy_var;
+	struct btf_type *vt;
+	struct btf_var_secinfo *vs;
+	const struct btf_type *func_proto;
+	struct btf_param *param;
+	int j;
+
+	dummy_var_btf_id = add_dummy_ksym_var(obj->btf);
+	if (dummy_var_btf_id < 0)
+		return dummy_var_btf_id;
+
+	for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) {
+		if (!sym_is_extern(&sym[i]))
+			continue;
+
+		ext_name = obj->strtab + sym[i].st_name;
+		ext = krealloc_array(obj->externs,
+				     obj->nr_extern + 1,
+				     sizeof(struct bpf_extern_desc),
+				     GFP_KERNEL);
+		if (!ext)
+			return -ENOMEM;
+
+		obj->externs = ext;
+		ext = &ext[obj->nr_extern];
+		memset(ext, 0, sizeof(*ext));
+		obj->nr_extern++;
+
+		ext->btf_id = find_extern_btf_id(obj->btf, ext_name);
+		if (ext->btf_id <= 0)
+			return ext->btf_id;
+
+		t = btf_type_by_id(obj->btf, ext->btf_id);
+		ext->name = btf_str_by_offset(obj->btf, t->name_off);
+		ext->sym_idx = i;
+		ext->is_weak = ELF64_ST_BIND(sym->st_info) == STB_WEAK;
+
+		ext_essent_len = bpf_core_essential_name_len(ext->name);
+		ext->essent_name = NULL;
+		if (ext_essent_len != strlen(ext->name)) {
+			ext->essent_name = kstrndup(ext->name, ext_essent_len, GFP_KERNEL);
+			if (!ext->essent_name)
+				return -ENOMEM;
+		}
+
+		ext->sec_btf_id = find_extern_sec_btf_id(obj->btf, ext->btf_id);
+		if (ext->sec_btf_id <= 0) {
+			pr_warn("failed to find BTF for extern '%s' [%d] section: %d\n",
+				ext_name, ext->btf_id, ext->sec_btf_id);
+			return ext->sec_btf_id;
+		}
+
+		sec = (void *)btf_type_by_id(obj->btf, ext->sec_btf_id);
+		sec_name = btf_str_by_offset(obj->btf, sec->name_off);
+
+		if (strcmp(sec_name, ".kconfig") == 0) {
+			if (btf_type_is_func(t)) {
+				pr_warn("extern function %s is unsupported under .kconfig section\n",
+					ext->name);
+				return -EOPNOTSUPP;
+			}
+			kcfg_sec = sec;
+			ext->type = EXT_KCFG;
+
+			if (!btf_resolve_size(obj->btf, t, &size)) {
+				pr_warn("failed to resolve size of extern (kcfg) '%s': %d\n",
+					ext_name, ext->kcfg.sz);
+				return -EINVAL;
+			}
+			ext->kcfg.sz = size;
+			ext->kcfg.align = btf_align_of(obj->btf, t->type);
+			if (ext->kcfg.align <= 0) {
+				pr_warn("failed to determine alignment of extern (kcfg) '%s': %d\n",
+					ext_name, ext->kcfg.align);
+				return -EINVAL;
+			}
+			ext->kcfg.type = find_kcfg_type(obj->btf, t->type,
+							&ext->kcfg.is_signed);
+			if (ext->kcfg.type == KCFG_UNKNOWN) {
+				pr_warn("extern (kcfg) '%s': type is unsupported\n", ext_name);
+				return -EOPNOTSUPP;
+			}
+		} else if (strcmp(sec_name, ".ksyms") == 0) {
+			ksym_sec = sec;
+			ext->type = EXT_KSYM;
+			skip_mods_and_typedefs(obj->btf, t->type,
+					       &ext->ksym.type_id);
+		} else {
+			pr_warn("unrecognized extern section '%s'\n", sec_name);
+			return -EOPNOTSUPP;
+		}
+	}
+
+	sort(obj->externs, obj->nr_extern, sizeof(struct bpf_extern_desc),
+	     cmp_externs, NULL);
+
+	if (ksym_sec) {
+		/* find existing 4-byte integer type in BTF to use for fake
+		 * extern variables in DATASEC
+		 */
+		int_btf_id = find_int_btf_id(obj->btf);
+
+		/* For extern function, a dummy_var added earlier
+		 * will be used to replace the vs->type and
+		 * its name string will be used to refill
+		 * the missing param's name.
+		 */
+		dummy_var = btf_type_by_id(obj->btf, dummy_var_btf_id);
+		for (i = 0; i < obj->nr_extern; i++) {
+			ext = &obj->externs[i];
+			if (ext->type != EXT_KSYM)
+				continue;
+			pr_debug("extern (ksym) #%d: symbol %d, name %s\n",
+				 i, ext->sym_idx, ext->name);
+		}
+
+		sec = ksym_sec;
+		n = btf_vlen(sec);
+		for (i = 0, off = 0; i < n; i++, off += sizeof(int)) {
+			vs = btf_var_secinfos(sec) + i;
+			vt = (void *)btf_type_by_id(obj->btf, vs->type);
+			ext_name = btf_str_by_offset(obj->btf, vt->name_off);
+			ext = find_extern_by_name(obj, ext_name);
+			if (!ext) {
+				pr_warn("failed to find extern definition for BTF %s\n",
+					ext_name);
+				return -ESRCH;
+			}
+			if (btf_type_is_func(vt)) {
+				func_proto = btf_type_by_id(obj->btf,
+							    vt->type);
+				param = btf_params(func_proto);
+				/* Reuse the dummy_var string if the
+				 * func proto does not have param name.
+				 */
+				for (j = 0; j < btf_vlen(func_proto); j++)
+					if (param[j].type && !param[j].name_off)
+						param[j].name_off =
+							dummy_var->name_off;
+				vs->type = dummy_var_btf_id;
+				vt->info &= ~0xffff;
+				vt->info |= BTF_FUNC_GLOBAL;
+			} else {
+				btf_var(vt)->linkage = BTF_VAR_GLOBAL_ALLOCATED;
+				vt->type = int_btf_id;
+			}
+			vs->offset = off;
+			vs->size = sizeof(int);
+		}
+		sec->size = off;
+	}
+
+	if (kcfg_sec) {
+		sec = kcfg_sec;
+		/* for kcfg externs calculate their offsets within a .kconfig map */
+		off = 0;
+		for (i = 0; i < obj->nr_extern; i++) {
+			ext = &obj->externs[i];
+			if (ext->type != EXT_KCFG)
+				continue;
+
+			ext->kcfg.data_off = roundup(off, ext->kcfg.align);
+			off = ext->kcfg.data_off + ext->kcfg.sz;
+			pr_debug("extern (kcfg) #%d: symbol %d, off %u, name %s\n",
+				 i, ext->sym_idx, ext->kcfg.data_off, ext->name);
+		}
+		sec->size = off;
+		n = btf_vlen(sec);
+		for (i = 0; i < n; i++) {
+			vs = btf_var_secinfos(sec) + i;
+			t = btf_type_by_id(obj->btf, vs->type);
+			ext_name = btf_str_by_offset(obj->btf, t->name_off);
+
+			ext = find_extern_by_name(obj, ext_name);
+			if (!ext) {
+				pr_warn("failed to find extern definition for BTF var '%s'\n",
+					ext_name);
+				return -ESRCH;
+			}
+			btf_var(t)->linkage = BTF_VAR_GLOBAL_ALLOCATED;
+			vs->offset = ext->kcfg.data_off;
+		}
+	}
+	return 0;
+}
+
 static void free_bpf_obj(struct bpf_obj *obj)
 {
 	int i;
@@ -6480,6 +6813,10 @@ static int load_fd(union bpf_attr *attr)
 	if (err < 0)
 		goto free;
 
+	err = collect_externs(obj);
+	if (err < 0)
+		goto free;
+
 	return obj_f;
 free:
 	free_bpf_obj(obj);
-- 
2.47.1


  parent reply	other threads:[~2025-01-09 21:48 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-09 21:43 [POC][RFC][PATCH] bpf: in-kernel bpf relocations on raw elf files Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 01/14] bpf: Port prerequiste BTF handling functions from userspace Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 02/14] bpf: Add data structures for managing in-kernel eBPF relocations Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 03/14] bpf: Port .btf.ext parsing functions from userspace Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 04/14] bpf: Port elf and btf utility helper " Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 05/14] fs/kernel_read_file: Add an eBPF specifier to kernel_read_file Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 06/14] bpf: Add BPF_LOAD_FD subcommand Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 07/14] bpf: Implement BPF_LOAD_FD subcommand handler Blaise Boscaccy
2025-01-10  6:05   ` Greg KH
2025-01-10 22:41     ` Blaise Boscaccy
2025-01-11  0:41   ` kernel test robot
2025-01-09 21:43 ` [PATCH 08/14] bpf: Add elf parsing support to the BPF_LOAD_FD subcommand Blaise Boscaccy
2025-01-09 21:43 ` Blaise Boscaccy [this message]
2025-01-11  1:35   ` [PATCH 09/14] bpf: Collect extern relocations kernel test robot
2025-01-09 21:43 ` [PATCH 10/14] bpf: Implement BTF fixup functionality Blaise Boscaccy
2025-01-11  3:19   ` kernel test robot
2025-01-09 21:43 ` [PATCH 11/14] bpf: Implement relocation collection Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 12/14] bpf: Resolve external relocations Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 13/14] bpf: Apply in-kernel bpf instruction relocations Blaise Boscaccy
2025-01-09 21:43 ` [PATCH 14/14] bpf: Augment BPF_PROG_LOAD to use in-kernel relocations Blaise Boscaccy
2025-01-10 18:40 ` [POC][RFC][PATCH] bpf: in-kernel bpf relocations on raw elf files Alexei Starovoitov
2025-01-10 23:27   ` Blaise Boscaccy
2025-01-13 17:54     ` Alexei Starovoitov
2025-01-14 18:24       ` Blaise Boscaccy
2025-01-24  5:08         ` bpf signing. " Alexei Starovoitov
2025-01-24  7:05           ` John Fastabend
2025-01-28 22:32             ` Blaise Boscaccy
2025-01-30  1:13 ` Cong Wang
2025-01-30 19:22   ` Blaise Boscaccy
2025-02-01 22:24     ` Cong Wang

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=20250109214617.485144-10-bboscaccy@linux.microsoft.com \
    --to=bboscaccy@linux.microsoft.com \
    --cc=bpf@vger.kernel.org \
    --cc=code@tyhicks.com \
    --cc=flaniel@linux.microsoft.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=nkapron@google.com \
    --cc=paul@paul-moore.com \
    --cc=roberto.sassu@huawei.com \
    --cc=teknoraver@meta.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.