12fa536d1SRichard Henderson/* 22fa536d1SRichard Henderson * Post-process a vdso elf image for inclusion into qemu. 32fa536d1SRichard Henderson * Elf size specialization. 42fa536d1SRichard Henderson * 52fa536d1SRichard Henderson * Copyright 2023 Linaro, Ltd. 62fa536d1SRichard Henderson * 72fa536d1SRichard Henderson * SPDX-License-Identifier: GPL-2.0-or-later 82fa536d1SRichard Henderson */ 92fa536d1SRichard Henderson 102fa536d1SRichard Hendersonstatic void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr) 112fa536d1SRichard Henderson{ 122fa536d1SRichard Henderson bswaps(&ehdr->e_type); /* Object file type */ 132fa536d1SRichard Henderson bswaps(&ehdr->e_machine); /* Architecture */ 142fa536d1SRichard Henderson bswaps(&ehdr->e_version); /* Object file version */ 152fa536d1SRichard Henderson bswaps(&ehdr->e_entry); /* Entry point virtual address */ 162fa536d1SRichard Henderson bswaps(&ehdr->e_phoff); /* Program header table file offset */ 172fa536d1SRichard Henderson bswaps(&ehdr->e_shoff); /* Section header table file offset */ 182fa536d1SRichard Henderson bswaps(&ehdr->e_flags); /* Processor-specific flags */ 192fa536d1SRichard Henderson bswaps(&ehdr->e_ehsize); /* ELF header size in bytes */ 202fa536d1SRichard Henderson bswaps(&ehdr->e_phentsize); /* Program header table entry size */ 212fa536d1SRichard Henderson bswaps(&ehdr->e_phnum); /* Program header table entry count */ 222fa536d1SRichard Henderson bswaps(&ehdr->e_shentsize); /* Section header table entry size */ 232fa536d1SRichard Henderson bswaps(&ehdr->e_shnum); /* Section header table entry count */ 242fa536d1SRichard Henderson bswaps(&ehdr->e_shstrndx); /* Section header string table index */ 252fa536d1SRichard Henderson} 262fa536d1SRichard Henderson 272fa536d1SRichard Hendersonstatic void elfN(bswap_phdr)(ElfN(Phdr) *phdr) 282fa536d1SRichard Henderson{ 292fa536d1SRichard Henderson bswaps(&phdr->p_type); /* Segment type */ 302fa536d1SRichard Henderson bswaps(&phdr->p_flags); /* Segment flags */ 312fa536d1SRichard Henderson bswaps(&phdr->p_offset); /* Segment file offset */ 322fa536d1SRichard Henderson bswaps(&phdr->p_vaddr); /* Segment virtual address */ 332fa536d1SRichard Henderson bswaps(&phdr->p_paddr); /* Segment physical address */ 342fa536d1SRichard Henderson bswaps(&phdr->p_filesz); /* Segment size in file */ 352fa536d1SRichard Henderson bswaps(&phdr->p_memsz); /* Segment size in memory */ 362fa536d1SRichard Henderson bswaps(&phdr->p_align); /* Segment alignment */ 372fa536d1SRichard Henderson} 382fa536d1SRichard Henderson 392fa536d1SRichard Hendersonstatic void elfN(bswap_shdr)(ElfN(Shdr) *shdr) 402fa536d1SRichard Henderson{ 412fa536d1SRichard Henderson bswaps(&shdr->sh_name); 422fa536d1SRichard Henderson bswaps(&shdr->sh_type); 432fa536d1SRichard Henderson bswaps(&shdr->sh_flags); 442fa536d1SRichard Henderson bswaps(&shdr->sh_addr); 452fa536d1SRichard Henderson bswaps(&shdr->sh_offset); 462fa536d1SRichard Henderson bswaps(&shdr->sh_size); 472fa536d1SRichard Henderson bswaps(&shdr->sh_link); 482fa536d1SRichard Henderson bswaps(&shdr->sh_info); 492fa536d1SRichard Henderson bswaps(&shdr->sh_addralign); 502fa536d1SRichard Henderson bswaps(&shdr->sh_entsize); 512fa536d1SRichard Henderson} 522fa536d1SRichard Henderson 532fa536d1SRichard Hendersonstatic void elfN(bswap_sym)(ElfN(Sym) *sym) 542fa536d1SRichard Henderson{ 552fa536d1SRichard Henderson bswaps(&sym->st_name); 562fa536d1SRichard Henderson bswaps(&sym->st_value); 572fa536d1SRichard Henderson bswaps(&sym->st_size); 582fa536d1SRichard Henderson bswaps(&sym->st_shndx); 592fa536d1SRichard Henderson} 602fa536d1SRichard Henderson 612fa536d1SRichard Hendersonstatic void elfN(bswap_dyn)(ElfN(Dyn) *dyn) 622fa536d1SRichard Henderson{ 632fa536d1SRichard Henderson bswaps(&dyn->d_tag); /* Dynamic type tag */ 642fa536d1SRichard Henderson bswaps(&dyn->d_un.d_ptr); /* Dynamic ptr or val, in union */ 652fa536d1SRichard Henderson} 662fa536d1SRichard Henderson 672fa536d1SRichard Hendersonstatic void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx, 682fa536d1SRichard Henderson void *buf, bool need_bswap) 692fa536d1SRichard Henderson{ 702fa536d1SRichard Henderson unsigned str_idx = shdr[sym_idx].sh_link; 712fa536d1SRichard Henderson ElfN(Sym) *sym = buf + shdr[sym_idx].sh_offset; 722fa536d1SRichard Henderson unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*sym); 732fa536d1SRichard Henderson const char *str = buf + shdr[str_idx].sh_offset; 742fa536d1SRichard Henderson 752fa536d1SRichard Henderson for (unsigned i = 0; i < sym_n; ++i) { 762fa536d1SRichard Henderson const char *name; 772fa536d1SRichard Henderson 782fa536d1SRichard Henderson if (need_bswap) { 792fa536d1SRichard Henderson elfN(bswap_sym)(sym + i); 802fa536d1SRichard Henderson } 812fa536d1SRichard Henderson name = str + sym[i].st_name; 822fa536d1SRichard Henderson 832fa536d1SRichard Henderson if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) { 842fa536d1SRichard Henderson sigreturn_addr = sym[i].st_value; 852fa536d1SRichard Henderson } 862fa536d1SRichard Henderson if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) { 872fa536d1SRichard Henderson rt_sigreturn_addr = sym[i].st_value; 882fa536d1SRichard Henderson } 892fa536d1SRichard Henderson } 902fa536d1SRichard Henderson} 912fa536d1SRichard Henderson 922fa536d1SRichard Hendersonstatic void elfN(process)(FILE *outf, void *buf, bool need_bswap) 932fa536d1SRichard Henderson{ 942fa536d1SRichard Henderson ElfN(Ehdr) *ehdr = buf; 952fa536d1SRichard Henderson ElfN(Phdr) *phdr; 962fa536d1SRichard Henderson ElfN(Shdr) *shdr; 972fa536d1SRichard Henderson unsigned phnum, shnum; 982fa536d1SRichard Henderson unsigned dynamic_ofs = 0; 992fa536d1SRichard Henderson unsigned dynamic_addr = 0; 1002fa536d1SRichard Henderson unsigned symtab_idx = 0; 1012fa536d1SRichard Henderson unsigned dynsym_idx = 0; 1022fa536d1SRichard Henderson unsigned first_segsz = 0; 1032fa536d1SRichard Henderson int errors = 0; 1042fa536d1SRichard Henderson 1052fa536d1SRichard Henderson if (need_bswap) { 1062fa536d1SRichard Henderson elfN(bswap_ehdr)(ehdr); 1072fa536d1SRichard Henderson } 1082fa536d1SRichard Henderson 1092fa536d1SRichard Henderson phnum = ehdr->e_phnum; 1102fa536d1SRichard Henderson phdr = buf + ehdr->e_phoff; 1112fa536d1SRichard Henderson if (need_bswap) { 1122fa536d1SRichard Henderson for (unsigned i = 0; i < phnum; ++i) { 1132fa536d1SRichard Henderson elfN(bswap_phdr)(phdr + i); 1142fa536d1SRichard Henderson } 1152fa536d1SRichard Henderson } 1162fa536d1SRichard Henderson 1172fa536d1SRichard Henderson shnum = ehdr->e_shnum; 1182fa536d1SRichard Henderson shdr = buf + ehdr->e_shoff; 1192fa536d1SRichard Henderson if (need_bswap) { 1202fa536d1SRichard Henderson for (unsigned i = 0; i < shnum; ++i) { 1212fa536d1SRichard Henderson elfN(bswap_shdr)(shdr + i); 1222fa536d1SRichard Henderson } 1232fa536d1SRichard Henderson } 1242fa536d1SRichard Henderson for (unsigned i = 0; i < shnum; ++i) { 1252fa536d1SRichard Henderson switch (shdr[i].sh_type) { 1262fa536d1SRichard Henderson case SHT_SYMTAB: 1272fa536d1SRichard Henderson symtab_idx = i; 1282fa536d1SRichard Henderson break; 1292fa536d1SRichard Henderson case SHT_DYNSYM: 1302fa536d1SRichard Henderson dynsym_idx = i; 1312fa536d1SRichard Henderson break; 1322fa536d1SRichard Henderson } 1332fa536d1SRichard Henderson } 1342fa536d1SRichard Henderson 1352fa536d1SRichard Henderson /* 1362fa536d1SRichard Henderson * Validate the VDSO is created as we expect: that PT_PHDR, 1372fa536d1SRichard Henderson * PT_DYNAMIC, and PT_NOTE located in a writable data segment. 1382fa536d1SRichard Henderson * PHDR and DYNAMIC require relocation, and NOTE will get the 1392fa536d1SRichard Henderson * linux version number. 1402fa536d1SRichard Henderson */ 1412fa536d1SRichard Henderson for (unsigned i = 0; i < phnum; ++i) { 1422fa536d1SRichard Henderson if (phdr[i].p_type != PT_LOAD) { 1432fa536d1SRichard Henderson continue; 1442fa536d1SRichard Henderson } 1452fa536d1SRichard Henderson if (first_segsz != 0) { 1462fa536d1SRichard Henderson fprintf(stderr, "Multiple LOAD segments\n"); 1472fa536d1SRichard Henderson errors++; 1482fa536d1SRichard Henderson } 1492fa536d1SRichard Henderson if (phdr[i].p_offset != 0) { 1502fa536d1SRichard Henderson fprintf(stderr, "LOAD segment does not cover EHDR\n"); 1512fa536d1SRichard Henderson errors++; 1522fa536d1SRichard Henderson } 1532fa536d1SRichard Henderson if (phdr[i].p_vaddr != 0) { 1542fa536d1SRichard Henderson fprintf(stderr, "LOAD segment not loaded at address 0\n"); 1552fa536d1SRichard Henderson errors++; 1562fa536d1SRichard Henderson } 1572fa536d1SRichard Henderson first_segsz = phdr[i].p_filesz; 1582fa536d1SRichard Henderson if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) { 1592fa536d1SRichard Henderson fprintf(stderr, "LOAD segment does not cover PHDRs\n"); 1602fa536d1SRichard Henderson errors++; 1612fa536d1SRichard Henderson } 1622fa536d1SRichard Henderson if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) { 1632fa536d1SRichard Henderson fprintf(stderr, "LOAD segment is not read-write\n"); 1642fa536d1SRichard Henderson errors++; 1652fa536d1SRichard Henderson } 1662fa536d1SRichard Henderson } 1672fa536d1SRichard Henderson for (unsigned i = 0; i < phnum; ++i) { 1682fa536d1SRichard Henderson const char *which; 1692fa536d1SRichard Henderson 1702fa536d1SRichard Henderson switch (phdr[i].p_type) { 1712fa536d1SRichard Henderson case PT_PHDR: 1722fa536d1SRichard Henderson which = "PT_PHDR"; 1732fa536d1SRichard Henderson break; 1742fa536d1SRichard Henderson case PT_NOTE: 1752fa536d1SRichard Henderson which = "PT_NOTE"; 1762fa536d1SRichard Henderson break; 1772fa536d1SRichard Henderson case PT_DYNAMIC: 1782fa536d1SRichard Henderson dynamic_ofs = phdr[i].p_offset; 1792fa536d1SRichard Henderson dynamic_addr = phdr[i].p_vaddr; 1802fa536d1SRichard Henderson which = "PT_DYNAMIC"; 1812fa536d1SRichard Henderson break; 1822fa536d1SRichard Henderson default: 1832fa536d1SRichard Henderson continue; 1842fa536d1SRichard Henderson } 1852fa536d1SRichard Henderson if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) { 1862fa536d1SRichard Henderson fprintf(stderr, "LOAD segment does not cover %s\n", which); 1872fa536d1SRichard Henderson errors++; 1882fa536d1SRichard Henderson } 1892fa536d1SRichard Henderson } 1902fa536d1SRichard Henderson if (errors) { 1912fa536d1SRichard Henderson exit(EXIT_FAILURE); 1922fa536d1SRichard Henderson } 1932fa536d1SRichard Henderson 1942fa536d1SRichard Henderson /* Relocate the program headers. */ 1952fa536d1SRichard Henderson for (unsigned i = 0; i < phnum; ++i) { 1962fa536d1SRichard Henderson output_reloc(outf, buf, &phdr[i].p_vaddr); 1972fa536d1SRichard Henderson output_reloc(outf, buf, &phdr[i].p_paddr); 1982fa536d1SRichard Henderson } 1992fa536d1SRichard Henderson 2002fa536d1SRichard Henderson /* Relocate the DYNAMIC entries. */ 2012fa536d1SRichard Henderson if (dynamic_addr) { 2022fa536d1SRichard Henderson ElfN(Dyn) *dyn = buf + dynamic_ofs; 2032fa536d1SRichard Henderson __typeof(dyn->d_tag) tag; 2042fa536d1SRichard Henderson 2052fa536d1SRichard Henderson do { 2062fa536d1SRichard Henderson 2072fa536d1SRichard Henderson if (need_bswap) { 2082fa536d1SRichard Henderson elfN(bswap_dyn)(dyn); 2092fa536d1SRichard Henderson } 2102fa536d1SRichard Henderson tag = dyn->d_tag; 2112fa536d1SRichard Henderson 2122fa536d1SRichard Henderson switch (tag) { 2132fa536d1SRichard Henderson case DT_HASH: 2142fa536d1SRichard Henderson case DT_SYMTAB: 2152fa536d1SRichard Henderson case DT_STRTAB: 2162fa536d1SRichard Henderson case DT_VERDEF: 2172fa536d1SRichard Henderson case DT_VERSYM: 2182fa536d1SRichard Henderson case DT_PLTGOT: 2192fa536d1SRichard Henderson case DT_ADDRRNGLO ... DT_ADDRRNGHI: 2202fa536d1SRichard Henderson /* These entries store an address in the entry. */ 2212fa536d1SRichard Henderson output_reloc(outf, buf, &dyn->d_un.d_val); 2222fa536d1SRichard Henderson break; 2232fa536d1SRichard Henderson 2242fa536d1SRichard Henderson case DT_NULL: 2252fa536d1SRichard Henderson case DT_STRSZ: 2262fa536d1SRichard Henderson case DT_SONAME: 2272fa536d1SRichard Henderson case DT_DEBUG: 2282fa536d1SRichard Henderson case DT_FLAGS: 2292fa536d1SRichard Henderson case DT_FLAGS_1: 2302fa536d1SRichard Henderson case DT_SYMBOLIC: 2312fa536d1SRichard Henderson case DT_BIND_NOW: 2322fa536d1SRichard Henderson case DT_VERDEFNUM: 2332fa536d1SRichard Henderson case DT_VALRNGLO ... DT_VALRNGHI: 2342fa536d1SRichard Henderson /* These entries store an integer in the entry. */ 2352fa536d1SRichard Henderson break; 2362fa536d1SRichard Henderson 2372fa536d1SRichard Henderson case DT_SYMENT: 2382fa536d1SRichard Henderson if (dyn->d_un.d_val != sizeof(ElfN(Sym))) { 2392fa536d1SRichard Henderson fprintf(stderr, "VDSO has incorrect dynamic symbol size\n"); 2402fa536d1SRichard Henderson errors++; 2412fa536d1SRichard Henderson } 2422fa536d1SRichard Henderson break; 2432fa536d1SRichard Henderson 2442fa536d1SRichard Henderson case DT_REL: 2452fa536d1SRichard Henderson case DT_RELSZ: 2462fa536d1SRichard Henderson case DT_RELA: 2472fa536d1SRichard Henderson case DT_RELASZ: 2482fa536d1SRichard Henderson /* 2492fa536d1SRichard Henderson * These entries indicate that the VDSO was built incorrectly. 2502fa536d1SRichard Henderson * It should not have any real relocations. 2512fa536d1SRichard Henderson * ??? The RISC-V toolchain will emit these even when there 2522fa536d1SRichard Henderson * are no relocations. Validate zeros. 2532fa536d1SRichard Henderson */ 2542fa536d1SRichard Henderson if (dyn->d_un.d_val != 0) { 2552fa536d1SRichard Henderson fprintf(stderr, "VDSO has dynamic relocations\n"); 2562fa536d1SRichard Henderson errors++; 2572fa536d1SRichard Henderson } 2582fa536d1SRichard Henderson break; 2592fa536d1SRichard Henderson case DT_RELENT: 2602fa536d1SRichard Henderson case DT_RELAENT: 2612fa536d1SRichard Henderson case DT_TEXTREL: 2622fa536d1SRichard Henderson /* These entries store an integer in the entry. */ 2632fa536d1SRichard Henderson /* Should not be required; see above. */ 2642fa536d1SRichard Henderson break; 2652fa536d1SRichard Henderson 2662fa536d1SRichard Henderson case DT_NEEDED: 2672fa536d1SRichard Henderson case DT_VERNEED: 2682fa536d1SRichard Henderson case DT_PLTREL: 2692fa536d1SRichard Henderson case DT_JMPREL: 2702fa536d1SRichard Henderson case DT_RPATH: 2712fa536d1SRichard Henderson case DT_RUNPATH: 2722fa536d1SRichard Henderson fprintf(stderr, "VDSO has external dependencies\n"); 2732fa536d1SRichard Henderson errors++; 2742fa536d1SRichard Henderson break; 2752fa536d1SRichard Henderson 276*e34136d9SRichard Henderson case PT_LOPROC + 3: 277*e34136d9SRichard Henderson if (ehdr->e_machine == EM_PPC64) { 278*e34136d9SRichard Henderson break; /* DT_PPC64_OPT: integer bitmask */ 279*e34136d9SRichard Henderson } 280*e34136d9SRichard Henderson goto do_default; 281*e34136d9SRichard Henderson 2822fa536d1SRichard Henderson default: 283*e34136d9SRichard Henderson do_default: 2842fa536d1SRichard Henderson /* This is probably something target specific. */ 2852fa536d1SRichard Henderson fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n", 2862fa536d1SRichard Henderson (unsigned long)tag); 2872fa536d1SRichard Henderson errors++; 2882fa536d1SRichard Henderson break; 2892fa536d1SRichard Henderson } 2902fa536d1SRichard Henderson dyn++; 2912fa536d1SRichard Henderson } while (tag != DT_NULL); 2922fa536d1SRichard Henderson if (errors) { 2932fa536d1SRichard Henderson exit(EXIT_FAILURE); 2942fa536d1SRichard Henderson } 2952fa536d1SRichard Henderson } 2962fa536d1SRichard Henderson 2972fa536d1SRichard Henderson /* Relocate the dynamic symbol table. */ 2982fa536d1SRichard Henderson if (dynsym_idx) { 2992fa536d1SRichard Henderson ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset; 3002fa536d1SRichard Henderson unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym); 3012fa536d1SRichard Henderson 3022fa536d1SRichard Henderson for (unsigned i = 0; i < sym_n; ++i) { 3032fa536d1SRichard Henderson output_reloc(outf, buf, &sym[i].st_value); 3042fa536d1SRichard Henderson } 3052fa536d1SRichard Henderson } 3062fa536d1SRichard Henderson 3072fa536d1SRichard Henderson /* Search both dynsym and symtab for the signal return symbols. */ 3082fa536d1SRichard Henderson if (dynsym_idx) { 3092fa536d1SRichard Henderson elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap); 3102fa536d1SRichard Henderson } 3112fa536d1SRichard Henderson if (symtab_idx) { 3122fa536d1SRichard Henderson elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap); 3132fa536d1SRichard Henderson } 3142fa536d1SRichard Henderson} 315