diff mbox series

[08/14] bpf: Add elf parsing support to the BPF_LOAD_FD subcommand

Message ID 20250109214617.485144-9-bboscaccy@linux.microsoft.com (mailing list archive)
State New
Delegated to: BPF
Headers show
Series [01/14] bpf: Port prerequiste BTF handling functions from userspace | expand

Checks

Context Check Description
netdev/series_format warning Series does not have a cover letter
netdev/tree_selection success Guessed tree name to be net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 9 this patch: 7
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers fail 12 maintainers not CCed: jolsa@kernel.org john.fastabend@gmail.com ast@kernel.org daniel@iogearbox.net martin.lau@linux.dev yonghong.song@linux.dev eddyz87@gmail.com andrii@kernel.org song@kernel.org sdf@fomichev.me kpsingh@kernel.org haoluo@google.com
netdev/build_clang success Errors and warnings before: 11 this patch: 9
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 9 this patch: 7
netdev/checkpatch warning CHECK: Blank lines aren't necessary before a close brace '}' WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-net-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-net-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-net-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-net-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-net-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-net-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-10 success Logs for aarch64-gcc / veristat-kernel
bpf/vmtest-bpf-net-VM_Test-11 success Logs for aarch64-gcc / veristat-meta
bpf/vmtest-bpf-net-VM_Test-13 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-net-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-12 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-net-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-net-VM_Test-17 success Logs for s390x-gcc / veristat-kernel
bpf/vmtest-bpf-net-VM_Test-18 success Logs for s390x-gcc / veristat-meta
bpf/vmtest-bpf-net-VM_Test-19 success Logs for set-matrix
bpf/vmtest-bpf-net-VM_Test-20 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-21 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-net-VM_Test-30 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-31 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-net-VM_Test-36 success Logs for x86_64-llvm-17 / veristat-kernel
bpf/vmtest-bpf-net-VM_Test-37 success Logs for x86_64-llvm-17 / veristat-meta
bpf/vmtest-bpf-net-VM_Test-38 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-net-VM_Test-39 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-net-VM_Test-45 success Logs for x86_64-llvm-18 / veristat-kernel
bpf/vmtest-bpf-net-VM_Test-46 success Logs for x86_64-llvm-18 / veristat-meta
bpf/vmtest-bpf-net-VM_Test-7 fail Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-8 fail Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-net-VM_Test-15 fail Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-net-VM_Test-35 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-14 fail Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-net-VM_Test-22 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-23 fail Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-24 fail Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-26 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-27 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-net-VM_Test-28 success Logs for x86_64-gcc / veristat-kernel / x86_64-gcc veristat_kernel
bpf/vmtest-bpf-net-VM_Test-29 success Logs for x86_64-gcc / veristat-meta / x86_64-gcc veristat_meta
bpf/vmtest-bpf-net-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-33 fail Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-34 fail Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-net-VM_Test-44 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-net-PR fail PR summary
bpf/vmtest-bpf-net-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-net-VM_Test-41 fail Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-net-VM_Test-42 fail Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-net-VM_Test-43 fail Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18

Commit Message

Blaise Boscaccy Jan. 9, 2025, 9:43 p.m. UTC
Using the sysfs entry passed into the subcommand, the previosly loaded
elf object file is parsed.  The objective of this parse is to identify
key elf file sections, specfically the text and btf sections. From
there, indicies are saved to relevant sections. Armed with the initial
parse info, we search for and create program definitions, along with
any respective btf information for them.

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

Patch

diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 3cfb497e1b236..03ab0bb7bf076 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6074,6 +6074,177 @@  skip_mods_and_typedefs(const struct btf *btf, u32 id, u32 *res_id)
 	return t;
 }
 
+static int init_btf(struct bpf_obj *obj, unsigned int btf_idx, unsigned int btf_ext_idx)
+{
+	Elf_Shdr *shdr =  &obj->sechdrs[btf_idx];
+	void *buffer = (void *)obj->hdr + shdr->sh_offset;
+	struct btf_ext_info *ext_segs[3];
+	int seg_num, sec_num;
+	int idx;
+	struct btf_ext_info *seg;
+	const struct btf_ext_info_sec *sec;
+	const char *sec_name;
+	struct btf *btf = btf_init_mem(buffer, shdr->sh_size, 0, 0, 0);
+
+	obj->btf = btf;
+	shdr = &obj->sechdrs[btf_ext_idx];
+	buffer = (void *)obj->hdr + shdr->sh_offset;
+	obj->btf_ext = btf_ext__new(buffer, shdr->sh_size);
+	obj->index.btf = btf_idx;
+	obj->index.btf_ext = btf_ext_idx;
+
+	/* setup .BTF.ext to ELF section mapping */
+	ext_segs[0] = &obj->btf_ext->func_info;
+	ext_segs[1] = &obj->btf_ext->line_info;
+	ext_segs[2] = &obj->btf_ext->core_relo_info;
+	for (seg_num = 0; seg_num < ARRAY_SIZE(ext_segs); seg_num++) {
+		seg = ext_segs[seg_num];
+
+		if (seg->sec_cnt == 0)
+			continue;
+
+		seg->sec_idxs = kcalloc(seg->sec_cnt, sizeof(*seg->sec_idxs), GFP_KERNEL);
+		if (!seg->sec_idxs)
+			return -ENOMEM;
+
+		sec_num = 0;
+		for_each_btf_ext_sec(seg, sec) {
+			/* preventively increment index to avoid doing
+			 * this before every continue below
+			 */
+			sec_num++;
+
+			sec_name = btf_str_by_offset(obj->btf, sec->sec_name_off);
+			if (str_is_empty(sec_name))
+				continue;
+
+			idx = elf_sec_idx_by_name(obj, sec_name);
+			if (idx < 0)
+				continue;
+			seg->sec_idxs[sec_num - 1] = idx;
+		}
+	}
+	return 0;
+}
+
+static int find_progs(struct bpf_obj *obj, unsigned int sec_idx)
+{
+	unsigned int i;
+	unsigned int prog_sz;
+	unsigned int sec_off;
+	Elf_Shdr *symsec = &obj->sechdrs[obj->index.sym];
+	Elf_Sym *sym = (void *)obj->hdr + symsec->sh_offset;
+	Elf_Shdr *shdr =  &obj->sechdrs[sec_idx];
+	struct bpf_prog_obj *progs;
+	int err;
+	struct bpf_insn *insns = NULL;
+	void *buffer;
+	unsigned int insn_cnt, ndx;
+	char *name;
+
+	for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) {
+		name = obj->strtab + sym[i].st_name;
+
+		if (sym[i].st_shndx != sec_idx)
+			continue;
+		if (ELF64_ST_TYPE(sym[i].st_info) != STT_FUNC)
+			continue;
+
+		prog_sz = sym[i].st_size;
+		sec_off = sym[i].st_value;
+		buffer = (void *)obj->hdr + shdr->sh_offset + sec_off;
+
+		insns = kmalloc(prog_sz, GFP_KERNEL);
+		if (!insns)
+			return -ENOMEM;
+
+		memcpy(insns, buffer, prog_sz);
+		insn_cnt = prog_sz / sizeof(struct bpf_insn);
+
+		progs = krealloc_array(obj->progs, obj->nr_programs + 1,
+				       sizeof(struct bpf_prog_obj), GFP_KERNEL);
+		if (!progs) {
+			err = -ENOMEM;
+			goto free_insns;
+		}
+
+		obj->progs = progs;
+		ndx = obj->nr_programs;
+		obj->progs[ndx].insn = insns;
+		obj->progs[ndx].insn_cnt = insn_cnt;
+		obj->progs[ndx].sec_idx = sec_idx;
+		obj->progs[ndx].sec_insn_off = sec_off / sizeof(struct bpf_insn);
+		obj->progs[ndx].sec_insn_cnt = insn_cnt;
+		obj->progs[ndx].name = name;
+		obj->progs[ndx].exception_cb_idx = -1;
+		obj->nr_programs++;
+
+	}
+	return 0;
+
+free_insns:
+	kfree(insns);
+	return err;
+}
+
+static int elf_collect(struct bpf_obj *obj)
+{
+	unsigned int i;
+	Elf_Shdr *shdr, *strhdr;
+	unsigned int sym_idx;
+	unsigned int sec_idx = 0;
+	unsigned int btf_idx = 0, btf_ext_idx = 0;
+	int err = 0;
+
+	obj->sechdrs = (void *)obj->hdr + obj->hdr->e_shoff;
+	strhdr = &obj->sechdrs[obj->hdr->e_shstrndx];
+	obj->secstrings = (void *)obj->hdr + strhdr->sh_offset;
+
+	for (i = 1; i < obj->hdr->e_shnum; i++) {
+		shdr = &obj->sechdrs[i];
+		switch (shdr->sh_type) {
+		case SHT_NULL:
+		case SHT_NOBITS:
+			continue;
+		case SHT_SYMTAB:
+			sym_idx = i;
+			fallthrough;
+		default:
+			break;
+		}
+	}
+
+	obj->index.sym = sym_idx;
+	shdr = &obj->sechdrs[sym_idx];
+	obj->index.str = shdr->sh_link;
+	obj->strtab = (char *)obj->hdr + obj->sechdrs[obj->index.str].sh_offset;
+
+	for (i = 1; i < obj->hdr->e_shnum; i++) {
+		shdr = &obj->sechdrs[i];
+		sec_idx = i;
+		if (strcmp(".text", obj->secstrings + shdr->sh_name) == 0)
+			obj->index.text = sec_idx;
+
+		if (shdr->sh_type == SHT_PROGBITS && shdr->sh_size > 0) {
+			err = find_progs(obj, sec_idx);
+			if (err)
+				return err;
+		}
+
+		if (strcmp(".BTF", obj->secstrings + shdr->sh_name) == 0)
+			btf_idx = i;
+
+		if (strcmp(".BTF.ext", obj->secstrings + shdr->sh_name) == 0)
+			btf_ext_idx = i;
+
+		if (strcmp(".addr_space.1", obj->secstrings + shdr->sh_name) == 0)
+			obj->index.arena = sec_idx;
+	}
+
+	err = init_btf(obj, btf_idx, btf_ext_idx);
+	return err;
+}
+
 static void free_bpf_obj(struct bpf_obj *obj)
 {
 	int i;
@@ -6305,6 +6476,10 @@  static int load_fd(union bpf_attr *attr)
 	}
 	kfree(modules);
 
+	err = elf_collect(obj);
+	if (err < 0)
+		goto free;
+
 	return obj_f;
 free:
 	free_bpf_obj(obj);