1/* 2 * Post-process a vdso elf image for inclusion into qemu. 3 * Elf size specialization. 4 * 5 * Copyright 2023 Linaro, Ltd. 6 * 7 * SPDX-License-Identifier: GPL-2.0-or-later 8 */ 9 10static void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr) 11{ 12 bswaps(&ehdr->e_type); /* Object file type */ 13 bswaps(&ehdr->e_machine); /* Architecture */ 14 bswaps(&ehdr->e_version); /* Object file version */ 15 bswaps(&ehdr->e_entry); /* Entry point virtual address */ 16 bswaps(&ehdr->e_phoff); /* Program header table file offset */ 17 bswaps(&ehdr->e_shoff); /* Section header table file offset */ 18 bswaps(&ehdr->e_flags); /* Processor-specific flags */ 19 bswaps(&ehdr->e_ehsize); /* ELF header size in bytes */ 20 bswaps(&ehdr->e_phentsize); /* Program header table entry size */ 21 bswaps(&ehdr->e_phnum); /* Program header table entry count */ 22 bswaps(&ehdr->e_shentsize); /* Section header table entry size */ 23 bswaps(&ehdr->e_shnum); /* Section header table entry count */ 24 bswaps(&ehdr->e_shstrndx); /* Section header string table index */ 25} 26 27static void elfN(bswap_phdr)(ElfN(Phdr) *phdr) 28{ 29 bswaps(&phdr->p_type); /* Segment type */ 30 bswaps(&phdr->p_flags); /* Segment flags */ 31 bswaps(&phdr->p_offset); /* Segment file offset */ 32 bswaps(&phdr->p_vaddr); /* Segment virtual address */ 33 bswaps(&phdr->p_paddr); /* Segment physical address */ 34 bswaps(&phdr->p_filesz); /* Segment size in file */ 35 bswaps(&phdr->p_memsz); /* Segment size in memory */ 36 bswaps(&phdr->p_align); /* Segment alignment */ 37} 38 39static void elfN(bswap_shdr)(ElfN(Shdr) *shdr) 40{ 41 bswaps(&shdr->sh_name); 42 bswaps(&shdr->sh_type); 43 bswaps(&shdr->sh_flags); 44 bswaps(&shdr->sh_addr); 45 bswaps(&shdr->sh_offset); 46 bswaps(&shdr->sh_size); 47 bswaps(&shdr->sh_link); 48 bswaps(&shdr->sh_info); 49 bswaps(&shdr->sh_addralign); 50 bswaps(&shdr->sh_entsize); 51} 52 53static void elfN(bswap_sym)(ElfN(Sym) *sym) 54{ 55 bswaps(&sym->st_name); 56 bswaps(&sym->st_value); 57 bswaps(&sym->st_size); 58 bswaps(&sym->st_shndx); 59} 60 61static void elfN(bswap_dyn)(ElfN(Dyn) *dyn) 62{ 63 bswaps(&dyn->d_tag); /* Dynamic type tag */ 64 bswaps(&dyn->d_un.d_ptr); /* Dynamic ptr or val, in union */ 65} 66 67static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx, 68 void *buf, bool need_bswap) 69{ 70 unsigned str_idx = shdr[sym_idx].sh_link; 71 ElfN(Sym) *target_sym = buf + shdr[sym_idx].sh_offset; 72 unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*target_sym); 73 const char *str = buf + shdr[str_idx].sh_offset; 74 75 for (unsigned i = 0; i < sym_n; ++i) { 76 const char *name; 77 ElfN(Sym) sym; 78 79 memcpy(&sym, &target_sym[i], sizeof(sym)); 80 if (need_bswap) { 81 elfN(bswap_sym)(&sym); 82 } 83 name = str + sym.st_name; 84 85 if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) { 86 sigreturn_addr = sym.st_value; 87 } 88 if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) { 89 rt_sigreturn_addr = sym.st_value; 90 } 91 } 92} 93 94static void elfN(bswap_ps_hdrs)(ElfN(Ehdr) *ehdr) 95{ 96 ElfN(Phdr) *phdr = (void *)ehdr + ehdr->e_phoff; 97 ElfN(Shdr) *shdr = (void *)ehdr + ehdr->e_shoff; 98 ElfN(Half) i; 99 100 for (i = 0; i < ehdr->e_phnum; ++i) { 101 elfN(bswap_phdr)(&phdr[i]); 102 } 103 104 for (i = 0; i < ehdr->e_shnum; ++i) { 105 elfN(bswap_shdr)(&shdr[i]); 106 } 107} 108 109static void elfN(process)(FILE *outf, void *buf, long len, bool need_bswap) 110{ 111 ElfN(Ehdr) *ehdr = buf; 112 ElfN(Phdr) *phdr; 113 ElfN(Shdr) *shdr; 114 unsigned phnum, shnum; 115 unsigned dynamic_ofs = 0; 116 unsigned dynamic_addr = 0; 117 unsigned symtab_idx = 0; 118 unsigned dynsym_idx = 0; 119 unsigned first_segsz = 0; 120 int errors = 0; 121 122 if (need_bswap) { 123 elfN(bswap_ehdr)(buf); 124 elfN(bswap_ps_hdrs)(buf); 125 } 126 127 phnum = ehdr->e_phnum; 128 phdr = buf + ehdr->e_phoff; 129 shnum = ehdr->e_shnum; 130 shdr = buf + ehdr->e_shoff; 131 for (unsigned i = 0; i < shnum; ++i) { 132 switch (shdr[i].sh_type) { 133 case SHT_SYMTAB: 134 symtab_idx = i; 135 break; 136 case SHT_DYNSYM: 137 dynsym_idx = i; 138 break; 139 } 140 } 141 142 /* 143 * Validate the VDSO is created as we expect: that PT_PHDR, 144 * PT_DYNAMIC, and PT_NOTE located in a writable data segment. 145 * PHDR and DYNAMIC require relocation, and NOTE will get the 146 * linux version number. 147 */ 148 for (unsigned i = 0; i < phnum; ++i) { 149 if (phdr[i].p_type != PT_LOAD) { 150 continue; 151 } 152 if (first_segsz != 0) { 153 fprintf(stderr, "Multiple LOAD segments\n"); 154 errors++; 155 } 156 if (phdr[i].p_offset != 0) { 157 fprintf(stderr, "LOAD segment does not cover EHDR\n"); 158 errors++; 159 } 160 if (phdr[i].p_vaddr != 0) { 161 fprintf(stderr, "LOAD segment not loaded at address 0\n"); 162 errors++; 163 } 164 /* 165 * Extend the program header to cover the entire VDSO, so that 166 * load_elf_vdso() loads everything, including section headers. 167 * 168 * Require that there is no .bss, since it would break this 169 * approach. 170 */ 171 if (phdr[i].p_filesz != phdr[i].p_memsz) { 172 fprintf(stderr, "LOAD segment's filesz and memsz differ\n"); 173 errors++; 174 } 175 if (phdr[i].p_filesz > len) { 176 fprintf(stderr, "LOAD segment is larger than the whole VDSO\n"); 177 errors++; 178 } 179 phdr[i].p_filesz = len; 180 phdr[i].p_memsz = len; 181 first_segsz = len; 182 if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) { 183 fprintf(stderr, "LOAD segment does not cover PHDRs\n"); 184 errors++; 185 } 186 if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) { 187 fprintf(stderr, "LOAD segment is not read-write\n"); 188 errors++; 189 } 190 } 191 for (unsigned i = 0; i < phnum; ++i) { 192 const char *which; 193 194 switch (phdr[i].p_type) { 195 case PT_PHDR: 196 which = "PT_PHDR"; 197 break; 198 case PT_NOTE: 199 which = "PT_NOTE"; 200 break; 201 case PT_DYNAMIC: 202 dynamic_ofs = phdr[i].p_offset; 203 dynamic_addr = phdr[i].p_vaddr; 204 which = "PT_DYNAMIC"; 205 break; 206 default: 207 continue; 208 } 209 if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) { 210 fprintf(stderr, "LOAD segment does not cover %s\n", which); 211 errors++; 212 } 213 } 214 if (errors) { 215 exit(EXIT_FAILURE); 216 } 217 218 /* Relocate the program headers. */ 219 for (unsigned i = 0; i < phnum; ++i) { 220 output_reloc(outf, buf, &phdr[i].p_vaddr); 221 output_reloc(outf, buf, &phdr[i].p_paddr); 222 } 223 224 /* Relocate the section headers. */ 225 for (unsigned i = 0; i < shnum; ++i) { 226 output_reloc(outf, buf, &shdr[i].sh_addr); 227 } 228 229 /* Relocate the DYNAMIC entries. */ 230 if (dynamic_addr) { 231 ElfN(Dyn) *target_dyn = buf + dynamic_ofs; 232 __typeof(((ElfN(Dyn) *)target_dyn)->d_tag) tag; 233 234 do { 235 ElfN(Dyn) dyn; 236 237 memcpy(&dyn, target_dyn, sizeof(dyn)); 238 if (need_bswap) { 239 elfN(bswap_dyn)(&dyn); 240 } 241 tag = dyn.d_tag; 242 243 switch (tag) { 244 case DT_HASH: 245 case DT_SYMTAB: 246 case DT_STRTAB: 247 case DT_VERDEF: 248 case DT_VERSYM: 249 case DT_PLTGOT: 250 case DT_ADDRRNGLO ... DT_ADDRRNGHI: 251 /* These entries store an address in the entry. */ 252 output_reloc(outf, buf, &target_dyn->d_un.d_val); 253 break; 254 255 case DT_NULL: 256 case DT_STRSZ: 257 case DT_SONAME: 258 case DT_DEBUG: 259 case DT_FLAGS: 260 case DT_FLAGS_1: 261 case DT_SYMBOLIC: 262 case DT_BIND_NOW: 263 case DT_VERDEFNUM: 264 case DT_VALRNGLO ... DT_VALRNGHI: 265 /* These entries store an integer in the entry. */ 266 break; 267 268 case DT_SYMENT: 269 if (dyn.d_un.d_val != sizeof(ElfN(Sym))) { 270 fprintf(stderr, "VDSO has incorrect dynamic symbol size\n"); 271 errors++; 272 } 273 break; 274 275 case DT_REL: 276 case DT_RELSZ: 277 case DT_RELA: 278 case DT_RELASZ: 279 /* 280 * These entries indicate that the VDSO was built incorrectly. 281 * It should not have any real relocations. 282 * ??? The RISC-V toolchain will emit these even when there 283 * are no relocations. Validate zeros. 284 */ 285 if (dyn.d_un.d_val != 0) { 286 fprintf(stderr, "VDSO has dynamic relocations\n"); 287 errors++; 288 } 289 break; 290 case DT_RELENT: 291 case DT_RELAENT: 292 case DT_TEXTREL: 293 /* These entries store an integer in the entry. */ 294 /* Should not be required; see above. */ 295 break; 296 297 case DT_NEEDED: 298 case DT_VERNEED: 299 case DT_PLTREL: 300 case DT_JMPREL: 301 case DT_RPATH: 302 case DT_RUNPATH: 303 fprintf(stderr, "VDSO has external dependencies\n"); 304 errors++; 305 break; 306 307 case PT_LOPROC + 3: 308 if (ehdr->e_machine == EM_PPC64) { 309 break; /* DT_PPC64_OPT: integer bitmask */ 310 } 311 goto do_default; 312 313 default: 314 do_default: 315 /* This is probably something target specific. */ 316 fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n", 317 (unsigned long)tag); 318 errors++; 319 break; 320 } 321 target_dyn++; 322 } while (tag != DT_NULL); 323 if (errors) { 324 exit(EXIT_FAILURE); 325 } 326 } 327 328 /* Relocate the dynamic symbol table. */ 329 if (dynsym_idx) { 330 ElfN(Sym) *target_sym = buf + shdr[dynsym_idx].sh_offset; 331 unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*target_sym); 332 333 for (unsigned i = 0; i < sym_n; ++i) { 334 output_reloc(outf, buf, &target_sym[i].st_value); 335 } 336 } 337 338 /* Search both dynsym and symtab for the signal return symbols. */ 339 if (dynsym_idx) { 340 elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap); 341 } 342 if (symtab_idx) { 343 elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap); 344 } 345 346 if (need_bswap) { 347 elfN(bswap_ps_hdrs)(buf); 348 elfN(bswap_ehdr)(buf); 349 } 350} 351