123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- /*
- * Post-process a vdso elf image for inclusion into qemu.
- * Elf size specialization.
- *
- * Copyright 2023 Linaro, Ltd.
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
- static void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr)
- {
- bswaps(&ehdr->e_type); /* Object file type */
- bswaps(&ehdr->e_machine); /* Architecture */
- bswaps(&ehdr->e_version); /* Object file version */
- bswaps(&ehdr->e_entry); /* Entry point virtual address */
- bswaps(&ehdr->e_phoff); /* Program header table file offset */
- bswaps(&ehdr->e_shoff); /* Section header table file offset */
- bswaps(&ehdr->e_flags); /* Processor-specific flags */
- bswaps(&ehdr->e_ehsize); /* ELF header size in bytes */
- bswaps(&ehdr->e_phentsize); /* Program header table entry size */
- bswaps(&ehdr->e_phnum); /* Program header table entry count */
- bswaps(&ehdr->e_shentsize); /* Section header table entry size */
- bswaps(&ehdr->e_shnum); /* Section header table entry count */
- bswaps(&ehdr->e_shstrndx); /* Section header string table index */
- }
- static void elfN(bswap_phdr)(ElfN(Phdr) *phdr)
- {
- bswaps(&phdr->p_type); /* Segment type */
- bswaps(&phdr->p_flags); /* Segment flags */
- bswaps(&phdr->p_offset); /* Segment file offset */
- bswaps(&phdr->p_vaddr); /* Segment virtual address */
- bswaps(&phdr->p_paddr); /* Segment physical address */
- bswaps(&phdr->p_filesz); /* Segment size in file */
- bswaps(&phdr->p_memsz); /* Segment size in memory */
- bswaps(&phdr->p_align); /* Segment alignment */
- }
- static void elfN(bswap_shdr)(ElfN(Shdr) *shdr)
- {
- bswaps(&shdr->sh_name);
- bswaps(&shdr->sh_type);
- bswaps(&shdr->sh_flags);
- bswaps(&shdr->sh_addr);
- bswaps(&shdr->sh_offset);
- bswaps(&shdr->sh_size);
- bswaps(&shdr->sh_link);
- bswaps(&shdr->sh_info);
- bswaps(&shdr->sh_addralign);
- bswaps(&shdr->sh_entsize);
- }
- static void elfN(bswap_sym)(ElfN(Sym) *sym)
- {
- bswaps(&sym->st_name);
- bswaps(&sym->st_value);
- bswaps(&sym->st_size);
- bswaps(&sym->st_shndx);
- }
- static void elfN(bswap_dyn)(ElfN(Dyn) *dyn)
- {
- bswaps(&dyn->d_tag); /* Dynamic type tag */
- bswaps(&dyn->d_un.d_ptr); /* Dynamic ptr or val, in union */
- }
- static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx,
- void *buf, bool need_bswap)
- {
- unsigned str_idx = shdr[sym_idx].sh_link;
- ElfN(Sym) *target_sym = buf + shdr[sym_idx].sh_offset;
- unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*target_sym);
- const char *str = buf + shdr[str_idx].sh_offset;
- for (unsigned i = 0; i < sym_n; ++i) {
- const char *name;
- ElfN(Sym) sym;
- memcpy(&sym, &target_sym[i], sizeof(sym));
- if (need_bswap) {
- elfN(bswap_sym)(&sym);
- }
- name = str + sym.st_name;
- if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) {
- sigreturn_addr = sym.st_value;
- }
- if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) {
- rt_sigreturn_addr = sym.st_value;
- }
- }
- }
- static void elfN(bswap_ps_hdrs)(ElfN(Ehdr) *ehdr)
- {
- ElfN(Phdr) *phdr = (void *)ehdr + ehdr->e_phoff;
- ElfN(Shdr) *shdr = (void *)ehdr + ehdr->e_shoff;
- ElfN(Half) i;
- for (i = 0; i < ehdr->e_phnum; ++i) {
- elfN(bswap_phdr)(&phdr[i]);
- }
- for (i = 0; i < ehdr->e_shnum; ++i) {
- elfN(bswap_shdr)(&shdr[i]);
- }
- }
- static void elfN(process)(FILE *outf, void *buf, long len, bool need_bswap)
- {
- ElfN(Ehdr) *ehdr = buf;
- ElfN(Phdr) *phdr;
- ElfN(Shdr) *shdr;
- unsigned phnum, shnum;
- unsigned dynamic_ofs = 0;
- unsigned dynamic_addr = 0;
- unsigned symtab_idx = 0;
- unsigned dynsym_idx = 0;
- unsigned first_segsz = 0;
- int errors = 0;
- if (need_bswap) {
- elfN(bswap_ehdr)(buf);
- elfN(bswap_ps_hdrs)(buf);
- }
- phnum = ehdr->e_phnum;
- phdr = buf + ehdr->e_phoff;
- shnum = ehdr->e_shnum;
- shdr = buf + ehdr->e_shoff;
- for (unsigned i = 0; i < shnum; ++i) {
- switch (shdr[i].sh_type) {
- case SHT_SYMTAB:
- symtab_idx = i;
- break;
- case SHT_DYNSYM:
- dynsym_idx = i;
- break;
- }
- }
- /*
- * Validate the VDSO is created as we expect: that PT_PHDR,
- * PT_DYNAMIC, and PT_NOTE located in a writable data segment.
- * PHDR and DYNAMIC require relocation, and NOTE will get the
- * linux version number.
- */
- for (unsigned i = 0; i < phnum; ++i) {
- if (phdr[i].p_type != PT_LOAD) {
- continue;
- }
- if (first_segsz != 0) {
- fprintf(stderr, "Multiple LOAD segments\n");
- errors++;
- }
- if (phdr[i].p_offset != 0) {
- fprintf(stderr, "LOAD segment does not cover EHDR\n");
- errors++;
- }
- if (phdr[i].p_vaddr != 0) {
- fprintf(stderr, "LOAD segment not loaded at address 0\n");
- errors++;
- }
- /*
- * Extend the program header to cover the entire VDSO, so that
- * load_elf_vdso() loads everything, including section headers.
- *
- * Require that there is no .bss, since it would break this
- * approach.
- */
- if (phdr[i].p_filesz != phdr[i].p_memsz) {
- fprintf(stderr, "LOAD segment's filesz and memsz differ\n");
- errors++;
- }
- if (phdr[i].p_filesz > len) {
- fprintf(stderr, "LOAD segment is larger than the whole VDSO\n");
- errors++;
- }
- phdr[i].p_filesz = len;
- phdr[i].p_memsz = len;
- first_segsz = len;
- if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) {
- fprintf(stderr, "LOAD segment does not cover PHDRs\n");
- errors++;
- }
- if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) {
- fprintf(stderr, "LOAD segment is not read-write\n");
- errors++;
- }
- }
- for (unsigned i = 0; i < phnum; ++i) {
- const char *which;
- switch (phdr[i].p_type) {
- case PT_PHDR:
- which = "PT_PHDR";
- break;
- case PT_NOTE:
- which = "PT_NOTE";
- break;
- case PT_DYNAMIC:
- dynamic_ofs = phdr[i].p_offset;
- dynamic_addr = phdr[i].p_vaddr;
- which = "PT_DYNAMIC";
- break;
- default:
- continue;
- }
- if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) {
- fprintf(stderr, "LOAD segment does not cover %s\n", which);
- errors++;
- }
- }
- if (errors) {
- exit(EXIT_FAILURE);
- }
- /* Relocate the program headers. */
- for (unsigned i = 0; i < phnum; ++i) {
- output_reloc(outf, buf, &phdr[i].p_vaddr);
- output_reloc(outf, buf, &phdr[i].p_paddr);
- }
- /* Relocate the section headers. */
- for (unsigned i = 0; i < shnum; ++i) {
- output_reloc(outf, buf, &shdr[i].sh_addr);
- }
- /* Relocate the DYNAMIC entries. */
- if (dynamic_addr) {
- ElfN(Dyn) *target_dyn = buf + dynamic_ofs;
- __typeof(((ElfN(Dyn) *)target_dyn)->d_tag) tag;
- do {
- ElfN(Dyn) dyn;
- memcpy(&dyn, target_dyn, sizeof(dyn));
- if (need_bswap) {
- elfN(bswap_dyn)(&dyn);
- }
- tag = dyn.d_tag;
- switch (tag) {
- case DT_HASH:
- case DT_SYMTAB:
- case DT_STRTAB:
- case DT_VERDEF:
- case DT_VERSYM:
- case DT_PLTGOT:
- case DT_ADDRRNGLO ... DT_ADDRRNGHI:
- /* These entries store an address in the entry. */
- output_reloc(outf, buf, &target_dyn->d_un.d_val);
- break;
- case DT_NULL:
- case DT_STRSZ:
- case DT_SONAME:
- case DT_DEBUG:
- case DT_FLAGS:
- case DT_FLAGS_1:
- case DT_SYMBOLIC:
- case DT_BIND_NOW:
- case DT_VERDEFNUM:
- case DT_VALRNGLO ... DT_VALRNGHI:
- /* These entries store an integer in the entry. */
- break;
- case DT_SYMENT:
- if (dyn.d_un.d_val != sizeof(ElfN(Sym))) {
- fprintf(stderr, "VDSO has incorrect dynamic symbol size\n");
- errors++;
- }
- break;
- case DT_REL:
- case DT_RELSZ:
- case DT_RELA:
- case DT_RELASZ:
- /*
- * These entries indicate that the VDSO was built incorrectly.
- * It should not have any real relocations.
- * ??? The RISC-V toolchain will emit these even when there
- * are no relocations. Validate zeros.
- */
- if (dyn.d_un.d_val != 0) {
- fprintf(stderr, "VDSO has dynamic relocations\n");
- errors++;
- }
- break;
- case DT_RELENT:
- case DT_RELAENT:
- case DT_TEXTREL:
- /* These entries store an integer in the entry. */
- /* Should not be required; see above. */
- break;
- case DT_NEEDED:
- case DT_VERNEED:
- case DT_PLTREL:
- case DT_JMPREL:
- case DT_RPATH:
- case DT_RUNPATH:
- fprintf(stderr, "VDSO has external dependencies\n");
- errors++;
- break;
- case PT_LOPROC + 3:
- if (ehdr->e_machine == EM_PPC64) {
- break; /* DT_PPC64_OPT: integer bitmask */
- }
- goto do_default;
- default:
- do_default:
- /* This is probably something target specific. */
- fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n",
- (unsigned long)tag);
- errors++;
- break;
- }
- target_dyn++;
- } while (tag != DT_NULL);
- if (errors) {
- exit(EXIT_FAILURE);
- }
- }
- /* Relocate the dynamic symbol table. */
- if (dynsym_idx) {
- ElfN(Sym) *target_sym = buf + shdr[dynsym_idx].sh_offset;
- unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*target_sym);
- for (unsigned i = 0; i < sym_n; ++i) {
- output_reloc(outf, buf, &target_sym[i].st_value);
- }
- }
- /* Search both dynsym and symtab for the signal return symbols. */
- if (dynsym_idx) {
- elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap);
- }
- if (symtab_idx) {
- elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap);
- }
- if (need_bswap) {
- elfN(bswap_ps_hdrs)(buf);
- elfN(bswap_ehdr)(buf);
- }
- }
|