qemu
314 строк · 9.6 Кб
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
10static void elfN(bswap_ehdr)(ElfN(Ehdr) *ehdr)
11{
12bswaps(&ehdr->e_type); /* Object file type */
13bswaps(&ehdr->e_machine); /* Architecture */
14bswaps(&ehdr->e_version); /* Object file version */
15bswaps(&ehdr->e_entry); /* Entry point virtual address */
16bswaps(&ehdr->e_phoff); /* Program header table file offset */
17bswaps(&ehdr->e_shoff); /* Section header table file offset */
18bswaps(&ehdr->e_flags); /* Processor-specific flags */
19bswaps(&ehdr->e_ehsize); /* ELF header size in bytes */
20bswaps(&ehdr->e_phentsize); /* Program header table entry size */
21bswaps(&ehdr->e_phnum); /* Program header table entry count */
22bswaps(&ehdr->e_shentsize); /* Section header table entry size */
23bswaps(&ehdr->e_shnum); /* Section header table entry count */
24bswaps(&ehdr->e_shstrndx); /* Section header string table index */
25}
26
27static void elfN(bswap_phdr)(ElfN(Phdr) *phdr)
28{
29bswaps(&phdr->p_type); /* Segment type */
30bswaps(&phdr->p_flags); /* Segment flags */
31bswaps(&phdr->p_offset); /* Segment file offset */
32bswaps(&phdr->p_vaddr); /* Segment virtual address */
33bswaps(&phdr->p_paddr); /* Segment physical address */
34bswaps(&phdr->p_filesz); /* Segment size in file */
35bswaps(&phdr->p_memsz); /* Segment size in memory */
36bswaps(&phdr->p_align); /* Segment alignment */
37}
38
39static void elfN(bswap_shdr)(ElfN(Shdr) *shdr)
40{
41bswaps(&shdr->sh_name);
42bswaps(&shdr->sh_type);
43bswaps(&shdr->sh_flags);
44bswaps(&shdr->sh_addr);
45bswaps(&shdr->sh_offset);
46bswaps(&shdr->sh_size);
47bswaps(&shdr->sh_link);
48bswaps(&shdr->sh_info);
49bswaps(&shdr->sh_addralign);
50bswaps(&shdr->sh_entsize);
51}
52
53static void elfN(bswap_sym)(ElfN(Sym) *sym)
54{
55bswaps(&sym->st_name);
56bswaps(&sym->st_value);
57bswaps(&sym->st_size);
58bswaps(&sym->st_shndx);
59}
60
61static void elfN(bswap_dyn)(ElfN(Dyn) *dyn)
62{
63bswaps(&dyn->d_tag); /* Dynamic type tag */
64bswaps(&dyn->d_un.d_ptr); /* Dynamic ptr or val, in union */
65}
66
67static void elfN(search_symtab)(ElfN(Shdr) *shdr, unsigned sym_idx,
68void *buf, bool need_bswap)
69{
70unsigned str_idx = shdr[sym_idx].sh_link;
71ElfN(Sym) *sym = buf + shdr[sym_idx].sh_offset;
72unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*sym);
73const char *str = buf + shdr[str_idx].sh_offset;
74
75for (unsigned i = 0; i < sym_n; ++i) {
76const char *name;
77
78if (need_bswap) {
79elfN(bswap_sym)(sym + i);
80}
81name = str + sym[i].st_name;
82
83if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) {
84sigreturn_addr = sym[i].st_value;
85}
86if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) {
87rt_sigreturn_addr = sym[i].st_value;
88}
89}
90}
91
92static void elfN(process)(FILE *outf, void *buf, bool need_bswap)
93{
94ElfN(Ehdr) *ehdr = buf;
95ElfN(Phdr) *phdr;
96ElfN(Shdr) *shdr;
97unsigned phnum, shnum;
98unsigned dynamic_ofs = 0;
99unsigned dynamic_addr = 0;
100unsigned symtab_idx = 0;
101unsigned dynsym_idx = 0;
102unsigned first_segsz = 0;
103int errors = 0;
104
105if (need_bswap) {
106elfN(bswap_ehdr)(ehdr);
107}
108
109phnum = ehdr->e_phnum;
110phdr = buf + ehdr->e_phoff;
111if (need_bswap) {
112for (unsigned i = 0; i < phnum; ++i) {
113elfN(bswap_phdr)(phdr + i);
114}
115}
116
117shnum = ehdr->e_shnum;
118shdr = buf + ehdr->e_shoff;
119if (need_bswap) {
120for (unsigned i = 0; i < shnum; ++i) {
121elfN(bswap_shdr)(shdr + i);
122}
123}
124for (unsigned i = 0; i < shnum; ++i) {
125switch (shdr[i].sh_type) {
126case SHT_SYMTAB:
127symtab_idx = i;
128break;
129case SHT_DYNSYM:
130dynsym_idx = i;
131break;
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*/
141for (unsigned i = 0; i < phnum; ++i) {
142if (phdr[i].p_type != PT_LOAD) {
143continue;
144}
145if (first_segsz != 0) {
146fprintf(stderr, "Multiple LOAD segments\n");
147errors++;
148}
149if (phdr[i].p_offset != 0) {
150fprintf(stderr, "LOAD segment does not cover EHDR\n");
151errors++;
152}
153if (phdr[i].p_vaddr != 0) {
154fprintf(stderr, "LOAD segment not loaded at address 0\n");
155errors++;
156}
157first_segsz = phdr[i].p_filesz;
158if (first_segsz < ehdr->e_phoff + phnum * sizeof(*phdr)) {
159fprintf(stderr, "LOAD segment does not cover PHDRs\n");
160errors++;
161}
162if ((phdr[i].p_flags & (PF_R | PF_W)) != (PF_R | PF_W)) {
163fprintf(stderr, "LOAD segment is not read-write\n");
164errors++;
165}
166}
167for (unsigned i = 0; i < phnum; ++i) {
168const char *which;
169
170switch (phdr[i].p_type) {
171case PT_PHDR:
172which = "PT_PHDR";
173break;
174case PT_NOTE:
175which = "PT_NOTE";
176break;
177case PT_DYNAMIC:
178dynamic_ofs = phdr[i].p_offset;
179dynamic_addr = phdr[i].p_vaddr;
180which = "PT_DYNAMIC";
181break;
182default:
183continue;
184}
185if (first_segsz < phdr[i].p_vaddr + phdr[i].p_filesz) {
186fprintf(stderr, "LOAD segment does not cover %s\n", which);
187errors++;
188}
189}
190if (errors) {
191exit(EXIT_FAILURE);
192}
193
194/* Relocate the program headers. */
195for (unsigned i = 0; i < phnum; ++i) {
196output_reloc(outf, buf, &phdr[i].p_vaddr);
197output_reloc(outf, buf, &phdr[i].p_paddr);
198}
199
200/* Relocate the DYNAMIC entries. */
201if (dynamic_addr) {
202ElfN(Dyn) *dyn = buf + dynamic_ofs;
203__typeof(dyn->d_tag) tag;
204
205do {
206
207if (need_bswap) {
208elfN(bswap_dyn)(dyn);
209}
210tag = dyn->d_tag;
211
212switch (tag) {
213case DT_HASH:
214case DT_SYMTAB:
215case DT_STRTAB:
216case DT_VERDEF:
217case DT_VERSYM:
218case DT_PLTGOT:
219case DT_ADDRRNGLO ... DT_ADDRRNGHI:
220/* These entries store an address in the entry. */
221output_reloc(outf, buf, &dyn->d_un.d_val);
222break;
223
224case DT_NULL:
225case DT_STRSZ:
226case DT_SONAME:
227case DT_DEBUG:
228case DT_FLAGS:
229case DT_FLAGS_1:
230case DT_SYMBOLIC:
231case DT_BIND_NOW:
232case DT_VERDEFNUM:
233case DT_VALRNGLO ... DT_VALRNGHI:
234/* These entries store an integer in the entry. */
235break;
236
237case DT_SYMENT:
238if (dyn->d_un.d_val != sizeof(ElfN(Sym))) {
239fprintf(stderr, "VDSO has incorrect dynamic symbol size\n");
240errors++;
241}
242break;
243
244case DT_REL:
245case DT_RELSZ:
246case DT_RELA:
247case 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*/
254if (dyn->d_un.d_val != 0) {
255fprintf(stderr, "VDSO has dynamic relocations\n");
256errors++;
257}
258break;
259case DT_RELENT:
260case DT_RELAENT:
261case DT_TEXTREL:
262/* These entries store an integer in the entry. */
263/* Should not be required; see above. */
264break;
265
266case DT_NEEDED:
267case DT_VERNEED:
268case DT_PLTREL:
269case DT_JMPREL:
270case DT_RPATH:
271case DT_RUNPATH:
272fprintf(stderr, "VDSO has external dependencies\n");
273errors++;
274break;
275
276case PT_LOPROC + 3:
277if (ehdr->e_machine == EM_PPC64) {
278break; /* DT_PPC64_OPT: integer bitmask */
279}
280goto do_default;
281
282default:
283do_default:
284/* This is probably something target specific. */
285fprintf(stderr, "VDSO has unknown DYNAMIC entry (%lx)\n",
286(unsigned long)tag);
287errors++;
288break;
289}
290dyn++;
291} while (tag != DT_NULL);
292if (errors) {
293exit(EXIT_FAILURE);
294}
295}
296
297/* Relocate the dynamic symbol table. */
298if (dynsym_idx) {
299ElfN(Sym) *sym = buf + shdr[dynsym_idx].sh_offset;
300unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*sym);
301
302for (unsigned i = 0; i < sym_n; ++i) {
303output_reloc(outf, buf, &sym[i].st_value);
304}
305}
306
307/* Search both dynsym and symtab for the signal return symbols. */
308if (dynsym_idx) {
309elfN(search_symtab)(shdr, dynsym_idx, buf, need_bswap);
310}
311if (symtab_idx) {
312elfN(search_symtab)(shdr, symtab_idx, buf, need_bswap);
313}
314}
315