]>
Commit | Line | Data |
---|---|---|
0ad10c0f CF |
1 | /* dumper.cc |
2 | ||
61522196 | 3 | Copyright 1999, 2001, 2002, 2004, 2006, 2007, 2011, 2013 Red Hat Inc. |
0ad10c0f CF |
4 | |
5 | Written by Egor Duda <deo@logos-m.ru> | |
6 | ||
eedc36cb | 7 | This file is part of Cygwin. |
0ad10c0f | 8 | |
dd67e696 CV |
9 | This program is free software; you can redistribute it and/or modify |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation; either version 2 of the License, or | |
12 | (at your option) any later version. | |
13 | ||
14 | This program is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License (file COPYING.dumper) for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with this program; if not, write to the Free Software | |
21 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ | |
0ad10c0f | 22 | |
6dcb2ec4 | 23 | #include <ansidecl.h> |
4c36016b | 24 | #define PACKAGE |
f2af71ea | 25 | #include <bfd.h> |
0ad10c0f CF |
26 | #include <elf/common.h> |
27 | #include <elf/external.h> | |
28 | #include <sys/procfs.h> | |
29 | #include <sys/cygwin.h> | |
92b499ac | 30 | #include <cygwin/version.h> |
0ad10c0f CF |
31 | #include <getopt.h> |
32 | #include <stdarg.h> | |
92b499ac | 33 | #include <errno.h> |
0ad10c0f CF |
34 | #include <stdio.h> |
35 | #include <stdlib.h> | |
36 | #include <unistd.h> | |
9cfc9511 | 37 | #include <sys/param.h> |
0ad10c0f CF |
38 | #include <windows.h> |
39 | ||
40 | #include "dumper.h" | |
41 | ||
42 | #define NOTE_NAME_SIZE 16 | |
43 | ||
44 | typedef struct _note_header | |
eedc36cb CF |
45 | { |
46 | Elf_External_Note elf_note_header; | |
47 | char name[NOTE_NAME_SIZE - 1]; /* external note contains first byte of data */ | |
48 | } | |
0ad10c0f | 49 | #ifdef __GNUC__ |
eedc36cb | 50 | __attribute__ ((packed)) |
0ad10c0f | 51 | #endif |
eedc36cb | 52 | note_header; |
0ad10c0f | 53 | |
92ef5188 | 54 | BOOL verbose = FALSE; |
0ad10c0f | 55 | |
92ef5188 | 56 | int deb_printf (const char *format,...) |
0ad10c0f | 57 | { |
eedc36cb CF |
58 | if (!verbose) |
59 | return 0; | |
0ad10c0f | 60 | va_list va; |
eedc36cb CF |
61 | va_start (va, format); |
62 | int ret_val = vprintf (format, va); | |
63 | va_end (va); | |
0ad10c0f CF |
64 | return ret_val; |
65 | } | |
66 | ||
eedc36cb | 67 | dumper::dumper (DWORD pid, DWORD tid, const char *file_name) |
0ad10c0f | 68 | { |
eedc36cb | 69 | this->file_name = strdup (file_name); |
0ad10c0f CF |
70 | |
71 | this->pid = pid; | |
72 | this->tid = tid; | |
73 | core_bfd = NULL; | |
eedc36cb | 74 | excl_list = new exclusion (20); |
0ad10c0f CF |
75 | |
76 | list = last = NULL; | |
77 | ||
78 | status_section = NULL; | |
79 | ||
80 | memory_num = module_num = thread_num = 0; | |
81 | ||
eedc36cb CF |
82 | hProcess = OpenProcess (PROCESS_ALL_ACCESS, |
83 | FALSE, /* no inheritance */ | |
84 | pid); | |
85 | if (!hProcess) | |
0ad10c0f | 86 | { |
61522196 CV |
87 | fprintf (stderr, "Failed to open process #%u, error %ld\n", |
88 | (unsigned int) pid, (long) GetLastError ()); | |
0ad10c0f CF |
89 | return; |
90 | } | |
91 | ||
92 | init_core_dump (); | |
93 | ||
eedc36cb CF |
94 | if (!sane ()) |
95 | dumper_abort (); | |
0ad10c0f CF |
96 | } |
97 | ||
ce475802 | 98 | dumper::~dumper () |
0ad10c0f CF |
99 | { |
100 | close (); | |
eedc36cb | 101 | free (file_name); |
0ad10c0f CF |
102 | } |
103 | ||
104 | void | |
105 | dumper::dumper_abort () | |
106 | { | |
107 | close (); | |
eedc36cb | 108 | unlink (file_name); |
0ad10c0f CF |
109 | } |
110 | ||
111 | void | |
112 | dumper::close () | |
113 | { | |
eedc36cb CF |
114 | if (core_bfd) |
115 | bfd_close (core_bfd); | |
116 | if (excl_list) | |
117 | delete excl_list; | |
118 | if (hProcess) | |
119 | CloseHandle (hProcess); | |
0ad10c0f CF |
120 | core_bfd = NULL; |
121 | hProcess = NULL; | |
122 | excl_list = NULL; | |
123 | } | |
124 | ||
125 | int | |
126 | dumper::sane () | |
127 | { | |
eedc36cb CF |
128 | if (hProcess == NULL || core_bfd == NULL || excl_list == NULL) |
129 | return 0; | |
0ad10c0f CF |
130 | return 1; |
131 | } | |
132 | ||
33bc8247 ED |
133 | void |
134 | print_section_name (bfd* abfd, asection* sect, PTR obj) | |
135 | { | |
d353d5d6 | 136 | deb_printf (" %s", bfd_get_section_name (abfd, sect)); |
33bc8247 ED |
137 | } |
138 | ||
139 | void | |
140 | dumper::print_core_section_list () | |
141 | { | |
142 | deb_printf ("current sections:"); | |
143 | bfd_map_over_sections (core_bfd, &print_section_name, NULL); | |
144 | deb_printf ("\n"); | |
145 | } | |
146 | ||
eedc36cb CF |
147 | process_entity * |
148 | dumper::add_process_entity_to_list (process_entity_type type) | |
0ad10c0f | 149 | { |
eedc36cb CF |
150 | if (!sane ()) |
151 | return NULL; | |
0ad10c0f | 152 | |
eedc36cb CF |
153 | process_entity *new_entity = (process_entity *) malloc (sizeof (process_entity)); |
154 | if (new_entity == NULL) | |
155 | return NULL; | |
0ad10c0f CF |
156 | new_entity->next = NULL; |
157 | new_entity->section = NULL; | |
eedc36cb | 158 | if (last == NULL) |
0ad10c0f CF |
159 | list = new_entity; |
160 | else | |
161 | last->next = new_entity; | |
162 | last = new_entity; | |
163 | return new_entity; | |
164 | } | |
165 | ||
166 | int | |
eedc36cb | 167 | dumper::add_thread (DWORD tid, HANDLE hThread) |
0ad10c0f | 168 | { |
eedc36cb CF |
169 | if (!sane ()) |
170 | return 0; | |
0ad10c0f | 171 | |
eedc36cb | 172 | CONTEXT *pcontext; |
0ad10c0f | 173 | |
eedc36cb CF |
174 | process_entity *new_entity = add_process_entity_to_list (pr_ent_thread); |
175 | if (new_entity == NULL) | |
176 | return 0; | |
0ad10c0f CF |
177 | new_entity->type = pr_ent_thread; |
178 | thread_num++; | |
179 | ||
180 | new_entity->u.thread.tid = tid; | |
181 | new_entity->u.thread.hThread = hThread; | |
182 | ||
eedc36cb | 183 | pcontext = &(new_entity->u.thread.context); |
0ad10c0f | 184 | pcontext->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; |
eedc36cb | 185 | if (!GetThreadContext (hThread, pcontext)) |
d353d5d6 ED |
186 | { |
187 | deb_printf ("Failed to read thread context (tid=%x), error %ld\n", tid, GetLastError ()); | |
188 | return 0; | |
189 | } | |
0ad10c0f | 190 | |
eedc36cb | 191 | deb_printf ("added thread %u\n", tid); |
0ad10c0f CF |
192 | return 1; |
193 | } | |
194 | ||
195 | int | |
61522196 | 196 | dumper::add_mem_region (LPBYTE base, SIZE_T size) |
0ad10c0f | 197 | { |
eedc36cb CF |
198 | if (!sane ()) |
199 | return 0; | |
0ad10c0f | 200 | |
eedc36cb CF |
201 | if (base == NULL || size == 0) |
202 | return 1; // just ignore empty regions | |
0ad10c0f | 203 | |
eedc36cb CF |
204 | process_entity *new_entity = add_process_entity_to_list (pr_ent_memory); |
205 | if (new_entity == NULL) | |
206 | return 0; | |
0ad10c0f CF |
207 | new_entity->type = pr_ent_memory; |
208 | memory_num++; | |
209 | ||
210 | new_entity->u.memory.base = base; | |
211 | new_entity->u.memory.size = size; | |
212 | ||
61522196 | 213 | deb_printf ("added memory region %p-%p\n", base, base + size); |
0ad10c0f CF |
214 | return 1; |
215 | } | |
216 | ||
eedc36cb CF |
217 | /* split_add_mem_region scans list of regions to be excluded from dumping process |
218 | (excl_list) and removes all "excluded" parts from given region. */ | |
0ad10c0f | 219 | int |
61522196 | 220 | dumper::split_add_mem_region (LPBYTE base, SIZE_T size) |
0ad10c0f | 221 | { |
eedc36cb CF |
222 | if (!sane ()) |
223 | return 0; | |
0ad10c0f | 224 | |
eedc36cb CF |
225 | if (base == NULL || size == 0) |
226 | return 1; // just ignore empty regions | |
0ad10c0f CF |
227 | |
228 | LPBYTE last_base = base; | |
229 | ||
eedc36cb CF |
230 | for (process_mem_region * p = excl_list->region; |
231 | p < excl_list->region + excl_list->last; | |
232 | p++) | |
0ad10c0f | 233 | { |
eedc36cb CF |
234 | if (p->base >= base + size || p->base + p->size <= base) |
235 | continue; | |
0ad10c0f | 236 | |
eedc36cb CF |
237 | if (p->base <= base) |
238 | { | |
239 | last_base = p->base + p->size; | |
240 | continue; | |
241 | } | |
0ad10c0f | 242 | |
eedc36cb | 243 | add_mem_region (last_base, p->base - last_base); |
0ad10c0f CF |
244 | last_base = p->base + p->size; |
245 | } | |
246 | ||
eedc36cb CF |
247 | if (last_base < base + size) |
248 | add_mem_region (last_base, base + size - last_base); | |
0ad10c0f CF |
249 | |
250 | return 1; | |
251 | } | |
252 | ||
253 | int | |
eedc36cb | 254 | dumper::add_module (LPVOID base_address) |
0ad10c0f | 255 | { |
eedc36cb CF |
256 | if (!sane ()) |
257 | return 0; | |
0ad10c0f | 258 | |
61522196 | 259 | char *module_name = psapi_get_module_name (hProcess, base_address); |
eedc36cb CF |
260 | if (module_name == NULL) |
261 | return 1; | |
0ad10c0f | 262 | |
eedc36cb CF |
263 | process_entity *new_entity = add_process_entity_to_list (pr_ent_module); |
264 | if (new_entity == NULL) | |
265 | return 0; | |
0ad10c0f CF |
266 | new_entity->type = pr_ent_module; |
267 | module_num++; | |
268 | ||
269 | new_entity->u.module.base_address = base_address; | |
270 | new_entity->u.module.name = module_name; | |
271 | ||
eedc36cb | 272 | parse_pe (module_name, excl_list); |
0ad10c0f | 273 | |
61522196 | 274 | deb_printf ("added module %p %s\n", base_address, module_name); |
0ad10c0f CF |
275 | return 1; |
276 | } | |
277 | ||
278 | #define PAGE_BUFFER_SIZE 4096 | |
279 | ||
280 | int | |
281 | dumper::collect_memory_sections () | |
282 | { | |
eedc36cb CF |
283 | if (!sane ()) |
284 | return 0; | |
0ad10c0f CF |
285 | |
286 | LPBYTE current_page_address; | |
287 | LPBYTE last_base = (LPBYTE) 0xFFFFFFFF; | |
61522196 CV |
288 | SIZE_T last_size = (SIZE_T) 0; |
289 | SIZE_T done; | |
0ad10c0f | 290 | |
eedc36cb | 291 | char mem_buf[PAGE_BUFFER_SIZE]; |
0ad10c0f CF |
292 | |
293 | MEMORY_BASIC_INFORMATION mbi; | |
294 | ||
eedc36cb CF |
295 | if (hProcess == NULL) |
296 | return 0; | |
0ad10c0f | 297 | |
eedc36cb | 298 | for (current_page_address = 0; current_page_address < (LPBYTE) 0xFFFF0000;) |
0ad10c0f | 299 | { |
eedc36cb CF |
300 | if (!VirtualQueryEx (hProcess, current_page_address, &mbi, sizeof (mbi))) |
301 | break; | |
0ad10c0f CF |
302 | |
303 | int skip_region_p = 0; | |
304 | ||
eedc36cb CF |
305 | if (mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD) || |
306 | mbi.State != MEM_COMMIT) | |
307 | skip_region_p = 1; | |
308 | ||
309 | if (!skip_region_p) | |
310 | { | |
311 | /* just to make sure that later we'll be able to read it. | |
312 | According to MS docs either region is all-readable or | |
313 | all-nonreadable */ | |
314 | if (!ReadProcessMemory (hProcess, current_page_address, mem_buf, sizeof (mem_buf), &done)) | |
315 | { | |
d353d5d6 | 316 | DWORD err = GetLastError (); |
eedc36cb CF |
317 | const char *pt[10]; |
318 | pt[0] = (mbi.Protect & PAGE_READONLY) ? "RO " : ""; | |
319 | pt[1] = (mbi.Protect & PAGE_READWRITE) ? "RW " : ""; | |
320 | pt[2] = (mbi.Protect & PAGE_WRITECOPY) ? "WC " : ""; | |
321 | pt[3] = (mbi.Protect & PAGE_EXECUTE) ? "EX " : ""; | |
322 | pt[4] = (mbi.Protect & PAGE_EXECUTE_READ) ? "EXRO " : ""; | |
323 | pt[5] = (mbi.Protect & PAGE_EXECUTE_READWRITE) ? "EXRW " : ""; | |
324 | pt[6] = (mbi.Protect & PAGE_EXECUTE_WRITECOPY) ? "EXWC " : ""; | |
325 | pt[7] = (mbi.Protect & PAGE_GUARD) ? "GRD " : ""; | |
326 | pt[8] = (mbi.Protect & PAGE_NOACCESS) ? "NA " : ""; | |
327 | pt[9] = (mbi.Protect & PAGE_NOCACHE) ? "NC " : ""; | |
328 | char buf[10 * 6]; | |
329 | buf[0] = '\0'; | |
330 | for (int i = 0; i < 10; i++) | |
331 | strcat (buf, pt[i]); | |
332 | ||
61522196 CV |
333 | deb_printf ("warning: failed to read memory at %p-%p (protect = %s), error %ld.\n", |
334 | current_page_address, | |
335 | current_page_address + mbi.RegionSize, | |
d353d5d6 | 336 | buf, err); |
0ad10c0f | 337 | skip_region_p = 1; |
eedc36cb CF |
338 | } |
339 | } | |
340 | ||
341 | if (!skip_region_p) | |
342 | { | |
343 | if (last_base + last_size == current_page_address) | |
344 | last_size += mbi.RegionSize; | |
345 | else | |
346 | { | |
347 | split_add_mem_region (last_base, last_size); | |
348 | last_base = (LPBYTE) mbi.BaseAddress; | |
349 | last_size = mbi.RegionSize; | |
350 | } | |
351 | } | |
0ad10c0f | 352 | else |
eedc36cb CF |
353 | { |
354 | split_add_mem_region (last_base, last_size); | |
0ad10c0f | 355 | last_base = NULL; |
eedc36cb CF |
356 | last_size = 0; |
357 | } | |
0ad10c0f CF |
358 | |
359 | current_page_address += mbi.RegionSize; | |
360 | } | |
361 | ||
362 | /* dump last sections, if any */ | |
eedc36cb | 363 | split_add_mem_region (last_base, last_size); |
0ad10c0f CF |
364 | return 1; |
365 | }; | |
366 | ||
367 | int | |
eedc36cb | 368 | dumper::dump_memory_region (asection * to, process_mem_region * memory) |
0ad10c0f | 369 | { |
eedc36cb CF |
370 | if (!sane ()) |
371 | return 0; | |
0ad10c0f | 372 | |
61522196 CV |
373 | SIZE_T size = memory->size; |
374 | SIZE_T todo; | |
375 | SIZE_T done; | |
0ad10c0f CF |
376 | LPBYTE pos = memory->base; |
377 | DWORD sect_pos = 0; | |
378 | ||
eedc36cb CF |
379 | if (to == NULL || memory == NULL) |
380 | return 0; | |
0ad10c0f | 381 | |
eedc36cb | 382 | char mem_buf[PAGE_BUFFER_SIZE]; |
0ad10c0f | 383 | |
eedc36cb | 384 | while (size > 0) |
0ad10c0f | 385 | { |
9cfc9511 | 386 | todo = MIN (size, PAGE_BUFFER_SIZE); |
eedc36cb | 387 | if (!ReadProcessMemory (hProcess, pos, mem_buf, todo, &done)) |
0ad10c0f | 388 | { |
d353d5d6 | 389 | deb_printf ("Failed to read process memory at %x(%x), error %ld\n", pos, todo, GetLastError ()); |
0ad10c0f CF |
390 | return 0; |
391 | } | |
392 | size -= done; | |
393 | pos += done; | |
eedc36cb CF |
394 | if (!bfd_set_section_contents (core_bfd, to, mem_buf, sect_pos, done)) |
395 | { | |
396 | bfd_perror ("writing memory region to bfd"); | |
397 | dumper_abort (); | |
398 | return 0; | |
399 | }; | |
0ad10c0f CF |
400 | sect_pos += done; |
401 | } | |
402 | return 1; | |
403 | } | |
404 | ||
405 | int | |
eedc36cb | 406 | dumper::dump_thread (asection * to, process_thread * thread) |
0ad10c0f | 407 | { |
eedc36cb CF |
408 | if (!sane ()) |
409 | return 0; | |
0ad10c0f | 410 | |
eedc36cb CF |
411 | if (to == NULL || thread == NULL) |
412 | return 0; | |
0ad10c0f CF |
413 | |
414 | win32_pstatus thread_pstatus; | |
415 | ||
416 | note_header header; | |
eedc36cb CF |
417 | bfd_putl32 (NOTE_NAME_SIZE, header.elf_note_header.namesz); |
418 | bfd_putl32 (sizeof (thread_pstatus), header.elf_note_header.descsz); | |
419 | bfd_putl32 (NT_WIN32PSTATUS, header.elf_note_header.type); | |
420 | strncpy ((char *) &header.elf_note_header.name, "win32thread", NOTE_NAME_SIZE); | |
0ad10c0f CF |
421 | |
422 | thread_pstatus.data_type = NOTE_INFO_THREAD; | |
423 | thread_pstatus.data.thread_info.tid = thread->tid; | |
424 | ||
eedc36cb | 425 | if (tid == 0) |
0ad10c0f CF |
426 | { |
427 | /* this is a special case. we don't know, which thread | |
4bfc614b CF |
428 | was active when exception occured, so let's blame |
429 | the first one */ | |
eedc36cb CF |
430 | thread_pstatus.data.thread_info.is_active_thread = TRUE; |
431 | tid = (DWORD) - 1; | |
0ad10c0f | 432 | } |
eedc36cb | 433 | else if (tid > 0 && thread->tid == tid) |
0ad10c0f CF |
434 | thread_pstatus.data.thread_info.is_active_thread = TRUE; |
435 | else | |
436 | thread_pstatus.data.thread_info.is_active_thread = FALSE; | |
437 | ||
eedc36cb CF |
438 | memcpy (&(thread_pstatus.data.thread_info.thread_context), |
439 | &(thread->context), | |
440 | sizeof (thread->context)); | |
0ad10c0f | 441 | |
eedc36cb CF |
442 | if (!bfd_set_section_contents (core_bfd, to, &header, |
443 | 0, | |
444 | sizeof (header)) || | |
445 | !bfd_set_section_contents (core_bfd, to, &thread_pstatus, | |
446 | sizeof (header), | |
447 | sizeof (thread_pstatus))) | |
0ad10c0f | 448 | { |
eedc36cb | 449 | bfd_perror ("writing thread info to bfd"); |
0ad10c0f CF |
450 | dumper_abort (); |
451 | return 0; | |
eedc36cb | 452 | }; |
0ad10c0f CF |
453 | return 1; |
454 | } | |
455 | ||
456 | int | |
eedc36cb | 457 | dumper::dump_module (asection * to, process_module * module) |
0ad10c0f | 458 | { |
eedc36cb CF |
459 | if (!sane ()) |
460 | return 0; | |
0ad10c0f | 461 | |
eedc36cb CF |
462 | if (to == NULL || module == NULL) |
463 | return 0; | |
0ad10c0f | 464 | |
eedc36cb | 465 | struct win32_pstatus *module_pstatus_ptr; |
0ad10c0f | 466 | |
eedc36cb | 467 | int note_length = sizeof (struct win32_pstatus) + strlen (module->name); |
0ad10c0f | 468 | |
eedc36cb | 469 | char *buf = (char *) malloc (note_length); |
0ad10c0f | 470 | |
eedc36cb | 471 | if (!buf) |
0ad10c0f | 472 | { |
eedc36cb | 473 | fprintf (stderr, "Error alloating memory. Dumping aborted.\n"); |
0ad10c0f | 474 | goto out; |
eedc36cb | 475 | }; |
0ad10c0f | 476 | |
eedc36cb | 477 | module_pstatus_ptr = (struct win32_pstatus *) buf; |
0ad10c0f CF |
478 | |
479 | note_header header; | |
eedc36cb CF |
480 | bfd_putl32 (NOTE_NAME_SIZE, header.elf_note_header.namesz); |
481 | bfd_putl32 (note_length, header.elf_note_header.descsz); | |
482 | bfd_putl32 (NT_WIN32PSTATUS, header.elf_note_header.type); | |
483 | strncpy ((char *) &header.elf_note_header.name, "win32module", NOTE_NAME_SIZE); | |
0ad10c0f CF |
484 | |
485 | module_pstatus_ptr->data_type = NOTE_INFO_MODULE; | |
486 | module_pstatus_ptr->data.module_info.base_address = module->base_address; | |
eedc36cb CF |
487 | module_pstatus_ptr->data.module_info.module_name_size = strlen (module->name) + 1; |
488 | strcpy (module_pstatus_ptr->data.module_info.module_name, module->name); | |
489 | ||
490 | if (!bfd_set_section_contents (core_bfd, to, &header, | |
491 | 0, | |
492 | sizeof (header)) || | |
493 | !bfd_set_section_contents (core_bfd, to, module_pstatus_ptr, | |
494 | sizeof (header), | |
495 | note_length)) | |
0ad10c0f | 496 | { |
eedc36cb | 497 | bfd_perror ("writing module info to bfd"); |
0ad10c0f CF |
498 | goto out; |
499 | }; | |
500 | return 1; | |
501 | ||
502 | out: | |
eedc36cb CF |
503 | if (buf) |
504 | free (buf); | |
0ad10c0f CF |
505 | dumper_abort (); |
506 | return 0; | |
507 | ||
508 | } | |
509 | ||
510 | int | |
511 | dumper::collect_process_information () | |
512 | { | |
33bc8247 ED |
513 | int exception_level = 0; |
514 | ||
eedc36cb CF |
515 | if (!sane ()) |
516 | return 0; | |
0ad10c0f | 517 | |
eedc36cb | 518 | if (!DebugActiveProcess (pid)) |
0ad10c0f | 519 | { |
61522196 CV |
520 | fprintf (stderr, "Cannot attach to process #%u, error %ld", |
521 | (unsigned int) pid, (long) GetLastError ()); | |
0ad10c0f CF |
522 | return 0; |
523 | } | |
524 | ||
eedc36cb | 525 | char event_name[sizeof ("cygwin_error_start_event") + 20]; |
61522196 | 526 | sprintf (event_name, "cygwin_error_start_event%16x", (unsigned int) pid); |
eedc36cb | 527 | HANDLE sync_with_debugee = OpenEvent (EVENT_MODIFY_STATE, FALSE, event_name); |
0ad10c0f CF |
528 | |
529 | DEBUG_EVENT current_event; | |
530 | ||
531 | while (1) | |
532 | { | |
eedc36cb CF |
533 | if (!WaitForDebugEvent (¤t_event, 20000)) |
534 | return 0; | |
0ad10c0f | 535 | |
33bc8247 ED |
536 | deb_printf ("got debug event %d\n", current_event.dwDebugEventCode); |
537 | ||
0ad10c0f CF |
538 | switch (current_event.dwDebugEventCode) |
539 | { | |
540 | case CREATE_THREAD_DEBUG_EVENT: | |
541 | ||
eedc36cb CF |
542 | if (!add_thread (current_event.dwThreadId, |
543 | current_event.u.CreateThread.hThread)) | |
0ad10c0f CF |
544 | goto failed; |
545 | ||
546 | break; | |
547 | ||
548 | case CREATE_PROCESS_DEBUG_EVENT: | |
549 | ||
eedc36cb CF |
550 | if (!add_module (current_event.u.CreateProcessInfo.lpBaseOfImage) || |
551 | !add_thread (current_event.dwThreadId, | |
552 | current_event.u.CreateProcessInfo.hThread)) | |
553 | goto failed; | |
0ad10c0f CF |
554 | |
555 | break; | |
556 | ||
557 | case EXIT_PROCESS_DEBUG_EVENT: | |
558 | ||
eedc36cb CF |
559 | deb_printf ("debugee quits"); |
560 | ContinueDebugEvent (current_event.dwProcessId, | |
561 | current_event.dwThreadId, | |
562 | DBG_CONTINUE); | |
0ad10c0f CF |
563 | |
564 | return 1; | |
565 | ||
566 | break; | |
567 | ||
568 | case LOAD_DLL_DEBUG_EVENT: | |
569 | ||
eedc36cb CF |
570 | if (!add_module (current_event.u.LoadDll.lpBaseOfDll)) |
571 | goto failed; | |
0ad10c0f CF |
572 | |
573 | break; | |
574 | ||
575 | case EXCEPTION_DEBUG_EVENT: | |
576 | ||
33bc8247 ED |
577 | exception_level++; |
578 | if (exception_level == 2) | |
579 | break; | |
580 | else if (exception_level > 2) | |
581 | return 0; | |
582 | ||
eedc36cb | 583 | collect_memory_sections (); |
0ad10c0f | 584 | |
eedc36cb | 585 | /* got all info. time to dump */ |
0ad10c0f | 586 | |
eedc36cb | 587 | if (!prepare_core_dump ()) |
0ad10c0f | 588 | { |
eedc36cb | 589 | fprintf (stderr, "Failed to prepare core dump\n"); |
0ad10c0f CF |
590 | goto failed; |
591 | }; | |
592 | ||
eedc36cb | 593 | if (!write_core_dump ()) |
0ad10c0f | 594 | { |
eedc36cb | 595 | fprintf (stderr, "Failed to write core dump\n"); |
0ad10c0f CF |
596 | goto failed; |
597 | }; | |
598 | ||
eedc36cb CF |
599 | /* signal a debugee that we've finished */ |
600 | if (sync_with_debugee) | |
601 | SetEvent (sync_with_debugee); | |
0ad10c0f CF |
602 | |
603 | break; | |
604 | ||
605 | default: | |
606 | ||
607 | break; | |
608 | ||
609 | } | |
610 | ||
eedc36cb CF |
611 | ContinueDebugEvent (current_event.dwProcessId, |
612 | current_event.dwThreadId, | |
613 | DBG_CONTINUE); | |
0ad10c0f CF |
614 | } |
615 | failed: | |
616 | /* set debugee free */ | |
eedc36cb CF |
617 | if (sync_with_debugee) |
618 | SetEvent (sync_with_debugee); | |
0ad10c0f CF |
619 | |
620 | return 0; | |
621 | } | |
622 | ||
623 | int | |
624 | dumper::init_core_dump () | |
625 | { | |
626 | bfd_init (); | |
627 | ||
eedc36cb CF |
628 | core_bfd = bfd_openw (file_name, "elf32-i386"); |
629 | if (core_bfd == NULL) | |
0ad10c0f | 630 | { |
eedc36cb | 631 | bfd_perror ("opening bfd"); |
0ad10c0f CF |
632 | goto failed; |
633 | } | |
634 | ||
eedc36cb | 635 | if (!bfd_set_format (core_bfd, bfd_core)) |
0ad10c0f | 636 | { |
eedc36cb | 637 | bfd_perror ("setting bfd format"); |
0ad10c0f CF |
638 | goto failed; |
639 | } | |
640 | ||
3ee14d68 ED |
641 | if (!bfd_set_arch_mach (core_bfd, bfd_arch_i386, 0)) |
642 | { | |
643 | bfd_perror ("setting bfd architecture"); | |
644 | goto failed; | |
645 | } | |
646 | ||
0ad10c0f CF |
647 | return 1; |
648 | ||
649 | failed: | |
650 | dumper_abort (); | |
651 | return 0; | |
652 | ||
653 | } | |
654 | ||
655 | int | |
656 | dumper::prepare_core_dump () | |
657 | { | |
eedc36cb CF |
658 | if (!sane ()) |
659 | return 0; | |
0ad10c0f CF |
660 | |
661 | int sect_no = 0; | |
eedc36cb | 662 | char sect_name[50]; |
0ad10c0f CF |
663 | |
664 | flagword sect_flags; | |
61522196 | 665 | SIZE_T sect_size; |
0ad10c0f CF |
666 | bfd_vma sect_vma; |
667 | ||
eedc36cb | 668 | asection *new_section; |
0ad10c0f | 669 | |
eedc36cb | 670 | for (process_entity * p = list; p != NULL; p = p->next) |
0ad10c0f CF |
671 | { |
672 | sect_no++; | |
673 | ||
8a11b13f CV |
674 | unsigned long phdr_type = PT_LOAD; |
675 | ||
eedc36cb CF |
676 | switch (p->type) |
677 | { | |
678 | case pr_ent_memory: | |
679 | sprintf (sect_name, ".mem/%u", sect_no); | |
680 | sect_flags = SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD; | |
681 | sect_size = p->u.memory.size; | |
682 | sect_vma = (bfd_vma) (p->u.memory.base); | |
8a11b13f | 683 | phdr_type = PT_LOAD; |
eedc36cb CF |
684 | break; |
685 | ||
686 | case pr_ent_thread: | |
687 | sprintf (sect_name, ".note/%u", sect_no); | |
688 | sect_flags = SEC_HAS_CONTENTS | SEC_LOAD; | |
689 | sect_size = sizeof (note_header) + sizeof (struct win32_pstatus); | |
690 | sect_vma = 0; | |
8a11b13f | 691 | phdr_type = PT_NOTE; |
0ad10c0f CF |
692 | break; |
693 | ||
eedc36cb CF |
694 | case pr_ent_module: |
695 | sprintf (sect_name, ".note/%u", sect_no); | |
696 | sect_flags = SEC_HAS_CONTENTS | SEC_LOAD; | |
697 | sect_size = sizeof (note_header) + sizeof (struct win32_pstatus) + | |
698 | (bfd_size_type) (strlen (p->u.module.name)); | |
699 | sect_vma = 0; | |
8a11b13f | 700 | phdr_type = PT_NOTE; |
eedc36cb | 701 | break; |
0ad10c0f | 702 | |
eedc36cb | 703 | default: |
0ad10c0f | 704 | continue; |
eedc36cb | 705 | } |
0ad10c0f | 706 | |
eedc36cb CF |
707 | if (p->type == pr_ent_module && status_section != NULL) |
708 | { | |
709 | if (!bfd_set_section_size (core_bfd, | |
710 | status_section, | |
64b49cce AM |
711 | (bfd_get_section_size (status_section) |
712 | + sect_size))) | |
0ad10c0f | 713 | { |
eedc36cb | 714 | bfd_perror ("resizing status section"); |
0ad10c0f CF |
715 | goto failed; |
716 | }; | |
eedc36cb CF |
717 | continue; |
718 | } | |
0ad10c0f | 719 | |
eedc36cb CF |
720 | deb_printf ("creating section (type%u) %s(%u), flags=%08x\n", |
721 | p->type, sect_name, sect_size, sect_flags); | |
0ad10c0f | 722 | |
33bc8247 | 723 | bfd_set_error (bfd_error_no_error); |
eedc36cb CF |
724 | char *buf = strdup (sect_name); |
725 | new_section = bfd_make_section (core_bfd, buf); | |
33bc8247 ED |
726 | if (new_section == NULL) |
727 | { | |
728 | if (bfd_get_error () == bfd_error_no_error) | |
729 | fprintf (stderr, "error creating new section (%s), section already exists.\n", buf); | |
730 | else | |
731 | bfd_perror ("creating section"); | |
732 | goto failed; | |
733 | } | |
0ad10c0f | 734 | |
33bc8247 | 735 | if (!bfd_set_section_flags (core_bfd, new_section, sect_flags) || |
eedc36cb | 736 | !bfd_set_section_size (core_bfd, new_section, sect_size)) |
0ad10c0f | 737 | { |
33bc8247 | 738 | bfd_perror ("setting section attributes"); |
0ad10c0f CF |
739 | goto failed; |
740 | }; | |
741 | ||
742 | new_section->vma = sect_vma; | |
8a11b13f | 743 | new_section->lma = 0; |
0ad10c0f CF |
744 | new_section->output_section = new_section; |
745 | new_section->output_offset = 0; | |
746 | p->section = new_section; | |
8a11b13f CV |
747 | int section_count = 1; |
748 | ||
749 | bfd_boolean filehdr = 0; | |
750 | bfd_boolean phdrs = 0; | |
751 | ||
752 | bfd_vma at = 0; | |
753 | bfd_boolean valid_at = 0; | |
754 | ||
755 | flagword flags = 0; | |
756 | bfd_boolean valid_flags = 1; | |
757 | ||
758 | if (p->type == pr_ent_memory) | |
759 | { | |
760 | MEMORY_BASIC_INFORMATION mbi; | |
761 | if (!VirtualQueryEx (hProcess, (LPVOID)sect_vma, &mbi, sizeof (mbi))) | |
762 | { | |
763 | bfd_perror ("getting mem region flags"); | |
764 | goto failed; | |
765 | } | |
766 | ||
767 | static const struct | |
768 | { | |
769 | DWORD protect; | |
770 | flagword flags; | |
771 | } mappings[] = | |
772 | { | |
773 | { PAGE_READONLY, PF_R }, | |
774 | { PAGE_READWRITE, PF_R | PF_W }, | |
775 | { PAGE_WRITECOPY, PF_W }, | |
776 | { PAGE_EXECUTE, PF_X }, | |
777 | { PAGE_EXECUTE_READ, PF_X | PF_R }, | |
778 | { PAGE_EXECUTE_READWRITE, PF_X | PF_R | PF_W }, | |
779 | { PAGE_EXECUTE_WRITECOPY, PF_X | PF_W } | |
780 | }; | |
781 | ||
782 | for (size_t i = 0; | |
783 | i < sizeof (mappings) / sizeof (mappings[0]); | |
784 | i++) | |
785 | if ((mbi.Protect & mappings[i].protect) != 0) | |
786 | flags |= mappings[i].flags; | |
787 | } | |
0ad10c0f | 788 | |
8a11b13f CV |
789 | if (!bfd_record_phdr (core_bfd, phdr_type, |
790 | valid_flags, flags, | |
791 | valid_at, at, | |
792 | filehdr, phdrs, | |
793 | section_count, &new_section)) | |
794 | { | |
795 | bfd_perror ("recording program headers"); | |
796 | goto failed; | |
797 | } | |
798 | } | |
0ad10c0f CF |
799 | return 1; |
800 | ||
801 | failed: | |
802 | dumper_abort (); | |
803 | return 0; | |
804 | } | |
805 | ||
806 | int | |
807 | dumper::write_core_dump () | |
808 | { | |
eedc36cb CF |
809 | if (!sane ()) |
810 | return 0; | |
0ad10c0f | 811 | |
eedc36cb | 812 | for (process_entity * p = list; p != NULL; p = p->next) |
0ad10c0f | 813 | { |
eedc36cb CF |
814 | if (p->section == NULL) |
815 | continue; | |
816 | ||
61522196 | 817 | deb_printf ("writing section type=%u base=%p size=%p flags=%08x\n", |
eedc36cb CF |
818 | p->type, |
819 | p->section->vma, | |
64b49cce | 820 | bfd_get_section_size (p->section), |
eedc36cb CF |
821 | p->section->flags); |
822 | ||
823 | switch (p->type) | |
824 | { | |
825 | case pr_ent_memory: | |
826 | dump_memory_region (p->section, &(p->u.memory)); | |
0ad10c0f CF |
827 | break; |
828 | ||
eedc36cb CF |
829 | case pr_ent_thread: |
830 | dump_thread (p->section, &(p->u.thread)); | |
831 | break; | |
832 | ||
833 | case pr_ent_module: | |
834 | dump_module (p->section, &(p->u.module)); | |
835 | break; | |
0ad10c0f | 836 | |
eedc36cb | 837 | default: |
0ad10c0f CF |
838 | continue; |
839 | ||
eedc36cb | 840 | } |
0ad10c0f CF |
841 | } |
842 | return 1; | |
843 | } | |
844 | ||
845 | static void | |
84d06cb6 CF |
846 | usage (FILE *stream, int status) |
847 | { | |
848 | fprintf (stream, "\ | |
92b499ac CV |
849 | Usage: %s [OPTION] FILENAME WIN32PID\n\ |
850 | \n\ | |
84d06cb6 | 851 | Dump core from WIN32PID to FILENAME.core\n\ |
aa275fe0 | 852 | \n\ |
84d06cb6 CF |
853 | -d, --verbose be verbose while dumping\n\ |
854 | -h, --help output help information and exit\n\ | |
855 | -q, --quiet be quiet while dumping (default)\n\ | |
92b499ac CV |
856 | -V, --version output version information and exit\n\ |
857 | \n", program_invocation_short_name); | |
84d06cb6 CF |
858 | exit (status); |
859 | } | |
860 | ||
861 | struct option longopts[] = { | |
862 | {"verbose", no_argument, NULL, 'd'}, | |
863 | {"help", no_argument, NULL, 'h'}, | |
864 | {"quiet", no_argument, NULL, 'q'}, | |
92b499ac | 865 | {"version", no_argument, 0, 'V'}, |
84d06cb6 CF |
866 | {0, no_argument, NULL, 0} |
867 | }; | |
92b499ac | 868 | const char *opts = "dhqV"; |
84d06cb6 | 869 | |
4bfc614b | 870 | static void |
84d06cb6 | 871 | print_version () |
0ad10c0f | 872 | { |
92b499ac | 873 | printf ("dumper (cygwin) %d.%d.%d\n" |
1b23b30b CF |
874 | "Core Dumper for Cygwin\n" |
875 | "Copyright (C) 1999 - %s Red Hat, Inc.\n" | |
876 | "This is free software; see the source for copying conditions. There is NO\n" | |
92b499ac | 877 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", |
1b23b30b CF |
878 | CYGWIN_VERSION_DLL_MAJOR / 1000, |
879 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
880 | CYGWIN_VERSION_DLL_MINOR, | |
881 | strrchr (__DATE__, ' ') + 1); | |
0ad10c0f CF |
882 | } |
883 | ||
884 | int | |
eedc36cb | 885 | main (int argc, char **argv) |
0ad10c0f CF |
886 | { |
887 | int opt; | |
ce475802 | 888 | const char *p = ""; |
0ad10c0f CF |
889 | DWORD pid; |
890 | ||
92b499ac | 891 | while ((opt = getopt_long (argc, argv, opts, longopts, NULL) ) != EOF) |
0ad10c0f CF |
892 | switch (opt) |
893 | { | |
894 | case 'd': | |
895 | verbose = TRUE; | |
896 | break; | |
92ef5188 ED |
897 | case 'q': |
898 | verbose = FALSE; | |
899 | break; | |
84d06cb6 CF |
900 | case 'h': |
901 | usage (stdout, 0); | |
92b499ac CV |
902 | case 'V': |
903 | print_version (); | |
904 | exit (0); | |
92ef5188 | 905 | default: |
92b499ac CV |
906 | fprintf (stderr, "Try `%s --help' for more information.\n", |
907 | program_invocation_short_name); | |
908 | exit (1); | |
0ad10c0f CF |
909 | } |
910 | ||
92ef5188 | 911 | if (argv && *(argv + optind) && *(argv + optind +1)) |
0ad10c0f | 912 | { |
2b2b42cf CV |
913 | ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, |
914 | *(argv + optind), NULL, 0); | |
915 | char *win32_name = (char *) alloca (len); | |
916 | cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, *(argv + optind), | |
917 | win32_name, len); | |
92ef5188 ED |
918 | if ((p = strrchr (win32_name, '\\'))) |
919 | p++; | |
920 | else | |
921 | p = win32_name; | |
4e8b5fc3 | 922 | pid = strtoul (*(argv + optind + 1), NULL, 10); |
0ad10c0f | 923 | } |
0ad10c0f CF |
924 | else |
925 | { | |
84d06cb6 | 926 | usage (stderr, 1); |
0ad10c0f CF |
927 | return -1; |
928 | } | |
929 | ||
92ef5188 ED |
930 | char *core_file = (char *) malloc (strlen (p) + sizeof (".core")); |
931 | if (!core_file) | |
932 | { | |
933 | fprintf (stderr, "error allocating memory\n"); | |
934 | return -1; | |
935 | } | |
936 | sprintf (core_file, "%s.core", p); | |
937 | ||
0ad10c0f CF |
938 | DWORD tid = 0; |
939 | ||
eedc36cb | 940 | if (verbose) |
61522196 | 941 | printf ("dumping process #%u to %s\n", (unsigned int) pid, core_file); |
0ad10c0f | 942 | |
eedc36cb CF |
943 | dumper d (pid, tid, core_file); |
944 | if (!d.sane ()) | |
0ad10c0f CF |
945 | return -1; |
946 | d.collect_process_information (); | |
eedc36cb | 947 | free (core_file); |
0ad10c0f CF |
948 | |
949 | return 0; | |
950 | }; |