162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This file is included twice from vdso2c.c.  It generates code for 32-bit
462306a36Sopenharmony_ci * and 64-bit vDSOs.  We need both for 64-bit builds, since 32-bit vDSOs
562306a36Sopenharmony_ci * are built for 32-bit userspace.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_cistatic void BITSFUNC(copy)(FILE *outfile, const unsigned char *data, size_t len)
962306a36Sopenharmony_ci{
1062306a36Sopenharmony_ci	size_t i;
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
1362306a36Sopenharmony_ci		if (i % 10 == 0)
1462306a36Sopenharmony_ci			fprintf(outfile, "\n\t");
1562306a36Sopenharmony_ci		fprintf(outfile, "0x%02X, ", (int)(data)[i]);
1662306a36Sopenharmony_ci	}
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * Extract a section from the input data into a standalone blob.  Used to
2262306a36Sopenharmony_ci * capture kernel-only data that needs to persist indefinitely, e.g. the
2362306a36Sopenharmony_ci * exception fixup tables, but only in the kernel, i.e. the section can
2462306a36Sopenharmony_ci * be stripped from the final vDSO image.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cistatic void BITSFUNC(extract)(const unsigned char *data, size_t data_len,
2762306a36Sopenharmony_ci			      FILE *outfile, ELF(Shdr) *sec, const char *name)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	unsigned long offset;
3062306a36Sopenharmony_ci	size_t len;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	offset = (unsigned long)GET_LE(&sec->sh_offset);
3362306a36Sopenharmony_ci	len = (size_t)GET_LE(&sec->sh_size);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (offset + len > data_len)
3662306a36Sopenharmony_ci		fail("section to extract overruns input data");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	fprintf(outfile, "static const unsigned char %s[%zu] = {", name, len);
3962306a36Sopenharmony_ci	BITSFUNC(copy)(outfile, data + offset, len);
4062306a36Sopenharmony_ci	fprintf(outfile, "\n};\n\n");
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void BITSFUNC(go)(void *raw_addr, size_t raw_len,
4462306a36Sopenharmony_ci			 void *stripped_addr, size_t stripped_len,
4562306a36Sopenharmony_ci			 FILE *outfile, const char *image_name)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	int found_load = 0;
4862306a36Sopenharmony_ci	unsigned long load_size = -1;  /* Work around bogus warning */
4962306a36Sopenharmony_ci	unsigned long mapping_size;
5062306a36Sopenharmony_ci	ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
5162306a36Sopenharmony_ci	unsigned long i, syms_nr;
5262306a36Sopenharmony_ci	ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
5362306a36Sopenharmony_ci		*alt_sec = NULL, *extable_sec = NULL;
5462306a36Sopenharmony_ci	ELF(Dyn) *dyn = 0, *dyn_end = 0;
5562306a36Sopenharmony_ci	const char *secstrings;
5662306a36Sopenharmony_ci	INT_BITS syms[NSYMS] = {};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_LE(&hdr->e_phoff));
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (GET_LE(&hdr->e_type) != ET_DYN)
6162306a36Sopenharmony_ci		fail("input is not a shared object\n");
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* Walk the segment table. */
6462306a36Sopenharmony_ci	for (i = 0; i < GET_LE(&hdr->e_phnum); i++) {
6562306a36Sopenharmony_ci		if (GET_LE(&pt[i].p_type) == PT_LOAD) {
6662306a36Sopenharmony_ci			if (found_load)
6762306a36Sopenharmony_ci				fail("multiple PT_LOAD segs\n");
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci			if (GET_LE(&pt[i].p_offset) != 0 ||
7062306a36Sopenharmony_ci			    GET_LE(&pt[i].p_vaddr) != 0)
7162306a36Sopenharmony_ci				fail("PT_LOAD in wrong place\n");
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci			if (GET_LE(&pt[i].p_memsz) != GET_LE(&pt[i].p_filesz))
7462306a36Sopenharmony_ci				fail("cannot handle memsz != filesz\n");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci			load_size = GET_LE(&pt[i].p_memsz);
7762306a36Sopenharmony_ci			found_load = 1;
7862306a36Sopenharmony_ci		} else if (GET_LE(&pt[i].p_type) == PT_DYNAMIC) {
7962306a36Sopenharmony_ci			dyn = raw_addr + GET_LE(&pt[i].p_offset);
8062306a36Sopenharmony_ci			dyn_end = raw_addr + GET_LE(&pt[i].p_offset) +
8162306a36Sopenharmony_ci				GET_LE(&pt[i].p_memsz);
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	if (!found_load)
8562306a36Sopenharmony_ci		fail("no PT_LOAD seg\n");
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (stripped_len < load_size)
8862306a36Sopenharmony_ci		fail("stripped input is too short\n");
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (!dyn)
9162306a36Sopenharmony_ci		fail("input has no PT_DYNAMIC section -- your toolchain is buggy\n");
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Walk the dynamic table */
9462306a36Sopenharmony_ci	for (i = 0; dyn + i < dyn_end &&
9562306a36Sopenharmony_ci		     GET_LE(&dyn[i].d_tag) != DT_NULL; i++) {
9662306a36Sopenharmony_ci		typeof(dyn[i].d_tag) tag = GET_LE(&dyn[i].d_tag);
9762306a36Sopenharmony_ci		if (tag == DT_REL || tag == DT_RELSZ || tag == DT_RELA ||
9862306a36Sopenharmony_ci		    tag == DT_RELENT || tag == DT_TEXTREL)
9962306a36Sopenharmony_ci			fail("vdso image contains dynamic relocations\n");
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Walk the section table */
10362306a36Sopenharmony_ci	secstrings_hdr = raw_addr + GET_LE(&hdr->e_shoff) +
10462306a36Sopenharmony_ci		GET_LE(&hdr->e_shentsize)*GET_LE(&hdr->e_shstrndx);
10562306a36Sopenharmony_ci	secstrings = raw_addr + GET_LE(&secstrings_hdr->sh_offset);
10662306a36Sopenharmony_ci	for (i = 0; i < GET_LE(&hdr->e_shnum); i++) {
10762306a36Sopenharmony_ci		ELF(Shdr) *sh = raw_addr + GET_LE(&hdr->e_shoff) +
10862306a36Sopenharmony_ci			GET_LE(&hdr->e_shentsize) * i;
10962306a36Sopenharmony_ci		if (GET_LE(&sh->sh_type) == SHT_SYMTAB)
11062306a36Sopenharmony_ci			symtab_hdr = sh;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		if (!strcmp(secstrings + GET_LE(&sh->sh_name),
11362306a36Sopenharmony_ci			    ".altinstructions"))
11462306a36Sopenharmony_ci			alt_sec = sh;
11562306a36Sopenharmony_ci		if (!strcmp(secstrings + GET_LE(&sh->sh_name), "__ex_table"))
11662306a36Sopenharmony_ci			extable_sec = sh;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (!symtab_hdr)
12062306a36Sopenharmony_ci		fail("no symbol table\n");
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	strtab_hdr = raw_addr + GET_LE(&hdr->e_shoff) +
12362306a36Sopenharmony_ci		GET_LE(&hdr->e_shentsize) * GET_LE(&symtab_hdr->sh_link);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	syms_nr = GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize);
12662306a36Sopenharmony_ci	/* Walk the symbol table */
12762306a36Sopenharmony_ci	for (i = 0; i < syms_nr; i++) {
12862306a36Sopenharmony_ci		unsigned int k;
12962306a36Sopenharmony_ci		ELF(Sym) *sym = raw_addr + GET_LE(&symtab_hdr->sh_offset) +
13062306a36Sopenharmony_ci			GET_LE(&symtab_hdr->sh_entsize) * i;
13162306a36Sopenharmony_ci		const char *sym_name = raw_addr +
13262306a36Sopenharmony_ci				       GET_LE(&strtab_hdr->sh_offset) +
13362306a36Sopenharmony_ci				       GET_LE(&sym->st_name);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		for (k = 0; k < NSYMS; k++) {
13662306a36Sopenharmony_ci			if (!strcmp(sym_name, required_syms[k].name)) {
13762306a36Sopenharmony_ci				if (syms[k]) {
13862306a36Sopenharmony_ci					fail("duplicate symbol %s\n",
13962306a36Sopenharmony_ci					     required_syms[k].name);
14062306a36Sopenharmony_ci				}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci				/*
14362306a36Sopenharmony_ci				 * Careful: we use negative addresses, but
14462306a36Sopenharmony_ci				 * st_value is unsigned, so we rely
14562306a36Sopenharmony_ci				 * on syms[k] being a signed type of the
14662306a36Sopenharmony_ci				 * correct width.
14762306a36Sopenharmony_ci				 */
14862306a36Sopenharmony_ci				syms[k] = GET_LE(&sym->st_value);
14962306a36Sopenharmony_ci			}
15062306a36Sopenharmony_ci		}
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* Validate mapping addresses. */
15462306a36Sopenharmony_ci	for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) {
15562306a36Sopenharmony_ci		INT_BITS symval = syms[special_pages[i]];
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		if (!symval)
15862306a36Sopenharmony_ci			continue;  /* The mapping isn't used; ignore it. */
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		if (symval % 4096)
16162306a36Sopenharmony_ci			fail("%s must be a multiple of 4096\n",
16262306a36Sopenharmony_ci			     required_syms[i].name);
16362306a36Sopenharmony_ci		if (symval + 4096 < syms[sym_vvar_start])
16462306a36Sopenharmony_ci			fail("%s underruns vvar_start\n",
16562306a36Sopenharmony_ci			     required_syms[i].name);
16662306a36Sopenharmony_ci		if (symval + 4096 > 0)
16762306a36Sopenharmony_ci			fail("%s is on the wrong side of the vdso text\n",
16862306a36Sopenharmony_ci			     required_syms[i].name);
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci	if (syms[sym_vvar_start] % 4096)
17162306a36Sopenharmony_ci		fail("vvar_begin must be a multiple of 4096\n");
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (!image_name) {
17462306a36Sopenharmony_ci		fwrite(stripped_addr, stripped_len, 1, outfile);
17562306a36Sopenharmony_ci		return;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	mapping_size = (stripped_len + 4095) / 4096 * 4096;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	fprintf(outfile, "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n\n");
18162306a36Sopenharmony_ci	fprintf(outfile, "#include <linux/linkage.h>\n");
18262306a36Sopenharmony_ci	fprintf(outfile, "#include <linux/init.h>\n");
18362306a36Sopenharmony_ci	fprintf(outfile, "#include <asm/page_types.h>\n");
18462306a36Sopenharmony_ci	fprintf(outfile, "#include <asm/vdso.h>\n");
18562306a36Sopenharmony_ci	fprintf(outfile, "\n");
18662306a36Sopenharmony_ci	fprintf(outfile,
18762306a36Sopenharmony_ci		"static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {",
18862306a36Sopenharmony_ci		mapping_size);
18962306a36Sopenharmony_ci	for (i = 0; i < stripped_len; i++) {
19062306a36Sopenharmony_ci		if (i % 10 == 0)
19162306a36Sopenharmony_ci			fprintf(outfile, "\n\t");
19262306a36Sopenharmony_ci		fprintf(outfile, "0x%02X, ",
19362306a36Sopenharmony_ci			(int)((unsigned char *)stripped_addr)[i]);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci	fprintf(outfile, "\n};\n\n");
19662306a36Sopenharmony_ci	if (extable_sec)
19762306a36Sopenharmony_ci		BITSFUNC(extract)(raw_addr, raw_len, outfile,
19862306a36Sopenharmony_ci				  extable_sec, "extable");
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	fprintf(outfile, "const struct vdso_image %s = {\n", image_name);
20162306a36Sopenharmony_ci	fprintf(outfile, "\t.data = raw_data,\n");
20262306a36Sopenharmony_ci	fprintf(outfile, "\t.size = %lu,\n", mapping_size);
20362306a36Sopenharmony_ci	if (alt_sec) {
20462306a36Sopenharmony_ci		fprintf(outfile, "\t.alt = %lu,\n",
20562306a36Sopenharmony_ci			(unsigned long)GET_LE(&alt_sec->sh_offset));
20662306a36Sopenharmony_ci		fprintf(outfile, "\t.alt_len = %lu,\n",
20762306a36Sopenharmony_ci			(unsigned long)GET_LE(&alt_sec->sh_size));
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	if (extable_sec) {
21062306a36Sopenharmony_ci		fprintf(outfile, "\t.extable_base = %lu,\n",
21162306a36Sopenharmony_ci			(unsigned long)GET_LE(&extable_sec->sh_offset));
21262306a36Sopenharmony_ci		fprintf(outfile, "\t.extable_len = %lu,\n",
21362306a36Sopenharmony_ci			(unsigned long)GET_LE(&extable_sec->sh_size));
21462306a36Sopenharmony_ci		fprintf(outfile, "\t.extable = extable,\n");
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	for (i = 0; i < NSYMS; i++) {
21862306a36Sopenharmony_ci		if (required_syms[i].export && syms[i])
21962306a36Sopenharmony_ci			fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
22062306a36Sopenharmony_ci				required_syms[i].name, (int64_t)syms[i]);
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	fprintf(outfile, "};\n\n");
22362306a36Sopenharmony_ci	fprintf(outfile, "static __init int init_%s(void) {\n", image_name);
22462306a36Sopenharmony_ci	fprintf(outfile, "\treturn init_vdso_image(&%s);\n", image_name);
22562306a36Sopenharmony_ci	fprintf(outfile, "};\n");
22662306a36Sopenharmony_ci	fprintf(outfile, "subsys_initcall(init_%s);\n", image_name);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci}
229