3 Written by Egor Duda <deo@logos-m.ru>
5 This file is part of Cygwin.
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
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
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.
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. */
24 #include <elf/common.h>
25 #include <elf/external.h>
26 #include <sys/procfs.h>
27 #include <sys/cygwin.h>
28 #include <cygwin/version.h>
35 #include <sys/param.h>
37 #define PSAPI_VERSION 1
42 #define NOTE_NAME_SIZE 16
44 #ifdef bfd_get_section_size
46 #define get_section_name(abfd, sect) bfd_get_section_name (abfd, sect)
47 #define get_section_size(sect) bfd_get_section_size(sect)
48 #define set_section_size(abfd, sect, size) bfd_set_section_size(abfd, sect, size)
49 #define set_section_flags(abfd, sect, flags) bfd_set_section_flags(abfd, sect, flags)
51 /* otherwise bfd >= 2.34 */
52 #define get_section_name(afbd, sect) bfd_section_name (sect)
53 #define get_section_size(sect) bfd_section_size(sect)
54 #define set_section_size(abfd, sect, size) bfd_set_section_size(sect, size)
55 #define set_section_flags(abfd, sect, flags) bfd_set_section_flags(sect, flags)
58 typedef struct _note_header
60 Elf_External_Note elf_note_header
;
61 char name
[NOTE_NAME_SIZE
- 1]; /* external note contains first byte of data */
64 __attribute__ ((packed
))
71 int deb_printf (const char *format
,...)
76 va_start (va
, format
);
77 int ret_val
= vprintf (format
, va
);
82 dumper::dumper (DWORD pid
, DWORD tid
, const char *file_name
)
84 this->file_name
= strdup (file_name
);
92 status_section
= NULL
;
94 memory_num
= module_num
= thread_num
= 0;
96 hProcess
= OpenProcess (PROCESS_ALL_ACCESS
,
97 FALSE
, /* no inheritance */
101 fprintf (stderr
, "Failed to open process #%u, error %ld\n",
102 (unsigned int) pid
, (long) GetLastError ());
119 dumper::dumper_abort ()
129 bfd_close (core_bfd
);
131 CloseHandle (hProcess
);
139 if (hProcess
== NULL
|| core_bfd
== NULL
)
145 print_section_name (bfd
* abfd
, asection
* sect
, PTR obj
)
147 deb_printf (" %s", get_section_name (abfd
, sect
));
151 dumper::print_core_section_list ()
153 deb_printf ("current sections:");
154 bfd_map_over_sections (core_bfd
, &print_section_name
, NULL
);
159 dumper::add_process_entity_to_list (process_entity_type type
)
164 process_entity
*new_entity
= (process_entity
*) malloc (sizeof (process_entity
));
165 if (new_entity
== NULL
)
167 new_entity
->next
= NULL
;
168 new_entity
->section
= NULL
;
172 last
->next
= new_entity
;
178 dumper::add_thread (DWORD tid
, HANDLE hThread
)
185 process_entity
*new_entity
= add_process_entity_to_list (pr_ent_thread
);
186 if (new_entity
== NULL
)
188 new_entity
->type
= pr_ent_thread
;
191 new_entity
->u
.thread
.tid
= tid
;
192 new_entity
->u
.thread
.hThread
= hThread
;
194 pcontext
= &(new_entity
->u
.thread
.context
);
195 pcontext
->ContextFlags
= CONTEXT_FULL
| CONTEXT_FLOATING_POINT
;
196 if (!GetThreadContext (hThread
, pcontext
))
198 deb_printf ("Failed to read thread context (tid=%x), error %ld\n", tid
, GetLastError ());
202 deb_printf ("added thread %u\n", tid
);
207 dumper::add_mem_region (LPBYTE base
, SIZE_T size
)
212 if (base
== NULL
|| size
== 0)
213 return 1; // just ignore empty regions
215 process_entity
*new_entity
= add_process_entity_to_list (pr_ent_memory
);
216 if (new_entity
== NULL
)
218 new_entity
->type
= pr_ent_memory
;
221 new_entity
->u
.memory
.base
= base
;
222 new_entity
->u
.memory
.size
= size
;
224 deb_printf ("added memory region %p-%p\n", base
, base
+ size
);
229 dumper::add_module (LPVOID base_address
)
234 char *module_name
= psapi_get_module_name (hProcess
, base_address
);
235 if (module_name
== NULL
)
238 process_entity
*new_entity
= add_process_entity_to_list (pr_ent_module
);
239 if (new_entity
== NULL
)
241 new_entity
->type
= pr_ent_module
;
244 new_entity
->u
.module
.base_address
= base_address
;
245 new_entity
->u
.module
.name
= module_name
;
247 deb_printf ("added module %p %s\n", base_address
, module_name
);
251 #define PAGE_BUFFER_SIZE 4096
253 void protect_dump(DWORD protect
, char *buf
)
256 pt
[0] = (protect
& PAGE_READONLY
) ? "RO " : "";
257 pt
[1] = (protect
& PAGE_READWRITE
) ? "RW " : "";
258 pt
[2] = (protect
& PAGE_WRITECOPY
) ? "WC " : "";
259 pt
[3] = (protect
& PAGE_EXECUTE
) ? "EX " : "";
260 pt
[4] = (protect
& PAGE_EXECUTE_READ
) ? "EXRO " : "";
261 pt
[5] = (protect
& PAGE_EXECUTE_READWRITE
) ? "EXRW " : "";
262 pt
[6] = (protect
& PAGE_EXECUTE_WRITECOPY
) ? "EXWC " : "";
263 pt
[7] = (protect
& PAGE_GUARD
) ? "GRD " : "";
264 pt
[8] = (protect
& PAGE_NOACCESS
) ? "NA " : "";
265 pt
[9] = (protect
& PAGE_NOCACHE
) ? "NC " : "";
268 for (int i
= 0; i
< 10; i
++)
272 #define PSWSEI_ATTRIB_SHARED (0x1 << 15)
275 getRegionAttributes(HANDLE hProcess
, LPVOID address
, DWORD
&attribs
)
277 PSAPI_WORKING_SET_EX_INFORMATION pswsei
= { address
};
279 if (QueryWorkingSetEx(hProcess
, &pswsei
, sizeof(pswsei
)))
281 attribs
= pswsei
.VirtualAttributes
.Flags
;
285 deb_printf("QueryWorkingSetEx failed status %08x\n", GetLastError());
290 dumper::collect_memory_sections ()
295 LPBYTE current_page_address
;
296 LPBYTE last_base
= (LPBYTE
) -1;
297 SIZE_T last_size
= (SIZE_T
) 0;
300 char mem_buf
[PAGE_BUFFER_SIZE
];
302 MEMORY_BASIC_INFORMATION mbi
;
304 if (hProcess
== NULL
)
307 for (current_page_address
= 0; current_page_address
< (LPBYTE
) -1;)
309 if (!VirtualQueryEx (hProcess
, current_page_address
, &mbi
, sizeof (mbi
)))
312 int skip_region_p
= 0;
313 const char *disposition
= "dumped";
315 if (mbi
.Type
& MEM_IMAGE
)
318 if (getRegionAttributes(hProcess
, current_page_address
, attribs
))
320 if (attribs
& PSWSEI_ATTRIB_SHARED
)
323 disposition
= "skipped due to shared MEM_IMAGE";
327 The undocumented MemoryWorkingSetExInformation is allegedly
328 supported since XP, so should always succeed, but if it fails,
329 fallback to looking at region protection.
331 else if (!(mbi
.Protect
& (PAGE_EXECUTE_READWRITE
| PAGE_READWRITE
)))
334 disposition
= "skipped due to non-writeable MEM_IMAGE";
338 if (mbi
.Protect
& PAGE_NOACCESS
)
341 disposition
= "skipped due to noaccess";
344 if (mbi
.Protect
& PAGE_GUARD
)
347 disposition
= "skipped due to guardpage";
350 if (mbi
.State
!= MEM_COMMIT
)
353 disposition
= "skipped due to uncommited";
358 protect_dump(mbi
.Protect
, buf
);
360 const char *state
= "";
361 const char *type
= "";
363 if (mbi
.State
& MEM_COMMIT
)
367 else if (mbi
.State
& MEM_FREE
)
372 else if (mbi
.State
& MEM_RESERVE
)
377 if (mbi
.Type
& MEM_IMAGE
)
381 else if (mbi
.Type
& MEM_MAPPED
)
385 else if (mbi
.Type
& MEM_PRIVATE
)
390 deb_printf ("region 0x%016lx-0x%016lx (protect = %-8s, state = %-7s, type = %-7s, %s)\n",
391 current_page_address
,
392 current_page_address
+ mbi
.RegionSize
,
393 buf
, state
, type
, disposition
);
398 /* just to make sure that later we'll be able to read it.
399 According to MS docs either region is all-readable or
401 if (!ReadProcessMemory (hProcess
, current_page_address
, mem_buf
, sizeof (mem_buf
), &done
))
403 DWORD err
= GetLastError ();
405 deb_printf ("warning: failed to read memory at %p-%p, error %ld.\n",
406 current_page_address
,
407 current_page_address
+ mbi
.RegionSize
,
415 if (last_base
+ last_size
== current_page_address
)
416 last_size
+= mbi
.RegionSize
;
419 add_mem_region (last_base
, last_size
);
420 last_base
= (LPBYTE
) mbi
.BaseAddress
;
421 last_size
= mbi
.RegionSize
;
426 add_mem_region (last_base
, last_size
);
431 current_page_address
+= mbi
.RegionSize
;
434 /* dump last sections, if any */
435 add_mem_region (last_base
, last_size
);
440 dumper::dump_memory_region (asection
* to
, process_mem_region
* memory
)
445 SIZE_T size
= memory
->size
;
448 LPBYTE pos
= memory
->base
;
451 if (to
== NULL
|| memory
== NULL
)
454 char mem_buf
[PAGE_BUFFER_SIZE
];
458 todo
= MIN (size
, PAGE_BUFFER_SIZE
);
459 if (!ReadProcessMemory (hProcess
, pos
, mem_buf
, todo
, &done
))
461 deb_printf ("Failed to read process memory at %x(%x), error %ld\n", pos
, todo
, GetLastError ());
466 if (!bfd_set_section_contents (core_bfd
, to
, mem_buf
, sect_pos
, done
))
468 bfd_perror ("writing memory region to bfd");
478 dumper::dump_thread (asection
* to
, process_thread
* thread
)
483 if (to
== NULL
|| thread
== NULL
)
486 win32_pstatus thread_pstatus
;
489 bfd_putl32 (NOTE_NAME_SIZE
, header
.elf_note_header
.namesz
);
490 bfd_putl32 (sizeof (thread_pstatus
), header
.elf_note_header
.descsz
);
491 bfd_putl32 (NT_WIN32PSTATUS
, header
.elf_note_header
.type
);
492 #pragma GCC diagnostic push
493 #pragma GCC diagnostic ignored "-Wstringop-overflow"
494 #pragma GCC diagnostic ignored "-Warray-bounds"
495 strncpy (header
.elf_note_header
.name
, "win32thread", NOTE_NAME_SIZE
);
496 #pragma GCC diagnostic pop
498 thread_pstatus
.data_type
= NOTE_INFO_THREAD
;
499 thread_pstatus
.data
.thread_info
.tid
= thread
->tid
;
503 /* this is a special case. we don't know, which thread
504 was active when exception occured, so let's blame
506 thread_pstatus
.data
.thread_info
.is_active_thread
= TRUE
;
509 else if (tid
> 0 && thread
->tid
== tid
)
510 thread_pstatus
.data
.thread_info
.is_active_thread
= TRUE
;
512 thread_pstatus
.data
.thread_info
.is_active_thread
= FALSE
;
514 memcpy (&(thread_pstatus
.data
.thread_info
.thread_context
),
516 sizeof (thread
->context
));
518 if (!bfd_set_section_contents (core_bfd
, to
, &header
,
521 !bfd_set_section_contents (core_bfd
, to
, &thread_pstatus
,
523 sizeof (thread_pstatus
)))
525 bfd_perror ("writing thread info to bfd");
533 dumper::dump_module (asection
* to
, process_module
* module
)
538 if (to
== NULL
|| module
== NULL
)
541 struct win32_pstatus
*module_pstatus_ptr
;
543 int note_length
= sizeof (struct win32_pstatus
) + strlen (module
->name
);
545 char *buf
= (char *) malloc (note_length
);
549 fprintf (stderr
, "Error alloating memory. Dumping aborted.\n");
553 module_pstatus_ptr
= (struct win32_pstatus
*) buf
;
556 bfd_putl32 (NOTE_NAME_SIZE
, header
.elf_note_header
.namesz
);
557 bfd_putl32 (note_length
, header
.elf_note_header
.descsz
);
558 bfd_putl32 (NT_WIN32PSTATUS
, header
.elf_note_header
.type
);
559 #pragma GCC diagnostic push
560 #pragma GCC diagnostic ignored "-Wstringop-overflow"
561 #pragma GCC diagnostic ignored "-Warray-bounds"
562 strncpy (header
.elf_note_header
.name
, "win32module", NOTE_NAME_SIZE
);
563 #pragma GCC diagnostic pop
566 module_pstatus_ptr
->data_type
= NOTE_INFO_MODULE64
;
568 module_pstatus_ptr
->data_type
= NOTE_INFO_MODULE
;
570 module_pstatus_ptr
->data
.module_info
.base_address
= module
->base_address
;
571 module_pstatus_ptr
->data
.module_info
.module_name_size
= strlen (module
->name
) + 1;
572 strcpy (module_pstatus_ptr
->data
.module_info
.module_name
, module
->name
);
574 if (!bfd_set_section_contents (core_bfd
, to
, &header
,
577 !bfd_set_section_contents (core_bfd
, to
, module_pstatus_ptr
,
581 bfd_perror ("writing module info to bfd");
595 dumper::collect_process_information ()
600 if (!DebugActiveProcess (pid
))
602 fprintf (stderr
, "Cannot attach to process #%u, error %ld",
603 (unsigned int) pid
, (long) GetLastError ());
607 DEBUG_EVENT current_event
;
611 if (!WaitForDebugEvent (¤t_event
, INFINITE
))
614 deb_printf ("got debug event %d\n", current_event
.dwDebugEventCode
);
616 switch (current_event
.dwDebugEventCode
)
618 case CREATE_THREAD_DEBUG_EVENT
:
620 if (!add_thread (current_event
.dwThreadId
,
621 current_event
.u
.CreateThread
.hThread
))
626 case CREATE_PROCESS_DEBUG_EVENT
:
628 if (!add_module (current_event
.u
.CreateProcessInfo
.lpBaseOfImage
) ||
629 !add_thread (current_event
.dwThreadId
,
630 current_event
.u
.CreateProcessInfo
.hThread
))
635 case EXIT_PROCESS_DEBUG_EVENT
:
637 deb_printf ("debugee quits");
638 ContinueDebugEvent (current_event
.dwProcessId
,
639 current_event
.dwThreadId
,
646 case LOAD_DLL_DEBUG_EVENT
:
648 if (!add_module (current_event
.u
.LoadDll
.lpBaseOfDll
))
653 case EXCEPTION_DEBUG_EVENT
:
655 collect_memory_sections ();
657 /* got all info. time to dump */
659 if (!prepare_core_dump ())
661 fprintf (stderr
, "Failed to prepare core dump\n");
665 if (!write_core_dump ())
667 fprintf (stderr
, "Failed to write core dump\n");
682 ContinueDebugEvent (current_event
.dwProcessId
,
683 current_event
.dwThreadId
,
690 if (!DebugActiveProcessStop (pid
))
692 fprintf (stderr
, "Cannot detach from process #%u, error %ld",
693 (unsigned int) pid
, (long) GetLastError ());
696 /* Otherwise, the debuggee is terminated when this process exits
697 (as DebugSetProcessKillOnExit() defaults to TRUE) */
703 dumper::init_core_dump ()
708 const char *target
= "elf64-x86-64";
710 const char *target
= "elf32-i386";
713 core_bfd
= bfd_openw (file_name
, target
);
714 if (core_bfd
== NULL
)
716 bfd_perror ("opening bfd");
720 if (!bfd_set_format (core_bfd
, bfd_core
))
722 bfd_perror ("setting bfd format");
726 if (!bfd_set_arch_mach (core_bfd
, bfd_arch_i386
, 0 /* = default */))
728 bfd_perror ("setting bfd architecture");
741 dumper::prepare_core_dump ()
753 asection
*new_section
;
755 for (process_entity
* p
= list
; p
!= NULL
; p
= p
->next
)
759 unsigned long phdr_type
= PT_LOAD
;
764 sprintf (sect_name
, ".mem/%u", sect_no
);
765 sect_flags
= SEC_HAS_CONTENTS
| SEC_ALLOC
| SEC_LOAD
;
766 sect_size
= p
->u
.memory
.size
;
767 sect_vma
= (bfd_vma
) (p
->u
.memory
.base
);
772 sprintf (sect_name
, ".note/%u", sect_no
);
773 sect_flags
= SEC_HAS_CONTENTS
| SEC_LOAD
;
774 sect_size
= sizeof (note_header
) + sizeof (struct win32_pstatus
);
780 sprintf (sect_name
, ".note/%u", sect_no
);
781 sect_flags
= SEC_HAS_CONTENTS
| SEC_LOAD
;
782 sect_size
= sizeof (note_header
) + sizeof (struct win32_pstatus
) +
783 (bfd_size_type
) (strlen (p
->u
.module
.name
));
792 if (p
->type
== pr_ent_module
&& status_section
!= NULL
)
794 if (!set_section_size (core_bfd
,
796 (get_section_size (status_section
)
799 bfd_perror ("resizing status section");
805 deb_printf ("creating section (type%u) %s(%u), flags=%08x\n",
806 p
->type
, sect_name
, sect_size
, sect_flags
);
808 bfd_set_error (bfd_error_no_error
);
809 char *buf
= strdup (sect_name
);
810 new_section
= bfd_make_section (core_bfd
, buf
);
811 if (new_section
== NULL
)
813 if (bfd_get_error () == bfd_error_no_error
)
814 fprintf (stderr
, "error creating new section (%s), section already exists.\n", buf
);
816 bfd_perror ("creating section");
820 if (!set_section_flags (core_bfd
, new_section
, sect_flags
) ||
821 !set_section_size (core_bfd
, new_section
, sect_size
))
823 bfd_perror ("setting section attributes");
827 new_section
->vma
= sect_vma
;
828 new_section
->lma
= 0;
829 new_section
->output_section
= new_section
;
830 new_section
->output_offset
= 0;
831 p
->section
= new_section
;
832 int section_count
= 1;
834 bfd_boolean filehdr
= 0;
835 bfd_boolean phdrs
= 0;
838 bfd_boolean valid_at
= 0;
841 bfd_boolean valid_flags
= 1;
843 if (p
->type
== pr_ent_memory
)
845 MEMORY_BASIC_INFORMATION mbi
;
846 if (!VirtualQueryEx (hProcess
, (LPVOID
)sect_vma
, &mbi
, sizeof (mbi
)))
848 bfd_perror ("getting mem region flags");
858 { PAGE_READONLY
, PF_R
},
859 { PAGE_READWRITE
, PF_R
| PF_W
},
860 { PAGE_WRITECOPY
, PF_W
},
861 { PAGE_EXECUTE
, PF_X
},
862 { PAGE_EXECUTE_READ
, PF_X
| PF_R
},
863 { PAGE_EXECUTE_READWRITE
, PF_X
| PF_R
| PF_W
},
864 { PAGE_EXECUTE_WRITECOPY
, PF_X
| PF_W
}
868 i
< sizeof (mappings
) / sizeof (mappings
[0]);
870 if ((mbi
.Protect
& mappings
[i
].protect
) != 0)
871 flags
|= mappings
[i
].flags
;
874 if (!bfd_record_phdr (core_bfd
, phdr_type
,
878 section_count
, &new_section
))
880 bfd_perror ("recording program headers");
892 dumper::write_core_dump ()
897 for (process_entity
* p
= list
; p
!= NULL
; p
= p
->next
)
899 if (p
->section
== NULL
)
902 deb_printf ("writing section type=%u base=%p size=%p flags=%08x\n",
905 get_section_size (p
->section
),
911 dump_memory_region (p
->section
, &(p
->u
.memory
));
915 dump_thread (p
->section
, &(p
->u
.thread
));
919 dump_module (p
->section
, &(p
->u
.module
));
930 static void __attribute__ ((__noreturn__
))
931 usage (FILE *stream
, int status
)
934 Usage: %s [OPTION] FILENAME WIN32PID\n\
936 Dump core from WIN32PID to FILENAME.core\n\
938 -n, --nokill don't terminate the dumped process\n\
939 -d, --verbose be verbose while dumping\n\
940 -h, --help output help information and exit\n\
941 -q, --quiet be quiet while dumping (default)\n\
942 -V, --version output version information and exit\n\
943 \n", program_invocation_short_name
);
947 struct option longopts
[] = {
948 {"nokill", no_argument
, NULL
, 'n'},
949 {"verbose", no_argument
, NULL
, 'd'},
950 {"help", no_argument
, NULL
, 'h'},
951 {"quiet", no_argument
, NULL
, 'q'},
952 {"version", no_argument
, 0, 'V'},
953 {0, no_argument
, NULL
, 0}
955 const char *opts
= "ndhqV";
960 printf ("dumper (cygwin) %d.%d.%d\n"
961 "Core Dumper for Cygwin\n"
962 "Copyright (C) 1999 - %s Cygwin Authors\n"
963 "This is free software; see the source for copying conditions. There is NO\n"
964 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
965 CYGWIN_VERSION_DLL_MAJOR
/ 1000,
966 CYGWIN_VERSION_DLL_MAJOR
% 1000,
967 CYGWIN_VERSION_DLL_MINOR
,
968 strrchr (__DATE__
, ' ') + 1);
972 main (int argc
, char **argv
)
978 while ((opt
= getopt_long (argc
, argv
, opts
, longopts
, NULL
) ) != EOF
)
996 fprintf (stderr
, "Try `%s --help' for more information.\n",
997 program_invocation_short_name
);
1001 if (argv
&& *(argv
+ optind
) && *(argv
+ optind
+1))
1003 ssize_t len
= cygwin_conv_path (CCP_POSIX_TO_WIN_A
| CCP_RELATIVE
,
1004 *(argv
+ optind
), NULL
, 0);
1005 char *win32_name
= (char *) alloca (len
);
1006 cygwin_conv_path (CCP_POSIX_TO_WIN_A
| CCP_RELATIVE
, *(argv
+ optind
),
1008 if ((p
= strrchr (win32_name
, '\\')))
1012 pid
= strtoul (*(argv
+ optind
+ 1), NULL
, 10);
1020 char *core_file
= (char *) malloc (strlen (p
) + sizeof (".core"));
1023 fprintf (stderr
, "error allocating memory\n");
1026 sprintf (core_file
, "%s.core", p
);
1031 printf ("dumping process #%u to %s\n", (unsigned int) pid
, core_file
);
1033 dumper
d (pid
, tid
, core_file
);
1036 d
.collect_process_information ();