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, const char *fname) 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", str); 133 if (fname) 134 fprintf(stderr, " in module %s\n", fname); 135 else 136 fprintf(stderr, "\n"); 137 } 138 } 139 140 free(etrace->array); 141 } 142 143 static void *tracepoint_check(struct elf_tracepoint *etrace, const char *fname) 144 { 145 make_trace_array(etrace); 146 check_tracepoints(etrace, fname); 147 148 return NULL; 149 } 150 151 static int process_tracepoints(bool mod, void *addr, const char *fname) 152 { 153 struct elf_tracepoint etrace = {0}; 154 Elf_Ehdr *ehdr = addr; 155 Elf_Shdr *shdr_start; 156 Elf_Shdr *string_sec; 157 const char *secstrings; 158 unsigned int shnum; 159 unsigned int shstrndx; 160 int shentsize; 161 int idx; 162 int done = 2; 163 164 shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); 165 shentsize = ehdr_shentsize(ehdr); 166 167 shstrndx = ehdr_shstrndx(ehdr); 168 if (shstrndx == SHN_XINDEX) 169 shstrndx = shdr_link(shdr_start); 170 string_sec = get_index(shdr_start, shentsize, shstrndx); 171 secstrings = (const char *)ehdr + shdr_offset(string_sec); 172 173 shnum = ehdr_shnum(ehdr); 174 if (shnum == SHN_UNDEF) 175 shnum = shdr_size(shdr_start); 176 177 for (int i = 0; done && i < shnum; i++) { 178 Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); 179 180 idx = shdr_name(shdr); 181 182 /* locate the __tracepoint_check in vmlinux */ 183 if (!strcmp(secstrings + idx, "__tracepoint_check")) { 184 check_data_sec = shdr; 185 done--; 186 } 187 188 /* locate the __tracepoints_ptrs section in vmlinux */ 189 if (!strcmp(secstrings + idx, "__tracepoints_strings")) { 190 tracepoint_data_sec = shdr; 191 done--; 192 } 193 } 194 195 /* 196 * Modules may not have either section. But if it has one section, 197 * it should have both of them. 198 */ 199 if (mod && !check_data_sec && !tracepoint_data_sec) 200 return 0; 201 202 if (!check_data_sec) { 203 if (mod) { 204 fprintf(stderr, "warning: Module %s has only unused tracepoints\n", fname); 205 /* Do not fail build */ 206 return 0; 207 } 208 fprintf(stderr, "no __tracepoint_check in file: %s\n", fname); 209 return -1; 210 } 211 212 if (!tracepoint_data_sec) { 213 fprintf(stderr, "no __tracepoint_strings in file: %s\n", fname); 214 return -1; 215 } 216 217 if (!mod) 218 fname = NULL; 219 220 etrace.ehdr = ehdr; 221 tracepoint_check(&etrace, fname); 222 return 0; 223 } 224 225 int main(int argc, char *argv[]) 226 { 227 int n_error = 0; 228 size_t size = 0; 229 void *addr = NULL; 230 bool mod = false; 231 232 if (argc > 1 && strcmp(argv[1], "--module") == 0) { 233 mod = true; 234 argc--; 235 argv++; 236 } 237 238 if (argc < 2) { 239 if (mod) 240 fprintf(stderr, "usage: tracepoint-update --module module...\n"); 241 else 242 fprintf(stderr, "usage: tracepoint-update vmlinux...\n"); 243 return 0; 244 } 245 246 /* Process each file in turn, allowing deep failure. */ 247 for (int i = 1; i < argc; i++) { 248 addr = elf_map(argv[i], &size, 1 << ET_REL); 249 if (!addr) { 250 ++n_error; 251 continue; 252 } 253 254 if (process_tracepoints(mod, addr, argv[i])) 255 ++n_error; 256 257 elf_unmap(addr, size); 258 } 259 260 return !!n_error; 261 } 262