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) *sym = buf + shdr[sym_idx].sh_offset; 72 unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*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 78 if (need_bswap) { 79 elfN(bswap_sym)(sym + i); 80 } 81 name = str + sym[i].st_name; 82 83 if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) { 84 sigreturn_addr = sym[i].st_value; 85 } 86 if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) { 87 rt_sigreturn_addr = sym[i].st_value; 88 } 89 } 90} 91 92static void elfN(process)(FILE *outf, void *buf, bool need_bswap) 93{ 94 ElfN(Ehdr) *ehdr = buf; 95 ElfN(Phdr) *phdr; 96 ElfN(Shdr) *shdr; 97 unsigned phnum, shnum; 98 unsigned dynamic_ofs = 0; 99 unsigned dynamic_addr = 0; 100 unsigned symtab_idx = 0; 101 unsigned dynsym_idx = 0; 102 unsigned first_segsz = 0; 103 int errors = 0; 104 105 if (need_bswap) { 106 elfN(bswap_ehdr)(ehdr); 107 } 108 109 phnum = ehdr->e_phnum; 110 phdr = buf + ehdr->e_phoff; 111 if (need_bswap) { 112 for (unsigned i = 0; i < phnum; ++i) { 113 elfN(bswap_phdr)(phdr + i); 114 } 115 } 116 117 shnum = ehdr->e_shnum; 118 shdr = buf + ehdr->e_shoff; 119 if (need_bswap) { 120 for (unsigned i = 0; i < shnum; ++i) { 121 elfN(bswap_shdr)(shdr + i); 122 } 123 } 124 for (unsigned i = 0; i < shnum; ++i) { 125 switch (shdr[i].sh_type) { 126 case SHT_SYMTAB: 127 symtab_idx = i; 128 break; 129 case SHT_DYNSYM: 130 dynsym_idx = i; 131 break; 132 } 133 } 134 135 /* 136 * Validate the VDSO is created as we expect: that PT_PHDR, 137 * PT_DYNAMIC, and PT_NOTE located in a writable data segment. 138 * PHDR and DYNAMIC require relocation, and NOTE will get the 139 * linux version number. 140 */ 141 for (unsigned i = 0; i < phnum; ++i) { 142 if (phdr[i].p_type != PT_LOAD) { 143 continue; 144 } 145 if (first_segsz != 0) { 146 fprintf(stderr, "Multiple LOAD segments\n"); 147 errors++; 148 } 149 if (phdr[i].p_offset != 0) { 150 fprintf(stderr, "LOAD segment does not cover EHDR\n"); 151 errors++; 152 } 153 if (phdr[i].p_vaddr != 0) { 154 fprintf(stderr, "LOAD segment not loaded at address 0\n"); 155 errors++; 156 } 157 first_segsz = phdr[i].p_filesz; 158 if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) { 159 fprintf(stderr, "LOAD segment does not cover PHDRs\n"); 160 errors++; 161 } 162 if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) { 163 fprintf(stderr, "LOAD segment is not read-write\n"); 164 errors++; 165 } 166 } 167 for (unsigned i = 0; i < phnum; ++i) { 168 const char *which; 169 170 switch (phdr[i].p_type) { 171 case PT_PHDR: 172 which = "PT_PHDR"; 173 break; 174 case PT_NOTE: 175 which = "PT_NOTE"; 176 break; 177 case PT_DYNAMIC: 178 dynamic_ofs = phdr[i].p_offset; 179 dynamic_addr = phdr[i].p_vaddr; 180 which = "PT_DYNAMIC"; 181 break; 182 default: 183 continue; 184 } 185 if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) { 186 fprintf(stderr, "LOAD segment does not cover %s\n", which); 187 errors++; 188 } 189 } 190 if (errors) { 191 exit(EXIT_FAILURE); 192 } 193 194 /* Relocate the program headers. */ 195 for (unsigned i = 0; i < phnum; ++i) { 196 output_reloc(outf, buf, &phdr[i].p_vaddr); 197 output_reloc(outf, buf, &phdr[i].p_paddr); 198 } 199 200 /* Relocate the DYNAMIC entries. */ 201 if (dynamic_addr) { 202 ElfN(Dyn) *dyn = buf + dynamic_ofs; 203 __typeof(dyn->d_tag) tag; 204 205 do { 206 207 if (need_bswap) { 208 elfN(bswap_dyn)(dyn); 209 } 210 tag = dyn->d_tag; 211 212 switch (tag) { 213 case DT_HASH: 214 case DT_SYMTAB: 215 case DT_STRTAB: 216 case DT_VERDEF: 217 case DT_VERSYM: 218 case DT_PLTGOT: 219 case DT_ADDRRNGLO ... DT_ADDRRNGHI: 220 /* These entries store an address in the entry. */ 221 output_reloc(outf, buf, &dyn->d_un.d_val); 222 break; 223 224 case DT_NULL: 225 case DT_STRSZ: 226 case DT_SONAME: 227 case DT_DEBUG: 228 case DT_FLAGS: 229 case DT_FLAGS_1: 230 case DT_SYMBOLIC: 231 case DT_BIND_NOW: 232 case DT_VERDEFNUM: 233 case DT_VALRNGLO ... DT_VALRNGHI: 234 /* These entries store an integer in the entry. */ 235 break; 236 237 case DT_SYMENT: 238 if (dyn->d_un.d_val != sizeof(ElfN(Sym))) { 239 fprintf(stderr, "VDSO has incorrect dynamic symbol size\n"); 240 errors++; 241 } 242 break; 243 244 case DT_REL: 245 case DT_RELSZ: 246 case DT_RELA: 247 case DT_RELASZ: 248 /* 249 * These entries indicate that the VDSO was built incorrectly. 250 * It should not have any real relocations. 251 * ??? The RISC-V toolchain will emit these even when there 252 * are no relocations. Validate zeros. 253 */ 254 if (dyn->d_un.d_val != 0) { 255 fprintf(stderr, "VDSO has dynamic relocations\n"); 256 errors++; 257 } 258 break; 259 case DT_RELENT: 260 case DT_RELAENT: 261 case DT_TEXTREL: 262 /* These entries store an integer in the entry. */ 263 /* Should not be required; see above. */ 264 break; 265 266 case DT_NEEDED: 267 case DT_VERNEED: 268 case DT_PLTREL: 269 case DT_JMPREL: 270 case DT_RPATH: 271 case DT_RUNPATH: 272 fprintf(stderr, "VDSO has external dependencies\n"); 273 errors++; 274 break; 275 276 case PT_LOPROC + 3: 277 if (ehdr->e_machine == EM_PPC64) { 278 break; /* DT_PPC64_OPT: integer bitmask */ 279 } 280 goto do_default; 281 282 default: 283 do_default: 284 /* This is probably something target specific. */ 285 fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n", 286 (unsigned long)tag); 287 errors++; 288 break; 289 } 290 dyn++; 291 } while (tag != DT_NULL); 292 if (errors) { 293 exit(EXIT_FAILURE); 294 } 295 } 296 297 /* Relocate the dynamic symbol table. */ 298 if (dynsym_idx) { 299 ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset; 300 unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym); 301 302 for (unsigned i = 0; i < sym_n; ++i) { 303 output_reloc(outf, buf, &sym[i].st_value); 304 } 305 } 306 307 /* Search both dynsym and symtab for the signal return symbols. */ 308 if (dynsym_idx) { 309 elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap); 310 } 311 if (symtab_idx) { 312 elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap); 313 } 314} 315