ba2f251d43
Update dumper for bfd API changes in binutils 2.34 libbfd doesn't guarantee API stability, so we've just been lucky this hasn't broken more often. See binutils commit fd361982.
971 lines
22 KiB
C++
971 lines
22 KiB
C++
/* dumper.cc
|
|
|
|
Written by Egor Duda <deo@logos-m.ru>
|
|
|
|
This file is part of Cygwin.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License (file COPYING.dumper) for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
|
|
|
|
#include <ansidecl.h>
|
|
#define PACKAGE
|
|
#include <bfd.h>
|
|
#include <elf/common.h>
|
|
#include <elf/external.h>
|
|
#include <sys/procfs.h>
|
|
#include <sys/cygwin.h>
|
|
#include <cygwin/version.h>
|
|
#include <getopt.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/param.h>
|
|
#include <windows.h>
|
|
|
|
#include "dumper.h"
|
|
|
|
#define NOTE_NAME_SIZE 16
|
|
|
|
#ifdef bfd_get_section_size
|
|
/* for bfd < 2.34 */
|
|
#define get_section_name(abfd, sect) bfd_get_section_name (abfd, sect)
|
|
#define get_section_size(sect) bfd_get_section_size(sect)
|
|
#define set_section_size(abfd, sect, size) bfd_set_section_size(abfd, sect, size)
|
|
#define set_section_flags(abfd, sect, flags) bfd_set_section_flags(abfd, sect, flags)
|
|
#else
|
|
/* otherwise bfd >= 2.34 */
|
|
#define get_section_name(afbd, sect) bfd_section_name (sect)
|
|
#define get_section_size(sect) bfd_section_size(sect)
|
|
#define set_section_size(abfd, sect, size) bfd_set_section_size(sect, size)
|
|
#define set_section_flags(abfd, sect, flags) bfd_set_section_flags(sect, flags)
|
|
#endif
|
|
|
|
typedef struct _note_header
|
|
{
|
|
Elf_External_Note elf_note_header;
|
|
char name[NOTE_NAME_SIZE - 1]; /* external note contains first byte of data */
|
|
}
|
|
#ifdef __GNUC__
|
|
__attribute__ ((packed))
|
|
#endif
|
|
note_header;
|
|
|
|
BOOL verbose = FALSE;
|
|
|
|
int deb_printf (const char *format,...)
|
|
{
|
|
if (!verbose)
|
|
return 0;
|
|
va_list va;
|
|
va_start (va, format);
|
|
int ret_val = vprintf (format, va);
|
|
va_end (va);
|
|
return ret_val;
|
|
}
|
|
|
|
dumper::dumper (DWORD pid, DWORD tid, const char *file_name)
|
|
{
|
|
this->file_name = strdup (file_name);
|
|
|
|
this->pid = pid;
|
|
this->tid = tid;
|
|
core_bfd = NULL;
|
|
excl_list = new exclusion (20);
|
|
|
|
list = last = NULL;
|
|
|
|
status_section = NULL;
|
|
|
|
memory_num = module_num = thread_num = 0;
|
|
|
|
hProcess = OpenProcess (PROCESS_ALL_ACCESS,
|
|
FALSE, /* no inheritance */
|
|
pid);
|
|
if (!hProcess)
|
|
{
|
|
fprintf (stderr, "Failed to open process #%u, error %ld\n",
|
|
(unsigned int) pid, (long) GetLastError ());
|
|
return;
|
|
}
|
|
|
|
init_core_dump ();
|
|
|
|
if (!sane ())
|
|
dumper_abort ();
|
|
}
|
|
|
|
dumper::~dumper ()
|
|
{
|
|
close ();
|
|
free (file_name);
|
|
}
|
|
|
|
void
|
|
dumper::dumper_abort ()
|
|
{
|
|
close ();
|
|
unlink (file_name);
|
|
}
|
|
|
|
void
|
|
dumper::close ()
|
|
{
|
|
if (core_bfd)
|
|
bfd_close (core_bfd);
|
|
if (excl_list)
|
|
delete excl_list;
|
|
if (hProcess)
|
|
CloseHandle (hProcess);
|
|
core_bfd = NULL;
|
|
hProcess = NULL;
|
|
excl_list = NULL;
|
|
}
|
|
|
|
int
|
|
dumper::sane ()
|
|
{
|
|
if (hProcess == NULL || core_bfd == NULL || excl_list == NULL)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
print_section_name (bfd* abfd, asection* sect, PTR obj)
|
|
{
|
|
deb_printf (" %s", get_section_name (abfd, sect));
|
|
}
|
|
|
|
void
|
|
dumper::print_core_section_list ()
|
|
{
|
|
deb_printf ("current sections:");
|
|
bfd_map_over_sections (core_bfd, &print_section_name, NULL);
|
|
deb_printf ("\n");
|
|
}
|
|
|
|
process_entity *
|
|
dumper::add_process_entity_to_list (process_entity_type type)
|
|
{
|
|
if (!sane ())
|
|
return NULL;
|
|
|
|
process_entity *new_entity = (process_entity *) malloc (sizeof (process_entity));
|
|
if (new_entity == NULL)
|
|
return NULL;
|
|
new_entity->next = NULL;
|
|
new_entity->section = NULL;
|
|
if (last == NULL)
|
|
list = new_entity;
|
|
else
|
|
last->next = new_entity;
|
|
last = new_entity;
|
|
return new_entity;
|
|
}
|
|
|
|
int
|
|
dumper::add_thread (DWORD tid, HANDLE hThread)
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
CONTEXT *pcontext;
|
|
|
|
process_entity *new_entity = add_process_entity_to_list (pr_ent_thread);
|
|
if (new_entity == NULL)
|
|
return 0;
|
|
new_entity->type = pr_ent_thread;
|
|
thread_num++;
|
|
|
|
new_entity->u.thread.tid = tid;
|
|
new_entity->u.thread.hThread = hThread;
|
|
|
|
pcontext = &(new_entity->u.thread.context);
|
|
pcontext->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
|
|
if (!GetThreadContext (hThread, pcontext))
|
|
{
|
|
deb_printf ("Failed to read thread context (tid=%x), error %ld\n", tid, GetLastError ());
|
|
return 0;
|
|
}
|
|
|
|
deb_printf ("added thread %u\n", tid);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
dumper::add_mem_region (LPBYTE base, SIZE_T size)
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
if (base == NULL || size == 0)
|
|
return 1; // just ignore empty regions
|
|
|
|
process_entity *new_entity = add_process_entity_to_list (pr_ent_memory);
|
|
if (new_entity == NULL)
|
|
return 0;
|
|
new_entity->type = pr_ent_memory;
|
|
memory_num++;
|
|
|
|
new_entity->u.memory.base = base;
|
|
new_entity->u.memory.size = size;
|
|
|
|
deb_printf ("added memory region %p-%p\n", base, base + size);
|
|
return 1;
|
|
}
|
|
|
|
/* split_add_mem_region scans list of regions to be excluded from dumping process
|
|
(excl_list) and removes all "excluded" parts from given region. */
|
|
int
|
|
dumper::split_add_mem_region (LPBYTE base, SIZE_T size)
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
if (base == NULL || size == 0)
|
|
return 1; // just ignore empty regions
|
|
|
|
LPBYTE last_base = base;
|
|
|
|
for (process_mem_region * p = excl_list->region;
|
|
p < excl_list->region + excl_list->last;
|
|
p++)
|
|
{
|
|
if (p->base >= base + size || p->base + p->size <= base)
|
|
continue;
|
|
|
|
if (p->base <= base)
|
|
{
|
|
last_base = p->base + p->size;
|
|
continue;
|
|
}
|
|
|
|
add_mem_region (last_base, p->base - last_base);
|
|
last_base = p->base + p->size;
|
|
}
|
|
|
|
if (last_base < base + size)
|
|
add_mem_region (last_base, base + size - last_base);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
dumper::add_module (LPVOID base_address)
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
char *module_name = psapi_get_module_name (hProcess, base_address);
|
|
if (module_name == NULL)
|
|
return 1;
|
|
|
|
process_entity *new_entity = add_process_entity_to_list (pr_ent_module);
|
|
if (new_entity == NULL)
|
|
return 0;
|
|
new_entity->type = pr_ent_module;
|
|
module_num++;
|
|
|
|
new_entity->u.module.base_address = base_address;
|
|
new_entity->u.module.name = module_name;
|
|
|
|
parse_pe (module_name, excl_list);
|
|
|
|
deb_printf ("added module %p %s\n", base_address, module_name);
|
|
return 1;
|
|
}
|
|
|
|
#define PAGE_BUFFER_SIZE 4096
|
|
|
|
int
|
|
dumper::collect_memory_sections ()
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
LPBYTE current_page_address;
|
|
LPBYTE last_base = (LPBYTE) 0xFFFFFFFF;
|
|
SIZE_T last_size = (SIZE_T) 0;
|
|
SIZE_T done;
|
|
|
|
char mem_buf[PAGE_BUFFER_SIZE];
|
|
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
|
|
if (hProcess == NULL)
|
|
return 0;
|
|
|
|
for (current_page_address = 0; current_page_address < (LPBYTE) 0xFFFF0000;)
|
|
{
|
|
if (!VirtualQueryEx (hProcess, current_page_address, &mbi, sizeof (mbi)))
|
|
break;
|
|
|
|
int skip_region_p = 0;
|
|
|
|
if (mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD) ||
|
|
mbi.State != MEM_COMMIT)
|
|
skip_region_p = 1;
|
|
|
|
if (!skip_region_p)
|
|
{
|
|
/* just to make sure that later we'll be able to read it.
|
|
According to MS docs either region is all-readable or
|
|
all-nonreadable */
|
|
if (!ReadProcessMemory (hProcess, current_page_address, mem_buf, sizeof (mem_buf), &done))
|
|
{
|
|
DWORD err = GetLastError ();
|
|
const char *pt[10];
|
|
pt[0] = (mbi.Protect & PAGE_READONLY) ? "RO " : "";
|
|
pt[1] = (mbi.Protect & PAGE_READWRITE) ? "RW " : "";
|
|
pt[2] = (mbi.Protect & PAGE_WRITECOPY) ? "WC " : "";
|
|
pt[3] = (mbi.Protect & PAGE_EXECUTE) ? "EX " : "";
|
|
pt[4] = (mbi.Protect & PAGE_EXECUTE_READ) ? "EXRO " : "";
|
|
pt[5] = (mbi.Protect & PAGE_EXECUTE_READWRITE) ? "EXRW " : "";
|
|
pt[6] = (mbi.Protect & PAGE_EXECUTE_WRITECOPY) ? "EXWC " : "";
|
|
pt[7] = (mbi.Protect & PAGE_GUARD) ? "GRD " : "";
|
|
pt[8] = (mbi.Protect & PAGE_NOACCESS) ? "NA " : "";
|
|
pt[9] = (mbi.Protect & PAGE_NOCACHE) ? "NC " : "";
|
|
char buf[10 * 6];
|
|
buf[0] = '\0';
|
|
for (int i = 0; i < 10; i++)
|
|
strcat (buf, pt[i]);
|
|
|
|
deb_printf ("warning: failed to read memory at %p-%p (protect = %s), error %ld.\n",
|
|
current_page_address,
|
|
current_page_address + mbi.RegionSize,
|
|
buf, err);
|
|
skip_region_p = 1;
|
|
}
|
|
}
|
|
|
|
if (!skip_region_p)
|
|
{
|
|
if (last_base + last_size == current_page_address)
|
|
last_size += mbi.RegionSize;
|
|
else
|
|
{
|
|
split_add_mem_region (last_base, last_size);
|
|
last_base = (LPBYTE) mbi.BaseAddress;
|
|
last_size = mbi.RegionSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
split_add_mem_region (last_base, last_size);
|
|
last_base = NULL;
|
|
last_size = 0;
|
|
}
|
|
|
|
current_page_address += mbi.RegionSize;
|
|
}
|
|
|
|
/* dump last sections, if any */
|
|
split_add_mem_region (last_base, last_size);
|
|
return 1;
|
|
};
|
|
|
|
int
|
|
dumper::dump_memory_region (asection * to, process_mem_region * memory)
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
SIZE_T size = memory->size;
|
|
SIZE_T todo;
|
|
SIZE_T done;
|
|
LPBYTE pos = memory->base;
|
|
DWORD sect_pos = 0;
|
|
|
|
if (to == NULL || memory == NULL)
|
|
return 0;
|
|
|
|
char mem_buf[PAGE_BUFFER_SIZE];
|
|
|
|
while (size > 0)
|
|
{
|
|
todo = MIN (size, PAGE_BUFFER_SIZE);
|
|
if (!ReadProcessMemory (hProcess, pos, mem_buf, todo, &done))
|
|
{
|
|
deb_printf ("Failed to read process memory at %x(%x), error %ld\n", pos, todo, GetLastError ());
|
|
return 0;
|
|
}
|
|
size -= done;
|
|
pos += done;
|
|
if (!bfd_set_section_contents (core_bfd, to, mem_buf, sect_pos, done))
|
|
{
|
|
bfd_perror ("writing memory region to bfd");
|
|
dumper_abort ();
|
|
return 0;
|
|
};
|
|
sect_pos += done;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
dumper::dump_thread (asection * to, process_thread * thread)
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
if (to == NULL || thread == NULL)
|
|
return 0;
|
|
|
|
win32_pstatus thread_pstatus;
|
|
|
|
note_header header;
|
|
bfd_putl32 (NOTE_NAME_SIZE, header.elf_note_header.namesz);
|
|
bfd_putl32 (sizeof (thread_pstatus), header.elf_note_header.descsz);
|
|
bfd_putl32 (NT_WIN32PSTATUS, header.elf_note_header.type);
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
|
#pragma GCC diagnostic ignored "-Warray-bounds"
|
|
strncpy (header.elf_note_header.name, "win32thread", NOTE_NAME_SIZE);
|
|
#pragma GCC diagnostic pop
|
|
|
|
thread_pstatus.data_type = NOTE_INFO_THREAD;
|
|
thread_pstatus.data.thread_info.tid = thread->tid;
|
|
|
|
if (tid == 0)
|
|
{
|
|
/* this is a special case. we don't know, which thread
|
|
was active when exception occured, so let's blame
|
|
the first one */
|
|
thread_pstatus.data.thread_info.is_active_thread = TRUE;
|
|
tid = (DWORD) - 1;
|
|
}
|
|
else if (tid > 0 && thread->tid == tid)
|
|
thread_pstatus.data.thread_info.is_active_thread = TRUE;
|
|
else
|
|
thread_pstatus.data.thread_info.is_active_thread = FALSE;
|
|
|
|
memcpy (&(thread_pstatus.data.thread_info.thread_context),
|
|
&(thread->context),
|
|
sizeof (thread->context));
|
|
|
|
if (!bfd_set_section_contents (core_bfd, to, &header,
|
|
0,
|
|
sizeof (header)) ||
|
|
!bfd_set_section_contents (core_bfd, to, &thread_pstatus,
|
|
sizeof (header),
|
|
sizeof (thread_pstatus)))
|
|
{
|
|
bfd_perror ("writing thread info to bfd");
|
|
dumper_abort ();
|
|
return 0;
|
|
};
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
dumper::dump_module (asection * to, process_module * module)
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
if (to == NULL || module == NULL)
|
|
return 0;
|
|
|
|
struct win32_pstatus *module_pstatus_ptr;
|
|
|
|
int note_length = sizeof (struct win32_pstatus) + strlen (module->name);
|
|
|
|
char *buf = (char *) malloc (note_length);
|
|
|
|
if (!buf)
|
|
{
|
|
fprintf (stderr, "Error alloating memory. Dumping aborted.\n");
|
|
goto out;
|
|
};
|
|
|
|
module_pstatus_ptr = (struct win32_pstatus *) buf;
|
|
|
|
note_header header;
|
|
bfd_putl32 (NOTE_NAME_SIZE, header.elf_note_header.namesz);
|
|
bfd_putl32 (note_length, header.elf_note_header.descsz);
|
|
bfd_putl32 (NT_WIN32PSTATUS, header.elf_note_header.type);
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
|
#pragma GCC diagnostic ignored "-Warray-bounds"
|
|
strncpy (header.elf_note_header.name, "win32module", NOTE_NAME_SIZE);
|
|
#pragma GCC diagnostic pop
|
|
|
|
module_pstatus_ptr->data_type = NOTE_INFO_MODULE;
|
|
module_pstatus_ptr->data.module_info.base_address = module->base_address;
|
|
module_pstatus_ptr->data.module_info.module_name_size = strlen (module->name) + 1;
|
|
strcpy (module_pstatus_ptr->data.module_info.module_name, module->name);
|
|
|
|
if (!bfd_set_section_contents (core_bfd, to, &header,
|
|
0,
|
|
sizeof (header)) ||
|
|
!bfd_set_section_contents (core_bfd, to, module_pstatus_ptr,
|
|
sizeof (header),
|
|
note_length))
|
|
{
|
|
bfd_perror ("writing module info to bfd");
|
|
goto out;
|
|
};
|
|
return 1;
|
|
|
|
out:
|
|
if (buf)
|
|
free (buf);
|
|
dumper_abort ();
|
|
return 0;
|
|
|
|
}
|
|
|
|
int
|
|
dumper::collect_process_information ()
|
|
{
|
|
int exception_level = 0;
|
|
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
if (!DebugActiveProcess (pid))
|
|
{
|
|
fprintf (stderr, "Cannot attach to process #%u, error %ld",
|
|
(unsigned int) pid, (long) GetLastError ());
|
|
return 0;
|
|
}
|
|
|
|
char event_name[sizeof ("cygwin_error_start_event") + 20];
|
|
sprintf (event_name, "cygwin_error_start_event%16x", (unsigned int) pid);
|
|
HANDLE sync_with_debugee = OpenEvent (EVENT_MODIFY_STATE, FALSE, event_name);
|
|
|
|
DEBUG_EVENT current_event;
|
|
|
|
while (1)
|
|
{
|
|
if (!WaitForDebugEvent (¤t_event, 20000))
|
|
return 0;
|
|
|
|
deb_printf ("got debug event %d\n", current_event.dwDebugEventCode);
|
|
|
|
switch (current_event.dwDebugEventCode)
|
|
{
|
|
case CREATE_THREAD_DEBUG_EVENT:
|
|
|
|
if (!add_thread (current_event.dwThreadId,
|
|
current_event.u.CreateThread.hThread))
|
|
goto failed;
|
|
|
|
break;
|
|
|
|
case CREATE_PROCESS_DEBUG_EVENT:
|
|
|
|
if (!add_module (current_event.u.CreateProcessInfo.lpBaseOfImage) ||
|
|
!add_thread (current_event.dwThreadId,
|
|
current_event.u.CreateProcessInfo.hThread))
|
|
goto failed;
|
|
|
|
break;
|
|
|
|
case EXIT_PROCESS_DEBUG_EVENT:
|
|
|
|
deb_printf ("debugee quits");
|
|
ContinueDebugEvent (current_event.dwProcessId,
|
|
current_event.dwThreadId,
|
|
DBG_CONTINUE);
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case LOAD_DLL_DEBUG_EVENT:
|
|
|
|
if (!add_module (current_event.u.LoadDll.lpBaseOfDll))
|
|
goto failed;
|
|
|
|
break;
|
|
|
|
case EXCEPTION_DEBUG_EVENT:
|
|
|
|
exception_level++;
|
|
if (exception_level == 2)
|
|
break;
|
|
else if (exception_level > 2)
|
|
return 0;
|
|
|
|
collect_memory_sections ();
|
|
|
|
/* got all info. time to dump */
|
|
|
|
if (!prepare_core_dump ())
|
|
{
|
|
fprintf (stderr, "Failed to prepare core dump\n");
|
|
goto failed;
|
|
};
|
|
|
|
if (!write_core_dump ())
|
|
{
|
|
fprintf (stderr, "Failed to write core dump\n");
|
|
goto failed;
|
|
};
|
|
|
|
/* signal a debugee that we've finished */
|
|
if (sync_with_debugee)
|
|
SetEvent (sync_with_debugee);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ContinueDebugEvent (current_event.dwProcessId,
|
|
current_event.dwThreadId,
|
|
DBG_CONTINUE);
|
|
}
|
|
failed:
|
|
/* set debugee free */
|
|
if (sync_with_debugee)
|
|
SetEvent (sync_with_debugee);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
dumper::init_core_dump ()
|
|
{
|
|
bfd_init ();
|
|
|
|
core_bfd = bfd_openw (file_name, "elf32-i386");
|
|
if (core_bfd == NULL)
|
|
{
|
|
bfd_perror ("opening bfd");
|
|
goto failed;
|
|
}
|
|
|
|
if (!bfd_set_format (core_bfd, bfd_core))
|
|
{
|
|
bfd_perror ("setting bfd format");
|
|
goto failed;
|
|
}
|
|
|
|
if (!bfd_set_arch_mach (core_bfd, bfd_arch_i386, 0))
|
|
{
|
|
bfd_perror ("setting bfd architecture");
|
|
goto failed;
|
|
}
|
|
|
|
return 1;
|
|
|
|
failed:
|
|
dumper_abort ();
|
|
return 0;
|
|
|
|
}
|
|
|
|
int
|
|
dumper::prepare_core_dump ()
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
int sect_no = 0;
|
|
char sect_name[50];
|
|
|
|
flagword sect_flags;
|
|
SIZE_T sect_size;
|
|
bfd_vma sect_vma;
|
|
|
|
asection *new_section;
|
|
|
|
for (process_entity * p = list; p != NULL; p = p->next)
|
|
{
|
|
sect_no++;
|
|
|
|
unsigned long phdr_type = PT_LOAD;
|
|
|
|
switch (p->type)
|
|
{
|
|
case pr_ent_memory:
|
|
sprintf (sect_name, ".mem/%u", sect_no);
|
|
sect_flags = SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD;
|
|
sect_size = p->u.memory.size;
|
|
sect_vma = (bfd_vma) (p->u.memory.base);
|
|
phdr_type = PT_LOAD;
|
|
break;
|
|
|
|
case pr_ent_thread:
|
|
sprintf (sect_name, ".note/%u", sect_no);
|
|
sect_flags = SEC_HAS_CONTENTS | SEC_LOAD;
|
|
sect_size = sizeof (note_header) + sizeof (struct win32_pstatus);
|
|
sect_vma = 0;
|
|
phdr_type = PT_NOTE;
|
|
break;
|
|
|
|
case pr_ent_module:
|
|
sprintf (sect_name, ".note/%u", sect_no);
|
|
sect_flags = SEC_HAS_CONTENTS | SEC_LOAD;
|
|
sect_size = sizeof (note_header) + sizeof (struct win32_pstatus) +
|
|
(bfd_size_type) (strlen (p->u.module.name));
|
|
sect_vma = 0;
|
|
phdr_type = PT_NOTE;
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
if (p->type == pr_ent_module && status_section != NULL)
|
|
{
|
|
if (!set_section_size (core_bfd,
|
|
status_section,
|
|
(get_section_size (status_section)
|
|
+ sect_size)))
|
|
{
|
|
bfd_perror ("resizing status section");
|
|
goto failed;
|
|
};
|
|
continue;
|
|
}
|
|
|
|
deb_printf ("creating section (type%u) %s(%u), flags=%08x\n",
|
|
p->type, sect_name, sect_size, sect_flags);
|
|
|
|
bfd_set_error (bfd_error_no_error);
|
|
char *buf = strdup (sect_name);
|
|
new_section = bfd_make_section (core_bfd, buf);
|
|
if (new_section == NULL)
|
|
{
|
|
if (bfd_get_error () == bfd_error_no_error)
|
|
fprintf (stderr, "error creating new section (%s), section already exists.\n", buf);
|
|
else
|
|
bfd_perror ("creating section");
|
|
goto failed;
|
|
}
|
|
|
|
if (!set_section_flags (core_bfd, new_section, sect_flags) ||
|
|
!set_section_size (core_bfd, new_section, sect_size))
|
|
{
|
|
bfd_perror ("setting section attributes");
|
|
goto failed;
|
|
};
|
|
|
|
new_section->vma = sect_vma;
|
|
new_section->lma = 0;
|
|
new_section->output_section = new_section;
|
|
new_section->output_offset = 0;
|
|
p->section = new_section;
|
|
int section_count = 1;
|
|
|
|
bfd_boolean filehdr = 0;
|
|
bfd_boolean phdrs = 0;
|
|
|
|
bfd_vma at = 0;
|
|
bfd_boolean valid_at = 0;
|
|
|
|
flagword flags = 0;
|
|
bfd_boolean valid_flags = 1;
|
|
|
|
if (p->type == pr_ent_memory)
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
if (!VirtualQueryEx (hProcess, (LPVOID)sect_vma, &mbi, sizeof (mbi)))
|
|
{
|
|
bfd_perror ("getting mem region flags");
|
|
goto failed;
|
|
}
|
|
|
|
static const struct
|
|
{
|
|
DWORD protect;
|
|
flagword flags;
|
|
} mappings[] =
|
|
{
|
|
{ PAGE_READONLY, PF_R },
|
|
{ PAGE_READWRITE, PF_R | PF_W },
|
|
{ PAGE_WRITECOPY, PF_W },
|
|
{ PAGE_EXECUTE, PF_X },
|
|
{ PAGE_EXECUTE_READ, PF_X | PF_R },
|
|
{ PAGE_EXECUTE_READWRITE, PF_X | PF_R | PF_W },
|
|
{ PAGE_EXECUTE_WRITECOPY, PF_X | PF_W }
|
|
};
|
|
|
|
for (size_t i = 0;
|
|
i < sizeof (mappings) / sizeof (mappings[0]);
|
|
i++)
|
|
if ((mbi.Protect & mappings[i].protect) != 0)
|
|
flags |= mappings[i].flags;
|
|
}
|
|
|
|
if (!bfd_record_phdr (core_bfd, phdr_type,
|
|
valid_flags, flags,
|
|
valid_at, at,
|
|
filehdr, phdrs,
|
|
section_count, &new_section))
|
|
{
|
|
bfd_perror ("recording program headers");
|
|
goto failed;
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
failed:
|
|
dumper_abort ();
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
dumper::write_core_dump ()
|
|
{
|
|
if (!sane ())
|
|
return 0;
|
|
|
|
for (process_entity * p = list; p != NULL; p = p->next)
|
|
{
|
|
if (p->section == NULL)
|
|
continue;
|
|
|
|
deb_printf ("writing section type=%u base=%p size=%p flags=%08x\n",
|
|
p->type,
|
|
p->section->vma,
|
|
get_section_size (p->section),
|
|
p->section->flags);
|
|
|
|
switch (p->type)
|
|
{
|
|
case pr_ent_memory:
|
|
dump_memory_region (p->section, &(p->u.memory));
|
|
break;
|
|
|
|
case pr_ent_thread:
|
|
dump_thread (p->section, &(p->u.thread));
|
|
break;
|
|
|
|
case pr_ent_module:
|
|
dump_module (p->section, &(p->u.module));
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
usage (FILE *stream, int status)
|
|
{
|
|
fprintf (stream, "\
|
|
Usage: %s [OPTION] FILENAME WIN32PID\n\
|
|
\n\
|
|
Dump core from WIN32PID to FILENAME.core\n\
|
|
\n\
|
|
-d, --verbose be verbose while dumping\n\
|
|
-h, --help output help information and exit\n\
|
|
-q, --quiet be quiet while dumping (default)\n\
|
|
-V, --version output version information and exit\n\
|
|
\n", program_invocation_short_name);
|
|
exit (status);
|
|
}
|
|
|
|
struct option longopts[] = {
|
|
{"verbose", no_argument, NULL, 'd'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{"quiet", no_argument, NULL, 'q'},
|
|
{"version", no_argument, 0, 'V'},
|
|
{0, no_argument, NULL, 0}
|
|
};
|
|
const char *opts = "dhqV";
|
|
|
|
static void
|
|
print_version ()
|
|
{
|
|
printf ("dumper (cygwin) %d.%d.%d\n"
|
|
"Core Dumper for Cygwin\n"
|
|
"Copyright (C) 1999 - %s Cygwin Authors\n"
|
|
"This is free software; see the source for copying conditions. There is NO\n"
|
|
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
|
|
CYGWIN_VERSION_DLL_MAJOR / 1000,
|
|
CYGWIN_VERSION_DLL_MAJOR % 1000,
|
|
CYGWIN_VERSION_DLL_MINOR,
|
|
strrchr (__DATE__, ' ') + 1);
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *p = "";
|
|
DWORD pid;
|
|
|
|
while ((opt = getopt_long (argc, argv, opts, longopts, NULL) ) != EOF)
|
|
switch (opt)
|
|
{
|
|
case 'd':
|
|
verbose = TRUE;
|
|
break;
|
|
case 'q':
|
|
verbose = FALSE;
|
|
break;
|
|
case 'h':
|
|
usage (stdout, 0);
|
|
case 'V':
|
|
print_version ();
|
|
exit (0);
|
|
default:
|
|
fprintf (stderr, "Try `%s --help' for more information.\n",
|
|
program_invocation_short_name);
|
|
exit (1);
|
|
}
|
|
|
|
if (argv && *(argv + optind) && *(argv + optind +1))
|
|
{
|
|
ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE,
|
|
*(argv + optind), NULL, 0);
|
|
char *win32_name = (char *) alloca (len);
|
|
cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, *(argv + optind),
|
|
win32_name, len);
|
|
if ((p = strrchr (win32_name, '\\')))
|
|
p++;
|
|
else
|
|
p = win32_name;
|
|
pid = strtoul (*(argv + optind + 1), NULL, 10);
|
|
}
|
|
else
|
|
{
|
|
usage (stderr, 1);
|
|
return -1;
|
|
}
|
|
|
|
char *core_file = (char *) malloc (strlen (p) + sizeof (".core"));
|
|
if (!core_file)
|
|
{
|
|
fprintf (stderr, "error allocating memory\n");
|
|
return -1;
|
|
}
|
|
sprintf (core_file, "%s.core", p);
|
|
|
|
DWORD tid = 0;
|
|
|
|
if (verbose)
|
|
printf ("dumping process #%u to %s\n", (unsigned int) pid, core_file);
|
|
|
|
dumper d (pid, tid, core_file);
|
|
if (!d.sane ())
|
|
return -1;
|
|
d.collect_process_information ();
|
|
free (core_file);
|
|
|
|
return 0;
|
|
};
|