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 /* 89 * Get the whole file as a programming convenience in order to avoid 90 * malloc+lseek+read+free of many pieces. If successful, then mmap 91 * avoids copying unused pieces; else just read the whole file. 92 * Open for both read and write. 93 */ 94 static void *mmap_file(char const *fname, size_t *size) 95 { 96 int fd; 97 struct stat sb; 98 void *addr = NULL; 99 100 fd = open(fname, O_RDWR); 101 if (fd < 0) { 102 perror(fname); 103 return NULL; 104 } 105 if (fstat(fd, &sb) < 0) { 106 perror(fname); 107 goto out; 108 } 109 if (!S_ISREG(sb.st_mode)) { 110 fprintf(stderr, "not a regular file: %s\n", fname); 111 goto out; 112 } 113 114 addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 115 if (addr == MAP_FAILED) { 116 fprintf(stderr, "Could not mmap file: %s\n", fname); 117 goto out; 118 } 119 120 *size = sb.st_size; 121 122 out: 123 close(fd); 124 return addr; 125 } 126 127 static uint32_t rbe(const uint32_t *x) 128 { 129 return get_unaligned_be32(x); 130 } 131 132 static uint16_t r2be(const uint16_t *x) 133 { 134 return get_unaligned_be16(x); 135 } 136 137 static uint64_t r8be(const uint64_t *x) 138 { 139 return get_unaligned_be64(x); 140 } 141 142 static uint32_t rle(const uint32_t *x) 143 { 144 return get_unaligned_le32(x); 145 } 146 147 static uint16_t r2le(const uint16_t *x) 148 { 149 return get_unaligned_le16(x); 150 } 151 152 static uint64_t r8le(const uint64_t *x) 153 { 154 return get_unaligned_le64(x); 155 } 156 157 static void wbe(uint32_t val, uint32_t *x) 158 { 159 put_unaligned_be32(val, x); 160 } 161 162 static void wle(uint32_t val, uint32_t *x) 163 { 164 put_unaligned_le32(val, x); 165 } 166 167 /* 168 * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of 169 * the way to -256..-1, to avoid conflicting with real section 170 * indices. 171 */ 172 #define SPECIAL(i) ((i) - (SHN_HIRESERVE + 1)) 173 174 static inline int is_shndx_special(unsigned int i) 175 { 176 return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE; 177 } 178 179 /* Accessor for sym->st_shndx, hides ugliness of "64k sections" */ 180 static inline unsigned int get_secindex(unsigned int shndx, 181 unsigned int sym_offs, 182 const Elf32_Word *symtab_shndx_start) 183 { 184 if (is_shndx_special(shndx)) 185 return SPECIAL(shndx); 186 if (shndx != SHN_XINDEX) 187 return shndx; 188 return r(&symtab_shndx_start[sym_offs]); 189 } 190 191 static int compare_extable_32(const void *a, const void *b) 192 { 193 Elf32_Addr av = r(a); 194 Elf32_Addr bv = r(b); 195 196 if (av < bv) 197 return -1; 198 return av > bv; 199 } 200 201 static int compare_extable_64(const void *a, const void *b) 202 { 203 Elf64_Addr av = r8(a); 204 Elf64_Addr bv = r8(b); 205 206 if (av < bv) 207 return -1; 208 return av > bv; 209 } 210 211 static inline void *get_index(void *start, int entsize, int index) 212 { 213 return start + (entsize * index); 214 } 215 216 /* 32 bit and 64 bit are very similar */ 217 #include "sorttable.h" 218 #define SORTTABLE_64 219 #include "sorttable.h" 220 221 static int compare_relative_table(const void *a, const void *b) 222 { 223 int32_t av = (int32_t)r(a); 224 int32_t bv = (int32_t)r(b); 225 226 if (av < bv) 227 return -1; 228 if (av > bv) 229 return 1; 230 return 0; 231 } 232 233 static void sort_relative_table(char *extab_image, int image_size) 234 { 235 int i = 0; 236 237 /* 238 * Do the same thing the runtime sort does, first normalize to 239 * being relative to the start of the section. 240 */ 241 while (i < image_size) { 242 uint32_t *loc = (uint32_t *)(extab_image + i); 243 w(r(loc) + i, loc); 244 i += 4; 245 } 246 247 qsort(extab_image, image_size / 8, 8, compare_relative_table); 248 249 /* Now denormalize. */ 250 i = 0; 251 while (i < image_size) { 252 uint32_t *loc = (uint32_t *)(extab_image + i); 253 w(r(loc) - i, loc); 254 i += 4; 255 } 256 } 257 258 static void sort_relative_table_with_data(char *extab_image, int image_size) 259 { 260 int i = 0; 261 262 while (i < image_size) { 263 uint32_t *loc = (uint32_t *)(extab_image + i); 264 265 w(r(loc) + i, loc); 266 w(r(loc + 1) + i + 4, loc + 1); 267 /* Don't touch the fixup type or data */ 268 269 i += sizeof(uint32_t) * 3; 270 } 271 272 qsort(extab_image, image_size / 12, 12, compare_relative_table); 273 274 i = 0; 275 while (i < image_size) { 276 uint32_t *loc = (uint32_t *)(extab_image + i); 277 278 w(r(loc) - i, loc); 279 w(r(loc + 1) - (i + 4), loc + 1); 280 /* Don't touch the fixup type or data */ 281 282 i += sizeof(uint32_t) * 3; 283 } 284 } 285 286 static int do_file(char const *const fname, void *addr) 287 { 288 int rc = -1; 289 Elf_Ehdr *ehdr = addr; 290 table_sort_t custom_sort = NULL; 291 292 switch (ehdr->e32.e_ident[EI_DATA]) { 293 case ELFDATA2LSB: 294 r = rle; 295 r2 = r2le; 296 r8 = r8le; 297 w = wle; 298 break; 299 case ELFDATA2MSB: 300 r = rbe; 301 r2 = r2be; 302 r8 = r8be; 303 w = wbe; 304 break; 305 default: 306 fprintf(stderr, "unrecognized ELF data encoding %d: %s\n", 307 ehdr->e32.e_ident[EI_DATA], fname); 308 return -1; 309 } 310 311 if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 || 312 (r2(&ehdr->e32.e_type) != ET_EXEC && r2(&ehdr->e32.e_type) != ET_DYN) || 313 ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) { 314 fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname); 315 return -1; 316 } 317 318 switch (r2(&ehdr->e32.e_machine)) { 319 case EM_386: 320 case EM_AARCH64: 321 case EM_LOONGARCH: 322 case EM_RISCV: 323 case EM_S390: 324 case EM_X86_64: 325 custom_sort = sort_relative_table_with_data; 326 break; 327 case EM_PARISC: 328 case EM_PPC: 329 case EM_PPC64: 330 custom_sort = sort_relative_table; 331 break; 332 case EM_ARCOMPACT: 333 case EM_ARCV2: 334 case EM_ARM: 335 case EM_MICROBLAZE: 336 case EM_MIPS: 337 case EM_XTENSA: 338 break; 339 default: 340 fprintf(stderr, "unrecognized e_machine %d %s\n", 341 r2(&ehdr->e32.e_machine), fname); 342 return -1; 343 } 344 345 switch (ehdr->e32.e_ident[EI_CLASS]) { 346 case ELFCLASS32: 347 if (r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) || 348 r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) { 349 fprintf(stderr, 350 "unrecognized ET_EXEC/ET_DYN file: %s\n", fname); 351 break; 352 } 353 rc = do_sort_32(ehdr, fname, custom_sort); 354 break; 355 case ELFCLASS64: 356 { 357 if (r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) || 358 r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) { 359 fprintf(stderr, 360 "unrecognized ET_EXEC/ET_DYN file: %s\n", 361 fname); 362 break; 363 } 364 rc = do_sort_64(ehdr, fname, custom_sort); 365 } 366 break; 367 default: 368 fprintf(stderr, "unrecognized ELF class %d %s\n", 369 ehdr->e32.e_ident[EI_CLASS], fname); 370 break; 371 } 372 373 return rc; 374 } 375 376 int main(int argc, char *argv[]) 377 { 378 int i, n_error = 0; /* gcc-4.3.0 false positive complaint */ 379 size_t size = 0; 380 void *addr = NULL; 381 382 if (argc < 2) { 383 fprintf(stderr, "usage: sorttable vmlinux...\n"); 384 return 0; 385 } 386 387 /* Process each file in turn, allowing deep failure. */ 388 for (i = 1; i < argc; i++) { 389 addr = mmap_file(argv[i], &size); 390 if (!addr) { 391 ++n_error; 392 continue; 393 } 394 395 if (do_file(argv[i], addr)) 396 ++n_error; 397 398 munmap(addr, size); 399 } 400 401 return !!n_error; 402 } 403