18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Imagination Technologies 48c2ecf20Sopenharmony_ci * Author: Alex Smith <alex.smith@imgtec.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci/* 88c2ecf20Sopenharmony_ci * This tool is used to generate the real VDSO images from the raw image. It 98c2ecf20Sopenharmony_ci * first patches up the MIPS ABI flags and GNU attributes sections defined in 108c2ecf20Sopenharmony_ci * elf.S to have the correct name and type. It then generates a C source file 118c2ecf20Sopenharmony_ci * to be compiled into the kernel containing the VDSO image data and a 128c2ecf20Sopenharmony_ci * mips_vdso_image struct for it, including symbol offsets extracted from the 138c2ecf20Sopenharmony_ci * image. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * We need to be passed both a stripped and unstripped VDSO image. The stripped 168c2ecf20Sopenharmony_ci * image is compiled into the kernel, but we must also patch up the unstripped 178c2ecf20Sopenharmony_ci * image's ABI flags sections so that it can be installed and used for 188c2ecf20Sopenharmony_ci * debugging. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <sys/mman.h> 228c2ecf20Sopenharmony_ci#include <sys/stat.h> 238c2ecf20Sopenharmony_ci#include <sys/types.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <byteswap.h> 268c2ecf20Sopenharmony_ci#include <elf.h> 278c2ecf20Sopenharmony_ci#include <errno.h> 288c2ecf20Sopenharmony_ci#include <fcntl.h> 298c2ecf20Sopenharmony_ci#include <inttypes.h> 308c2ecf20Sopenharmony_ci#include <stdarg.h> 318c2ecf20Sopenharmony_ci#include <stdbool.h> 328c2ecf20Sopenharmony_ci#include <stdio.h> 338c2ecf20Sopenharmony_ci#include <stdlib.h> 348c2ecf20Sopenharmony_ci#include <string.h> 358c2ecf20Sopenharmony_ci#include <unistd.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Define these in case the system elf.h is not new enough to have them. */ 388c2ecf20Sopenharmony_ci#ifndef SHT_GNU_ATTRIBUTES 398c2ecf20Sopenharmony_ci# define SHT_GNU_ATTRIBUTES 0x6ffffff5 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci#ifndef SHT_MIPS_ABIFLAGS 428c2ecf20Sopenharmony_ci# define SHT_MIPS_ABIFLAGS 0x7000002a 438c2ecf20Sopenharmony_ci#endif 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cienum { 468c2ecf20Sopenharmony_ci ABI_O32 = (1 << 0), 478c2ecf20Sopenharmony_ci ABI_N32 = (1 << 1), 488c2ecf20Sopenharmony_ci ABI_N64 = (1 << 2), 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci ABI_ALL = ABI_O32 | ABI_N32 | ABI_N64, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Symbols the kernel requires offsets for. */ 548c2ecf20Sopenharmony_cistatic struct { 558c2ecf20Sopenharmony_ci const char *name; 568c2ecf20Sopenharmony_ci const char *offset_name; 578c2ecf20Sopenharmony_ci unsigned int abis; 588c2ecf20Sopenharmony_ci} vdso_symbols[] = { 598c2ecf20Sopenharmony_ci { "__vdso_sigreturn", "off_sigreturn", ABI_O32 }, 608c2ecf20Sopenharmony_ci { "__vdso_rt_sigreturn", "off_rt_sigreturn", ABI_ALL }, 618c2ecf20Sopenharmony_ci {} 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic const char *program_name; 658c2ecf20Sopenharmony_cistatic const char *vdso_name; 668c2ecf20Sopenharmony_cistatic unsigned char elf_class; 678c2ecf20Sopenharmony_cistatic unsigned int elf_abi; 688c2ecf20Sopenharmony_cistatic bool need_swap; 698c2ecf20Sopenharmony_cistatic FILE *out_file; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 728c2ecf20Sopenharmony_ci# define HOST_ORDER ELFDATA2LSB 738c2ecf20Sopenharmony_ci#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 748c2ecf20Sopenharmony_ci# define HOST_ORDER ELFDATA2MSB 758c2ecf20Sopenharmony_ci#endif 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define BUILD_SWAP(bits) \ 788c2ecf20Sopenharmony_ci static uint##bits##_t swap_uint##bits(uint##bits##_t val) \ 798c2ecf20Sopenharmony_ci { \ 808c2ecf20Sopenharmony_ci return need_swap ? bswap_##bits(val) : val; \ 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciBUILD_SWAP(16) 848c2ecf20Sopenharmony_ciBUILD_SWAP(32) 858c2ecf20Sopenharmony_ciBUILD_SWAP(64) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define __FUNC(name, bits) name##bits 888c2ecf20Sopenharmony_ci#define _FUNC(name, bits) __FUNC(name, bits) 898c2ecf20Sopenharmony_ci#define FUNC(name) _FUNC(name, ELF_BITS) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define __ELF(x, bits) Elf##bits##_##x 928c2ecf20Sopenharmony_ci#define _ELF(x, bits) __ELF(x, bits) 938c2ecf20Sopenharmony_ci#define ELF(x) _ELF(x, ELF_BITS) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * Include genvdso.h twice with ELF_BITS defined differently to get functions 978c2ecf20Sopenharmony_ci * for both ELF32 and ELF64. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define ELF_BITS 64 1018c2ecf20Sopenharmony_ci#include "genvdso.h" 1028c2ecf20Sopenharmony_ci#undef ELF_BITS 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define ELF_BITS 32 1058c2ecf20Sopenharmony_ci#include "genvdso.h" 1068c2ecf20Sopenharmony_ci#undef ELF_BITS 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void *map_vdso(const char *path, size_t *_size) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci int fd; 1118c2ecf20Sopenharmony_ci struct stat stat; 1128c2ecf20Sopenharmony_ci void *addr; 1138c2ecf20Sopenharmony_ci const Elf32_Ehdr *ehdr; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci fd = open(path, O_RDWR); 1168c2ecf20Sopenharmony_ci if (fd < 0) { 1178c2ecf20Sopenharmony_ci fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name, 1188c2ecf20Sopenharmony_ci path, strerror(errno)); 1198c2ecf20Sopenharmony_ci return NULL; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (fstat(fd, &stat) != 0) { 1238c2ecf20Sopenharmony_ci fprintf(stderr, "%s: Failed to stat '%s': %s\n", program_name, 1248c2ecf20Sopenharmony_ci path, strerror(errno)); 1258c2ecf20Sopenharmony_ci close(fd); 1268c2ecf20Sopenharmony_ci return NULL; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci addr = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 1308c2ecf20Sopenharmony_ci 0); 1318c2ecf20Sopenharmony_ci if (addr == MAP_FAILED) { 1328c2ecf20Sopenharmony_ci fprintf(stderr, "%s: Failed to map '%s': %s\n", program_name, 1338c2ecf20Sopenharmony_ci path, strerror(errno)); 1348c2ecf20Sopenharmony_ci close(fd); 1358c2ecf20Sopenharmony_ci return NULL; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* ELF32/64 header formats are the same for the bits we're checking. */ 1398c2ecf20Sopenharmony_ci ehdr = addr; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { 1428c2ecf20Sopenharmony_ci fprintf(stderr, "%s: '%s' is not an ELF file\n", program_name, 1438c2ecf20Sopenharmony_ci path); 1448c2ecf20Sopenharmony_ci close(fd); 1458c2ecf20Sopenharmony_ci return NULL; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci elf_class = ehdr->e_ident[EI_CLASS]; 1498c2ecf20Sopenharmony_ci switch (elf_class) { 1508c2ecf20Sopenharmony_ci case ELFCLASS32: 1518c2ecf20Sopenharmony_ci case ELFCLASS64: 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci default: 1548c2ecf20Sopenharmony_ci fprintf(stderr, "%s: '%s' has invalid ELF class\n", 1558c2ecf20Sopenharmony_ci program_name, path); 1568c2ecf20Sopenharmony_ci close(fd); 1578c2ecf20Sopenharmony_ci return NULL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci switch (ehdr->e_ident[EI_DATA]) { 1618c2ecf20Sopenharmony_ci case ELFDATA2LSB: 1628c2ecf20Sopenharmony_ci case ELFDATA2MSB: 1638c2ecf20Sopenharmony_ci need_swap = ehdr->e_ident[EI_DATA] != HOST_ORDER; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci default: 1668c2ecf20Sopenharmony_ci fprintf(stderr, "%s: '%s' has invalid ELF data order\n", 1678c2ecf20Sopenharmony_ci program_name, path); 1688c2ecf20Sopenharmony_ci close(fd); 1698c2ecf20Sopenharmony_ci return NULL; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (swap_uint16(ehdr->e_machine) != EM_MIPS) { 1738c2ecf20Sopenharmony_ci fprintf(stderr, 1748c2ecf20Sopenharmony_ci "%s: '%s' has invalid ELF machine (expected EM_MIPS)\n", 1758c2ecf20Sopenharmony_ci program_name, path); 1768c2ecf20Sopenharmony_ci close(fd); 1778c2ecf20Sopenharmony_ci return NULL; 1788c2ecf20Sopenharmony_ci } else if (swap_uint16(ehdr->e_type) != ET_DYN) { 1798c2ecf20Sopenharmony_ci fprintf(stderr, 1808c2ecf20Sopenharmony_ci "%s: '%s' has invalid ELF type (expected ET_DYN)\n", 1818c2ecf20Sopenharmony_ci program_name, path); 1828c2ecf20Sopenharmony_ci close(fd); 1838c2ecf20Sopenharmony_ci return NULL; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci *_size = stat.st_size; 1878c2ecf20Sopenharmony_ci close(fd); 1888c2ecf20Sopenharmony_ci return addr; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic bool patch_vdso(const char *path, void *vdso) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci if (elf_class == ELFCLASS64) 1948c2ecf20Sopenharmony_ci return patch_vdso64(path, vdso); 1958c2ecf20Sopenharmony_ci else 1968c2ecf20Sopenharmony_ci return patch_vdso32(path, vdso); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic bool get_symbols(const char *path, void *vdso) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci if (elf_class == ELFCLASS64) 2028c2ecf20Sopenharmony_ci return get_symbols64(path, vdso); 2038c2ecf20Sopenharmony_ci else 2048c2ecf20Sopenharmony_ci return get_symbols32(path, vdso); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ciint main(int argc, char **argv) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci const char *dbg_vdso_path, *vdso_path, *out_path; 2108c2ecf20Sopenharmony_ci void *dbg_vdso, *vdso; 2118c2ecf20Sopenharmony_ci size_t dbg_vdso_size, vdso_size, i; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci program_name = argv[0]; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (argc < 4 || argc > 5) { 2168c2ecf20Sopenharmony_ci fprintf(stderr, 2178c2ecf20Sopenharmony_ci "Usage: %s <debug VDSO> <stripped VDSO> <output file> [<name>]\n", 2188c2ecf20Sopenharmony_ci program_name); 2198c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci dbg_vdso_path = argv[1]; 2238c2ecf20Sopenharmony_ci vdso_path = argv[2]; 2248c2ecf20Sopenharmony_ci out_path = argv[3]; 2258c2ecf20Sopenharmony_ci vdso_name = (argc > 4) ? argv[4] : ""; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci dbg_vdso = map_vdso(dbg_vdso_path, &dbg_vdso_size); 2288c2ecf20Sopenharmony_ci if (!dbg_vdso) 2298c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci vdso = map_vdso(vdso_path, &vdso_size); 2328c2ecf20Sopenharmony_ci if (!vdso) 2338c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Patch both the VDSOs' ABI flags sections. */ 2368c2ecf20Sopenharmony_ci if (!patch_vdso(dbg_vdso_path, dbg_vdso)) 2378c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2388c2ecf20Sopenharmony_ci if (!patch_vdso(vdso_path, vdso)) 2398c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (msync(dbg_vdso, dbg_vdso_size, MS_SYNC) != 0) { 2428c2ecf20Sopenharmony_ci fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name, 2438c2ecf20Sopenharmony_ci dbg_vdso_path, strerror(errno)); 2448c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2458c2ecf20Sopenharmony_ci } else if (msync(vdso, vdso_size, MS_SYNC) != 0) { 2468c2ecf20Sopenharmony_ci fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name, 2478c2ecf20Sopenharmony_ci vdso_path, strerror(errno)); 2488c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci out_file = fopen(out_path, "w"); 2528c2ecf20Sopenharmony_ci if (!out_file) { 2538c2ecf20Sopenharmony_ci fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name, 2548c2ecf20Sopenharmony_ci out_path, strerror(errno)); 2558c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci fprintf(out_file, "/* Automatically generated - do not edit */\n"); 2598c2ecf20Sopenharmony_ci fprintf(out_file, "#include <linux/linkage.h>\n"); 2608c2ecf20Sopenharmony_ci fprintf(out_file, "#include <linux/mm.h>\n"); 2618c2ecf20Sopenharmony_ci fprintf(out_file, "#include <asm/vdso.h>\n"); 2628c2ecf20Sopenharmony_ci fprintf(out_file, "static int vdso_mremap(\n"); 2638c2ecf20Sopenharmony_ci fprintf(out_file, " const struct vm_special_mapping *sm,\n"); 2648c2ecf20Sopenharmony_ci fprintf(out_file, " struct vm_area_struct *new_vma)\n"); 2658c2ecf20Sopenharmony_ci fprintf(out_file, "{\n"); 2668c2ecf20Sopenharmony_ci fprintf(out_file, " unsigned long new_size =\n"); 2678c2ecf20Sopenharmony_ci fprintf(out_file, " new_vma->vm_end - new_vma->vm_start;\n"); 2688c2ecf20Sopenharmony_ci fprintf(out_file, " if (vdso_image.size != new_size)\n"); 2698c2ecf20Sopenharmony_ci fprintf(out_file, " return -EINVAL;\n"); 2708c2ecf20Sopenharmony_ci fprintf(out_file, " current->mm->context.vdso =\n"); 2718c2ecf20Sopenharmony_ci fprintf(out_file, " (void *)(new_vma->vm_start);\n"); 2728c2ecf20Sopenharmony_ci fprintf(out_file, " return 0;\n"); 2738c2ecf20Sopenharmony_ci fprintf(out_file, "}\n"); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Write out the stripped VDSO data. */ 2768c2ecf20Sopenharmony_ci fprintf(out_file, 2778c2ecf20Sopenharmony_ci "static unsigned char vdso_data[PAGE_ALIGN(%zu)] __page_aligned_data = {\n\t", 2788c2ecf20Sopenharmony_ci vdso_size); 2798c2ecf20Sopenharmony_ci for (i = 0; i < vdso_size; i++) { 2808c2ecf20Sopenharmony_ci if (!(i % 10)) 2818c2ecf20Sopenharmony_ci fprintf(out_file, "\n\t"); 2828c2ecf20Sopenharmony_ci fprintf(out_file, "0x%02x, ", ((unsigned char *)vdso)[i]); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci fprintf(out_file, "\n};\n"); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Preallocate a page array. */ 2878c2ecf20Sopenharmony_ci fprintf(out_file, 2888c2ecf20Sopenharmony_ci "static struct page *vdso_pages[PAGE_ALIGN(%zu) / PAGE_SIZE];\n", 2898c2ecf20Sopenharmony_ci vdso_size); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci fprintf(out_file, "struct mips_vdso_image vdso_image%s%s = {\n", 2928c2ecf20Sopenharmony_ci (vdso_name[0]) ? "_" : "", vdso_name); 2938c2ecf20Sopenharmony_ci fprintf(out_file, "\t.data = vdso_data,\n"); 2948c2ecf20Sopenharmony_ci fprintf(out_file, "\t.size = PAGE_ALIGN(%zu),\n", vdso_size); 2958c2ecf20Sopenharmony_ci fprintf(out_file, "\t.mapping = {\n"); 2968c2ecf20Sopenharmony_ci fprintf(out_file, "\t\t.name = \"[vdso]\",\n"); 2978c2ecf20Sopenharmony_ci fprintf(out_file, "\t\t.pages = vdso_pages,\n"); 2988c2ecf20Sopenharmony_ci fprintf(out_file, "\t\t.mremap = vdso_mremap,\n"); 2998c2ecf20Sopenharmony_ci fprintf(out_file, "\t},\n"); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Calculate and write symbol offsets to <output file> */ 3028c2ecf20Sopenharmony_ci if (!get_symbols(dbg_vdso_path, dbg_vdso)) { 3038c2ecf20Sopenharmony_ci unlink(out_path); 3048c2ecf20Sopenharmony_ci fclose(out_file); 3058c2ecf20Sopenharmony_ci return EXIT_FAILURE; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci fprintf(out_file, "};\n"); 3098c2ecf20Sopenharmony_ci fclose(out_file); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return EXIT_SUCCESS; 3128c2ecf20Sopenharmony_ci} 313