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
get_index(void * start,int entsize,int index)20 static inline void *get_index(void *start, int entsize, int index)
21 {
22 return start + (entsize * index);
23 }
24
compare_strings(const void * a,const void * b)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
add_string(const char * str,const char *** vals,int * count)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
make_trace_array(struct elf_tracepoint * etrace)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
find_event(const char * str,void * array,size_t size)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
check_tracepoints(struct elf_tracepoint * etrace,const char * fname)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
tracepoint_check(struct elf_tracepoint * etrace,const char * fname)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
process_tracepoints(bool mod,void * addr,const char * fname)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
main(int argc,char * argv[])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