Back to home page

LXR

 
 

    


0001 /*
0002  * sortextable.c: Sort the kernel's exception table
0003  *
0004  * Copyright 2011 - 2012 Cavium, Inc.
0005  *
0006  * Based on code taken from recortmcount.c which is:
0007  *
0008  * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
0009  * Licensed under the GNU General Public License, version 2 (GPLv2).
0010  *
0011  * Restructured to fit Linux format, as well as other updates:
0012  *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
0013  */
0014 
0015 /*
0016  * Strategy: alter the vmlinux file in-place.
0017  */
0018 
0019 #include <sys/types.h>
0020 #include <sys/mman.h>
0021 #include <sys/stat.h>
0022 #include <getopt.h>
0023 #include <elf.h>
0024 #include <fcntl.h>
0025 #include <setjmp.h>
0026 #include <stdio.h>
0027 #include <stdlib.h>
0028 #include <string.h>
0029 #include <unistd.h>
0030 
0031 #include <tools/be_byteshift.h>
0032 #include <tools/le_byteshift.h>
0033 
0034 #ifndef EM_ARCOMPACT
0035 #define EM_ARCOMPACT    93
0036 #endif
0037 
0038 #ifndef EM_XTENSA
0039 #define EM_XTENSA   94
0040 #endif
0041 
0042 #ifndef EM_AARCH64
0043 #define EM_AARCH64  183
0044 #endif
0045 
0046 #ifndef EM_MICROBLAZE
0047 #define EM_MICROBLAZE   189
0048 #endif
0049 
0050 #ifndef EM_ARCV2
0051 #define EM_ARCV2    195
0052 #endif
0053 
0054 static int fd_map;  /* File descriptor for file being modified. */
0055 static int mmap_failed; /* Boolean flag. */
0056 static void *ehdr_curr; /* current ElfXX_Ehdr *  for resource cleanup */
0057 static struct stat sb;  /* Remember .st_size, etc. */
0058 static jmp_buf jmpenv;  /* setjmp/longjmp per-file error escape */
0059 
0060 /* setjmp() return values */
0061 enum {
0062     SJ_SETJMP = 0,  /* hardwired first return */
0063     SJ_FAIL,
0064     SJ_SUCCEED
0065 };
0066 
0067 /* Per-file resource cleanup when multiple files. */
0068 static void
0069 cleanup(void)
0070 {
0071     if (!mmap_failed)
0072         munmap(ehdr_curr, sb.st_size);
0073     close(fd_map);
0074 }
0075 
0076 static void __attribute__((noreturn))
0077 fail_file(void)
0078 {
0079     cleanup();
0080     longjmp(jmpenv, SJ_FAIL);
0081 }
0082 
0083 /*
0084  * Get the whole file as a programming convenience in order to avoid
0085  * malloc+lseek+read+free of many pieces.  If successful, then mmap
0086  * avoids copying unused pieces; else just read the whole file.
0087  * Open for both read and write.
0088  */
0089 static void *mmap_file(char const *fname)
0090 {
0091     void *addr;
0092 
0093     fd_map = open(fname, O_RDWR);
0094     if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
0095         perror(fname);
0096         fail_file();
0097     }
0098     if (!S_ISREG(sb.st_mode)) {
0099         fprintf(stderr, "not a regular file: %s\n", fname);
0100         fail_file();
0101     }
0102     addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
0103             fd_map, 0);
0104     if (addr == MAP_FAILED) {
0105         mmap_failed = 1;
0106         fprintf(stderr, "Could not mmap file: %s\n", fname);
0107         fail_file();
0108     }
0109     return addr;
0110 }
0111 
0112 static uint64_t r8be(const uint64_t *x)
0113 {
0114     return get_unaligned_be64(x);
0115 }
0116 static uint32_t rbe(const uint32_t *x)
0117 {
0118     return get_unaligned_be32(x);
0119 }
0120 static uint16_t r2be(const uint16_t *x)
0121 {
0122     return get_unaligned_be16(x);
0123 }
0124 static uint64_t r8le(const uint64_t *x)
0125 {
0126     return get_unaligned_le64(x);
0127 }
0128 static uint32_t rle(const uint32_t *x)
0129 {
0130     return get_unaligned_le32(x);
0131 }
0132 static uint16_t r2le(const uint16_t *x)
0133 {
0134     return get_unaligned_le16(x);
0135 }
0136 
0137 static void w8be(uint64_t val, uint64_t *x)
0138 {
0139     put_unaligned_be64(val, x);
0140 }
0141 static void wbe(uint32_t val, uint32_t *x)
0142 {
0143     put_unaligned_be32(val, x);
0144 }
0145 static void w2be(uint16_t val, uint16_t *x)
0146 {
0147     put_unaligned_be16(val, x);
0148 }
0149 static void w8le(uint64_t val, uint64_t *x)
0150 {
0151     put_unaligned_le64(val, x);
0152 }
0153 static void wle(uint32_t val, uint32_t *x)
0154 {
0155     put_unaligned_le32(val, x);
0156 }
0157 static void w2le(uint16_t val, uint16_t *x)
0158 {
0159     put_unaligned_le16(val, x);
0160 }
0161 
0162 static uint64_t (*r8)(const uint64_t *);
0163 static uint32_t (*r)(const uint32_t *);
0164 static uint16_t (*r2)(const uint16_t *);
0165 static void (*w8)(uint64_t, uint64_t *);
0166 static void (*w)(uint32_t, uint32_t *);
0167 static void (*w2)(uint16_t, uint16_t *);
0168 
0169 typedef void (*table_sort_t)(char *, int);
0170 
0171 /*
0172  * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
0173  * the way to -256..-1, to avoid conflicting with real section
0174  * indices.
0175  */
0176 #define SPECIAL(i) ((i) - (SHN_HIRESERVE + 1))
0177 
0178 static inline int is_shndx_special(unsigned int i)
0179 {
0180     return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE;
0181 }
0182 
0183 /* Accessor for sym->st_shndx, hides ugliness of "64k sections" */
0184 static inline unsigned int get_secindex(unsigned int shndx,
0185                     unsigned int sym_offs,
0186                     const Elf32_Word *symtab_shndx_start)
0187 {
0188     if (is_shndx_special(shndx))
0189         return SPECIAL(shndx);
0190     if (shndx != SHN_XINDEX)
0191         return shndx;
0192     return r(&symtab_shndx_start[sym_offs]);
0193 }
0194 
0195 /* 32 bit and 64 bit are very similar */
0196 #include "sortextable.h"
0197 #define SORTEXTABLE_64
0198 #include "sortextable.h"
0199 
0200 static int compare_relative_table(const void *a, const void *b)
0201 {
0202     int32_t av = (int32_t)r(a);
0203     int32_t bv = (int32_t)r(b);
0204 
0205     if (av < bv)
0206         return -1;
0207     if (av > bv)
0208         return 1;
0209     return 0;
0210 }
0211 
0212 static void x86_sort_relative_table(char *extab_image, int image_size)
0213 {
0214     int i;
0215 
0216     i = 0;
0217     while (i < image_size) {
0218         uint32_t *loc = (uint32_t *)(extab_image + i);
0219 
0220         w(r(loc) + i, loc);
0221         w(r(loc + 1) + i + 4, loc + 1);
0222         w(r(loc + 2) + i + 8, loc + 2);
0223 
0224         i += sizeof(uint32_t) * 3;
0225     }
0226 
0227     qsort(extab_image, image_size / 12, 12, compare_relative_table);
0228 
0229     i = 0;
0230     while (i < image_size) {
0231         uint32_t *loc = (uint32_t *)(extab_image + i);
0232 
0233         w(r(loc) - i, loc);
0234         w(r(loc + 1) - (i + 4), loc + 1);
0235         w(r(loc + 2) - (i + 8), loc + 2);
0236 
0237         i += sizeof(uint32_t) * 3;
0238     }
0239 }
0240 
0241 static void sort_relative_table(char *extab_image, int image_size)
0242 {
0243     int i;
0244 
0245     /*
0246      * Do the same thing the runtime sort does, first normalize to
0247      * being relative to the start of the section.
0248      */
0249     i = 0;
0250     while (i < image_size) {
0251         uint32_t *loc = (uint32_t *)(extab_image + i);
0252         w(r(loc) + i, loc);
0253         i += 4;
0254     }
0255 
0256     qsort(extab_image, image_size / 8, 8, compare_relative_table);
0257 
0258     /* Now denormalize. */
0259     i = 0;
0260     while (i < image_size) {
0261         uint32_t *loc = (uint32_t *)(extab_image + i);
0262         w(r(loc) - i, loc);
0263         i += 4;
0264     }
0265 }
0266 
0267 static void
0268 do_file(char const *const fname)
0269 {
0270     table_sort_t custom_sort;
0271     Elf32_Ehdr *ehdr = mmap_file(fname);
0272 
0273     ehdr_curr = ehdr;
0274     switch (ehdr->e_ident[EI_DATA]) {
0275     default:
0276         fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
0277             ehdr->e_ident[EI_DATA], fname);
0278         fail_file();
0279         break;
0280     case ELFDATA2LSB:
0281         r = rle;
0282         r2 = r2le;
0283         r8 = r8le;
0284         w = wle;
0285         w2 = w2le;
0286         w8 = w8le;
0287         break;
0288     case ELFDATA2MSB:
0289         r = rbe;
0290         r2 = r2be;
0291         r8 = r8be;
0292         w = wbe;
0293         w2 = w2be;
0294         w8 = w8be;
0295         break;
0296     }  /* end switch */
0297     if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
0298     ||  (r2(&ehdr->e_type) != ET_EXEC && r2(&ehdr->e_type) != ET_DYN)
0299     ||  ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
0300         fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname);
0301         fail_file();
0302     }
0303 
0304     custom_sort = NULL;
0305     switch (r2(&ehdr->e_machine)) {
0306     default:
0307         fprintf(stderr, "unrecognized e_machine %d %s\n",
0308             r2(&ehdr->e_machine), fname);
0309         fail_file();
0310         break;
0311     case EM_386:
0312     case EM_X86_64:
0313         custom_sort = x86_sort_relative_table;
0314         break;
0315 
0316     case EM_S390:
0317     case EM_AARCH64:
0318     case EM_PARISC:
0319     case EM_PPC:
0320     case EM_PPC64:
0321         custom_sort = sort_relative_table;
0322         break;
0323     case EM_ARCOMPACT:
0324     case EM_ARCV2:
0325     case EM_ARM:
0326     case EM_MICROBLAZE:
0327     case EM_MIPS:
0328     case EM_XTENSA:
0329         break;
0330     }  /* end switch */
0331 
0332     switch (ehdr->e_ident[EI_CLASS]) {
0333     default:
0334         fprintf(stderr, "unrecognized ELF class %d %s\n",
0335             ehdr->e_ident[EI_CLASS], fname);
0336         fail_file();
0337         break;
0338     case ELFCLASS32:
0339         if (r2(&ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
0340         ||  r2(&ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
0341             fprintf(stderr,
0342                 "unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
0343             fail_file();
0344         }
0345         do32(ehdr, fname, custom_sort);
0346         break;
0347     case ELFCLASS64: {
0348         Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
0349         if (r2(&ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
0350         ||  r2(&ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
0351             fprintf(stderr,
0352                 "unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
0353             fail_file();
0354         }
0355         do64(ghdr, fname, custom_sort);
0356         break;
0357     }
0358     }  /* end switch */
0359 
0360     cleanup();
0361 }
0362 
0363 int
0364 main(int argc, char *argv[])
0365 {
0366     int n_error = 0;  /* gcc-4.3.0 false positive complaint */
0367     int i;
0368 
0369     if (argc < 2) {
0370         fprintf(stderr, "usage: sortextable vmlinux...\n");
0371         return 0;
0372     }
0373 
0374     /* Process each file in turn, allowing deep failure. */
0375     for (i = 1; i < argc; i++) {
0376         char *file = argv[i];
0377         int const sjval = setjmp(jmpenv);
0378 
0379         switch (sjval) {
0380         default:
0381             fprintf(stderr, "internal error: %s\n", file);
0382             exit(1);
0383             break;
0384         case SJ_SETJMP:    /* normal sequence */
0385             /* Avoid problems if early cleanup() */
0386             fd_map = -1;
0387             ehdr_curr = NULL;
0388             mmap_failed = 1;
0389             do_file(file);
0390             break;
0391         case SJ_FAIL:    /* error in do_file or below */
0392             ++n_error;
0393             break;
0394         case SJ_SUCCEED:    /* premature success */
0395             /* do nothing */
0396             break;
0397         }  /* end switch */
0398     }
0399     return !!n_error;
0400 }