linux/tools/perf/util/genelf.c
<<
>>
Prefs
   1/*
   2 * genelf.c
   3 * Copyright (C) 2014, Google, Inc
   4 *
   5 * Contributed by:
   6 *      Stephane Eranian <eranian@gmail.com>
   7 *
   8 * Released under the GPL v2. (and only v2, not any later version)
   9 */
  10
  11#include <sys/types.h>
  12#include <stdio.h>
  13#include <getopt.h>
  14#include <stddef.h>
  15#include <libelf.h>
  16#include <string.h>
  17#include <stdlib.h>
  18#include <inttypes.h>
  19#include <limits.h>
  20#include <fcntl.h>
  21#include <err.h>
  22#include <dwarf.h>
  23
  24#include "perf.h"
  25#include "genelf.h"
  26#include "../util/jitdump.h"
  27
  28#define JVMTI
  29
  30#define BUILD_ID_URANDOM /* different uuid for each run */
  31
  32#ifdef HAVE_LIBCRYPTO
  33
  34#define BUILD_ID_MD5
  35#undef BUILD_ID_SHA     /* does not seem to work well when linked with Java */
  36#undef BUILD_ID_URANDOM /* different uuid for each run */
  37
  38#ifdef BUILD_ID_SHA
  39#include <openssl/sha.h>
  40#endif
  41
  42#ifdef BUILD_ID_MD5
  43#include <openssl/md5.h>
  44#endif
  45#endif
  46
  47
  48typedef struct {
  49  unsigned int namesz;  /* Size of entry's owner string */
  50  unsigned int descsz;  /* Size of the note descriptor */
  51  unsigned int type;    /* Interpretation of the descriptor */
  52  char         name[0]; /* Start of the name+desc data */
  53} Elf_Note;
  54
  55struct options {
  56        char *output;
  57        int fd;
  58};
  59
  60static char shd_string_table[] = {
  61        0,
  62        '.', 't', 'e', 'x', 't', 0,                     /*  1 */
  63        '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0, /*  7 */
  64        '.', 's', 'y', 'm', 't', 'a', 'b', 0,           /* 17 */
  65        '.', 's', 't', 'r', 't', 'a', 'b', 0,           /* 25 */
  66        '.', 'n', 'o', 't', 'e', '.', 'g', 'n', 'u', '.', 'b', 'u', 'i', 'l', 'd', '-', 'i', 'd', 0, /* 33 */
  67        '.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0, /* 52 */
  68        '.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0, /* 64 */
  69        '.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0, /* 76 */
  70};
  71
  72static struct buildid_note {
  73        Elf_Note desc;          /* descsz: size of build-id, must be multiple of 4 */
  74        char     name[4];       /* GNU\0 */
  75        char     build_id[20];
  76} bnote;
  77
  78static Elf_Sym symtab[]={
  79        /* symbol 0 MUST be the undefined symbol */
  80        { .st_name  = 0, /* index in sym_string table */
  81          .st_info  = ELF_ST_TYPE(STT_NOTYPE),
  82          .st_shndx = 0, /* for now */
  83          .st_value = 0x0,
  84          .st_other = ELF_ST_VIS(STV_DEFAULT),
  85          .st_size  = 0,
  86        },
  87        { .st_name  = 1, /* index in sym_string table */
  88          .st_info  = ELF_ST_BIND(STB_LOCAL) | ELF_ST_TYPE(STT_FUNC),
  89          .st_shndx = 1,
  90          .st_value = 0, /* for now */
  91          .st_other = ELF_ST_VIS(STV_DEFAULT),
  92          .st_size  = 0, /* for now */
  93        }
  94};
  95
  96#ifdef BUILD_ID_URANDOM
  97static void
  98gen_build_id(struct buildid_note *note,
  99             unsigned long load_addr __maybe_unused,
 100             const void *code __maybe_unused,
 101             size_t csize __maybe_unused)
 102{
 103        int fd;
 104        size_t sz = sizeof(note->build_id);
 105        ssize_t sret;
 106
 107        fd = open("/dev/urandom", O_RDONLY);
 108        if (fd == -1)
 109                err(1, "cannot access /dev/urandom for builid");
 110
 111        sret = read(fd, note->build_id, sz);
 112
 113        close(fd);
 114
 115        if (sret != (ssize_t)sz)
 116                memset(note->build_id, 0, sz);
 117}
 118#endif
 119
 120#ifdef BUILD_ID_SHA
 121static void
 122gen_build_id(struct buildid_note *note,
 123             unsigned long load_addr __maybe_unused,
 124             const void *code,
 125             size_t csize)
 126{
 127        if (sizeof(note->build_id) < SHA_DIGEST_LENGTH)
 128                errx(1, "build_id too small for SHA1");
 129
 130        SHA1(code, csize, (unsigned char *)note->build_id);
 131}
 132#endif
 133
 134#ifdef BUILD_ID_MD5
 135static void
 136gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *code, size_t csize)
 137{
 138        MD5_CTX context;
 139
 140        if (sizeof(note->build_id) < 16)
 141                errx(1, "build_id too small for MD5");
 142
 143        MD5_Init(&context);
 144        MD5_Update(&context, &load_addr, sizeof(load_addr));
 145        MD5_Update(&context, code, csize);
 146        MD5_Final((unsigned char *)note->build_id, &context);
 147}
 148#endif
 149
 150/*
 151 * fd: file descriptor open for writing for the output file
 152 * load_addr: code load address (could be zero, just used for buildid)
 153 * sym: function name (for native code - used as the symbol)
 154 * code: the native code
 155 * csize: the code size in bytes
 156 */
 157int
 158jit_write_elf(int fd, uint64_t load_addr, const char *sym,
 159              const void *code, int csize,
 160              void *debug, int nr_debug_entries)
 161{
 162        Elf *e;
 163        Elf_Data *d;
 164        Elf_Scn *scn;
 165        Elf_Ehdr *ehdr;
 166        Elf_Shdr *shdr;
 167        char *strsym = NULL;
 168        int symlen;
 169        int retval = -1;
 170
 171        if (elf_version(EV_CURRENT) == EV_NONE) {
 172                warnx("ELF initialization failed");
 173                return -1;
 174        }
 175
 176        e = elf_begin(fd, ELF_C_WRITE, NULL);
 177        if (!e) {
 178                warnx("elf_begin failed");
 179                goto error;
 180        }
 181
 182        /*
 183         * setup ELF header
 184         */
 185        ehdr = elf_newehdr(e);
 186        if (!ehdr) {
 187                warnx("cannot get ehdr");
 188                goto error;
 189        }
 190
 191        ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN;
 192        ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS;
 193        ehdr->e_machine = GEN_ELF_ARCH;
 194        ehdr->e_type = ET_DYN;
 195        ehdr->e_entry = GEN_ELF_TEXT_OFFSET;
 196        ehdr->e_version = EV_CURRENT;
 197        ehdr->e_shstrndx= 2; /* shdr index for section name */
 198
 199        /*
 200         * setup text section
 201         */
 202        scn = elf_newscn(e);
 203        if (!scn) {
 204                warnx("cannot create section");
 205                goto error;
 206        }
 207
 208        d = elf_newdata(scn);
 209        if (!d) {
 210                warnx("cannot get new data");
 211                goto error;
 212        }
 213
 214        d->d_align = 16;
 215        d->d_off = 0LL;
 216        d->d_buf = (void *)code;
 217        d->d_type = ELF_T_BYTE;
 218        d->d_size = csize;
 219        d->d_version = EV_CURRENT;
 220
 221        shdr = elf_getshdr(scn);
 222        if (!shdr) {
 223                warnx("cannot get section header");
 224                goto error;
 225        }
 226
 227        shdr->sh_name = 1;
 228        shdr->sh_type = SHT_PROGBITS;
 229        shdr->sh_addr = GEN_ELF_TEXT_OFFSET;
 230        shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
 231        shdr->sh_entsize = 0;
 232
 233        /*
 234         * setup section headers string table
 235         */
 236        scn = elf_newscn(e);
 237        if (!scn) {
 238                warnx("cannot create section");
 239                goto error;
 240        }
 241
 242        d = elf_newdata(scn);
 243        if (!d) {
 244                warnx("cannot get new data");
 245                goto error;
 246        }
 247
 248        d->d_align = 1;
 249        d->d_off = 0LL;
 250        d->d_buf = shd_string_table;
 251        d->d_type = ELF_T_BYTE;
 252        d->d_size = sizeof(shd_string_table);
 253        d->d_version = EV_CURRENT;
 254
 255        shdr = elf_getshdr(scn);
 256        if (!shdr) {
 257                warnx("cannot get section header");
 258                goto error;
 259        }
 260
 261        shdr->sh_name = 7; /* offset of '.shstrtab' in shd_string_table */
 262        shdr->sh_type = SHT_STRTAB;
 263        shdr->sh_flags = 0;
 264        shdr->sh_entsize = 0;
 265
 266        /*
 267         * setup symtab section
 268         */
 269        symtab[1].st_size  = csize;
 270        symtab[1].st_value = GEN_ELF_TEXT_OFFSET;
 271
 272        scn = elf_newscn(e);
 273        if (!scn) {
 274                warnx("cannot create section");
 275                goto error;
 276        }
 277
 278        d = elf_newdata(scn);
 279        if (!d) {
 280                warnx("cannot get new data");
 281                goto error;
 282        }
 283
 284        d->d_align = 8;
 285        d->d_off = 0LL;
 286        d->d_buf = symtab;
 287        d->d_type = ELF_T_SYM;
 288        d->d_size = sizeof(symtab);
 289        d->d_version = EV_CURRENT;
 290
 291        shdr = elf_getshdr(scn);
 292        if (!shdr) {
 293                warnx("cannot get section header");
 294                goto error;
 295        }
 296
 297        shdr->sh_name = 17; /* offset of '.symtab' in shd_string_table */
 298        shdr->sh_type = SHT_SYMTAB;
 299        shdr->sh_flags = 0;
 300        shdr->sh_entsize = sizeof(Elf_Sym);
 301        shdr->sh_link = 4; /* index of .strtab section */
 302
 303        /*
 304         * setup symbols string table
 305         * 2 = 1 for 0 in 1st entry, 1 for the 0 at end of symbol for 2nd entry
 306         */
 307        symlen = 2 + strlen(sym);
 308        strsym = calloc(1, symlen);
 309        if (!strsym) {
 310                warnx("cannot allocate strsym");
 311                goto error;
 312        }
 313        strcpy(strsym + 1, sym);
 314
 315        scn = elf_newscn(e);
 316        if (!scn) {
 317                warnx("cannot create section");
 318                goto error;
 319        }
 320
 321        d = elf_newdata(scn);
 322        if (!d) {
 323                warnx("cannot get new data");
 324                goto error;
 325        }
 326
 327        d->d_align = 1;
 328        d->d_off = 0LL;
 329        d->d_buf = strsym;
 330        d->d_type = ELF_T_BYTE;
 331        d->d_size = symlen;
 332        d->d_version = EV_CURRENT;
 333
 334        shdr = elf_getshdr(scn);
 335        if (!shdr) {
 336                warnx("cannot get section header");
 337                goto error;
 338        }
 339
 340        shdr->sh_name = 25; /* offset in shd_string_table */
 341        shdr->sh_type = SHT_STRTAB;
 342        shdr->sh_flags = 0;
 343        shdr->sh_entsize = 0;
 344
 345        /*
 346         * setup build-id section
 347         */
 348        scn = elf_newscn(e);
 349        if (!scn) {
 350                warnx("cannot create section");
 351                goto error;
 352        }
 353
 354        d = elf_newdata(scn);
 355        if (!d) {
 356                warnx("cannot get new data");
 357                goto error;
 358        }
 359
 360        /*
 361         * build-id generation
 362         */
 363        gen_build_id(&bnote, load_addr, code, csize);
 364        bnote.desc.namesz = sizeof(bnote.name); /* must include 0 termination */
 365        bnote.desc.descsz = sizeof(bnote.build_id);
 366        bnote.desc.type   = NT_GNU_BUILD_ID;
 367        strcpy(bnote.name, "GNU");
 368
 369        d->d_align = 4;
 370        d->d_off = 0LL;
 371        d->d_buf = &bnote;
 372        d->d_type = ELF_T_BYTE;
 373        d->d_size = sizeof(bnote);
 374        d->d_version = EV_CURRENT;
 375
 376        shdr = elf_getshdr(scn);
 377        if (!shdr) {
 378                warnx("cannot get section header");
 379                goto error;
 380        }
 381
 382        shdr->sh_name = 33; /* offset in shd_string_table */
 383        shdr->sh_type = SHT_NOTE;
 384        shdr->sh_addr = 0x0;
 385        shdr->sh_flags = SHF_ALLOC;
 386        shdr->sh_size = sizeof(bnote);
 387        shdr->sh_entsize = 0;
 388
 389        if (debug && nr_debug_entries) {
 390                retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries);
 391                if (retval)
 392                        goto error;
 393        } else {
 394                if (elf_update(e, ELF_C_WRITE) < 0) {
 395                        warnx("elf_update 4 failed");
 396                        goto error;
 397                }
 398        }
 399
 400        retval = 0;
 401error:
 402        (void)elf_end(e);
 403
 404        free(strsym);
 405
 406
 407        return retval;
 408}
 409
 410#ifndef JVMTI
 411
 412static unsigned char x86_code[] = {
 413    0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */
 414    0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */
 415    0xCD, 0x80            /* int $0x80 */
 416};
 417
 418static struct options options;
 419
 420int main(int argc, char **argv)
 421{
 422        int c, fd, ret;
 423
 424        while ((c = getopt(argc, argv, "o:h")) != -1) {
 425                switch (c) {
 426                case 'o':
 427                        options.output = optarg;
 428                        break;
 429                case 'h':
 430                        printf("Usage: genelf -o output_file [-h]\n");
 431                        return 0;
 432                default:
 433                        errx(1, "unknown option");
 434                }
 435        }
 436
 437        fd = open(options.output, O_CREAT|O_TRUNC|O_RDWR, 0666);
 438        if (fd == -1)
 439                err(1, "cannot create file %s", options.output);
 440
 441        ret = jit_write_elf(fd, "main", x86_code, sizeof(x86_code));
 442        close(fd);
 443
 444        if (ret != 0)
 445                unlink(options.output);
 446
 447        return ret;
 448}
 449#endif
 450