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