diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog index 4b5b1430a..8dcd6e7ca 100644 --- a/winsup/utils/ChangeLog +++ b/winsup/utils/ChangeLog @@ -1,3 +1,11 @@ +Thu Aug 24 15:00:31 2000 Christopher Faylor + + * Makefile.in: Add dumper.exe target and associated mechanisms for building it. + * dumper.cc: New file. + * dumper.h: New file. + * module_info.cc: New file. + * parse_pe.cc: New file. + Mon Jul 31 15:12:00 2000 Corinna Vinschen * passwd.c (main): Initialize oldpwd before beeing used. diff --git a/winsup/utils/Makefile.in b/winsup/utils/Makefile.in index 80fbdd86d..092829ac1 100644 --- a/winsup/utils/Makefile.in +++ b/winsup/utils/Makefile.in @@ -35,9 +35,13 @@ include $(srcdir)/../Makefile.common MINGW_INCLUDES:=-I$(mingw_source)/include -I$(cygwin_source)/include -I$(w32api_include) +DUMPER_INCLUDES:=-I$(bupdir2)/bfd -I$(updir1)/include + MINGW_CXXFLAGS:=$(CXXFLAGS) -mno-cygwin $(MINGW_INCLUDES) MINGW_CFLAGS:=$(CFLAGS) -mno-cygwin $(MINGW_INCLUDES) +DUMPER_CFLAGS:=$(CFLAGS) $(INCLUDES) $(DUMPER_INCLUDES) + libcygwin:=$(cygwin_build)/libcygwin.a libuser32:=$(w32api_lib)/libuser32.a libkernel32:=$(w32api_lib)/libkernel32.a @@ -51,16 +55,18 @@ ALL_LDLIBS:=${patsubst $(w32api_lib)/lib%.a,-l%,\ ${filter-out $(libcygwin), $(ALL_DEP_LDLIBS)}}}} MINGW_LIB:=$(mingw_build)/libmingw32.a +DUMPER_LIB:=-L$(bupdir2)/bfd -lbfd -L$(bupdir2)/libiberty -liberty -L$(bupdir2)/intl -lintl MINGW_LDLIBS:=$(ALL_LDLIBS) $(MINGW_LIB) MINGW_DEP_LDLIBS:=${ALL_DEP_LDLIBS} ${MINGW_LIB} ALL_LDFLAGS:=-B$(newlib_build)/libc/ -B$(newlib_build)/libm/ -B$(w32api_lib)/ \ $(LDFLAGS) $(ALL_LDLIBS) MINGW_LDFLAGS:=$(ALL_LDFLAGS) $(MINGW_LIB) +DUMPER_LDFLAGS:=$(ALL_LDFLAGS) $(DUMPER_LIB) PROGS:=mount$(EXEEXT) umount$(EXEEXT) ps$(EXEEXT) kill$(EXEEXT) \ mkpasswd$(EXEEXT) mkgroup$(EXEEXT) cygpath$(EXEEXT) cygcheck$(EXEEXT) \ passwd$(EXEEXT) getfacl$(EXEEXT) setfacl$(EXEEXT) strace$(EXEEXT) \ - regtool$(EXEEXT) + regtool$(EXEEXT) dumper$(EXEEXT) .SUFFIXES: .NOEXPORT: @@ -77,6 +83,15 @@ else $(CC) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,2,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) endif +dumper.o: dumper.cc dumper.h + -$(CC) -c -o $@ $(DUMPER_CFLAGS) ${firstword $^} + +module_info.o: module_info.cc + -$(CC) -c -o $@ $(DUMPER_CFLAGS) $^ + +parse_pe.o: parse_pe.cc dumper.h + -$(CC) -c -o $@ $(DUMPER_CFLAGS) ${firstword $^} + mingw_getopt.o: $(cygwin_source)/lib/getopt.c $(CC) -c -o $@ $(MINGW_CFLAGS) $^ @@ -99,6 +114,14 @@ $(cygwin_build)/libcygwin.a: $(cygwin_build)/Makefile $(mingw_build)/libmingw32.a: $(mingw_build)/Makefile @$(MAKE) -C $(@D) $(@F) +dumper.exe: module_info.o parse_pe.o dumper.o $(ALL_DEP_LDLIBS) +ifdef VERBOSE + $(CC) -o $@ ${wordlist 1,3,$^} -B$(cygwin_build)/ $(DUMPER_LDFLAGS) +else + @echo $(CC) -o $@ ${wordlist 1,3,$^} ${filter-out -B%, $(DUMPER_LDFLAGS)};\ + $(CC) -o $@ ${wordlist 1,3,$^} -B$(cygwin_build)/ $(DUMPER_LDFLAGS) +endif + %.exe: %.o $(ALL_DEP_LDLIBS) ifdef VERBOSE $(CC) -o $@ ${firstword $^} -B$(cygwin_build)/ $(ALL_LDFLAGS) diff --git a/winsup/utils/dumper.cc b/winsup/utils/dumper.cc new file mode 100644 index 000000000..6ccf2f1c9 --- /dev/null +++ b/winsup/utils/dumper.cc @@ -0,0 +1,754 @@ +/* dumper.cc + + Copyright 1999 Cygnus Solutions. + + Written by Egor Duda + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dumper.h" + +#define NOTE_NAME_SIZE 16 + +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 #%lu\n", pid ); + 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; +} + +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 ) ) return 0; + + deb_printf ( "added thread %u\n", tid ); + return 1; +} + +int +dumper::add_mem_region ( LPBYTE base, DWORD 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 %08x-%08x\n", (DWORD)base, (DWORD)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, DWORD 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, (DWORD) 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 %08x %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; + DWORD last_size = 0; + DWORD 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 ) ) + { + 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 %08x-%08x. protect = %s\n", + (DWORD)current_page_address, + (DWORD)current_page_address + mbi.RegionSize, + buf ); + 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; + + DWORD size = memory->size; + DWORD todo; + DWORD 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 ( "Error reading process memory at %x(%x) %u\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 ); + strncpy ( (char*) & header.elf_note_header.name, "win32thread", NOTE_NAME_SIZE ); + + 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 ); + strncpy ( (char*) & header.elf_note_header.name, "win32module", NOTE_NAME_SIZE ); + + 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 () +{ + if ( ! sane () ) return 0; + + if ( ! DebugActiveProcess ( pid ) ) + { + fprintf ( stderr, "Cannot attach to process #%lu", pid ); + return 0; + } + + char event_name [ sizeof ( "cygwin_error_start_event" ) + 20 ]; + sprintf ( event_name, "cygwin_error_start_event%16lx", 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; + + 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: + + 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; + } + + 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; + DWORD sect_size; + bfd_vma sect_vma; + + asection* new_section; + + for ( process_entity* p = list; p != NULL; p = p->next ) + { + sect_no++; + + 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); + + 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; + 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; + break; + + default: + continue; + } + + if ( p->type == pr_ent_module && status_section != NULL ) + { + if ( ! bfd_set_section_size ( core_bfd, + status_section, + status_section->_raw_size + 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 ); + + char* buf = strdup ( sect_name ); + new_section = bfd_make_section ( core_bfd, buf ); + + if ( new_section == NULL || + ! bfd_set_section_flags ( core_bfd, new_section, sect_flags ) || + ! bfd_set_section_size ( core_bfd, new_section, sect_size ) ) + { + bfd_perror ( "creating section" ); + goto failed; + }; + + new_section->vma = sect_vma; + new_section->output_section = new_section; + new_section->output_offset = 0; + p->section = new_section; + } + + 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=%08x size=%08x flags=%08x\n", + p->type, + p->section->vma, + p->section->_raw_size, + 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 () +{ + fprintf ( stderr, "Usage: dumper [-v] [-c filename] pid\n" ); + fprintf ( stderr, "-c filename -- dump core to filename.core\n" ); + fprintf ( stderr, "-d -- print some debugging info while dumping\n" ); + fprintf ( stderr, "pid -- win32-pid of process to dump\n" ); +} + +int +main( int argc, char** argv ) +{ + int opt; + char* p = ""; + DWORD pid; + + while ((opt = getopt (argc, argv, "dc:")) != EOF) + switch (opt) + { + case 'd': + verbose = TRUE; + break; + case 'c': + char win32_name [ MAX_PATH ]; + cygwin_conv_to_win32_path ( optarg, win32_name ); + if ( ( p = strrchr ( win32_name, '\\' ) ) ) + p++; + else + p = win32_name; + break; + } + + 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 ); + + if ( argv && *(argv+optind) ) + pid = atoi ( *(argv+optind) ); + else + { + usage (); + return -1; + } + + DWORD tid = 0; + + if ( verbose ) + printf ( "dumping process #%lu to %s\n", pid, core_file ); + + dumper d ( pid, tid, core_file ); + if ( ! d.sane () ) + return -1; + d.collect_process_information (); + free ( core_file ); + + return 0; +}; diff --git a/winsup/utils/dumper.h b/winsup/utils/dumper.h new file mode 100644 index 000000000..673a0037e --- /dev/null +++ b/winsup/utils/dumper.h @@ -0,0 +1,131 @@ +/* dumper.h + + Copyright 1999 Cygnus Solutions. + + Written by Egor Duda + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _DUMPER_H_ +#define _DUMPER_H_ + +#include + +typedef struct +{ + LPBYTE base; + DWORD size; +} process_mem_region; + +typedef struct +{ + DWORD tid; + HANDLE hThread; + CONTEXT context; +} process_thread; + +typedef struct +{ + LPVOID base_address; + char* name; +} process_module; + +enum process_entity_type +{ + pr_ent_memory, + pr_ent_thread, + pr_ent_module +}; + +typedef struct _process_entity +{ + process_entity_type type; + union + { + process_thread thread; + process_mem_region memory; + process_module module; + } u; + asection* section; + struct _process_entity* next; +} process_entity; + +class exclusion +{ +public: + int last; + int size; + int step; + process_mem_region* region; + + exclusion ( int step ) { last = size = 0; + this->step = step; + region = NULL; } + ~exclusion () { free ( region ); } + int add ( LPBYTE mem_base, DWORD mem_size ); + int sort_and_check (); +}; + +#define PAGE_BUFFER_SIZE 4096 + +class dumper +{ + DWORD pid; + DWORD tid; /* thread id of active thread */ + HANDLE hProcess; + process_entity* list; + process_entity* last; + exclusion* excl_list; + + char* file_name; + bfd* core_bfd; + + asection* status_section; + + int memory_num; + int module_num; + int thread_num; + + void close (); + void dumper_abort (); + + process_entity* add_process_entity_to_list ( process_entity_type type ); + int add_thread ( DWORD tid, HANDLE hThread ); + int add_mem_region ( LPBYTE base, DWORD size ); + + /* break mem_region by excl_list and add add all subregions */ + int split_add_mem_region ( LPBYTE base, DWORD size ); + + int add_module ( LPVOID base_address ); + + int collect_memory_sections (); + int dump_memory_region ( asection* to, process_mem_region* memory ); + int dump_thread ( asection* to, process_thread* thread ); + int dump_module ( asection* to, process_module* module ); + +public: + int sane (); + + int collect_process_information (); + + dumper ( DWORD pid, DWORD tid, const char* name ); + ~dumper (); + + int init_core_dump (); + int prepare_core_dump (); + int write_core_dump (); +}; + +extern int deb_printf ( const char* format, ... ); + +extern char* psapi_get_module_name ( HANDLE hProcess, DWORD BaseAddress ); + +extern int parse_pe ( const char* file_name, exclusion* excl_list ); + +extern BOOL verbose; + +#endif diff --git a/winsup/utils/kill.cc b/winsup/utils/kill.cc index a7913e43c..5e2a30d50 100644 --- a/winsup/utils/kill.cc +++ b/winsup/utils/kill.cc @@ -30,7 +30,7 @@ main (int argc, char **argv) if (argc == 1) usage (); - while (*(++argv)[0] == '-') + while (*++argv && **argv == '-') if (strcmp (*argv + 1, "f") == 0) force = 1; else if (gotsig) diff --git a/winsup/utils/module_info.cc b/winsup/utils/module_info.cc new file mode 100644 index 000000000..a48d7105c --- /dev/null +++ b/winsup/utils/module_info.cc @@ -0,0 +1,112 @@ +/* module_info.cc + + Copyright 1999 Cygnus Solutions. + + Written by Egor Duda + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include +#include +#include + +static int psapi_loaded = 0; +static HMODULE psapi_module_handle = NULL; + +typedef BOOL WINAPI (tf_EnumProcessModules ) ( HANDLE, HMODULE*, DWORD, LPDWORD ); +typedef BOOL WINAPI (tf_GetModuleInformation) ( HANDLE, HMODULE, LPMODULEINFO, DWORD ); +typedef DWORD WINAPI (tf_GetModuleFileNameExA) ( HANDLE, HMODULE, LPSTR, DWORD ); + +static tf_EnumProcessModules *psapi_EnumProcessModules = NULL; +static tf_GetModuleInformation *psapi_GetModuleInformation = NULL; +static tf_GetModuleFileNameExA *psapi_GetModuleFileNameExA = NULL; + +/* + * Returns full name of Dll, which is loaded by hProcess at BaseAddress + * Uses psapi.dll + */ + +char* +psapi_get_module_name ( HANDLE hProcess, DWORD BaseAddress ) +{ + DWORD len; + MODULEINFO mi; + unsigned int i; + HMODULE dh_buf [ 1 ]; + HMODULE* DllHandle = dh_buf; + DWORD cbNeeded; + BOOL ok; + + char name_buf [ MAX_PATH + 1 ]; + + if ( !psapi_loaded || + psapi_EnumProcessModules == NULL || + psapi_GetModuleInformation == NULL || + psapi_GetModuleFileNameExA == NULL ) + { + if ( psapi_loaded ) goto failed; + psapi_loaded = 1; + psapi_module_handle = LoadLibrary ( "psapi.dll" ); + if ( ! psapi_module_handle ) + goto failed; + psapi_EnumProcessModules = (tf_EnumProcessModules *) GetProcAddress ( psapi_module_handle, "EnumProcessModules" ); + psapi_GetModuleInformation = (tf_GetModuleInformation *) GetProcAddress ( psapi_module_handle, "GetModuleInformation" ); + psapi_GetModuleFileNameExA = (tf_GetModuleFileNameExA*) GetProcAddress ( psapi_module_handle, "GetModuleFileNameExA" ); + if ( psapi_EnumProcessModules == NULL || + psapi_GetModuleInformation == NULL || + psapi_GetModuleFileNameExA == NULL ) goto failed; + } + + ok = (*psapi_EnumProcessModules) ( hProcess, + DllHandle, + sizeof ( HMODULE ), + &cbNeeded ); + + if ( !ok || !cbNeeded ) goto failed; + DllHandle = (HMODULE*) malloc ( cbNeeded ); + if ( ! DllHandle ) goto failed; + ok = (*psapi_EnumProcessModules) ( hProcess, + DllHandle, + cbNeeded, + &cbNeeded ); + if ( ! ok ) + { + free ( DllHandle ); + goto failed; + } + + for ( i = 0; i < cbNeeded / sizeof ( HMODULE ); i++ ) + { + if ( ! (*psapi_GetModuleInformation) ( hProcess, + DllHandle [ i ], + &mi, + sizeof ( mi ) ) ) + { + free ( DllHandle ); + goto failed; + } + + len = (*psapi_GetModuleFileNameExA) ( hProcess, + DllHandle [ i ], + name_buf, + MAX_PATH ); + if ( len == 0 ) + { + free ( DllHandle ); + goto failed; + } + + if ( (DWORD) (mi.lpBaseOfDll) == BaseAddress ) + { + free ( DllHandle ); + return strdup ( name_buf ); + } + } + +failed: + return NULL; +} diff --git a/winsup/utils/parse_pe.cc b/winsup/utils/parse_pe.cc new file mode 100644 index 000000000..00aeb5502 --- /dev/null +++ b/winsup/utils/parse_pe.cc @@ -0,0 +1,88 @@ +/* parse_pe.cc + + Copyright 1999 Cygnus Solutions. + + Written by Egor Duda + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include +#include +#include + +#include "dumper.h" + +int +exclusion::add ( LPBYTE mem_base, DWORD mem_size ) +{ + while ( last >= size ) size += step; + region = (process_mem_region*) realloc ( region, size * sizeof ( process_mem_region ) ); + if ( region == NULL ) return 0; + region [ last ].base = mem_base; + region [ last ].size = mem_size; + last++; + return 1; +}; + +int cmp_regions ( const void* r1, const void* r2 ) +{ + if ( ((process_mem_region*) r1)->base < ((process_mem_region*) r2)->base ) + return -1; + if ( ((process_mem_region*) r1)->base > ((process_mem_region*) r2)->base ) + return 1; + return 0; +} + +int +exclusion::sort_and_check () +{ + qsort ( region, last, sizeof ( process_mem_region ), &cmp_regions ); + for ( process_mem_region* p = region; p < region + last - 1; p++ ) + { + process_mem_region* q = p + 1; + if ( p->base + size > q->base ) + { + fprintf ( stderr, "region error @ %08x", p->base ); + return 0; + } + } + return 1; +} + +static void +select_data_section ( bfd *abfd, asection *sect, PTR obj ) +{ + exclusion* excl_list = (exclusion*) obj; + + if ( ( sect->flags & ( SEC_CODE | SEC_DEBUGGING ) ) && + sect->vma && sect->_raw_size ) + { + excl_list->add ( (LPBYTE)sect->vma, (DWORD)sect->_raw_size ); + deb_printf ( "excluding section: %20s %08lx\n", sect->name, sect->_raw_size); + } +} + +int +parse_pe ( const char* file_name, exclusion* excl_list ) +{ + if ( file_name == NULL || excl_list == NULL ) return 0; + + bfd* abfd = bfd_openr ( file_name, "pei-i386" ); + if ( abfd == NULL ) + { + bfd_perror ( "failed to open file" ); + return 0; + } + + bfd_check_format ( abfd, bfd_object ); + bfd_map_over_sections ( abfd, &select_data_section, (PTR)excl_list ); + excl_list->sort_and_check (); + + bfd_close ( abfd ); + return 1; +} + diff --git a/winsup/utils/ps.cc b/winsup/utils/ps.cc index 593629c22..362761290 100644 --- a/winsup/utils/ps.cc +++ b/winsup/utils/ps.cc @@ -261,9 +261,10 @@ main (int argc, char *argv[]) char pname[MAX_PATH]; if (p->process_state & PID_ZOMBIE) strcpy (pname, ""); - else if (p->progname[0]) + else if (query != CW_GETPINFO_FULL) { char *s; + pname[0] = '\0'; cygwin_conv_to_posix_path (p->progname, pname); s = strchr (pname, '\0') - 4; if (s > pname && strcasecmp (s, ".exe") == 0)