linux/scripts/recordmcount.c
<<
>>
Prefs
   1/*
   2 * recordmcount.c: construct a table of the locations of calls to 'mcount'
   3 * so that ftrace can find them quickly.
   4 * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
   5 * Licensed under the GNU General Public License, version 2 (GPLv2).
   6 *
   7 * Restructured to fit Linux format, as well as other updates:
   8 *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
   9 */
  10
  11/*
  12 * Strategy: alter the .o file in-place.
  13 *
  14 * Append a new STRTAB that has the new section names, followed by a new array
  15 * ElfXX_Shdr[] that has the new section headers, followed by the section
  16 * contents for __mcount_loc and its relocations.  The old shstrtab strings,
  17 * and the old ElfXX_Shdr[] array, remain as "garbage" (commonly, a couple
  18 * kilobytes.)  Subsequent processing by /bin/ld (or the kernel module loader)
  19 * will ignore the garbage regions, because they are not designated by the
  20 * new .e_shoff nor the new ElfXX_Shdr[].  [In order to remove the garbage,
  21 * then use "ld -r" to create a new file that omits the garbage.]
  22 */
  23
  24#include <sys/types.h>
  25#include <sys/mman.h>
  26#include <sys/stat.h>
  27#include <getopt.h>
  28#include <elf.h>
  29#include <fcntl.h>
  30#include <setjmp.h>
  31#include <stdio.h>
  32#include <stdlib.h>
  33#include <string.h>
  34#include <unistd.h>
  35
  36#ifndef EM_METAG
  37/* Remove this when these make it to the standard system elf.h. */
  38#define EM_METAG      174
  39#define R_METAG_ADDR32                   2
  40#define R_METAG_NONE                     3
  41#endif
  42
  43#ifndef EM_AARCH64
  44#define EM_AARCH64      183
  45#define R_AARCH64_NONE          0
  46#define R_AARCH64_ABS64 257
  47#endif
  48
  49static int fd_map;      /* File descriptor for file being modified. */
  50static int mmap_failed; /* Boolean flag. */
  51static char gpfx;       /* prefix for global symbol name (sometimes '_') */
  52static struct stat sb;  /* Remember .st_size, etc. */
  53static jmp_buf jmpenv;  /* setjmp/longjmp per-file error escape */
  54static const char *altmcount;   /* alternate mcount symbol name */
  55static int warn_on_notrace_sect; /* warn when section has mcount not being recorded */
  56static void *file_map;  /* pointer of the mapped file */
  57static void *file_end;  /* pointer to the end of the mapped file */
  58static int file_updated; /* flag to state file was changed */
  59static void *file_ptr;  /* current file pointer location */
  60static void *file_append; /* added to the end of the file */
  61static size_t file_append_size; /* how much is added to end of file */
  62
  63/* setjmp() return values */
  64enum {
  65        SJ_SETJMP = 0,  /* hardwired first return */
  66        SJ_FAIL,
  67        SJ_SUCCEED
  68};
  69
  70/* Per-file resource cleanup when multiple files. */
  71static void
  72cleanup(void)
  73{
  74        if (!mmap_failed)
  75                munmap(file_map, sb.st_size);
  76        else
  77                free(file_map);
  78        file_map = NULL;
  79        free(file_append);
  80        file_append = NULL;
  81        file_append_size = 0;
  82        file_updated = 0;
  83}
  84
  85static void __attribute__((noreturn))
  86fail_file(void)
  87{
  88        cleanup();
  89        longjmp(jmpenv, SJ_FAIL);
  90}
  91
  92static void __attribute__((noreturn))
  93succeed_file(void)
  94{
  95        cleanup();
  96        longjmp(jmpenv, SJ_SUCCEED);
  97}
  98
  99/* ulseek, uread, ...:  Check return value for errors. */
 100
 101static off_t
 102ulseek(int const fd, off_t const offset, int const whence)
 103{
 104        switch (whence) {
 105        case SEEK_SET:
 106                file_ptr = file_map + offset;
 107                break;
 108        case SEEK_CUR:
 109                file_ptr += offset;
 110                break;
 111        case SEEK_END:
 112                file_ptr = file_map + (sb.st_size - offset);
 113                break;
 114        }
 115        if (file_ptr < file_map) {
 116                fprintf(stderr, "lseek: seek before file\n");
 117                fail_file();
 118        }
 119        return file_ptr - file_map;
 120}
 121
 122static size_t
 123uread(int const fd, void *const buf, size_t const count)
 124{
 125        size_t const n = read(fd, buf, count);
 126        if (n != count) {
 127                perror("read");
 128                fail_file();
 129        }
 130        return n;
 131}
 132
 133static size_t
 134uwrite(int const fd, void const *const buf, size_t const count)
 135{
 136        size_t cnt = count;
 137        off_t idx = 0;
 138
 139        file_updated = 1;
 140
 141        if (file_ptr + count >= file_end) {
 142                off_t aoffset = (file_ptr + count) - file_end;
 143
 144                if (aoffset > file_append_size) {
 145                        file_append = realloc(file_append, aoffset);
 146                        file_append_size = aoffset;
 147                }
 148                if (!file_append) {
 149                        perror("write");
 150                        fail_file();
 151                }
 152                if (file_ptr < file_end) {
 153                        cnt = file_end - file_ptr;
 154                } else {
 155                        cnt = 0;
 156                        idx = aoffset - count;
 157                }
 158        }
 159
 160        if (cnt)
 161                memcpy(file_ptr, buf, cnt);
 162
 163        if (cnt < count)
 164                memcpy(file_append + idx, buf + cnt, count - cnt);
 165
 166        file_ptr += count;
 167        return count;
 168}
 169
 170static void *
 171umalloc(size_t size)
 172{
 173        void *const addr = malloc(size);
 174        if (addr == 0) {
 175                fprintf(stderr, "malloc failed: %zu bytes\n", size);
 176                fail_file();
 177        }
 178        return addr;
 179}
 180
 181static unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
 182static unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };
 183static unsigned char *ideal_nop;
 184
 185static char rel_type_nop;
 186
 187static int (*make_nop)(void *map, size_t const offset);
 188
 189static int make_nop_x86(void *map, size_t const offset)
 190{
 191        uint32_t *ptr;
 192        unsigned char *op;
 193
 194        /* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */
 195        ptr = map + offset;
 196        if (*ptr != 0)
 197                return -1;
 198
 199        op = map + offset - 1;
 200        if (*op != 0xe8)
 201                return -1;
 202
 203        /* convert to nop */
 204        ulseek(fd_map, offset - 1, SEEK_SET);
 205        uwrite(fd_map, ideal_nop, 5);
 206        return 0;
 207}
 208
 209static unsigned char ideal_nop4_arm64[4] = {0x1f, 0x20, 0x03, 0xd5};
 210static int make_nop_arm64(void *map, size_t const offset)
 211{
 212        uint32_t *ptr;
 213
 214        ptr = map + offset;
 215        /* bl <_mcount> is 0x94000000 before relocation */
 216        if (*ptr != 0x94000000)
 217                return -1;
 218
 219        /* Convert to nop */
 220        ulseek(fd_map, offset, SEEK_SET);
 221        uwrite(fd_map, ideal_nop, 4);
 222        return 0;
 223}
 224
 225/*
 226 * Get the whole file as a programming convenience in order to avoid
 227 * malloc+lseek+read+free of many pieces.  If successful, then mmap
 228 * avoids copying unused pieces; else just read the whole file.
 229 * Open for both read and write; new info will be appended to the file.
 230 * Use MAP_PRIVATE so that a few changes to the in-memory ElfXX_Ehdr
 231 * do not propagate to the file until an explicit overwrite at the last.
 232 * This preserves most aspects of consistency (all except .st_size)
 233 * for simultaneous readers of the file while we are appending to it.
 234 * However, multiple writers still are bad.  We choose not to use
 235 * locking because it is expensive and the use case of kernel build
 236 * makes multiple writers unlikely.
 237 */
 238static void *mmap_file(char const *fname)
 239{
 240        fd_map = open(fname, O_RDONLY);
 241        if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
 242                perror(fname);
 243                fail_file();
 244        }
 245        if (!S_ISREG(sb.st_mode)) {
 246                fprintf(stderr, "not a regular file: %s\n", fname);
 247                fail_file();
 248        }
 249        file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
 250                        fd_map, 0);
 251        mmap_failed = 0;
 252        if (file_map == MAP_FAILED) {
 253                mmap_failed = 1;
 254                file_map = umalloc(sb.st_size);
 255                uread(fd_map, file_map, sb.st_size);
 256        }
 257        close(fd_map);
 258
 259        file_end = file_map + sb.st_size;
 260
 261        return file_map;
 262}
 263
 264static void write_file(const char *fname)
 265{
 266        char tmp_file[strlen(fname) + 4];
 267        size_t n;
 268
 269        if (!file_updated)
 270                return;
 271
 272        sprintf(tmp_file, "%s.rc", fname);
 273
 274        /*
 275         * After reading the entire file into memory, delete it
 276         * and write it back, to prevent weird side effects of modifying
 277         * an object file in place.
 278         */
 279        fd_map = open(tmp_file, O_WRONLY | O_TRUNC | O_CREAT, sb.st_mode);
 280        if (fd_map < 0) {
 281                perror(fname);
 282                fail_file();
 283        }
 284        n = write(fd_map, file_map, sb.st_size);
 285        if (n != sb.st_size) {
 286                perror("write");
 287                fail_file();
 288        }
 289        if (file_append_size) {
 290                n = write(fd_map, file_append, file_append_size);
 291                if (n != file_append_size) {
 292                        perror("write");
 293                        fail_file();
 294                }
 295        }
 296        close(fd_map);
 297        if (rename(tmp_file, fname) < 0) {
 298                perror(fname);
 299                fail_file();
 300        }
 301}
 302
 303/* w8rev, w8nat, ...: Handle endianness. */
 304
 305static uint64_t w8rev(uint64_t const x)
 306{
 307        return   ((0xff & (x >> (0 * 8))) << (7 * 8))
 308               | ((0xff & (x >> (1 * 8))) << (6 * 8))
 309               | ((0xff & (x >> (2 * 8))) << (5 * 8))
 310               | ((0xff & (x >> (3 * 8))) << (4 * 8))
 311               | ((0xff & (x >> (4 * 8))) << (3 * 8))
 312               | ((0xff & (x >> (5 * 8))) << (2 * 8))
 313               | ((0xff & (x >> (6 * 8))) << (1 * 8))
 314               | ((0xff & (x >> (7 * 8))) << (0 * 8));
 315}
 316
 317static uint32_t w4rev(uint32_t const x)
 318{
 319        return   ((0xff & (x >> (0 * 8))) << (3 * 8))
 320               | ((0xff & (x >> (1 * 8))) << (2 * 8))
 321               | ((0xff & (x >> (2 * 8))) << (1 * 8))
 322               | ((0xff & (x >> (3 * 8))) << (0 * 8));
 323}
 324
 325static uint32_t w2rev(uint16_t const x)
 326{
 327        return   ((0xff & (x >> (0 * 8))) << (1 * 8))
 328               | ((0xff & (x >> (1 * 8))) << (0 * 8));
 329}
 330
 331static uint64_t w8nat(uint64_t const x)
 332{
 333        return x;
 334}
 335
 336static uint32_t w4nat(uint32_t const x)
 337{
 338        return x;
 339}
 340
 341static uint32_t w2nat(uint16_t const x)
 342{
 343        return x;
 344}
 345
 346static uint64_t (*w8)(uint64_t);
 347static uint32_t (*w)(uint32_t);
 348static uint32_t (*w2)(uint16_t);
 349
 350/* Names of the sections that could contain calls to mcount. */
 351static int
 352is_mcounted_section_name(char const *const txtname)
 353{
 354        return strcmp(".text",           txtname) == 0 ||
 355                strcmp(".ref.text",      txtname) == 0 ||
 356                strcmp(".sched.text",    txtname) == 0 ||
 357                strcmp(".spinlock.text", txtname) == 0 ||
 358                strcmp(".irqentry.text", txtname) == 0 ||
 359                strcmp(".kprobes.text", txtname) == 0 ||
 360                strcmp(".text.unlikely", txtname) == 0;
 361}
 362
 363/* 32 bit and 64 bit are very similar */
 364#include "recordmcount.h"
 365#define RECORD_MCOUNT_64
 366#include "recordmcount.h"
 367
 368/* 64-bit EM_MIPS has weird ELF64_Rela.r_info.
 369 * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
 370 * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40]
 371 * to imply the order of the members; the spec does not say so.
 372 *      typedef unsigned char Elf64_Byte;
 373 * fails on MIPS64 because their <elf.h> already has it!
 374 */
 375
 376typedef uint8_t myElf64_Byte;           /* Type for a 8-bit quantity.  */
 377
 378union mips_r_info {
 379        Elf64_Xword r_info;
 380        struct {
 381                Elf64_Word r_sym;               /* Symbol index.  */
 382                myElf64_Byte r_ssym;            /* Special symbol.  */
 383                myElf64_Byte r_type3;           /* Third relocation.  */
 384                myElf64_Byte r_type2;           /* Second relocation.  */
 385                myElf64_Byte r_type;            /* First relocation.  */
 386        } r_mips;
 387};
 388
 389static uint64_t MIPS64_r_sym(Elf64_Rel const *rp)
 390{
 391        return w(((union mips_r_info){ .r_info = rp->r_info }).r_mips.r_sym);
 392}
 393
 394static void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)
 395{
 396        rp->r_info = ((union mips_r_info){
 397                .r_mips = { .r_sym = w(sym), .r_type = type }
 398        }).r_info;
 399}
 400
 401static void
 402do_file(char const *const fname)
 403{
 404        Elf32_Ehdr *const ehdr = mmap_file(fname);
 405        unsigned int reltype = 0;
 406
 407        w = w4nat;
 408        w2 = w2nat;
 409        w8 = w8nat;
 410        switch (ehdr->e_ident[EI_DATA]) {
 411                static unsigned int const endian = 1;
 412        default:
 413                fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
 414                        ehdr->e_ident[EI_DATA], fname);
 415                fail_file();
 416                break;
 417        case ELFDATA2LSB:
 418                if (*(unsigned char const *)&endian != 1) {
 419                        /* main() is big endian, file.o is little endian. */
 420                        w = w4rev;
 421                        w2 = w2rev;
 422                        w8 = w8rev;
 423                }
 424                break;
 425        case ELFDATA2MSB:
 426                if (*(unsigned char const *)&endian != 0) {
 427                        /* main() is little endian, file.o is big endian. */
 428                        w = w4rev;
 429                        w2 = w2rev;
 430                        w8 = w8rev;
 431                }
 432                break;
 433        }  /* end switch */
 434        if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
 435        ||  w2(ehdr->e_type) != ET_REL
 436        ||  ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
 437                fprintf(stderr, "unrecognized ET_REL file %s\n", fname);
 438                fail_file();
 439        }
 440
 441        gpfx = 0;
 442        switch (w2(ehdr->e_machine)) {
 443        default:
 444                fprintf(stderr, "unrecognized e_machine %d %s\n",
 445                        w2(ehdr->e_machine), fname);
 446                fail_file();
 447                break;
 448        case EM_386:
 449                reltype = R_386_32;
 450                rel_type_nop = R_386_NONE;
 451                make_nop = make_nop_x86;
 452                ideal_nop = ideal_nop5_x86_32;
 453                mcount_adjust_32 = -1;
 454                break;
 455        case EM_ARM:     reltype = R_ARM_ABS32;
 456                         altmcount = "__gnu_mcount_nc";
 457                         break;
 458        case EM_AARCH64:
 459                        reltype = R_AARCH64_ABS64;
 460                        make_nop = make_nop_arm64;
 461                        rel_type_nop = R_AARCH64_NONE;
 462                        ideal_nop = ideal_nop4_arm64;
 463                        gpfx = '_';
 464                        break;
 465        case EM_IA_64:   reltype = R_IA64_IMM64;   gpfx = '_'; break;
 466        case EM_METAG:   reltype = R_METAG_ADDR32;
 467                         altmcount = "_mcount_wrapper";
 468                         rel_type_nop = R_METAG_NONE;
 469                         /* We happen to have the same requirement as MIPS */
 470                         is_fake_mcount32 = MIPS32_is_fake_mcount;
 471                         break;
 472        case EM_MIPS:    /* reltype: e_class    */ gpfx = '_'; break;
 473        case EM_PPC:     reltype = R_PPC_ADDR32;   gpfx = '_'; break;
 474        case EM_PPC64:   reltype = R_PPC64_ADDR64; gpfx = '_'; break;
 475        case EM_S390:    /* reltype: e_class    */ gpfx = '_'; break;
 476        case EM_SH:      reltype = R_SH_DIR32;                 break;
 477        case EM_SPARCV9: reltype = R_SPARC_64;     gpfx = '_'; break;
 478        case EM_X86_64:
 479                make_nop = make_nop_x86;
 480                ideal_nop = ideal_nop5_x86_64;
 481                reltype = R_X86_64_64;
 482                rel_type_nop = R_X86_64_NONE;
 483                mcount_adjust_64 = -1;
 484                break;
 485        }  /* end switch */
 486
 487        switch (ehdr->e_ident[EI_CLASS]) {
 488        default:
 489                fprintf(stderr, "unrecognized ELF class %d %s\n",
 490                        ehdr->e_ident[EI_CLASS], fname);
 491                fail_file();
 492                break;
 493        case ELFCLASS32:
 494                if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
 495                ||  w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
 496                        fprintf(stderr,
 497                                "unrecognized ET_REL file: %s\n", fname);
 498                        fail_file();
 499                }
 500                if (w2(ehdr->e_machine) == EM_MIPS) {
 501                        reltype = R_MIPS_32;
 502                        is_fake_mcount32 = MIPS32_is_fake_mcount;
 503                }
 504                do32(ehdr, fname, reltype);
 505                break;
 506        case ELFCLASS64: {
 507                Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
 508                if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
 509                ||  w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
 510                        fprintf(stderr,
 511                                "unrecognized ET_REL file: %s\n", fname);
 512                        fail_file();
 513                }
 514                if (w2(ghdr->e_machine) == EM_S390) {
 515                        reltype = R_390_64;
 516                        mcount_adjust_64 = -14;
 517                }
 518                if (w2(ghdr->e_machine) == EM_MIPS) {
 519                        reltype = R_MIPS_64;
 520                        Elf64_r_sym = MIPS64_r_sym;
 521                        Elf64_r_info = MIPS64_r_info;
 522                        is_fake_mcount64 = MIPS64_is_fake_mcount;
 523                }
 524                do64(ghdr, fname, reltype);
 525                break;
 526        }
 527        }  /* end switch */
 528
 529        write_file(fname);
 530        cleanup();
 531}
 532
 533int
 534main(int argc, char *argv[])
 535{
 536        const char ftrace[] = "/ftrace.o";
 537        int ftrace_size = sizeof(ftrace) - 1;
 538        int n_error = 0;  /* gcc-4.3.0 false positive complaint */
 539        int c;
 540        int i;
 541
 542        while ((c = getopt(argc, argv, "w")) >= 0) {
 543                switch (c) {
 544                case 'w':
 545                        warn_on_notrace_sect = 1;
 546                        break;
 547                default:
 548                        fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
 549                        return 0;
 550                }
 551        }
 552
 553        if ((argc - optind) < 1) {
 554                fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
 555                return 0;
 556        }
 557
 558        /* Process each file in turn, allowing deep failure. */
 559        for (i = optind; i < argc; i++) {
 560                char *file = argv[i];
 561                int const sjval = setjmp(jmpenv);
 562                int len;
 563
 564                /*
 565                 * The file kernel/trace/ftrace.o references the mcount
 566                 * function but does not call it. Since ftrace.o should
 567                 * not be traced anyway, we just skip it.
 568                 */
 569                len = strlen(file);
 570                if (len >= ftrace_size &&
 571                    strcmp(file + (len - ftrace_size), ftrace) == 0)
 572                        continue;
 573
 574                switch (sjval) {
 575                default:
 576                        fprintf(stderr, "internal error: %s\n", file);
 577                        exit(1);
 578                        break;
 579                case SJ_SETJMP:    /* normal sequence */
 580                        /* Avoid problems if early cleanup() */
 581                        fd_map = -1;
 582                        mmap_failed = 1;
 583                        file_map = NULL;
 584                        file_ptr = NULL;
 585                        file_updated = 0;
 586                        do_file(file);
 587                        break;
 588                case SJ_FAIL:    /* error in do_file or below */
 589                        fprintf(stderr, "%s: failed\n", file);
 590                        ++n_error;
 591                        break;
 592                case SJ_SUCCEED:    /* premature success */
 593                        /* do nothing */
 594                        break;
 595                }  /* end switch */
 596        }
 597        return !!n_error;
 598}
 599