Commit edf8e2af1453ce56c72b2f25a745e3734177a05d
Committed by
Riku Voipio
1 parent
88a8c984
linux-user: implemented ELF coredump support for ARM target
When target process is killed with signal (such signal that should dump core) a coredump file is created. This file is similar than coredump generated by Linux (there are few exceptions though). Riku Voipio: added support for rlimit Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi> Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
Showing
9 changed files
with
1101 additions
and
68 deletions
cpu-all.h
| @@ -735,6 +735,8 @@ extern unsigned long qemu_host_page_mask; | @@ -735,6 +735,8 @@ extern unsigned long qemu_host_page_mask; | ||
| 735 | #define PAGE_RESERVED 0x0020 | 735 | #define PAGE_RESERVED 0x0020 |
| 736 | 736 | ||
| 737 | void page_dump(FILE *f); | 737 | void page_dump(FILE *f); |
| 738 | +int walk_memory_regions(void *, | ||
| 739 | + int (*fn)(void *, unsigned long, unsigned long, unsigned long)); | ||
| 738 | int page_get_flags(target_ulong address); | 740 | int page_get_flags(target_ulong address); |
| 739 | void page_set_flags(target_ulong start, target_ulong end, int flags); | 741 | void page_set_flags(target_ulong start, target_ulong end, int flags); |
| 740 | int page_check_range(target_ulong start, target_ulong len, int flags); | 742 | int page_check_range(target_ulong start, target_ulong len, int flags); |
elf.h
| @@ -1081,7 +1081,23 @@ typedef struct elf64_shdr { | @@ -1081,7 +1081,23 @@ typedef struct elf64_shdr { | ||
| 1081 | #define EI_CLASS 4 | 1081 | #define EI_CLASS 4 |
| 1082 | #define EI_DATA 5 | 1082 | #define EI_DATA 5 |
| 1083 | #define EI_VERSION 6 | 1083 | #define EI_VERSION 6 |
| 1084 | -#define EI_PAD 7 | 1084 | +#define EI_OSABI 7 |
| 1085 | +#define EI_PAD 8 | ||
| 1086 | + | ||
| 1087 | +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ | ||
| 1088 | +#define ELFOSABI_SYSV 0 /* Alias. */ | ||
| 1089 | +#define ELFOSABI_HPUX 1 /* HP-UX */ | ||
| 1090 | +#define ELFOSABI_NETBSD 2 /* NetBSD. */ | ||
| 1091 | +#define ELFOSABI_LINUX 3 /* Linux. */ | ||
| 1092 | +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ | ||
| 1093 | +#define ELFOSABI_AIX 7 /* IBM AIX. */ | ||
| 1094 | +#define ELFOSABI_IRIX 8 /* SGI Irix. */ | ||
| 1095 | +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ | ||
| 1096 | +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ | ||
| 1097 | +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ | ||
| 1098 | +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ | ||
| 1099 | +#define ELFOSABI_ARM 97 /* ARM */ | ||
| 1100 | +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ | ||
| 1085 | 1101 | ||
| 1086 | #define ELFMAG0 0x7f /* EI_MAG */ | 1102 | #define ELFMAG0 0x7f /* EI_MAG */ |
| 1087 | #define ELFMAG1 'E' | 1103 | #define ELFMAG1 'E' |
| @@ -1108,6 +1124,7 @@ typedef struct elf64_shdr { | @@ -1108,6 +1124,7 @@ typedef struct elf64_shdr { | ||
| 1108 | #define NT_PRFPREG 2 | 1124 | #define NT_PRFPREG 2 |
| 1109 | #define NT_PRPSINFO 3 | 1125 | #define NT_PRPSINFO 3 |
| 1110 | #define NT_TASKSTRUCT 4 | 1126 | #define NT_TASKSTRUCT 4 |
| 1127 | +#define NT_AUXV 6 | ||
| 1111 | #define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ | 1128 | #define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ |
| 1112 | 1129 | ||
| 1113 | 1130 |
exec.c
| @@ -2131,36 +2131,36 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, | @@ -2131,36 +2131,36 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, | ||
| 2131 | return 0; | 2131 | return 0; |
| 2132 | } | 2132 | } |
| 2133 | 2133 | ||
| 2134 | -/* dump memory mappings */ | ||
| 2135 | -void page_dump(FILE *f) | 2134 | +/* |
| 2135 | + * Walks guest process memory "regions" one by one | ||
| 2136 | + * and calls callback function 'fn' for each region. | ||
| 2137 | + */ | ||
| 2138 | +int walk_memory_regions(void *priv, | ||
| 2139 | + int (*fn)(void *, unsigned long, unsigned long, unsigned long)) | ||
| 2136 | { | 2140 | { |
| 2137 | unsigned long start, end; | 2141 | unsigned long start, end; |
| 2142 | + PageDesc *p = NULL; | ||
| 2138 | int i, j, prot, prot1; | 2143 | int i, j, prot, prot1; |
| 2139 | - PageDesc *p; | 2144 | + int rc = 0; |
| 2140 | 2145 | ||
| 2141 | - fprintf(f, "%-8s %-8s %-8s %s\n", | ||
| 2142 | - "start", "end", "size", "prot"); | ||
| 2143 | - start = -1; | ||
| 2144 | - end = -1; | 2146 | + start = end = -1; |
| 2145 | prot = 0; | 2147 | prot = 0; |
| 2146 | - for(i = 0; i <= L1_SIZE; i++) { | ||
| 2147 | - if (i < L1_SIZE) | ||
| 2148 | - p = l1_map[i]; | ||
| 2149 | - else | ||
| 2150 | - p = NULL; | ||
| 2151 | - for(j = 0;j < L2_SIZE; j++) { | ||
| 2152 | - if (!p) | ||
| 2153 | - prot1 = 0; | ||
| 2154 | - else | ||
| 2155 | - prot1 = p[j].flags; | 2148 | + |
| 2149 | + for (i = 0; i <= L1_SIZE; i++) { | ||
| 2150 | + p = (i < L1_SIZE) ? l1_map[i] : NULL; | ||
| 2151 | + for (j = 0; j < L2_SIZE; j++) { | ||
| 2152 | + prot1 = (p == NULL) ? 0 : p[j].flags; | ||
| 2153 | + /* | ||
| 2154 | + * "region" is one continuous chunk of memory | ||
| 2155 | + * that has same protection flags set. | ||
| 2156 | + */ | ||
| 2156 | if (prot1 != prot) { | 2157 | if (prot1 != prot) { |
| 2157 | end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); | 2158 | end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); |
| 2158 | if (start != -1) { | 2159 | if (start != -1) { |
| 2159 | - fprintf(f, "%08lx-%08lx %08lx %c%c%c\n", | ||
| 2160 | - start, end, end - start, | ||
| 2161 | - prot & PAGE_READ ? 'r' : '-', | ||
| 2162 | - prot & PAGE_WRITE ? 'w' : '-', | ||
| 2163 | - prot & PAGE_EXEC ? 'x' : '-'); | 2160 | + rc = (*fn)(priv, start, end, prot); |
| 2161 | + /* callback can stop iteration by returning != 0 */ | ||
| 2162 | + if (rc != 0) | ||
| 2163 | + return (rc); | ||
| 2164 | } | 2164 | } |
| 2165 | if (prot1 != 0) | 2165 | if (prot1 != 0) |
| 2166 | start = end; | 2166 | start = end; |
| @@ -2168,10 +2168,33 @@ void page_dump(FILE *f) | @@ -2168,10 +2168,33 @@ void page_dump(FILE *f) | ||
| 2168 | start = -1; | 2168 | start = -1; |
| 2169 | prot = prot1; | 2169 | prot = prot1; |
| 2170 | } | 2170 | } |
| 2171 | - if (!p) | 2171 | + if (p == NULL) |
| 2172 | break; | 2172 | break; |
| 2173 | } | 2173 | } |
| 2174 | } | 2174 | } |
| 2175 | + return (rc); | ||
| 2176 | +} | ||
| 2177 | + | ||
| 2178 | +static int dump_region(void *priv, unsigned long start, | ||
| 2179 | + unsigned long end, unsigned long prot) | ||
| 2180 | +{ | ||
| 2181 | + FILE *f = (FILE *)priv; | ||
| 2182 | + | ||
| 2183 | + (void) fprintf(f, "%08lx-%08lx %08lx %c%c%c\n", | ||
| 2184 | + start, end, end - start, | ||
| 2185 | + ((prot & PAGE_READ) ? 'r' : '-'), | ||
| 2186 | + ((prot & PAGE_WRITE) ? 'w' : '-'), | ||
| 2187 | + ((prot & PAGE_EXEC) ? 'x' : '-')); | ||
| 2188 | + | ||
| 2189 | + return (0); | ||
| 2190 | +} | ||
| 2191 | + | ||
| 2192 | +/* dump memory mappings */ | ||
| 2193 | +void page_dump(FILE *f) | ||
| 2194 | +{ | ||
| 2195 | + (void) fprintf(f, "%-8s %-8s %-8s %s\n", | ||
| 2196 | + "start", "end", "size", "prot"); | ||
| 2197 | + walk_memory_regions(f, dump_region); | ||
| 2175 | } | 2198 | } |
| 2176 | 2199 | ||
| 2177 | int page_get_flags(target_ulong address) | 2200 | int page_get_flags(target_ulong address) |
linux-user/elfload.c
| 1 | /* This is the Linux kernel elf-loading code, ported into user space */ | 1 | /* This is the Linux kernel elf-loading code, ported into user space */ |
| 2 | +#include <sys/time.h> | ||
| 3 | +#include <sys/param.h> | ||
| 2 | 4 | ||
| 3 | #include <stdio.h> | 5 | #include <stdio.h> |
| 4 | #include <sys/types.h> | 6 | #include <sys/types.h> |
| @@ -6,8 +8,10 @@ | @@ -6,8 +8,10 @@ | ||
| 6 | #include <errno.h> | 8 | #include <errno.h> |
| 7 | #include <unistd.h> | 9 | #include <unistd.h> |
| 8 | #include <sys/mman.h> | 10 | #include <sys/mman.h> |
| 11 | +#include <sys/resource.h> | ||
| 9 | #include <stdlib.h> | 12 | #include <stdlib.h> |
| 10 | #include <string.h> | 13 | #include <string.h> |
| 14 | +#include <time.h> | ||
| 11 | 15 | ||
| 12 | #include "qemu.h" | 16 | #include "qemu.h" |
| 13 | #include "disas.h" | 17 | #include "disas.h" |
| @@ -21,6 +25,8 @@ | @@ -21,6 +25,8 @@ | ||
| 21 | #undef ELF_ARCH | 25 | #undef ELF_ARCH |
| 22 | #endif | 26 | #endif |
| 23 | 27 | ||
| 28 | +#define ELF_OSABI ELFOSABI_SYSV | ||
| 29 | + | ||
| 24 | /* from personality.h */ | 30 | /* from personality.h */ |
| 25 | 31 | ||
| 26 | /* | 32 | /* |
| @@ -160,7 +166,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | @@ -160,7 +166,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | ||
| 160 | } | 166 | } |
| 161 | #endif | 167 | #endif |
| 162 | 168 | ||
| 163 | -#define USE_ELF_CORE_DUMP | ||
| 164 | #define ELF_EXEC_PAGESIZE 4096 | 169 | #define ELF_EXEC_PAGESIZE 4096 |
| 165 | 170 | ||
| 166 | #endif | 171 | #endif |
| @@ -198,6 +203,37 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | @@ -198,6 +203,37 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | ||
| 198 | regs->ARM_r10 = infop->start_data; | 203 | regs->ARM_r10 = infop->start_data; |
| 199 | } | 204 | } |
| 200 | 205 | ||
| 206 | +typedef uint32_t elf_greg_t; | ||
| 207 | +typedef uint16_t target_uid_t; | ||
| 208 | +typedef uint16_t target_gid_t; | ||
| 209 | +typedef int32_t target_pid_t; | ||
| 210 | + | ||
| 211 | +#define ELF_NREG 18 | ||
| 212 | +typedef elf_greg_t elf_gregset_t[ELF_NREG]; | ||
| 213 | + | ||
| 214 | +static void elf_core_copy_regs(elf_gregset_t *regs, const CPUState *env) | ||
| 215 | +{ | ||
| 216 | + (*regs)[0] = env->regs[0]; | ||
| 217 | + (*regs)[1] = env->regs[1]; | ||
| 218 | + (*regs)[2] = env->regs[2]; | ||
| 219 | + (*regs)[3] = env->regs[3]; | ||
| 220 | + (*regs)[4] = env->regs[4]; | ||
| 221 | + (*regs)[5] = env->regs[5]; | ||
| 222 | + (*regs)[6] = env->regs[6]; | ||
| 223 | + (*regs)[7] = env->regs[7]; | ||
| 224 | + (*regs)[8] = env->regs[8]; | ||
| 225 | + (*regs)[9] = env->regs[9]; | ||
| 226 | + (*regs)[10] = env->regs[10]; | ||
| 227 | + (*regs)[11] = env->regs[11]; | ||
| 228 | + (*regs)[12] = env->regs[12]; | ||
| 229 | + (*regs)[13] = env->regs[13]; | ||
| 230 | + (*regs)[14] = env->regs[14]; | ||
| 231 | + (*regs)[15] = env->regs[15]; | ||
| 232 | + | ||
| 233 | + (*regs)[16] = cpsr_read((CPUState *)env); | ||
| 234 | + (*regs)[17] = env->regs[0]; /* XXX */ | ||
| 235 | +} | ||
| 236 | + | ||
| 201 | #define USE_ELF_CORE_DUMP | 237 | #define USE_ELF_CORE_DUMP |
| 202 | #define ELF_EXEC_PAGESIZE 4096 | 238 | #define ELF_EXEC_PAGESIZE 4096 |
| 203 | 239 | ||
| @@ -418,7 +454,6 @@ static inline void init_thread(struct target_pt_regs *_regs, struct image_info * | @@ -418,7 +454,6 @@ static inline void init_thread(struct target_pt_regs *_regs, struct image_info * | ||
| 418 | _regs->gpr[5] = pos; | 454 | _regs->gpr[5] = pos; |
| 419 | } | 455 | } |
| 420 | 456 | ||
| 421 | -#define USE_ELF_CORE_DUMP | ||
| 422 | #define ELF_EXEC_PAGESIZE 4096 | 457 | #define ELF_EXEC_PAGESIZE 4096 |
| 423 | 458 | ||
| 424 | #endif | 459 | #endif |
| @@ -448,7 +483,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | @@ -448,7 +483,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | ||
| 448 | regs->regs[29] = infop->start_stack; | 483 | regs->regs[29] = infop->start_stack; |
| 449 | } | 484 | } |
| 450 | 485 | ||
| 451 | -#define USE_ELF_CORE_DUMP | ||
| 452 | #define ELF_EXEC_PAGESIZE 4096 | 486 | #define ELF_EXEC_PAGESIZE 4096 |
| 453 | 487 | ||
| 454 | #endif /* TARGET_MIPS */ | 488 | #endif /* TARGET_MIPS */ |
| @@ -470,7 +504,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | @@ -470,7 +504,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | ||
| 470 | 504 | ||
| 471 | } | 505 | } |
| 472 | 506 | ||
| 473 | -#define USE_ELF_CORE_DUMP | ||
| 474 | #define ELF_EXEC_PAGESIZE 4096 | 507 | #define ELF_EXEC_PAGESIZE 4096 |
| 475 | 508 | ||
| 476 | #endif /* TARGET_MICROBLAZE */ | 509 | #endif /* TARGET_MICROBLAZE */ |
| @@ -492,7 +525,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | @@ -492,7 +525,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | ||
| 492 | regs->regs[15] = infop->start_stack; | 525 | regs->regs[15] = infop->start_stack; |
| 493 | } | 526 | } |
| 494 | 527 | ||
| 495 | -#define USE_ELF_CORE_DUMP | ||
| 496 | #define ELF_EXEC_PAGESIZE 4096 | 528 | #define ELF_EXEC_PAGESIZE 4096 |
| 497 | 529 | ||
| 498 | #endif | 530 | #endif |
| @@ -512,7 +544,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | @@ -512,7 +544,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | ||
| 512 | regs->erp = infop->entry; | 544 | regs->erp = infop->entry; |
| 513 | } | 545 | } |
| 514 | 546 | ||
| 515 | -#define USE_ELF_CORE_DUMP | ||
| 516 | #define ELF_EXEC_PAGESIZE 8192 | 547 | #define ELF_EXEC_PAGESIZE 8192 |
| 517 | 548 | ||
| 518 | #endif | 549 | #endif |
| @@ -537,7 +568,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | @@ -537,7 +568,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | ||
| 537 | regs->pc = infop->entry; | 568 | regs->pc = infop->entry; |
| 538 | } | 569 | } |
| 539 | 570 | ||
| 540 | -#define USE_ELF_CORE_DUMP | ||
| 541 | #define ELF_EXEC_PAGESIZE 8192 | 571 | #define ELF_EXEC_PAGESIZE 8192 |
| 542 | 572 | ||
| 543 | #endif | 573 | #endif |
| @@ -562,7 +592,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | @@ -562,7 +592,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i | ||
| 562 | regs->unique, infop->start_data); | 592 | regs->unique, infop->start_data); |
| 563 | } | 593 | } |
| 564 | 594 | ||
| 565 | -#define USE_ELF_CORE_DUMP | ||
| 566 | #define ELF_EXEC_PAGESIZE 8192 | 595 | #define ELF_EXEC_PAGESIZE 8192 |
| 567 | 596 | ||
| 568 | #endif /* TARGET_ALPHA */ | 597 | #endif /* TARGET_ALPHA */ |
| @@ -680,6 +709,20 @@ static void bswap_sym(struct elf_sym *sym) | @@ -680,6 +709,20 @@ static void bswap_sym(struct elf_sym *sym) | ||
| 680 | } | 709 | } |
| 681 | #endif | 710 | #endif |
| 682 | 711 | ||
| 712 | +#ifdef USE_ELF_CORE_DUMP | ||
| 713 | +static int elf_core_dump(int, const CPUState *); | ||
| 714 | + | ||
| 715 | +#ifdef BSWAP_NEEDED | ||
| 716 | +static void bswap_note(struct elf_note *en) | ||
| 717 | +{ | ||
| 718 | + bswaptls(&en->n_namesz); | ||
| 719 | + bswaptls(&en->n_descsz); | ||
| 720 | + bswaptls(&en->n_type); | ||
| 721 | +} | ||
| 722 | +#endif /* BSWAP_NEEDED */ | ||
| 723 | + | ||
| 724 | +#endif /* USE_ELF_CORE_DUMP */ | ||
| 725 | + | ||
| 683 | /* | 726 | /* |
| 684 | * 'copy_elf_strings()' copies argument/envelope strings from user | 727 | * 'copy_elf_strings()' copies argument/envelope strings from user |
| 685 | * memory to free pages in kernel mem. These are in a format ready | 728 | * memory to free pages in kernel mem. These are in a format ready |
| @@ -904,6 +947,8 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, | @@ -904,6 +947,8 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, | ||
| 904 | #endif | 947 | #endif |
| 905 | #undef NEW_AUX_ENT | 948 | #undef NEW_AUX_ENT |
| 906 | 949 | ||
| 950 | + info->saved_auxv = sp; | ||
| 951 | + | ||
| 907 | sp = loader_build_argptr(envc, argc, sp, p, !ibcs); | 952 | sp = loader_build_argptr(envc, argc, sp, p, !ibcs); |
| 908 | return sp; | 953 | return sp; |
| 909 | } | 954 | } |
| @@ -1586,9 +1631,876 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, | @@ -1586,9 +1631,876 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, | ||
| 1586 | 1631 | ||
| 1587 | info->entry = elf_entry; | 1632 | info->entry = elf_entry; |
| 1588 | 1633 | ||
| 1634 | +#ifdef USE_ELF_CORE_DUMP | ||
| 1635 | + bprm->core_dump = &elf_core_dump; | ||
| 1636 | +#endif | ||
| 1637 | + | ||
| 1589 | return 0; | 1638 | return 0; |
| 1590 | } | 1639 | } |
| 1591 | 1640 | ||
| 1641 | +#ifdef USE_ELF_CORE_DUMP | ||
| 1642 | + | ||
| 1643 | +/* | ||
| 1644 | + * Definitions to generate Intel SVR4-like core files. | ||
| 1645 | + * These mostly have the same names as the SVR4 types with "elf_" | ||
| 1646 | + * tacked on the front to prevent clashes with linux definitions, | ||
| 1647 | + * and the typedef forms have been avoided. This is mostly like | ||
| 1648 | + * the SVR4 structure, but more Linuxy, with things that Linux does | ||
| 1649 | + * not support and which gdb doesn't really use excluded. | ||
| 1650 | + * | ||
| 1651 | + * Fields we don't dump (their contents is zero) in linux-user qemu | ||
| 1652 | + * are marked with XXX. | ||
| 1653 | + * | ||
| 1654 | + * Core dump code is copied from linux kernel (fs/binfmt_elf.c). | ||
| 1655 | + * | ||
| 1656 | + * Porting ELF coredump for target is (quite) simple process. First you | ||
| 1657 | + * define ELF_USE_CORE_DUMP in target ELF code (where init_thread() for | ||
| 1658 | + * the target resides): | ||
| 1659 | + * | ||
| 1660 | + * #define USE_ELF_CORE_DUMP | ||
| 1661 | + * | ||
| 1662 | + * Next you define type of register set used for dumping. ELF specification | ||
| 1663 | + * says that it needs to be array of elf_greg_t that has size of ELF_NREG. | ||
| 1664 | + * | ||
| 1665 | + * typedef <target_regtype> elf_greg_t; | ||
| 1666 | + * #define ELF_NREG <number of registers> | ||
| 1667 | + * typedef elf_greg_t elf_gregset_t[ELF_NREG]; | ||
| 1668 | + * | ||
| 1669 | + * Then define following types to match target types. Actual types can | ||
| 1670 | + * be found from linux kernel (arch/<ARCH>/include/asm/posix_types.h): | ||
| 1671 | + * | ||
| 1672 | + * typedef <target_uid_type> target_uid_t; | ||
| 1673 | + * typedef <target_gid_type> target_gid_t; | ||
| 1674 | + * typedef <target_pid_type> target_pid_t; | ||
| 1675 | + * | ||
| 1676 | + * Last step is to implement target specific function that copies registers | ||
| 1677 | + * from given cpu into just specified register set. Prototype is: | ||
| 1678 | + * | ||
| 1679 | + * static void elf_core_copy_regs(elf_gregset_t *regs, const CPUState *env); | ||
| 1680 | + * | ||
| 1681 | + * Parameters: | ||
| 1682 | + * regs - copy register values into here (allocated and zeroed by caller) | ||
| 1683 | + * env - copy registers from here | ||
| 1684 | + * | ||
| 1685 | + * Example for ARM target is provided in this file. | ||
| 1686 | + */ | ||
| 1687 | + | ||
| 1688 | +/* An ELF note in memory */ | ||
| 1689 | +struct memelfnote { | ||
| 1690 | + const char *name; | ||
| 1691 | + size_t namesz; | ||
| 1692 | + size_t namesz_rounded; | ||
| 1693 | + int type; | ||
| 1694 | + size_t datasz; | ||
| 1695 | + void *data; | ||
| 1696 | + size_t notesz; | ||
| 1697 | +}; | ||
| 1698 | + | ||
| 1699 | +struct elf_siginfo { | ||
| 1700 | + int si_signo; /* signal number */ | ||
| 1701 | + int si_code; /* extra code */ | ||
| 1702 | + int si_errno; /* errno */ | ||
| 1703 | +}; | ||
| 1704 | + | ||
| 1705 | +struct elf_prstatus { | ||
| 1706 | + struct elf_siginfo pr_info; /* Info associated with signal */ | ||
| 1707 | + short pr_cursig; /* Current signal */ | ||
| 1708 | + target_ulong pr_sigpend; /* XXX */ | ||
| 1709 | + target_ulong pr_sighold; /* XXX */ | ||
| 1710 | + target_pid_t pr_pid; | ||
| 1711 | + target_pid_t pr_ppid; | ||
| 1712 | + target_pid_t pr_pgrp; | ||
| 1713 | + target_pid_t pr_sid; | ||
| 1714 | + struct target_timeval pr_utime; /* XXX User time */ | ||
| 1715 | + struct target_timeval pr_stime; /* XXX System time */ | ||
| 1716 | + struct target_timeval pr_cutime; /* XXX Cumulative user time */ | ||
| 1717 | + struct target_timeval pr_cstime; /* XXX Cumulative system time */ | ||
| 1718 | + elf_gregset_t pr_reg; /* GP registers */ | ||
| 1719 | + int pr_fpvalid; /* XXX */ | ||
| 1720 | +}; | ||
| 1721 | + | ||
| 1722 | +#define ELF_PRARGSZ (80) /* Number of chars for args */ | ||
| 1723 | + | ||
| 1724 | +struct elf_prpsinfo { | ||
| 1725 | + char pr_state; /* numeric process state */ | ||
| 1726 | + char pr_sname; /* char for pr_state */ | ||
| 1727 | + char pr_zomb; /* zombie */ | ||
| 1728 | + char pr_nice; /* nice val */ | ||
| 1729 | + target_ulong pr_flag; /* flags */ | ||
| 1730 | + target_uid_t pr_uid; | ||
| 1731 | + target_gid_t pr_gid; | ||
| 1732 | + target_pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; | ||
| 1733 | + /* Lots missing */ | ||
| 1734 | + char pr_fname[16]; /* filename of executable */ | ||
| 1735 | + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ | ||
| 1736 | +}; | ||
| 1737 | + | ||
| 1738 | +/* Here is the structure in which status of each thread is captured. */ | ||
| 1739 | +struct elf_thread_status { | ||
| 1740 | + TAILQ_ENTRY(elf_thread_status) ets_link; | ||
| 1741 | + struct elf_prstatus prstatus; /* NT_PRSTATUS */ | ||
| 1742 | +#if 0 | ||
| 1743 | + elf_fpregset_t fpu; /* NT_PRFPREG */ | ||
| 1744 | + struct task_struct *thread; | ||
| 1745 | + elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */ | ||
| 1746 | +#endif | ||
| 1747 | + struct memelfnote notes[1]; | ||
| 1748 | + int num_notes; | ||
| 1749 | +}; | ||
| 1750 | + | ||
| 1751 | +struct elf_note_info { | ||
| 1752 | + struct memelfnote *notes; | ||
| 1753 | + struct elf_prstatus *prstatus; /* NT_PRSTATUS */ | ||
| 1754 | + struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ | ||
| 1755 | + | ||
| 1756 | + TAILQ_HEAD(thread_list_head, elf_thread_status) thread_list; | ||
| 1757 | +#if 0 | ||
| 1758 | + /* | ||
| 1759 | + * Current version of ELF coredump doesn't support | ||
| 1760 | + * dumping fp regs etc. | ||
| 1761 | + */ | ||
| 1762 | + elf_fpregset_t *fpu; | ||
| 1763 | + elf_fpxregset_t *xfpu; | ||
| 1764 | + int thread_status_size; | ||
| 1765 | +#endif | ||
| 1766 | + int notes_size; | ||
| 1767 | + int numnote; | ||
| 1768 | +}; | ||
| 1769 | + | ||
| 1770 | +struct vm_area_struct { | ||
| 1771 | + abi_ulong vma_start; /* start vaddr of memory region */ | ||
| 1772 | + abi_ulong vma_end; /* end vaddr of memory region */ | ||
| 1773 | + abi_ulong vma_flags; /* protection etc. flags for the region */ | ||
| 1774 | + TAILQ_ENTRY(vm_area_struct) vma_link; | ||
| 1775 | +}; | ||
| 1776 | + | ||
| 1777 | +struct mm_struct { | ||
| 1778 | + TAILQ_HEAD(, vm_area_struct) mm_mmap; | ||
| 1779 | + int mm_count; /* number of mappings */ | ||
| 1780 | +}; | ||
| 1781 | + | ||
| 1782 | +static struct mm_struct *vma_init(void); | ||
| 1783 | +static void vma_delete(struct mm_struct *); | ||
| 1784 | +static int vma_add_mapping(struct mm_struct *, abi_ulong, | ||
| 1785 | + abi_ulong, abi_ulong); | ||
| 1786 | +static int vma_get_mapping_count(const struct mm_struct *); | ||
| 1787 | +static struct vm_area_struct *vma_first(const struct mm_struct *); | ||
| 1788 | +static struct vm_area_struct *vma_next(struct vm_area_struct *); | ||
| 1789 | +static abi_ulong vma_dump_size(const struct vm_area_struct *); | ||
| 1790 | +static int vma_walker(void *priv, unsigned long start, unsigned long end, | ||
| 1791 | + unsigned long flags); | ||
| 1792 | + | ||
| 1793 | +static void fill_elf_header(struct elfhdr *, int, uint16_t, uint32_t); | ||
| 1794 | +static void fill_note(struct memelfnote *, const char *, int, | ||
| 1795 | + unsigned int, void *); | ||
| 1796 | +static void fill_prstatus(struct elf_prstatus *, const TaskState *, int); | ||
| 1797 | +static int fill_psinfo(struct elf_prpsinfo *, const TaskState *); | ||
| 1798 | +static void fill_auxv_note(struct memelfnote *, const TaskState *); | ||
| 1799 | +static void fill_elf_note_phdr(struct elf_phdr *, int, off_t); | ||
| 1800 | +static size_t note_size(const struct memelfnote *); | ||
| 1801 | +static void free_note_info(struct elf_note_info *); | ||
| 1802 | +static int fill_note_info(struct elf_note_info *, long, const CPUState *); | ||
| 1803 | +static void fill_thread_info(struct elf_note_info *, const CPUState *); | ||
| 1804 | +static int core_dump_filename(const TaskState *, char *, size_t); | ||
| 1805 | + | ||
| 1806 | +static int dump_write(int, const void *, size_t); | ||
| 1807 | +static int write_note(struct memelfnote *, int); | ||
| 1808 | +static int write_note_info(struct elf_note_info *, int); | ||
| 1809 | + | ||
| 1810 | +#ifdef BSWAP_NEEDED | ||
| 1811 | +static void bswap_prstatus(struct elf_prstatus *); | ||
| 1812 | +static void bswap_psinfo(struct elf_prpsinfo *); | ||
| 1813 | + | ||
| 1814 | +static void bswap_prstatus(struct elf_prstatus *prstatus) | ||
| 1815 | +{ | ||
| 1816 | + prstatus->pr_info.si_signo = tswapl(prstatus->pr_info.si_signo); | ||
| 1817 | + prstatus->pr_info.si_code = tswapl(prstatus->pr_info.si_code); | ||
| 1818 | + prstatus->pr_info.si_errno = tswapl(prstatus->pr_info.si_errno); | ||
| 1819 | + prstatus->pr_cursig = tswap16(prstatus->pr_cursig); | ||
| 1820 | + prstatus->pr_sigpend = tswapl(prstatus->pr_sigpend); | ||
| 1821 | + prstatus->pr_sighold = tswapl(prstatus->pr_sighold); | ||
| 1822 | + prstatus->pr_pid = tswap32(prstatus->pr_pid); | ||
| 1823 | + prstatus->pr_ppid = tswap32(prstatus->pr_ppid); | ||
| 1824 | + prstatus->pr_pgrp = tswap32(prstatus->pr_pgrp); | ||
| 1825 | + prstatus->pr_sid = tswap32(prstatus->pr_sid); | ||
| 1826 | + /* cpu times are not filled, so we skip them */ | ||
| 1827 | + /* regs should be in correct format already */ | ||
| 1828 | + prstatus->pr_fpvalid = tswap32(prstatus->pr_fpvalid); | ||
| 1829 | +} | ||
| 1830 | + | ||
| 1831 | +static void bswap_psinfo(struct elf_prpsinfo *psinfo) | ||
| 1832 | +{ | ||
| 1833 | + psinfo->pr_flag = tswapl(psinfo->pr_flag); | ||
| 1834 | + psinfo->pr_uid = tswap16(psinfo->pr_uid); | ||
| 1835 | + psinfo->pr_gid = tswap16(psinfo->pr_gid); | ||
| 1836 | + psinfo->pr_pid = tswap32(psinfo->pr_pid); | ||
| 1837 | + psinfo->pr_ppid = tswap32(psinfo->pr_ppid); | ||
| 1838 | + psinfo->pr_pgrp = tswap32(psinfo->pr_pgrp); | ||
| 1839 | + psinfo->pr_sid = tswap32(psinfo->pr_sid); | ||
| 1840 | +} | ||
| 1841 | +#endif /* BSWAP_NEEDED */ | ||
| 1842 | + | ||
| 1843 | +/* | ||
| 1844 | + * Minimal support for linux memory regions. These are needed | ||
| 1845 | + * when we are finding out what memory exactly belongs to | ||
| 1846 | + * emulated process. No locks needed here, as long as | ||
| 1847 | + * thread that received the signal is stopped. | ||
| 1848 | + */ | ||
| 1849 | + | ||
| 1850 | +static struct mm_struct *vma_init(void) | ||
| 1851 | +{ | ||
| 1852 | + struct mm_struct *mm; | ||
| 1853 | + | ||
| 1854 | + if ((mm = qemu_malloc(sizeof (*mm))) == NULL) | ||
| 1855 | + return (NULL); | ||
| 1856 | + | ||
| 1857 | + mm->mm_count = 0; | ||
| 1858 | + TAILQ_INIT(&mm->mm_mmap); | ||
| 1859 | + | ||
| 1860 | + return (mm); | ||
| 1861 | +} | ||
| 1862 | + | ||
| 1863 | +static void vma_delete(struct mm_struct *mm) | ||
| 1864 | +{ | ||
| 1865 | + struct vm_area_struct *vma; | ||
| 1866 | + | ||
| 1867 | + while ((vma = vma_first(mm)) != NULL) { | ||
| 1868 | + TAILQ_REMOVE(&mm->mm_mmap, vma, vma_link); | ||
| 1869 | + qemu_free(vma); | ||
| 1870 | + } | ||
| 1871 | + qemu_free(mm); | ||
| 1872 | +} | ||
| 1873 | + | ||
| 1874 | +static int vma_add_mapping(struct mm_struct *mm, abi_ulong start, | ||
| 1875 | + abi_ulong end, abi_ulong flags) | ||
| 1876 | +{ | ||
| 1877 | + struct vm_area_struct *vma; | ||
| 1878 | + | ||
| 1879 | + if ((vma = qemu_mallocz(sizeof (*vma))) == NULL) | ||
| 1880 | + return (-1); | ||
| 1881 | + | ||
| 1882 | + vma->vma_start = start; | ||
| 1883 | + vma->vma_end = end; | ||
| 1884 | + vma->vma_flags = flags; | ||
| 1885 | + | ||
| 1886 | + TAILQ_INSERT_TAIL(&mm->mm_mmap, vma, vma_link); | ||
| 1887 | + mm->mm_count++; | ||
| 1888 | + | ||
| 1889 | + return (0); | ||
| 1890 | +} | ||
| 1891 | + | ||
| 1892 | +static struct vm_area_struct *vma_first(const struct mm_struct *mm) | ||
| 1893 | +{ | ||
| 1894 | + return (TAILQ_FIRST(&mm->mm_mmap)); | ||
| 1895 | +} | ||
| 1896 | + | ||
| 1897 | +static struct vm_area_struct *vma_next(struct vm_area_struct *vma) | ||
| 1898 | +{ | ||
| 1899 | + return (TAILQ_NEXT(vma, vma_link)); | ||
| 1900 | +} | ||
| 1901 | + | ||
| 1902 | +static int vma_get_mapping_count(const struct mm_struct *mm) | ||
| 1903 | +{ | ||
| 1904 | + return (mm->mm_count); | ||
| 1905 | +} | ||
| 1906 | + | ||
| 1907 | +/* | ||
| 1908 | + * Calculate file (dump) size of given memory region. | ||
| 1909 | + */ | ||
| 1910 | +static abi_ulong vma_dump_size(const struct vm_area_struct *vma) | ||
| 1911 | +{ | ||
| 1912 | + /* if we cannot even read the first page, skip it */ | ||
| 1913 | + if (!access_ok(VERIFY_READ, vma->vma_start, TARGET_PAGE_SIZE)) | ||
| 1914 | + return (0); | ||
| 1915 | + | ||
| 1916 | + /* | ||
| 1917 | + * Usually we don't dump executable pages as they contain | ||
| 1918 | + * non-writable code that debugger can read directly from | ||
| 1919 | + * target library etc. However, thread stacks are marked | ||
| 1920 | + * also executable so we read in first page of given region | ||
| 1921 | + * and check whether it contains elf header. If there is | ||
| 1922 | + * no elf header, we dump it. | ||
| 1923 | + */ | ||
| 1924 | + if (vma->vma_flags & PROT_EXEC) { | ||
| 1925 | + char page[TARGET_PAGE_SIZE]; | ||
| 1926 | + | ||
| 1927 | + copy_from_user(page, vma->vma_start, sizeof (page)); | ||
| 1928 | + if ((page[EI_MAG0] == ELFMAG0) && | ||
| 1929 | + (page[EI_MAG1] == ELFMAG1) && | ||
| 1930 | + (page[EI_MAG2] == ELFMAG2) && | ||
| 1931 | + (page[EI_MAG3] == ELFMAG3)) { | ||
| 1932 | + /* | ||
| 1933 | + * Mappings are possibly from ELF binary. Don't dump | ||
| 1934 | + * them. | ||
| 1935 | + */ | ||
| 1936 | + return (0); | ||
| 1937 | + } | ||
| 1938 | + } | ||
| 1939 | + | ||
| 1940 | + return (vma->vma_end - vma->vma_start); | ||
| 1941 | +} | ||
| 1942 | + | ||
| 1943 | +static int vma_walker(void *priv, unsigned long start, unsigned long end, | ||
| 1944 | + unsigned long flags) | ||
| 1945 | +{ | ||
| 1946 | + struct mm_struct *mm = (struct mm_struct *)priv; | ||
| 1947 | + | ||
| 1948 | + /* | ||
| 1949 | + * Don't dump anything that qemu has reserved for internal use. | ||
| 1950 | + */ | ||
| 1951 | + if (flags & PAGE_RESERVED) | ||
| 1952 | + return (0); | ||
| 1953 | + | ||
| 1954 | + vma_add_mapping(mm, start, end, flags); | ||
| 1955 | + return (0); | ||
| 1956 | +} | ||
| 1957 | + | ||
| 1958 | +static void fill_note(struct memelfnote *note, const char *name, int type, | ||
| 1959 | + unsigned int sz, void *data) | ||
| 1960 | +{ | ||
| 1961 | + unsigned int namesz; | ||
| 1962 | + | ||
| 1963 | + namesz = strlen(name) + 1; | ||
| 1964 | + note->name = name; | ||
| 1965 | + note->namesz = namesz; | ||
| 1966 | + note->namesz_rounded = roundup(namesz, sizeof (int32_t)); | ||
| 1967 | + note->type = type; | ||
| 1968 | + note->datasz = roundup(sz, sizeof (int32_t));; | ||
| 1969 | + note->data = data; | ||
| 1970 | + | ||
| 1971 | + /* | ||
| 1972 | + * We calculate rounded up note size here as specified by | ||
| 1973 | + * ELF document. | ||
| 1974 | + */ | ||
| 1975 | + note->notesz = sizeof (struct elf_note) + | ||
| 1976 | + note->namesz_rounded + note->datasz; | ||
| 1977 | +} | ||
| 1978 | + | ||
| 1979 | +static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine, | ||
| 1980 | + uint32_t flags) | ||
| 1981 | +{ | ||
| 1982 | + (void) memset(elf, 0, sizeof(*elf)); | ||
| 1983 | + | ||
| 1984 | + (void) memcpy(elf->e_ident, ELFMAG, SELFMAG); | ||
| 1985 | + elf->e_ident[EI_CLASS] = ELF_CLASS; | ||
| 1986 | + elf->e_ident[EI_DATA] = ELF_DATA; | ||
| 1987 | + elf->e_ident[EI_VERSION] = EV_CURRENT; | ||
| 1988 | + elf->e_ident[EI_OSABI] = ELF_OSABI; | ||
| 1989 | + | ||
| 1990 | + elf->e_type = ET_CORE; | ||
| 1991 | + elf->e_machine = machine; | ||
| 1992 | + elf->e_version = EV_CURRENT; | ||
| 1993 | + elf->e_phoff = sizeof(struct elfhdr); | ||
| 1994 | + elf->e_flags = flags; | ||
| 1995 | + elf->e_ehsize = sizeof(struct elfhdr); | ||
| 1996 | + elf->e_phentsize = sizeof(struct elf_phdr); | ||
| 1997 | + elf->e_phnum = segs; | ||
| 1998 | + | ||
| 1999 | +#ifdef BSWAP_NEEDED | ||
| 2000 | + bswap_ehdr(elf); | ||
| 2001 | +#endif | ||
| 2002 | +} | ||
| 2003 | + | ||
| 2004 | +static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset) | ||
| 2005 | +{ | ||
| 2006 | + phdr->p_type = PT_NOTE; | ||
| 2007 | + phdr->p_offset = offset; | ||
| 2008 | + phdr->p_vaddr = 0; | ||
| 2009 | + phdr->p_paddr = 0; | ||
| 2010 | + phdr->p_filesz = sz; | ||
| 2011 | + phdr->p_memsz = 0; | ||
| 2012 | + phdr->p_flags = 0; | ||
| 2013 | + phdr->p_align = 0; | ||
| 2014 | + | ||
| 2015 | +#ifdef BSWAP_NEEDED | ||
| 2016 | + bswap_phdr(phdr); | ||
| 2017 | +#endif | ||
| 2018 | +} | ||
| 2019 | + | ||
| 2020 | +static size_t note_size(const struct memelfnote *note) | ||
| 2021 | +{ | ||
| 2022 | + return (note->notesz); | ||
| 2023 | +} | ||
| 2024 | + | ||
| 2025 | +static void fill_prstatus(struct elf_prstatus *prstatus, | ||
| 2026 | + const TaskState *ts, int signr) | ||
| 2027 | +{ | ||
| 2028 | + (void) memset(prstatus, 0, sizeof (*prstatus)); | ||
| 2029 | + prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; | ||
| 2030 | + prstatus->pr_pid = ts->ts_tid; | ||
| 2031 | + prstatus->pr_ppid = getppid(); | ||
| 2032 | + prstatus->pr_pgrp = getpgrp(); | ||
| 2033 | + prstatus->pr_sid = getsid(0); | ||
| 2034 | + | ||
| 2035 | +#ifdef BSWAP_NEEDED | ||
| 2036 | + bswap_prstatus(prstatus); | ||
| 2037 | +#endif | ||
| 2038 | +} | ||
| 2039 | + | ||
| 2040 | +static int fill_psinfo(struct elf_prpsinfo *psinfo, const TaskState *ts) | ||
| 2041 | +{ | ||
| 2042 | + char *filename, *base_filename; | ||
| 2043 | + unsigned int i, len; | ||
| 2044 | + | ||
| 2045 | + (void) memset(psinfo, 0, sizeof (*psinfo)); | ||
| 2046 | + | ||
| 2047 | + len = ts->info->arg_end - ts->info->arg_start; | ||
| 2048 | + if (len >= ELF_PRARGSZ) | ||
| 2049 | + len = ELF_PRARGSZ - 1; | ||
| 2050 | + if (copy_from_user(&psinfo->pr_psargs, ts->info->arg_start, len)) | ||
| 2051 | + return -EFAULT; | ||
| 2052 | + for (i = 0; i < len; i++) | ||
| 2053 | + if (psinfo->pr_psargs[i] == 0) | ||
| 2054 | + psinfo->pr_psargs[i] = ' '; | ||
| 2055 | + psinfo->pr_psargs[len] = 0; | ||
| 2056 | + | ||
| 2057 | + psinfo->pr_pid = getpid(); | ||
| 2058 | + psinfo->pr_ppid = getppid(); | ||
| 2059 | + psinfo->pr_pgrp = getpgrp(); | ||
| 2060 | + psinfo->pr_sid = getsid(0); | ||
| 2061 | + psinfo->pr_uid = getuid(); | ||
| 2062 | + psinfo->pr_gid = getgid(); | ||
| 2063 | + | ||
| 2064 | + filename = strdup(ts->bprm->filename); | ||
| 2065 | + base_filename = strdup(basename(filename)); | ||
| 2066 | + (void) strncpy(psinfo->pr_fname, base_filename, | ||
| 2067 | + sizeof(psinfo->pr_fname)); | ||
| 2068 | + free(base_filename); | ||
| 2069 | + free(filename); | ||
| 2070 | + | ||
| 2071 | +#ifdef BSWAP_NEEDED | ||
| 2072 | + bswap_psinfo(psinfo); | ||
| 2073 | +#endif | ||
| 2074 | + return (0); | ||
| 2075 | +} | ||
| 2076 | + | ||
| 2077 | +static void fill_auxv_note(struct memelfnote *note, const TaskState *ts) | ||
| 2078 | +{ | ||
| 2079 | + elf_addr_t auxv = (elf_addr_t)ts->info->saved_auxv; | ||
| 2080 | + elf_addr_t orig_auxv = auxv; | ||
| 2081 | + abi_ulong val; | ||
| 2082 | + void *ptr; | ||
| 2083 | + int i, len; | ||
| 2084 | + | ||
| 2085 | + /* | ||
| 2086 | + * Auxiliary vector is stored in target process stack. It contains | ||
| 2087 | + * {type, value} pairs that we need to dump into note. This is not | ||
| 2088 | + * strictly necessary but we do it here for sake of completeness. | ||
| 2089 | + */ | ||
| 2090 | + | ||
| 2091 | + /* find out lenght of the vector, AT_NULL is terminator */ | ||
| 2092 | + i = len = 0; | ||
| 2093 | + do { | ||
| 2094 | + get_user_ual(val, auxv); | ||
| 2095 | + i += 2; | ||
| 2096 | + auxv += 2 * sizeof (elf_addr_t); | ||
| 2097 | + } while (val != AT_NULL); | ||
| 2098 | + len = i * sizeof (elf_addr_t); | ||
| 2099 | + | ||
| 2100 | + /* read in whole auxv vector and copy it to memelfnote */ | ||
| 2101 | + ptr = lock_user(VERIFY_READ, orig_auxv, len, 0); | ||
| 2102 | + if (ptr != NULL) { | ||
| 2103 | + fill_note(note, "CORE", NT_AUXV, len, ptr); | ||
| 2104 | + unlock_user(ptr, auxv, len); | ||
| 2105 | + } | ||
| 2106 | +} | ||
| 2107 | + | ||
| 2108 | +/* | ||
| 2109 | + * Constructs name of coredump file. We have following convention | ||
| 2110 | + * for the name: | ||
| 2111 | + * qemu_<basename-of-target-binary>_<date>-<time>_<pid>.core | ||
| 2112 | + * | ||
| 2113 | + * Returns 0 in case of success, -1 otherwise (errno is set). | ||
| 2114 | + */ | ||
| 2115 | +static int core_dump_filename(const TaskState *ts, char *buf, | ||
| 2116 | + size_t bufsize) | ||
| 2117 | +{ | ||
| 2118 | + char timestamp[64]; | ||
| 2119 | + char *filename = NULL; | ||
| 2120 | + char *base_filename = NULL; | ||
| 2121 | + struct timeval tv; | ||
| 2122 | + struct tm tm; | ||
| 2123 | + | ||
| 2124 | + assert(bufsize >= PATH_MAX); | ||
| 2125 | + | ||
| 2126 | + if (gettimeofday(&tv, NULL) < 0) { | ||
| 2127 | + (void) fprintf(stderr, "unable to get current timestamp: %s", | ||
| 2128 | + strerror(errno)); | ||
| 2129 | + return (-1); | ||
| 2130 | + } | ||
| 2131 | + | ||
| 2132 | + filename = strdup(ts->bprm->filename); | ||
| 2133 | + base_filename = strdup(basename(filename)); | ||
| 2134 | + (void) strftime(timestamp, sizeof (timestamp), "%Y%m%d-%H%M%S", | ||
| 2135 | + localtime_r(&tv.tv_sec, &tm)); | ||
| 2136 | + (void) snprintf(buf, bufsize, "qemu_%s_%s_%d.core", | ||
| 2137 | + base_filename, timestamp, (int)getpid()); | ||
| 2138 | + free(base_filename); | ||
| 2139 | + free(filename); | ||
| 2140 | + | ||
| 2141 | + return (0); | ||
| 2142 | +} | ||
| 2143 | + | ||
| 2144 | +static int dump_write(int fd, const void *ptr, size_t size) | ||
| 2145 | +{ | ||
| 2146 | + const char *bufp = (const char *)ptr; | ||
| 2147 | + ssize_t bytes_written, bytes_left; | ||
| 2148 | + struct rlimit dumpsize; | ||
| 2149 | + off_t pos; | ||
| 2150 | + | ||
| 2151 | + bytes_written = 0; | ||
| 2152 | + getrlimit(RLIMIT_CORE, &dumpsize); | ||
| 2153 | + if ((pos = lseek(fd, 0, SEEK_CUR))==-1) { | ||
| 2154 | + if (errno == ESPIPE) { /* not a seekable stream */ | ||
| 2155 | + bytes_left = size; | ||
| 2156 | + } else { | ||
| 2157 | + return pos; | ||
| 2158 | + } | ||
| 2159 | + } else { | ||
| 2160 | + if (dumpsize.rlim_cur <= pos) { | ||
| 2161 | + return -1; | ||
| 2162 | + } else if (dumpsize.rlim_cur == RLIM_INFINITY) { | ||
| 2163 | + bytes_left = size; | ||
| 2164 | + } else { | ||
| 2165 | + size_t limit_left=dumpsize.rlim_cur - pos; | ||
| 2166 | + bytes_left = limit_left >= size ? size : limit_left ; | ||
| 2167 | + } | ||
| 2168 | + } | ||
| 2169 | + | ||
| 2170 | + /* | ||
| 2171 | + * In normal conditions, single write(2) should do but | ||
| 2172 | + * in case of socket etc. this mechanism is more portable. | ||
| 2173 | + */ | ||
| 2174 | + do { | ||
| 2175 | + bytes_written = write(fd, bufp, bytes_left); | ||
| 2176 | + if (bytes_written < 0) { | ||
| 2177 | + if (errno == EINTR) | ||
| 2178 | + continue; | ||
| 2179 | + return (-1); | ||
| 2180 | + } else if (bytes_written == 0) { /* eof */ | ||
| 2181 | + return (-1); | ||
| 2182 | + } | ||
| 2183 | + bufp += bytes_written; | ||
| 2184 | + bytes_left -= bytes_written; | ||
| 2185 | + } while (bytes_left > 0); | ||
| 2186 | + | ||
| 2187 | + return (0); | ||
| 2188 | +} | ||
| 2189 | + | ||
| 2190 | +static int write_note(struct memelfnote *men, int fd) | ||
| 2191 | +{ | ||
| 2192 | + struct elf_note en; | ||
| 2193 | + | ||
| 2194 | + en.n_namesz = men->namesz; | ||
| 2195 | + en.n_type = men->type; | ||
| 2196 | + en.n_descsz = men->datasz; | ||
| 2197 | + | ||
| 2198 | +#ifdef BSWAP_NEEDED | ||
| 2199 | + bswap_note(&en); | ||
| 2200 | +#endif | ||
| 2201 | + | ||
| 2202 | + if (dump_write(fd, &en, sizeof(en)) != 0) | ||
| 2203 | + return (-1); | ||
| 2204 | + if (dump_write(fd, men->name, men->namesz_rounded) != 0) | ||
| 2205 | + return (-1); | ||
| 2206 | + if (dump_write(fd, men->data, men->datasz) != 0) | ||
| 2207 | + return (-1); | ||
| 2208 | + | ||
| 2209 | + return (0); | ||
| 2210 | +} | ||
| 2211 | + | ||
| 2212 | +static void fill_thread_info(struct elf_note_info *info, const CPUState *env) | ||
| 2213 | +{ | ||
| 2214 | + TaskState *ts = (TaskState *)env->opaque; | ||
| 2215 | + struct elf_thread_status *ets; | ||
| 2216 | + | ||
| 2217 | + ets = qemu_mallocz(sizeof (*ets)); | ||
| 2218 | + ets->num_notes = 1; /* only prstatus is dumped */ | ||
| 2219 | + fill_prstatus(&ets->prstatus, ts, 0); | ||
| 2220 | + elf_core_copy_regs(&ets->prstatus.pr_reg, env); | ||
| 2221 | + fill_note(&ets->notes[0], "CORE", NT_PRSTATUS, sizeof (ets->prstatus), | ||
| 2222 | + &ets->prstatus); | ||
| 2223 | + | ||
| 2224 | + TAILQ_INSERT_TAIL(&info->thread_list, ets, ets_link); | ||
| 2225 | + | ||
| 2226 | + info->notes_size += note_size(&ets->notes[0]); | ||
| 2227 | +} | ||
| 2228 | + | ||
| 2229 | +static int fill_note_info(struct elf_note_info *info, | ||
| 2230 | + long signr, const CPUState *env) | ||
| 2231 | +{ | ||
| 2232 | +#define NUMNOTES 3 | ||
| 2233 | + CPUState *cpu = NULL; | ||
| 2234 | + TaskState *ts = (TaskState *)env->opaque; | ||
| 2235 | + int i; | ||
| 2236 | + | ||
| 2237 | + (void) memset(info, 0, sizeof (*info)); | ||
| 2238 | + | ||
| 2239 | + TAILQ_INIT(&info->thread_list); | ||
| 2240 | + | ||
| 2241 | + info->notes = qemu_mallocz(NUMNOTES * sizeof (struct memelfnote)); | ||
| 2242 | + if (info->notes == NULL) | ||
| 2243 | + return (-ENOMEM); | ||
| 2244 | + info->prstatus = qemu_mallocz(sizeof (*info->prstatus)); | ||
| 2245 | + if (info->prstatus == NULL) | ||
| 2246 | + return (-ENOMEM); | ||
| 2247 | + info->psinfo = qemu_mallocz(sizeof (*info->psinfo)); | ||
| 2248 | + if (info->prstatus == NULL) | ||
| 2249 | + return (-ENOMEM); | ||
| 2250 | + | ||
| 2251 | + /* | ||
| 2252 | + * First fill in status (and registers) of current thread | ||
| 2253 | + * including process info & aux vector. | ||
| 2254 | + */ | ||
| 2255 | + fill_prstatus(info->prstatus, ts, signr); | ||
| 2256 | + elf_core_copy_regs(&info->prstatus->pr_reg, env); | ||
| 2257 | + fill_note(&info->notes[0], "CORE", NT_PRSTATUS, | ||
| 2258 | + sizeof (*info->prstatus), info->prstatus); | ||
| 2259 | + fill_psinfo(info->psinfo, ts); | ||
| 2260 | + fill_note(&info->notes[1], "CORE", NT_PRPSINFO, | ||
| 2261 | + sizeof (*info->psinfo), info->psinfo); | ||
| 2262 | + fill_auxv_note(&info->notes[2], ts); | ||
| 2263 | + info->numnote = 3; | ||
| 2264 | + | ||
| 2265 | + info->notes_size = 0; | ||
| 2266 | + for (i = 0; i < info->numnote; i++) | ||
| 2267 | + info->notes_size += note_size(&info->notes[i]); | ||
| 2268 | + | ||
| 2269 | + /* read and fill status of all threads */ | ||
| 2270 | + cpu_list_lock(); | ||
| 2271 | + for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) { | ||
| 2272 | + if (cpu == thread_env) | ||
| 2273 | + continue; | ||
| 2274 | + fill_thread_info(info, cpu); | ||
| 2275 | + } | ||
| 2276 | + cpu_list_unlock(); | ||
| 2277 | + | ||
| 2278 | + return (0); | ||
| 2279 | +} | ||
| 2280 | + | ||
| 2281 | +static void free_note_info(struct elf_note_info *info) | ||
| 2282 | +{ | ||
| 2283 | + struct elf_thread_status *ets; | ||
| 2284 | + | ||
| 2285 | + while (!TAILQ_EMPTY(&info->thread_list)) { | ||
| 2286 | + ets = TAILQ_FIRST(&info->thread_list); | ||
| 2287 | + TAILQ_REMOVE(&info->thread_list, ets, ets_link); | ||
| 2288 | + qemu_free(ets); | ||
| 2289 | + } | ||
| 2290 | + | ||
| 2291 | + qemu_free(info->prstatus); | ||
| 2292 | + qemu_free(info->psinfo); | ||
| 2293 | + qemu_free(info->notes); | ||
| 2294 | +} | ||
| 2295 | + | ||
| 2296 | +static int write_note_info(struct elf_note_info *info, int fd) | ||
| 2297 | +{ | ||
| 2298 | + struct elf_thread_status *ets; | ||
| 2299 | + int i, error = 0; | ||
| 2300 | + | ||
| 2301 | + /* write prstatus, psinfo and auxv for current thread */ | ||
| 2302 | + for (i = 0; i < info->numnote; i++) | ||
| 2303 | + if ((error = write_note(&info->notes[i], fd)) != 0) | ||
| 2304 | + return (error); | ||
| 2305 | + | ||
| 2306 | + /* write prstatus for each thread */ | ||
| 2307 | + for (ets = info->thread_list.tqh_first; ets != NULL; | ||
| 2308 | + ets = ets->ets_link.tqe_next) { | ||
| 2309 | + if ((error = write_note(&ets->notes[0], fd)) != 0) | ||
| 2310 | + return (error); | ||
| 2311 | + } | ||
| 2312 | + | ||
| 2313 | + return (0); | ||
| 2314 | +} | ||
| 2315 | + | ||
| 2316 | +/* | ||
| 2317 | + * Write out ELF coredump. | ||
| 2318 | + * | ||
| 2319 | + * See documentation of ELF object file format in: | ||
| 2320 | + * http://www.caldera.com/developers/devspecs/gabi41.pdf | ||
| 2321 | + * | ||
| 2322 | + * Coredump format in linux is following: | ||
| 2323 | + * | ||
| 2324 | + * 0 +----------------------+ \ | ||
| 2325 | + * | ELF header | ET_CORE | | ||
| 2326 | + * +----------------------+ | | ||
| 2327 | + * | ELF program headers | |--- headers | ||
| 2328 | + * | - NOTE section | | | ||
| 2329 | + * | - PT_LOAD sections | | | ||
| 2330 | + * +----------------------+ / | ||
| 2331 | + * | NOTEs: | | ||
| 2332 | + * | - NT_PRSTATUS | | ||
| 2333 | + * | - NT_PRSINFO | | ||
| 2334 | + * | - NT_AUXV | | ||
| 2335 | + * +----------------------+ <-- aligned to target page | ||
| 2336 | + * | Process memory dump | | ||
| 2337 | + * : : | ||
| 2338 | + * . . | ||
| 2339 | + * : : | ||
| 2340 | + * | | | ||
| 2341 | + * +----------------------+ | ||
| 2342 | + * | ||
| 2343 | + * NT_PRSTATUS -> struct elf_prstatus (per thread) | ||
| 2344 | + * NT_PRSINFO -> struct elf_prpsinfo | ||
| 2345 | + * NT_AUXV is array of { type, value } pairs (see fill_auxv_note()). | ||
| 2346 | + * | ||
| 2347 | + * Format follows System V format as close as possible. Current | ||
| 2348 | + * version limitations are as follows: | ||
| 2349 | + * - no floating point registers are dumped | ||
| 2350 | + * | ||
| 2351 | + * Function returns 0 in case of success, negative errno otherwise. | ||
| 2352 | + * | ||
| 2353 | + * TODO: make this work also during runtime: it should be | ||
| 2354 | + * possible to force coredump from running process and then | ||
| 2355 | + * continue processing. For example qemu could set up SIGUSR2 | ||
| 2356 | + * handler (provided that target process haven't registered | ||
| 2357 | + * handler for that) that does the dump when signal is received. | ||
| 2358 | + */ | ||
| 2359 | +static int elf_core_dump(int signr, const CPUState *env) | ||
| 2360 | +{ | ||
| 2361 | + const TaskState *ts = (const TaskState *)env->opaque; | ||
| 2362 | + struct vm_area_struct *vma = NULL; | ||
| 2363 | + char corefile[PATH_MAX]; | ||
| 2364 | + struct elf_note_info info; | ||
| 2365 | + struct elfhdr elf; | ||
| 2366 | + struct elf_phdr phdr; | ||
| 2367 | + struct rlimit dumpsize; | ||
| 2368 | + struct mm_struct *mm = NULL; | ||
| 2369 | + off_t offset = 0, data_offset = 0; | ||
| 2370 | + int segs = 0; | ||
| 2371 | + int fd = -1; | ||
| 2372 | + | ||
| 2373 | + errno = 0; | ||
| 2374 | + getrlimit(RLIMIT_CORE, &dumpsize); | ||
| 2375 | + if (dumpsize.rlim_cur == 0) | ||
| 2376 | + return 0; | ||
| 2377 | + | ||
| 2378 | + if (core_dump_filename(ts, corefile, sizeof (corefile)) < 0) | ||
| 2379 | + return (-errno); | ||
| 2380 | + | ||
| 2381 | + if ((fd = open(corefile, O_WRONLY | O_CREAT, | ||
| 2382 | + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) | ||
| 2383 | + return (-errno); | ||
| 2384 | + | ||
| 2385 | + /* | ||
| 2386 | + * Walk through target process memory mappings and | ||
| 2387 | + * set up structure containing this information. After | ||
| 2388 | + * this point vma_xxx functions can be used. | ||
| 2389 | + */ | ||
| 2390 | + if ((mm = vma_init()) == NULL) | ||
| 2391 | + goto out; | ||
| 2392 | + | ||
| 2393 | + walk_memory_regions(mm, vma_walker); | ||
| 2394 | + segs = vma_get_mapping_count(mm); | ||
| 2395 | + | ||
| 2396 | + /* | ||
| 2397 | + * Construct valid coredump ELF header. We also | ||
| 2398 | + * add one more segment for notes. | ||
| 2399 | + */ | ||
| 2400 | + fill_elf_header(&elf, segs + 1, ELF_MACHINE, 0); | ||
| 2401 | + if (dump_write(fd, &elf, sizeof (elf)) != 0) | ||
| 2402 | + goto out; | ||
| 2403 | + | ||
| 2404 | + /* fill in in-memory version of notes */ | ||
| 2405 | + if (fill_note_info(&info, signr, env) < 0) | ||
| 2406 | + goto out; | ||
| 2407 | + | ||
| 2408 | + offset += sizeof (elf); /* elf header */ | ||
| 2409 | + offset += (segs + 1) * sizeof (struct elf_phdr); /* program headers */ | ||
| 2410 | + | ||
| 2411 | + /* write out notes program header */ | ||
| 2412 | + fill_elf_note_phdr(&phdr, info.notes_size, offset); | ||
| 2413 | + | ||
| 2414 | + offset += info.notes_size; | ||
| 2415 | + if (dump_write(fd, &phdr, sizeof (phdr)) != 0) | ||
| 2416 | + goto out; | ||
| 2417 | + | ||
| 2418 | + /* | ||
| 2419 | + * ELF specification wants data to start at page boundary so | ||
| 2420 | + * we align it here. | ||
| 2421 | + */ | ||
| 2422 | + offset = roundup(offset, ELF_EXEC_PAGESIZE); | ||
| 2423 | + | ||
| 2424 | + /* | ||
| 2425 | + * Write program headers for memory regions mapped in | ||
| 2426 | + * the target process. | ||
| 2427 | + */ | ||
| 2428 | + for (vma = vma_first(mm); vma != NULL; vma = vma_next(vma)) { | ||
| 2429 | + (void) memset(&phdr, 0, sizeof (phdr)); | ||
| 2430 | + | ||
| 2431 | + phdr.p_type = PT_LOAD; | ||
| 2432 | + phdr.p_offset = offset; | ||
| 2433 | + phdr.p_vaddr = vma->vma_start; | ||
| 2434 | + phdr.p_paddr = 0; | ||
| 2435 | + phdr.p_filesz = vma_dump_size(vma); | ||
| 2436 | + offset += phdr.p_filesz; | ||
| 2437 | + phdr.p_memsz = vma->vma_end - vma->vma_start; | ||
| 2438 | + phdr.p_flags = vma->vma_flags & PROT_READ ? PF_R : 0; | ||
| 2439 | + if (vma->vma_flags & PROT_WRITE) | ||
| 2440 | + phdr.p_flags |= PF_W; | ||
| 2441 | + if (vma->vma_flags & PROT_EXEC) | ||
| 2442 | + phdr.p_flags |= PF_X; | ||
| 2443 | + phdr.p_align = ELF_EXEC_PAGESIZE; | ||
| 2444 | + | ||
| 2445 | + dump_write(fd, &phdr, sizeof (phdr)); | ||
| 2446 | + } | ||
| 2447 | + | ||
| 2448 | + /* | ||
| 2449 | + * Next we write notes just after program headers. No | ||
| 2450 | + * alignment needed here. | ||
| 2451 | + */ | ||
| 2452 | + if (write_note_info(&info, fd) < 0) | ||
| 2453 | + goto out; | ||
| 2454 | + | ||
| 2455 | + /* align data to page boundary */ | ||
| 2456 | + data_offset = lseek(fd, 0, SEEK_CUR); | ||
| 2457 | + data_offset = TARGET_PAGE_ALIGN(data_offset); | ||
| 2458 | + if (lseek(fd, data_offset, SEEK_SET) != data_offset) | ||
| 2459 | + goto out; | ||
| 2460 | + | ||
| 2461 | + /* | ||
| 2462 | + * Finally we can dump process memory into corefile as well. | ||
| 2463 | + */ | ||
| 2464 | + for (vma = vma_first(mm); vma != NULL; vma = vma_next(vma)) { | ||
| 2465 | + abi_ulong addr; | ||
| 2466 | + abi_ulong end; | ||
| 2467 | + | ||
| 2468 | + end = vma->vma_start + vma_dump_size(vma); | ||
| 2469 | + | ||
| 2470 | + for (addr = vma->vma_start; addr < end; | ||
| 2471 | + addr += TARGET_PAGE_SIZE) { | ||
| 2472 | + char page[TARGET_PAGE_SIZE]; | ||
| 2473 | + int error; | ||
| 2474 | + | ||
| 2475 | + /* | ||
| 2476 | + * Read in page from target process memory and | ||
| 2477 | + * write it to coredump file. | ||
| 2478 | + */ | ||
| 2479 | + error = copy_from_user(page, addr, sizeof (page)); | ||
| 2480 | + if (error != 0) { | ||
| 2481 | + (void) fprintf(stderr, "unable to dump " TARGET_FMT_lx "\n", | ||
| 2482 | + addr); | ||
| 2483 | + errno = -error; | ||
| 2484 | + goto out; | ||
| 2485 | + } | ||
| 2486 | + if (dump_write(fd, page, TARGET_PAGE_SIZE) < 0) | ||
| 2487 | + goto out; | ||
| 2488 | + } | ||
| 2489 | + } | ||
| 2490 | + | ||
| 2491 | +out: | ||
| 2492 | + free_note_info(&info); | ||
| 2493 | + if (mm != NULL) | ||
| 2494 | + vma_delete(mm); | ||
| 2495 | + (void) close(fd); | ||
| 2496 | + | ||
| 2497 | + if (errno != 0) | ||
| 2498 | + return (-errno); | ||
| 2499 | + return (0); | ||
| 2500 | +} | ||
| 2501 | + | ||
| 2502 | +#endif /* USE_ELF_CORE_DUMP */ | ||
| 2503 | + | ||
| 1592 | static int load_aout_interp(void * exptr, int interp_fd) | 2504 | static int load_aout_interp(void * exptr, int interp_fd) |
| 1593 | { | 2505 | { |
| 1594 | printf("a.out interpreter not yet supported\n"); | 2506 | printf("a.out interpreter not yet supported\n"); |
linux-user/linuxload.c
| @@ -115,6 +115,7 @@ static int prepare_binprm(struct linux_binprm *bprm) | @@ -115,6 +115,7 @@ static int prepare_binprm(struct linux_binprm *bprm) | ||
| 115 | abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, | 115 | abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, |
| 116 | abi_ulong stringp, int push_ptr) | 116 | abi_ulong stringp, int push_ptr) |
| 117 | { | 117 | { |
| 118 | + TaskState *ts = (TaskState *)thread_env->opaque; | ||
| 118 | int n = sizeof(abi_ulong); | 119 | int n = sizeof(abi_ulong); |
| 119 | abi_ulong envp; | 120 | abi_ulong envp; |
| 120 | abi_ulong argv; | 121 | abi_ulong argv; |
| @@ -133,13 +134,14 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, | @@ -133,13 +134,14 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, | ||
| 133 | sp -= n; | 134 | sp -= n; |
| 134 | /* FIXME - handle put_user() failures */ | 135 | /* FIXME - handle put_user() failures */ |
| 135 | put_user_ual(argc, sp); | 136 | put_user_ual(argc, sp); |
| 136 | - | 137 | + ts->info->arg_start = stringp; |
| 137 | while (argc-- > 0) { | 138 | while (argc-- > 0) { |
| 138 | /* FIXME - handle put_user() failures */ | 139 | /* FIXME - handle put_user() failures */ |
| 139 | put_user_ual(stringp, argv); | 140 | put_user_ual(stringp, argv); |
| 140 | argv += n; | 141 | argv += n; |
| 141 | stringp += target_strlen(stringp) + 1; | 142 | stringp += target_strlen(stringp) + 1; |
| 142 | } | 143 | } |
| 144 | + ts->info->arg_end = stringp; | ||
| 143 | /* FIXME - handle put_user() failures */ | 145 | /* FIXME - handle put_user() failures */ |
| 144 | put_user_ual(0, argv); | 146 | put_user_ual(0, argv); |
| 145 | while (envc-- > 0) { | 147 | while (envc-- > 0) { |
| @@ -155,45 +157,45 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, | @@ -155,45 +157,45 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, | ||
| 155 | } | 157 | } |
| 156 | 158 | ||
| 157 | int loader_exec(const char * filename, char ** argv, char ** envp, | 159 | int loader_exec(const char * filename, char ** argv, char ** envp, |
| 158 | - struct target_pt_regs * regs, struct image_info *infop) | 160 | + struct target_pt_regs * regs, struct image_info *infop, |
| 161 | + struct linux_binprm *bprm) | ||
| 159 | { | 162 | { |
| 160 | - struct linux_binprm bprm; | ||
| 161 | int retval; | 163 | int retval; |
| 162 | int i; | 164 | int i; |
| 163 | 165 | ||
| 164 | - bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); | 166 | + bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); |
| 165 | for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ | 167 | for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ |
| 166 | - bprm.page[i] = 0; | 168 | + bprm->page[i] = 0; |
| 167 | retval = open(filename, O_RDONLY); | 169 | retval = open(filename, O_RDONLY); |
| 168 | if (retval < 0) | 170 | if (retval < 0) |
| 169 | return retval; | 171 | return retval; |
| 170 | - bprm.fd = retval; | ||
| 171 | - bprm.filename = (char *)filename; | ||
| 172 | - bprm.argc = count(argv); | ||
| 173 | - bprm.argv = argv; | ||
| 174 | - bprm.envc = count(envp); | ||
| 175 | - bprm.envp = envp; | 172 | + bprm->fd = retval; |
| 173 | + bprm->filename = (char *)filename; | ||
| 174 | + bprm->argc = count(argv); | ||
| 175 | + bprm->argv = argv; | ||
| 176 | + bprm->envc = count(envp); | ||
| 177 | + bprm->envp = envp; | ||
| 176 | 178 | ||
| 177 | - retval = prepare_binprm(&bprm); | 179 | + retval = prepare_binprm(bprm); |
| 178 | 180 | ||
| 179 | infop->host_argv = argv; | 181 | infop->host_argv = argv; |
| 180 | 182 | ||
| 181 | if(retval>=0) { | 183 | if(retval>=0) { |
| 182 | - if (bprm.buf[0] == 0x7f | ||
| 183 | - && bprm.buf[1] == 'E' | ||
| 184 | - && bprm.buf[2] == 'L' | ||
| 185 | - && bprm.buf[3] == 'F') { | 184 | + if (bprm->buf[0] == 0x7f |
| 185 | + && bprm->buf[1] == 'E' | ||
| 186 | + && bprm->buf[2] == 'L' | ||
| 187 | + && bprm->buf[3] == 'F') { | ||
| 186 | #ifndef TARGET_HAS_ELFLOAD32 | 188 | #ifndef TARGET_HAS_ELFLOAD32 |
| 187 | - retval = load_elf_binary(&bprm,regs,infop); | 189 | + retval = load_elf_binary(bprm,regs,infop); |
| 188 | #else | 190 | #else |
| 189 | - retval = load_elf_binary_multi(&bprm, regs, infop); | 191 | + retval = load_elf_binary_multi(bprm, regs, infop); |
| 190 | #endif | 192 | #endif |
| 191 | #if defined(TARGET_HAS_BFLT) | 193 | #if defined(TARGET_HAS_BFLT) |
| 192 | - } else if (bprm.buf[0] == 'b' | ||
| 193 | - && bprm.buf[1] == 'F' | ||
| 194 | - && bprm.buf[2] == 'L' | ||
| 195 | - && bprm.buf[3] == 'T') { | ||
| 196 | - retval = load_flt_binary(&bprm,regs,infop); | 194 | + } else if (bprm->buf[0] == 'b' |
| 195 | + && bprm->buf[1] == 'F' | ||
| 196 | + && bprm->buf[2] == 'L' | ||
| 197 | + && bprm->buf[3] == 'T') { | ||
| 198 | + retval = load_flt_binary(bprm,regs,infop); | ||
| 197 | #endif | 199 | #endif |
| 198 | } else { | 200 | } else { |
| 199 | fprintf(stderr, "Unknown binary format\n"); | 201 | fprintf(stderr, "Unknown binary format\n"); |
| @@ -209,7 +211,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp, | @@ -209,7 +211,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp, | ||
| 209 | 211 | ||
| 210 | /* Something went wrong, return the inode and free the argument pages*/ | 212 | /* Something went wrong, return the inode and free the argument pages*/ |
| 211 | for (i=0 ; i<MAX_ARG_PAGES ; i++) { | 213 | for (i=0 ; i<MAX_ARG_PAGES ; i++) { |
| 212 | - free(bprm.page[i]); | 214 | + free(bprm->page[i]); |
| 213 | } | 215 | } |
| 214 | return(retval); | 216 | return(retval); |
| 215 | } | 217 | } |
linux-user/main.c
| @@ -25,6 +25,7 @@ | @@ -25,6 +25,7 @@ | ||
| 25 | #include <errno.h> | 25 | #include <errno.h> |
| 26 | #include <unistd.h> | 26 | #include <unistd.h> |
| 27 | #include <sys/mman.h> | 27 | #include <sys/mman.h> |
| 28 | +#include <sys/syscall.h> | ||
| 28 | 29 | ||
| 29 | #include "qemu.h" | 30 | #include "qemu.h" |
| 30 | #include "qemu-common.h" | 31 | #include "qemu-common.h" |
| @@ -2319,6 +2320,27 @@ static void usage(void) | @@ -2319,6 +2320,27 @@ static void usage(void) | ||
| 2319 | 2320 | ||
| 2320 | THREAD CPUState *thread_env; | 2321 | THREAD CPUState *thread_env; |
| 2321 | 2322 | ||
| 2323 | +void task_settid(TaskState *ts) | ||
| 2324 | +{ | ||
| 2325 | + if (ts->ts_tid == 0) { | ||
| 2326 | +#ifdef USE_NPTL | ||
| 2327 | + ts->ts_tid = (pid_t)syscall(SYS_gettid); | ||
| 2328 | +#else | ||
| 2329 | + /* when no threads are used, tid becomes pid */ | ||
| 2330 | + ts->ts_tid = getpid(); | ||
| 2331 | +#endif | ||
| 2332 | + } | ||
| 2333 | +} | ||
| 2334 | + | ||
| 2335 | +void stop_all_tasks(void) | ||
| 2336 | +{ | ||
| 2337 | + /* | ||
| 2338 | + * We trust that when using NPTL, start_exclusive() | ||
| 2339 | + * handles thread stopping correctly. | ||
| 2340 | + */ | ||
| 2341 | + start_exclusive(); | ||
| 2342 | +} | ||
| 2343 | + | ||
| 2322 | /* Assumes contents are already zeroed. */ | 2344 | /* Assumes contents are already zeroed. */ |
| 2323 | void init_task_state(TaskState *ts) | 2345 | void init_task_state(TaskState *ts) |
| 2324 | { | 2346 | { |
| @@ -2338,6 +2360,7 @@ int main(int argc, char **argv, char **envp) | @@ -2338,6 +2360,7 @@ int main(int argc, char **argv, char **envp) | ||
| 2338 | const char *cpu_model; | 2360 | const char *cpu_model; |
| 2339 | struct target_pt_regs regs1, *regs = ®s1; | 2361 | struct target_pt_regs regs1, *regs = ®s1; |
| 2340 | struct image_info info1, *info = &info1; | 2362 | struct image_info info1, *info = &info1; |
| 2363 | + struct linux_binprm bprm; | ||
| 2341 | TaskState ts1, *ts = &ts1; | 2364 | TaskState ts1, *ts = &ts1; |
| 2342 | CPUState *env; | 2365 | CPUState *env; |
| 2343 | int optind; | 2366 | int optind; |
| @@ -2467,6 +2490,8 @@ int main(int argc, char **argv, char **envp) | @@ -2467,6 +2490,8 @@ int main(int argc, char **argv, char **envp) | ||
| 2467 | /* Zero out image_info */ | 2490 | /* Zero out image_info */ |
| 2468 | memset(info, 0, sizeof(struct image_info)); | 2491 | memset(info, 0, sizeof(struct image_info)); |
| 2469 | 2492 | ||
| 2493 | + memset(&bprm, 0, sizeof (bprm)); | ||
| 2494 | + | ||
| 2470 | /* Scan interp_prefix dir for replacement files. */ | 2495 | /* Scan interp_prefix dir for replacement files. */ |
| 2471 | init_paths(interp_prefix); | 2496 | init_paths(interp_prefix); |
| 2472 | 2497 | ||
| @@ -2543,7 +2568,16 @@ int main(int argc, char **argv, char **envp) | @@ -2543,7 +2568,16 @@ int main(int argc, char **argv, char **envp) | ||
| 2543 | } | 2568 | } |
| 2544 | target_argv[target_argc] = NULL; | 2569 | target_argv[target_argc] = NULL; |
| 2545 | 2570 | ||
| 2546 | - if (loader_exec(filename, target_argv, target_environ, regs, info) != 0) { | 2571 | + memset(ts, 0, sizeof(TaskState)); |
| 2572 | + init_task_state(ts); | ||
| 2573 | + /* build Task State */ | ||
| 2574 | + ts->info = info; | ||
| 2575 | + ts->bprm = &bprm; | ||
| 2576 | + env->opaque = ts; | ||
| 2577 | + task_settid(ts); | ||
| 2578 | + | ||
| 2579 | + if (loader_exec(filename, target_argv, target_environ, regs, | ||
| 2580 | + info, &bprm) != 0) { | ||
| 2547 | printf("Error loading %s\n", filename); | 2581 | printf("Error loading %s\n", filename); |
| 2548 | _exit(1); | 2582 | _exit(1); |
| 2549 | } | 2583 | } |
| @@ -2579,12 +2613,6 @@ int main(int argc, char **argv, char **envp) | @@ -2579,12 +2613,6 @@ int main(int argc, char **argv, char **envp) | ||
| 2579 | syscall_init(); | 2613 | syscall_init(); |
| 2580 | signal_init(); | 2614 | signal_init(); |
| 2581 | 2615 | ||
| 2582 | - /* build Task State */ | ||
| 2583 | - memset(ts, 0, sizeof(TaskState)); | ||
| 2584 | - init_task_state(ts); | ||
| 2585 | - ts->info = info; | ||
| 2586 | - env->opaque = ts; | ||
| 2587 | - | ||
| 2588 | #if defined(TARGET_I386) | 2616 | #if defined(TARGET_I386) |
| 2589 | cpu_x86_set_cpl(env, 3); | 2617 | cpu_x86_set_cpl(env, 3); |
| 2590 | 2618 |
linux-user/qemu.h
| @@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
| 18 | #include "syscall.h" | 18 | #include "syscall.h" |
| 19 | #include "target_signal.h" | 19 | #include "target_signal.h" |
| 20 | #include "gdbstub.h" | 20 | #include "gdbstub.h" |
| 21 | +#include "sys-queue.h" | ||
| 21 | 22 | ||
| 22 | #if defined(USE_NPTL) | 23 | #if defined(USE_NPTL) |
| 23 | #define THREAD __thread | 24 | #define THREAD __thread |
| @@ -44,6 +45,9 @@ struct image_info { | @@ -44,6 +45,9 @@ struct image_info { | ||
| 44 | abi_ulong entry; | 45 | abi_ulong entry; |
| 45 | abi_ulong code_offset; | 46 | abi_ulong code_offset; |
| 46 | abi_ulong data_offset; | 47 | abi_ulong data_offset; |
| 48 | + abi_ulong saved_auxv; | ||
| 49 | + abi_ulong arg_start; | ||
| 50 | + abi_ulong arg_end; | ||
| 47 | char **host_argv; | 51 | char **host_argv; |
| 48 | int personality; | 52 | int personality; |
| 49 | }; | 53 | }; |
| @@ -87,7 +91,7 @@ struct emulated_sigtable { | @@ -87,7 +91,7 @@ struct emulated_sigtable { | ||
| 87 | /* NOTE: we force a big alignment so that the stack stored after is | 91 | /* NOTE: we force a big alignment so that the stack stored after is |
| 88 | aligned too */ | 92 | aligned too */ |
| 89 | typedef struct TaskState { | 93 | typedef struct TaskState { |
| 90 | - struct TaskState *next; | 94 | + pid_t ts_tid; /* tid (or pid) of this task */ |
| 91 | #ifdef TARGET_ARM | 95 | #ifdef TARGET_ARM |
| 92 | /* FPA state */ | 96 | /* FPA state */ |
| 93 | FPA11 fpa; | 97 | FPA11 fpa; |
| @@ -114,6 +118,7 @@ typedef struct TaskState { | @@ -114,6 +118,7 @@ typedef struct TaskState { | ||
| 114 | #endif | 118 | #endif |
| 115 | int used; /* non zero if used */ | 119 | int used; /* non zero if used */ |
| 116 | struct image_info *info; | 120 | struct image_info *info; |
| 121 | + struct linux_binprm *bprm; | ||
| 117 | 122 | ||
| 118 | struct emulated_sigtable sigtab[TARGET_NSIG]; | 123 | struct emulated_sigtable sigtab[TARGET_NSIG]; |
| 119 | struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ | 124 | struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ |
| @@ -125,6 +130,8 @@ typedef struct TaskState { | @@ -125,6 +130,8 @@ typedef struct TaskState { | ||
| 125 | 130 | ||
| 126 | extern char *exec_path; | 131 | extern char *exec_path; |
| 127 | void init_task_state(TaskState *ts); | 132 | void init_task_state(TaskState *ts); |
| 133 | +void task_settid(TaskState *); | ||
| 134 | +void stop_all_tasks(void); | ||
| 128 | extern const char *qemu_uname_release; | 135 | extern const char *qemu_uname_release; |
| 129 | 136 | ||
| 130 | /* ??? See if we can avoid exposing so much of the loader internals. */ | 137 | /* ??? See if we can avoid exposing so much of the loader internals. */ |
| @@ -149,13 +156,15 @@ struct linux_binprm { | @@ -149,13 +156,15 @@ struct linux_binprm { | ||
| 149 | char **argv; | 156 | char **argv; |
| 150 | char **envp; | 157 | char **envp; |
| 151 | char * filename; /* Name of binary */ | 158 | char * filename; /* Name of binary */ |
| 159 | + int (*core_dump)(int, const CPUState *); /* coredump routine */ | ||
| 152 | }; | 160 | }; |
| 153 | 161 | ||
| 154 | void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); | 162 | void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); |
| 155 | abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, | 163 | abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, |
| 156 | abi_ulong stringp, int push_ptr); | 164 | abi_ulong stringp, int push_ptr); |
| 157 | int loader_exec(const char * filename, char ** argv, char ** envp, | 165 | int loader_exec(const char * filename, char ** argv, char ** envp, |
| 158 | - struct target_pt_regs * regs, struct image_info *infop); | 166 | + struct target_pt_regs * regs, struct image_info *infop, |
| 167 | + struct linux_binprm *); | ||
| 159 | 168 | ||
| 160 | int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, | 169 | int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, |
| 161 | struct image_info * info); | 170 | struct image_info * info); |
linux-user/signal.c
| @@ -27,6 +27,7 @@ | @@ -27,6 +27,7 @@ | ||
| 27 | #include <errno.h> | 27 | #include <errno.h> |
| 28 | #include <assert.h> | 28 | #include <assert.h> |
| 29 | #include <sys/ucontext.h> | 29 | #include <sys/ucontext.h> |
| 30 | +#include <sys/resource.h> | ||
| 30 | 31 | ||
| 31 | #include "qemu.h" | 32 | #include "qemu.h" |
| 32 | #include "qemu-common.h" | 33 | #include "qemu-common.h" |
| @@ -287,6 +288,23 @@ static int fatal_signal (int sig) | @@ -287,6 +288,23 @@ static int fatal_signal (int sig) | ||
| 287 | } | 288 | } |
| 288 | } | 289 | } |
| 289 | 290 | ||
| 291 | +/* returns 1 if given signal should dump core if not handled */ | ||
| 292 | +static int core_dump_signal(int sig) | ||
| 293 | +{ | ||
| 294 | + switch (sig) { | ||
| 295 | + case TARGET_SIGABRT: | ||
| 296 | + case TARGET_SIGFPE: | ||
| 297 | + case TARGET_SIGILL: | ||
| 298 | + case TARGET_SIGQUIT: | ||
| 299 | + case TARGET_SIGSEGV: | ||
| 300 | + case TARGET_SIGTRAP: | ||
| 301 | + case TARGET_SIGBUS: | ||
| 302 | + return (1); | ||
| 303 | + default: | ||
| 304 | + return (0); | ||
| 305 | + } | ||
| 306 | +} | ||
| 307 | + | ||
| 290 | void signal_init(void) | 308 | void signal_init(void) |
| 291 | { | 309 | { |
| 292 | struct sigaction act; | 310 | struct sigaction act; |
| @@ -352,13 +370,29 @@ static inline void free_sigqueue(CPUState *env, struct sigqueue *q) | @@ -352,13 +370,29 @@ static inline void free_sigqueue(CPUState *env, struct sigqueue *q) | ||
| 352 | /* abort execution with signal */ | 370 | /* abort execution with signal */ |
| 353 | static void QEMU_NORETURN force_sig(int sig) | 371 | static void QEMU_NORETURN force_sig(int sig) |
| 354 | { | 372 | { |
| 355 | - int host_sig; | 373 | + TaskState *ts = (TaskState *)thread_env->opaque; |
| 374 | + int host_sig, core_dumped = 0; | ||
| 356 | struct sigaction act; | 375 | struct sigaction act; |
| 357 | host_sig = target_to_host_signal(sig); | 376 | host_sig = target_to_host_signal(sig); |
| 358 | - fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n", | ||
| 359 | - sig, strsignal(host_sig)); | ||
| 360 | gdb_signalled(thread_env, sig); | 377 | gdb_signalled(thread_env, sig); |
| 361 | 378 | ||
| 379 | + /* dump core if supported by target binary format */ | ||
| 380 | + if (core_dump_signal(sig) && (ts->bprm->core_dump != NULL)) { | ||
| 381 | + stop_all_tasks(); | ||
| 382 | + core_dumped = | ||
| 383 | + ((*ts->bprm->core_dump)(sig, thread_env) == 0); | ||
| 384 | + } | ||
| 385 | + if (core_dumped) { | ||
| 386 | + /* we already dumped the core of target process, we don't want | ||
| 387 | + * a coredump of qemu itself */ | ||
| 388 | + struct rlimit nodump; | ||
| 389 | + getrlimit(RLIMIT_CORE, &nodump); | ||
| 390 | + nodump.rlim_cur=0; | ||
| 391 | + setrlimit(RLIMIT_CORE, &nodump); | ||
| 392 | + (void) fprintf(stderr, "qemu: uncaught target signal %d (%s) - %s\n", | ||
| 393 | + sig, strsignal(host_sig), "core dumped" ); | ||
| 394 | + } | ||
| 395 | + | ||
| 362 | /* The proper exit code for dieing from an uncaught signal is | 396 | /* The proper exit code for dieing from an uncaught signal is |
| 363 | * -<signal>. The kernel doesn't allow exit() or _exit() to pass | 397 | * -<signal>. The kernel doesn't allow exit() or _exit() to pass |
| 364 | * a negative value. To get the proper exit code we need to | 398 | * a negative value. To get the proper exit code we need to |
linux-user/syscall.c
| @@ -3379,11 +3379,14 @@ static void *clone_func(void *arg) | @@ -3379,11 +3379,14 @@ static void *clone_func(void *arg) | ||
| 3379 | { | 3379 | { |
| 3380 | new_thread_info *info = arg; | 3380 | new_thread_info *info = arg; |
| 3381 | CPUState *env; | 3381 | CPUState *env; |
| 3382 | + TaskState *ts; | ||
| 3382 | 3383 | ||
| 3383 | env = info->env; | 3384 | env = info->env; |
| 3384 | thread_env = env; | 3385 | thread_env = env; |
| 3386 | + ts = (TaskState *)thread_env->opaque; | ||
| 3385 | info->tid = gettid(); | 3387 | info->tid = gettid(); |
| 3386 | env->host_tid = info->tid; | 3388 | env->host_tid = info->tid; |
| 3389 | + task_settid(ts); | ||
| 3387 | if (info->child_tidptr) | 3390 | if (info->child_tidptr) |
| 3388 | put_user_u32(info->tid, info->child_tidptr); | 3391 | put_user_u32(info->tid, info->child_tidptr); |
| 3389 | if (info->parent_tidptr) | 3392 | if (info->parent_tidptr) |
| @@ -3435,6 +3438,7 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, | @@ -3435,6 +3438,7 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, | ||
| 3435 | flags &= ~(CLONE_VFORK | CLONE_VM); | 3438 | flags &= ~(CLONE_VFORK | CLONE_VM); |
| 3436 | 3439 | ||
| 3437 | if (flags & CLONE_VM) { | 3440 | if (flags & CLONE_VM) { |
| 3441 | + TaskState *parent_ts = (TaskState *)env->opaque; | ||
| 3438 | #if defined(USE_NPTL) | 3442 | #if defined(USE_NPTL) |
| 3439 | new_thread_info info; | 3443 | new_thread_info info; |
| 3440 | pthread_attr_t attr; | 3444 | pthread_attr_t attr; |
| @@ -3447,6 +3451,8 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, | @@ -3447,6 +3451,8 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, | ||
| 3447 | /* Init regs that differ from the parent. */ | 3451 | /* Init regs that differ from the parent. */ |
| 3448 | cpu_clone_regs(new_env, newsp); | 3452 | cpu_clone_regs(new_env, newsp); |
| 3449 | new_env->opaque = ts; | 3453 | new_env->opaque = ts; |
| 3454 | + ts->bprm = parent_ts->bprm; | ||
| 3455 | + ts->info = parent_ts->info; | ||
| 3450 | #if defined(USE_NPTL) | 3456 | #if defined(USE_NPTL) |
| 3451 | nptl_flags = flags; | 3457 | nptl_flags = flags; |
| 3452 | flags &= ~CLONE_NPTL_FLAGS2; | 3458 | flags &= ~CLONE_NPTL_FLAGS2; |