Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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}