diff options
| author | Vivek Goyal <vgoyal@in.ibm.com> | 2005-06-25 17:58:22 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-25 19:24:53 -0400 |
| commit | 72658e9d5004fc0dd807bea9eda49e6a52e40103 (patch) | |
| tree | e5abcecc18a02144c9e0413f9ff3f6601aa795c7 | |
| parent | 666bfddbe8b8fd4fd44617d6c55193d5ac7edb29 (diff) | |
[PATCH] kdump: Parse elf32 headers and export through /proc/vmcore
o Adds support for parsing core ELF32 headers.
o I am expecting ELF32 support to go away down the line. This patch has been
introduced for testing purposes as gdb can not parse ELF64 headers for
i386. When a decent user space solution is available, ELF32 support
can go away.
Signed-off-by: Vivek Goyal <vgoyal@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -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"); |
