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 | 735 | #define PAGE_RESERVED 0x0020 |
736 | 736 | |
737 | 737 | void page_dump(FILE *f); |
738 | +int walk_memory_regions(void *, | |
739 | + int (*fn)(void *, unsigned long, unsigned long, unsigned long)); | |
738 | 740 | int page_get_flags(target_ulong address); |
739 | 741 | void page_set_flags(target_ulong start, target_ulong end, int flags); |
740 | 742 | int page_check_range(target_ulong start, target_ulong len, int flags); | ... | ... |
elf.h
... | ... | @@ -1081,7 +1081,23 @@ typedef struct elf64_shdr { |
1081 | 1081 | #define EI_CLASS 4 |
1082 | 1082 | #define EI_DATA 5 |
1083 | 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 | 1102 | #define ELFMAG0 0x7f /* EI_MAG */ |
1087 | 1103 | #define ELFMAG1 'E' |
... | ... | @@ -1108,6 +1124,7 @@ typedef struct elf64_shdr { |
1108 | 1124 | #define NT_PRFPREG 2 |
1109 | 1125 | #define NT_PRPSINFO 3 |
1110 | 1126 | #define NT_TASKSTRUCT 4 |
1127 | +#define NT_AUXV 6 | |
1111 | 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 | 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 | 2141 | unsigned long start, end; |
2142 | + PageDesc *p = NULL; | |
2138 | 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 | 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 | 2157 | if (prot1 != prot) { |
2157 | 2158 | end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); |
2158 | 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 | 2165 | if (prot1 != 0) |
2166 | 2166 | start = end; |
... | ... | @@ -2168,10 +2168,33 @@ void page_dump(FILE *f) |
2168 | 2168 | start = -1; |
2169 | 2169 | prot = prot1; |
2170 | 2170 | } |
2171 | - if (!p) | |
2171 | + if (p == NULL) | |
2172 | 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 | 2200 | int page_get_flags(target_ulong address) | ... | ... |
linux-user/elfload.c
1 | 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 | 5 | #include <stdio.h> |
4 | 6 | #include <sys/types.h> |
... | ... | @@ -6,8 +8,10 @@ |
6 | 8 | #include <errno.h> |
7 | 9 | #include <unistd.h> |
8 | 10 | #include <sys/mman.h> |
11 | +#include <sys/resource.h> | |
9 | 12 | #include <stdlib.h> |
10 | 13 | #include <string.h> |
14 | +#include <time.h> | |
11 | 15 | |
12 | 16 | #include "qemu.h" |
13 | 17 | #include "disas.h" |
... | ... | @@ -21,6 +25,8 @@ |
21 | 25 | #undef ELF_ARCH |
22 | 26 | #endif |
23 | 27 | |
28 | +#define ELF_OSABI ELFOSABI_SYSV | |
29 | + | |
24 | 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 | 166 | } |
161 | 167 | #endif |
162 | 168 | |
163 | -#define USE_ELF_CORE_DUMP | |
164 | 169 | #define ELF_EXEC_PAGESIZE 4096 |
165 | 170 | |
166 | 171 | #endif |
... | ... | @@ -198,6 +203,37 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i |
198 | 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 | 237 | #define USE_ELF_CORE_DUMP |
202 | 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 | 454 | _regs->gpr[5] = pos; |
419 | 455 | } |
420 | 456 | |
421 | -#define USE_ELF_CORE_DUMP | |
422 | 457 | #define ELF_EXEC_PAGESIZE 4096 |
423 | 458 | |
424 | 459 | #endif |
... | ... | @@ -448,7 +483,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i |
448 | 483 | regs->regs[29] = infop->start_stack; |
449 | 484 | } |
450 | 485 | |
451 | -#define USE_ELF_CORE_DUMP | |
452 | 486 | #define ELF_EXEC_PAGESIZE 4096 |
453 | 487 | |
454 | 488 | #endif /* TARGET_MIPS */ |
... | ... | @@ -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 | 507 | #define ELF_EXEC_PAGESIZE 4096 |
475 | 508 | |
476 | 509 | #endif /* TARGET_MICROBLAZE */ |
... | ... | @@ -492,7 +525,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i |
492 | 525 | regs->regs[15] = infop->start_stack; |
493 | 526 | } |
494 | 527 | |
495 | -#define USE_ELF_CORE_DUMP | |
496 | 528 | #define ELF_EXEC_PAGESIZE 4096 |
497 | 529 | |
498 | 530 | #endif |
... | ... | @@ -512,7 +544,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i |
512 | 544 | regs->erp = infop->entry; |
513 | 545 | } |
514 | 546 | |
515 | -#define USE_ELF_CORE_DUMP | |
516 | 547 | #define ELF_EXEC_PAGESIZE 8192 |
517 | 548 | |
518 | 549 | #endif |
... | ... | @@ -537,7 +568,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i |
537 | 568 | regs->pc = infop->entry; |
538 | 569 | } |
539 | 570 | |
540 | -#define USE_ELF_CORE_DUMP | |
541 | 571 | #define ELF_EXEC_PAGESIZE 8192 |
542 | 572 | |
543 | 573 | #endif |
... | ... | @@ -562,7 +592,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i |
562 | 592 | regs->unique, infop->start_data); |
563 | 593 | } |
564 | 594 | |
565 | -#define USE_ELF_CORE_DUMP | |
566 | 595 | #define ELF_EXEC_PAGESIZE 8192 |
567 | 596 | |
568 | 597 | #endif /* TARGET_ALPHA */ |
... | ... | @@ -680,6 +709,20 @@ static void bswap_sym(struct elf_sym *sym) |
680 | 709 | } |
681 | 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 | 727 | * 'copy_elf_strings()' copies argument/envelope strings from user |
685 | 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 | 947 | #endif |
905 | 948 | #undef NEW_AUX_ENT |
906 | 949 | |
950 | + info->saved_auxv = sp; | |
951 | + | |
907 | 952 | sp = loader_build_argptr(envc, argc, sp, p, !ibcs); |
908 | 953 | return sp; |
909 | 954 | } |
... | ... | @@ -1586,9 +1631,876 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, |
1586 | 1631 | |
1587 | 1632 | info->entry = elf_entry; |
1588 | 1633 | |
1634 | +#ifdef USE_ELF_CORE_DUMP | |
1635 | + bprm->core_dump = &elf_core_dump; | |
1636 | +#endif | |
1637 | + | |
1589 | 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 | 2504 | static int load_aout_interp(void * exptr, int interp_fd) |
1593 | 2505 | { |
1594 | 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 | 115 | abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, |
116 | 116 | abi_ulong stringp, int push_ptr) |
117 | 117 | { |
118 | + TaskState *ts = (TaskState *)thread_env->opaque; | |
118 | 119 | int n = sizeof(abi_ulong); |
119 | 120 | abi_ulong envp; |
120 | 121 | abi_ulong argv; |
... | ... | @@ -133,13 +134,14 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, |
133 | 134 | sp -= n; |
134 | 135 | /* FIXME - handle put_user() failures */ |
135 | 136 | put_user_ual(argc, sp); |
136 | - | |
137 | + ts->info->arg_start = stringp; | |
137 | 138 | while (argc-- > 0) { |
138 | 139 | /* FIXME - handle put_user() failures */ |
139 | 140 | put_user_ual(stringp, argv); |
140 | 141 | argv += n; |
141 | 142 | stringp += target_strlen(stringp) + 1; |
142 | 143 | } |
144 | + ts->info->arg_end = stringp; | |
143 | 145 | /* FIXME - handle put_user() failures */ |
144 | 146 | put_user_ual(0, argv); |
145 | 147 | while (envc-- > 0) { |
... | ... | @@ -155,45 +157,45 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, |
155 | 157 | } |
156 | 158 | |
157 | 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 | 163 | int retval; |
162 | 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 | 167 | for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ |
166 | - bprm.page[i] = 0; | |
168 | + bprm->page[i] = 0; | |
167 | 169 | retval = open(filename, O_RDONLY); |
168 | 170 | if (retval < 0) |
169 | 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 | 181 | infop->host_argv = argv; |
180 | 182 | |
181 | 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 | 188 | #ifndef TARGET_HAS_ELFLOAD32 |
187 | - retval = load_elf_binary(&bprm,regs,infop); | |
189 | + retval = load_elf_binary(bprm,regs,infop); | |
188 | 190 | #else |
189 | - retval = load_elf_binary_multi(&bprm, regs, infop); | |
191 | + retval = load_elf_binary_multi(bprm, regs, infop); | |
190 | 192 | #endif |
191 | 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 | 199 | #endif |
198 | 200 | } else { |
199 | 201 | fprintf(stderr, "Unknown binary format\n"); |
... | ... | @@ -209,7 +211,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp, |
209 | 211 | |
210 | 212 | /* Something went wrong, return the inode and free the argument pages*/ |
211 | 213 | for (i=0 ; i<MAX_ARG_PAGES ; i++) { |
212 | - free(bprm.page[i]); | |
214 | + free(bprm->page[i]); | |
213 | 215 | } |
214 | 216 | return(retval); |
215 | 217 | } | ... | ... |
linux-user/main.c
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | #include <errno.h> |
26 | 26 | #include <unistd.h> |
27 | 27 | #include <sys/mman.h> |
28 | +#include <sys/syscall.h> | |
28 | 29 | |
29 | 30 | #include "qemu.h" |
30 | 31 | #include "qemu-common.h" |
... | ... | @@ -2319,6 +2320,27 @@ static void usage(void) |
2319 | 2320 | |
2320 | 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 | 2344 | /* Assumes contents are already zeroed. */ |
2323 | 2345 | void init_task_state(TaskState *ts) |
2324 | 2346 | { |
... | ... | @@ -2338,6 +2360,7 @@ int main(int argc, char **argv, char **envp) |
2338 | 2360 | const char *cpu_model; |
2339 | 2361 | struct target_pt_regs regs1, *regs = ®s1; |
2340 | 2362 | struct image_info info1, *info = &info1; |
2363 | + struct linux_binprm bprm; | |
2341 | 2364 | TaskState ts1, *ts = &ts1; |
2342 | 2365 | CPUState *env; |
2343 | 2366 | int optind; |
... | ... | @@ -2467,6 +2490,8 @@ int main(int argc, char **argv, char **envp) |
2467 | 2490 | /* Zero out image_info */ |
2468 | 2491 | memset(info, 0, sizeof(struct image_info)); |
2469 | 2492 | |
2493 | + memset(&bprm, 0, sizeof (bprm)); | |
2494 | + | |
2470 | 2495 | /* Scan interp_prefix dir for replacement files. */ |
2471 | 2496 | init_paths(interp_prefix); |
2472 | 2497 | |
... | ... | @@ -2543,7 +2568,16 @@ int main(int argc, char **argv, char **envp) |
2543 | 2568 | } |
2544 | 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 | 2581 | printf("Error loading %s\n", filename); |
2548 | 2582 | _exit(1); |
2549 | 2583 | } |
... | ... | @@ -2579,12 +2613,6 @@ int main(int argc, char **argv, char **envp) |
2579 | 2613 | syscall_init(); |
2580 | 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 | 2616 | #if defined(TARGET_I386) |
2589 | 2617 | cpu_x86_set_cpl(env, 3); |
2590 | 2618 | ... | ... |
linux-user/qemu.h
... | ... | @@ -18,6 +18,7 @@ |
18 | 18 | #include "syscall.h" |
19 | 19 | #include "target_signal.h" |
20 | 20 | #include "gdbstub.h" |
21 | +#include "sys-queue.h" | |
21 | 22 | |
22 | 23 | #if defined(USE_NPTL) |
23 | 24 | #define THREAD __thread |
... | ... | @@ -44,6 +45,9 @@ struct image_info { |
44 | 45 | abi_ulong entry; |
45 | 46 | abi_ulong code_offset; |
46 | 47 | abi_ulong data_offset; |
48 | + abi_ulong saved_auxv; | |
49 | + abi_ulong arg_start; | |
50 | + abi_ulong arg_end; | |
47 | 51 | char **host_argv; |
48 | 52 | int personality; |
49 | 53 | }; |
... | ... | @@ -87,7 +91,7 @@ struct emulated_sigtable { |
87 | 91 | /* NOTE: we force a big alignment so that the stack stored after is |
88 | 92 | aligned too */ |
89 | 93 | typedef struct TaskState { |
90 | - struct TaskState *next; | |
94 | + pid_t ts_tid; /* tid (or pid) of this task */ | |
91 | 95 | #ifdef TARGET_ARM |
92 | 96 | /* FPA state */ |
93 | 97 | FPA11 fpa; |
... | ... | @@ -114,6 +118,7 @@ typedef struct TaskState { |
114 | 118 | #endif |
115 | 119 | int used; /* non zero if used */ |
116 | 120 | struct image_info *info; |
121 | + struct linux_binprm *bprm; | |
117 | 122 | |
118 | 123 | struct emulated_sigtable sigtab[TARGET_NSIG]; |
119 | 124 | struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ |
... | ... | @@ -125,6 +130,8 @@ typedef struct TaskState { |
125 | 130 | |
126 | 131 | extern char *exec_path; |
127 | 132 | void init_task_state(TaskState *ts); |
133 | +void task_settid(TaskState *); | |
134 | +void stop_all_tasks(void); | |
128 | 135 | extern const char *qemu_uname_release; |
129 | 136 | |
130 | 137 | /* ??? See if we can avoid exposing so much of the loader internals. */ |
... | ... | @@ -149,13 +156,15 @@ struct linux_binprm { |
149 | 156 | char **argv; |
150 | 157 | char **envp; |
151 | 158 | char * filename; /* Name of binary */ |
159 | + int (*core_dump)(int, const CPUState *); /* coredump routine */ | |
152 | 160 | }; |
153 | 161 | |
154 | 162 | void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); |
155 | 163 | abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, |
156 | 164 | abi_ulong stringp, int push_ptr); |
157 | 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 | 169 | int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, |
161 | 170 | struct image_info * info); | ... | ... |
linux-user/signal.c
... | ... | @@ -27,6 +27,7 @@ |
27 | 27 | #include <errno.h> |
28 | 28 | #include <assert.h> |
29 | 29 | #include <sys/ucontext.h> |
30 | +#include <sys/resource.h> | |
30 | 31 | |
31 | 32 | #include "qemu.h" |
32 | 33 | #include "qemu-common.h" |
... | ... | @@ -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 | 308 | void signal_init(void) |
291 | 309 | { |
292 | 310 | struct sigaction act; |
... | ... | @@ -352,13 +370,29 @@ static inline void free_sigqueue(CPUState *env, struct sigqueue *q) |
352 | 370 | /* abort execution with signal */ |
353 | 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 | 375 | struct sigaction act; |
357 | 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 | 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 | 396 | /* The proper exit code for dieing from an uncaught signal is |
363 | 397 | * -<signal>. The kernel doesn't allow exit() or _exit() to pass |
364 | 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 | 3379 | { |
3380 | 3380 | new_thread_info *info = arg; |
3381 | 3381 | CPUState *env; |
3382 | + TaskState *ts; | |
3382 | 3383 | |
3383 | 3384 | env = info->env; |
3384 | 3385 | thread_env = env; |
3386 | + ts = (TaskState *)thread_env->opaque; | |
3385 | 3387 | info->tid = gettid(); |
3386 | 3388 | env->host_tid = info->tid; |
3389 | + task_settid(ts); | |
3387 | 3390 | if (info->child_tidptr) |
3388 | 3391 | put_user_u32(info->tid, info->child_tidptr); |
3389 | 3392 | if (info->parent_tidptr) |
... | ... | @@ -3435,6 +3438,7 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, |
3435 | 3438 | flags &= ~(CLONE_VFORK | CLONE_VM); |
3436 | 3439 | |
3437 | 3440 | if (flags & CLONE_VM) { |
3441 | + TaskState *parent_ts = (TaskState *)env->opaque; | |
3438 | 3442 | #if defined(USE_NPTL) |
3439 | 3443 | new_thread_info info; |
3440 | 3444 | pthread_attr_t attr; |
... | ... | @@ -3447,6 +3451,8 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, |
3447 | 3451 | /* Init regs that differ from the parent. */ |
3448 | 3452 | cpu_clone_regs(new_env, newsp); |
3449 | 3453 | new_env->opaque = ts; |
3454 | + ts->bprm = parent_ts->bprm; | |
3455 | + ts->info = parent_ts->info; | |
3450 | 3456 | #if defined(USE_NPTL) |
3451 | 3457 | nptl_flags = flags; |
3452 | 3458 | flags &= ~CLONE_NPTL_FLAGS2; | ... | ... |