18a138aedSMartin KaFai Lau /* SPDX-License-Identifier: GPL-2.0 */ 28a138aedSMartin KaFai Lau /* Copyright (c) 2018 Facebook */ 38a138aedSMartin KaFai Lau 48a138aedSMartin KaFai Lau #include <stdlib.h> 58a138aedSMartin KaFai Lau #include <string.h> 68a138aedSMartin KaFai Lau #include <unistd.h> 78a138aedSMartin KaFai Lau #include <errno.h> 88a138aedSMartin KaFai Lau #include <linux/err.h> 98a138aedSMartin KaFai Lau #include <linux/btf.h> 108a138aedSMartin KaFai Lau #include "btf.h" 118a138aedSMartin KaFai Lau #include "bpf.h" 128a138aedSMartin KaFai Lau 138a138aedSMartin KaFai Lau #define elog(fmt, ...) { if (err_log) err_log(fmt, ##__VA_ARGS__); } 148a138aedSMartin KaFai Lau #define max(a, b) ((a) > (b) ? (a) : (b)) 158a138aedSMartin KaFai Lau #define min(a, b) ((a) < (b) ? (a) : (b)) 168a138aedSMartin KaFai Lau 178a138aedSMartin KaFai Lau #define BTF_MAX_NR_TYPES 65535 188a138aedSMartin KaFai Lau 198a138aedSMartin KaFai Lau static struct btf_type btf_void; 208a138aedSMartin KaFai Lau 218a138aedSMartin KaFai Lau struct btf { 228a138aedSMartin KaFai Lau union { 238a138aedSMartin KaFai Lau struct btf_header *hdr; 248a138aedSMartin KaFai Lau void *data; 258a138aedSMartin KaFai Lau }; 268a138aedSMartin KaFai Lau struct btf_type **types; 278a138aedSMartin KaFai Lau const char *strings; 288a138aedSMartin KaFai Lau void *nohdr_data; 295b891af7SMartin KaFai Lau __u32 nr_types; 305b891af7SMartin KaFai Lau __u32 types_size; 315b891af7SMartin KaFai Lau __u32 data_size; 328a138aedSMartin KaFai Lau int fd; 338a138aedSMartin KaFai Lau }; 348a138aedSMartin KaFai Lau 355b891af7SMartin KaFai Lau static const char *btf_name_by_offset(const struct btf *btf, __u32 offset) 368a138aedSMartin KaFai Lau { 3761746dbeSMartin KaFai Lau if (offset < btf->hdr->str_len) 3861746dbeSMartin KaFai Lau return &btf->strings[offset]; 398a138aedSMartin KaFai Lau else 408a138aedSMartin KaFai Lau return NULL; 418a138aedSMartin KaFai Lau } 428a138aedSMartin KaFai Lau 438a138aedSMartin KaFai Lau static int btf_add_type(struct btf *btf, struct btf_type *t) 448a138aedSMartin KaFai Lau { 458a138aedSMartin KaFai Lau if (btf->types_size - btf->nr_types < 2) { 468a138aedSMartin KaFai Lau struct btf_type **new_types; 475b891af7SMartin KaFai Lau __u32 expand_by, new_size; 488a138aedSMartin KaFai Lau 498a138aedSMartin KaFai Lau if (btf->types_size == BTF_MAX_NR_TYPES) 508a138aedSMartin KaFai Lau return -E2BIG; 518a138aedSMartin KaFai Lau 528a138aedSMartin KaFai Lau expand_by = max(btf->types_size >> 2, 16); 538a138aedSMartin KaFai Lau new_size = min(BTF_MAX_NR_TYPES, btf->types_size + expand_by); 548a138aedSMartin KaFai Lau 558a138aedSMartin KaFai Lau new_types = realloc(btf->types, sizeof(*new_types) * new_size); 568a138aedSMartin KaFai Lau if (!new_types) 578a138aedSMartin KaFai Lau return -ENOMEM; 588a138aedSMartin KaFai Lau 598a138aedSMartin KaFai Lau if (btf->nr_types == 0) 608a138aedSMartin KaFai Lau new_types[0] = &btf_void; 618a138aedSMartin KaFai Lau 628a138aedSMartin KaFai Lau btf->types = new_types; 638a138aedSMartin KaFai Lau btf->types_size = new_size; 648a138aedSMartin KaFai Lau } 658a138aedSMartin KaFai Lau 668a138aedSMartin KaFai Lau btf->types[++(btf->nr_types)] = t; 678a138aedSMartin KaFai Lau 688a138aedSMartin KaFai Lau return 0; 698a138aedSMartin KaFai Lau } 708a138aedSMartin KaFai Lau 718a138aedSMartin KaFai Lau static int btf_parse_hdr(struct btf *btf, btf_print_fn_t err_log) 728a138aedSMartin KaFai Lau { 738a138aedSMartin KaFai Lau const struct btf_header *hdr = btf->hdr; 745b891af7SMartin KaFai Lau __u32 meta_left; 758a138aedSMartin KaFai Lau 768a138aedSMartin KaFai Lau if (btf->data_size < sizeof(struct btf_header)) { 778a138aedSMartin KaFai Lau elog("BTF header not found\n"); 788a138aedSMartin KaFai Lau return -EINVAL; 798a138aedSMartin KaFai Lau } 808a138aedSMartin KaFai Lau 818a138aedSMartin KaFai Lau if (hdr->magic != BTF_MAGIC) { 828a138aedSMartin KaFai Lau elog("Invalid BTF magic:%x\n", hdr->magic); 838a138aedSMartin KaFai Lau return -EINVAL; 848a138aedSMartin KaFai Lau } 858a138aedSMartin KaFai Lau 868a138aedSMartin KaFai Lau if (hdr->version != BTF_VERSION) { 878a138aedSMartin KaFai Lau elog("Unsupported BTF version:%u\n", hdr->version); 888a138aedSMartin KaFai Lau return -ENOTSUP; 898a138aedSMartin KaFai Lau } 908a138aedSMartin KaFai Lau 918a138aedSMartin KaFai Lau if (hdr->flags) { 928a138aedSMartin KaFai Lau elog("Unsupported BTF flags:%x\n", hdr->flags); 938a138aedSMartin KaFai Lau return -ENOTSUP; 948a138aedSMartin KaFai Lau } 958a138aedSMartin KaFai Lau 968a138aedSMartin KaFai Lau meta_left = btf->data_size - sizeof(*hdr); 978a138aedSMartin KaFai Lau if (!meta_left) { 988a138aedSMartin KaFai Lau elog("BTF has no data\n"); 998a138aedSMartin KaFai Lau return -EINVAL; 1008a138aedSMartin KaFai Lau } 1018a138aedSMartin KaFai Lau 1028a138aedSMartin KaFai Lau if (meta_left < hdr->type_off) { 1038a138aedSMartin KaFai Lau elog("Invalid BTF type section offset:%u\n", hdr->type_off); 1048a138aedSMartin KaFai Lau return -EINVAL; 1058a138aedSMartin KaFai Lau } 1068a138aedSMartin KaFai Lau 1078a138aedSMartin KaFai Lau if (meta_left < hdr->str_off) { 1088a138aedSMartin KaFai Lau elog("Invalid BTF string section offset:%u\n", hdr->str_off); 1098a138aedSMartin KaFai Lau return -EINVAL; 1108a138aedSMartin KaFai Lau } 1118a138aedSMartin KaFai Lau 1128a138aedSMartin KaFai Lau if (hdr->type_off >= hdr->str_off) { 1138a138aedSMartin KaFai Lau elog("BTF type section offset >= string section offset. No type?\n"); 1148a138aedSMartin KaFai Lau return -EINVAL; 1158a138aedSMartin KaFai Lau } 1168a138aedSMartin KaFai Lau 1178a138aedSMartin KaFai Lau if (hdr->type_off & 0x02) { 1188a138aedSMartin KaFai Lau elog("BTF type section is not aligned to 4 bytes\n"); 1198a138aedSMartin KaFai Lau return -EINVAL; 1208a138aedSMartin KaFai Lau } 1218a138aedSMartin KaFai Lau 1228a138aedSMartin KaFai Lau btf->nohdr_data = btf->hdr + 1; 1238a138aedSMartin KaFai Lau 1248a138aedSMartin KaFai Lau return 0; 1258a138aedSMartin KaFai Lau } 1268a138aedSMartin KaFai Lau 1278a138aedSMartin KaFai Lau static int btf_parse_str_sec(struct btf *btf, btf_print_fn_t err_log) 1288a138aedSMartin KaFai Lau { 1298a138aedSMartin KaFai Lau const struct btf_header *hdr = btf->hdr; 1308a138aedSMartin KaFai Lau const char *start = btf->nohdr_data + hdr->str_off; 1318a138aedSMartin KaFai Lau const char *end = start + btf->hdr->str_len; 1328a138aedSMartin KaFai Lau 1338a138aedSMartin KaFai Lau if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET || 1348a138aedSMartin KaFai Lau start[0] || end[-1]) { 1358a138aedSMartin KaFai Lau elog("Invalid BTF string section\n"); 1368a138aedSMartin KaFai Lau return -EINVAL; 1378a138aedSMartin KaFai Lau } 1388a138aedSMartin KaFai Lau 1398a138aedSMartin KaFai Lau btf->strings = start; 1408a138aedSMartin KaFai Lau 1418a138aedSMartin KaFai Lau return 0; 1428a138aedSMartin KaFai Lau } 1438a138aedSMartin KaFai Lau 1448a138aedSMartin KaFai Lau static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log) 1458a138aedSMartin KaFai Lau { 1468a138aedSMartin KaFai Lau struct btf_header *hdr = btf->hdr; 1478a138aedSMartin KaFai Lau void *nohdr_data = btf->nohdr_data; 1488a138aedSMartin KaFai Lau void *next_type = nohdr_data + hdr->type_off; 1498a138aedSMartin KaFai Lau void *end_type = nohdr_data + hdr->str_off; 1508a138aedSMartin KaFai Lau 1518a138aedSMartin KaFai Lau while (next_type < end_type) { 1528a138aedSMartin KaFai Lau struct btf_type *t = next_type; 1535b891af7SMartin KaFai Lau __u16 vlen = BTF_INFO_VLEN(t->info); 1548a138aedSMartin KaFai Lau int err; 1558a138aedSMartin KaFai Lau 1568a138aedSMartin KaFai Lau next_type += sizeof(*t); 1578a138aedSMartin KaFai Lau switch (BTF_INFO_KIND(t->info)) { 1588a138aedSMartin KaFai Lau case BTF_KIND_INT: 1598a138aedSMartin KaFai Lau next_type += sizeof(int); 1608a138aedSMartin KaFai Lau break; 1618a138aedSMartin KaFai Lau case BTF_KIND_ARRAY: 1628a138aedSMartin KaFai Lau next_type += sizeof(struct btf_array); 1638a138aedSMartin KaFai Lau break; 1648a138aedSMartin KaFai Lau case BTF_KIND_STRUCT: 1658a138aedSMartin KaFai Lau case BTF_KIND_UNION: 1668a138aedSMartin KaFai Lau next_type += vlen * sizeof(struct btf_member); 1678a138aedSMartin KaFai Lau break; 1688a138aedSMartin KaFai Lau case BTF_KIND_ENUM: 1698a138aedSMartin KaFai Lau next_type += vlen * sizeof(struct btf_enum); 1708a138aedSMartin KaFai Lau break; 1718a138aedSMartin KaFai Lau case BTF_KIND_TYPEDEF: 1728a138aedSMartin KaFai Lau case BTF_KIND_PTR: 1738a138aedSMartin KaFai Lau case BTF_KIND_FWD: 1748a138aedSMartin KaFai Lau case BTF_KIND_VOLATILE: 1758a138aedSMartin KaFai Lau case BTF_KIND_CONST: 1768a138aedSMartin KaFai Lau case BTF_KIND_RESTRICT: 1778a138aedSMartin KaFai Lau break; 1788a138aedSMartin KaFai Lau default: 1798a138aedSMartin KaFai Lau elog("Unsupported BTF_KIND:%u\n", 1808a138aedSMartin KaFai Lau BTF_INFO_KIND(t->info)); 1818a138aedSMartin KaFai Lau return -EINVAL; 1828a138aedSMartin KaFai Lau } 1838a138aedSMartin KaFai Lau 1848a138aedSMartin KaFai Lau err = btf_add_type(btf, t); 1858a138aedSMartin KaFai Lau if (err) 1868a138aedSMartin KaFai Lau return err; 1878a138aedSMartin KaFai Lau } 1888a138aedSMartin KaFai Lau 1898a138aedSMartin KaFai Lau return 0; 1908a138aedSMartin KaFai Lau } 1918a138aedSMartin KaFai Lau 1928a138aedSMartin KaFai Lau static const struct btf_type *btf_type_by_id(const struct btf *btf, 1935b891af7SMartin KaFai Lau __u32 type_id) 1948a138aedSMartin KaFai Lau { 1958a138aedSMartin KaFai Lau if (type_id > btf->nr_types) 1968a138aedSMartin KaFai Lau return NULL; 1978a138aedSMartin KaFai Lau 1988a138aedSMartin KaFai Lau return btf->types[type_id]; 1998a138aedSMartin KaFai Lau } 2008a138aedSMartin KaFai Lau 2018a138aedSMartin KaFai Lau static bool btf_type_is_void(const struct btf_type *t) 2028a138aedSMartin KaFai Lau { 2038a138aedSMartin KaFai Lau return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD; 2048a138aedSMartin KaFai Lau } 2058a138aedSMartin KaFai Lau 2068a138aedSMartin KaFai Lau static bool btf_type_is_void_or_null(const struct btf_type *t) 2078a138aedSMartin KaFai Lau { 2088a138aedSMartin KaFai Lau return !t || btf_type_is_void(t); 2098a138aedSMartin KaFai Lau } 2108a138aedSMartin KaFai Lau 2115b891af7SMartin KaFai Lau static __s64 btf_type_size(const struct btf_type *t) 2128a138aedSMartin KaFai Lau { 2138a138aedSMartin KaFai Lau switch (BTF_INFO_KIND(t->info)) { 2148a138aedSMartin KaFai Lau case BTF_KIND_INT: 2158a138aedSMartin KaFai Lau case BTF_KIND_STRUCT: 2168a138aedSMartin KaFai Lau case BTF_KIND_UNION: 2178a138aedSMartin KaFai Lau case BTF_KIND_ENUM: 2188a138aedSMartin KaFai Lau return t->size; 2198a138aedSMartin KaFai Lau case BTF_KIND_PTR: 2208a138aedSMartin KaFai Lau return sizeof(void *); 2218a138aedSMartin KaFai Lau default: 2228a138aedSMartin KaFai Lau return -EINVAL; 2238a138aedSMartin KaFai Lau } 2248a138aedSMartin KaFai Lau } 2258a138aedSMartin KaFai Lau 2268a138aedSMartin KaFai Lau #define MAX_RESOLVE_DEPTH 32 2278a138aedSMartin KaFai Lau 2285b891af7SMartin KaFai Lau __s64 btf__resolve_size(const struct btf *btf, __u32 type_id) 2298a138aedSMartin KaFai Lau { 2308a138aedSMartin KaFai Lau const struct btf_array *array; 2318a138aedSMartin KaFai Lau const struct btf_type *t; 2325b891af7SMartin KaFai Lau __u32 nelems = 1; 2335b891af7SMartin KaFai Lau __s64 size = -1; 2348a138aedSMartin KaFai Lau int i; 2358a138aedSMartin KaFai Lau 2368a138aedSMartin KaFai Lau t = btf_type_by_id(btf, type_id); 2378a138aedSMartin KaFai Lau for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t); 2388a138aedSMartin KaFai Lau i++) { 2398a138aedSMartin KaFai Lau size = btf_type_size(t); 2408a138aedSMartin KaFai Lau if (size >= 0) 2418a138aedSMartin KaFai Lau break; 2428a138aedSMartin KaFai Lau 2438a138aedSMartin KaFai Lau switch (BTF_INFO_KIND(t->info)) { 2448a138aedSMartin KaFai Lau case BTF_KIND_TYPEDEF: 2458a138aedSMartin KaFai Lau case BTF_KIND_VOLATILE: 2468a138aedSMartin KaFai Lau case BTF_KIND_CONST: 2478a138aedSMartin KaFai Lau case BTF_KIND_RESTRICT: 2488a138aedSMartin KaFai Lau type_id = t->type; 2498a138aedSMartin KaFai Lau break; 2508a138aedSMartin KaFai Lau case BTF_KIND_ARRAY: 2518a138aedSMartin KaFai Lau array = (const struct btf_array *)(t + 1); 2528a138aedSMartin KaFai Lau if (nelems && array->nelems > UINT32_MAX / nelems) 2538a138aedSMartin KaFai Lau return -E2BIG; 2548a138aedSMartin KaFai Lau nelems *= array->nelems; 2558a138aedSMartin KaFai Lau type_id = array->type; 2568a138aedSMartin KaFai Lau break; 2578a138aedSMartin KaFai Lau default: 2588a138aedSMartin KaFai Lau return -EINVAL; 2598a138aedSMartin KaFai Lau } 2608a138aedSMartin KaFai Lau 2618a138aedSMartin KaFai Lau t = btf_type_by_id(btf, type_id); 2628a138aedSMartin KaFai Lau } 2638a138aedSMartin KaFai Lau 2648a138aedSMartin KaFai Lau if (size < 0) 2658a138aedSMartin KaFai Lau return -EINVAL; 2668a138aedSMartin KaFai Lau 2678a138aedSMartin KaFai Lau if (nelems && size > UINT32_MAX / nelems) 2688a138aedSMartin KaFai Lau return -E2BIG; 2698a138aedSMartin KaFai Lau 2708a138aedSMartin KaFai Lau return nelems * size; 2718a138aedSMartin KaFai Lau } 2728a138aedSMartin KaFai Lau 2735b891af7SMartin KaFai Lau __s32 btf__find_by_name(const struct btf *btf, const char *type_name) 2748a138aedSMartin KaFai Lau { 2755b891af7SMartin KaFai Lau __u32 i; 2768a138aedSMartin KaFai Lau 2778a138aedSMartin KaFai Lau if (!strcmp(type_name, "void")) 2788a138aedSMartin KaFai Lau return 0; 2798a138aedSMartin KaFai Lau 2808a138aedSMartin KaFai Lau for (i = 1; i <= btf->nr_types; i++) { 2818a138aedSMartin KaFai Lau const struct btf_type *t = btf->types[i]; 282fbcf93ebSMartin KaFai Lau const char *name = btf_name_by_offset(btf, t->name_off); 2838a138aedSMartin KaFai Lau 2848a138aedSMartin KaFai Lau if (name && !strcmp(type_name, name)) 2858a138aedSMartin KaFai Lau return i; 2868a138aedSMartin KaFai Lau } 2878a138aedSMartin KaFai Lau 2888a138aedSMartin KaFai Lau return -ENOENT; 2898a138aedSMartin KaFai Lau } 2908a138aedSMartin KaFai Lau 2918a138aedSMartin KaFai Lau void btf__free(struct btf *btf) 2928a138aedSMartin KaFai Lau { 2938a138aedSMartin KaFai Lau if (!btf) 2948a138aedSMartin KaFai Lau return; 2958a138aedSMartin KaFai Lau 2968a138aedSMartin KaFai Lau if (btf->fd != -1) 2978a138aedSMartin KaFai Lau close(btf->fd); 2988a138aedSMartin KaFai Lau 2998a138aedSMartin KaFai Lau free(btf->data); 3008a138aedSMartin KaFai Lau free(btf->types); 3018a138aedSMartin KaFai Lau free(btf); 3028a138aedSMartin KaFai Lau } 3038a138aedSMartin KaFai Lau 3045b891af7SMartin KaFai Lau struct btf *btf__new(__u8 *data, __u32 size, btf_print_fn_t err_log) 3058a138aedSMartin KaFai Lau { 3065b891af7SMartin KaFai Lau __u32 log_buf_size = 0; 3078a138aedSMartin KaFai Lau char *log_buf = NULL; 3088a138aedSMartin KaFai Lau struct btf *btf; 3098a138aedSMartin KaFai Lau int err; 3108a138aedSMartin KaFai Lau 3118a138aedSMartin KaFai Lau btf = calloc(1, sizeof(struct btf)); 3128a138aedSMartin KaFai Lau if (!btf) 3138a138aedSMartin KaFai Lau return ERR_PTR(-ENOMEM); 3148a138aedSMartin KaFai Lau 3158a138aedSMartin KaFai Lau btf->fd = -1; 3168a138aedSMartin KaFai Lau 3178a138aedSMartin KaFai Lau if (err_log) { 3188a138aedSMartin KaFai Lau log_buf = malloc(BPF_LOG_BUF_SIZE); 3198a138aedSMartin KaFai Lau if (!log_buf) { 3208a138aedSMartin KaFai Lau err = -ENOMEM; 3218a138aedSMartin KaFai Lau goto done; 3228a138aedSMartin KaFai Lau } 3238a138aedSMartin KaFai Lau *log_buf = 0; 3248a138aedSMartin KaFai Lau log_buf_size = BPF_LOG_BUF_SIZE; 3258a138aedSMartin KaFai Lau } 3268a138aedSMartin KaFai Lau 3278a138aedSMartin KaFai Lau btf->data = malloc(size); 3288a138aedSMartin KaFai Lau if (!btf->data) { 3298a138aedSMartin KaFai Lau err = -ENOMEM; 3308a138aedSMartin KaFai Lau goto done; 3318a138aedSMartin KaFai Lau } 3328a138aedSMartin KaFai Lau 3338a138aedSMartin KaFai Lau memcpy(btf->data, data, size); 3348a138aedSMartin KaFai Lau btf->data_size = size; 3358a138aedSMartin KaFai Lau 3368a138aedSMartin KaFai Lau btf->fd = bpf_load_btf(btf->data, btf->data_size, 3378a138aedSMartin KaFai Lau log_buf, log_buf_size, false); 3388a138aedSMartin KaFai Lau 3398a138aedSMartin KaFai Lau if (btf->fd == -1) { 3408a138aedSMartin KaFai Lau err = -errno; 3418a138aedSMartin KaFai Lau elog("Error loading BTF: %s(%d)\n", strerror(errno), errno); 3428a138aedSMartin KaFai Lau if (log_buf && *log_buf) 3438a138aedSMartin KaFai Lau elog("%s\n", log_buf); 3448a138aedSMartin KaFai Lau goto done; 3458a138aedSMartin KaFai Lau } 3468a138aedSMartin KaFai Lau 3478a138aedSMartin KaFai Lau err = btf_parse_hdr(btf, err_log); 3488a138aedSMartin KaFai Lau if (err) 3498a138aedSMartin KaFai Lau goto done; 3508a138aedSMartin KaFai Lau 3518a138aedSMartin KaFai Lau err = btf_parse_str_sec(btf, err_log); 3528a138aedSMartin KaFai Lau if (err) 3538a138aedSMartin KaFai Lau goto done; 3548a138aedSMartin KaFai Lau 3558a138aedSMartin KaFai Lau err = btf_parse_type_sec(btf, err_log); 3568a138aedSMartin KaFai Lau 3578a138aedSMartin KaFai Lau done: 3588a138aedSMartin KaFai Lau free(log_buf); 3598a138aedSMartin KaFai Lau 3608a138aedSMartin KaFai Lau if (err) { 3618a138aedSMartin KaFai Lau btf__free(btf); 3628a138aedSMartin KaFai Lau return ERR_PTR(err); 3638a138aedSMartin KaFai Lau } 3648a138aedSMartin KaFai Lau 3658a138aedSMartin KaFai Lau return btf; 3668a138aedSMartin KaFai Lau } 3678a138aedSMartin KaFai Lau 3688a138aedSMartin KaFai Lau int btf__fd(const struct btf *btf) 3698a138aedSMartin KaFai Lau { 3708a138aedSMartin KaFai Lau return btf->fd; 3718a138aedSMartin KaFai Lau } 372