xref: /linux-6.15/scripts/sorttable.c (revision 67afb7f5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * sorttable.c: Sort the kernel's table
4  *
5  * Added ORC unwind tables sort support and other updates:
6  * Copyright (C) 1999-2019 Alibaba Group Holding Limited. by:
7  * Shile Zhang <[email protected]>
8  *
9  * Copyright 2011 - 2012 Cavium, Inc.
10  *
11  * Based on code taken from recortmcount.c which is:
12  *
13  * Copyright 2009 John F. Reiser <[email protected]>.  All rights reserved.
14  *
15  * Restructured to fit Linux format, as well as other updates:
16  * Copyright 2010 Steven Rostedt <[email protected]>, Red Hat Inc.
17  */
18 
19 /*
20  * Strategy: alter the vmlinux file in-place.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <getopt.h>
27 #include <elf.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <pthread.h>
35 
36 #include <tools/be_byteshift.h>
37 #include <tools/le_byteshift.h>
38 
39 #ifndef EM_ARCOMPACT
40 #define EM_ARCOMPACT	93
41 #endif
42 
43 #ifndef EM_XTENSA
44 #define EM_XTENSA	94
45 #endif
46 
47 #ifndef EM_AARCH64
48 #define EM_AARCH64	183
49 #endif
50 
51 #ifndef EM_MICROBLAZE
52 #define EM_MICROBLAZE	189
53 #endif
54 
55 #ifndef EM_ARCV2
56 #define EM_ARCV2	195
57 #endif
58 
59 #ifndef EM_RISCV
60 #define EM_RISCV	243
61 #endif
62 
63 #ifndef EM_LOONGARCH
64 #define EM_LOONGARCH	258
65 #endif
66 
67 typedef union {
68 	Elf32_Ehdr	e32;
69 	Elf64_Ehdr	e64;
70 } Elf_Ehdr;
71 
72 typedef union {
73 	Elf32_Shdr	e32;
74 	Elf64_Shdr	e64;
75 } Elf_Shdr;
76 
77 typedef union {
78 	Elf32_Sym	e32;
79 	Elf64_Sym	e64;
80 } Elf_Sym;
81 
82 static uint32_t (*r)(const uint32_t *);
83 static uint16_t (*r2)(const uint16_t *);
84 static uint64_t (*r8)(const uint64_t *);
85 static void (*w)(uint32_t, uint32_t *);
86 typedef void (*table_sort_t)(char *, int);
87 
88 static uint64_t ehdr64_shoff(Elf_Ehdr *ehdr)
89 {
90 	return r8(&ehdr->e64.e_shoff);
91 }
92 
93 static uint64_t ehdr32_shoff(Elf_Ehdr *ehdr)
94 {
95 	return r(&ehdr->e32.e_shoff);
96 }
97 
98 #define EHDR_HALF(fn_name)				\
99 static uint16_t ehdr64_##fn_name(Elf_Ehdr *ehdr)	\
100 {							\
101 	return r2(&ehdr->e64.e_##fn_name);		\
102 }							\
103 							\
104 static uint16_t ehdr32_##fn_name(Elf_Ehdr *ehdr)	\
105 {							\
106 	return r2(&ehdr->e32.e_##fn_name);		\
107 }
108 
109 EHDR_HALF(shentsize)
110 EHDR_HALF(shstrndx)
111 EHDR_HALF(shnum)
112 
113 #define SHDR_WORD(fn_name)				\
114 static uint32_t shdr64_##fn_name(Elf_Shdr *shdr)	\
115 {							\
116 	return r(&shdr->e64.sh_##fn_name);		\
117 }							\
118 							\
119 static uint32_t shdr32_##fn_name(Elf_Shdr *shdr)	\
120 {							\
121 	return r(&shdr->e32.sh_##fn_name);		\
122 }
123 
124 #define SHDR_ADDR(fn_name)				\
125 static uint64_t shdr64_##fn_name(Elf_Shdr *shdr)	\
126 {							\
127 	return r8(&shdr->e64.sh_##fn_name);		\
128 }							\
129 							\
130 static uint64_t shdr32_##fn_name(Elf_Shdr *shdr)	\
131 {							\
132 	return r(&shdr->e32.sh_##fn_name);		\
133 }
134 
135 #define SHDR_WORD(fn_name)				\
136 static uint32_t shdr64_##fn_name(Elf_Shdr *shdr)	\
137 {							\
138 	return r(&shdr->e64.sh_##fn_name);		\
139 }							\
140 							\
141 static uint32_t shdr32_##fn_name(Elf_Shdr *shdr)	\
142 {							\
143 	return r(&shdr->e32.sh_##fn_name);		\
144 }
145 
146 SHDR_ADDR(addr)
147 SHDR_ADDR(offset)
148 SHDR_ADDR(size)
149 SHDR_ADDR(entsize)
150 
151 SHDR_WORD(link)
152 SHDR_WORD(name)
153 SHDR_WORD(type)
154 
155 /*
156  * Get the whole file as a programming convenience in order to avoid
157  * malloc+lseek+read+free of many pieces.  If successful, then mmap
158  * avoids copying unused pieces; else just read the whole file.
159  * Open for both read and write.
160  */
161 static void *mmap_file(char const *fname, size_t *size)
162 {
163 	int fd;
164 	struct stat sb;
165 	void *addr = NULL;
166 
167 	fd = open(fname, O_RDWR);
168 	if (fd < 0) {
169 		perror(fname);
170 		return NULL;
171 	}
172 	if (fstat(fd, &sb) < 0) {
173 		perror(fname);
174 		goto out;
175 	}
176 	if (!S_ISREG(sb.st_mode)) {
177 		fprintf(stderr, "not a regular file: %s\n", fname);
178 		goto out;
179 	}
180 
181 	addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
182 	if (addr == MAP_FAILED) {
183 		fprintf(stderr, "Could not mmap file: %s\n", fname);
184 		goto out;
185 	}
186 
187 	*size = sb.st_size;
188 
189 out:
190 	close(fd);
191 	return addr;
192 }
193 
194 static uint32_t rbe(const uint32_t *x)
195 {
196 	return get_unaligned_be32(x);
197 }
198 
199 static uint16_t r2be(const uint16_t *x)
200 {
201 	return get_unaligned_be16(x);
202 }
203 
204 static uint64_t r8be(const uint64_t *x)
205 {
206 	return get_unaligned_be64(x);
207 }
208 
209 static uint32_t rle(const uint32_t *x)
210 {
211 	return get_unaligned_le32(x);
212 }
213 
214 static uint16_t r2le(const uint16_t *x)
215 {
216 	return get_unaligned_le16(x);
217 }
218 
219 static uint64_t r8le(const uint64_t *x)
220 {
221 	return get_unaligned_le64(x);
222 }
223 
224 static void wbe(uint32_t val, uint32_t *x)
225 {
226 	put_unaligned_be32(val, x);
227 }
228 
229 static void wle(uint32_t val, uint32_t *x)
230 {
231 	put_unaligned_le32(val, x);
232 }
233 
234 /*
235  * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
236  * the way to -256..-1, to avoid conflicting with real section
237  * indices.
238  */
239 #define SPECIAL(i) ((i) - (SHN_HIRESERVE + 1))
240 
241 static inline int is_shndx_special(unsigned int i)
242 {
243 	return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE;
244 }
245 
246 /* Accessor for sym->st_shndx, hides ugliness of "64k sections" */
247 static inline unsigned int get_secindex(unsigned int shndx,
248 					unsigned int sym_offs,
249 					const Elf32_Word *symtab_shndx_start)
250 {
251 	if (is_shndx_special(shndx))
252 		return SPECIAL(shndx);
253 	if (shndx != SHN_XINDEX)
254 		return shndx;
255 	return r(&symtab_shndx_start[sym_offs]);
256 }
257 
258 static int compare_extable_32(const void *a, const void *b)
259 {
260 	Elf32_Addr av = r(a);
261 	Elf32_Addr bv = r(b);
262 
263 	if (av < bv)
264 		return -1;
265 	return av > bv;
266 }
267 
268 static int compare_extable_64(const void *a, const void *b)
269 {
270 	Elf64_Addr av = r8(a);
271 	Elf64_Addr bv = r8(b);
272 
273 	if (av < bv)
274 		return -1;
275 	return av > bv;
276 }
277 
278 static inline void *get_index(void *start, int entsize, int index)
279 {
280 	return start + (entsize * index);
281 }
282 
283 /* 32 bit and 64 bit are very similar */
284 #include "sorttable.h"
285 #define SORTTABLE_64
286 #include "sorttable.h"
287 
288 static int compare_relative_table(const void *a, const void *b)
289 {
290 	int32_t av = (int32_t)r(a);
291 	int32_t bv = (int32_t)r(b);
292 
293 	if (av < bv)
294 		return -1;
295 	if (av > bv)
296 		return 1;
297 	return 0;
298 }
299 
300 static void sort_relative_table(char *extab_image, int image_size)
301 {
302 	int i = 0;
303 
304 	/*
305 	 * Do the same thing the runtime sort does, first normalize to
306 	 * being relative to the start of the section.
307 	 */
308 	while (i < image_size) {
309 		uint32_t *loc = (uint32_t *)(extab_image + i);
310 		w(r(loc) + i, loc);
311 		i += 4;
312 	}
313 
314 	qsort(extab_image, image_size / 8, 8, compare_relative_table);
315 
316 	/* Now denormalize. */
317 	i = 0;
318 	while (i < image_size) {
319 		uint32_t *loc = (uint32_t *)(extab_image + i);
320 		w(r(loc) - i, loc);
321 		i += 4;
322 	}
323 }
324 
325 static void sort_relative_table_with_data(char *extab_image, int image_size)
326 {
327 	int i = 0;
328 
329 	while (i < image_size) {
330 		uint32_t *loc = (uint32_t *)(extab_image + i);
331 
332 		w(r(loc) + i, loc);
333 		w(r(loc + 1) + i + 4, loc + 1);
334 		/* Don't touch the fixup type or data */
335 
336 		i += sizeof(uint32_t) * 3;
337 	}
338 
339 	qsort(extab_image, image_size / 12, 12, compare_relative_table);
340 
341 	i = 0;
342 	while (i < image_size) {
343 		uint32_t *loc = (uint32_t *)(extab_image + i);
344 
345 		w(r(loc) - i, loc);
346 		w(r(loc + 1) - (i + 4), loc + 1);
347 		/* Don't touch the fixup type or data */
348 
349 		i += sizeof(uint32_t) * 3;
350 	}
351 }
352 
353 static int do_file(char const *const fname, void *addr)
354 {
355 	int rc = -1;
356 	Elf_Ehdr *ehdr = addr;
357 	table_sort_t custom_sort = NULL;
358 
359 	switch (ehdr->e32.e_ident[EI_DATA]) {
360 	case ELFDATA2LSB:
361 		r	= rle;
362 		r2	= r2le;
363 		r8	= r8le;
364 		w	= wle;
365 		break;
366 	case ELFDATA2MSB:
367 		r	= rbe;
368 		r2	= r2be;
369 		r8	= r8be;
370 		w	= wbe;
371 		break;
372 	default:
373 		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
374 			ehdr->e32.e_ident[EI_DATA], fname);
375 		return -1;
376 	}
377 
378 	if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
379 	    (r2(&ehdr->e32.e_type) != ET_EXEC && r2(&ehdr->e32.e_type) != ET_DYN) ||
380 	    ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
381 		fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname);
382 		return -1;
383 	}
384 
385 	switch (r2(&ehdr->e32.e_machine)) {
386 	case EM_386:
387 	case EM_AARCH64:
388 	case EM_LOONGARCH:
389 	case EM_RISCV:
390 	case EM_S390:
391 	case EM_X86_64:
392 		custom_sort = sort_relative_table_with_data;
393 		break;
394 	case EM_PARISC:
395 	case EM_PPC:
396 	case EM_PPC64:
397 		custom_sort = sort_relative_table;
398 		break;
399 	case EM_ARCOMPACT:
400 	case EM_ARCV2:
401 	case EM_ARM:
402 	case EM_MICROBLAZE:
403 	case EM_MIPS:
404 	case EM_XTENSA:
405 		break;
406 	default:
407 		fprintf(stderr, "unrecognized e_machine %d %s\n",
408 			r2(&ehdr->e32.e_machine), fname);
409 		return -1;
410 	}
411 
412 	switch (ehdr->e32.e_ident[EI_CLASS]) {
413 	case ELFCLASS32:
414 		if (r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||
415 		    r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {
416 			fprintf(stderr,
417 				"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
418 			break;
419 		}
420 		rc = do_sort_32(ehdr, fname, custom_sort);
421 		break;
422 	case ELFCLASS64:
423 		{
424 		if (r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||
425 		    r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {
426 			fprintf(stderr,
427 				"unrecognized ET_EXEC/ET_DYN file: %s\n",
428 				fname);
429 			break;
430 		}
431 		rc = do_sort_64(ehdr, fname, custom_sort);
432 		}
433 		break;
434 	default:
435 		fprintf(stderr, "unrecognized ELF class %d %s\n",
436 			ehdr->e32.e_ident[EI_CLASS], fname);
437 		break;
438 	}
439 
440 	return rc;
441 }
442 
443 int main(int argc, char *argv[])
444 {
445 	int i, n_error = 0;  /* gcc-4.3.0 false positive complaint */
446 	size_t size = 0;
447 	void *addr = NULL;
448 
449 	if (argc < 2) {
450 		fprintf(stderr, "usage: sorttable vmlinux...\n");
451 		return 0;
452 	}
453 
454 	/* Process each file in turn, allowing deep failure. */
455 	for (i = 1; i < argc; i++) {
456 		addr = mmap_file(argv[i], &size);
457 		if (!addr) {
458 			++n_error;
459 			continue;
460 		}
461 
462 		if (do_file(argv[i], addr))
463 			++n_error;
464 
465 		munmap(addr, size);
466 	}
467 
468 	return !!n_error;
469 }
470