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