linux/scripts/sortextable.c
<<
>>
Prefs
   1/*
   2 * sortextable.c: Sort the kernel's exception table
   3 *
   4 * Copyright 2011 - 2012 Cavium, Inc.
   5 *
   6 * Based on code taken from recortmcount.c which is:
   7 *
   8 * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
   9 * Licensed under the GNU General Public License, version 2 (GPLv2).
  10 *
  11 * Restructured to fit Linux format, as well as other updates:
  12 *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
  13 */
  14
  15/*
  16 * Strategy: alter the vmlinux file in-place.
  17 */
  18
  19#include <sys/types.h>
  20#include <sys/mman.h>
  21#include <sys/stat.h>
  22#include <getopt.h>
  23#include <elf.h>
  24#include <fcntl.h>
  25#include <setjmp.h>
  26#include <stdio.h>
  27#include <stdlib.h>
  28#include <string.h>
  29#include <unistd.h>
  30
  31#include <tools/be_byteshift.h>
  32#include <tools/le_byteshift.h>
  33
  34#ifndef EM_ARCOMPACT
  35#define EM_ARCOMPACT    93
  36#endif
  37
  38#ifndef EM_XTENSA
  39#define EM_XTENSA       94
  40#endif
  41
  42#ifndef EM_AARCH64
  43#define EM_AARCH64      183
  44#endif
  45
  46#ifndef EM_MICROBLAZE
  47#define EM_MICROBLAZE   189
  48#endif
  49
  50static int fd_map;      /* File descriptor for file being modified. */
  51static int mmap_failed; /* Boolean flag. */
  52static void *ehdr_curr; /* current ElfXX_Ehdr *  for resource cleanup */
  53static struct stat sb;  /* Remember .st_size, etc. */
  54static jmp_buf jmpenv;  /* setjmp/longjmp per-file error escape */
  55
  56/* setjmp() return values */
  57enum {
  58        SJ_SETJMP = 0,  /* hardwired first return */
  59        SJ_FAIL,
  60        SJ_SUCCEED
  61};
  62
  63/* Per-file resource cleanup when multiple files. */
  64static void
  65cleanup(void)
  66{
  67        if (!mmap_failed)
  68                munmap(ehdr_curr, sb.st_size);
  69        close(fd_map);
  70}
  71
  72static void __attribute__((noreturn))
  73fail_file(void)
  74{
  75        cleanup();
  76        longjmp(jmpenv, SJ_FAIL);
  77}
  78
  79/*
  80 * Get the whole file as a programming convenience in order to avoid
  81 * malloc+lseek+read+free of many pieces.  If successful, then mmap
  82 * avoids copying unused pieces; else just read the whole file.
  83 * Open for both read and write.
  84 */
  85static void *mmap_file(char const *fname)
  86{
  87        void *addr;
  88
  89        fd_map = open(fname, O_RDWR);
  90        if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
  91                perror(fname);
  92                fail_file();
  93        }
  94        if (!S_ISREG(sb.st_mode)) {
  95                fprintf(stderr, "not a regular file: %s\n", fname);
  96                fail_file();
  97        }
  98        addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
  99                    fd_map, 0);
 100        if (addr == MAP_FAILED) {
 101                mmap_failed = 1;
 102                fprintf(stderr, "Could not mmap file: %s\n", fname);
 103                fail_file();
 104        }
 105        return addr;
 106}
 107
 108static uint64_t r8be(const uint64_t *x)
 109{
 110        return get_unaligned_be64(x);
 111}
 112static uint32_t rbe(const uint32_t *x)
 113{
 114        return get_unaligned_be32(x);
 115}
 116static uint16_t r2be(const uint16_t *x)
 117{
 118        return get_unaligned_be16(x);
 119}
 120static uint64_t r8le(const uint64_t *x)
 121{
 122        return get_unaligned_le64(x);
 123}
 124static uint32_t rle(const uint32_t *x)
 125{
 126        return get_unaligned_le32(x);
 127}
 128static uint16_t r2le(const uint16_t *x)
 129{
 130        return get_unaligned_le16(x);
 131}
 132
 133static void w8be(uint64_t val, uint64_t *x)
 134{
 135        put_unaligned_be64(val, x);
 136}
 137static void wbe(uint32_t val, uint32_t *x)
 138{
 139        put_unaligned_be32(val, x);
 140}
 141static void w2be(uint16_t val, uint16_t *x)
 142{
 143        put_unaligned_be16(val, x);
 144}
 145static void w8le(uint64_t val, uint64_t *x)
 146{
 147        put_unaligned_le64(val, x);
 148}
 149static void wle(uint32_t val, uint32_t *x)
 150{
 151        put_unaligned_le32(val, x);
 152}
 153static void w2le(uint16_t val, uint16_t *x)
 154{
 155        put_unaligned_le16(val, x);
 156}
 157
 158static uint64_t (*r8)(const uint64_t *);
 159static uint32_t (*r)(const uint32_t *);
 160static uint16_t (*r2)(const uint16_t *);
 161static void (*w8)(uint64_t, uint64_t *);
 162static void (*w)(uint32_t, uint32_t *);
 163static void (*w2)(uint16_t, uint16_t *);
 164
 165typedef void (*table_sort_t)(char *, int);
 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
 174static 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" */
 180static 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/* 32 bit and 64 bit are very similar */
 192#include "sortextable.h"
 193#define SORTEXTABLE_64
 194#include "sortextable.h"
 195
 196static int compare_relative_table(const void *a, const void *b)
 197{
 198        int32_t av = (int32_t)r(a);
 199        int32_t bv = (int32_t)r(b);
 200
 201        if (av < bv)
 202                return -1;
 203        if (av > bv)
 204                return 1;
 205        return 0;
 206}
 207
 208static void sort_relative_table(char *extab_image, int image_size)
 209{
 210        int i;
 211
 212        /*
 213         * Do the same thing the runtime sort does, first normalize to
 214         * being relative to the start of the section.
 215         */
 216        i = 0;
 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
 234static void
 235do_file(char const *const fname)
 236{
 237        table_sort_t custom_sort;
 238        Elf32_Ehdr *ehdr = mmap_file(fname);
 239
 240        ehdr_curr = ehdr;
 241        switch (ehdr->e_ident[EI_DATA]) {
 242        default:
 243                fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
 244                        ehdr->e_ident[EI_DATA], fname);
 245                fail_file();
 246                break;
 247        case ELFDATA2LSB:
 248                r = rle;
 249                r2 = r2le;
 250                r8 = r8le;
 251                w = wle;
 252                w2 = w2le;
 253                w8 = w8le;
 254                break;
 255        case ELFDATA2MSB:
 256                r = rbe;
 257                r2 = r2be;
 258                r8 = r8be;
 259                w = wbe;
 260                w2 = w2be;
 261                w8 = w8be;
 262                break;
 263        }  /* end switch */
 264        if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
 265        ||  r2(&ehdr->e_type) != ET_EXEC
 266        ||  ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
 267                fprintf(stderr, "unrecognized ET_EXEC file %s\n", fname);
 268                fail_file();
 269        }
 270
 271        custom_sort = NULL;
 272        switch (r2(&ehdr->e_machine)) {
 273        default:
 274                fprintf(stderr, "unrecognized e_machine %d %s\n",
 275                        r2(&ehdr->e_machine), fname);
 276                fail_file();
 277                break;
 278        case EM_386:
 279        case EM_X86_64:
 280        case EM_S390:
 281                custom_sort = sort_relative_table;
 282                break;
 283        case EM_ARCOMPACT:
 284        case EM_ARM:
 285        case EM_AARCH64:
 286        case EM_MICROBLAZE:
 287        case EM_MIPS:
 288        case EM_XTENSA:
 289                break;
 290        }  /* end switch */
 291
 292        switch (ehdr->e_ident[EI_CLASS]) {
 293        default:
 294                fprintf(stderr, "unrecognized ELF class %d %s\n",
 295                        ehdr->e_ident[EI_CLASS], fname);
 296                fail_file();
 297                break;
 298        case ELFCLASS32:
 299                if (r2(&ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
 300                ||  r2(&ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
 301                        fprintf(stderr,
 302                                "unrecognized ET_EXEC file: %s\n", fname);
 303                        fail_file();
 304                }
 305                do32(ehdr, fname, custom_sort);
 306                break;
 307        case ELFCLASS64: {
 308                Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
 309                if (r2(&ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
 310                ||  r2(&ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
 311                        fprintf(stderr,
 312                                "unrecognized ET_EXEC file: %s\n", fname);
 313                        fail_file();
 314                }
 315                do64(ghdr, fname, custom_sort);
 316                break;
 317        }
 318        }  /* end switch */
 319
 320        cleanup();
 321}
 322
 323int
 324main(int argc, char *argv[])
 325{
 326        int n_error = 0;  /* gcc-4.3.0 false positive complaint */
 327        int i;
 328
 329        if (argc < 2) {
 330                fprintf(stderr, "usage: sortextable vmlinux...\n");
 331                return 0;
 332        }
 333
 334        /* Process each file in turn, allowing deep failure. */
 335        for (i = 1; i < argc; i++) {
 336                char *file = argv[i];
 337                int const sjval = setjmp(jmpenv);
 338
 339                switch (sjval) {
 340                default:
 341                        fprintf(stderr, "internal error: %s\n", file);
 342                        exit(1);
 343                        break;
 344                case SJ_SETJMP:    /* normal sequence */
 345                        /* Avoid problems if early cleanup() */
 346                        fd_map = -1;
 347                        ehdr_curr = NULL;
 348                        mmap_failed = 1;
 349                        do_file(file);
 350                        break;
 351                case SJ_FAIL:    /* error in do_file or below */
 352                        ++n_error;
 353                        break;
 354                case SJ_SUCCEED:    /* premature success */
 355                        /* do nothing */
 356                        break;
 357                }  /* end switch */
 358        }
 359        return !!n_error;
 360}
 361