266 lines · 6.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only #include <sys/types.h>#include <sys/stat.h>#include <getopt.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <stdbool.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <pthread.h> #include "elf-parse.h" static Elf_Shdr *check_data_sec;static Elf_Shdr *tracepoint_data_sec; static inline void *get_index(void *start, int entsize, int index){	return start + (entsize * index);} static int compare_strings(const void *a, const void *b){	const char *av = *(const char **)a;	const char *bv = *(const char **)b; 	return strcmp(av, bv);} struct elf_tracepoint {	Elf_Ehdr *ehdr;	const char **array;	int count;}; #define REALLOC_SIZE (1 << 10)#define REALLOC_MASK (REALLOC_SIZE - 1) static int add_string(const char *str, const char ***vals, int *count){	const char **array = *vals; 	if (!(*count & REALLOC_MASK)) {		int size = (*count) + REALLOC_SIZE; 		array = realloc(array, sizeof(char *) * size);		if (!array) {			fprintf(stderr, "Failed memory allocation\n");			free(*vals);			*vals = NULL;			return -1;		}		*vals = array;	} 	array[(*count)++] = str;	return 0;} /** * for_each_shdr_str - iterator that reads strings that are in an ELF section. * @len: "int" to hold the length of the current string * @ehdr: A pointer to the ehdr of the ELF file * @sec: The section that has the strings to iterate on * * This is a for loop that iterates over all the nul terminated strings * that are in a given ELF section. The variable "str" will hold * the current string for each iteration and the passed in @len will * contain the strlen() of that string. */#define for_each_shdr_str(len, ehdr, sec)				\	for (const char *str = (void *)(ehdr) + shdr_offset(sec),	\			*end = str + shdr_size(sec);			\	     len = strlen(str), str < end;				\	     str += (len) + 1)  static void make_trace_array(struct elf_tracepoint *etrace){	Elf_Ehdr *ehdr = etrace->ehdr;	const char **vals = NULL;	int count = 0;	int len; 	etrace->array = NULL; 	/*	 * The __tracepoint_check section is filled with strings of the	 * names of tracepoints (in tracepoint_strings). Create an array	 * that points to each string and then sort the array.	 */	for_each_shdr_str(len, ehdr, check_data_sec) {		if (!len)			continue;		if (add_string(str, &vals, &count) < 0)			return;	} 	/* If CONFIG_TRACEPOINT_VERIFY_USED is not set, there's nothing to do */	if (!count)		return; 	qsort(vals, count, sizeof(char *), compare_strings); 	etrace->array = vals;	etrace->count = count;} static int find_event(const char *str, void *array, size_t size){	return bsearch(&str, array, size, sizeof(char *), compare_strings) != NULL;} static void check_tracepoints(struct elf_tracepoint *etrace, const char *fname){	Elf_Ehdr *ehdr = etrace->ehdr;	int len; 	if (!etrace->array)		return; 	/*	 * The __tracepoints_strings section holds all the names of the	 * defined tracepoints. If any of them are not in the	 * __tracepoint_check_section it means they are not used.	 */	for_each_shdr_str(len, ehdr, tracepoint_data_sec) {		if (!len)			continue;		if (!find_event(str, etrace->array, etrace->count)) {			fprintf(stderr, "warning: tracepoint '%s' is unused", str);			if (fname)				fprintf(stderr, " in module %s\n", fname);			else				fprintf(stderr, "\n");		}	} 	free(etrace->array);} static void *tracepoint_check(struct elf_tracepoint *etrace, const char *fname){	make_trace_array(etrace);	check_tracepoints(etrace, fname); 	return NULL;} static int process_tracepoints(bool mod, void *addr, const char *fname){	struct elf_tracepoint etrace = {0};	Elf_Ehdr *ehdr = addr;	Elf_Shdr *shdr_start;	Elf_Shdr *string_sec;	const char *secstrings;	unsigned int shnum;	unsigned int shstrndx;	int shentsize;	int idx;	int done = 2; 	shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));	shentsize = ehdr_shentsize(ehdr); 	shstrndx = ehdr_shstrndx(ehdr);	if (shstrndx == SHN_XINDEX)		shstrndx = shdr_link(shdr_start);	string_sec = get_index(shdr_start, shentsize, shstrndx);	secstrings = (const char *)ehdr + shdr_offset(string_sec); 	shnum = ehdr_shnum(ehdr);	if (shnum == SHN_UNDEF)		shnum = shdr_size(shdr_start); 	for (int i = 0; done && i < shnum; i++) {		Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); 		idx = shdr_name(shdr); 		/* locate the __tracepoint_check in vmlinux */		if (!strcmp(secstrings + idx, "__tracepoint_check")) {			check_data_sec = shdr;			done--;		} 		/* locate the __tracepoints_ptrs section in vmlinux */		if (!strcmp(secstrings + idx, "__tracepoints_strings")) {			tracepoint_data_sec = shdr;			done--;		}	} 	/*	 * Modules may not have either section. But if it has one section,	 * it should have both of them.	 */	if (mod && !check_data_sec && !tracepoint_data_sec)		return 0; 	if (!check_data_sec) {		if (mod) {			fprintf(stderr, "warning: Module %s has only unused tracepoints\n", fname);			/* Do not fail build */			return 0;		}		fprintf(stderr,	"no __tracepoint_check in file: %s\n", fname);		return -1;	} 	if (!tracepoint_data_sec) {		/* A module may reference only exported tracepoints */		if (mod)			return 0;		fprintf(stderr,	"no __tracepoint_strings in file: %s\n", fname);		return -1;	} 	if (!mod)		fname = NULL; 	etrace.ehdr = ehdr;	tracepoint_check(&etrace, fname);	return 0;} int main(int argc, char *argv[]){	int n_error = 0;	size_t size = 0;	void *addr = NULL;	bool mod = false; 	if (argc > 1 && strcmp(argv[1], "--module") == 0) {		mod = true;		argc--;		argv++;	} 	if (argc < 2) {		if (mod)			fprintf(stderr, "usage: tracepoint-update --module module...\n");		else			fprintf(stderr, "usage: tracepoint-update vmlinux...\n");		return 0;	} 	/* Process each file in turn, allowing deep failure. */	for (int i = 1; i < argc; i++) {		addr = elf_map(argv[i], &size, 1 << ET_REL);		if (!addr) {			++n_error;			continue;		} 		if (process_tracepoints(mod, addr, argv[i]))			++n_error; 		elf_unmap(addr, size);	} 	return !!n_error;}