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