 e34136d930
			
		
	
	
		e34136d930
		
	
	
	
	
		
			
			Add support in gen-vdso-elfn.c.inc for the DT_PPC64_OPT dynamic tag: this is an integer, so does not need relocation. Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
		
			
				
	
	
		
			315 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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) *sym = buf + shdr[sym_idx].sh_offset;
 | |
|     unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*sym);
 | |
|     const char *str = buf + shdr[str_idx].sh_offset;
 | |
| 
 | |
|     for (unsigned i = 0; i < sym_n; ++i) {
 | |
|         const char *name;
 | |
| 
 | |
|         if (need_bswap) {
 | |
|             elfN(bswap_sym)(sym + i);
 | |
|         }
 | |
|         name = str + sym[i].st_name;
 | |
| 
 | |
|         if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) {
 | |
|             sigreturn_addr = sym[i].st_value;
 | |
|         }
 | |
|         if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) {
 | |
|             rt_sigreturn_addr = sym[i].st_value;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void elfN(process)(FILE *outf, void *buf, 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)(ehdr);
 | |
|     }
 | |
| 
 | |
|     phnum = ehdr->e_phnum;
 | |
|     phdr = buf + ehdr->e_phoff;
 | |
|     if (need_bswap) {
 | |
|         for (unsigned i = 0; i < phnum; ++i) {
 | |
|             elfN(bswap_phdr)(phdr + i);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     shnum = ehdr->e_shnum;
 | |
|     shdr = buf + ehdr->e_shoff;
 | |
|     if (need_bswap) {
 | |
|         for (unsigned i = 0; i < shnum; ++i) {
 | |
|             elfN(bswap_shdr)(shdr + i);
 | |
|         }
 | |
|     }
 | |
|     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++;
 | |
|         }
 | |
|         first_segsz = phdr[i].p_filesz;
 | |
|         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 DYNAMIC entries. */
 | |
|     if (dynamic_addr) {
 | |
|         ElfN(Dyn) *dyn = buf + dynamic_ofs;
 | |
|         __typeof(dyn->d_tag) tag;
 | |
| 
 | |
|         do {
 | |
| 
 | |
|             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, &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;
 | |
|             }
 | |
|             dyn++;
 | |
|         } while (tag != DT_NULL);
 | |
|         if (errors) {
 | |
|             exit(EXIT_FAILURE);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Relocate the dynamic symbol table. */
 | |
|     if (dynsym_idx) {
 | |
|         ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset;
 | |
|         unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym);
 | |
| 
 | |
|         for (unsigned i = 0; i < sym_n; ++i) {
 | |
|             output_reloc(outf, buf, &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);
 | |
|     }
 | |
| }
 |