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 10/14] bpf: Implement BTF fixup functionality
Date: Thu, 9 Jan 2025 13:43:52 -0800 [thread overview]
Message-ID: <20250109214617.485144-11-bboscaccy@linux.microsoft.com> (raw)
In-Reply-To: <20250109214617.485144-1-bboscaccy@linux.microsoft.com>
This code heavily borrows from bpf_object_fixup_btf. There are certain
things that clang doesn't quite handle properly for our needs, mostly
related to zeroed sizes and offsets.
Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
kernel/bpf/syscall.c | 189 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 189 insertions(+)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 51b14cb9c4ca1..f47e95c1ab975 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6578,6 +6578,191 @@ static int collect_externs(struct bpf_obj *obj)
return 0;
}
+static int compare_vsi_off(const void *_a, const void *_b)
+{
+ const struct btf_var_secinfo *a = _a;
+ const struct btf_var_secinfo *b = _b;
+
+ return a->offset - b->offset;
+}
+
+static Elf_Shdr *elf_sec_by_name(const struct bpf_obj *obj, const char *name)
+{
+ unsigned int i;
+ Elf_Shdr *shdr;
+
+ for (i = 1; i < obj->hdr->e_shnum; i++) {
+ shdr = &obj->sechdrs[i];
+ if (strcmp(name, obj->secstrings + shdr->sh_name) == 0)
+ return shdr;
+ }
+ return NULL;
+}
+
+static int find_elf_sec_sz(const struct bpf_obj *obj, const char *name, u32 *size)
+{
+ Elf_Shdr *scn;
+
+ if (!name)
+ return -EINVAL;
+
+ scn = elf_sec_by_name(obj, name);
+ if (scn) {
+ *size = scn->sh_size;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static Elf64_Sym *find_elf_var_sym(const struct bpf_obj *obj, const char *name)
+{
+ unsigned int i;
+ Elf_Shdr *symsec = &obj->sechdrs[obj->index.sym];
+ Elf_Sym *sym = (void *)obj->hdr + symsec->sh_offset;
+
+ for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) {
+ if (ELF64_ST_TYPE(sym[i].st_info) != STT_OBJECT)
+ continue;
+
+ if (ELF64_ST_BIND(sym[i].st_info) != STB_GLOBAL &&
+ ELF64_ST_BIND(sym[i].st_info) != STB_WEAK)
+ continue;
+
+ if (strcmp(name, obj->strtab + sym[i].st_name) == 0)
+ return &sym[i];
+
+ }
+ return ERR_PTR(-ENOENT);
+}
+
+#define ELF64_ST_VISIBILITY(o) ((o) & 0x03)
+
+/* Symbol visibility specification encoded in the st_other field. */
+#define STV_DEFAULT 0 /* Default symbol visibility rules */
+#define STV_INTERNAL 1 /* Processor specific hidden class */
+#define STV_HIDDEN 2 /* Sym unavailable in other modules */
+#define STV_PROTECTED 3 /* Not preemptible, not exported */
+
+static int btf_fixup_datasec(struct bpf_obj *obj, struct btf *btf,
+ struct btf_type *t)
+{
+ __u32 size = 0, i, vars = btf_vlen(t);
+ const char *sec_name = btf_str_by_offset(btf, t->name_off);
+ struct btf_var_secinfo *vsi;
+ bool fixup_offsets = false;
+ int err;
+
+ if (!sec_name) {
+ pr_debug("No name found in string section for DATASEC kind.\n");
+ return -ENOENT;
+ }
+
+ /* Extern-backing datasecs (.ksyms, .kconfig) have their size and
+ * variable offsets set at the previous step. Further, not every
+ * extern BTF VAR has corresponding ELF symbol preserved, so we skip
+ * all fixups altogether for such sections and go straight to sorting
+ * VARs within their DATASEC.
+ */
+ if (strcmp(sec_name, ".kconfig") == 0 || strcmp(sec_name, ".ksyms") == 0)
+ goto sort_vars;
+
+ /* Clang leaves DATASEC size and VAR offsets as zeroes, so we need to
+ * fix this up. But BPF static linker already fixes this up and fills
+ * all the sizes and offsets during static linking. So this step has
+ * to be optional. But the STV_HIDDEN handling is non-optional for any
+ * non-extern DATASEC, so the variable fixup loop below handles both
+ * functions at the same time, paying the cost of BTF VAR <-> ELF
+ * symbol matching just once.
+ */
+ if (t->size == 0) {
+ err = find_elf_sec_sz(obj, sec_name, &size);
+ if (err || !size) {
+ pr_debug("sec '%s': failed to determine size from ELF: size %u, err %d\n",
+ sec_name, size, err);
+ return -ENOENT;
+ }
+
+ t->size = size;
+ fixup_offsets = true;
+ }
+
+ for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) {
+ const struct btf_type *t_var;
+ struct btf_var *var;
+ const char *var_name;
+ Elf64_Sym *sym;
+
+ t_var = btf_type_by_id(btf, vsi->type);
+ if (!t_var || !(btf_kind(t_var) == BTF_KIND_VAR)) {
+ pr_debug("sec '%s': unexpected non-VAR type found\n", sec_name);
+ return -EINVAL;
+ }
+
+ var = btf_var(t_var);
+ if (var->linkage == BTF_VAR_STATIC || var->linkage == BTF_VAR_GLOBAL_EXTERN)
+ continue;
+
+ var_name = btf_str_by_offset(btf, t_var->name_off);
+ if (!var_name) {
+ pr_debug("sec '%s': failed to find name of DATASEC's member #%d\n",
+ sec_name, i);
+ return -ENOENT;
+ }
+
+ sym = find_elf_var_sym(obj, var_name);
+ if (IS_ERR(sym)) {
+ pr_debug("sec '%s': failed to find ELF symbol for VAR '%s'\n",
+ sec_name, var_name);
+ return -ENOENT;
+ }
+
+ if (fixup_offsets)
+ vsi->offset = sym->st_value;
+
+ /* if variable is a global/weak symbol, but has restricted
+ * (STV_HIDDEN or STV_INTERNAL) visibility, mark its BTF VAR
+ * as static. This follows similar logic for functions (BPF
+ * subprogs) and influences libbpf's further decisions about
+ * whether to make global data BPF array maps as
+ * BPF_F_MMAPABLE.
+ */
+ if (ELF64_ST_VISIBILITY(sym->st_other) == STV_HIDDEN
+ || ELF64_ST_VISIBILITY(sym->st_other) == STV_INTERNAL)
+ var->linkage = BTF_VAR_STATIC;
+ }
+
+sort_vars:
+ sort(btf_var_secinfos(t), vars, sizeof(*vsi), compare_vsi_off, NULL);
+ return 0;
+}
+
+static int fixup_btf(struct bpf_obj *obj)
+{
+ int i, n, err = 0;
+
+ if (!obj->btf)
+ return 0;
+
+ n = btf_type_cnt(obj->btf);
+ for (i = 1; i < n; i++) {
+ struct btf_type *t = (struct btf_type *)btf_type_by_id(obj->btf, i);
+
+ /* Loader needs to fix up some of the things compiler
+ * couldn't get its hands on while emitting BTF. This
+ * is section size and global variable offset. We use
+ * the info from the ELF itself for this purpose.
+ */
+ if (btf_kind(t) == BTF_KIND_DATASEC) {
+ err = btf_fixup_datasec(obj, obj->btf, t);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static void free_bpf_obj(struct bpf_obj *obj)
{
int i;
@@ -6817,6 +7002,10 @@ static int load_fd(union bpf_attr *attr)
if (err < 0)
goto free;
+ err = fixup_btf(obj);
+ if (err < 0)
+ goto free;
+
return obj_f;
free:
free_bpf_obj(obj);
--
2.47.1
next prev 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 ` [PATCH 09/14] bpf: Collect extern relocations Blaise Boscaccy
2025-01-11 1:35 ` kernel test robot
2025-01-09 21:43 ` Blaise Boscaccy [this message]
2025-01-11 3:19 ` [PATCH 10/14] bpf: Implement BTF fixup functionality 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-11-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 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).