1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <getopt.h> 6 #include <fcntl.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <stdbool.h> 10 #include <string.h> 11 #include <unistd.h> 12 #include <errno.h> 13 #include <pthread.h> 14 15 #include "elf-parse.h" 16 17 static Elf_Shdr *check_data_sec; 18 static Elf_Shdr *tracepoint_data_sec; 19 20 static inline void *get_index(void *start, int entsize, int index) 21 { 22 return start + (entsize * index); 23 } 24 25 static int compare_strings(const void *a, const void *b) 26 { 27 const char *av = *(const char **)a; 28 const char *bv = *(const char **)b; 29 30 return strcmp(av, bv); 31 } 32 33 struct elf_tracepoint { 34 Elf_Ehdr *ehdr; 35 const char **array; 36 int count; 37 }; 38 39 #define REALLOC_SIZE (1 << 10) 40 #define REALLOC_MASK (REALLOC_SIZE - 1) 41 42 static int add_string(const char *str, const char ***vals, int *count) 43 { 44 const char **array = *vals; 45 46 if (!(*count & REALLOC_MASK)) { 47 int size = (*count) + REALLOC_SIZE; 48 49 array = realloc(array, sizeof(char *) * size); 50 if (!array) { 51 fprintf(stderr, "Failed memory allocation\n"); 52 return -1; 53 } 54 *vals = array; 55 } 56 57 array[(*count)++] = str; 58 return 0; 59 } 60 61 /** 62 * for_each_shdr_str - iterator that reads strings that are in an ELF section. 63 * @len: "int" to hold the length of the current string 64 * @ehdr: A pointer to the ehdr of the ELF file 65 * @sec: The section that has the strings to iterate on 66 * 67 * This is a for loop that iterates over all the nul terminated strings 68 * that are in a given ELF section. The variable "str" will hold 69 * the current string for each iteration and the passed in @len will 70 * contain the strlen() of that string. 71 */ 72 #define for_each_shdr_str(len, ehdr, sec) \ 73 for (const char *str = (void *)(ehdr) + shdr_offset(sec), \ 74 *end = str + shdr_size(sec); \ 75 len = strlen(str), str < end; \ 76 str += (len) + 1) 77 78 79 static void make_trace_array(struct elf_tracepoint *etrace) 80 { 81 Elf_Ehdr *ehdr = etrace->ehdr; 82 const char **vals = NULL; 83 int count = 0; 84 int len; 85 86 etrace->array = NULL; 87 88 /* 89 * The __tracepoint_check section is filled with strings of the 90 * names of tracepoints (in tracepoint_strings). Create an array 91 * that points to each string and then sort the array. 92 */ 93 for_each_shdr_str(len, ehdr, check_data_sec) { 94 if (!len) 95 continue; 96 if (add_string(str, &vals, &count) < 0) 97 return; 98 } 99 100 /* If CONFIG_TRACEPOINT_VERIFY_USED is not set, there's nothing to do */ 101 if (!count) 102 return; 103 104 qsort(vals, count, sizeof(char *), compare_strings); 105 106 etrace->array = vals; 107 etrace->count = count; 108 } 109 110 static int find_event(const char *str, void *array, size_t size) 111 { 112 return bsearch(&str, array, size, sizeof(char *), compare_strings) != NULL; 113 } 114 115 static void check_tracepoints(struct elf_tracepoint *etrace) 116 { 117 Elf_Ehdr *ehdr = etrace->ehdr; 118 int len; 119 120 if (!etrace->array) 121 return; 122 123 /* 124 * The __tracepoints_strings section holds all the names of the 125 * defined tracepoints. If any of them are not in the 126 * __tracepoint_check_section it means they are not used. 127 */ 128 for_each_shdr_str(len, ehdr, tracepoint_data_sec) { 129 if (!len) 130 continue; 131 if (!find_event(str, etrace->array, etrace->count)) { 132 fprintf(stderr, "warning: tracepoint '%s' is unused.\n", str); 133 } 134 } 135 136 free(etrace->array); 137 } 138 139 static void *tracepoint_check(struct elf_tracepoint *etrace) 140 { 141 make_trace_array(etrace); 142 check_tracepoints(etrace); 143 144 return NULL; 145 } 146 147 static int process_tracepoints(void *addr, char const *const fname) 148 { 149 struct elf_tracepoint etrace = {0}; 150 Elf_Ehdr *ehdr = addr; 151 Elf_Shdr *shdr_start; 152 Elf_Shdr *string_sec; 153 const char *secstrings; 154 unsigned int shnum; 155 unsigned int shstrndx; 156 int shentsize; 157 int idx; 158 int done = 2; 159 160 shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); 161 shentsize = ehdr_shentsize(ehdr); 162 163 shstrndx = ehdr_shstrndx(ehdr); 164 if (shstrndx == SHN_XINDEX) 165 shstrndx = shdr_link(shdr_start); 166 string_sec = get_index(shdr_start, shentsize, shstrndx); 167 secstrings = (const char *)ehdr + shdr_offset(string_sec); 168 169 shnum = ehdr_shnum(ehdr); 170 if (shnum == SHN_UNDEF) 171 shnum = shdr_size(shdr_start); 172 173 for (int i = 0; done && i < shnum; i++) { 174 Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); 175 176 idx = shdr_name(shdr); 177 178 /* locate the __tracepoint_check in vmlinux */ 179 if (!strcmp(secstrings + idx, "__tracepoint_check")) { 180 check_data_sec = shdr; 181 done--; 182 } 183 184 /* locate the __tracepoints_ptrs section in vmlinux */ 185 if (!strcmp(secstrings + idx, "__tracepoints_strings")) { 186 tracepoint_data_sec = shdr; 187 done--; 188 } 189 } 190 191 if (!check_data_sec) { 192 fprintf(stderr, "no __tracepoint_check in file: %s\n", fname); 193 return -1; 194 } 195 196 if (!tracepoint_data_sec) { 197 fprintf(stderr, "no __tracepoint_strings in file: %s\n", fname); 198 return -1; 199 } 200 201 etrace.ehdr = ehdr; 202 tracepoint_check(&etrace); 203 return 0; 204 } 205 206 int main(int argc, char *argv[]) 207 { 208 int n_error = 0; 209 size_t size = 0; 210 void *addr = NULL; 211 212 if (argc < 2) { 213 fprintf(stderr, "usage: tracepoint-update vmlinux...\n"); 214 return 0; 215 } 216 217 /* Process each file in turn, allowing deep failure. */ 218 for (int i = 1; i < argc; i++) { 219 addr = elf_map(argv[i], &size, 1 << ET_REL); 220 if (!addr) { 221 ++n_error; 222 continue; 223 } 224 225 if (process_tracepoints(addr, argv[i])) 226 ++n_error; 227 228 elf_unmap(addr, size); 229 } 230 231 return !!n_error; 232 } 233