linux/arch/mips/vdso/genvdso.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Imagination Technologies
   3 * Author: Alex Smith <alex.smith@imgtec.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License as published by the
   7 * Free Software Foundation;  either version 2 of the  License, or (at your
   8 * option) any later version.
   9 */
  10
  11/*
  12 * This tool is used to generate the real VDSO images from the raw image. It
  13 * first patches up the MIPS ABI flags and GNU attributes sections defined in
  14 * elf.S to have the correct name and type. It then generates a C source file
  15 * to be compiled into the kernel containing the VDSO image data and a
  16 * mips_vdso_image struct for it, including symbol offsets extracted from the
  17 * image.
  18 *
  19 * We need to be passed both a stripped and unstripped VDSO image. The stripped
  20 * image is compiled into the kernel, but we must also patch up the unstripped
  21 * image's ABI flags sections so that it can be installed and used for
  22 * debugging.
  23 */
  24
  25#include <sys/mman.h>
  26#include <sys/stat.h>
  27#include <sys/types.h>
  28
  29#include <byteswap.h>
  30#include <elf.h>
  31#include <errno.h>
  32#include <fcntl.h>
  33#include <inttypes.h>
  34#include <stdarg.h>
  35#include <stdbool.h>
  36#include <stdio.h>
  37#include <stdlib.h>
  38#include <string.h>
  39#include <unistd.h>
  40
  41/* Define these in case the system elf.h is not new enough to have them. */
  42#ifndef SHT_GNU_ATTRIBUTES
  43# define SHT_GNU_ATTRIBUTES     0x6ffffff5
  44#endif
  45#ifndef SHT_MIPS_ABIFLAGS
  46# define SHT_MIPS_ABIFLAGS      0x7000002a
  47#endif
  48
  49enum {
  50        ABI_O32 = (1 << 0),
  51        ABI_N32 = (1 << 1),
  52        ABI_N64 = (1 << 2),
  53
  54        ABI_ALL = ABI_O32 | ABI_N32 | ABI_N64,
  55};
  56
  57/* Symbols the kernel requires offsets for. */
  58static struct {
  59        const char *name;
  60        const char *offset_name;
  61        unsigned int abis;
  62} vdso_symbols[] = {
  63        { "__vdso_sigreturn", "off_sigreturn", ABI_O32 },
  64        { "__vdso_rt_sigreturn", "off_rt_sigreturn", ABI_ALL },
  65        {}
  66};
  67
  68static const char *program_name;
  69static const char *vdso_name;
  70static unsigned char elf_class;
  71static unsigned int elf_abi;
  72static bool need_swap;
  73static FILE *out_file;
  74
  75#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  76# define HOST_ORDER             ELFDATA2LSB
  77#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  78# define HOST_ORDER             ELFDATA2MSB
  79#endif
  80
  81#define BUILD_SWAP(bits)                                                \
  82        static uint##bits##_t swap_uint##bits(uint##bits##_t val)       \
  83        {                                                               \
  84                return need_swap ? bswap_##bits(val) : val;             \
  85        }
  86
  87BUILD_SWAP(16)
  88BUILD_SWAP(32)
  89BUILD_SWAP(64)
  90
  91#define __FUNC(name, bits) name##bits
  92#define _FUNC(name, bits) __FUNC(name, bits)
  93#define FUNC(name) _FUNC(name, ELF_BITS)
  94
  95#define __ELF(x, bits) Elf##bits##_##x
  96#define _ELF(x, bits) __ELF(x, bits)
  97#define ELF(x) _ELF(x, ELF_BITS)
  98
  99/*
 100 * Include genvdso.h twice with ELF_BITS defined differently to get functions
 101 * for both ELF32 and ELF64.
 102 */
 103
 104#define ELF_BITS 64
 105#include "genvdso.h"
 106#undef ELF_BITS
 107
 108#define ELF_BITS 32
 109#include "genvdso.h"
 110#undef ELF_BITS
 111
 112static void *map_vdso(const char *path, size_t *_size)
 113{
 114        int fd;
 115        struct stat stat;
 116        void *addr;
 117        const Elf32_Ehdr *ehdr;
 118
 119        fd = open(path, O_RDWR);
 120        if (fd < 0) {
 121                fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name,
 122                        path, strerror(errno));
 123                return NULL;
 124        }
 125
 126        if (fstat(fd, &stat) != 0) {
 127                fprintf(stderr, "%s: Failed to stat '%s': %s\n", program_name,
 128                        path, strerror(errno));
 129                return NULL;
 130        }
 131
 132        addr = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
 133                    0);
 134        if (addr == MAP_FAILED) {
 135                fprintf(stderr, "%s: Failed to map '%s': %s\n", program_name,
 136                        path, strerror(errno));
 137                return NULL;
 138        }
 139
 140        /* ELF32/64 header formats are the same for the bits we're checking. */
 141        ehdr = addr;
 142
 143        if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
 144                fprintf(stderr, "%s: '%s' is not an ELF file\n", program_name,
 145                        path);
 146                return NULL;
 147        }
 148
 149        elf_class = ehdr->e_ident[EI_CLASS];
 150        switch (elf_class) {
 151        case ELFCLASS32:
 152        case ELFCLASS64:
 153                break;
 154        default:
 155                fprintf(stderr, "%s: '%s' has invalid ELF class\n",
 156                        program_name, path);
 157                return NULL;
 158        }
 159
 160        switch (ehdr->e_ident[EI_DATA]) {
 161        case ELFDATA2LSB:
 162        case ELFDATA2MSB:
 163                need_swap = ehdr->e_ident[EI_DATA] != HOST_ORDER;
 164                break;
 165        default:
 166                fprintf(stderr, "%s: '%s' has invalid ELF data order\n",
 167                        program_name, path);
 168                return NULL;
 169        }
 170
 171        if (swap_uint16(ehdr->e_machine) != EM_MIPS) {
 172                fprintf(stderr,
 173                        "%s: '%s' has invalid ELF machine (expected EM_MIPS)\n",
 174                        program_name, path);
 175                return NULL;
 176        } else if (swap_uint16(ehdr->e_type) != ET_DYN) {
 177                fprintf(stderr,
 178                        "%s: '%s' has invalid ELF type (expected ET_DYN)\n",
 179                        program_name, path);
 180                return NULL;
 181        }
 182
 183        *_size = stat.st_size;
 184        return addr;
 185}
 186
 187static bool patch_vdso(const char *path, void *vdso)
 188{
 189        if (elf_class == ELFCLASS64)
 190                return patch_vdso64(path, vdso);
 191        else
 192                return patch_vdso32(path, vdso);
 193}
 194
 195static bool get_symbols(const char *path, void *vdso)
 196{
 197        if (elf_class == ELFCLASS64)
 198                return get_symbols64(path, vdso);
 199        else
 200                return get_symbols32(path, vdso);
 201}
 202
 203int main(int argc, char **argv)
 204{
 205        const char *dbg_vdso_path, *vdso_path, *out_path;
 206        void *dbg_vdso, *vdso;
 207        size_t dbg_vdso_size, vdso_size, i;
 208
 209        program_name = argv[0];
 210
 211        if (argc < 4 || argc > 5) {
 212                fprintf(stderr,
 213                        "Usage: %s <debug VDSO> <stripped VDSO> <output file> [<name>]\n",
 214                        program_name);
 215                return EXIT_FAILURE;
 216        }
 217
 218        dbg_vdso_path = argv[1];
 219        vdso_path = argv[2];
 220        out_path = argv[3];
 221        vdso_name = (argc > 4) ? argv[4] : "";
 222
 223        dbg_vdso = map_vdso(dbg_vdso_path, &dbg_vdso_size);
 224        if (!dbg_vdso)
 225                return EXIT_FAILURE;
 226
 227        vdso = map_vdso(vdso_path, &vdso_size);
 228        if (!vdso)
 229                return EXIT_FAILURE;
 230
 231        /* Patch both the VDSOs' ABI flags sections. */
 232        if (!patch_vdso(dbg_vdso_path, dbg_vdso))
 233                return EXIT_FAILURE;
 234        if (!patch_vdso(vdso_path, vdso))
 235                return EXIT_FAILURE;
 236
 237        if (msync(dbg_vdso, dbg_vdso_size, MS_SYNC) != 0) {
 238                fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name,
 239                        dbg_vdso_path, strerror(errno));
 240                return EXIT_FAILURE;
 241        } else if (msync(vdso, vdso_size, MS_SYNC) != 0) {
 242                fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name,
 243                        vdso_path, strerror(errno));
 244                return EXIT_FAILURE;
 245        }
 246
 247        out_file = fopen(out_path, "w");
 248        if (!out_file) {
 249                fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name,
 250                        out_path, strerror(errno));
 251                return EXIT_FAILURE;
 252        }
 253
 254        fprintf(out_file, "/* Automatically generated - do not edit */\n");
 255        fprintf(out_file, "#include <linux/linkage.h>\n");
 256        fprintf(out_file, "#include <linux/mm.h>\n");
 257        fprintf(out_file, "#include <asm/vdso.h>\n");
 258
 259        /* Write out the stripped VDSO data. */
 260        fprintf(out_file,
 261                "static unsigned char vdso_data[PAGE_ALIGN(%zu)] __page_aligned_data = {\n\t",
 262                vdso_size);
 263        for (i = 0; i < vdso_size; i++) {
 264                if (!(i % 10))
 265                        fprintf(out_file, "\n\t");
 266                fprintf(out_file, "0x%02x, ", ((unsigned char *)vdso)[i]);
 267        }
 268        fprintf(out_file, "\n};\n");
 269
 270        /* Preallocate a page array. */
 271        fprintf(out_file,
 272                "static struct page *vdso_pages[PAGE_ALIGN(%zu) / PAGE_SIZE];\n",
 273                vdso_size);
 274
 275        fprintf(out_file, "struct mips_vdso_image vdso_image%s%s = {\n",
 276                (vdso_name[0]) ? "_" : "", vdso_name);
 277        fprintf(out_file, "\t.data = vdso_data,\n");
 278        fprintf(out_file, "\t.size = PAGE_ALIGN(%zu),\n", vdso_size);
 279        fprintf(out_file, "\t.mapping = {\n");
 280        fprintf(out_file, "\t\t.name = \"[vdso]\",\n");
 281        fprintf(out_file, "\t\t.pages = vdso_pages,\n");
 282        fprintf(out_file, "\t},\n");
 283
 284        /* Calculate and write symbol offsets to <output file> */
 285        if (!get_symbols(dbg_vdso_path, dbg_vdso)) {
 286                unlink(out_path);
 287                return EXIT_FAILURE;
 288        }
 289
 290        fprintf(out_file, "};\n");
 291
 292        return EXIT_SUCCESS;
 293}
 294