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>
41 #define NOTE_NAME_SIZE 16
43 #ifdef bfd_get_section_size
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)
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)
57 typedef struct _note_header
59 Elf_External_Note elf_note_header
;
60 char name
[NOTE_NAME_SIZE
- 1]; /* external note contains first byte of data */
63 __attribute__ ((packed
))
70 int deb_printf (const char *format
,...)
75 va_start (va
, format
);
76 int ret_val
= vprintf (format
, va
);
81 dumper::dumper (DWORD pid
, DWORD tid
, const char *file_name
)
83 this->file_name
= strdup (file_name
);
91 status_section
= NULL
;
93 memory_num
= module_num
= thread_num
= 0;
95 hProcess
= OpenProcess (PROCESS_ALL_ACCESS
,
96 FALSE
, /* no inheritance */
100 fprintf (stderr
, "Failed to open process #%u, error %ld\n",
101 (unsigned int) pid
, (long) GetLastError ());
118 dumper::dumper_abort ()
128 bfd_close (core_bfd
);
130 CloseHandle (hProcess
);
138 if (hProcess
== NULL
|| core_bfd
== NULL
)
144 print_section_name (bfd
* abfd
, asection
* sect
, PTR obj
)
146 deb_printf (" %s", get_section_name (abfd
, sect
));
150 dumper::print_core_section_list ()
152 deb_printf ("current sections:");
153 bfd_map_over_sections (core_bfd
, &print_section_name
, NULL
);
158 dumper::add_process_entity_to_list (process_entity_type type
)
163 process_entity
*new_entity
= (process_entity
*) malloc (sizeof (process_entity
));
164 if (new_entity
== NULL
)
166 new_entity
->next
= NULL
;
167 new_entity
->section
= NULL
;
171 last
->next
= new_entity
;
177 dumper::add_thread (DWORD tid
, HANDLE hThread
)
184 process_entity
*new_entity
= add_process_entity_to_list (pr_ent_thread
);
185 if (new_entity
== NULL
)
187 new_entity
->type
= pr_ent_thread
;
190 new_entity
->u
.thread
.tid
= tid
;
191 new_entity
->u
.thread
.hThread
= hThread
;
193 pcontext
= &(new_entity
->u
.thread
.context
);
194 pcontext
->ContextFlags
= CONTEXT_FULL
| CONTEXT_FLOATING_POINT
;
195 if (!GetThreadContext (hThread
, pcontext
))
197 deb_printf ("Failed to read thread context (tid=%x), error %ld\n", tid
, GetLastError ());
201 deb_printf ("added thread %u\n", tid
);
206 dumper::add_mem_region (LPBYTE base
, SIZE_T size
)
211 if (base
== NULL
|| size
== 0)
212 return 1; // just ignore empty regions
214 process_entity
*new_entity
= add_process_entity_to_list (pr_ent_memory
);
215 if (new_entity
== NULL
)
217 new_entity
->type
= pr_ent_memory
;
220 new_entity
->u
.memory
.base
= base
;
221 new_entity
->u
.memory
.size
= size
;
223 deb_printf ("added memory region %p-%p\n", base
, base
+ size
);
228 dumper::add_module (LPVOID base_address
)
233 char *module_name
= psapi_get_module_name (hProcess
, base_address
);
234 if (module_name
== NULL
)
237 process_entity
*new_entity
= add_process_entity_to_list (pr_ent_module
);
238 if (new_entity
== NULL
)
240 new_entity
->type
= pr_ent_module
;
243 new_entity
->u
.module
.base_address
= base_address
;
244 new_entity
->u
.module
.name
= module_name
;
246 deb_printf ("added module %p %s\n", base_address
, module_name
);
250 #define PAGE_BUFFER_SIZE 4096
252 void protect_dump(DWORD protect
, char *buf
)
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 " : "";
267 for (int i
= 0; i
< 10; i
++)
271 #define PSWSEI_ATTRIB_SHARED (0x1 << 15)
274 getRegionAttributes(HANDLE hProcess
, LPVOID address
, DWORD
&attribs
)
276 PSAPI_WORKING_SET_EX_INFORMATION pswsei
= { address
};
278 if (QueryWorkingSetEx(hProcess
, &pswsei
, sizeof(pswsei
)))
280 attribs
= pswsei
.VirtualAttributes
.Flags
;
284 deb_printf("QueryWorkingSetEx failed status %08x\n", GetLastError());
289 dumper::collect_memory_sections ()
294 LPBYTE current_page_address
;
295 LPBYTE last_base
= (LPBYTE
) -1;
296 SIZE_T last_size
= (SIZE_T
) 0;
299 char mem_buf
[PAGE_BUFFER_SIZE
];
301 MEMORY_BASIC_INFORMATION mbi
;
303 if (hProcess
== NULL
)
306 for (current_page_address
= 0; current_page_address
< (LPBYTE
) -1;)
308 if (!VirtualQueryEx (hProcess
, current_page_address
, &mbi
, sizeof (mbi
)))
311 int skip_region_p
= 0;
312 const char *disposition
= "dumped";
314 if (mbi
.Type
& MEM_IMAGE
)
317 if (getRegionAttributes(hProcess
, current_page_address
, attribs
))
319 if (attribs
& PSWSEI_ATTRIB_SHARED
)
322 disposition
= "skipped due to shared MEM_IMAGE";
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.
330 else if (!(mbi
.Protect
& (PAGE_EXECUTE_READWRITE
| PAGE_READWRITE
)))
333 disposition
= "skipped due to non-writeable MEM_IMAGE";
337 if (mbi
.Protect
& PAGE_NOACCESS
)
340 disposition
= "skipped due to noaccess";
343 if (mbi
.Protect
& PAGE_GUARD
)
346 disposition
= "skipped due to guardpage";
349 if (mbi
.State
!= MEM_COMMIT
)
352 disposition
= "skipped due to uncommited";
357 protect_dump(mbi
.Protect
, buf
);
359 const char *state
= "";
360 const char *type
= "";
362 if (mbi
.State
& MEM_COMMIT
)
366 else if (mbi
.State
& MEM_FREE
)
371 else if (mbi
.State
& MEM_RESERVE
)
376 if (mbi
.Type
& MEM_IMAGE
)
380 else if (mbi
.Type
& MEM_MAPPED
)
384 else if (mbi
.Type
& MEM_PRIVATE
)
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
);
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
400 if (!ReadProcessMemory (hProcess
, current_page_address
, mem_buf
, sizeof (mem_buf
), &done
))
402 DWORD err
= GetLastError ();
404 deb_printf ("warning: failed to read memory at %p-%p, error %ld.\n",
405 current_page_address
,
406 current_page_address
+ mbi
.RegionSize
,
414 if (last_base
+ last_size
== current_page_address
)
415 last_size
+= mbi
.RegionSize
;
418 add_mem_region (last_base
, last_size
);
419 last_base
= (LPBYTE
) mbi
.BaseAddress
;
420 last_size
= mbi
.RegionSize
;
425 add_mem_region (last_base
, last_size
);
430 current_page_address
+= mbi
.RegionSize
;
433 /* dump last sections, if any */
434 add_mem_region (last_base
, last_size
);
439 dumper::dump_memory_region (asection
* to
, process_mem_region
* memory
)
444 SIZE_T size
= memory
->size
;
447 LPBYTE pos
= memory
->base
;
450 if (to
== NULL
|| memory
== NULL
)
453 char mem_buf
[PAGE_BUFFER_SIZE
];
457 todo
= MIN (size
, PAGE_BUFFER_SIZE
);
458 if (!ReadProcessMemory (hProcess
, pos
, mem_buf
, todo
, &done
))
460 deb_printf ("Failed to read process memory at %x(%x), error %ld\n", pos
, todo
, GetLastError ());
465 if (!bfd_set_section_contents (core_bfd
, to
, mem_buf
, sect_pos
, done
))
467 bfd_perror ("writing memory region to bfd");
477 dumper::dump_thread (asection
* to
, process_thread
* thread
)
482 if (to
== NULL
|| thread
== NULL
)
485 win32_pstatus thread_pstatus
;
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
);
491 #pragma GCC diagnostic push
492 #pragma GCC diagnostic ignored "-Wstringop-overflow"
493 #pragma GCC diagnostic ignored "-Warray-bounds"
494 strncpy (header
.elf_note_header
.name
, "win32thread", NOTE_NAME_SIZE
);
495 #pragma GCC diagnostic pop
497 thread_pstatus
.data_type
= NOTE_INFO_THREAD
;
498 thread_pstatus
.data
.thread_info
.tid
= thread
->tid
;
502 /* this is a special case. we don't know, which thread
503 was active when exception occured, so let's blame
505 thread_pstatus
.data
.thread_info
.is_active_thread
= TRUE
;
508 else if (tid
> 0 && thread
->tid
== tid
)
509 thread_pstatus
.data
.thread_info
.is_active_thread
= TRUE
;
511 thread_pstatus
.data
.thread_info
.is_active_thread
= FALSE
;
513 memcpy (&(thread_pstatus
.data
.thread_info
.thread_context
),
515 sizeof (thread
->context
));
517 if (!bfd_set_section_contents (core_bfd
, to
, &header
,
520 !bfd_set_section_contents (core_bfd
, to
, &thread_pstatus
,
522 sizeof (thread_pstatus
)))
524 bfd_perror ("writing thread info to bfd");
532 dumper::dump_module (asection
* to
, process_module
* module
)
537 if (to
== NULL
|| module
== NULL
)
540 struct win32_pstatus
*module_pstatus_ptr
;
542 int note_length
= sizeof (struct win32_pstatus
) + strlen (module
->name
);
544 char *buf
= (char *) malloc (note_length
);
548 fprintf (stderr
, "Error alloating memory. Dumping aborted.\n");
552 module_pstatus_ptr
= (struct win32_pstatus
*) buf
;
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
);
558 #pragma GCC diagnostic push
559 #pragma GCC diagnostic ignored "-Wstringop-overflow"
560 #pragma GCC diagnostic ignored "-Warray-bounds"
561 strncpy (header
.elf_note_header
.name
, "win32module", NOTE_NAME_SIZE
);
562 #pragma GCC diagnostic pop
565 module_pstatus_ptr
->data_type
= NOTE_INFO_MODULE64
;
567 module_pstatus_ptr
->data_type
= NOTE_INFO_MODULE
;
569 module_pstatus_ptr
->data
.module_info
.base_address
= module
->base_address
;
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
);
573 if (!bfd_set_section_contents (core_bfd
, to
, &header
,
576 !bfd_set_section_contents (core_bfd
, to
, module_pstatus_ptr
,
580 bfd_perror ("writing module info to bfd");
594 dumper::collect_process_information ()
599 if (!DebugActiveProcess (pid
))
601 fprintf (stderr
, "Cannot attach to process #%u, error %ld",
602 (unsigned int) pid
, (long) GetLastError ());
606 DEBUG_EVENT current_event
;
610 if (!WaitForDebugEvent (¤t_event
, INFINITE
))
613 deb_printf ("got debug event %d\n", current_event
.dwDebugEventCode
);
615 switch (current_event
.dwDebugEventCode
)
617 case CREATE_THREAD_DEBUG_EVENT
:
619 if (!add_thread (current_event
.dwThreadId
,
620 current_event
.u
.CreateThread
.hThread
))
625 case CREATE_PROCESS_DEBUG_EVENT
:
627 if (!add_module (current_event
.u
.CreateProcessInfo
.lpBaseOfImage
) ||
628 !add_thread (current_event
.dwThreadId
,
629 current_event
.u
.CreateProcessInfo
.hThread
))
634 case EXIT_PROCESS_DEBUG_EVENT
:
636 deb_printf ("debugee quits");
637 ContinueDebugEvent (current_event
.dwProcessId
,
638 current_event
.dwThreadId
,
645 case LOAD_DLL_DEBUG_EVENT
:
647 if (!add_module (current_event
.u
.LoadDll
.lpBaseOfDll
))
652 case EXCEPTION_DEBUG_EVENT
:
654 collect_memory_sections ();
656 /* got all info. time to dump */
658 if (!prepare_core_dump ())
660 fprintf (stderr
, "Failed to prepare core dump\n");
664 if (!write_core_dump ())
666 fprintf (stderr
, "Failed to write core dump\n");
681 ContinueDebugEvent (current_event
.dwProcessId
,
682 current_event
.dwThreadId
,
689 if (!DebugActiveProcessStop (pid
))
691 fprintf (stderr
, "Cannot detach from process #%u, error %ld",
692 (unsigned int) pid
, (long) GetLastError ());
695 /* Otherwise, the debuggee is terminated when this process exits
696 (as DebugSetProcessKillOnExit() defaults to TRUE) */
702 dumper::init_core_dump ()
707 const char *target
= "elf64-x86-64";
709 const char *target
= "elf32-i386";
712 core_bfd
= bfd_openw (file_name
, target
);
713 if (core_bfd
== NULL
)
715 bfd_perror ("opening bfd");
719 if (!bfd_set_format (core_bfd
, bfd_core
))
721 bfd_perror ("setting bfd format");
725 if (!bfd_set_arch_mach (core_bfd
, bfd_arch_i386
, 0 /* = default */))
727 bfd_perror ("setting bfd architecture");
740 dumper::prepare_core_dump ()
752 asection
*new_section
;
754 for (process_entity
* p
= list
; p
!= NULL
; p
= p
->next
)
758 unsigned long phdr_type
= PT_LOAD
;
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
);
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
);
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
));
791 if (p
->type
== pr_ent_module
&& status_section
!= NULL
)
793 if (!set_section_size (core_bfd
,
795 (get_section_size (status_section
)
798 bfd_perror ("resizing status section");
804 deb_printf ("creating section (type%u) %s(%u), flags=%08x\n",
805 p
->type
, sect_name
, sect_size
, sect_flags
);
807 bfd_set_error (bfd_error_no_error
);
808 char *buf
= strdup (sect_name
);
809 new_section
= bfd_make_section (core_bfd
, buf
);
810 if (new_section
== NULL
)
812 if (bfd_get_error () == bfd_error_no_error
)
813 fprintf (stderr
, "error creating new section (%s), section already exists.\n", buf
);
815 bfd_perror ("creating section");
819 if (!set_section_flags (core_bfd
, new_section
, sect_flags
) ||
820 !set_section_size (core_bfd
, new_section
, sect_size
))
822 bfd_perror ("setting section attributes");
826 new_section
->vma
= sect_vma
;
827 new_section
->lma
= 0;
828 new_section
->output_section
= new_section
;
829 new_section
->output_offset
= 0;
830 p
->section
= new_section
;
831 int section_count
= 1;
833 bfd_boolean filehdr
= 0;
834 bfd_boolean phdrs
= 0;
837 bfd_boolean valid_at
= 0;
840 bfd_boolean valid_flags
= 1;
842 if (p
->type
== pr_ent_memory
)
844 MEMORY_BASIC_INFORMATION mbi
;
845 if (!VirtualQueryEx (hProcess
, (LPVOID
)sect_vma
, &mbi
, sizeof (mbi
)))
847 bfd_perror ("getting mem region flags");
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
}
867 i
< sizeof (mappings
) / sizeof (mappings
[0]);
869 if ((mbi
.Protect
& mappings
[i
].protect
) != 0)
870 flags
|= mappings
[i
].flags
;
873 if (!bfd_record_phdr (core_bfd
, phdr_type
,
877 section_count
, &new_section
))
879 bfd_perror ("recording program headers");
891 dumper::write_core_dump ()
896 for (process_entity
* p
= list
; p
!= NULL
; p
= p
->next
)
898 if (p
->section
== NULL
)
901 deb_printf ("writing section type=%u base=%p size=%p flags=%08x\n",
904 get_section_size (p
->section
),
910 dump_memory_region (p
->section
, &(p
->u
.memory
));
914 dump_thread (p
->section
, &(p
->u
.thread
));
918 dump_module (p
->section
, &(p
->u
.module
));
929 static void __attribute__ ((__noreturn__
))
930 usage (FILE *stream
, int status
)
933 Usage: %s [OPTION] FILENAME WIN32PID\n\
935 Dump core from WIN32PID to FILENAME.core\n\
937 -n, --nokill don't terminate the dumped process\n\
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\
941 -V, --version output version information and exit\n\
942 \n", program_invocation_short_name
);
946 struct option longopts
[] = {
947 {"nokill", no_argument
, NULL
, 'n'},
948 {"verbose", no_argument
, NULL
, 'd'},
949 {"help", no_argument
, NULL
, 'h'},
950 {"quiet", no_argument
, NULL
, 'q'},
951 {"version", no_argument
, 0, 'V'},
952 {0, no_argument
, NULL
, 0}
954 const char *opts
= "ndhqV";
959 printf ("dumper (cygwin) %d.%d.%d\n"
960 "Core Dumper for Cygwin\n"
961 "Copyright (C) 1999 - %s Cygwin Authors\n"
962 "This is free software; see the source for copying conditions. There is NO\n"
963 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
964 CYGWIN_VERSION_DLL_MAJOR
/ 1000,
965 CYGWIN_VERSION_DLL_MAJOR
% 1000,
966 CYGWIN_VERSION_DLL_MINOR
,
967 strrchr (__DATE__
, ' ') + 1);
971 main (int argc
, char **argv
)
977 while ((opt
= getopt_long (argc
, argv
, opts
, longopts
, NULL
) ) != EOF
)
995 fprintf (stderr
, "Try `%s --help' for more information.\n",
996 program_invocation_short_name
);
1000 if (argv
&& *(argv
+ optind
) && *(argv
+ optind
+1))
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
),
1007 if ((p
= strrchr (win32_name
, '\\')))
1011 pid
= strtoul (*(argv
+ optind
+ 1), NULL
, 10);
1019 char *core_file
= (char *) malloc (strlen (p
) + sizeof (".core"));
1022 fprintf (stderr
, "error allocating memory\n");
1025 sprintf (core_file
, "%s.core", p
);
1030 printf ("dumping process #%u to %s\n", (unsigned int) pid
, core_file
);
1032 dumper
d (pid
, tid
, core_file
);
1035 d
.collect_process_information ();