brintos

brintos / linux-shallow public Read only

0
0
Text · 18.4 KiB · d54aaa0 Raw
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