at v6.19-rc7 266 lines 6.1 kB view raw
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 17static Elf_Shdr *check_data_sec; 18static Elf_Shdr *tracepoint_data_sec; 19 20static inline void *get_index(void *start, int entsize, int index) 21{ 22 return start + (entsize * index); 23} 24 25static 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 33struct 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 42static 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 free(*vals); 53 *vals = NULL; 54 return -1; 55 } 56 *vals = array; 57 } 58 59 array[(*count)++] = str; 60 return 0; 61} 62 63/** 64 * for_each_shdr_str - iterator that reads strings that are in an ELF section. 65 * @len: "int" to hold the length of the current string 66 * @ehdr: A pointer to the ehdr of the ELF file 67 * @sec: The section that has the strings to iterate on 68 * 69 * This is a for loop that iterates over all the nul terminated strings 70 * that are in a given ELF section. The variable "str" will hold 71 * the current string for each iteration and the passed in @len will 72 * contain the strlen() of that string. 73 */ 74#define for_each_shdr_str(len, ehdr, sec) \ 75 for (const char *str = (void *)(ehdr) + shdr_offset(sec), \ 76 *end = str + shdr_size(sec); \ 77 len = strlen(str), str < end; \ 78 str += (len) + 1) 79 80 81static void make_trace_array(struct elf_tracepoint *etrace) 82{ 83 Elf_Ehdr *ehdr = etrace->ehdr; 84 const char **vals = NULL; 85 int count = 0; 86 int len; 87 88 etrace->array = NULL; 89 90 /* 91 * The __tracepoint_check section is filled with strings of the 92 * names of tracepoints (in tracepoint_strings). Create an array 93 * that points to each string and then sort the array. 94 */ 95 for_each_shdr_str(len, ehdr, check_data_sec) { 96 if (!len) 97 continue; 98 if (add_string(str, &vals, &count) < 0) 99 return; 100 } 101 102 /* If CONFIG_TRACEPOINT_VERIFY_USED is not set, there's nothing to do */ 103 if (!count) 104 return; 105 106 qsort(vals, count, sizeof(char *), compare_strings); 107 108 etrace->array = vals; 109 etrace->count = count; 110} 111 112static int find_event(const char *str, void *array, size_t size) 113{ 114 return bsearch(&str, array, size, sizeof(char *), compare_strings) != NULL; 115} 116 117static void check_tracepoints(struct elf_tracepoint *etrace, const char *fname) 118{ 119 Elf_Ehdr *ehdr = etrace->ehdr; 120 int len; 121 122 if (!etrace->array) 123 return; 124 125 /* 126 * The __tracepoints_strings section holds all the names of the 127 * defined tracepoints. If any of them are not in the 128 * __tracepoint_check_section it means they are not used. 129 */ 130 for_each_shdr_str(len, ehdr, tracepoint_data_sec) { 131 if (!len) 132 continue; 133 if (!find_event(str, etrace->array, etrace->count)) { 134 fprintf(stderr, "warning: tracepoint '%s' is unused", str); 135 if (fname) 136 fprintf(stderr, " in module %s\n", fname); 137 else 138 fprintf(stderr, "\n"); 139 } 140 } 141 142 free(etrace->array); 143} 144 145static void *tracepoint_check(struct elf_tracepoint *etrace, const char *fname) 146{ 147 make_trace_array(etrace); 148 check_tracepoints(etrace, fname); 149 150 return NULL; 151} 152 153static int process_tracepoints(bool mod, void *addr, const char *fname) 154{ 155 struct elf_tracepoint etrace = {0}; 156 Elf_Ehdr *ehdr = addr; 157 Elf_Shdr *shdr_start; 158 Elf_Shdr *string_sec; 159 const char *secstrings; 160 unsigned int shnum; 161 unsigned int shstrndx; 162 int shentsize; 163 int idx; 164 int done = 2; 165 166 shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr)); 167 shentsize = ehdr_shentsize(ehdr); 168 169 shstrndx = ehdr_shstrndx(ehdr); 170 if (shstrndx == SHN_XINDEX) 171 shstrndx = shdr_link(shdr_start); 172 string_sec = get_index(shdr_start, shentsize, shstrndx); 173 secstrings = (const char *)ehdr + shdr_offset(string_sec); 174 175 shnum = ehdr_shnum(ehdr); 176 if (shnum == SHN_UNDEF) 177 shnum = shdr_size(shdr_start); 178 179 for (int i = 0; done && i < shnum; i++) { 180 Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); 181 182 idx = shdr_name(shdr); 183 184 /* locate the __tracepoint_check in vmlinux */ 185 if (!strcmp(secstrings + idx, "__tracepoint_check")) { 186 check_data_sec = shdr; 187 done--; 188 } 189 190 /* locate the __tracepoints_ptrs section in vmlinux */ 191 if (!strcmp(secstrings + idx, "__tracepoints_strings")) { 192 tracepoint_data_sec = shdr; 193 done--; 194 } 195 } 196 197 /* 198 * Modules may not have either section. But if it has one section, 199 * it should have both of them. 200 */ 201 if (mod && !check_data_sec && !tracepoint_data_sec) 202 return 0; 203 204 if (!check_data_sec) { 205 if (mod) { 206 fprintf(stderr, "warning: Module %s has only unused tracepoints\n", fname); 207 /* Do not fail build */ 208 return 0; 209 } 210 fprintf(stderr, "no __tracepoint_check in file: %s\n", fname); 211 return -1; 212 } 213 214 if (!tracepoint_data_sec) { 215 /* A module may reference only exported tracepoints */ 216 if (mod) 217 return 0; 218 fprintf(stderr, "no __tracepoint_strings in file: %s\n", fname); 219 return -1; 220 } 221 222 if (!mod) 223 fname = NULL; 224 225 etrace.ehdr = ehdr; 226 tracepoint_check(&etrace, fname); 227 return 0; 228} 229 230int main(int argc, char *argv[]) 231{ 232 int n_error = 0; 233 size_t size = 0; 234 void *addr = NULL; 235 bool mod = false; 236 237 if (argc > 1 && strcmp(argv[1], "--module") == 0) { 238 mod = true; 239 argc--; 240 argv++; 241 } 242 243 if (argc < 2) { 244 if (mod) 245 fprintf(stderr, "usage: tracepoint-update --module module...\n"); 246 else 247 fprintf(stderr, "usage: tracepoint-update vmlinux...\n"); 248 return 0; 249 } 250 251 /* Process each file in turn, allowing deep failure. */ 252 for (int i = 1; i < argc; i++) { 253 addr = elf_map(argv[i], &size, 1 << ET_REL); 254 if (!addr) { 255 ++n_error; 256 continue; 257 } 258 259 if (process_tracepoints(mod, addr, argv[i])) 260 ++n_error; 261 262 elf_unmap(addr, size); 263 } 264 265 return !!n_error; 266}