xref: /linux/scripts/sorttable.c (revision 71a16df164b23210d4dcaf35c70825f47d7c5599)
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 <shile.zhang@linux.alibaba.com>
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 <jreiser@BitWagon.com>.  All rights reserved.
14   *
15   * Restructured to fit Linux format, as well as other updates:
16   * Copyright 2010 Steven Rostedt <srostedt@redhat.com>, 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  
34  #include <tools/be_byteshift.h>
35  #include <tools/le_byteshift.h>
36  
37  #ifndef EM_ARCOMPACT
38  #define EM_ARCOMPACT	93
39  #endif
40  
41  #ifndef EM_XTENSA
42  #define EM_XTENSA	94
43  #endif
44  
45  #ifndef EM_AARCH64
46  #define EM_AARCH64	183
47  #endif
48  
49  #ifndef EM_MICROBLAZE
50  #define EM_MICROBLAZE	189
51  #endif
52  
53  #ifndef EM_ARCV2
54  #define EM_ARCV2	195
55  #endif
56  
57  #ifndef EM_RISCV
58  #define EM_RISCV	243
59  #endif
60  
61  static uint32_t (*r)(const uint32_t *);
62  static uint16_t (*r2)(const uint16_t *);
63  static uint64_t (*r8)(const uint64_t *);
64  static void (*w)(uint32_t, uint32_t *);
65  static void (*w2)(uint16_t, uint16_t *);
66  static void (*w8)(uint64_t, uint64_t *);
67  typedef void (*table_sort_t)(char *, int);
68  
69  /*
70   * Get the whole file as a programming convenience in order to avoid
71   * malloc+lseek+read+free of many pieces.  If successful, then mmap
72   * avoids copying unused pieces; else just read the whole file.
73   * Open for both read and write.
74   */
75  static void *mmap_file(char const *fname, size_t *size)
76  {
77  	int fd;
78  	struct stat sb;
79  	void *addr = NULL;
80  
81  	fd = open(fname, O_RDWR);
82  	if (fd < 0) {
83  		perror(fname);
84  		return NULL;
85  	}
86  	if (fstat(fd, &sb) < 0) {
87  		perror(fname);
88  		goto out;
89  	}
90  	if (!S_ISREG(sb.st_mode)) {
91  		fprintf(stderr, "not a regular file: %s\n", fname);
92  		goto out;
93  	}
94  
95  	addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
96  	if (addr == MAP_FAILED) {
97  		fprintf(stderr, "Could not mmap file: %s\n", fname);
98  		goto out;
99  	}
100  
101  	*size = sb.st_size;
102  
103  out:
104  	close(fd);
105  	return addr;
106  }
107  
108  static uint32_t rbe(const uint32_t *x)
109  {
110  	return get_unaligned_be32(x);
111  }
112  
113  static uint16_t r2be(const uint16_t *x)
114  {
115  	return get_unaligned_be16(x);
116  }
117  
118  static uint64_t r8be(const uint64_t *x)
119  {
120  	return get_unaligned_be64(x);
121  }
122  
123  static uint32_t rle(const uint32_t *x)
124  {
125  	return get_unaligned_le32(x);
126  }
127  
128  static uint16_t r2le(const uint16_t *x)
129  {
130  	return get_unaligned_le16(x);
131  }
132  
133  static uint64_t r8le(const uint64_t *x)
134  {
135  	return get_unaligned_le64(x);
136  }
137  
138  static void wbe(uint32_t val, uint32_t *x)
139  {
140  	put_unaligned_be32(val, x);
141  }
142  
143  static void w2be(uint16_t val, uint16_t *x)
144  {
145  	put_unaligned_be16(val, x);
146  }
147  
148  static void w8be(uint64_t val, uint64_t *x)
149  {
150  	put_unaligned_be64(val, x);
151  }
152  
153  static void wle(uint32_t val, uint32_t *x)
154  {
155  	put_unaligned_le32(val, x);
156  }
157  
158  static void w2le(uint16_t val, uint16_t *x)
159  {
160  	put_unaligned_le16(val, x);
161  }
162  
163  static void w8le(uint64_t val, uint64_t *x)
164  {
165  	put_unaligned_le64(val, x);
166  }
167  
168  /*
169   * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
170   * the way to -256..-1, to avoid conflicting with real section
171   * indices.
172   */
173  #define SPECIAL(i) ((i) - (SHN_HIRESERVE + 1))
174  
175  static inline int is_shndx_special(unsigned int i)
176  {
177  	return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE;
178  }
179  
180  /* Accessor for sym->st_shndx, hides ugliness of "64k sections" */
181  static inline unsigned int get_secindex(unsigned int shndx,
182  					unsigned int sym_offs,
183  					const Elf32_Word *symtab_shndx_start)
184  {
185  	if (is_shndx_special(shndx))
186  		return SPECIAL(shndx);
187  	if (shndx != SHN_XINDEX)
188  		return shndx;
189  	return r(&symtab_shndx_start[sym_offs]);
190  }
191  
192  /* 32 bit and 64 bit are very similar */
193  #include "sorttable.h"
194  #define SORTTABLE_64
195  #include "sorttable.h"
196  
197  static int compare_relative_table(const void *a, const void *b)
198  {
199  	int32_t av = (int32_t)r(a);
200  	int32_t bv = (int32_t)r(b);
201  
202  	if (av < bv)
203  		return -1;
204  	if (av > bv)
205  		return 1;
206  	return 0;
207  }
208  
209  static void sort_relative_table(char *extab_image, int image_size)
210  {
211  	int i = 0;
212  
213  	/*
214  	 * Do the same thing the runtime sort does, first normalize to
215  	 * being relative to the start of the section.
216  	 */
217  	while (i < image_size) {
218  		uint32_t *loc = (uint32_t *)(extab_image + i);
219  		w(r(loc) + i, loc);
220  		i += 4;
221  	}
222  
223  	qsort(extab_image, image_size / 8, 8, compare_relative_table);
224  
225  	/* Now denormalize. */
226  	i = 0;
227  	while (i < image_size) {
228  		uint32_t *loc = (uint32_t *)(extab_image + i);
229  		w(r(loc) - i, loc);
230  		i += 4;
231  	}
232  }
233  
234  static void arm64_sort_relative_table(char *extab_image, int image_size)
235  {
236  	int i = 0;
237  
238  	while (i < image_size) {
239  		uint32_t *loc = (uint32_t *)(extab_image + i);
240  
241  		w(r(loc) + i, loc);
242  		w(r(loc + 1) + i + 4, loc + 1);
243  		/* Don't touch the fixup type or data */
244  
245  		i += sizeof(uint32_t) * 3;
246  	}
247  
248  	qsort(extab_image, image_size / 12, 12, compare_relative_table);
249  
250  	i = 0;
251  	while (i < image_size) {
252  		uint32_t *loc = (uint32_t *)(extab_image + i);
253  
254  		w(r(loc) - i, loc);
255  		w(r(loc + 1) - (i + 4), loc + 1);
256  		/* Don't touch the fixup type or data */
257  
258  		i += sizeof(uint32_t) * 3;
259  	}
260  }
261  
262  static void x86_sort_relative_table(char *extab_image, int image_size)
263  {
264  	int i = 0;
265  
266  	while (i < image_size) {
267  		uint32_t *loc = (uint32_t *)(extab_image + i);
268  
269  		w(r(loc) + i, loc);
270  		w(r(loc + 1) + i + 4, loc + 1);
271  		/* Don't touch the fixup type */
272  
273  		i += sizeof(uint32_t) * 3;
274  	}
275  
276  	qsort(extab_image, image_size / 12, 12, compare_relative_table);
277  
278  	i = 0;
279  	while (i < image_size) {
280  		uint32_t *loc = (uint32_t *)(extab_image + i);
281  
282  		w(r(loc) - i, loc);
283  		w(r(loc + 1) - (i + 4), loc + 1);
284  		/* Don't touch the fixup type */
285  
286  		i += sizeof(uint32_t) * 3;
287  	}
288  }
289  
290  static void s390_sort_relative_table(char *extab_image, int image_size)
291  {
292  	int i;
293  
294  	for (i = 0; i < image_size; i += 16) {
295  		char *loc = extab_image + i;
296  		uint64_t handler;
297  
298  		w(r((uint32_t *)loc) + i, (uint32_t *)loc);
299  		w(r((uint32_t *)(loc + 4)) + (i + 4), (uint32_t *)(loc + 4));
300  		/*
301  		 * 0 is a special self-relative handler value, which means that
302  		 * handler should be ignored. It is safe, because it means that
303  		 * handler field points to itself, which should never happen.
304  		 * When creating extable-relative values, keep it as 0, since
305  		 * this should never occur either: it would mean that handler
306  		 * field points to the first extable entry.
307  		 */
308  		handler = r8((uint64_t *)(loc + 8));
309  		if (handler)
310  			handler += i + 8;
311  		w8(handler, (uint64_t *)(loc + 8));
312  	}
313  
314  	qsort(extab_image, image_size / 16, 16, compare_relative_table);
315  
316  	for (i = 0; i < image_size; i += 16) {
317  		char *loc = extab_image + i;
318  		uint64_t handler;
319  
320  		w(r((uint32_t *)loc) - i, (uint32_t *)loc);
321  		w(r((uint32_t *)(loc + 4)) - (i + 4), (uint32_t *)(loc + 4));
322  		handler = r8((uint64_t *)(loc + 8));
323  		if (handler)
324  			handler -= i + 8;
325  		w8(handler, (uint64_t *)(loc + 8));
326  	}
327  }
328  
329  static int do_file(char const *const fname, void *addr)
330  {
331  	int rc = -1;
332  	Elf32_Ehdr *ehdr = addr;
333  	table_sort_t custom_sort = NULL;
334  
335  	switch (ehdr->e_ident[EI_DATA]) {
336  	case ELFDATA2LSB:
337  		r	= rle;
338  		r2	= r2le;
339  		r8	= r8le;
340  		w	= wle;
341  		w2	= w2le;
342  		w8	= w8le;
343  		break;
344  	case ELFDATA2MSB:
345  		r	= rbe;
346  		r2	= r2be;
347  		r8	= r8be;
348  		w	= wbe;
349  		w2	= w2be;
350  		w8	= w8be;
351  		break;
352  	default:
353  		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
354  			ehdr->e_ident[EI_DATA], fname);
355  		return -1;
356  	}
357  
358  	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 ||
359  	    (r2(&ehdr->e_type) != ET_EXEC && r2(&ehdr->e_type) != ET_DYN) ||
360  	    ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
361  		fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname);
362  		return -1;
363  	}
364  
365  	switch (r2(&ehdr->e_machine)) {
366  	case EM_386:
367  	case EM_X86_64:
368  		custom_sort = x86_sort_relative_table;
369  		break;
370  	case EM_S390:
371  		custom_sort = s390_sort_relative_table;
372  		break;
373  	case EM_AARCH64:
374  		custom_sort = arm64_sort_relative_table;
375  		break;
376  	case EM_PARISC:
377  	case EM_PPC:
378  	case EM_PPC64:
379  		custom_sort = sort_relative_table;
380  		break;
381  	case EM_ARCOMPACT:
382  	case EM_ARCV2:
383  	case EM_ARM:
384  	case EM_MICROBLAZE:
385  	case EM_MIPS:
386  	case EM_RISCV:
387  	case EM_XTENSA:
388  		break;
389  	default:
390  		fprintf(stderr, "unrecognized e_machine %d %s\n",
391  			r2(&ehdr->e_machine), fname);
392  		return -1;
393  	}
394  
395  	switch (ehdr->e_ident[EI_CLASS]) {
396  	case ELFCLASS32:
397  		if (r2(&ehdr->e_ehsize) != sizeof(Elf32_Ehdr) ||
398  		    r2(&ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
399  			fprintf(stderr,
400  				"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
401  			break;
402  		}
403  		rc = do_sort_32(ehdr, fname, custom_sort);
404  		break;
405  	case ELFCLASS64:
406  		{
407  		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
408  		if (r2(&ghdr->e_ehsize) != sizeof(Elf64_Ehdr) ||
409  		    r2(&ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
410  			fprintf(stderr,
411  				"unrecognized ET_EXEC/ET_DYN file: %s\n",
412  				fname);
413  			break;
414  		}
415  		rc = do_sort_64(ghdr, fname, custom_sort);
416  		}
417  		break;
418  	default:
419  		fprintf(stderr, "unrecognized ELF class %d %s\n",
420  			ehdr->e_ident[EI_CLASS], fname);
421  		break;
422  	}
423  
424  	return rc;
425  }
426  
427  int main(int argc, char *argv[])
428  {
429  	int i, n_error = 0;  /* gcc-4.3.0 false positive complaint */
430  	size_t size = 0;
431  	void *addr = NULL;
432  
433  	if (argc < 2) {
434  		fprintf(stderr, "usage: sorttable vmlinux...\n");
435  		return 0;
436  	}
437  
438  	/* Process each file in turn, allowing deep failure. */
439  	for (i = 1; i < argc; i++) {
440  		addr = mmap_file(argv[i], &size);
441  		if (!addr) {
442  			++n_error;
443  			continue;
444  		}
445  
446  		if (do_file(argv[i], addr))
447  			++n_error;
448  
449  		munmap(addr, size);
450  	}
451  
452  	return !!n_error;
453  }
454