]>
Commit | Line | Data |
---|---|---|
2fa536d1 RH |
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) *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; | |
74 | ||
75 | for (unsigned i = 0; i < sym_n; ++i) { | |
76 | const char *name; | |
77 | ||
78 | if (need_bswap) { | |
79 | elfN(bswap_sym)(sym + i); | |
80 | } | |
81 | name = str + sym[i].st_name; | |
82 | ||
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 | } | |
90 | } | |
91 | ||
92 | static void elfN(process)(FILE *outf, void *buf, bool need_bswap) | |
93 | { | |
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; | |
104 | ||
105 | if (need_bswap) { | |
106 | elfN(bswap_ehdr)(ehdr); | |
107 | } | |
108 | ||
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 | } | |
116 | ||
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 | } | |
134 | ||
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; | |
169 | ||
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 | } | |
193 | ||
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 | } | |
199 | ||
200 | /* Relocate the DYNAMIC entries. */ | |
201 | if (dynamic_addr) { | |
202 | ElfN(Dyn) *dyn = buf + dynamic_ofs; | |
203 | __typeof(dyn->d_tag) tag; | |
204 | ||
205 | do { | |
206 | ||
207 | if (need_bswap) { | |
208 | elfN(bswap_dyn)(dyn); | |
209 | } | |
210 | tag = dyn->d_tag; | |
211 | ||
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; | |
223 | ||
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; | |
236 | ||
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; | |
243 | ||
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; | |
265 | ||
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; | |
275 | ||
e34136d9 RH |
276 | case PT_LOPROC + 3: |
277 | if (ehdr->e_machine == EM_PPC64) { | |
278 | break; /* DT_PPC64_OPT: integer bitmask */ | |
279 | } | |
280 | goto do_default; | |
281 | ||
2fa536d1 | 282 | default: |
e34136d9 | 283 | do_default: |
2fa536d1 RH |
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 | } | |
296 | ||
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); | |
301 | ||
302 | for (unsigned i = 0; i < sym_n; ++i) { | |
303 | output_reloc(outf, buf, &sym[i].st_value); | |
304 | } | |
305 | } | |
306 | ||
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 | } | |
314 | } |