OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2012 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 #include "native_client/src/untrusted/crash_dump/untrusted_crash_dump.h" | |
8 | |
9 #include <assert.h> | |
10 #include <inttypes.h> | |
11 #include <pthread.h> | |
12 #include <stdio.h> | |
13 #include <stdlib.h> | |
14 #include <string.h> | |
15 #include <sys/mman.h> | |
16 #include <sys/nacl_syscalls.h> | |
17 | |
18 #ifdef __GLIBC__ | |
19 #include <elf.h> | |
20 #include <link.h> | |
21 #endif /* __GLIBC__ */ | |
22 | |
23 #include "native_client/src/untrusted/nacl/syscall_bindings_trampoline.h" | |
24 | |
25 | |
26 #define CRASH_PAGE_CHUNK (64 * 1024) | |
27 #define CRASH_STACK_SIZE (CRASH_PAGE_CHUNK * 4) | |
28 #define CRASH_STACK_GUARD_SIZE CRASH_PAGE_CHUNK | |
29 #define CRASH_STACK_COMPLETE_SIZE (CRASH_STACK_GUARD_SIZE + CRASH_STACK_SIZE) | |
30 | |
31 | |
32 static pthread_key_t g_CrashStackKey; | |
33 | |
34 | |
35 #ifdef __GLIBC__ | |
36 | |
37 struct ProgramTableData { | |
38 FILE *core; | |
39 int first; | |
40 }; | |
41 | |
42 static void JsonEscape(const char *str, FILE *file) { | |
Mark Seaborn
2012/02/17 17:37:28
WriteJsonString() would be a little more descripti
bradn
2012/02/17 18:02:34
K, will fix in follow on CL.
| |
43 char ch; | |
44 | |
45 while ((ch = *str++)) { | |
Mark Seaborn
2012/02/17 17:37:28
I'd prefer to avoid assignments inside conditions,
bradn
2012/02/17 18:02:34
HAH, that's what I had and then changed it before
| |
46 if (ch == '"') { | |
47 fprintf(file, "\\\""); | |
48 } else if (ch == '\\') { | |
49 fprintf(file, "\\\\"); | |
50 } else if (ch < 32 || ch > 126) { | |
51 fprintf(file, "\\x%02x", (uint8_t)ch); | |
52 } else { | |
53 fputc(ch, file); | |
54 } | |
55 } | |
56 } | |
57 | |
58 static int PrintSegmentsOne( | |
59 struct dl_phdr_info *info, size_t size, void *data) { | |
60 int i; | |
61 struct ProgramTableData *ptd = (struct ProgramTableData*) data; | |
62 | |
63 if (ptd->first) { | |
64 ptd->first = 0; | |
65 } else { | |
66 fprintf(ptd->core, ",\n"); | |
67 } | |
68 fprintf(ptd->core, "{\n"); | |
69 fprintf(ptd->core, "\"dlpi_name\": \""); | |
70 JsonEscape(info->dlpi_name, ptd->core); | |
71 fprintf(ptd->core, "\",\n"); | |
72 fprintf(ptd->core, "\"dlpi_addr\": %"PRIuPTR",\n", info->dlpi_addr); | |
73 fprintf(ptd->core, "\"dlpi_phdr\": [\n"); | |
74 for (i = 0; i < info->dlpi_phnum; i++) { | |
75 /* Skip non-LOAD type segments. */ | |
76 if (info->dlpi_phdr[i].p_type != PT_LOAD) { | |
77 continue; | |
78 } | |
79 if (i != 0) { | |
80 fprintf(ptd->core, ",\n"); | |
81 } | |
82 fprintf(ptd->core, "{\n"); | |
83 fprintf(ptd->core, "\"p_vaddr\": %"PRIuPTR",\n", | |
84 info->dlpi_phdr[i].p_vaddr); | |
85 fprintf(ptd->core, "\"p_memsz\": %"PRIuPTR"\n", | |
86 info->dlpi_phdr[i].p_memsz); | |
87 fprintf(ptd->core, "}\n"); | |
88 } | |
89 fprintf(ptd->core, "]\n"); | |
90 fprintf(ptd->core, "}\n"); | |
91 return 0; | |
92 } | |
93 | |
94 static void PrintSegments(FILE *core) { | |
95 struct ProgramTableData data; | |
96 data.core = core; | |
97 data.first = 1; | |
98 dl_iterate_phdr(PrintSegmentsOne, &data); | |
99 } | |
100 | |
101 #else /* __GLIBC__ */ | |
102 | |
103 static void PrintSegments(FILE *core) { | |
104 } | |
105 | |
106 #endif /* __GLIBC__ */ | |
107 | |
108 uintptr_t SafeRead(uintptr_t a) { | |
109 /* TODO(bradnelson): use exception handling to recover from reads. */ | |
110 return *(uintptr_t*)a; | |
111 } | |
112 | |
113 static void StackWalk(FILE *core, uintptr_t prog_ctr, uintptr_t frame_ptr) { | |
114 uintptr_t next; | |
115 uintptr_t i; | |
116 int first = 1; | |
117 | |
118 fprintf(core, "\"frames\": [\n"); | |
119 for (;;) { | |
120 next = SafeRead(frame_ptr); | |
121 if (next <= frame_ptr || next == 0) { | |
122 break; | |
123 } | |
124 if (first) { | |
125 first = 0; | |
126 } else { | |
127 fprintf(core, ","); | |
128 } | |
129 fprintf(core, "{\n"); | |
130 fprintf(core, "\"frame_ptr\": %"PRIuPTR",\n", frame_ptr); | |
131 fprintf(core, "\"prog_ctr\": %"PRIuPTR",\n", prog_ctr); | |
132 fprintf(core, "\"data\": [\n"); | |
133 for (i = frame_ptr + 8; i < next; i += 4) { | |
134 if (i != frame_ptr + 8) { | |
135 fprintf(core, ","); | |
136 } | |
137 fprintf(core, "%"PRIuPTR"\n", SafeRead(i)); | |
138 } | |
139 fprintf(core, "]\n"); | |
140 fprintf(core, "}\n"); | |
141 | |
142 prog_ctr = SafeRead(frame_ptr + 4); | |
143 frame_ptr = next; | |
144 } | |
145 | |
146 fprintf(core, "]\n"); | |
147 } | |
148 | |
149 void CrashHandlerWrapper(int prog_ctr, int stack_ptr); | |
150 asm(".pushsection .text, \"ax\", @progbits\n" | |
151 ".p2align NACLENTRYALIGN\n" | |
152 "CrashHandlerWrapper:\n" | |
153 "popl %eax\n" | |
154 "pushl %ebp\n" | |
155 "call CrashHandler\n" | |
156 ".popsection\n"); | |
157 | |
158 void CrashHandler(int frame_ptr, int prog_ctr, int stack_ptr) { | |
159 FILE *core; | |
160 const char *core_filename; | |
161 | |
162 /* Pick core file name. */ | |
163 core_filename = getenv("NACLCOREFILE"); | |
164 if (core_filename == NULL) { | |
165 core_filename = "naclcore.json"; | |
166 } | |
167 | |
168 /* Attempt to open core file, otherwise use stdout. */ | |
169 core = fopen(core_filename, "w"); | |
170 if (core == NULL) { | |
171 core = stdout; | |
172 } | |
173 | |
174 fprintf(core, "{\n"); | |
175 | |
176 fprintf(core, "\"segments\": ["); | |
177 PrintSegments(core); | |
178 fprintf(core, "],\n"); | |
179 | |
180 fprintf(core, "\"handler\": {\n"); | |
181 fprintf(core, "\"prog_ctr\": %"PRIuPTR",\n", prog_ctr); | |
182 fprintf(core, "\"stack_ptr\": %"PRIuPTR",\n", stack_ptr); | |
183 fprintf(core, "\"frame_ptr\": %"PRIuPTR"\n", frame_ptr); | |
184 fprintf(core, "},\n"); | |
185 | |
186 StackWalk(core, (uintptr_t) prog_ctr, (uintptr_t) frame_ptr); | |
187 | |
188 fprintf(core, "}\n"); | |
189 | |
190 if (core != stdout) { | |
191 fclose(core); | |
192 } | |
193 | |
194 exit(166); | |
195 } | |
196 | |
197 void NaClCrashDumpThreadDestructor(void *arg) { | |
198 munmap(arg, CRASH_STACK_COMPLETE_SIZE); | |
199 } | |
200 | |
201 void NaClCrashDumpInit(void) { | |
202 int result; | |
203 result = pthread_key_create(&g_CrashStackKey, NaClCrashDumpThreadDestructor); | |
204 assert(result == 0); | |
205 result = NACL_SYSCALL(exception_handler)(CrashHandlerWrapper, NULL); | |
206 assert(result == 0); | |
207 NaClCrashDumpInitThread(); | |
208 } | |
209 | |
210 void NaClCrashDumpInitThread(void) { | |
211 void *stack; | |
212 void *guard; | |
213 int result; | |
214 /* | |
215 * NOTE: Setting up a per thread stack is only particularly interesting | |
216 * for stack overflow. | |
217 */ | |
218 stack = mmap(NULL, CRASH_STACK_COMPLETE_SIZE, | |
219 PROT_READ | PROT_WRITE, | |
220 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
221 assert(stack != MAP_FAILED); | |
222 guard = mmap(stack, CRASH_STACK_GUARD_SIZE, | |
223 PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
224 assert(guard == stack); | |
225 pthread_setspecific(g_CrashStackKey, stack); | |
226 result = NACL_SYSCALL(exception_stack)(stack, CRASH_STACK_COMPLETE_SIZE); | |
227 assert(result == 0); | |
228 } | |
OLD | NEW |