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