18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file is included twice from vdso2c.c. It generates code for 32-bit 48c2ecf20Sopenharmony_ci * and 64-bit vDSOs. We need both for 64-bit builds, since 32-bit vDSOs 58c2ecf20Sopenharmony_ci * are built for 32-bit userspace. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_cistatic void BITSFUNC(go)(void *raw_addr, size_t raw_len, 98c2ecf20Sopenharmony_ci void *stripped_addr, size_t stripped_len, 108c2ecf20Sopenharmony_ci FILE *outfile, const char *image_name) 118c2ecf20Sopenharmony_ci{ 128c2ecf20Sopenharmony_ci int found_load = 0; 138c2ecf20Sopenharmony_ci unsigned long load_size = -1; /* Work around bogus warning */ 148c2ecf20Sopenharmony_ci unsigned long mapping_size; 158c2ecf20Sopenharmony_ci ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr; 168c2ecf20Sopenharmony_ci unsigned long i, syms_nr; 178c2ecf20Sopenharmony_ci ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr, 188c2ecf20Sopenharmony_ci *alt_sec = NULL; 198c2ecf20Sopenharmony_ci ELF(Dyn) *dyn = 0, *dyn_end = 0; 208c2ecf20Sopenharmony_ci const char *secstrings; 218c2ecf20Sopenharmony_ci INT_BITS syms[NSYMS] = {}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_LE(&hdr->e_phoff)); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (GET_LE(&hdr->e_type) != ET_DYN) 268c2ecf20Sopenharmony_ci fail("input is not a shared object\n"); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* Walk the segment table. */ 298c2ecf20Sopenharmony_ci for (i = 0; i < GET_LE(&hdr->e_phnum); i++) { 308c2ecf20Sopenharmony_ci if (GET_LE(&pt[i].p_type) == PT_LOAD) { 318c2ecf20Sopenharmony_ci if (found_load) 328c2ecf20Sopenharmony_ci fail("multiple PT_LOAD segs\n"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (GET_LE(&pt[i].p_offset) != 0 || 358c2ecf20Sopenharmony_ci GET_LE(&pt[i].p_vaddr) != 0) 368c2ecf20Sopenharmony_ci fail("PT_LOAD in wrong place\n"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (GET_LE(&pt[i].p_memsz) != GET_LE(&pt[i].p_filesz)) 398c2ecf20Sopenharmony_ci fail("cannot handle memsz != filesz\n"); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci load_size = GET_LE(&pt[i].p_memsz); 428c2ecf20Sopenharmony_ci found_load = 1; 438c2ecf20Sopenharmony_ci } else if (GET_LE(&pt[i].p_type) == PT_DYNAMIC) { 448c2ecf20Sopenharmony_ci dyn = raw_addr + GET_LE(&pt[i].p_offset); 458c2ecf20Sopenharmony_ci dyn_end = raw_addr + GET_LE(&pt[i].p_offset) + 468c2ecf20Sopenharmony_ci GET_LE(&pt[i].p_memsz); 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci if (!found_load) 508c2ecf20Sopenharmony_ci fail("no PT_LOAD seg\n"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (stripped_len < load_size) 538c2ecf20Sopenharmony_ci fail("stripped input is too short\n"); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (!dyn) 568c2ecf20Sopenharmony_ci fail("input has no PT_DYNAMIC section -- your toolchain is buggy\n"); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Walk the dynamic table */ 598c2ecf20Sopenharmony_ci for (i = 0; dyn + i < dyn_end && 608c2ecf20Sopenharmony_ci GET_LE(&dyn[i].d_tag) != DT_NULL; i++) { 618c2ecf20Sopenharmony_ci typeof(dyn[i].d_tag) tag = GET_LE(&dyn[i].d_tag); 628c2ecf20Sopenharmony_ci if (tag == DT_REL || tag == DT_RELSZ || tag == DT_RELA || 638c2ecf20Sopenharmony_ci tag == DT_RELENT || tag == DT_TEXTREL) 648c2ecf20Sopenharmony_ci fail("vdso image contains dynamic relocations\n"); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* Walk the section table */ 688c2ecf20Sopenharmony_ci secstrings_hdr = raw_addr + GET_LE(&hdr->e_shoff) + 698c2ecf20Sopenharmony_ci GET_LE(&hdr->e_shentsize)*GET_LE(&hdr->e_shstrndx); 708c2ecf20Sopenharmony_ci secstrings = raw_addr + GET_LE(&secstrings_hdr->sh_offset); 718c2ecf20Sopenharmony_ci for (i = 0; i < GET_LE(&hdr->e_shnum); i++) { 728c2ecf20Sopenharmony_ci ELF(Shdr) *sh = raw_addr + GET_LE(&hdr->e_shoff) + 738c2ecf20Sopenharmony_ci GET_LE(&hdr->e_shentsize) * i; 748c2ecf20Sopenharmony_ci if (GET_LE(&sh->sh_type) == SHT_SYMTAB) 758c2ecf20Sopenharmony_ci symtab_hdr = sh; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (!strcmp(secstrings + GET_LE(&sh->sh_name), 788c2ecf20Sopenharmony_ci ".altinstructions")) 798c2ecf20Sopenharmony_ci alt_sec = sh; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (!symtab_hdr) 838c2ecf20Sopenharmony_ci fail("no symbol table\n"); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci strtab_hdr = raw_addr + GET_LE(&hdr->e_shoff) + 868c2ecf20Sopenharmony_ci GET_LE(&hdr->e_shentsize) * GET_LE(&symtab_hdr->sh_link); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci syms_nr = GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize); 898c2ecf20Sopenharmony_ci /* Walk the symbol table */ 908c2ecf20Sopenharmony_ci for (i = 0; i < syms_nr; i++) { 918c2ecf20Sopenharmony_ci unsigned int k; 928c2ecf20Sopenharmony_ci ELF(Sym) *sym = raw_addr + GET_LE(&symtab_hdr->sh_offset) + 938c2ecf20Sopenharmony_ci GET_LE(&symtab_hdr->sh_entsize) * i; 948c2ecf20Sopenharmony_ci const char *sym_name = raw_addr + 958c2ecf20Sopenharmony_ci GET_LE(&strtab_hdr->sh_offset) + 968c2ecf20Sopenharmony_ci GET_LE(&sym->st_name); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci for (k = 0; k < NSYMS; k++) { 998c2ecf20Sopenharmony_ci if (!strcmp(sym_name, required_syms[k].name)) { 1008c2ecf20Sopenharmony_ci if (syms[k]) { 1018c2ecf20Sopenharmony_ci fail("duplicate symbol %s\n", 1028c2ecf20Sopenharmony_ci required_syms[k].name); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * Careful: we use negative addresses, but 1078c2ecf20Sopenharmony_ci * st_value is unsigned, so we rely 1088c2ecf20Sopenharmony_ci * on syms[k] being a signed type of the 1098c2ecf20Sopenharmony_ci * correct width. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci syms[k] = GET_LE(&sym->st_value); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Validate mapping addresses. */ 1178c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) { 1188c2ecf20Sopenharmony_ci INT_BITS symval = syms[special_pages[i]]; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!symval) 1218c2ecf20Sopenharmony_ci continue; /* The mapping isn't used; ignore it. */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (symval % 4096) 1248c2ecf20Sopenharmony_ci fail("%s must be a multiple of 4096\n", 1258c2ecf20Sopenharmony_ci required_syms[i].name); 1268c2ecf20Sopenharmony_ci if (symval + 4096 < syms[sym_vvar_start]) 1278c2ecf20Sopenharmony_ci fail("%s underruns vvar_start\n", 1288c2ecf20Sopenharmony_ci required_syms[i].name); 1298c2ecf20Sopenharmony_ci if (symval + 4096 > 0) 1308c2ecf20Sopenharmony_ci fail("%s is on the wrong side of the vdso text\n", 1318c2ecf20Sopenharmony_ci required_syms[i].name); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci if (syms[sym_vvar_start] % 4096) 1348c2ecf20Sopenharmony_ci fail("vvar_begin must be a multiple of 4096\n"); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!image_name) { 1378c2ecf20Sopenharmony_ci fwrite(stripped_addr, stripped_len, 1, outfile); 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci mapping_size = (stripped_len + 4095) / 4096 * 4096; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci fprintf(outfile, "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n\n"); 1448c2ecf20Sopenharmony_ci fprintf(outfile, "#include <linux/linkage.h>\n"); 1458c2ecf20Sopenharmony_ci fprintf(outfile, "#include <asm/page_types.h>\n"); 1468c2ecf20Sopenharmony_ci fprintf(outfile, "#include <asm/vdso.h>\n"); 1478c2ecf20Sopenharmony_ci fprintf(outfile, "\n"); 1488c2ecf20Sopenharmony_ci fprintf(outfile, 1498c2ecf20Sopenharmony_ci "static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {", 1508c2ecf20Sopenharmony_ci mapping_size); 1518c2ecf20Sopenharmony_ci for (i = 0; i < stripped_len; i++) { 1528c2ecf20Sopenharmony_ci if (i % 10 == 0) 1538c2ecf20Sopenharmony_ci fprintf(outfile, "\n\t"); 1548c2ecf20Sopenharmony_ci fprintf(outfile, "0x%02X, ", 1558c2ecf20Sopenharmony_ci (int)((unsigned char *)stripped_addr)[i]); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci fprintf(outfile, "\n};\n\n"); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci fprintf(outfile, "const struct vdso_image %s = {\n", image_name); 1608c2ecf20Sopenharmony_ci fprintf(outfile, "\t.data = raw_data,\n"); 1618c2ecf20Sopenharmony_ci fprintf(outfile, "\t.size = %lu,\n", mapping_size); 1628c2ecf20Sopenharmony_ci if (alt_sec) { 1638c2ecf20Sopenharmony_ci fprintf(outfile, "\t.alt = %lu,\n", 1648c2ecf20Sopenharmony_ci (unsigned long)GET_LE(&alt_sec->sh_offset)); 1658c2ecf20Sopenharmony_ci fprintf(outfile, "\t.alt_len = %lu,\n", 1668c2ecf20Sopenharmony_ci (unsigned long)GET_LE(&alt_sec->sh_size)); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci for (i = 0; i < NSYMS; i++) { 1698c2ecf20Sopenharmony_ci if (required_syms[i].export && syms[i]) 1708c2ecf20Sopenharmony_ci fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n", 1718c2ecf20Sopenharmony_ci required_syms[i].name, (int64_t)syms[i]); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci fprintf(outfile, "};\n"); 1748c2ecf20Sopenharmony_ci} 175