834 lines · c
1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)2 3/*4 * resolve_btfids scans ELF object for .BTF_ids section and resolves5 * its symbols with BTF ID values.6 *7 * Each symbol points to 4 bytes data and is expected to have8 * following name syntax:9 *10 * __BTF_ID__<type>__<symbol>[__<id>]11 *12 * type is:13 *14 * func - lookup BTF_KIND_FUNC symbol with <symbol> name15 * and store its ID into the data:16 *17 * __BTF_ID__func__vfs_close__1:18 * .zero 419 *20 * struct - lookup BTF_KIND_STRUCT symbol with <symbol> name21 * and store its ID into the data:22 *23 * __BTF_ID__struct__sk_buff__1:24 * .zero 425 *26 * union - lookup BTF_KIND_UNION symbol with <symbol> name27 * and store its ID into the data:28 *29 * __BTF_ID__union__thread_union__1:30 * .zero 431 *32 * typedef - lookup BTF_KIND_TYPEDEF symbol with <symbol> name33 * and store its ID into the data:34 *35 * __BTF_ID__typedef__pid_t__1:36 * .zero 437 *38 * set - store symbol size into first 4 bytes and sort following39 * ID list40 *41 * __BTF_ID__set__list:42 * .zero 443 * list:44 * __BTF_ID__func__vfs_getattr__3:45 * .zero 446 * __BTF_ID__func__vfs_fallocate__4:47 * .zero 448 *49 * set8 - store symbol size into first 4 bytes and sort following50 * ID list51 *52 * __BTF_ID__set8__list:53 * .zero 854 * list:55 * __BTF_ID__func__vfs_getattr__3:56 * .zero 457 * .word (1 << 0) | (1 << 2)58 * __BTF_ID__func__vfs_fallocate__5:59 * .zero 460 * .word (1 << 3) | (1 << 1) | (1 << 2)61 */62 63#define _GNU_SOURCE64#include <stdio.h>65#include <string.h>66#include <unistd.h>67#include <stdlib.h>68#include <libelf.h>69#include <gelf.h>70#include <sys/stat.h>71#include <fcntl.h>72#include <errno.h>73#include <linux/btf_ids.h>74#include <linux/rbtree.h>75#include <linux/zalloc.h>76#include <linux/err.h>77#include <bpf/btf.h>78#include <bpf/libbpf.h>79#include <subcmd/parse-options.h>80 81#define BTF_IDS_SECTION ".BTF_ids"82#define BTF_ID_PREFIX "__BTF_ID__"83 84#define BTF_STRUCT "struct"85#define BTF_UNION "union"86#define BTF_TYPEDEF "typedef"87#define BTF_FUNC "func"88#define BTF_SET "set"89#define BTF_SET8 "set8"90 91#define ADDR_CNT 10092 93#if __BYTE_ORDER == __LITTLE_ENDIAN94# define ELFDATANATIVE ELFDATA2LSB95#elif __BYTE_ORDER == __BIG_ENDIAN96# define ELFDATANATIVE ELFDATA2MSB97#else98# error "Unknown machine endianness!"99#endif100 101struct btf_id {102 struct rb_node rb_node;103 char *name;104 union {105 int id;106 int cnt;107 };108 int addr_cnt;109 bool is_set;110 bool is_set8;111 Elf64_Addr addr[ADDR_CNT];112};113 114struct object {115 const char *path;116 const char *btf;117 const char *base_btf_path;118 119 struct {120 int fd;121 Elf *elf;122 Elf_Data *symbols;123 Elf_Data *idlist;124 int symbols_shndx;125 int idlist_shndx;126 size_t strtabidx;127 unsigned long idlist_addr;128 int encoding;129 } efile;130 131 struct rb_root sets;132 struct rb_root structs;133 struct rb_root unions;134 struct rb_root typedefs;135 struct rb_root funcs;136 137 int nr_funcs;138 int nr_structs;139 int nr_unions;140 int nr_typedefs;141};142 143static int verbose;144 145static int eprintf(int level, int var, const char *fmt, ...)146{147 va_list args;148 int ret = 0;149 150 if (var >= level) {151 va_start(args, fmt);152 ret = vfprintf(stderr, fmt, args);153 va_end(args);154 }155 return ret;156}157 158#ifndef pr_fmt159#define pr_fmt(fmt) fmt160#endif161 162#define pr_debug(fmt, ...) \163 eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__)164#define pr_debugN(n, fmt, ...) \165 eprintf(n, verbose, pr_fmt(fmt), ##__VA_ARGS__)166#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)167#define pr_err(fmt, ...) \168 eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)169#define pr_info(fmt, ...) \170 eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)171 172static bool is_btf_id(const char *name)173{174 return name && !strncmp(name, BTF_ID_PREFIX, sizeof(BTF_ID_PREFIX) - 1);175}176 177static struct btf_id *btf_id__find(struct rb_root *root, const char *name)178{179 struct rb_node *p = root->rb_node;180 struct btf_id *id;181 int cmp;182 183 while (p) {184 id = rb_entry(p, struct btf_id, rb_node);185 cmp = strcmp(id->name, name);186 if (cmp < 0)187 p = p->rb_left;188 else if (cmp > 0)189 p = p->rb_right;190 else191 return id;192 }193 return NULL;194}195 196static struct btf_id *197btf_id__add(struct rb_root *root, char *name, bool unique)198{199 struct rb_node **p = &root->rb_node;200 struct rb_node *parent = NULL;201 struct btf_id *id;202 int cmp;203 204 while (*p != NULL) {205 parent = *p;206 id = rb_entry(parent, struct btf_id, rb_node);207 cmp = strcmp(id->name, name);208 if (cmp < 0)209 p = &(*p)->rb_left;210 else if (cmp > 0)211 p = &(*p)->rb_right;212 else213 return unique ? NULL : id;214 }215 216 id = zalloc(sizeof(*id));217 if (id) {218 pr_debug("adding symbol %s\n", name);219 id->name = name;220 rb_link_node(&id->rb_node, parent, p);221 rb_insert_color(&id->rb_node, root);222 }223 return id;224}225 226static char *get_id(const char *prefix_end)227{228 /*229 * __BTF_ID__func__vfs_truncate__0230 * prefix_end = ^231 * pos = ^232 */233 int len = strlen(prefix_end);234 int pos = sizeof("__") - 1;235 char *p, *id;236 237 if (pos >= len)238 return NULL;239 240 id = strdup(prefix_end + pos);241 if (id) {242 /*243 * __BTF_ID__func__vfs_truncate__0244 * id = ^245 *246 * cut the unique id part247 */248 p = strrchr(id, '_');249 p--;250 if (*p != '_') {251 free(id);252 return NULL;253 }254 *p = '\0';255 }256 return id;257}258 259static struct btf_id *add_set(struct object *obj, char *name, bool is_set8)260{261 /*262 * __BTF_ID__set__name263 * name = ^264 * id = ^265 */266 char *id = name + (is_set8 ? sizeof(BTF_SET8 "__") : sizeof(BTF_SET "__")) - 1;267 int len = strlen(name);268 269 if (id >= name + len) {270 pr_err("FAILED to parse set name: %s\n", name);271 return NULL;272 }273 274 return btf_id__add(&obj->sets, id, true);275}276 277static struct btf_id *add_symbol(struct rb_root *root, char *name, size_t size)278{279 char *id;280 281 id = get_id(name + size);282 if (!id) {283 pr_err("FAILED to parse symbol name: %s\n", name);284 return NULL;285 }286 287 return btf_id__add(root, id, false);288}289 290/* Older libelf.h and glibc elf.h might not yet define the ELF compression types. */291#ifndef SHF_COMPRESSED292#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */293#endif294 295/*296 * The data of compressed section should be aligned to 4297 * (for 32bit) or 8 (for 64 bit) bytes. The binutils ld298 * sets sh_addralign to 1, which makes libelf fail with299 * misaligned section error during the update:300 * FAILED elf_update(WRITE): invalid section alignment301 *302 * While waiting for ld fix, we fix the compressed sections303 * sh_addralign value manualy.304 */305static int compressed_section_fix(Elf *elf, Elf_Scn *scn, GElf_Shdr *sh)306{307 int expected = gelf_getclass(elf) == ELFCLASS32 ? 4 : 8;308 309 if (!(sh->sh_flags & SHF_COMPRESSED))310 return 0;311 312 if (sh->sh_addralign == expected)313 return 0;314 315 pr_debug2(" - fixing wrong alignment sh_addralign %u, expected %u\n",316 sh->sh_addralign, expected);317 318 sh->sh_addralign = expected;319 320 if (gelf_update_shdr(scn, sh) == 0) {321 pr_err("FAILED cannot update section header: %s\n",322 elf_errmsg(-1));323 return -1;324 }325 return 0;326}327 328static int elf_collect(struct object *obj)329{330 Elf_Scn *scn = NULL;331 size_t shdrstrndx;332 GElf_Ehdr ehdr;333 int idx = 0;334 Elf *elf;335 int fd;336 337 fd = open(obj->path, O_RDWR, 0666);338 if (fd == -1) {339 pr_err("FAILED cannot open %s: %s\n",340 obj->path, strerror(errno));341 return -1;342 }343 344 elf_version(EV_CURRENT);345 346 elf = elf_begin(fd, ELF_C_RDWR_MMAP, NULL);347 if (!elf) {348 close(fd);349 pr_err("FAILED cannot create ELF descriptor: %s\n",350 elf_errmsg(-1));351 return -1;352 }353 354 obj->efile.fd = fd;355 obj->efile.elf = elf;356 357 elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);358 359 if (elf_getshdrstrndx(elf, &shdrstrndx) != 0) {360 pr_err("FAILED cannot get shdr str ndx\n");361 return -1;362 }363 364 if (gelf_getehdr(obj->efile.elf, &ehdr) == NULL) {365 pr_err("FAILED cannot get ELF header: %s\n",366 elf_errmsg(-1));367 return -1;368 }369 obj->efile.encoding = ehdr.e_ident[EI_DATA];370 371 /*372 * Scan all the elf sections and look for save data373 * from .BTF_ids section and symbols.374 */375 while ((scn = elf_nextscn(elf, scn)) != NULL) {376 Elf_Data *data;377 GElf_Shdr sh;378 char *name;379 380 idx++;381 if (gelf_getshdr(scn, &sh) != &sh) {382 pr_err("FAILED get section(%d) header\n", idx);383 return -1;384 }385 386 name = elf_strptr(elf, shdrstrndx, sh.sh_name);387 if (!name) {388 pr_err("FAILED get section(%d) name\n", idx);389 return -1;390 }391 392 data = elf_getdata(scn, 0);393 if (!data) {394 pr_err("FAILED to get section(%d) data from %s\n",395 idx, name);396 return -1;397 }398 399 pr_debug2("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",400 idx, name, (unsigned long) data->d_size,401 (int) sh.sh_link, (unsigned long) sh.sh_flags,402 (int) sh.sh_type);403 404 if (sh.sh_type == SHT_SYMTAB) {405 obj->efile.symbols = data;406 obj->efile.symbols_shndx = idx;407 obj->efile.strtabidx = sh.sh_link;408 } else if (!strcmp(name, BTF_IDS_SECTION)) {409 obj->efile.idlist = data;410 obj->efile.idlist_shndx = idx;411 obj->efile.idlist_addr = sh.sh_addr;412 } else if (!strcmp(name, BTF_BASE_ELF_SEC)) {413 /* If a .BTF.base section is found, do not resolve414 * BTF ids relative to vmlinux; resolve relative415 * to the .BTF.base section instead. btf__parse_split()416 * will take care of this once the base BTF it is417 * passed is NULL.418 */419 obj->base_btf_path = NULL;420 }421 422 if (compressed_section_fix(elf, scn, &sh))423 return -1;424 }425 426 return 0;427}428 429static int symbols_collect(struct object *obj)430{431 Elf_Scn *scn = NULL;432 int n, i;433 GElf_Shdr sh;434 char *name;435 436 scn = elf_getscn(obj->efile.elf, obj->efile.symbols_shndx);437 if (!scn)438 return -1;439 440 if (gelf_getshdr(scn, &sh) != &sh)441 return -1;442 443 n = sh.sh_size / sh.sh_entsize;444 445 /*446 * Scan symbols and look for the ones starting with447 * __BTF_ID__* over .BTF_ids section.448 */449 for (i = 0; i < n; i++) {450 char *prefix;451 struct btf_id *id;452 GElf_Sym sym;453 454 if (!gelf_getsym(obj->efile.symbols, i, &sym))455 return -1;456 457 if (sym.st_shndx != obj->efile.idlist_shndx)458 continue;459 460 name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,461 sym.st_name);462 463 if (!is_btf_id(name))464 continue;465 466 /*467 * __BTF_ID__TYPE__vfs_truncate__0468 * prefix = ^469 */470 prefix = name + sizeof(BTF_ID_PREFIX) - 1;471 472 /* struct */473 if (!strncmp(prefix, BTF_STRUCT, sizeof(BTF_STRUCT) - 1)) {474 obj->nr_structs++;475 id = add_symbol(&obj->structs, prefix, sizeof(BTF_STRUCT) - 1);476 /* union */477 } else if (!strncmp(prefix, BTF_UNION, sizeof(BTF_UNION) - 1)) {478 obj->nr_unions++;479 id = add_symbol(&obj->unions, prefix, sizeof(BTF_UNION) - 1);480 /* typedef */481 } else if (!strncmp(prefix, BTF_TYPEDEF, sizeof(BTF_TYPEDEF) - 1)) {482 obj->nr_typedefs++;483 id = add_symbol(&obj->typedefs, prefix, sizeof(BTF_TYPEDEF) - 1);484 /* func */485 } else if (!strncmp(prefix, BTF_FUNC, sizeof(BTF_FUNC) - 1)) {486 obj->nr_funcs++;487 id = add_symbol(&obj->funcs, prefix, sizeof(BTF_FUNC) - 1);488 /* set8 */489 } else if (!strncmp(prefix, BTF_SET8, sizeof(BTF_SET8) - 1)) {490 id = add_set(obj, prefix, true);491 /*492 * SET8 objects store list's count, which is encoded493 * in symbol's size, together with 'cnt' field hence494 * that - 1.495 */496 if (id) {497 id->cnt = sym.st_size / sizeof(uint64_t) - 1;498 id->is_set8 = true;499 }500 /* set */501 } else if (!strncmp(prefix, BTF_SET, sizeof(BTF_SET) - 1)) {502 id = add_set(obj, prefix, false);503 /*504 * SET objects store list's count, which is encoded505 * in symbol's size, together with 'cnt' field hence506 * that - 1.507 */508 if (id) {509 id->cnt = sym.st_size / sizeof(int) - 1;510 id->is_set = true;511 }512 } else {513 pr_err("FAILED unsupported prefix %s\n", prefix);514 return -1;515 }516 517 if (!id)518 return -ENOMEM;519 520 if (id->addr_cnt >= ADDR_CNT) {521 pr_err("FAILED symbol %s crossed the number of allowed lists\n",522 id->name);523 return -1;524 }525 id->addr[id->addr_cnt++] = sym.st_value;526 }527 528 return 0;529}530 531static int symbols_resolve(struct object *obj)532{533 int nr_typedefs = obj->nr_typedefs;534 int nr_structs = obj->nr_structs;535 int nr_unions = obj->nr_unions;536 int nr_funcs = obj->nr_funcs;537 struct btf *base_btf = NULL;538 int err, type_id;539 struct btf *btf;540 __u32 nr_types;541 542 if (obj->base_btf_path) {543 base_btf = btf__parse(obj->base_btf_path, NULL);544 err = libbpf_get_error(base_btf);545 if (err) {546 pr_err("FAILED: load base BTF from %s: %s\n",547 obj->base_btf_path, strerror(-err));548 return -1;549 }550 }551 552 btf = btf__parse_split(obj->btf ?: obj->path, base_btf);553 err = libbpf_get_error(btf);554 if (err) {555 pr_err("FAILED: load BTF from %s: %s\n",556 obj->btf ?: obj->path, strerror(-err));557 goto out;558 }559 560 err = -1;561 nr_types = btf__type_cnt(btf);562 563 /*564 * Iterate all the BTF types and search for collected symbol IDs.565 */566 for (type_id = 1; type_id < nr_types; type_id++) {567 const struct btf_type *type;568 struct rb_root *root;569 struct btf_id *id;570 const char *str;571 int *nr;572 573 type = btf__type_by_id(btf, type_id);574 if (!type) {575 pr_err("FAILED: malformed BTF, can't resolve type for ID %d\n",576 type_id);577 goto out;578 }579 580 if (btf_is_func(type) && nr_funcs) {581 nr = &nr_funcs;582 root = &obj->funcs;583 } else if (btf_is_struct(type) && nr_structs) {584 nr = &nr_structs;585 root = &obj->structs;586 } else if (btf_is_union(type) && nr_unions) {587 nr = &nr_unions;588 root = &obj->unions;589 } else if (btf_is_typedef(type) && nr_typedefs) {590 nr = &nr_typedefs;591 root = &obj->typedefs;592 } else593 continue;594 595 str = btf__name_by_offset(btf, type->name_off);596 if (!str) {597 pr_err("FAILED: malformed BTF, can't resolve name for ID %d\n",598 type_id);599 goto out;600 }601 602 id = btf_id__find(root, str);603 if (id) {604 if (id->id) {605 pr_info("WARN: multiple IDs found for '%s': %d, %d - using %d\n",606 str, id->id, type_id, id->id);607 } else {608 id->id = type_id;609 (*nr)--;610 }611 }612 }613 614 err = 0;615out:616 btf__free(base_btf);617 btf__free(btf);618 return err;619}620 621static int id_patch(struct object *obj, struct btf_id *id)622{623 Elf_Data *data = obj->efile.idlist;624 int *ptr = data->d_buf;625 int i;626 627 /* For set, set8, id->id may be 0 */628 if (!id->id && !id->is_set && !id->is_set8)629 pr_err("WARN: resolve_btfids: unresolved symbol %s\n", id->name);630 631 for (i = 0; i < id->addr_cnt; i++) {632 unsigned long addr = id->addr[i];633 unsigned long idx = addr - obj->efile.idlist_addr;634 635 pr_debug("patching addr %5lu: ID %7d [%s]\n",636 idx, id->id, id->name);637 638 if (idx >= data->d_size) {639 pr_err("FAILED patching index %lu out of bounds %lu\n",640 idx, data->d_size);641 return -1;642 }643 644 idx = idx / sizeof(int);645 ptr[idx] = id->id;646 }647 648 return 0;649}650 651static int __symbols_patch(struct object *obj, struct rb_root *root)652{653 struct rb_node *next;654 struct btf_id *id;655 656 next = rb_first(root);657 while (next) {658 id = rb_entry(next, struct btf_id, rb_node);659 660 if (id_patch(obj, id))661 return -1;662 663 next = rb_next(next);664 }665 return 0;666}667 668static int cmp_id(const void *pa, const void *pb)669{670 const int *a = pa, *b = pb;671 672 return *a - *b;673}674 675static int sets_patch(struct object *obj)676{677 Elf_Data *data = obj->efile.idlist;678 struct rb_node *next;679 680 next = rb_first(&obj->sets);681 while (next) {682 struct btf_id_set8 *set8;683 struct btf_id_set *set;684 unsigned long addr, off;685 struct btf_id *id;686 687 id = rb_entry(next, struct btf_id, rb_node);688 addr = id->addr[0];689 off = addr - obj->efile.idlist_addr;690 691 /* sets are unique */692 if (id->addr_cnt != 1) {693 pr_err("FAILED malformed data for set '%s'\n",694 id->name);695 return -1;696 }697 698 if (id->is_set) {699 set = data->d_buf + off;700 qsort(set->ids, set->cnt, sizeof(set->ids[0]), cmp_id);701 } else {702 set8 = data->d_buf + off;703 /*704 * Make sure id is at the beginning of the pairs705 * struct, otherwise the below qsort would not work.706 */707 BUILD_BUG_ON((u32 *)set8->pairs != &set8->pairs[0].id);708 qsort(set8->pairs, set8->cnt, sizeof(set8->pairs[0]), cmp_id);709 710 /*711 * When ELF endianness does not match endianness of the712 * host, libelf will do the translation when updating713 * the ELF. This, however, corrupts SET8 flags which are714 * already in the target endianness. So, let's bswap715 * them to the host endianness and libelf will then716 * correctly translate everything.717 */718 if (obj->efile.encoding != ELFDATANATIVE) {719 int i;720 721 set8->flags = bswap_32(set8->flags);722 for (i = 0; i < set8->cnt; i++) {723 set8->pairs[i].flags =724 bswap_32(set8->pairs[i].flags);725 }726 }727 }728 729 pr_debug("sorting addr %5lu: cnt %6d [%s]\n",730 off, id->is_set ? set->cnt : set8->cnt, id->name);731 732 next = rb_next(next);733 }734 return 0;735}736 737static int symbols_patch(struct object *obj)738{739 off_t err;740 741 if (__symbols_patch(obj, &obj->structs) ||742 __symbols_patch(obj, &obj->unions) ||743 __symbols_patch(obj, &obj->typedefs) ||744 __symbols_patch(obj, &obj->funcs) ||745 __symbols_patch(obj, &obj->sets))746 return -1;747 748 if (sets_patch(obj))749 return -1;750 751 /* Set type to ensure endian translation occurs. */752 obj->efile.idlist->d_type = ELF_T_WORD;753 754 elf_flagdata(obj->efile.idlist, ELF_C_SET, ELF_F_DIRTY);755 756 err = elf_update(obj->efile.elf, ELF_C_WRITE);757 if (err < 0) {758 pr_err("FAILED elf_update(WRITE): %s\n",759 elf_errmsg(-1));760 }761 762 pr_debug("update %s for %s\n",763 err >= 0 ? "ok" : "failed", obj->path);764 return err < 0 ? -1 : 0;765}766 767static const char * const resolve_btfids_usage[] = {768 "resolve_btfids [<options>] <ELF object>",769 NULL770};771 772int main(int argc, const char **argv)773{774 struct object obj = {775 .efile = {776 .idlist_shndx = -1,777 .symbols_shndx = -1,778 },779 .structs = RB_ROOT,780 .unions = RB_ROOT,781 .typedefs = RB_ROOT,782 .funcs = RB_ROOT,783 .sets = RB_ROOT,784 };785 struct option btfid_options[] = {786 OPT_INCR('v', "verbose", &verbose,787 "be more verbose (show errors, etc)"),788 OPT_STRING(0, "btf", &obj.btf, "BTF data",789 "BTF data"),790 OPT_STRING('b', "btf_base", &obj.base_btf_path, "file",791 "path of file providing base BTF"),792 OPT_END()793 };794 int err = -1;795 796 argc = parse_options(argc, argv, btfid_options, resolve_btfids_usage,797 PARSE_OPT_STOP_AT_NON_OPTION);798 if (argc != 1)799 usage_with_options(resolve_btfids_usage, btfid_options);800 801 obj.path = argv[0];802 803 if (elf_collect(&obj))804 goto out;805 806 /*807 * We did not find .BTF_ids section or symbols section,808 * nothing to do..809 */810 if (obj.efile.idlist_shndx == -1 ||811 obj.efile.symbols_shndx == -1) {812 pr_debug("Cannot find .BTF_ids or symbols sections, nothing to do\n");813 err = 0;814 goto out;815 }816 817 if (symbols_collect(&obj))818 goto out;819 820 if (symbols_resolve(&obj))821 goto out;822 823 if (symbols_patch(&obj))824 goto out;825 826 err = 0;827out:828 if (obj.efile.elf) {829 elf_end(obj.efile.elf);830 close(obj.efile.fd);831 }832 return err;833}834