direct-io.hg

view xen/common/elf.c @ 13505:1e5ad8ccb286

[XEN] Avoid void* arithmetic in elfnote code.
From: Christoph Egger <Christoph.Egger@amd.com>
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Fri Jan 19 15:03:02 2007 +0000 (2007-01-19)
parents 204a4a040a42
children d86a96ca47a3
line source
1 /******************************************************************************
2 * elf.c
3 *
4 * Generic Elf-loading routines.
5 */
7 #include <xen/config.h>
8 #include <xen/init.h>
9 #include <xen/lib.h>
10 #include <xen/mm.h>
11 #include <xen/elf.h>
12 #include <xen/sched.h>
13 #include <xen/errno.h>
14 #include <xen/inttypes.h>
16 #include <public/elfnote.h>
18 static void loadelfsymtab(struct domain_setup_info *dsi, int doload);
19 static inline int is_loadable_phdr(const Elf_Phdr *phdr)
20 {
21 return ((phdr->p_type == PT_LOAD) &&
22 ((phdr->p_flags & (PF_W|PF_X)) != 0));
23 }
25 /*
26 * Fallback for kernels containing only the legacy __xen_guest string
27 * and no ELF notes.
28 */
29 static int is_xen_guest_section(const Elf_Shdr *shdr, const char *shstrtab)
30 {
31 return strcmp(&shstrtab[shdr->sh_name], "__xen_guest") == 0;
32 }
34 static const char *xen_guest_lookup(struct domain_setup_info *dsi, int type)
35 {
36 const char *xenguest_fallbacks[] = {
37 [XEN_ELFNOTE_ENTRY] = "VIRT_ENTRY=",
38 [XEN_ELFNOTE_HYPERCALL_PAGE] = "HYPERCALL_PAGE=",
39 [XEN_ELFNOTE_VIRT_BASE] = "VIRT_BASE=",
40 [XEN_ELFNOTE_PADDR_OFFSET] = "ELF_PADDR_OFFSET=",
41 [XEN_ELFNOTE_XEN_VERSION] = "XEN_VER=",
42 [XEN_ELFNOTE_GUEST_OS] = "GUEST_OS=",
43 [XEN_ELFNOTE_GUEST_VERSION] = "GUEST_VER=",
44 [XEN_ELFNOTE_LOADER] = "LOADER=",
45 [XEN_ELFNOTE_PAE_MODE] = "PAE=",
46 [XEN_ELFNOTE_FEATURES] = "FEATURES=",
47 [XEN_ELFNOTE_BSD_SYMTAB] = "BSD_SYMTAB=",
48 };
49 const char *fallback;
50 const char *p;
52 if ( !dsi->__xen_guest_string )
53 return NULL;
55 if ( type > sizeof(xenguest_fallbacks) )
56 return NULL;
58 if ( (fallback = xenguest_fallbacks[type]) == NULL )
59 return NULL;
61 if ( (p = strstr(dsi->__xen_guest_string,fallback)) == NULL )
62 return NULL;
64 return p + strlen(fallback);
65 }
67 static const char *xen_guest_string(struct domain_setup_info *dsi, int type)
68 {
69 const char *p = xen_guest_lookup(dsi, type);
71 /*
72 * We special case this since the __xen_guest_section treats the
73 * mere precense of the BSD_SYMTAB string as true or false.
74 */
75 if ( type == XEN_ELFNOTE_BSD_SYMTAB )
76 return p ? "yes" : "no";
78 return p;
79 }
81 static unsigned long long xen_guest_numeric(struct domain_setup_info *dsi,
82 int type, int *defined)
83 {
84 const char *p = xen_guest_lookup(dsi, type);
85 unsigned long long value;
87 if ( p == NULL )
88 return 0;
90 value = simple_strtoull(p, NULL, 0);
92 /* We special case this since __xen_guest_section contains a PFN
93 * for this field not a virtual address.
94 */
95 if (type == XEN_ELFNOTE_HYPERCALL_PAGE)
96 value = dsi->v_start + (value<<PAGE_SHIFT);
98 *defined = 1;
99 return value;
100 }
102 /*
103 * Interface to the Xen ELF notes.
104 */
105 #define ELFNOTE_NAME(_n_) ((const char*)(_n_) + sizeof(*(_n_)))
106 #define ELFNOTE_DESC(_n_) (ELFNOTE_NAME(_n_) + (((_n_)->namesz+3)&~3))
107 #define ELFNOTE_NEXT(_n_) (ELFNOTE_DESC(_n_) + (((_n_)->descsz+3)&~3))
109 static int is_xen_elfnote_section(const char *image, const Elf_Shdr *shdr)
110 {
111 const Elf_Note *note;
113 if ( shdr->sh_type != SHT_NOTE )
114 return 0;
116 for ( note = (const Elf_Note *)(image + shdr->sh_offset);
117 note < (const Elf_Note *)(image + shdr->sh_offset + shdr->sh_size);
118 note = (const Elf_Note *)ELFNOTE_NEXT(note) )
119 {
120 if ( !strncmp(ELFNOTE_NAME(note), "Xen", 4) )
121 return 1;
122 }
124 return 0;
125 }
127 static const Elf_Note *xen_elfnote_lookup(
128 struct domain_setup_info *dsi, int type)
129 {
130 const Elf_Note *note;
132 if ( !dsi->__elfnote_section )
133 return NULL;
135 for ( note = (const Elf_Note *)dsi->__elfnote_section;
136 note < (const Elf_Note *)dsi->__elfnote_section_end;
137 note = (const Elf_Note *)ELFNOTE_NEXT(note) )
138 {
139 if ( strncmp(ELFNOTE_NAME(note), "Xen", 4) )
140 continue;
142 if ( note->type == type )
143 return note;
144 }
146 return NULL;
147 }
149 const char *xen_elfnote_string(struct domain_setup_info *dsi, int type)
150 {
151 const Elf_Note *note;
153 if ( !dsi->__elfnote_section )
154 return xen_guest_string(dsi, type);
156 note = xen_elfnote_lookup(dsi, type);
157 if ( note == NULL )
158 return NULL;
160 return (const char *)ELFNOTE_DESC(note);
161 }
163 unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi,
164 int type, int *defined)
165 {
166 const Elf_Note *note;
168 *defined = 0;
170 if ( !dsi->__elfnote_section )
171 return xen_guest_numeric(dsi, type, defined);
173 note = xen_elfnote_lookup(dsi, type);
174 if ( note == NULL )
175 {
176 return 0;
177 }
179 switch ( note->descsz )
180 {
181 case 4:
182 *defined = 1;
183 return *(const uint32_t*)ELFNOTE_DESC(note);
184 case 8:
185 *defined = 1;
186 return *(const uint64_t*)ELFNOTE_DESC(note);
187 default:
188 printk("ERROR: unknown data size %#x for numeric type note %#x\n",
189 note->descsz, type);
190 return 0;
191 }
192 }
194 int parseelfimage(struct domain_setup_info *dsi)
195 {
196 const Elf_Ehdr *ehdr = (const Elf_Ehdr *)dsi->image_addr;
197 const Elf_Phdr *phdr;
198 const Elf_Shdr *shdr;
199 Elf_Addr kernstart = ~0, kernend = 0, vaddr, virt_entry;
200 const char *shstrtab, *p;
201 const char *image = (char *)dsi->image_addr;
202 const unsigned long image_len = dsi->image_len;
203 int h, virt_base_defined, elf_pa_off_defined, virt_entry_defined;
205 if ( !elf_sanity_check(ehdr) )
206 return -ENOSYS;
208 if ( (ehdr->e_phoff + (ehdr->e_phnum*ehdr->e_phentsize)) > image_len )
209 {
210 printk("ELF program headers extend beyond end of image.\n");
211 return -EINVAL;
212 }
214 if ( (ehdr->e_shoff + (ehdr->e_shnum*ehdr->e_shentsize)) > image_len )
215 {
216 printk("ELF section headers extend beyond end of image.\n");
217 return -EINVAL;
218 }
220 dsi->__elfnote_section = NULL;
221 dsi->__xen_guest_string = NULL;
223 /* Look for .notes segment containing at least one Xen note */
224 for ( h = 0; h < ehdr->e_shnum; h++ )
225 {
226 shdr = (const Elf_Shdr *)(
227 image + ehdr->e_shoff + (h*ehdr->e_shentsize));
228 if ( !is_xen_elfnote_section(image, shdr) )
229 continue;
230 dsi->__elfnote_section = (const char *)image + shdr->sh_offset;
231 dsi->__elfnote_section_end =
232 (const char *)image + shdr->sh_offset + shdr->sh_size;
233 break;
234 }
236 /* Fall back to looking for the special '__xen_guest' section. */
237 if ( dsi->__elfnote_section == NULL )
238 {
239 /* Find the section-header strings table. */
240 if ( ehdr->e_shstrndx == SHN_UNDEF )
241 {
242 printk("ELF image has no section-header strings table.\n");
243 return -EINVAL;
244 }
245 shdr = (const Elf_Shdr *)(image + ehdr->e_shoff +
246 (ehdr->e_shstrndx*ehdr->e_shentsize));
247 shstrtab = image + shdr->sh_offset;
249 for ( h = 0; h < ehdr->e_shnum; h++ )
250 {
251 shdr = (const Elf_Shdr *)(
252 image + ehdr->e_shoff + (h*ehdr->e_shentsize));
253 if ( is_xen_guest_section(shdr, shstrtab) )
254 {
255 dsi->__xen_guest_string =
256 (const char *)image + shdr->sh_offset;
257 break;
258 }
259 }
260 }
262 /* Check the contents of the Xen notes or guest string. */
263 if ( dsi->__elfnote_section || dsi->__xen_guest_string )
264 {
265 const char *loader = xen_elfnote_string(dsi, XEN_ELFNOTE_LOADER);
266 const char *guest_os = xen_elfnote_string(dsi, XEN_ELFNOTE_GUEST_OS);
267 const char *xen_version =
268 xen_elfnote_string(dsi, XEN_ELFNOTE_XEN_VERSION);
270 if ( ( loader == NULL || strncmp(loader, "generic", 7) ) &&
271 ( guest_os == NULL || strncmp(guest_os, "linux", 5) ) )
272 {
273 printk("ERROR: Will only load images built for the generic "
274 "loader or Linux images");
275 return -EINVAL;
276 }
278 if ( xen_version == NULL || strncmp(xen_version, "xen-3.0", 7) )
279 {
280 printk("ERROR: Xen will only load images built for Xen v3.0\n");
281 }
282 }
283 else
284 {
285 #if defined(__x86_64__) || defined(__i386__)
286 printk("ERROR: Not a Xen-ELF image: "
287 "No ELF notes or '__xen_guest' section found.\n");
288 return -EINVAL;
289 #endif
290 }
292 /*
293 * A "bimodal" ELF note indicates the kernel will adjust to the
294 * current paging mode, including handling extended cr3 syntax.
295 * If we have ELF notes then PAE=yes implies that we must support
296 * the extended cr3 syntax. Otherwise we need to find the
297 * [extended-cr3] syntax in the __xen_guest string.
298 */
299 dsi->pae_kernel = PAEKERN_no;
300 if ( dsi->__elfnote_section )
301 {
302 p = xen_elfnote_string(dsi, XEN_ELFNOTE_PAE_MODE);
303 if ( p != NULL && strstr(p, "bimodal") != NULL )
304 dsi->pae_kernel = PAEKERN_bimodal;
305 else if ( p != NULL && strncmp(p, "yes", 3) == 0 )
306 dsi->pae_kernel = PAEKERN_extended_cr3;
307 }
308 else
309 {
310 p = xen_guest_lookup(dsi, XEN_ELFNOTE_PAE_MODE);
311 if ( p != NULL && strncmp(p, "yes", 3) == 0 )
312 {
313 dsi->pae_kernel = PAEKERN_yes;
314 if ( !strncmp(p+3, "[extended-cr3]", 14) )
315 dsi->pae_kernel = PAEKERN_extended_cr3;
316 }
317 }
319 /* Initial guess for v_start is 0 if it is not explicitly defined. */
320 dsi->v_start =
321 xen_elfnote_numeric(dsi, XEN_ELFNOTE_VIRT_BASE, &virt_base_defined);
322 if ( !virt_base_defined )
323 dsi->v_start = 0;
325 /*
326 * If we are using the legacy __xen_guest section then elf_pa_off
327 * defaults to v_start in order to maintain compatibility with
328 * older hypervisors which set padd in the ELF header to
329 * virt_base.
330 *
331 * If we are using the modern ELF notes interface then the default
332 * is 0.
333 */
334 dsi->elf_paddr_offset = xen_elfnote_numeric(dsi, XEN_ELFNOTE_PADDR_OFFSET,
335 &elf_pa_off_defined);
336 if ( !elf_pa_off_defined )
337 {
338 if ( dsi->__elfnote_section )
339 dsi->elf_paddr_offset = 0;
340 else
341 dsi->elf_paddr_offset = dsi->v_start;
342 }
344 if ( elf_pa_off_defined && !virt_base_defined )
345 {
346 printk("ERROR: Neither ELF_PADDR_OFFSET nor VIRT_BASE found in"
347 " Xen ELF notes.\n");
348 return -EINVAL;
349 }
351 for ( h = 0; h < ehdr->e_phnum; h++ )
352 {
353 phdr = (const Elf_Phdr *)(
354 image + ehdr->e_phoff + (h*ehdr->e_phentsize));
355 if ( !is_loadable_phdr(phdr) )
356 continue;
357 vaddr = phdr->p_paddr - dsi->elf_paddr_offset + dsi->v_start;
358 if ( (vaddr + phdr->p_memsz) < vaddr )
359 {
360 printk("ERROR: ELF program header %d is too large.\n", h);
361 return -EINVAL;
362 }
364 if ( vaddr < kernstart )
365 kernstart = vaddr;
366 if ( (vaddr + phdr->p_memsz) > kernend )
367 kernend = vaddr + phdr->p_memsz;
368 }
370 dsi->v_kernentry = ehdr->e_entry;
372 virt_entry =
373 xen_elfnote_numeric(dsi, XEN_ELFNOTE_ENTRY, &virt_entry_defined);
374 if ( virt_entry_defined )
375 dsi->v_kernentry = virt_entry;
377 if ( (kernstart > kernend) ||
378 (dsi->v_kernentry < kernstart) ||
379 (dsi->v_kernentry > kernend) ||
380 (dsi->v_start > kernstart) )
381 {
382 printk("ERROR: ELF start or entries are out of bounds.\n");
383 return -EINVAL;
384 }
386 p = xen_elfnote_string(dsi, XEN_ELFNOTE_BSD_SYMTAB);
387 if ( p != NULL && strncmp(p, "yes", 3) == 0 )
388 dsi->load_symtab = 1;
390 dsi->v_kernstart = kernstart;
391 dsi->v_kernend = kernend;
392 dsi->v_end = dsi->v_kernend;
394 loadelfsymtab(dsi, 0);
396 return 0;
397 }
399 int loadelfimage(struct domain_setup_info *dsi)
400 {
401 char *image = (char *)dsi->image_addr;
402 Elf_Ehdr *ehdr = (Elf_Ehdr *)dsi->image_addr;
403 Elf_Phdr *phdr;
404 unsigned long vaddr;
405 int h;
407 for ( h = 0; h < ehdr->e_phnum; h++ )
408 {
409 phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
410 if ( !is_loadable_phdr(phdr) )
411 continue;
412 vaddr = phdr->p_paddr - dsi->elf_paddr_offset + dsi->v_start;
413 if ( phdr->p_filesz != 0 )
414 memcpy((char *)vaddr, image + phdr->p_offset, phdr->p_filesz);
415 if ( phdr->p_memsz > phdr->p_filesz )
416 memset((char *)vaddr + phdr->p_filesz, 0,
417 phdr->p_memsz - phdr->p_filesz);
418 }
420 loadelfsymtab(dsi, 1);
422 return 0;
423 }
425 #define ELFROUND (ELFSIZE / 8)
427 static void loadelfsymtab(struct domain_setup_info *dsi, int doload)
428 {
429 Elf_Ehdr *ehdr = (Elf_Ehdr *)dsi->image_addr, *sym_ehdr;
430 Elf_Shdr *shdr;
431 unsigned long maxva, symva;
432 char *p, *image = (char *)dsi->image_addr;
433 int h, i;
435 if ( !dsi->load_symtab )
436 return;
438 maxva = (dsi->v_kernend + ELFROUND - 1) & ~(ELFROUND - 1);
439 symva = maxva;
440 maxva += sizeof(int);
441 dsi->symtab_addr = maxva;
442 dsi->symtab_len = 0;
443 maxva += sizeof(Elf_Ehdr) + ehdr->e_shnum * sizeof(Elf_Shdr);
444 maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
445 if ( doload )
446 {
447 p = (void *)symva;
448 shdr = (Elf_Shdr *)(p + sizeof(int) + sizeof(Elf_Ehdr));
449 memcpy(shdr, image + ehdr->e_shoff, ehdr->e_shnum*sizeof(Elf_Shdr));
450 }
451 else
452 {
453 p = NULL;
454 shdr = (Elf_Shdr *)(image + ehdr->e_shoff);
455 }
457 for ( h = 0; h < ehdr->e_shnum; h++ )
458 {
459 if ( shdr[h].sh_type == SHT_STRTAB )
460 {
461 /* Look for a strtab @i linked to symtab @h. */
462 for ( i = 0; i < ehdr->e_shnum; i++ )
463 if ( (shdr[i].sh_type == SHT_SYMTAB) &&
464 (shdr[i].sh_link == h) )
465 break;
466 /* Skip symtab @h if we found no corresponding strtab @i. */
467 if ( i == ehdr->e_shnum )
468 {
469 if (doload) {
470 shdr[h].sh_offset = 0;
471 }
472 continue;
473 }
474 }
476 if ( (shdr[h].sh_type == SHT_STRTAB) ||
477 (shdr[h].sh_type == SHT_SYMTAB) )
478 {
479 if (doload) {
480 memcpy((void *)maxva, image + shdr[h].sh_offset,
481 shdr[h].sh_size);
483 /* Mangled to be based on ELF header location. */
484 shdr[h].sh_offset = maxva - dsi->symtab_addr;
486 }
487 dsi->symtab_len += shdr[h].sh_size;
488 maxva += shdr[h].sh_size;
489 maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
490 }
492 if ( doload )
493 shdr[h].sh_name = 0; /* Name is NULL. */
494 }
496 if ( dsi->symtab_len == 0 )
497 {
498 dsi->symtab_addr = 0;
499 return;
500 }
502 if ( doload )
503 {
504 *(int *)p = maxva - dsi->symtab_addr;
505 sym_ehdr = (Elf_Ehdr *)(p + sizeof(int));
506 memcpy(sym_ehdr, ehdr, sizeof(Elf_Ehdr));
507 sym_ehdr->e_phoff = 0;
508 sym_ehdr->e_shoff = sizeof(Elf_Ehdr);
509 sym_ehdr->e_phentsize = 0;
510 sym_ehdr->e_phnum = 0;
511 sym_ehdr->e_shstrndx = SHN_UNDEF;
512 }
514 dsi->symtab_len = maxva - dsi->symtab_addr;
515 dsi->v_end = maxva;
516 }
518 /*
519 * Local variables:
520 * mode: C
521 * c-set-style: "BSD"
522 * c-basic-offset: 4
523 * tab-width: 4
524 * indent-tabs-mode: nil
525 * End:
526 */