diff options
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/vmcore.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 8ad467855845..3b2e7b69e63a 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c | |||
@@ -202,6 +202,23 @@ static u64 __init get_vmcore_size_elf64(char *elfptr) | |||
202 | return size; | 202 | return size; |
203 | } | 203 | } |
204 | 204 | ||
205 | static u64 __init get_vmcore_size_elf32(char *elfptr) | ||
206 | { | ||
207 | int i; | ||
208 | u64 size; | ||
209 | Elf32_Ehdr *ehdr_ptr; | ||
210 | Elf32_Phdr *phdr_ptr; | ||
211 | |||
212 | ehdr_ptr = (Elf32_Ehdr *)elfptr; | ||
213 | phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); | ||
214 | size = sizeof(Elf32_Ehdr) + ((ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr)); | ||
215 | for (i = 0; i < ehdr_ptr->e_phnum; i++) { | ||
216 | size += phdr_ptr->p_memsz; | ||
217 | phdr_ptr++; | ||
218 | } | ||
219 | return size; | ||
220 | } | ||
221 | |||
205 | /* Merges all the PT_NOTE headers into one. */ | 222 | /* Merges all the PT_NOTE headers into one. */ |
206 | static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, | 223 | static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, |
207 | struct list_head *vc_list) | 224 | struct list_head *vc_list) |
@@ -283,6 +300,87 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, | |||
283 | return 0; | 300 | return 0; |
284 | } | 301 | } |
285 | 302 | ||
303 | /* Merges all the PT_NOTE headers into one. */ | ||
304 | static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, | ||
305 | struct list_head *vc_list) | ||
306 | { | ||
307 | int i, nr_ptnote=0, rc=0; | ||
308 | char *tmp; | ||
309 | Elf32_Ehdr *ehdr_ptr; | ||
310 | Elf32_Phdr phdr, *phdr_ptr; | ||
311 | Elf32_Nhdr *nhdr_ptr; | ||
312 | u64 phdr_sz = 0, note_off; | ||
313 | |||
314 | ehdr_ptr = (Elf32_Ehdr *)elfptr; | ||
315 | phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); | ||
316 | for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { | ||
317 | int j; | ||
318 | void *notes_section; | ||
319 | struct vmcore *new; | ||
320 | u64 offset, max_sz, sz, real_sz = 0; | ||
321 | if (phdr_ptr->p_type != PT_NOTE) | ||
322 | continue; | ||
323 | nr_ptnote++; | ||
324 | max_sz = phdr_ptr->p_memsz; | ||
325 | offset = phdr_ptr->p_offset; | ||
326 | notes_section = kmalloc(max_sz, GFP_KERNEL); | ||
327 | if (!notes_section) | ||
328 | return -ENOMEM; | ||
329 | rc = read_from_oldmem(notes_section, max_sz, &offset, 0); | ||
330 | if (rc < 0) { | ||
331 | kfree(notes_section); | ||
332 | return rc; | ||
333 | } | ||
334 | nhdr_ptr = notes_section; | ||
335 | for (j = 0; j < max_sz; j += sz) { | ||
336 | if (nhdr_ptr->n_namesz == 0) | ||
337 | break; | ||
338 | sz = sizeof(Elf32_Nhdr) + | ||
339 | ((nhdr_ptr->n_namesz + 3) & ~3) + | ||
340 | ((nhdr_ptr->n_descsz + 3) & ~3); | ||
341 | real_sz += sz; | ||
342 | nhdr_ptr = (Elf32_Nhdr*)((char*)nhdr_ptr + sz); | ||
343 | } | ||
344 | |||
345 | /* Add this contiguous chunk of notes section to vmcore list.*/ | ||
346 | new = get_new_element(); | ||
347 | if (!new) { | ||
348 | kfree(notes_section); | ||
349 | return -ENOMEM; | ||
350 | } | ||
351 | new->paddr = phdr_ptr->p_offset; | ||
352 | new->size = real_sz; | ||
353 | list_add_tail(&new->list, vc_list); | ||
354 | phdr_sz += real_sz; | ||
355 | kfree(notes_section); | ||
356 | } | ||
357 | |||
358 | /* Prepare merged PT_NOTE program header. */ | ||
359 | phdr.p_type = PT_NOTE; | ||
360 | phdr.p_flags = 0; | ||
361 | note_off = sizeof(Elf32_Ehdr) + | ||
362 | (ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf32_Phdr); | ||
363 | phdr.p_offset = note_off; | ||
364 | phdr.p_vaddr = phdr.p_paddr = 0; | ||
365 | phdr.p_filesz = phdr.p_memsz = phdr_sz; | ||
366 | phdr.p_align = 0; | ||
367 | |||
368 | /* Add merged PT_NOTE program header*/ | ||
369 | tmp = elfptr + sizeof(Elf32_Ehdr); | ||
370 | memcpy(tmp, &phdr, sizeof(phdr)); | ||
371 | tmp += sizeof(phdr); | ||
372 | |||
373 | /* Remove unwanted PT_NOTE program headers. */ | ||
374 | i = (nr_ptnote - 1) * sizeof(Elf32_Phdr); | ||
375 | *elfsz = *elfsz - i; | ||
376 | memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf32_Ehdr)-sizeof(Elf32_Phdr))); | ||
377 | |||
378 | /* Modify e_phnum to reflect merged headers. */ | ||
379 | ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1; | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | |||
286 | /* Add memory chunks represented by program headers to vmcore list. Also update | 384 | /* Add memory chunks represented by program headers to vmcore list. Also update |
287 | * the new offset fields of exported program headers. */ | 385 | * the new offset fields of exported program headers. */ |
288 | static int __init process_ptload_program_headers_elf64(char *elfptr, | 386 | static int __init process_ptload_program_headers_elf64(char *elfptr, |
@@ -322,6 +420,43 @@ static int __init process_ptload_program_headers_elf64(char *elfptr, | |||
322 | return 0; | 420 | return 0; |
323 | } | 421 | } |
324 | 422 | ||
423 | static int __init process_ptload_program_headers_elf32(char *elfptr, | ||
424 | size_t elfsz, | ||
425 | struct list_head *vc_list) | ||
426 | { | ||
427 | int i; | ||
428 | Elf32_Ehdr *ehdr_ptr; | ||
429 | Elf32_Phdr *phdr_ptr; | ||
430 | loff_t vmcore_off; | ||
431 | struct vmcore *new; | ||
432 | |||
433 | ehdr_ptr = (Elf32_Ehdr *)elfptr; | ||
434 | phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); /* PT_NOTE hdr */ | ||
435 | |||
436 | /* First program header is PT_NOTE header. */ | ||
437 | vmcore_off = sizeof(Elf32_Ehdr) + | ||
438 | (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr) + | ||
439 | phdr_ptr->p_memsz; /* Note sections */ | ||
440 | |||
441 | for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { | ||
442 | if (phdr_ptr->p_type != PT_LOAD) | ||
443 | continue; | ||
444 | |||
445 | /* Add this contiguous chunk of memory to vmcore list.*/ | ||
446 | new = get_new_element(); | ||
447 | if (!new) | ||
448 | return -ENOMEM; | ||
449 | new->paddr = phdr_ptr->p_offset; | ||
450 | new->size = phdr_ptr->p_memsz; | ||
451 | list_add_tail(&new->list, vc_list); | ||
452 | |||
453 | /* Update the program header offset */ | ||
454 | phdr_ptr->p_offset = vmcore_off; | ||
455 | vmcore_off = vmcore_off + phdr_ptr->p_memsz; | ||
456 | } | ||
457 | return 0; | ||
458 | } | ||
459 | |||
325 | /* Sets offset fields of vmcore elements. */ | 460 | /* Sets offset fields of vmcore elements. */ |
326 | static void __init set_vmcore_list_offsets_elf64(char *elfptr, | 461 | static void __init set_vmcore_list_offsets_elf64(char *elfptr, |
327 | struct list_head *vc_list) | 462 | struct list_head *vc_list) |
@@ -342,6 +477,26 @@ static void __init set_vmcore_list_offsets_elf64(char *elfptr, | |||
342 | } | 477 | } |
343 | } | 478 | } |
344 | 479 | ||
480 | /* Sets offset fields of vmcore elements. */ | ||
481 | static void __init set_vmcore_list_offsets_elf32(char *elfptr, | ||
482 | struct list_head *vc_list) | ||
483 | { | ||
484 | loff_t vmcore_off; | ||
485 | Elf32_Ehdr *ehdr_ptr; | ||
486 | struct vmcore *m; | ||
487 | |||
488 | ehdr_ptr = (Elf32_Ehdr *)elfptr; | ||
489 | |||
490 | /* Skip Elf header and program headers. */ | ||
491 | vmcore_off = sizeof(Elf32_Ehdr) + | ||
492 | (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr); | ||
493 | |||
494 | list_for_each_entry(m, vc_list, list) { | ||
495 | m->offset = vmcore_off; | ||
496 | vmcore_off += m->size; | ||
497 | } | ||
498 | } | ||
499 | |||
345 | static int __init parse_crash_elf64_headers(void) | 500 | static int __init parse_crash_elf64_headers(void) |
346 | { | 501 | { |
347 | int rc=0; | 502 | int rc=0; |
@@ -398,6 +553,62 @@ static int __init parse_crash_elf64_headers(void) | |||
398 | return 0; | 553 | return 0; |
399 | } | 554 | } |
400 | 555 | ||
556 | static int __init parse_crash_elf32_headers(void) | ||
557 | { | ||
558 | int rc=0; | ||
559 | Elf32_Ehdr ehdr; | ||
560 | u64 addr; | ||
561 | |||
562 | addr = elfcorehdr_addr; | ||
563 | |||
564 | /* Read Elf header */ | ||
565 | rc = read_from_oldmem((char*)&ehdr, sizeof(Elf32_Ehdr), &addr, 0); | ||
566 | if (rc < 0) | ||
567 | return rc; | ||
568 | |||
569 | /* Do some basic Verification. */ | ||
570 | if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 || | ||
571 | (ehdr.e_type != ET_CORE) || | ||
572 | !elf_check_arch(&ehdr) || | ||
573 | ehdr.e_ident[EI_CLASS] != ELFCLASS32|| | ||
574 | ehdr.e_ident[EI_VERSION] != EV_CURRENT || | ||
575 | ehdr.e_version != EV_CURRENT || | ||
576 | ehdr.e_ehsize != sizeof(Elf32_Ehdr) || | ||
577 | ehdr.e_phentsize != sizeof(Elf32_Phdr) || | ||
578 | ehdr.e_phnum == 0) { | ||
579 | printk(KERN_WARNING "Warning: Core image elf header is not" | ||
580 | "sane\n"); | ||
581 | return -EINVAL; | ||
582 | } | ||
583 | |||
584 | /* Read in all elf headers. */ | ||
585 | elfcorebuf_sz = sizeof(Elf32_Ehdr) + ehdr.e_phnum * sizeof(Elf32_Phdr); | ||
586 | elfcorebuf = kmalloc(elfcorebuf_sz, GFP_KERNEL); | ||
587 | if (!elfcorebuf) | ||
588 | return -ENOMEM; | ||
589 | addr = elfcorehdr_addr; | ||
590 | rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz, &addr, 0); | ||
591 | if (rc < 0) { | ||
592 | kfree(elfcorebuf); | ||
593 | return rc; | ||
594 | } | ||
595 | |||
596 | /* Merge all PT_NOTE headers into one. */ | ||
597 | rc = merge_note_headers_elf32(elfcorebuf, &elfcorebuf_sz, &vmcore_list); | ||
598 | if (rc) { | ||
599 | kfree(elfcorebuf); | ||
600 | return rc; | ||
601 | } | ||
602 | rc = process_ptload_program_headers_elf32(elfcorebuf, elfcorebuf_sz, | ||
603 | &vmcore_list); | ||
604 | if (rc) { | ||
605 | kfree(elfcorebuf); | ||
606 | return rc; | ||
607 | } | ||
608 | set_vmcore_list_offsets_elf32(elfcorebuf, &vmcore_list); | ||
609 | return 0; | ||
610 | } | ||
611 | |||
401 | static int __init parse_crash_elf_headers(void) | 612 | static int __init parse_crash_elf_headers(void) |
402 | { | 613 | { |
403 | unsigned char e_ident[EI_NIDENT]; | 614 | unsigned char e_ident[EI_NIDENT]; |
@@ -421,6 +632,13 @@ static int __init parse_crash_elf_headers(void) | |||
421 | 632 | ||
422 | /* Determine vmcore size. */ | 633 | /* Determine vmcore size. */ |
423 | vmcore_size = get_vmcore_size_elf64(elfcorebuf); | 634 | vmcore_size = get_vmcore_size_elf64(elfcorebuf); |
635 | } else if (e_ident[EI_CLASS] == ELFCLASS32) { | ||
636 | rc = parse_crash_elf32_headers(); | ||
637 | if (rc) | ||
638 | return rc; | ||
639 | |||
640 | /* Determine vmcore size. */ | ||
641 | vmcore_size = get_vmcore_size_elf32(elfcorebuf); | ||
424 | } else { | 642 | } else { |
425 | printk(KERN_WARNING "Warning: Core image elf header is not" | 643 | printk(KERN_WARNING "Warning: Core image elf header is not" |
426 | " sane\n"); | 644 | " sane\n"); |