162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Imagination Technologies 462306a36Sopenharmony_ci * Author: Alex Smith <alex.smith@imgtec.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci * This tool is used to generate the real VDSO images from the raw image. It 962306a36Sopenharmony_ci * first patches up the MIPS ABI flags and GNU attributes sections defined in 1062306a36Sopenharmony_ci * elf.S to have the correct name and type. It then generates a C source file 1162306a36Sopenharmony_ci * to be compiled into the kernel containing the VDSO image data and a 1262306a36Sopenharmony_ci * mips_vdso_image struct for it, including symbol offsets extracted from the 1362306a36Sopenharmony_ci * image. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * We need to be passed both a stripped and unstripped VDSO image. The stripped 1662306a36Sopenharmony_ci * image is compiled into the kernel, but we must also patch up the unstripped 1762306a36Sopenharmony_ci * image's ABI flags sections so that it can be installed and used for 1862306a36Sopenharmony_ci * debugging. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <sys/mman.h> 2262306a36Sopenharmony_ci#include <sys/stat.h> 2362306a36Sopenharmony_ci#include <sys/types.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <byteswap.h> 2662306a36Sopenharmony_ci#include <elf.h> 2762306a36Sopenharmony_ci#include <errno.h> 2862306a36Sopenharmony_ci#include <fcntl.h> 2962306a36Sopenharmony_ci#include <inttypes.h> 3062306a36Sopenharmony_ci#include <stdarg.h> 3162306a36Sopenharmony_ci#include <stdbool.h> 3262306a36Sopenharmony_ci#include <stdio.h> 3362306a36Sopenharmony_ci#include <stdlib.h> 3462306a36Sopenharmony_ci#include <string.h> 3562306a36Sopenharmony_ci#include <unistd.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Define these in case the system elf.h is not new enough to have them. */ 3862306a36Sopenharmony_ci#ifndef SHT_GNU_ATTRIBUTES 3962306a36Sopenharmony_ci# define SHT_GNU_ATTRIBUTES 0x6ffffff5 4062306a36Sopenharmony_ci#endif 4162306a36Sopenharmony_ci#ifndef SHT_MIPS_ABIFLAGS 4262306a36Sopenharmony_ci# define SHT_MIPS_ABIFLAGS 0x7000002a 4362306a36Sopenharmony_ci#endif 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cienum { 4662306a36Sopenharmony_ci ABI_O32 = (1 << 0), 4762306a36Sopenharmony_ci ABI_N32 = (1 << 1), 4862306a36Sopenharmony_ci ABI_N64 = (1 << 2), 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci ABI_ALL = ABI_O32 | ABI_N32 | ABI_N64, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Symbols the kernel requires offsets for. */ 5462306a36Sopenharmony_cistatic struct { 5562306a36Sopenharmony_ci const char *name; 5662306a36Sopenharmony_ci const char *offset_name; 5762306a36Sopenharmony_ci unsigned int abis; 5862306a36Sopenharmony_ci} vdso_symbols[] = { 5962306a36Sopenharmony_ci { "__vdso_sigreturn", "off_sigreturn", ABI_O32 }, 6062306a36Sopenharmony_ci { "__vdso_rt_sigreturn", "off_rt_sigreturn", ABI_ALL }, 6162306a36Sopenharmony_ci {} 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic const char *program_name; 6562306a36Sopenharmony_cistatic const char *vdso_name; 6662306a36Sopenharmony_cistatic unsigned char elf_class; 6762306a36Sopenharmony_cistatic unsigned int elf_abi; 6862306a36Sopenharmony_cistatic bool need_swap; 6962306a36Sopenharmony_cistatic FILE *out_file; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 7262306a36Sopenharmony_ci# define HOST_ORDER ELFDATA2LSB 7362306a36Sopenharmony_ci#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 7462306a36Sopenharmony_ci# define HOST_ORDER ELFDATA2MSB 7562306a36Sopenharmony_ci#endif 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define BUILD_SWAP(bits) \ 7862306a36Sopenharmony_ci static uint##bits##_t swap_uint##bits(uint##bits##_t val) \ 7962306a36Sopenharmony_ci { \ 8062306a36Sopenharmony_ci return need_swap ? bswap_##bits(val) : val; \ 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ciBUILD_SWAP(16) 8462306a36Sopenharmony_ciBUILD_SWAP(32) 8562306a36Sopenharmony_ciBUILD_SWAP(64) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define __FUNC(name, bits) name##bits 8862306a36Sopenharmony_ci#define _FUNC(name, bits) __FUNC(name, bits) 8962306a36Sopenharmony_ci#define FUNC(name) _FUNC(name, ELF_BITS) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define __ELF(x, bits) Elf##bits##_##x 9262306a36Sopenharmony_ci#define _ELF(x, bits) __ELF(x, bits) 9362306a36Sopenharmony_ci#define ELF(x) _ELF(x, ELF_BITS) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Include genvdso.h twice with ELF_BITS defined differently to get functions 9762306a36Sopenharmony_ci * for both ELF32 and ELF64. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define ELF_BITS 64 10162306a36Sopenharmony_ci#include "genvdso.h" 10262306a36Sopenharmony_ci#undef ELF_BITS 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define ELF_BITS 32 10562306a36Sopenharmony_ci#include "genvdso.h" 10662306a36Sopenharmony_ci#undef ELF_BITS 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void *map_vdso(const char *path, size_t *_size) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int fd; 11162306a36Sopenharmony_ci struct stat stat; 11262306a36Sopenharmony_ci void *addr; 11362306a36Sopenharmony_ci const Elf32_Ehdr *ehdr; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci fd = open(path, O_RDWR); 11662306a36Sopenharmony_ci if (fd < 0) { 11762306a36Sopenharmony_ci fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name, 11862306a36Sopenharmony_ci path, strerror(errno)); 11962306a36Sopenharmony_ci return NULL; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (fstat(fd, &stat) != 0) { 12362306a36Sopenharmony_ci fprintf(stderr, "%s: Failed to stat '%s': %s\n", program_name, 12462306a36Sopenharmony_ci path, strerror(errno)); 12562306a36Sopenharmony_ci close(fd); 12662306a36Sopenharmony_ci return NULL; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci addr = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 13062306a36Sopenharmony_ci 0); 13162306a36Sopenharmony_ci if (addr == MAP_FAILED) { 13262306a36Sopenharmony_ci fprintf(stderr, "%s: Failed to map '%s': %s\n", program_name, 13362306a36Sopenharmony_ci path, strerror(errno)); 13462306a36Sopenharmony_ci close(fd); 13562306a36Sopenharmony_ci return NULL; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* ELF32/64 header formats are the same for the bits we're checking. */ 13962306a36Sopenharmony_ci ehdr = addr; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { 14262306a36Sopenharmony_ci fprintf(stderr, "%s: '%s' is not an ELF file\n", program_name, 14362306a36Sopenharmony_ci path); 14462306a36Sopenharmony_ci close(fd); 14562306a36Sopenharmony_ci return NULL; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci elf_class = ehdr->e_ident[EI_CLASS]; 14962306a36Sopenharmony_ci switch (elf_class) { 15062306a36Sopenharmony_ci case ELFCLASS32: 15162306a36Sopenharmony_ci case ELFCLASS64: 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci default: 15462306a36Sopenharmony_ci fprintf(stderr, "%s: '%s' has invalid ELF class\n", 15562306a36Sopenharmony_ci program_name, path); 15662306a36Sopenharmony_ci close(fd); 15762306a36Sopenharmony_ci return NULL; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci switch (ehdr->e_ident[EI_DATA]) { 16162306a36Sopenharmony_ci case ELFDATA2LSB: 16262306a36Sopenharmony_ci case ELFDATA2MSB: 16362306a36Sopenharmony_ci need_swap = ehdr->e_ident[EI_DATA] != HOST_ORDER; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci default: 16662306a36Sopenharmony_ci fprintf(stderr, "%s: '%s' has invalid ELF data order\n", 16762306a36Sopenharmony_ci program_name, path); 16862306a36Sopenharmony_ci close(fd); 16962306a36Sopenharmony_ci return NULL; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (swap_uint16(ehdr->e_machine) != EM_MIPS) { 17362306a36Sopenharmony_ci fprintf(stderr, 17462306a36Sopenharmony_ci "%s: '%s' has invalid ELF machine (expected EM_MIPS)\n", 17562306a36Sopenharmony_ci program_name, path); 17662306a36Sopenharmony_ci close(fd); 17762306a36Sopenharmony_ci return NULL; 17862306a36Sopenharmony_ci } else if (swap_uint16(ehdr->e_type) != ET_DYN) { 17962306a36Sopenharmony_ci fprintf(stderr, 18062306a36Sopenharmony_ci "%s: '%s' has invalid ELF type (expected ET_DYN)\n", 18162306a36Sopenharmony_ci program_name, path); 18262306a36Sopenharmony_ci close(fd); 18362306a36Sopenharmony_ci return NULL; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci *_size = stat.st_size; 18762306a36Sopenharmony_ci close(fd); 18862306a36Sopenharmony_ci return addr; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic bool patch_vdso(const char *path, void *vdso) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci if (elf_class == ELFCLASS64) 19462306a36Sopenharmony_ci return patch_vdso64(path, vdso); 19562306a36Sopenharmony_ci else 19662306a36Sopenharmony_ci return patch_vdso32(path, vdso); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic bool get_symbols(const char *path, void *vdso) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci if (elf_class == ELFCLASS64) 20262306a36Sopenharmony_ci return get_symbols64(path, vdso); 20362306a36Sopenharmony_ci else 20462306a36Sopenharmony_ci return get_symbols32(path, vdso); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciint main(int argc, char **argv) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci const char *dbg_vdso_path, *vdso_path, *out_path; 21062306a36Sopenharmony_ci void *dbg_vdso, *vdso; 21162306a36Sopenharmony_ci size_t dbg_vdso_size, vdso_size, i; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci program_name = argv[0]; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (argc < 4 || argc > 5) { 21662306a36Sopenharmony_ci fprintf(stderr, 21762306a36Sopenharmony_ci "Usage: %s <debug VDSO> <stripped VDSO> <output file> [<name>]\n", 21862306a36Sopenharmony_ci program_name); 21962306a36Sopenharmony_ci return EXIT_FAILURE; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci dbg_vdso_path = argv[1]; 22362306a36Sopenharmony_ci vdso_path = argv[2]; 22462306a36Sopenharmony_ci out_path = argv[3]; 22562306a36Sopenharmony_ci vdso_name = (argc > 4) ? argv[4] : ""; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci dbg_vdso = map_vdso(dbg_vdso_path, &dbg_vdso_size); 22862306a36Sopenharmony_ci if (!dbg_vdso) 22962306a36Sopenharmony_ci return EXIT_FAILURE; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci vdso = map_vdso(vdso_path, &vdso_size); 23262306a36Sopenharmony_ci if (!vdso) 23362306a36Sopenharmony_ci return EXIT_FAILURE; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Patch both the VDSOs' ABI flags sections. */ 23662306a36Sopenharmony_ci if (!patch_vdso(dbg_vdso_path, dbg_vdso)) 23762306a36Sopenharmony_ci return EXIT_FAILURE; 23862306a36Sopenharmony_ci if (!patch_vdso(vdso_path, vdso)) 23962306a36Sopenharmony_ci return EXIT_FAILURE; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (msync(dbg_vdso, dbg_vdso_size, MS_SYNC) != 0) { 24262306a36Sopenharmony_ci fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name, 24362306a36Sopenharmony_ci dbg_vdso_path, strerror(errno)); 24462306a36Sopenharmony_ci return EXIT_FAILURE; 24562306a36Sopenharmony_ci } else if (msync(vdso, vdso_size, MS_SYNC) != 0) { 24662306a36Sopenharmony_ci fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name, 24762306a36Sopenharmony_ci vdso_path, strerror(errno)); 24862306a36Sopenharmony_ci return EXIT_FAILURE; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci out_file = fopen(out_path, "w"); 25262306a36Sopenharmony_ci if (!out_file) { 25362306a36Sopenharmony_ci fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name, 25462306a36Sopenharmony_ci out_path, strerror(errno)); 25562306a36Sopenharmony_ci return EXIT_FAILURE; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci fprintf(out_file, "/* Automatically generated - do not edit */\n"); 25962306a36Sopenharmony_ci fprintf(out_file, "#include <linux/linkage.h>\n"); 26062306a36Sopenharmony_ci fprintf(out_file, "#include <linux/mm.h>\n"); 26162306a36Sopenharmony_ci fprintf(out_file, "#include <asm/vdso.h>\n"); 26262306a36Sopenharmony_ci fprintf(out_file, "static int vdso_mremap(\n"); 26362306a36Sopenharmony_ci fprintf(out_file, " const struct vm_special_mapping *sm,\n"); 26462306a36Sopenharmony_ci fprintf(out_file, " struct vm_area_struct *new_vma)\n"); 26562306a36Sopenharmony_ci fprintf(out_file, "{\n"); 26662306a36Sopenharmony_ci fprintf(out_file, " current->mm->context.vdso =\n"); 26762306a36Sopenharmony_ci fprintf(out_file, " (void *)(new_vma->vm_start);\n"); 26862306a36Sopenharmony_ci fprintf(out_file, " return 0;\n"); 26962306a36Sopenharmony_ci fprintf(out_file, "}\n"); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Write out the stripped VDSO data. */ 27262306a36Sopenharmony_ci fprintf(out_file, 27362306a36Sopenharmony_ci "static unsigned char vdso_data[PAGE_ALIGN(%zu)] __page_aligned_data = {\n\t", 27462306a36Sopenharmony_ci vdso_size); 27562306a36Sopenharmony_ci for (i = 0; i < vdso_size; i++) { 27662306a36Sopenharmony_ci if (!(i % 10)) 27762306a36Sopenharmony_ci fprintf(out_file, "\n\t"); 27862306a36Sopenharmony_ci fprintf(out_file, "0x%02x, ", ((unsigned char *)vdso)[i]); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci fprintf(out_file, "\n};\n"); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Preallocate a page array. */ 28362306a36Sopenharmony_ci fprintf(out_file, 28462306a36Sopenharmony_ci "static struct page *vdso_pages[PAGE_ALIGN(%zu) / PAGE_SIZE];\n", 28562306a36Sopenharmony_ci vdso_size); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci fprintf(out_file, "struct mips_vdso_image vdso_image%s%s = {\n", 28862306a36Sopenharmony_ci (vdso_name[0]) ? "_" : "", vdso_name); 28962306a36Sopenharmony_ci fprintf(out_file, "\t.data = vdso_data,\n"); 29062306a36Sopenharmony_ci fprintf(out_file, "\t.size = PAGE_ALIGN(%zu),\n", vdso_size); 29162306a36Sopenharmony_ci fprintf(out_file, "\t.mapping = {\n"); 29262306a36Sopenharmony_ci fprintf(out_file, "\t\t.name = \"[vdso]\",\n"); 29362306a36Sopenharmony_ci fprintf(out_file, "\t\t.pages = vdso_pages,\n"); 29462306a36Sopenharmony_ci fprintf(out_file, "\t\t.mremap = vdso_mremap,\n"); 29562306a36Sopenharmony_ci fprintf(out_file, "\t},\n"); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Calculate and write symbol offsets to <output file> */ 29862306a36Sopenharmony_ci if (!get_symbols(dbg_vdso_path, dbg_vdso)) { 29962306a36Sopenharmony_ci unlink(out_path); 30062306a36Sopenharmony_ci fclose(out_file); 30162306a36Sopenharmony_ci return EXIT_FAILURE; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci fprintf(out_file, "};\n"); 30562306a36Sopenharmony_ci fclose(out_file); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return EXIT_SUCCESS; 30862306a36Sopenharmony_ci} 309