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