Skip to content

Commit

Permalink
Merge branch 'resolve_btfids'
Browse files Browse the repository at this point in the history
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: Alexei Starovoitov <ast@kernel.org>
  • Loading branch information
Alexei Starovoitov committed Jul 13, 2020
2 parents eef8a42 + cc15a20 commit 24a38b7
Show file tree
Hide file tree
Showing 19 changed files with 1,267 additions and 104 deletions.
36 changes: 36 additions & 0 deletions Documentation/bpf/btf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
************

Expand Down
25 changes: 20 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1053,9 +1054,10 @@ export mod_sign_cmd

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

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

ifdef CONFIG_STACK_VALIDATION
has_libelf := $(call try-run,\
echo "int main() {}" | $(HOSTCC) -xc -o /dev/null $(HOST_LIBELF_LIBS) -,1,0)
ifeq ($(has_libelf),1)
objtool_target := tools/objtool FORCE
else
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
# ---------------------------------------------------------------------------

Expand Down
4 changes: 4 additions & 0 deletions include/asm-generic/vmlinux.lds.h
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,10 @@
__start_BTF = .; \
*(.BTF) \
__stop_BTF = .; \
} \
. = ALIGN(4); \
.BTF_ids : AT(ADDR(.BTF_ids) - LOAD_OFFSET) { \
*(.BTF_ids) \
}
#else
#define BTF
Expand Down
87 changes: 87 additions & 0 deletions include/linux/btf_ids.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* 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
103 changes: 11 additions & 92 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -4079,96 +4077,17 @@ int btf_struct_access(struct bpf_verifier_log *log,
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,
Expand Down
5 changes: 4 additions & 1 deletion kernel/bpf/stackmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/elf.h>
#include <linux/pagemap.h>
#include <linux/irq_work.h>
#include <linux/btf_ids.h>
#include "percpu_freelist.h"

#define STACK_CREATE_FLAG_MASK \
Expand Down Expand Up @@ -576,7 +577,9 @@ BPF_CALL_4(bpf_get_task_stack, struct task_struct *, task, void *, buf,
return __bpf_get_stack(regs, task, buf, size, flags);
}

static int bpf_get_task_stack_btf_ids[5];
BTF_ID_LIST(bpf_get_task_stack_btf_ids)
BTF_ID(struct, task_struct)

const struct bpf_func_proto bpf_get_task_stack_proto = {
.func = bpf_get_task_stack,
.gpl_only = false,
Expand Down
Loading

0 comments on commit 24a38b7

Please sign in to comment.