xref: /linux-6.15/scripts/sorttable.c (revision 17bed33a)
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 #define SYM_ADDR(fn_name)			\
156 static uint64_t sym64_##fn_name(Elf_Sym *sym)	\
157 {						\
158 	return r8(&sym->e64.st_##fn_name);	\
159 }						\
160 						\
161 static uint64_t sym32_##fn_name(Elf_Sym *sym)	\
162 {						\
163 	return r(&sym->e32.st_##fn_name);	\
164 }
165 
166 #define SYM_WORD(fn_name)			\
167 static uint32_t sym64_##fn_name(Elf_Sym *sym)	\
168 {						\
169 	return r(&sym->e64.st_##fn_name);	\
170 }						\
171 						\
172 static uint32_t sym32_##fn_name(Elf_Sym *sym)	\
173 {						\
174 	return r(&sym->e32.st_##fn_name);	\
175 }
176 
177 #define SYM_HALF(fn_name)			\
178 static uint16_t sym64_##fn_name(Elf_Sym *sym)	\
179 {						\
180 	return r2(&sym->e64.st_##fn_name);	\
181 }						\
182 						\
183 static uint16_t sym32_##fn_name(Elf_Sym *sym)	\
184 {						\
185 	return r2(&sym->e32.st_##fn_name);	\
186 }
187 
188 static uint8_t sym64_type(Elf_Sym *sym)
189 {
190 	return ELF64_ST_TYPE(sym->e64.st_info);
191 }
192 
193 static uint8_t sym32_type(Elf_Sym *sym)
194 {
195 	return ELF32_ST_TYPE(sym->e32.st_info);
196 }
197 
198 SYM_ADDR(value)
199 SYM_WORD(name)
200 SYM_HALF(shndx)
201 
202 /*
203  * Get the whole file as a programming convenience in order to avoid
204  * malloc+lseek+read+free of many pieces.  If successful, then mmap
205  * avoids copying unused pieces; else just read the whole file.
206  * Open for both read and write.
207  */
208 static void *mmap_file(char const *fname, size_t *size)
209 {
210 	int fd;
211 	struct stat sb;
212 	void *addr = NULL;
213 
214 	fd = open(fname, O_RDWR);
215 	if (fd < 0) {
216 		perror(fname);
217 		return NULL;
218 	}
219 	if (fstat(fd, &sb) < 0) {
220 		perror(fname);
221 		goto out;
222 	}
223 	if (!S_ISREG(sb.st_mode)) {
224 		fprintf(stderr, "not a regular file: %s\n", fname);
225 		goto out;
226 	}
227 
228 	addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
229 	if (addr == MAP_FAILED) {
230 		fprintf(stderr, "Could not mmap file: %s\n", fname);
231 		goto out;
232 	}
233 
234 	*size = sb.st_size;
235 
236 out:
237 	close(fd);
238 	return addr;
239 }
240 
241 static uint32_t rbe(const uint32_t *x)
242 {
243 	return get_unaligned_be32(x);
244 }
245 
246 static uint16_t r2be(const uint16_t *x)
247 {
248 	return get_unaligned_be16(x);
249 }
250 
251 static uint64_t r8be(const uint64_t *x)
252 {
253 	return get_unaligned_be64(x);
254 }
255 
256 static uint32_t rle(const uint32_t *x)
257 {
258 	return get_unaligned_le32(x);
259 }
260 
261 static uint16_t r2le(const uint16_t *x)
262 {
263 	return get_unaligned_le16(x);
264 }
265 
266 static uint64_t r8le(const uint64_t *x)
267 {
268 	return get_unaligned_le64(x);
269 }
270 
271 static void wbe(uint32_t val, uint32_t *x)
272 {
273 	put_unaligned_be32(val, x);
274 }
275 
276 static void wle(uint32_t val, uint32_t *x)
277 {
278 	put_unaligned_le32(val, x);
279 }
280 
281 /*
282  * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
283  * the way to -256..-1, to avoid conflicting with real section
284  * indices.
285  */
286 #define SPECIAL(i) ((i) - (SHN_HIRESERVE + 1))
287 
288 static inline int is_shndx_special(unsigned int i)
289 {
290 	return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE;
291 }
292 
293 /* Accessor for sym->st_shndx, hides ugliness of "64k sections" */
294 static inline unsigned int get_secindex(unsigned int shndx,
295 					unsigned int sym_offs,
296 					const Elf32_Word *symtab_shndx_start)
297 {
298 	if (is_shndx_special(shndx))
299 		return SPECIAL(shndx);
300 	if (shndx != SHN_XINDEX)
301 		return shndx;
302 	return r(&symtab_shndx_start[sym_offs]);
303 }
304 
305 static int compare_extable_32(const void *a, const void *b)
306 {
307 	Elf32_Addr av = r(a);
308 	Elf32_Addr bv = r(b);
309 
310 	if (av < bv)
311 		return -1;
312 	return av > bv;
313 }
314 
315 static int compare_extable_64(const void *a, const void *b)
316 {
317 	Elf64_Addr av = r8(a);
318 	Elf64_Addr bv = r8(b);
319 
320 	if (av < bv)
321 		return -1;
322 	return av > bv;
323 }
324 
325 static inline void *get_index(void *start, int entsize, int index)
326 {
327 	return start + (entsize * index);
328 }
329 
330 /* 32 bit and 64 bit are very similar */
331 #include "sorttable.h"
332 #define SORTTABLE_64
333 #include "sorttable.h"
334 
335 static int compare_relative_table(const void *a, const void *b)
336 {
337 	int32_t av = (int32_t)r(a);
338 	int32_t bv = (int32_t)r(b);
339 
340 	if (av < bv)
341 		return -1;
342 	if (av > bv)
343 		return 1;
344 	return 0;
345 }
346 
347 static void sort_relative_table(char *extab_image, int image_size)
348 {
349 	int i = 0;
350 
351 	/*
352 	 * Do the same thing the runtime sort does, first normalize to
353 	 * being relative to the start of the section.
354 	 */
355 	while (i < image_size) {
356 		uint32_t *loc = (uint32_t *)(extab_image + i);
357 		w(r(loc) + i, loc);
358 		i += 4;
359 	}
360 
361 	qsort(extab_image, image_size / 8, 8, compare_relative_table);
362 
363 	/* Now denormalize. */
364 	i = 0;
365 	while (i < image_size) {
366 		uint32_t *loc = (uint32_t *)(extab_image + i);
367 		w(r(loc) - i, loc);
368 		i += 4;
369 	}
370 }
371 
372 static void sort_relative_table_with_data(char *extab_image, int image_size)
373 {
374 	int i = 0;
375 
376 	while (i < image_size) {
377 		uint32_t *loc = (uint32_t *)(extab_image + i);
378 
379 		w(r(loc) + i, loc);
380 		w(r(loc + 1) + i + 4, loc + 1);
381 		/* Don't touch the fixup type or data */
382 
383 		i += sizeof(uint32_t) * 3;
384 	}
385 
386 	qsort(extab_image, image_size / 12, 12, compare_relative_table);
387 
388 	i = 0;
389 	while (i < image_size) {
390 		uint32_t *loc = (uint32_t *)(extab_image + i);
391 
392 		w(r(loc) - i, loc);
393 		w(r(loc + 1) - (i + 4), loc + 1);
394 		/* Don't touch the fixup type or data */
395 
396 		i += sizeof(uint32_t) * 3;
397 	}
398 }
399 
400 static int do_file(char const *const fname, void *addr)
401 {
402 	int rc = -1;
403 	Elf_Ehdr *ehdr = addr;
404 	table_sort_t custom_sort = NULL;
405 
406 	switch (ehdr->e32.e_ident[EI_DATA]) {
407 	case ELFDATA2LSB:
408 		r	= rle;
409 		r2	= r2le;
410 		r8	= r8le;
411 		w	= wle;
412 		break;
413 	case ELFDATA2MSB:
414 		r	= rbe;
415 		r2	= r2be;
416 		r8	= r8be;
417 		w	= wbe;
418 		break;
419 	default:
420 		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
421 			ehdr->e32.e_ident[EI_DATA], fname);
422 		return -1;
423 	}
424 
425 	if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
426 	    (r2(&ehdr->e32.e_type) != ET_EXEC && r2(&ehdr->e32.e_type) != ET_DYN) ||
427 	    ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
428 		fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname);
429 		return -1;
430 	}
431 
432 	switch (r2(&ehdr->e32.e_machine)) {
433 	case EM_386:
434 	case EM_AARCH64:
435 	case EM_LOONGARCH:
436 	case EM_RISCV:
437 	case EM_S390:
438 	case EM_X86_64:
439 		custom_sort = sort_relative_table_with_data;
440 		break;
441 	case EM_PARISC:
442 	case EM_PPC:
443 	case EM_PPC64:
444 		custom_sort = sort_relative_table;
445 		break;
446 	case EM_ARCOMPACT:
447 	case EM_ARCV2:
448 	case EM_ARM:
449 	case EM_MICROBLAZE:
450 	case EM_MIPS:
451 	case EM_XTENSA:
452 		break;
453 	default:
454 		fprintf(stderr, "unrecognized e_machine %d %s\n",
455 			r2(&ehdr->e32.e_machine), fname);
456 		return -1;
457 	}
458 
459 	switch (ehdr->e32.e_ident[EI_CLASS]) {
460 	case ELFCLASS32:
461 		if (r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||
462 		    r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {
463 			fprintf(stderr,
464 				"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
465 			break;
466 		}
467 		rc = do_sort_32(ehdr, fname, custom_sort);
468 		break;
469 	case ELFCLASS64:
470 		{
471 		if (r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||
472 		    r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {
473 			fprintf(stderr,
474 				"unrecognized ET_EXEC/ET_DYN file: %s\n",
475 				fname);
476 			break;
477 		}
478 		rc = do_sort_64(ehdr, fname, custom_sort);
479 		}
480 		break;
481 	default:
482 		fprintf(stderr, "unrecognized ELF class %d %s\n",
483 			ehdr->e32.e_ident[EI_CLASS], fname);
484 		break;
485 	}
486 
487 	return rc;
488 }
489 
490 int main(int argc, char *argv[])
491 {
492 	int i, n_error = 0;  /* gcc-4.3.0 false positive complaint */
493 	size_t size = 0;
494 	void *addr = NULL;
495 
496 	if (argc < 2) {
497 		fprintf(stderr, "usage: sorttable vmlinux...\n");
498 		return 0;
499 	}
500 
501 	/* Process each file in turn, allowing deep failure. */
502 	for (i = 1; i < argc; i++) {
503 		addr = mmap_file(argv[i], &size);
504 		if (!addr) {
505 			++n_error;
506 			continue;
507 		}
508 
509 		if (do_file(argv[i], addr))
510 			++n_error;
511 
512 		munmap(addr, size);
513 	}
514 
515 	return !!n_error;
516 }
517