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 49 enum { 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. */ 58 static 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 68 static const char *program_name; 69 static const char *vdso_name; 70 static unsigned char elf_class; 71 static unsigned int elf_abi; 72 static bool need_swap; 73 static 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 87 BUILD_SWAP(16) 88 BUILD_SWAP(32) 89 BUILD_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 112 static 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 187 static 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 195 static 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 203 int 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