Commit 24a38b7c authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'resolve_btfids'



Jiri Olsa says:

====================
This patchset adds:
  - support to generate BTF ID lists that are resolved during
    kernel linking and usable within kernel code with following
    macros:

      BTF_ID_LIST(bpf_skb_output_btf_ids)
      BTF_ID(struct, sk_buff)

    and access it in kernel code via:
      extern u32 bpf_skb_output_btf_ids[];

  - resolve_btfids tool that scans elf object for .BTF_ids
    section and resolves its symbols with BTF ID values
  - resolving of bpf_ctx_convert struct and several other
    objects with BTF_ID_LIST

v7 changes:
  - added more acks [Andrii]
  - added some name-conflicting entries and fixed resolve_btfids
    to process them properly [Andrii]
  - changed bpf_get_task_stack_proto to use BTF_IDS_LIST/BTF_ID
    macros [Andrii]
  - fixed selftest build for resolve_btfids test
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents eef8a42d cc15a20d
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -691,6 +691,42 @@ kernel API, the ``insn_off`` is the instruction offset in the unit of ``struct
bpf_insn``. For ELF API, the ``insn_off`` is the byte offset from the
beginning of section (``btf_ext_info_sec->sec_name_off``).

4.2 .BTF_ids section
====================

The .BTF_ids section encodes BTF ID values that are used within the kernel.

This section is created during the kernel compilation with the help of
macros defined in ``include/linux/btf_ids.h`` header file. Kernel code can
use them to create lists and sets (sorted lists) of BTF ID values.

The ``BTF_ID_LIST`` and ``BTF_ID`` macros define unsorted list of BTF ID values,
with following syntax::

  BTF_ID_LIST(list)
  BTF_ID(type1, name1)
  BTF_ID(type2, name2)

resulting in following layout in .BTF_ids section::

  __BTF_ID__type1__name1__1:
  .zero 4
  __BTF_ID__type2__name2__2:
  .zero 4

The ``u32 list[];`` variable is defined to access the list.

The ``BTF_ID_UNUSED`` macro defines 4 zero bytes. It's used when we
want to define unused entry in BTF_ID_LIST, like::

      BTF_ID_LIST(bpf_skb_output_btf_ids)
      BTF_ID(struct, sk_buff)
      BTF_ID_UNUSED
      BTF_ID(struct, task_struct)

All the BTF ID lists and sets are compiled in the .BTF_ids section and
resolved during the linking phase of kernel build by ``resolve_btfids`` tool.

5. Using BTF
************

+20 −5
Original line number Diff line number Diff line
@@ -448,6 +448,7 @@ OBJSIZE = $(CROSS_COMPILE)size
STRIP		= $(CROSS_COMPILE)strip
endif
PAHOLE		= pahole
RESOLVE_BTFIDS	= $(objtree)/tools/bpf/resolve_btfids/resolve_btfids
LEX		= flex
YACC		= bison
AWK		= awk
@@ -510,7 +511,7 @@ GCC_PLUGINS_CFLAGS :=
CLANG_FLAGS :=

export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
export CPP AR NM STRIP OBJCOPY OBJDUMP OBJSIZE READELF PAHOLE LEX YACC AWK INSTALLKERNEL
export CPP AR NM STRIP OBJCOPY OBJDUMP OBJSIZE READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL
export PERL PYTHON PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ
export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE
@@ -1053,9 +1054,10 @@ export mod_sign_cmd

HOST_LIBELF_LIBS = $(shell pkg-config libelf --libs 2>/dev/null || echo -lelf)

ifdef CONFIG_STACK_VALIDATION
  has_libelf := $(call try-run,\
has_libelf = $(call try-run,\
               echo "int main() {}" | $(HOSTCC) -xc -o /dev/null $(HOST_LIBELF_LIBS) -,1,0)

ifdef CONFIG_STACK_VALIDATION
  ifeq ($(has_libelf),1)
    objtool_target := tools/objtool FORCE
  else
@@ -1064,6 +1066,14 @@ ifdef CONFIG_STACK_VALIDATION
  endif
endif

ifdef CONFIG_DEBUG_INFO_BTF
  ifeq ($(has_libelf),1)
    resolve_btfids_target := tools/bpf/resolve_btfids FORCE
  else
    ERROR_RESOLVE_BTFIDS := 1
  endif
endif

PHONY += prepare0

export MODORDER := $(extmod-prefix)modules.order
@@ -1175,7 +1185,7 @@ prepare0: archprepare
	$(Q)$(MAKE) $(build)=.

# All the preparing..
prepare: prepare0 prepare-objtool
prepare: prepare0 prepare-objtool prepare-resolve_btfids

# Support for using generic headers in asm-generic
asm-generic := -f $(srctree)/scripts/Makefile.asm-generic obj
@@ -1188,7 +1198,7 @@ uapi-asm-generic:
	$(Q)$(MAKE) $(asm-generic)=arch/$(SRCARCH)/include/generated/uapi/asm \
	generic=include/uapi/asm-generic

PHONY += prepare-objtool
PHONY += prepare-objtool prepare-resolve_btfids
prepare-objtool: $(objtool_target)
ifeq ($(SKIP_STACK_VALIDATION),1)
ifdef CONFIG_UNWINDER_ORC
@@ -1199,6 +1209,11 @@ else
endif
endif

prepare-resolve_btfids: $(resolve_btfids_target)
ifeq ($(ERROR_RESOLVE_BTFIDS),1)
	@echo "error: Cannot resolve BTF IDs for CONFIG_DEBUG_INFO_BTF, please install libelf-dev, libelf-devel or elfutils-libelf-devel" >&2
	@false
endif
# Generate some files
# ---------------------------------------------------------------------------

+4 −0
Original line number Diff line number Diff line
@@ -641,6 +641,10 @@
		__start_BTF = .;					\
		*(.BTF)							\
		__stop_BTF = .;						\
	}								\
	. = ALIGN(4);							\
	.BTF_ids : AT(ADDR(.BTF_ids) - LOAD_OFFSET) {			\
		*(.BTF_ids)						\
	}
#else
#define BTF
+87 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef _LINUX_BTF_IDS_H
#define _LINUX_BTF_IDS_H

#include <linux/compiler.h> /* for __PASTE */

/*
 * Following macros help to define lists of BTF IDs placed
 * in .BTF_ids section. They are initially filled with zeros
 * (during compilation) and resolved later during the
 * linking phase by resolve_btfids tool.
 *
 * Any change in list layout must be reflected in resolve_btfids
 * tool logic.
 */

#define BTF_IDS_SECTION ".BTF_ids"

#define ____BTF_ID(symbol)				\
asm(							\
".pushsection " BTF_IDS_SECTION ",\"a\";       \n"	\
".local " #symbol " ;                          \n"	\
".type  " #symbol ", @object;                  \n"	\
".size  " #symbol ", 4;                        \n"	\
#symbol ":                                     \n"	\
".zero 4                                       \n"	\
".popsection;                                  \n");

#define __BTF_ID(symbol) \
	____BTF_ID(symbol)

#define __ID(prefix) \
	__PASTE(prefix, __COUNTER__)

/*
 * The BTF_ID defines unique symbol for each ID pointing
 * to 4 zero bytes.
 */
#define BTF_ID(prefix, name) \
	__BTF_ID(__ID(__BTF_ID__##prefix##__##name##__))

/*
 * The BTF_ID_LIST macro defines pure (unsorted) list
 * of BTF IDs, with following layout:
 *
 * BTF_ID_LIST(list1)
 * BTF_ID(type1, name1)
 * BTF_ID(type2, name2)
 *
 * list1:
 * __BTF_ID__type1__name1__1:
 * .zero 4
 * __BTF_ID__type2__name2__2:
 * .zero 4
 *
 */
#define __BTF_ID_LIST(name)				\
asm(							\
".pushsection " BTF_IDS_SECTION ",\"a\";       \n"	\
".local " #name ";                             \n"	\
#name ":;                                      \n"	\
".popsection;                                  \n");	\

#define BTF_ID_LIST(name)				\
__BTF_ID_LIST(name)					\
extern u32 name[];

/*
 * The BTF_ID_UNUSED macro defines 4 zero bytes.
 * It's used when we want to define 'unused' entry
 * in BTF_ID_LIST, like:
 *
 *   BTF_ID_LIST(bpf_skb_output_btf_ids)
 *   BTF_ID(struct, sk_buff)
 *   BTF_ID_UNUSED
 *   BTF_ID(struct, task_struct)
 */

#define BTF_ID_UNUSED					\
asm(							\
".pushsection " BTF_IDS_SECTION ",\"a\";       \n"	\
".zero 4                                       \n"	\
".popsection;                                  \n");


#endif
+11 −92
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/sort.h>
#include <linux/bpf_verifier.h>
#include <linux/btf.h>
#include <linux/btf_ids.h>
#include <linux/skmsg.h>
#include <linux/perf_event.h>
#include <net/sock.h>
@@ -3621,12 +3622,15 @@ static int btf_translate_to_vmlinux(struct bpf_verifier_log *log,
	return kern_ctx_type->type;
}

BTF_ID_LIST(bpf_ctx_convert_btf_id)
BTF_ID(struct, bpf_ctx_convert)

struct btf *btf_parse_vmlinux(void)
{
	struct btf_verifier_env *env = NULL;
	struct bpf_verifier_log *log;
	struct btf *btf = NULL;
	int err, btf_id;
	int err;

	env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN);
	if (!env)
@@ -3659,14 +3663,8 @@ struct btf *btf_parse_vmlinux(void)
	if (err)
		goto errout;

	/* find struct bpf_ctx_convert for type checking later */
	btf_id = btf_find_by_name_kind(btf, "bpf_ctx_convert", BTF_KIND_STRUCT);
	if (btf_id < 0) {
		err = btf_id;
		goto errout;
	}
	/* btf_parse_vmlinux() runs under bpf_verifier_lock */
	bpf_ctx_convert.t = btf_type_by_id(btf, btf_id);
	bpf_ctx_convert.t = btf_type_by_id(btf, bpf_ctx_convert_btf_id[0]);

	/* find bpf map structs for map_ptr access checking */
	err = btf_vmlinux_map_ids_init(btf, log);
@@ -4079,96 +4077,17 @@ error:
	return -EINVAL;
}

static int __btf_resolve_helper_id(struct bpf_verifier_log *log, void *fn,
				   int arg)
{
	char fnname[KSYM_SYMBOL_LEN + 4] = "btf_";
	const struct btf_param *args;
	const struct btf_type *t;
	const char *tname, *sym;
	u32 btf_id, i;

	if (IS_ERR(btf_vmlinux)) {
		bpf_log(log, "btf_vmlinux is malformed\n");
		return -EINVAL;
	}

	sym = kallsyms_lookup((long)fn, NULL, NULL, NULL, fnname + 4);
	if (!sym) {
		bpf_log(log, "kernel doesn't have kallsyms\n");
		return -EFAULT;
	}

	for (i = 1; i <= btf_vmlinux->nr_types; i++) {
		t = btf_type_by_id(btf_vmlinux, i);
		if (BTF_INFO_KIND(t->info) != BTF_KIND_TYPEDEF)
			continue;
		tname = __btf_name_by_offset(btf_vmlinux, t->name_off);
		if (!strcmp(tname, fnname))
			break;
	}
	if (i > btf_vmlinux->nr_types) {
		bpf_log(log, "helper %s type is not found\n", fnname);
		return -ENOENT;
	}

	t = btf_type_by_id(btf_vmlinux, t->type);
	if (!btf_type_is_ptr(t))
		return -EFAULT;
	t = btf_type_by_id(btf_vmlinux, t->type);
	if (!btf_type_is_func_proto(t))
		return -EFAULT;

	args = (const struct btf_param *)(t + 1);
	if (arg >= btf_type_vlen(t)) {
		bpf_log(log, "bpf helper %s doesn't have %d-th argument\n",
			fnname, arg);
		return -EINVAL;
	}

	t = btf_type_by_id(btf_vmlinux, args[arg].type);
	if (!btf_type_is_ptr(t) || !t->type) {
		/* anything but the pointer to struct is a helper config bug */
		bpf_log(log, "ARG_PTR_TO_BTF is misconfigured\n");
		return -EFAULT;
	}
	btf_id = t->type;
	t = btf_type_by_id(btf_vmlinux, t->type);
	/* skip modifiers */
	while (btf_type_is_modifier(t)) {
		btf_id = t->type;
		t = btf_type_by_id(btf_vmlinux, t->type);
	}
	if (!btf_type_is_struct(t)) {
		bpf_log(log, "ARG_PTR_TO_BTF is not a struct\n");
		return -EFAULT;
	}
	bpf_log(log, "helper %s arg%d has btf_id %d struct %s\n", fnname + 4,
		arg, btf_id, __btf_name_by_offset(btf_vmlinux, t->name_off));
	return btf_id;
}

int btf_resolve_helper_id(struct bpf_verifier_log *log,
			  const struct bpf_func_proto *fn, int arg)
{
	int *btf_id = &fn->btf_id[arg];
	int ret;
	int id;

	if (fn->arg_type[arg] != ARG_PTR_TO_BTF_ID)
		return -EINVAL;

	ret = READ_ONCE(*btf_id);
	if (ret)
		return ret;
	/* ok to race the search. The result is the same */
	ret = __btf_resolve_helper_id(log, fn->func, arg);
	if (!ret) {
		/* Function argument cannot be type 'void' */
		bpf_log(log, "BTF resolution bug\n");
		return -EFAULT;
	}
	WRITE_ONCE(*btf_id, ret);
	return ret;
	id = fn->btf_id[arg];
	if (!id || id > btf_vmlinux->nr_types)
		return -EINVAL;
	return id;
}

static int __get_type_size(struct btf *btf, u32 btf_id,
Loading