]>
Commit | Line | Data |
---|---|---|
0ad10c0f CF |
1 | /* dumper.cc |
2 | ||
3 | Copyright 1999 Cygnus Solutions. | |
4 | ||
5 | Written by Egor Duda <deo@logos-m.ru> | |
6 | ||
eedc36cb | 7 | This file is part of Cygwin. |
0ad10c0f | 8 | |
eedc36cb CF |
9 | This software is a copyrighted work licensed under the terms of the |
10 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
11 | details. */ | |
0ad10c0f CF |
12 | |
13 | #include <bfd.h> | |
14 | #include <elf/common.h> | |
15 | #include <elf/external.h> | |
16 | #include <sys/procfs.h> | |
17 | #include <sys/cygwin.h> | |
18 | #include <getopt.h> | |
19 | #include <stdarg.h> | |
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <unistd.h> | |
23 | #include <windows.h> | |
24 | ||
25 | #include "dumper.h" | |
26 | ||
27 | #define NOTE_NAME_SIZE 16 | |
28 | ||
29 | typedef struct _note_header | |
eedc36cb CF |
30 | { |
31 | Elf_External_Note elf_note_header; | |
32 | char name[NOTE_NAME_SIZE - 1]; /* external note contains first byte of data */ | |
33 | } | |
0ad10c0f | 34 | #ifdef __GNUC__ |
eedc36cb | 35 | __attribute__ ((packed)) |
0ad10c0f | 36 | #endif |
eedc36cb | 37 | note_header; |
0ad10c0f | 38 | |
eedc36cb | 39 | BOOL verbose = FALSE; |
0ad10c0f | 40 | |
eedc36cb | 41 | int deb_printf (const char *format,...) |
0ad10c0f | 42 | { |
eedc36cb CF |
43 | if (!verbose) |
44 | return 0; | |
0ad10c0f | 45 | va_list va; |
eedc36cb CF |
46 | va_start (va, format); |
47 | int ret_val = vprintf (format, va); | |
48 | va_end (va); | |
0ad10c0f CF |
49 | return ret_val; |
50 | } | |
51 | ||
eedc36cb | 52 | dumper::dumper (DWORD pid, DWORD tid, const char *file_name) |
0ad10c0f | 53 | { |
eedc36cb | 54 | this->file_name = strdup (file_name); |
0ad10c0f CF |
55 | |
56 | this->pid = pid; | |
57 | this->tid = tid; | |
58 | core_bfd = NULL; | |
eedc36cb | 59 | excl_list = new exclusion (20); |
0ad10c0f CF |
60 | |
61 | list = last = NULL; | |
62 | ||
63 | status_section = NULL; | |
64 | ||
65 | memory_num = module_num = thread_num = 0; | |
66 | ||
eedc36cb CF |
67 | hProcess = OpenProcess (PROCESS_ALL_ACCESS, |
68 | FALSE, /* no inheritance */ | |
69 | pid); | |
70 | if (!hProcess) | |
0ad10c0f | 71 | { |
eedc36cb | 72 | fprintf (stderr, "Failed to open process #%lu\n", pid); |
0ad10c0f CF |
73 | return; |
74 | } | |
75 | ||
76 | init_core_dump (); | |
77 | ||
eedc36cb CF |
78 | if (!sane ()) |
79 | dumper_abort (); | |
0ad10c0f CF |
80 | } |
81 | ||
ce475802 | 82 | dumper::~dumper () |
0ad10c0f CF |
83 | { |
84 | close (); | |
eedc36cb | 85 | free (file_name); |
0ad10c0f CF |
86 | } |
87 | ||
88 | void | |
89 | dumper::dumper_abort () | |
90 | { | |
91 | close (); | |
eedc36cb | 92 | unlink (file_name); |
0ad10c0f CF |
93 | } |
94 | ||
95 | void | |
96 | dumper::close () | |
97 | { | |
eedc36cb CF |
98 | if (core_bfd) |
99 | bfd_close (core_bfd); | |
100 | if (excl_list) | |
101 | delete excl_list; | |
102 | if (hProcess) | |
103 | CloseHandle (hProcess); | |
0ad10c0f CF |
104 | core_bfd = NULL; |
105 | hProcess = NULL; | |
106 | excl_list = NULL; | |
107 | } | |
108 | ||
109 | int | |
110 | dumper::sane () | |
111 | { | |
eedc36cb CF |
112 | if (hProcess == NULL || core_bfd == NULL || excl_list == NULL) |
113 | return 0; | |
0ad10c0f CF |
114 | return 1; |
115 | } | |
116 | ||
eedc36cb CF |
117 | process_entity * |
118 | dumper::add_process_entity_to_list (process_entity_type type) | |
0ad10c0f | 119 | { |
eedc36cb CF |
120 | if (!sane ()) |
121 | return NULL; | |
0ad10c0f | 122 | |
eedc36cb CF |
123 | process_entity *new_entity = (process_entity *) malloc (sizeof (process_entity)); |
124 | if (new_entity == NULL) | |
125 | return NULL; | |
0ad10c0f CF |
126 | new_entity->next = NULL; |
127 | new_entity->section = NULL; | |
eedc36cb | 128 | if (last == NULL) |
0ad10c0f CF |
129 | list = new_entity; |
130 | else | |
131 | last->next = new_entity; | |
132 | last = new_entity; | |
133 | return new_entity; | |
134 | } | |
135 | ||
136 | int | |
eedc36cb | 137 | dumper::add_thread (DWORD tid, HANDLE hThread) |
0ad10c0f | 138 | { |
eedc36cb CF |
139 | if (!sane ()) |
140 | return 0; | |
0ad10c0f | 141 | |
eedc36cb | 142 | CONTEXT *pcontext; |
0ad10c0f | 143 | |
eedc36cb CF |
144 | process_entity *new_entity = add_process_entity_to_list (pr_ent_thread); |
145 | if (new_entity == NULL) | |
146 | return 0; | |
0ad10c0f CF |
147 | new_entity->type = pr_ent_thread; |
148 | thread_num++; | |
149 | ||
150 | new_entity->u.thread.tid = tid; | |
151 | new_entity->u.thread.hThread = hThread; | |
152 | ||
eedc36cb | 153 | pcontext = &(new_entity->u.thread.context); |
0ad10c0f | 154 | pcontext->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; |
eedc36cb CF |
155 | if (!GetThreadContext (hThread, pcontext)) |
156 | return 0; | |
0ad10c0f | 157 | |
eedc36cb | 158 | deb_printf ("added thread %u\n", tid); |
0ad10c0f CF |
159 | return 1; |
160 | } | |
161 | ||
162 | int | |
eedc36cb | 163 | dumper::add_mem_region (LPBYTE base, DWORD size) |
0ad10c0f | 164 | { |
eedc36cb CF |
165 | if (!sane ()) |
166 | return 0; | |
0ad10c0f | 167 | |
eedc36cb CF |
168 | if (base == NULL || size == 0) |
169 | return 1; // just ignore empty regions | |
0ad10c0f | 170 | |
eedc36cb CF |
171 | process_entity *new_entity = add_process_entity_to_list (pr_ent_memory); |
172 | if (new_entity == NULL) | |
173 | return 0; | |
0ad10c0f CF |
174 | new_entity->type = pr_ent_memory; |
175 | memory_num++; | |
176 | ||
177 | new_entity->u.memory.base = base; | |
178 | new_entity->u.memory.size = size; | |
179 | ||
eedc36cb | 180 | deb_printf ("added memory region %08x-%08x\n", (DWORD) base, (DWORD) base + size); |
0ad10c0f CF |
181 | return 1; |
182 | } | |
183 | ||
eedc36cb CF |
184 | /* split_add_mem_region scans list of regions to be excluded from dumping process |
185 | (excl_list) and removes all "excluded" parts from given region. */ | |
0ad10c0f | 186 | int |
eedc36cb | 187 | dumper::split_add_mem_region (LPBYTE base, DWORD size) |
0ad10c0f | 188 | { |
eedc36cb CF |
189 | if (!sane ()) |
190 | return 0; | |
0ad10c0f | 191 | |
eedc36cb CF |
192 | if (base == NULL || size == 0) |
193 | return 1; // just ignore empty regions | |
0ad10c0f CF |
194 | |
195 | LPBYTE last_base = base; | |
196 | ||
eedc36cb CF |
197 | for (process_mem_region * p = excl_list->region; |
198 | p < excl_list->region + excl_list->last; | |
199 | p++) | |
0ad10c0f | 200 | { |
eedc36cb CF |
201 | if (p->base >= base + size || p->base + p->size <= base) |
202 | continue; | |
0ad10c0f | 203 | |
eedc36cb CF |
204 | if (p->base <= base) |
205 | { | |
206 | last_base = p->base + p->size; | |
207 | continue; | |
208 | } | |
0ad10c0f | 209 | |
eedc36cb | 210 | add_mem_region (last_base, p->base - last_base); |
0ad10c0f CF |
211 | last_base = p->base + p->size; |
212 | } | |
213 | ||
eedc36cb CF |
214 | if (last_base < base + size) |
215 | add_mem_region (last_base, base + size - last_base); | |
0ad10c0f CF |
216 | |
217 | return 1; | |
218 | } | |
219 | ||
220 | int | |
eedc36cb | 221 | dumper::add_module (LPVOID base_address) |
0ad10c0f | 222 | { |
eedc36cb CF |
223 | if (!sane ()) |
224 | return 0; | |
0ad10c0f | 225 | |
eedc36cb CF |
226 | char *module_name = psapi_get_module_name (hProcess, (DWORD) base_address); |
227 | if (module_name == NULL) | |
228 | return 1; | |
0ad10c0f | 229 | |
eedc36cb CF |
230 | process_entity *new_entity = add_process_entity_to_list (pr_ent_module); |
231 | if (new_entity == NULL) | |
232 | return 0; | |
0ad10c0f CF |
233 | new_entity->type = pr_ent_module; |
234 | module_num++; | |
235 | ||
236 | new_entity->u.module.base_address = base_address; | |
237 | new_entity->u.module.name = module_name; | |
238 | ||
eedc36cb | 239 | parse_pe (module_name, excl_list); |
0ad10c0f | 240 | |
eedc36cb | 241 | deb_printf ("added module %08x %s\n", base_address, module_name); |
0ad10c0f CF |
242 | return 1; |
243 | } | |
244 | ||
245 | #define PAGE_BUFFER_SIZE 4096 | |
246 | ||
247 | int | |
248 | dumper::collect_memory_sections () | |
249 | { | |
eedc36cb CF |
250 | if (!sane ()) |
251 | return 0; | |
0ad10c0f CF |
252 | |
253 | LPBYTE current_page_address; | |
254 | LPBYTE last_base = (LPBYTE) 0xFFFFFFFF; | |
255 | DWORD last_size = 0; | |
256 | DWORD done; | |
257 | ||
eedc36cb | 258 | char mem_buf[PAGE_BUFFER_SIZE]; |
0ad10c0f CF |
259 | |
260 | MEMORY_BASIC_INFORMATION mbi; | |
261 | ||
eedc36cb CF |
262 | if (hProcess == NULL) |
263 | return 0; | |
0ad10c0f | 264 | |
eedc36cb | 265 | for (current_page_address = 0; current_page_address < (LPBYTE) 0xFFFF0000;) |
0ad10c0f | 266 | { |
eedc36cb CF |
267 | if (!VirtualQueryEx (hProcess, current_page_address, &mbi, sizeof (mbi))) |
268 | break; | |
0ad10c0f CF |
269 | |
270 | int skip_region_p = 0; | |
271 | ||
eedc36cb CF |
272 | if (mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD) || |
273 | mbi.State != MEM_COMMIT) | |
274 | skip_region_p = 1; | |
275 | ||
276 | if (!skip_region_p) | |
277 | { | |
278 | /* just to make sure that later we'll be able to read it. | |
279 | According to MS docs either region is all-readable or | |
280 | all-nonreadable */ | |
281 | if (!ReadProcessMemory (hProcess, current_page_address, mem_buf, sizeof (mem_buf), &done)) | |
282 | { | |
283 | const char *pt[10]; | |
284 | pt[0] = (mbi.Protect & PAGE_READONLY) ? "RO " : ""; | |
285 | pt[1] = (mbi.Protect & PAGE_READWRITE) ? "RW " : ""; | |
286 | pt[2] = (mbi.Protect & PAGE_WRITECOPY) ? "WC " : ""; | |
287 | pt[3] = (mbi.Protect & PAGE_EXECUTE) ? "EX " : ""; | |
288 | pt[4] = (mbi.Protect & PAGE_EXECUTE_READ) ? "EXRO " : ""; | |
289 | pt[5] = (mbi.Protect & PAGE_EXECUTE_READWRITE) ? "EXRW " : ""; | |
290 | pt[6] = (mbi.Protect & PAGE_EXECUTE_WRITECOPY) ? "EXWC " : ""; | |
291 | pt[7] = (mbi.Protect & PAGE_GUARD) ? "GRD " : ""; | |
292 | pt[8] = (mbi.Protect & PAGE_NOACCESS) ? "NA " : ""; | |
293 | pt[9] = (mbi.Protect & PAGE_NOCACHE) ? "NC " : ""; | |
294 | char buf[10 * 6]; | |
295 | buf[0] = '\0'; | |
296 | for (int i = 0; i < 10; i++) | |
297 | strcat (buf, pt[i]); | |
298 | ||
299 | deb_printf ("warning: failed to read memory at %08x-%08x. protect = %s\n", | |
300 | (DWORD) current_page_address, | |
301 | (DWORD) current_page_address + mbi.RegionSize, | |
302 | buf); | |
0ad10c0f | 303 | skip_region_p = 1; |
eedc36cb CF |
304 | } |
305 | } | |
306 | ||
307 | if (!skip_region_p) | |
308 | { | |
309 | if (last_base + last_size == current_page_address) | |
310 | last_size += mbi.RegionSize; | |
311 | else | |
312 | { | |
313 | split_add_mem_region (last_base, last_size); | |
314 | last_base = (LPBYTE) mbi.BaseAddress; | |
315 | last_size = mbi.RegionSize; | |
316 | } | |
317 | } | |
0ad10c0f | 318 | else |
eedc36cb CF |
319 | { |
320 | split_add_mem_region (last_base, last_size); | |
0ad10c0f | 321 | last_base = NULL; |
eedc36cb CF |
322 | last_size = 0; |
323 | } | |
0ad10c0f CF |
324 | |
325 | current_page_address += mbi.RegionSize; | |
326 | } | |
327 | ||
328 | /* dump last sections, if any */ | |
eedc36cb | 329 | split_add_mem_region (last_base, last_size); |
0ad10c0f CF |
330 | return 1; |
331 | }; | |
332 | ||
333 | int | |
eedc36cb | 334 | dumper::dump_memory_region (asection * to, process_mem_region * memory) |
0ad10c0f | 335 | { |
eedc36cb CF |
336 | if (!sane ()) |
337 | return 0; | |
0ad10c0f CF |
338 | |
339 | DWORD size = memory->size; | |
340 | DWORD todo; | |
341 | DWORD done; | |
342 | LPBYTE pos = memory->base; | |
343 | DWORD sect_pos = 0; | |
344 | ||
eedc36cb CF |
345 | if (to == NULL || memory == NULL) |
346 | return 0; | |
0ad10c0f | 347 | |
eedc36cb | 348 | char mem_buf[PAGE_BUFFER_SIZE]; |
0ad10c0f | 349 | |
eedc36cb | 350 | while (size > 0) |
0ad10c0f | 351 | { |
eedc36cb CF |
352 | todo = min (size, PAGE_BUFFER_SIZE); |
353 | if (!ReadProcessMemory (hProcess, pos, mem_buf, todo, &done)) | |
0ad10c0f | 354 | { |
eedc36cb | 355 | deb_printf ("Error reading process memory at %x(%x) %u\n", pos, todo, GetLastError ()); |
0ad10c0f CF |
356 | return 0; |
357 | } | |
358 | size -= done; | |
359 | pos += done; | |
eedc36cb CF |
360 | if (!bfd_set_section_contents (core_bfd, to, mem_buf, sect_pos, done)) |
361 | { | |
362 | bfd_perror ("writing memory region to bfd"); | |
363 | dumper_abort (); | |
364 | return 0; | |
365 | }; | |
0ad10c0f CF |
366 | sect_pos += done; |
367 | } | |
368 | return 1; | |
369 | } | |
370 | ||
371 | int | |
eedc36cb | 372 | dumper::dump_thread (asection * to, process_thread * thread) |
0ad10c0f | 373 | { |
eedc36cb CF |
374 | if (!sane ()) |
375 | return 0; | |
0ad10c0f | 376 | |
eedc36cb CF |
377 | if (to == NULL || thread == NULL) |
378 | return 0; | |
0ad10c0f CF |
379 | |
380 | win32_pstatus thread_pstatus; | |
381 | ||
382 | note_header header; | |
eedc36cb CF |
383 | bfd_putl32 (NOTE_NAME_SIZE, header.elf_note_header.namesz); |
384 | bfd_putl32 (sizeof (thread_pstatus), header.elf_note_header.descsz); | |
385 | bfd_putl32 (NT_WIN32PSTATUS, header.elf_note_header.type); | |
386 | strncpy ((char *) &header.elf_note_header.name, "win32thread", NOTE_NAME_SIZE); | |
0ad10c0f CF |
387 | |
388 | thread_pstatus.data_type = NOTE_INFO_THREAD; | |
389 | thread_pstatus.data.thread_info.tid = thread->tid; | |
390 | ||
eedc36cb | 391 | if (tid == 0) |
0ad10c0f CF |
392 | { |
393 | /* this is a special case. we don't know, which thread | |
eedc36cb | 394 | was active when exception occured, so let's blame |
0ad10c0f | 395 | the first one */ |
eedc36cb CF |
396 | thread_pstatus.data.thread_info.is_active_thread = TRUE; |
397 | tid = (DWORD) - 1; | |
0ad10c0f | 398 | } |
eedc36cb | 399 | else if (tid > 0 && thread->tid == tid) |
0ad10c0f CF |
400 | thread_pstatus.data.thread_info.is_active_thread = TRUE; |
401 | else | |
402 | thread_pstatus.data.thread_info.is_active_thread = FALSE; | |
403 | ||
eedc36cb CF |
404 | memcpy (&(thread_pstatus.data.thread_info.thread_context), |
405 | &(thread->context), | |
406 | sizeof (thread->context)); | |
0ad10c0f | 407 | |
eedc36cb CF |
408 | if (!bfd_set_section_contents (core_bfd, to, &header, |
409 | 0, | |
410 | sizeof (header)) || | |
411 | !bfd_set_section_contents (core_bfd, to, &thread_pstatus, | |
412 | sizeof (header), | |
413 | sizeof (thread_pstatus))) | |
0ad10c0f | 414 | { |
eedc36cb | 415 | bfd_perror ("writing thread info to bfd"); |
0ad10c0f CF |
416 | dumper_abort (); |
417 | return 0; | |
eedc36cb | 418 | }; |
0ad10c0f CF |
419 | return 1; |
420 | } | |
421 | ||
422 | int | |
eedc36cb | 423 | dumper::dump_module (asection * to, process_module * module) |
0ad10c0f | 424 | { |
eedc36cb CF |
425 | if (!sane ()) |
426 | return 0; | |
0ad10c0f | 427 | |
eedc36cb CF |
428 | if (to == NULL || module == NULL) |
429 | return 0; | |
0ad10c0f | 430 | |
eedc36cb | 431 | struct win32_pstatus *module_pstatus_ptr; |
0ad10c0f | 432 | |
eedc36cb | 433 | int note_length = sizeof (struct win32_pstatus) + strlen (module->name); |
0ad10c0f | 434 | |
eedc36cb | 435 | char *buf = (char *) malloc (note_length); |
0ad10c0f | 436 | |
eedc36cb | 437 | if (!buf) |
0ad10c0f | 438 | { |
eedc36cb | 439 | fprintf (stderr, "Error alloating memory. Dumping aborted.\n"); |
0ad10c0f | 440 | goto out; |
eedc36cb | 441 | }; |
0ad10c0f | 442 | |
eedc36cb | 443 | module_pstatus_ptr = (struct win32_pstatus *) buf; |
0ad10c0f CF |
444 | |
445 | note_header header; | |
eedc36cb CF |
446 | bfd_putl32 (NOTE_NAME_SIZE, header.elf_note_header.namesz); |
447 | bfd_putl32 (note_length, header.elf_note_header.descsz); | |
448 | bfd_putl32 (NT_WIN32PSTATUS, header.elf_note_header.type); | |
449 | strncpy ((char *) &header.elf_note_header.name, "win32module", NOTE_NAME_SIZE); | |
0ad10c0f CF |
450 | |
451 | module_pstatus_ptr->data_type = NOTE_INFO_MODULE; | |
452 | module_pstatus_ptr->data.module_info.base_address = module->base_address; | |
eedc36cb CF |
453 | module_pstatus_ptr->data.module_info.module_name_size = strlen (module->name) + 1; |
454 | strcpy (module_pstatus_ptr->data.module_info.module_name, module->name); | |
455 | ||
456 | if (!bfd_set_section_contents (core_bfd, to, &header, | |
457 | 0, | |
458 | sizeof (header)) || | |
459 | !bfd_set_section_contents (core_bfd, to, module_pstatus_ptr, | |
460 | sizeof (header), | |
461 | note_length)) | |
0ad10c0f | 462 | { |
eedc36cb | 463 | bfd_perror ("writing module info to bfd"); |
0ad10c0f CF |
464 | goto out; |
465 | }; | |
466 | return 1; | |
467 | ||
468 | out: | |
eedc36cb CF |
469 | if (buf) |
470 | free (buf); | |
0ad10c0f CF |
471 | dumper_abort (); |
472 | return 0; | |
473 | ||
474 | } | |
475 | ||
476 | int | |
477 | dumper::collect_process_information () | |
478 | { | |
eedc36cb CF |
479 | if (!sane ()) |
480 | return 0; | |
0ad10c0f | 481 | |
eedc36cb | 482 | if (!DebugActiveProcess (pid)) |
0ad10c0f | 483 | { |
eedc36cb | 484 | fprintf (stderr, "Cannot attach to process #%lu", pid); |
0ad10c0f CF |
485 | return 0; |
486 | } | |
487 | ||
eedc36cb CF |
488 | char event_name[sizeof ("cygwin_error_start_event") + 20]; |
489 | sprintf (event_name, "cygwin_error_start_event%16lx", pid); | |
490 | HANDLE sync_with_debugee = OpenEvent (EVENT_MODIFY_STATE, FALSE, event_name); | |
0ad10c0f CF |
491 | |
492 | DEBUG_EVENT current_event; | |
493 | ||
494 | while (1) | |
495 | { | |
eedc36cb CF |
496 | if (!WaitForDebugEvent (¤t_event, 20000)) |
497 | return 0; | |
0ad10c0f CF |
498 | |
499 | switch (current_event.dwDebugEventCode) | |
500 | { | |
501 | case CREATE_THREAD_DEBUG_EVENT: | |
502 | ||
eedc36cb CF |
503 | if (!add_thread (current_event.dwThreadId, |
504 | current_event.u.CreateThread.hThread)) | |
0ad10c0f CF |
505 | goto failed; |
506 | ||
507 | break; | |
508 | ||
509 | case CREATE_PROCESS_DEBUG_EVENT: | |
510 | ||
eedc36cb CF |
511 | if (!add_module (current_event.u.CreateProcessInfo.lpBaseOfImage) || |
512 | !add_thread (current_event.dwThreadId, | |
513 | current_event.u.CreateProcessInfo.hThread)) | |
514 | goto failed; | |
0ad10c0f CF |
515 | |
516 | break; | |
517 | ||
518 | case EXIT_PROCESS_DEBUG_EVENT: | |
519 | ||
eedc36cb CF |
520 | deb_printf ("debugee quits"); |
521 | ContinueDebugEvent (current_event.dwProcessId, | |
522 | current_event.dwThreadId, | |
523 | DBG_CONTINUE); | |
0ad10c0f CF |
524 | |
525 | return 1; | |
526 | ||
527 | break; | |
528 | ||
529 | case LOAD_DLL_DEBUG_EVENT: | |
530 | ||
eedc36cb CF |
531 | if (!add_module (current_event.u.LoadDll.lpBaseOfDll)) |
532 | goto failed; | |
0ad10c0f CF |
533 | |
534 | break; | |
535 | ||
536 | case EXCEPTION_DEBUG_EVENT: | |
537 | ||
eedc36cb | 538 | collect_memory_sections (); |
0ad10c0f | 539 | |
eedc36cb | 540 | /* got all info. time to dump */ |
0ad10c0f | 541 | |
eedc36cb | 542 | if (!prepare_core_dump ()) |
0ad10c0f | 543 | { |
eedc36cb | 544 | fprintf (stderr, "Failed to prepare core dump\n"); |
0ad10c0f CF |
545 | goto failed; |
546 | }; | |
547 | ||
eedc36cb | 548 | if (!write_core_dump ()) |
0ad10c0f | 549 | { |
eedc36cb | 550 | fprintf (stderr, "Failed to write core dump\n"); |
0ad10c0f CF |
551 | goto failed; |
552 | }; | |
553 | ||
eedc36cb CF |
554 | /* signal a debugee that we've finished */ |
555 | if (sync_with_debugee) | |
556 | SetEvent (sync_with_debugee); | |
0ad10c0f CF |
557 | |
558 | break; | |
559 | ||
560 | default: | |
561 | ||
562 | break; | |
563 | ||
564 | } | |
565 | ||
eedc36cb CF |
566 | ContinueDebugEvent (current_event.dwProcessId, |
567 | current_event.dwThreadId, | |
568 | DBG_CONTINUE); | |
0ad10c0f CF |
569 | } |
570 | failed: | |
571 | /* set debugee free */ | |
eedc36cb CF |
572 | if (sync_with_debugee) |
573 | SetEvent (sync_with_debugee); | |
0ad10c0f CF |
574 | |
575 | return 0; | |
576 | } | |
577 | ||
578 | int | |
579 | dumper::init_core_dump () | |
580 | { | |
581 | bfd_init (); | |
582 | ||
eedc36cb CF |
583 | core_bfd = bfd_openw (file_name, "elf32-i386"); |
584 | if (core_bfd == NULL) | |
0ad10c0f | 585 | { |
eedc36cb | 586 | bfd_perror ("opening bfd"); |
0ad10c0f CF |
587 | goto failed; |
588 | } | |
589 | ||
eedc36cb | 590 | if (!bfd_set_format (core_bfd, bfd_core)) |
0ad10c0f | 591 | { |
eedc36cb | 592 | bfd_perror ("setting bfd format"); |
0ad10c0f CF |
593 | goto failed; |
594 | } | |
595 | ||
3ee14d68 ED |
596 | if (!bfd_set_arch_mach (core_bfd, bfd_arch_i386, 0)) |
597 | { | |
598 | bfd_perror ("setting bfd architecture"); | |
599 | goto failed; | |
600 | } | |
601 | ||
0ad10c0f CF |
602 | return 1; |
603 | ||
604 | failed: | |
605 | dumper_abort (); | |
606 | return 0; | |
607 | ||
608 | } | |
609 | ||
610 | int | |
611 | dumper::prepare_core_dump () | |
612 | { | |
eedc36cb CF |
613 | if (!sane ()) |
614 | return 0; | |
0ad10c0f CF |
615 | |
616 | int sect_no = 0; | |
eedc36cb | 617 | char sect_name[50]; |
0ad10c0f CF |
618 | |
619 | flagword sect_flags; | |
620 | DWORD sect_size; | |
621 | bfd_vma sect_vma; | |
622 | ||
eedc36cb | 623 | asection *new_section; |
0ad10c0f | 624 | |
eedc36cb | 625 | for (process_entity * p = list; p != NULL; p = p->next) |
0ad10c0f CF |
626 | { |
627 | sect_no++; | |
628 | ||
eedc36cb CF |
629 | switch (p->type) |
630 | { | |
631 | case pr_ent_memory: | |
632 | sprintf (sect_name, ".mem/%u", sect_no); | |
633 | sect_flags = SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD; | |
634 | sect_size = p->u.memory.size; | |
635 | sect_vma = (bfd_vma) (p->u.memory.base); | |
636 | ||
637 | break; | |
638 | ||
639 | case pr_ent_thread: | |
640 | sprintf (sect_name, ".note/%u", sect_no); | |
641 | sect_flags = SEC_HAS_CONTENTS | SEC_LOAD; | |
642 | sect_size = sizeof (note_header) + sizeof (struct win32_pstatus); | |
643 | sect_vma = 0; | |
0ad10c0f CF |
644 | break; |
645 | ||
eedc36cb CF |
646 | case pr_ent_module: |
647 | sprintf (sect_name, ".note/%u", sect_no); | |
648 | sect_flags = SEC_HAS_CONTENTS | SEC_LOAD; | |
649 | sect_size = sizeof (note_header) + sizeof (struct win32_pstatus) + | |
650 | (bfd_size_type) (strlen (p->u.module.name)); | |
651 | sect_vma = 0; | |
652 | break; | |
0ad10c0f | 653 | |
eedc36cb | 654 | default: |
0ad10c0f | 655 | continue; |
eedc36cb | 656 | } |
0ad10c0f | 657 | |
eedc36cb CF |
658 | if (p->type == pr_ent_module && status_section != NULL) |
659 | { | |
660 | if (!bfd_set_section_size (core_bfd, | |
661 | status_section, | |
662 | status_section->_raw_size + sect_size)) | |
0ad10c0f | 663 | { |
eedc36cb | 664 | bfd_perror ("resizing status section"); |
0ad10c0f CF |
665 | goto failed; |
666 | }; | |
eedc36cb CF |
667 | continue; |
668 | } | |
0ad10c0f | 669 | |
eedc36cb CF |
670 | deb_printf ("creating section (type%u) %s(%u), flags=%08x\n", |
671 | p->type, sect_name, sect_size, sect_flags); | |
0ad10c0f | 672 | |
eedc36cb CF |
673 | char *buf = strdup (sect_name); |
674 | new_section = bfd_make_section (core_bfd, buf); | |
0ad10c0f | 675 | |
eedc36cb CF |
676 | if (new_section == NULL || |
677 | !bfd_set_section_flags (core_bfd, new_section, sect_flags) || | |
678 | !bfd_set_section_size (core_bfd, new_section, sect_size)) | |
0ad10c0f | 679 | { |
eedc36cb | 680 | bfd_perror ("creating section"); |
0ad10c0f CF |
681 | goto failed; |
682 | }; | |
683 | ||
684 | new_section->vma = sect_vma; | |
685 | new_section->output_section = new_section; | |
686 | new_section->output_offset = 0; | |
687 | p->section = new_section; | |
688 | } | |
689 | ||
690 | return 1; | |
691 | ||
692 | failed: | |
693 | dumper_abort (); | |
694 | return 0; | |
695 | } | |
696 | ||
697 | int | |
698 | dumper::write_core_dump () | |
699 | { | |
eedc36cb CF |
700 | if (!sane ()) |
701 | return 0; | |
0ad10c0f | 702 | |
eedc36cb | 703 | for (process_entity * p = list; p != NULL; p = p->next) |
0ad10c0f | 704 | { |
eedc36cb CF |
705 | if (p->section == NULL) |
706 | continue; | |
707 | ||
708 | deb_printf ("writing section type=%u base=%08x size=%08x flags=%08x\n", | |
709 | p->type, | |
710 | p->section->vma, | |
711 | p->section->_raw_size, | |
712 | p->section->flags); | |
713 | ||
714 | switch (p->type) | |
715 | { | |
716 | case pr_ent_memory: | |
717 | dump_memory_region (p->section, &(p->u.memory)); | |
0ad10c0f CF |
718 | break; |
719 | ||
eedc36cb CF |
720 | case pr_ent_thread: |
721 | dump_thread (p->section, &(p->u.thread)); | |
722 | break; | |
723 | ||
724 | case pr_ent_module: | |
725 | dump_module (p->section, &(p->u.module)); | |
726 | break; | |
0ad10c0f | 727 | |
eedc36cb | 728 | default: |
0ad10c0f CF |
729 | continue; |
730 | ||
eedc36cb | 731 | } |
0ad10c0f CF |
732 | } |
733 | return 1; | |
734 | } | |
735 | ||
736 | static void | |
737 | usage () | |
738 | { | |
eedc36cb CF |
739 | fprintf (stderr, "Usage: dumper [-v] [-c filename] pid\n"); |
740 | fprintf (stderr, "-c filename -- dump core to filename.core\n"); | |
741 | fprintf (stderr, "-d -- print some debugging info while dumping\n"); | |
742 | fprintf (stderr, "pid -- win32-pid of process to dump\n"); | |
0ad10c0f CF |
743 | } |
744 | ||
745 | int | |
eedc36cb | 746 | main (int argc, char **argv) |
0ad10c0f CF |
747 | { |
748 | int opt; | |
ce475802 | 749 | const char *p = ""; |
0ad10c0f CF |
750 | DWORD pid; |
751 | ||
752 | while ((opt = getopt (argc, argv, "dc:")) != EOF) | |
753 | switch (opt) | |
754 | { | |
755 | case 'd': | |
756 | verbose = TRUE; | |
757 | break; | |
758 | case 'c': | |
eedc36cb CF |
759 | char win32_name[MAX_PATH]; |
760 | cygwin_conv_to_win32_path (optarg, win32_name); | |
761 | if ((p = strrchr (win32_name, '\\'))) | |
0ad10c0f CF |
762 | p++; |
763 | else | |
764 | p = win32_name; | |
765 | break; | |
766 | } | |
767 | ||
eedc36cb CF |
768 | char *core_file = (char *) malloc (strlen (p) + sizeof (".core")); |
769 | if (!core_file) | |
0ad10c0f | 770 | { |
eedc36cb | 771 | fprintf (stderr, "error allocating memory\n"); |
0ad10c0f CF |
772 | return -1; |
773 | } | |
eedc36cb | 774 | sprintf (core_file, "%s.core", p); |
0ad10c0f | 775 | |
eedc36cb CF |
776 | if (argv && *(argv + optind)) |
777 | pid = atoi (*(argv + optind)); | |
0ad10c0f CF |
778 | else |
779 | { | |
780 | usage (); | |
781 | return -1; | |
782 | } | |
783 | ||
784 | DWORD tid = 0; | |
785 | ||
eedc36cb CF |
786 | if (verbose) |
787 | printf ("dumping process #%lu to %s\n", pid, core_file); | |
0ad10c0f | 788 | |
eedc36cb CF |
789 | dumper d (pid, tid, core_file); |
790 | if (!d.sane ()) | |
0ad10c0f CF |
791 | return -1; |
792 | d.collect_process_information (); | |
eedc36cb | 793 | free (core_file); |
0ad10c0f CF |
794 | |
795 | return 0; | |
796 | }; |