Merge tag 'pull-target-arm-20240325-1' of https://git.linaro.org/people/pmaydell...
[qemu/armbru.git] / linux-user / gen-vdso-elfn.c.inc
blob95856eb839b516085e119f6b368907ebe1f5b886
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  */
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) {
76         const char *name;
78         if (need_bswap) {
79             elfN(bswap_sym)(sym + i);
80         }
81         name = str + sym[i].st_name;
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     }
92 static void elfN(process)(FILE *outf, void *buf, bool need_bswap)
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;
105     if (need_bswap) {
106         elfN(bswap_ehdr)(ehdr);
107     }
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     }
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     }
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;
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     }
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     }
200     /* Relocate the DYNAMIC entries. */
201     if (dynamic_addr) {
202         ElfN(Dyn) *dyn = buf + dynamic_ofs;
203         __typeof(dyn->d_tag) tag;
205         do {
207             if (need_bswap) {
208                 elfN(bswap_dyn)(dyn);
209             }
210             tag = dyn->d_tag;
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;
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;
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;
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;
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;
276             case PT_LOPROC + 3:
277                 if (ehdr->e_machine == EM_PPC64) {
278                     break;  /* DT_PPC64_OPT: integer bitmask */
279                 }
280                 goto do_default;
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     }
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);
302         for (unsigned i = 0; i < sym_n; ++i) {
303             output_reloc(outf, buf, &sym[i].st_value);
304         }
305     }
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     }