2 * Post-process a vdso elf image for inclusion into qemu.
3 * Elf size specialization.
5 * Copyright 2023 Linaro, Ltd.
7 * SPDX-License-Identifier: GPL-2.0-or-later
10 static void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr)
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 */
27 static void elfN(bswap_phdr)(ElfN(Phdr) *phdr)
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 */
39 static void elfN(bswap_shdr)(ElfN(Shdr) *shdr)
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);
53 static void elfN(bswap_sym)(ElfN(Sym) *sym)
55 bswaps(&sym->st_name);
56 bswaps(&sym->st_value);
57 bswaps(&sym->st_size);
58 bswaps(&sym->st_shndx);
61 static void elfN(bswap_dyn)(ElfN(Dyn) *dyn)
63 bswaps(&dyn->d_tag); /* Dynamic type tag */
64 bswaps(&dyn->d_un.d_ptr); /* Dynamic ptr or val, in union */
67 static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx,
68 void *buf, bool need_bswap)
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;
75 for (unsigned i = 0; i < sym_n; ++i) {
79 elfN(bswap_sym)(sym + i);
81 name = str + sym[i].st_name;
83 if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) {
84 sigreturn_addr = sym[i].st_value;
86 if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) {
87 rt_sigreturn_addr = sym[i].st_value;
92 static void elfN(process)(FILE *outf, void *buf, bool need_bswap)
94 ElfN(Ehdr) *ehdr = buf;
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;
106 elfN(bswap_ehdr)(ehdr);
109 phnum = ehdr->e_phnum;
110 phdr = buf + ehdr->e_phoff;
112 for (unsigned i = 0; i < phnum; ++i) {
113 elfN(bswap_phdr)(phdr + i);
117 shnum = ehdr->e_shnum;
118 shdr = buf + ehdr->e_shoff;
120 for (unsigned i = 0; i < shnum; ++i) {
121 elfN(bswap_shdr)(shdr + i);
124 for (unsigned i = 0; i < shnum; ++i) {
125 switch (shdr[i].sh_type) {
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.
141 for (unsigned i = 0; i < phnum; ++i) {
142 if (phdr[i].p_type != PT_LOAD) {
145 if (first_segsz != 0) {
146 fprintf(stderr, "Multiple LOAD segments\n");
149 if (phdr[i].p_offset != 0) {
150 fprintf(stderr, "LOAD segment does not cover EHDR\n");
153 if (phdr[i].p_vaddr != 0) {
154 fprintf(stderr, "LOAD segment not loaded at address 0\n");
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");
162 if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) {
163 fprintf(stderr, "LOAD segment is not read-write\n");
167 for (unsigned i = 0; i < phnum; ++i) {
170 switch (phdr[i].p_type) {
178 dynamic_ofs = phdr[i].p_offset;
179 dynamic_addr = phdr[i].p_vaddr;
180 which = "PT_DYNAMIC";
185 if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) {
186 fprintf(stderr, "LOAD segment does not cover %s\n", which);
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);
200 /* Relocate the DYNAMIC entries. */
202 ElfN(Dyn) *dyn = buf + dynamic_ofs;
203 __typeof(dyn->d_tag) tag;
208 elfN(bswap_dyn)(dyn);
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);
233 case DT_VALRNGLO ... DT_VALRNGHI:
234 /* These entries store an integer in the entry. */
238 if (dyn->d_un.d_val != sizeof(ElfN(Sym))) {
239 fprintf(stderr, "VDSO has incorrect dynamic symbol size\n");
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.
254 if (dyn->d_un.d_val != 0) {
255 fprintf(stderr, "VDSO has dynamic relocations\n");
262 /* These entries store an integer in the entry. */
263 /* Should not be required; see above. */
272 fprintf(stderr, "VDSO has external dependencies\n");
277 if (ehdr->e_machine == EM_PPC64) {
278 break; /* DT_PPC64_OPT: integer bitmask */
284 /* This is probably something target specific. */
285 fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n",
291 } while (tag != DT_NULL);
297 /* Relocate the dynamic symbol table. */
299 ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset;
300 unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym);
302 for (unsigned i = 0; i < sym_n; ++i) {
303 output_reloc(outf, buf, &sym[i].st_value);
307 /* Search both dynsym and symtab for the signal return symbols. */
309 elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap);
312 elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap);