diff options
author | David S. Miller <davem@davemloft.net> | 2018-04-21 15:56:15 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-04-21 15:56:15 -0400 |
commit | 1b80f86ed6b0e98a7e3d1e7d547f66163aa8a1af (patch) | |
tree | cb133d8c521b7f34357c80c843e6f66bf47587c5 /tools/lib/bpf/btf.c | |
parent | cf1a1e07fc8bb29947ad3c9568d73aee3f851431 (diff) | |
parent | 878a4d328104fed86ea321c48a5245eed44fbe14 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Daniel Borkmann says:
====================
pull-request: bpf-next 2018-04-21
The following pull-request contains BPF updates for your *net-next* tree.
The main changes are:
1) Initial work on BPF Type Format (BTF) is added, which is a meta
data format which describes the data types of BPF programs / maps.
BTF has its roots from CTF (Compact C-Type format) with a number
of changes to it. First use case is to provide a generic pretty
print capability for BPF maps inspection, later work will also
add BTF to bpftool. pahole support to convert dwarf to BTF will
be upstreamed as well (https://github.com/iamkafai/pahole/tree/btf),
from Martin.
2) Add a new xdp_bpf_adjust_tail() BPF helper for XDP that allows
for changing the data_end pointer. Only shrinking is currently
supported which helps for crafting ICMP control messages. Minor
changes in drivers have been added where needed so they recalc
the packet's length also when data_end was adjusted, from Nikita.
3) Improve bpftool to make it easier to feed hex bytes via cmdline
for map operations, from Quentin.
4) Add support for various missing BPF prog types and attach types
that have been added to kernel recently but neither to bpftool
nor libbpf yet. Doc and bash completion updates have been added
as well for bpftool, from Andrey.
5) Proper fix for avoiding to leak info stored in frame data on page
reuse for the two bpf_xdp_adjust_{head,meta} helpers by disallowing
to move the pointers into struct xdp_frame area, from Jesper.
6) Follow-up compile fix from BTF in order to include stdbool.h in
libbpf, from Björn.
7) Few fixes in BPF sample code, that is, a typo on the netdevice
in a comment and fixup proper dump of XDP action code in the
tracepoint exception, from Wang and Jesper.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'tools/lib/bpf/btf.c')
-rw-r--r-- | tools/lib/bpf/btf.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c new file mode 100644 index 000000000000..58b6255abc7a --- /dev/null +++ b/tools/lib/bpf/btf.c @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2018 Facebook */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <linux/err.h> +#include <linux/btf.h> +#include "btf.h" +#include "bpf.h" + +#define elog(fmt, ...) { if (err_log) err_log(fmt, ##__VA_ARGS__); } +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#define BTF_MAX_NR_TYPES 65535 + +static struct btf_type btf_void; + +struct btf { + union { + struct btf_header *hdr; + void *data; + }; + struct btf_type **types; + const char *strings; + void *nohdr_data; + uint32_t nr_types; + uint32_t types_size; + uint32_t data_size; + int fd; +}; + +static const char *btf_name_by_offset(const struct btf *btf, uint32_t offset) +{ + if (!BTF_STR_TBL_ELF_ID(offset) && + BTF_STR_OFFSET(offset) < btf->hdr->str_len) + return &btf->strings[BTF_STR_OFFSET(offset)]; + else + return NULL; +} + +static int btf_add_type(struct btf *btf, struct btf_type *t) +{ + if (btf->types_size - btf->nr_types < 2) { + struct btf_type **new_types; + u32 expand_by, new_size; + + if (btf->types_size == BTF_MAX_NR_TYPES) + return -E2BIG; + + expand_by = max(btf->types_size >> 2, 16); + new_size = min(BTF_MAX_NR_TYPES, btf->types_size + expand_by); + + new_types = realloc(btf->types, sizeof(*new_types) * new_size); + if (!new_types) + return -ENOMEM; + + if (btf->nr_types == 0) + new_types[0] = &btf_void; + + btf->types = new_types; + btf->types_size = new_size; + } + + btf->types[++(btf->nr_types)] = t; + + return 0; +} + +static int btf_parse_hdr(struct btf *btf, btf_print_fn_t err_log) +{ + const struct btf_header *hdr = btf->hdr; + u32 meta_left; + + if (btf->data_size < sizeof(struct btf_header)) { + elog("BTF header not found\n"); + return -EINVAL; + } + + if (hdr->magic != BTF_MAGIC) { + elog("Invalid BTF magic:%x\n", hdr->magic); + return -EINVAL; + } + + if (hdr->version != BTF_VERSION) { + elog("Unsupported BTF version:%u\n", hdr->version); + return -ENOTSUP; + } + + if (hdr->flags) { + elog("Unsupported BTF flags:%x\n", hdr->flags); + return -ENOTSUP; + } + + meta_left = btf->data_size - sizeof(*hdr); + if (!meta_left) { + elog("BTF has no data\n"); + return -EINVAL; + } + + if (meta_left < hdr->type_off) { + elog("Invalid BTF type section offset:%u\n", hdr->type_off); + return -EINVAL; + } + + if (meta_left < hdr->str_off) { + elog("Invalid BTF string section offset:%u\n", hdr->str_off); + return -EINVAL; + } + + if (hdr->type_off >= hdr->str_off) { + elog("BTF type section offset >= string section offset. No type?\n"); + return -EINVAL; + } + + if (hdr->type_off & 0x02) { + elog("BTF type section is not aligned to 4 bytes\n"); + return -EINVAL; + } + + btf->nohdr_data = btf->hdr + 1; + + return 0; +} + +static int btf_parse_str_sec(struct btf *btf, btf_print_fn_t err_log) +{ + const struct btf_header *hdr = btf->hdr; + const char *start = btf->nohdr_data + hdr->str_off; + const char *end = start + btf->hdr->str_len; + + if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET || + start[0] || end[-1]) { + elog("Invalid BTF string section\n"); + return -EINVAL; + } + + btf->strings = start; + + return 0; +} + +static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log) +{ + struct btf_header *hdr = btf->hdr; + void *nohdr_data = btf->nohdr_data; + void *next_type = nohdr_data + hdr->type_off; + void *end_type = nohdr_data + hdr->str_off; + + while (next_type < end_type) { + struct btf_type *t = next_type; + uint16_t vlen = BTF_INFO_VLEN(t->info); + int err; + + next_type += sizeof(*t); + switch (BTF_INFO_KIND(t->info)) { + case BTF_KIND_INT: + next_type += sizeof(int); + break; + case BTF_KIND_ARRAY: + next_type += sizeof(struct btf_array); + break; + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + next_type += vlen * sizeof(struct btf_member); + break; + case BTF_KIND_ENUM: + next_type += vlen * sizeof(struct btf_enum); + break; + case BTF_KIND_TYPEDEF: + case BTF_KIND_PTR: + case BTF_KIND_FWD: + case BTF_KIND_VOLATILE: + case BTF_KIND_CONST: + case BTF_KIND_RESTRICT: + break; + default: + elog("Unsupported BTF_KIND:%u\n", + BTF_INFO_KIND(t->info)); + return -EINVAL; + } + + err = btf_add_type(btf, t); + if (err) + return err; + } + + return 0; +} + +static const struct btf_type *btf_type_by_id(const struct btf *btf, + uint32_t type_id) +{ + if (type_id > btf->nr_types) + return NULL; + + return btf->types[type_id]; +} + +static bool btf_type_is_void(const struct btf_type *t) +{ + return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD; +} + +static bool btf_type_is_void_or_null(const struct btf_type *t) +{ + return !t || btf_type_is_void(t); +} + +static int64_t btf_type_size(const struct btf_type *t) +{ + switch (BTF_INFO_KIND(t->info)) { + case BTF_KIND_INT: + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + case BTF_KIND_ENUM: + return t->size; + case BTF_KIND_PTR: + return sizeof(void *); + default: + return -EINVAL; + } +} + +#define MAX_RESOLVE_DEPTH 32 + +int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id) +{ + const struct btf_array *array; + const struct btf_type *t; + uint32_t nelems = 1; + int64_t size = -1; + int i; + + t = btf_type_by_id(btf, type_id); + for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t); + i++) { + size = btf_type_size(t); + if (size >= 0) + break; + + switch (BTF_INFO_KIND(t->info)) { + case BTF_KIND_TYPEDEF: + case BTF_KIND_VOLATILE: + case BTF_KIND_CONST: + case BTF_KIND_RESTRICT: + type_id = t->type; + break; + case BTF_KIND_ARRAY: + array = (const struct btf_array *)(t + 1); + if (nelems && array->nelems > UINT32_MAX / nelems) + return -E2BIG; + nelems *= array->nelems; + type_id = array->type; + break; + default: + return -EINVAL; + } + + t = btf_type_by_id(btf, type_id); + } + + if (size < 0) + return -EINVAL; + + if (nelems && size > UINT32_MAX / nelems) + return -E2BIG; + + return nelems * size; +} + +int32_t btf__find_by_name(const struct btf *btf, const char *type_name) +{ + uint32_t i; + + if (!strcmp(type_name, "void")) + return 0; + + for (i = 1; i <= btf->nr_types; i++) { + const struct btf_type *t = btf->types[i]; + const char *name = btf_name_by_offset(btf, t->name); + + if (name && !strcmp(type_name, name)) + return i; + } + + return -ENOENT; +} + +void btf__free(struct btf *btf) +{ + if (!btf) + return; + + if (btf->fd != -1) + close(btf->fd); + + free(btf->data); + free(btf->types); + free(btf); +} + +struct btf *btf__new(uint8_t *data, uint32_t size, + btf_print_fn_t err_log) +{ + uint32_t log_buf_size = 0; + char *log_buf = NULL; + struct btf *btf; + int err; + + btf = calloc(1, sizeof(struct btf)); + if (!btf) + return ERR_PTR(-ENOMEM); + + btf->fd = -1; + + if (err_log) { + log_buf = malloc(BPF_LOG_BUF_SIZE); + if (!log_buf) { + err = -ENOMEM; + goto done; + } + *log_buf = 0; + log_buf_size = BPF_LOG_BUF_SIZE; + } + + btf->data = malloc(size); + if (!btf->data) { + err = -ENOMEM; + goto done; + } + + memcpy(btf->data, data, size); + btf->data_size = size; + + btf->fd = bpf_load_btf(btf->data, btf->data_size, + log_buf, log_buf_size, false); + + if (btf->fd == -1) { + err = -errno; + elog("Error loading BTF: %s(%d)\n", strerror(errno), errno); + if (log_buf && *log_buf) + elog("%s\n", log_buf); + goto done; + } + + err = btf_parse_hdr(btf, err_log); + if (err) + goto done; + + err = btf_parse_str_sec(btf, err_log); + if (err) + goto done; + + err = btf_parse_type_sec(btf, err_log); + +done: + free(log_buf); + + if (err) { + btf__free(btf); + return ERR_PTR(err); + } + + return btf; +} + +int btf__fd(const struct btf *btf) +{ + return btf->fd; +} |