Commit 0e6f601e authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'Integrate kernel module BTF support'

Andrii Nakryiko says:

====================

This patch set adds BTF generation for kernel modules using a compact split
BTF approach. Respective patches have all the details.

Kernel module BTFs rely on pahole's split BTF support, which is added in [0]
and will be available starting from v1.19. Support for it is detected
automatically during kernel build time.

This patch set implements in-kernel support for split BTF loading and
validation. It also extends GET_OBJ_INFO API for BTFs to return BTF's module
name and a flag whether BTF itself is in-kernel or user-provided. vmlinux BTF
is also exposed to user-space through the same BTF object iteration APIs.

Follow up patch set will utilize the fact that vmlinux and module BTFs now
have associated ID to provide ability to attach BPF fentry/fexit/etc programs
to functions defined in kernel modules.

bpftool is also extended to show module/vmlinux BTF's name.

  [0] https://patchwork.kernel.org/project/netdevbpf/list/?series=378699&state=*

v3->v4:
  - copy_to_user() on ENOSPC in btf_get_info_by_fd() (Martin);
v2->v3:
  - get rid of unnecessary gotos (Song);
v2->v1:
  - drop WARNs, add fewer pr_warn()'s instead (Greg);
  - properly initialize sysfs binary attribute structure (Greg);
  - add __maybe_unused to any_section_objs, used conditionally by module BTF;
rfc->v1:
  - CONFIG_DEBUG_INFO_BTF_MODULES is derived automatically (Alexei);
  - vmlinux BTF now has explicit "vmlinux" name (Alexei);
  - added sysfs ABI documentation for /sys/kernel/btf/<module> (Greg).

Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents f52b8fd3 cecaf4a0
...@@ -15,3 +15,11 @@ Description: ...@@ -15,3 +15,11 @@ Description:
information with description of all internal kernel types. See information with description of all internal kernel types. See
Documentation/bpf/btf.rst for detailed description of format Documentation/bpf/btf.rst for detailed description of format
itself. itself.
What: /sys/kernel/btf/<module-name>
Date: Nov 2020
KernelVersion: 5.11
Contact: bpf@vger.kernel.org
Description:
Read-only binary attribute exposing kernel module's BTF type
information as an add-on to the kernel's BTF (/sys/kernel/btf/vmlinux).
...@@ -36,9 +36,11 @@ struct seq_operations; ...@@ -36,9 +36,11 @@ struct seq_operations;
struct bpf_iter_aux_info; struct bpf_iter_aux_info;
struct bpf_local_storage; struct bpf_local_storage;
struct bpf_local_storage_map; struct bpf_local_storage_map;
struct kobject;
extern struct idr btf_idr; extern struct idr btf_idr;
extern spinlock_t btf_idr_lock; extern spinlock_t btf_idr_lock;
extern struct kobject *btf_kobj;
typedef int (*bpf_iter_init_seq_priv_t)(void *private_data, typedef int (*bpf_iter_init_seq_priv_t)(void *private_data,
struct bpf_iter_aux_info *aux); struct bpf_iter_aux_info *aux);
......
...@@ -475,6 +475,10 @@ struct module { ...@@ -475,6 +475,10 @@ struct module {
unsigned int num_bpf_raw_events; unsigned int num_bpf_raw_events;
struct bpf_raw_event_map *bpf_raw_events; struct bpf_raw_event_map *bpf_raw_events;
#endif #endif
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
unsigned int btf_data_size;
void *btf_data;
#endif
#ifdef CONFIG_JUMP_LABEL #ifdef CONFIG_JUMP_LABEL
struct jump_entry *jump_entries; struct jump_entry *jump_entries;
unsigned int num_jump_entries; unsigned int num_jump_entries;
......
...@@ -4466,6 +4466,9 @@ struct bpf_btf_info { ...@@ -4466,6 +4466,9 @@ struct bpf_btf_info {
__aligned_u64 btf; __aligned_u64 btf;
__u32 btf_size; __u32 btf_size;
__u32 id; __u32 id;
__aligned_u64 name;
__u32 name_len;
__u32 kernel_btf;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
struct bpf_link_info { struct bpf_link_info {
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <linux/skmsg.h> #include <linux/skmsg.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/bsearch.h> #include <linux/bsearch.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <net/sock.h> #include <net/sock.h>
/* BTF (BPF Type Format) is the meta data format which describes /* BTF (BPF Type Format) is the meta data format which describes
...@@ -203,12 +205,19 @@ struct btf { ...@@ -203,12 +205,19 @@ struct btf {
const char *strings; const char *strings;
void *nohdr_data; void *nohdr_data;
struct btf_header hdr; struct btf_header hdr;
u32 nr_types; u32 nr_types; /* includes VOID for base BTF */
u32 types_size; u32 types_size;
u32 data_size; u32 data_size;
refcount_t refcnt; refcount_t refcnt;
u32 id; u32 id;
struct rcu_head rcu; struct rcu_head rcu;
/* split BTF support */
struct btf *base_btf;
u32 start_id; /* first type ID in this BTF (0 for base BTF) */
u32 start_str_off; /* first string offset (0 for base BTF) */
char name[MODULE_NAME_LEN];
bool kernel_btf;
}; };
enum verifier_phase { enum verifier_phase {
...@@ -449,14 +458,27 @@ static bool btf_type_is_datasec(const struct btf_type *t) ...@@ -449,14 +458,27 @@ static bool btf_type_is_datasec(const struct btf_type *t)
return BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC; return BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC;
} }
static u32 btf_nr_types_total(const struct btf *btf)
{
u32 total = 0;
while (btf) {
total += btf->nr_types;
btf = btf->base_btf;
}
return total;
}
s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind) s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
{ {
const struct btf_type *t; const struct btf_type *t;
const char *tname; const char *tname;
u32 i; u32 i, total;
for (i = 1; i <= btf->nr_types; i++) { total = btf_nr_types_total(btf);
t = btf->types[i]; for (i = 1; i < total; i++) {
t = btf_type_by_id(btf, i);
if (BTF_INFO_KIND(t->info) != kind) if (BTF_INFO_KIND(t->info) != kind)
continue; continue;
...@@ -599,8 +621,14 @@ static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t) ...@@ -599,8 +621,14 @@ static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t)
static bool btf_name_offset_valid(const struct btf *btf, u32 offset) static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
{ {
return BTF_STR_OFFSET_VALID(offset) && if (!BTF_STR_OFFSET_VALID(offset))
offset < btf->hdr.str_len; return false;
while (offset < btf->start_str_off)
btf = btf->base_btf;
offset -= btf->start_str_off;
return offset < btf->hdr.str_len;
} }
static bool __btf_name_char_ok(char c, bool first, bool dot_ok) static bool __btf_name_char_ok(char c, bool first, bool dot_ok)
...@@ -614,10 +642,22 @@ static bool __btf_name_char_ok(char c, bool first, bool dot_ok) ...@@ -614,10 +642,22 @@ static bool __btf_name_char_ok(char c, bool first, bool dot_ok)
return true; return true;
} }
static const char *btf_str_by_offset(const struct btf *btf, u32 offset)
{
while (offset < btf->start_str_off)
btf = btf->base_btf;
offset -= btf->start_str_off;
if (offset < btf->hdr.str_len)
return &btf->strings[offset];
return NULL;
}
static bool __btf_name_valid(const struct btf *btf, u32 offset, bool dot_ok) static bool __btf_name_valid(const struct btf *btf, u32 offset, bool dot_ok)
{ {
/* offset must be valid */ /* offset must be valid */
const char *src = &btf->strings[offset]; const char *src = btf_str_by_offset(btf, offset);
const char *src_limit; const char *src_limit;
if (!__btf_name_char_ok(*src, true, dot_ok)) if (!__btf_name_char_ok(*src, true, dot_ok))
...@@ -650,27 +690,28 @@ static bool btf_name_valid_section(const struct btf *btf, u32 offset) ...@@ -650,27 +690,28 @@ static bool btf_name_valid_section(const struct btf *btf, u32 offset)
static const char *__btf_name_by_offset(const struct btf *btf, u32 offset) static const char *__btf_name_by_offset(const struct btf *btf, u32 offset)
{ {
const char *name;
if (!offset) if (!offset)
return "(anon)"; return "(anon)";
else if (offset < btf->hdr.str_len)
return &btf->strings[offset]; name = btf_str_by_offset(btf, offset);
else return name ?: "(invalid-name-offset)";
return "(invalid-name-offset)";
} }
const char *btf_name_by_offset(const struct btf *btf, u32 offset) const char *btf_name_by_offset(const struct btf *btf, u32 offset)
{ {
if (offset < btf->hdr.str_len) return btf_str_by_offset(btf, offset);
return &btf->strings[offset];
return NULL;
} }
const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id) const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
{ {
if (type_id > btf->nr_types) while (type_id < btf->start_id)
return NULL; btf = btf->base_btf;
type_id -= btf->start_id;
if (type_id >= btf->nr_types)
return NULL;
return btf->types[type_id]; return btf->types[type_id];
} }
...@@ -1390,17 +1431,13 @@ static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t) ...@@ -1390,17 +1431,13 @@ static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t)
{ {
struct btf *btf = env->btf; struct btf *btf = env->btf;
/* < 2 because +1 for btf_void which is always in btf->types[0]. if (btf->types_size == btf->nr_types) {
* btf_void is not accounted in btf->nr_types because btf_void
* does not come from the BTF file.
*/
if (btf->types_size - btf->nr_types < 2) {
/* Expand 'types' array */ /* Expand 'types' array */
struct btf_type **new_types; struct btf_type **new_types;
u32 expand_by, new_size; u32 expand_by, new_size;
if (btf->types_size == BTF_MAX_TYPE) { if (btf->start_id + btf->types_size == BTF_MAX_TYPE) {
btf_verifier_log(env, "Exceeded max num of types"); btf_verifier_log(env, "Exceeded max num of types");
return -E2BIG; return -E2BIG;
} }
...@@ -1414,18 +1451,23 @@ static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t) ...@@ -1414,18 +1451,23 @@ static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t)
if (!new_types) if (!new_types)
return -ENOMEM; return -ENOMEM;
if (btf->nr_types == 0) if (btf->nr_types == 0) {
if (!btf->base_btf) {
/* lazily init VOID type */
new_types[0] = &btf_void; new_types[0] = &btf_void;
else btf->nr_types++;
}
} else {
memcpy(new_types, btf->types, memcpy(new_types, btf->types,
sizeof(*btf->types) * (btf->nr_types + 1)); sizeof(*btf->types) * btf->nr_types);
}
kvfree(btf->types); kvfree(btf->types);
btf->types = new_types; btf->types = new_types;
btf->types_size = new_size; btf->types_size = new_size;
} }
btf->types[++(btf->nr_types)] = t; btf->types[btf->nr_types++] = t;
return 0; return 0;
} }
...@@ -1498,18 +1540,17 @@ static int env_resolve_init(struct btf_verifier_env *env) ...@@ -1498,18 +1540,17 @@ static int env_resolve_init(struct btf_verifier_env *env)
u32 *resolved_ids = NULL; u32 *resolved_ids = NULL;
u8 *visit_states = NULL; u8 *visit_states = NULL;
/* +1 for btf_void */ resolved_sizes = kvcalloc(nr_types, sizeof(*resolved_sizes),
resolved_sizes = kvcalloc(nr_types + 1, sizeof(*resolved_sizes),
GFP_KERNEL | __GFP_NOWARN); GFP_KERNEL | __GFP_NOWARN);
if (!resolved_sizes) if (!resolved_sizes)
goto nomem; goto nomem;
resolved_ids = kvcalloc(nr_types + 1, sizeof(*resolved_ids), resolved_ids = kvcalloc(nr_types, sizeof(*resolved_ids),
GFP_KERNEL | __GFP_NOWARN); GFP_KERNEL | __GFP_NOWARN);
if (!resolved_ids) if (!resolved_ids)
goto nomem; goto nomem;
visit_states = kvcalloc(nr_types + 1, sizeof(*visit_states), visit_states = kvcalloc(nr_types, sizeof(*visit_states),
GFP_KERNEL | __GFP_NOWARN); GFP_KERNEL | __GFP_NOWARN);
if (!visit_states) if (!visit_states)
goto nomem; goto nomem;
...@@ -1561,21 +1602,27 @@ static bool env_type_is_resolve_sink(const struct btf_verifier_env *env, ...@@ -1561,21 +1602,27 @@ static bool env_type_is_resolve_sink(const struct btf_verifier_env *env,
static bool env_type_is_resolved(const struct btf_verifier_env *env, static bool env_type_is_resolved(const struct btf_verifier_env *env,
u32 type_id) u32 type_id)
{ {
return env->visit_states[type_id] == RESOLVED; /* base BTF types should be resolved by now */
if (type_id < env->btf->start_id)
return true;
return env->visit_states[type_id - env->btf->start_id] == RESOLVED;
} }
static int env_stack_push(struct btf_verifier_env *env, static int env_stack_push(struct btf_verifier_env *env,
const struct btf_type *t, u32 type_id) const struct btf_type *t, u32 type_id)
{ {
const struct btf *btf = env->btf;
struct resolve_vertex *v; struct resolve_vertex *v;
if (env->top_stack == MAX_RESOLVE_DEPTH) if (env->top_stack == MAX_RESOLVE_DEPTH)
return -E2BIG; return -E2BIG;
if (env->visit_states[type_id] != NOT_VISITED) if (type_id < btf->start_id
|| env->visit_states[type_id - btf->start_id] != NOT_VISITED)
return -EEXIST; return -EEXIST;
env->visit_states[type_id] = VISITED; env->visit_states[type_id - btf->start_id] = VISITED;
v = &env->stack[env->top_stack++]; v = &env->stack[env->top_stack++];
v->t = t; v->t = t;
...@@ -1605,6 +1652,7 @@ static void env_stack_pop_resolved(struct btf_verifier_env *env, ...@@ -1605,6 +1652,7 @@ static void env_stack_pop_resolved(struct btf_verifier_env *env,
u32 type_id = env->stack[--(env->top_stack)].type_id; u32 type_id = env->stack[--(env->top_stack)].type_id;
struct btf *btf = env->btf; struct btf *btf = env->btf;
type_id -= btf->start_id; /* adjust to local type id */
btf->resolved_sizes[type_id] = resolved_size; btf->resolved_sizes[type_id] = resolved_size;
btf->resolved_ids[type_id] = resolved_type_id; btf->resolved_ids[type_id] = resolved_type_id;
env->visit_states[type_id] = RESOLVED; env->visit_states[type_id] = RESOLVED;
...@@ -1709,14 +1757,30 @@ btf_resolve_size(const struct btf *btf, const struct btf_type *type, ...@@ -1709,14 +1757,30 @@ btf_resolve_size(const struct btf *btf, const struct btf_type *type,
return __btf_resolve_size(btf, type, type_size, NULL, NULL, NULL, NULL); return __btf_resolve_size(btf, type, type_size, NULL, NULL, NULL, NULL);
} }
static u32 btf_resolved_type_id(const struct btf *btf, u32 type_id)
{
while (type_id < btf->start_id)
btf = btf->base_btf;
return btf->resolved_ids[type_id - btf->start_id];
}
/* The input param "type_id" must point to a needs_resolve type */ /* The input param "type_id" must point to a needs_resolve type */
static const struct btf_type *btf_type_id_resolve(const struct btf *btf, static const struct btf_type *btf_type_id_resolve(const struct btf *btf,
u32 *type_id) u32 *type_id)
{ {
*type_id = btf->resolved_ids[*type_id]; *type_id = btf_resolved_type_id(btf, *type_id);
return btf_type_by_id(btf, *type_id); return btf_type_by_id(btf, *type_id);
} }
static u32 btf_resolved_type_size(const struct btf *btf, u32 type_id)
{
while (type_id < btf->start_id)
btf = btf->base_btf;
return btf->resolved_sizes[type_id - btf->start_id];
}
const struct btf_type *btf_type_id_size(const struct btf *btf, const struct btf_type *btf_type_id_size(const struct btf *btf,
u32 *type_id, u32 *ret_size) u32 *type_id, u32 *ret_size)
{ {
...@@ -1731,7 +1795,7 @@ const struct btf_type *btf_type_id_size(const struct btf *btf, ...@@ -1731,7 +1795,7 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
if (btf_type_has_size(size_type)) { if (btf_type_has_size(size_type)) {
size = size_type->size; size = size_type->size;
} else if (btf_type_is_array(size_type)) { } else if (btf_type_is_array(size_type)) {
size = btf->resolved_sizes[size_type_id]; size = btf_resolved_type_size(btf, size_type_id);
} else if (btf_type_is_ptr(size_type)) { } else if (btf_type_is_ptr(size_type)) {
size = sizeof(void *); size = sizeof(void *);
} else { } else {
...@@ -1739,14 +1803,14 @@ const struct btf_type *btf_type_id_size(const struct btf *btf, ...@@ -1739,14 +1803,14 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
!btf_type_is_var(size_type))) !btf_type_is_var(size_type)))
return NULL; return NULL;
size_type_id = btf->resolved_ids[size_type_id]; size_type_id = btf_resolved_type_id(btf, size_type_id);
size_type = btf_type_by_id(btf, size_type_id); size_type = btf_type_by_id(btf, size_type_id);
if (btf_type_nosize_or_null(size_type)) if (btf_type_nosize_or_null(size_type))
return NULL; return NULL;
else if (btf_type_has_size(size_type)) else if (btf_type_has_size(size_type))
size = size_type->size; size = size_type->size;
else if (btf_type_is_array(size_type)) else if (btf_type_is_array(size_type))
size = btf->resolved_sizes[size_type_id]; size = btf_resolved_type_size(btf, size_type_id);
else if (btf_type_is_ptr(size_type)) else if (btf_type_is_ptr(size_type))
size = sizeof(void *); size = sizeof(void *);
else else
...@@ -3798,7 +3862,7 @@ static int btf_check_all_metas(struct btf_verifier_env *env) ...@@ -3798,7 +3862,7 @@ static int btf_check_all_metas(struct btf_verifier_env *env)
cur = btf->nohdr_data + hdr->type_off; cur = btf->nohdr_data + hdr->type_off;
end = cur + hdr->type_len; end = cur + hdr->type_len;
env->log_type_id = 1; env->log_type_id = btf->base_btf ? btf->start_id : 1;
while (cur < end) { while (cur < end) {
struct btf_type *t = cur; struct btf_type *t = cur;
s32 meta_size; s32 meta_size;
...@@ -3825,8 +3889,8 @@ static bool btf_resolve_valid(struct btf_verifier_env *env, ...@@ -3825,8 +3889,8 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
return false; return false;
if (btf_type_is_struct(t) || btf_type_is_datasec(t)) if (btf_type_is_struct(t) || btf_type_is_datasec(t))
return !btf->resolved_ids[type_id] && return !btf_resolved_type_id(btf, type_id) &&
!btf->resolved_sizes[type_id]; !btf_resolved_type_size(btf, type_id);
if (btf_type_is_modifier(t) || btf_type_is_ptr(t) || if (btf_type_is_modifier(t) || btf_type_is_ptr(t) ||
btf_type_is_var(t)) { btf_type_is_var(t)) {
...@@ -3846,7 +3910,7 @@ static bool btf_resolve_valid(struct btf_verifier_env *env, ...@@ -3846,7 +3910,7 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
elem_type = btf_type_id_size(btf, &elem_type_id, &elem_size); elem_type = btf_type_id_size(btf, &elem_type_id, &elem_size);
return elem_type && !btf_type_is_modifier(elem_type) && return elem_type && !btf_type_is_modifier(elem_type) &&
(array->nelems * elem_size == (array->nelems * elem_size ==
btf->resolved_sizes[type_id]); btf_resolved_type_size(btf, type_id));
} }
return false; return false;
...@@ -3888,7 +3952,8 @@ static int btf_resolve(struct btf_verifier_env *env, ...@@ -3888,7 +3952,8 @@ static int btf_resolve(struct btf_verifier_env *env,
static int btf_check_all_types(struct btf_verifier_env *env) static int btf_check_all_types(struct btf_verifier_env *env)
{ {
struct btf *btf = env->btf; struct btf *btf = env->btf;
u32 type_id; const struct btf_type *t;
u32 type_id, i;
int err; int err;
err = env_resolve_init(env); err = env_resolve_init(env);
...@@ -3896,8 +3961,9 @@ static int btf_check_all_types(struct btf_verifier_env *env) ...@@ -3896,8 +3961,9 @@ static int btf_check_all_types(struct btf_verifier_env *env)
return err; return err;
env->phase++; env->phase++;
for (type_id = 1; type_id <= btf->nr_types; type_id++) { for (i = btf->base_btf ? 0 : 1; i < btf->nr_types; i++) {
const struct btf_type *t = btf_type_by_id(btf, type_id); type_id = btf->start_id + i;
t = btf_type_by_id(btf, type_id);
env->log_type_id = type_id; env->log_type_id = type_id;
if (btf_type_needs_resolve(t) && if (btf_type_needs_resolve(t) &&
...@@ -3934,7 +4000,7 @@ static int btf_parse_type_sec(struct btf_verifier_env *env) ...@@ -3934,7 +4000,7 @@ static int btf_parse_type_sec(struct btf_verifier_env *env)
return -EINVAL; return -EINVAL;
} }
if (!hdr->type_len) { if (!env->btf->base_btf && !hdr->type_len) {
btf_verifier_log(env, "No type found"); btf_verifier_log(env, "No type found");
return -EINVAL; return -EINVAL;
} }
...@@ -3961,13 +4027,18 @@ static int btf_parse_str_sec(struct btf_verifier_env *env) ...@@ -3961,13 +4027,18 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
return -EINVAL; return -EINVAL;
} }
if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET || btf->strings = start;
start[0] || end[-1]) {
if (btf->base_btf && !hdr->str_len)
return 0;
if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET || end[-1]) {
btf_verifier_log(env, "Invalid string section");
return -EINVAL;
}
if (!btf->base_btf && start[0]) {
btf_verifier_log(env, "Invalid string section"); btf_verifier_log(env, "Invalid string section");
return -EINVAL; return -EINVAL;
} }
btf->strings = start;
return 0; return 0;
} }
...@@ -4362,6 +4433,8 @@ struct btf *btf_parse_vmlinux(void) ...@@ -4362,6 +4433,8 @@ struct btf *btf_parse_vmlinux(void)
btf->data = __start_BTF; btf->data = __start_BTF;
btf->data_size = __stop_BTF - __start_BTF; btf->data_size = __stop_BTF - __start_BTF;
btf->kernel_btf = true;
snprintf(btf->name, sizeof(btf->name), "vmlinux");
err = btf_parse_hdr(env); err = btf_parse_hdr(env);
if (err) if (err)
...@@ -4387,6 +4460,79 @@ struct btf *btf_parse_vmlinux(void) ...@@ -4387,6 +4460,79 @@ struct btf *btf_parse_vmlinux(void)
bpf_struct_ops_init(btf, log); bpf_struct_ops_init(btf, log);
refcount_set(&btf->refcnt, 1);
err = btf_alloc_id(btf);
if (err)
goto errout;
btf_verifier_env_free(env);
return btf;
errout:
btf_verifier_env_free(env);
if (btf) {
kvfree(btf->types);
kfree(btf);
}
return ERR_PTR(err);
}
static struct btf *btf_parse_module(const char *module_name, const void *data, unsigned int data_size)
{
struct btf_verifier_env *env = NULL;
struct bpf_verifier_log *log;
struct btf *btf = NULL, *base_btf;
int err;
base_btf = bpf_get_btf_vmlinux();
if (IS_ERR(base_btf))
return base_btf;
if (!base_btf)
return ERR_PTR(-EINVAL);
env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN);
if (!env)
return ERR_PTR(-ENOMEM);
log = &env->log;
log->level = BPF_LOG_KERNEL;
btf = kzalloc(sizeof(*btf), GFP_KERNEL | __GFP_NOWARN);
if (!btf) {
err = -ENOMEM;
goto errout;
}
env->btf = btf;
btf->base_btf = base_btf;
btf->start_id = base_btf->nr_types;
btf->start_str_off = base_btf->hdr.str_len;
btf->kernel_btf = true;
snprintf(btf->name, sizeof(btf->name), "%s", module_name);
btf->data = kvmalloc(data_size, GFP_KERNEL | __GFP_NOWARN);
if (!btf->data) {
err = -ENOMEM;
goto errout;
}
memcpy(btf->data, data, data_size);
btf->data_size = data_size;
err = btf_parse_hdr(env);
if (err)
goto errout;
btf->nohdr_data = btf->data + btf->hdr.hdr_len;
err = btf_parse_str_sec(env);
if (err)
goto errout;
err = btf_check_all_metas(env);
if (err)
goto errout;
btf_verifier_env_free(env); btf_verifier_env_free(env);
refcount_set(&btf->refcnt, 1); refcount_set(&btf->refcnt, 1);
return btf; return btf;
...@@ -4394,6 +4540,7 @@ struct btf *btf_parse_vmlinux(void) ...@@ -4394,6 +4540,7 @@ struct btf *btf_parse_vmlinux(void)
errout: errout:
btf_verifier_env_free(env); btf_verifier_env_free(env);
if (btf) { if (btf) {
kvfree(btf->data);
kvfree(btf->types); kvfree(btf->types);
kfree(btf); kfree(btf);
} }
...@@ -4908,7 +5055,7 @@ static int __get_type_size(struct btf *btf, u32 btf_id, ...@@ -4908,7 +5055,7 @@ static int __get_type_size(struct btf *btf, u32 btf_id,
while (t && btf_type_is_modifier(t)) while (t && btf_type_is_modifier(t))
t = btf_type_by_id(btf, t->type); t = btf_type_by_id(btf, t->type);
if (!t) { if (!t) {
*bad_type = btf->types[0]; *bad_type = btf_type_by_id(btf, 0);
return -EINVAL; return -EINVAL;
} }
if (btf_type_is_ptr(t)) if (btf_type_is_ptr(t))
...@@ -5486,7 +5633,9 @@ int btf_get_info_by_fd(const struct btf *btf, ...@@ -5486,7 +5633,9 @@ int btf_get_info_by_fd(const struct btf *btf,
struct bpf_btf_info info; struct bpf_btf_info info;
u32 info_copy, btf_copy; u32 info_copy, btf_copy;
void __user *ubtf; void __user *ubtf;
u32 uinfo_len; char __user *uname;
u32 uinfo_len, uname_len, name_len;
int ret = 0;
uinfo = u64_to_user_ptr(attr->info.info); uinfo = u64_to_user_ptr(attr->info.info);
uinfo_len = attr->info.info_len; uinfo_len = attr->info.info_len;
...@@ -5503,11 +5652,37 @@ int btf_get_info_by_fd(const struct btf *btf, ...@@ -5503,11 +5652,37 @@ int btf_get_info_by_fd(const struct btf *btf,
return -EFAULT; return -EFAULT;
info.btf_size = btf->data_size; info.btf_size = btf->data_size;
info.kernel_btf = btf->kernel_btf;
uname = u64_to_user_ptr(info.name);
uname_len = info.name_len;
if (!uname ^ !uname_len)
return -EINVAL;
name_len = strlen(btf->name);
info.name_len = name_len;
if (uname) {
if (uname_len >= name_len + 1) {
if (copy_to_user(uname, btf->name, name_len + 1))
return -EFAULT;
} else {
char zero = '\0';
if (copy_to_user(uname, btf->name, uname_len - 1))
return -EFAULT;
if (put_user(zero, uname + uname_len - 1))
return -EFAULT;
/* let user-space know about too short buffer */
ret = -ENOSPC;
}
}
if (copy_to_user(uinfo, &info, info_copy) || if (copy_to_user(uinfo, &info, info_copy) ||
put_user(info_copy, &uattr->info.info_len)) put_user(info_copy, &uattr->info.info_len))
return -EFAULT; return -EFAULT;
return 0; return ret;
} }
int btf_get_fd_by_id(u32 id) int btf_get_fd_by_id(u32 id)
...@@ -5547,3 +5722,126 @@ bool btf_id_set_contains(const struct btf_id_set *set, u32 id) ...@@ -5547,3 +5722,126 @@ bool btf_id_set_contains(const struct btf_id_set *set, u32 id)
{ {
return bsearch(&id, set->ids, set->cnt, sizeof(u32), btf_id_cmp_func) != NULL; return bsearch(&id, set->ids, set->cnt, sizeof(u32), btf_id_cmp_func) != NULL;
} }
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
struct btf_module {
struct list_head list;
struct module *module;
struct btf *btf;
struct bin_attribute *sysfs_attr;
};
static LIST_HEAD(btf_modules);
static DEFINE_MUTEX(btf_module_mutex);
static ssize_t
btf_module_read(struct file *file, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t len)
{
const struct btf *btf = bin_attr->private;
memcpy(buf, btf->data + off, len);
return len;
}
static int btf_module_notify(struct notifier_block *nb, unsigned long op,
void *module)
{
struct btf_module *btf_mod, *tmp;
struct module *mod = module;
struct btf *btf;
int err = 0;
if (mod->btf_data_size == 0 ||
(op != MODULE_STATE_COMING && op != MODULE_STATE_GOING))
goto out;
switch (op) {
case MODULE_STATE_COMING:
btf_mod = kzalloc(sizeof(*btf_mod), GFP_KERNEL);
if (!btf_mod) {
err = -ENOMEM;
goto out;
}
btf = btf_parse_module(mod->name, mod->btf_data, mod->btf_data_size);
if (IS_ERR(btf)) {
pr_warn("failed to validate module [%s] BTF: %ld\n",
mod->name, PTR_ERR(btf));
kfree(btf_mod);
err = PTR_ERR(btf);
goto out;
}
err = btf_alloc_id(btf);
if (err) {
btf_free(btf);
kfree(btf_mod);
goto out;
}
mutex_lock(&btf_module_mutex);
btf_mod->module = module;
btf_mod->btf = btf;
list_add(&btf_mod->list, &btf_modules);
mutex_unlock(&btf_module_mutex);
if (IS_ENABLED(CONFIG_SYSFS)) {
struct bin_attribute *attr;
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
if (!attr)
goto out;
sysfs_bin_attr_init(attr);
attr->attr.name = btf->name;
attr->attr.mode = 0444;
attr->size = btf->data_size;
attr->private = btf;
attr->read = btf_module_read;
err = sysfs_create_bin_file(btf_kobj, attr);
if (err) {
pr_warn("failed to register module [%s] BTF in sysfs: %d\n",
mod->name, err);
kfree(attr);
err = 0;
goto out;
}
btf_mod->sysfs_attr = attr;
}
break;
case MODULE_STATE_GOING:
mutex_lock(&btf_module_mutex);
list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
if (btf_mod->module != module)
continue;
list_del(&btf_mod->list);
if (btf_mod->sysfs_attr)
sysfs_remove_bin_file(btf_kobj, btf_mod->sysfs_attr);
btf_put(btf_mod->btf);
kfree(btf_mod->sysfs_attr);
kfree(btf_mod);
break;
}
mutex_unlock(&btf_module_mutex);
break;
}
out:
return notifier_from_errno(err);
}
static struct notifier_block btf_module_nb = {
.notifier_call = btf_module_notify,
};
static int __init btf_module_init(void)
{
register_module_notifier(&btf_module_nb);
return 0;
}
fs_initcall(btf_module_init);
#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */
...@@ -26,7 +26,7 @@ static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = { ...@@ -26,7 +26,7 @@ static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = {
.read = btf_vmlinux_read, .read = btf_vmlinux_read,
}; };
static struct kobject *btf_kobj; struct kobject *btf_kobj;
static int __init btf_vmlinux_init(void) static int __init btf_vmlinux_init(void)
{ {
......
...@@ -380,6 +380,35 @@ static void *section_objs(const struct load_info *info, ...@@ -380,6 +380,35 @@ static void *section_objs(const struct load_info *info,
return (void *)info->sechdrs[sec].sh_addr; return (void *)info->sechdrs[sec].sh_addr;
} }
/* Find a module section: 0 means not found. Ignores SHF_ALLOC flag. */
static unsigned int find_any_sec(const struct load_info *info, const char *name)
{
unsigned int i;
for (i = 1; i < info->hdr->e_shnum; i++) {
Elf_Shdr *shdr = &info->sechdrs[i];
if (strcmp(info->secstrings + shdr->sh_name, name) == 0)
return i;
}
return 0;
}
/*
* Find a module section, or NULL. Fill in number of "objects" in section.
* Ignores SHF_ALLOC flag.
*/
static __maybe_unused void *any_section_objs(const struct load_info *info,
const char *name,
size_t object_size,
unsigned int *num)
{
unsigned int sec = find_any_sec(info, name);
/* Section 0 has sh_addr 0 and sh_size 0. */
*num = info->sechdrs[sec].sh_size / object_size;
return (void *)info->sechdrs[sec].sh_addr;
}
/* Provided by the linker */ /* Provided by the linker */
extern const struct kernel_symbol __start___ksymtab[]; extern const struct kernel_symbol __start___ksymtab[];
extern const struct kernel_symbol __stop___ksymtab[]; extern const struct kernel_symbol __stop___ksymtab[];
...@@ -3250,6 +3279,9 @@ static int find_module_sections(struct module *mod, struct load_info *info) ...@@ -3250,6 +3279,9 @@ static int find_module_sections(struct module *mod, struct load_info *info)
sizeof(*mod->bpf_raw_events), sizeof(*mod->bpf_raw_events),
&mod->num_bpf_raw_events); &mod->num_bpf_raw_events);
#endif #endif
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
mod->btf_data = any_section_objs(info, ".BTF", 1, &mod->btf_data_size);
#endif
#ifdef CONFIG_JUMP_LABEL #ifdef CONFIG_JUMP_LABEL
mod->jump_entries = section_objs(info, "__jump_table", mod->jump_entries = section_objs(info, "__jump_table",
sizeof(*mod->jump_entries), sizeof(*mod->jump_entries),
......
...@@ -274,6 +274,15 @@ config DEBUG_INFO_BTF ...@@ -274,6 +274,15 @@ config DEBUG_INFO_BTF
Turning this on expects presence of pahole tool, which will convert Turning this on expects presence of pahole tool, which will convert
DWARF type info into equivalent deduplicated BTF type info. DWARF type info into equivalent deduplicated BTF type info.
config PAHOLE_HAS_SPLIT_BTF
def_bool $(success, test `$(PAHOLE) --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/'` -ge "119")
config DEBUG_INFO_BTF_MODULES
def_bool y
depends on DEBUG_INFO_BTF && MODULES && PAHOLE_HAS_SPLIT_BTF
help
Generate compact split BTF type information for kernel modules.
config GDB_SCRIPTS config GDB_SCRIPTS
bool "Provide GDB scripts for kernel debugging" bool "Provide GDB scripts for kernel debugging"
help help
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
PHONY := __modfinal PHONY := __modfinal
__modfinal: __modfinal:
include include/config/auto.conf
include $(srctree)/scripts/Kbuild.include include $(srctree)/scripts/Kbuild.include
# for c_flags # for c_flags
...@@ -36,8 +37,23 @@ quiet_cmd_ld_ko_o = LD [M] $@ ...@@ -36,8 +37,23 @@ quiet_cmd_ld_ko_o = LD [M] $@
-T scripts/module.lds -o $@ $(filter %.o, $^); \ -T scripts/module.lds -o $@ $(filter %.o, $^); \
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
$(modules): %.ko: %.o %.mod.o scripts/module.lds FORCE quiet_cmd_btf_ko = BTF [M] $@
+$(call if_changed,ld_ko_o) cmd_btf_ko = LLVM_OBJCOPY=$(OBJCOPY) $(PAHOLE) -J --btf_base vmlinux $@
# Same as newer-prereqs, but allows to exclude specified extra dependencies
newer_prereqs_except = $(filter-out $(PHONY) $(1),$?)
# Same as if_changed, but allows to exclude specified extra dependencies
if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \
$(cmd); \
printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
# Re-generate module BTFs if either module's .ko or vmlinux changed
$(modules): %.ko: %.o %.mod.o scripts/module.lds vmlinux FORCE
+$(call if_changed_except,ld_ko_o,vmlinux)
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+$(if $(newer-prereqs),$(call cmd,btf_ko))
endif
targets += $(modules) $(modules:.ko=.mod.o) targets += $(modules) $(modules:.ko=.mod.o)
......
...@@ -742,9 +742,14 @@ show_btf_plain(struct bpf_btf_info *info, int fd, ...@@ -742,9 +742,14 @@ show_btf_plain(struct bpf_btf_info *info, int fd,
struct btf_attach_table *btf_map_table) struct btf_attach_table *btf_map_table)
{ {
struct btf_attach_point *obj; struct btf_attach_point *obj;
const char *name = u64_to_ptr(info->name);
int n; int n;
printf("%u: ", info->id); printf("%u: ", info->id);
if (info->kernel_btf)
printf("name [%s] ", name);
else if (name && name[0])
printf("name %s ", name);
printf("size %uB", info->btf_size); printf("size %uB", info->btf_size);
n = 0; n = 0;
...@@ -771,6 +776,7 @@ show_btf_json(struct bpf_btf_info *info, int fd, ...@@ -771,6 +776,7 @@ show_btf_json(struct bpf_btf_info *info, int fd,
struct btf_attach_table *btf_map_table) struct btf_attach_table *btf_map_table)
{ {
struct btf_attach_point *obj; struct btf_attach_point *obj;
const char *name = u64_to_ptr(info->name);
jsonw_start_object(json_wtr); /* btf object */ jsonw_start_object(json_wtr); /* btf object */
jsonw_uint_field(json_wtr, "id", info->id); jsonw_uint_field(json_wtr, "id", info->id);
...@@ -796,6 +802,11 @@ show_btf_json(struct bpf_btf_info *info, int fd, ...@@ -796,6 +802,11 @@ show_btf_json(struct bpf_btf_info *info, int fd,
emit_obj_refs_json(&refs_table, info->id, json_wtr); /* pids */ emit_obj_refs_json(&refs_table, info->id, json_wtr); /* pids */
jsonw_bool_field(json_wtr, "kernel", info->kernel_btf);
if (name && name[0])
jsonw_string_field(json_wtr, "name", name);
jsonw_end_object(json_wtr); /* btf object */ jsonw_end_object(json_wtr); /* btf object */
} }
...@@ -803,15 +814,30 @@ static int ...@@ -803,15 +814,30 @@ static int
show_btf(int fd, struct btf_attach_table *btf_prog_table, show_btf(int fd, struct btf_attach_table *btf_prog_table,
struct btf_attach_table *btf_map_table) struct btf_attach_table *btf_map_table)
{ {
struct bpf_btf_info info = {}; struct bpf_btf_info info;
__u32 len = sizeof(info); __u32 len = sizeof(info);
char name[64];
int err; int err;
memset(&info, 0, sizeof(info));
err = bpf_obj_get_info_by_fd(fd, &info, &len); err = bpf_obj_get_info_by_fd(fd, &info, &len);
if (err) { if (err) {
p_err("can't get BTF object info: %s", strerror(errno)); p_err("can't get BTF object info: %s", strerror(errno));
return -1; return -1;
} }
/* if kernel support emitting BTF object name, pass name pointer */
if (info.name_len) {
memset(&info, 0, sizeof(info));
info.name_len = sizeof(name);
info.name = ptr_to_u64(name);
len = sizeof(info);
err = bpf_obj_get_info_by_fd(fd, &info, &len);
if (err) {
p_err("can't get BTF object info: %s", strerror(errno));
return -1;
}
}
if (json_output) if (json_output)
show_btf_json(&info, fd, btf_prog_table, btf_map_table); show_btf_json(&info, fd, btf_prog_table, btf_map_table);
......
...@@ -4466,6 +4466,9 @@ struct bpf_btf_info { ...@@ -4466,6 +4466,9 @@ struct bpf_btf_info {
__aligned_u64 btf; __aligned_u64 btf;
__u32 btf_size; __u32 btf_size;
__u32 id; __u32 id;
__aligned_u64 name;
__u32 name_len;
__u32 kernel_btf;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
struct bpf_link_info { struct bpf_link_info {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment