/* Low level interface to Windows debugging, for gdbserver. Copyright (C) 2006, 2007 Free Software Foundation, Inc. Contributed by Leo Zayas. Based on "win32-nat.c" from GDB. This file is part of GDB. 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 2 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 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 "server.h" #include "regcache.h" #include "gdb/signals.h" #include "mem-break.h" #include "win32-low.h" #include #include #include #include #include #include #include #include #ifndef USE_WIN32API #include #endif #define LOG 0 #define OUTMSG(X) do { printf X; fflush (stdout); } while (0) #if LOG #define OUTMSG2(X) do { printf X; fflush (stdout); } while (0) #else #define OUTMSG2(X) do ; while (0) #endif #ifndef _T #define _T(x) TEXT (x) #endif #ifndef COUNTOF #define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0])) #endif #ifdef _WIN32_WCE # define GETPROCADDRESS(DLL, PROC) \ ((winapi_ ## PROC) GetProcAddress (DLL, TEXT (#PROC))) #else # define GETPROCADDRESS(DLL, PROC) \ ((winapi_ ## PROC) GetProcAddress (DLL, #PROC)) #endif int using_threads = 1; /* Globals. */ static HANDLE current_process_handle = NULL; static DWORD current_process_id = 0; static enum target_signal last_sig = TARGET_SIGNAL_0; /* The current debug event from WaitForDebugEvent. */ static DEBUG_EVENT current_event; #define NUM_REGS (the_low_target.num_regs) typedef BOOL WINAPI (*winapi_DebugActiveProcessStop) (DWORD dwProcessId); typedef BOOL WINAPI (*winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit); typedef BOOL WINAPI (*winapi_DebugBreakProcess) (HANDLE); typedef BOOL WINAPI (*winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD); static DWORD main_thread_id = 0; static void win32_resume (struct thread_resume *resume_info); /* Get the thread ID from the current selected inferior (the current thread). */ static DWORD current_inferior_tid (void) { win32_thread_info *th = inferior_target_data (current_inferior); return th->tid; } /* Find a thread record given a thread id. If GET_CONTEXT is set then also retrieve the context for this thread. */ static win32_thread_info * thread_rec (DWORD id, int get_context) { struct thread_info *thread; win32_thread_info *th; thread = (struct thread_info *) find_inferior_id (&all_threads, id); if (thread == NULL) return NULL; th = inferior_target_data (thread); if (!th->suspend_count && get_context) { if (id != current_event.dwThreadId) th->suspend_count = SuspendThread (th->h) + 1; (*the_low_target.get_thread_context) (th, ¤t_event); } return th; } /* Add a thread to the thread list. */ static win32_thread_info * child_add_thread (DWORD tid, HANDLE h) { win32_thread_info *th; if ((th = thread_rec (tid, FALSE))) return th; th = (win32_thread_info *) malloc (sizeof (*th)); memset (th, 0, sizeof (*th)); th->tid = tid; th->h = h; add_thread (tid, th, (unsigned int) tid); set_inferior_regcache_data ((struct thread_info *) find_inferior_id (&all_threads, tid), new_register_cache ()); if (the_low_target.thread_added != NULL) (*the_low_target.thread_added) (th); return th; } /* Delete a thread from the list of threads. */ static void delete_thread_info (struct inferior_list_entry *thread) { win32_thread_info *th = inferior_target_data ((struct thread_info *) thread); remove_thread ((struct thread_info *) thread); CloseHandle (th->h); free (th); } /* Delete a thread from the list of threads. */ static void child_delete_thread (DWORD id) { struct inferior_list_entry *thread; /* If the last thread is exiting, just return. */ if (all_threads.head == all_threads.tail) return; thread = find_inferior_id (&all_threads, id); if (thread == NULL) return; delete_thread_info (thread); } /* Transfer memory from/to the debugged process. */ static int child_xfer_memory (CORE_ADDR memaddr, char *our, int len, int write, struct target_ops *target) { SIZE_T done; long addr = (long) memaddr; if (write) { WriteProcessMemory (current_process_handle, (LPVOID) addr, (LPCVOID) our, len, &done); FlushInstructionCache (current_process_handle, (LPCVOID) addr, len); } else { ReadProcessMemory (current_process_handle, (LPCVOID) addr, (LPVOID) our, len, &done); } return done; } /* Generally, what has the program done? */ enum target_waitkind { /* The program has exited. The exit status is in value.integer. */ TARGET_WAITKIND_EXITED, /* The program has stopped with a signal. Which signal is in value.sig. */ TARGET_WAITKIND_STOPPED, /* The program is letting us know that it dynamically loaded or unloaded something. */ TARGET_WAITKIND_LOADED, /* The program has exec'ed a new executable file. The new file's pathname is pointed to by value.execd_pathname. */ TARGET_WAITKIND_EXECD, /* Nothing interesting happened, but we stopped anyway. We take the chance to check if GDB requested an interrupt. */ TARGET_WAITKIND_SPURIOUS, }; struct target_waitstatus { enum target_waitkind kind; /* Forked child pid, execd pathname, exit status or signal number. */ union { int integer; enum target_signal sig; int related_pid; char *execd_pathname; int syscall_id; } value; }; /* Clear out any old thread list and reinitialize it to a pristine state. */ static void child_init_thread_list (void) { for_each_inferior (&all_threads, delete_thread_info); } static void do_initial_child_stuff (DWORD pid) { last_sig = TARGET_SIGNAL_0; memset (¤t_event, 0, sizeof (current_event)); child_init_thread_list (); if (the_low_target.initial_stuff != NULL) (*the_low_target.initial_stuff) (); } /* Resume all artificially suspended threads if we are continuing execution. */ static int continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr) { struct thread_info *thread = (struct thread_info *) this_thread; int thread_id = * (int *) id_ptr; win32_thread_info *th = inferior_target_data (thread); int i; if ((thread_id == -1 || thread_id == th->tid) && th->suspend_count) { if (th->context.ContextFlags) { (*the_low_target.set_thread_context) (th, ¤t_event); th->context.ContextFlags = 0; } for (i = 0; i < th->suspend_count; i++) (void) ResumeThread (th->h); th->suspend_count = 0; } return 0; } static BOOL child_continue (DWORD continue_status, int thread_id) { BOOL res; res = ContinueDebugEvent (current_event.dwProcessId, current_event.dwThreadId, continue_status); if (res) find_inferior (&all_threads, continue_one_thread, &thread_id); return res; } /* Fetch register(s) from the current thread context. */ static void child_fetch_inferior_registers (int r) { int regno; win32_thread_info *th = thread_rec (current_inferior_tid (), TRUE); if (r == -1 || r == 0 || r > NUM_REGS) child_fetch_inferior_registers (NUM_REGS); else for (regno = 0; regno < r; regno++) (*the_low_target.fetch_inferior_register) (th, regno); } /* Store a new register value into the current thread context. We don't change the program's context until later, when we resume it. */ static void child_store_inferior_registers (int r) { int regno; win32_thread_info *th = thread_rec (current_inferior_tid (), TRUE); if (r == -1 || r == 0 || r > NUM_REGS) child_store_inferior_registers (NUM_REGS); else for (regno = 0; regno < r; regno++) (*the_low_target.store_inferior_register) (th, regno); } /* Map the Windows error number in ERROR to a locale-dependent error message string and return a pointer to it. Typically, the values for ERROR come from GetLastError. The string pointed to shall not be modified by the application, but may be overwritten by a subsequent call to strwinerror The strwinerror function does not change the current setting of GetLastError. */ char * strwinerror (DWORD error) { static char buf[1024]; TCHAR *msgbuf; DWORD lasterr = GetLastError (); DWORD chars = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error, 0, /* Default language */ (LPVOID)&msgbuf, 0, NULL); if (chars != 0) { /* If there is an \r\n appended, zap it. */ if (chars >= 2 && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { chars -= 2; msgbuf[chars] = 0; } if (chars > ((COUNTOF (buf)) - 1)) { chars = COUNTOF (buf) - 1; msgbuf [chars] = 0; } #ifdef UNICODE wcstombs (buf, msgbuf, chars + 1); #else strncpy (buf, msgbuf, chars + 1); #endif LocalFree (msgbuf); } else sprintf (buf, "unknown win32 error (%ld)", error); SetLastError (lasterr); return buf; } /* Start a new process. PROGRAM is a path to the program to execute. ARGS is a standard NULL-terminated array of arguments, to be passed to the inferior as ``argv''. Returns the new PID on success, -1 on failure. Registers the new process with the process list. */ static int win32_create_inferior (char *program, char **program_args) { #ifndef USE_WIN32API char real_path[MAXPATHLEN]; char *orig_path, *new_path, *path_ptr; #endif BOOL ret; DWORD flags; char *args; int argslen; int argc; PROCESS_INFORMATION pi; #ifndef __MINGW32CE__ STARTUPINFOA si = { sizeof (STARTUPINFOA) }; char *winenv = NULL; #else wchar_t *wargs, *wprogram; #endif if (!program) error ("No executable specified, specify executable to debug.\n"); flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; #ifndef USE_WIN32API orig_path = NULL; path_ptr = getenv ("PATH"); if (path_ptr) { orig_path = alloca (strlen (path_ptr) + 1); new_path = alloca (cygwin_posix_to_win32_path_list_buf_size (path_ptr)); strcpy (orig_path, path_ptr); cygwin_posix_to_win32_path_list (path_ptr, new_path); setenv ("PATH", new_path, 1); } cygwin_conv_to_win32_path (program, real_path); program = real_path; #endif argslen = 1; for (argc = 1; program_args[argc]; argc++) argslen += strlen (program_args[argc]) + 1; args = alloca (argslen); args[0] = '\0'; for (argc = 1; program_args[argc]; argc++) { /* FIXME: Can we do better about quoting? How does Cygwin handle this? */ strcat (args, " "); strcat (args, program_args[argc]); } OUTMSG2 (("Command line is \"%s\"\n", args)); #ifdef CREATE_NEW_PROCESS_GROUP flags |= CREATE_NEW_PROCESS_GROUP; #endif #ifdef __MINGW32CE__ to_back_slashes (program); wargs = alloca (argslen * sizeof (wchar_t)); mbstowcs (wargs, args, argslen); wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t)); mbstowcs (wprogram, program, strlen (program) + 1); ret = CreateProcessW (wprogram, /* image name */ wargs, /* command line */ NULL, /* security, not supported */ NULL, /* thread, not supported */ FALSE, /* inherit handles, not supported */ flags, /* start flags */ NULL, /* environment, not supported */ NULL, /* current directory, not supported */ NULL, /* start info, not supported */ &pi); /* proc info */ #else ret = CreateProcessA (program, /* image name */ args, /* command line */ NULL, /* security */ NULL, /* thread */ TRUE, /* inherit handles */ flags, /* start flags */ winenv, /* environment */ NULL, /* current directory */ &si, /* start info */ &pi); /* proc info */ #endif #ifndef USE_WIN32API if (orig_path) setenv ("PATH", orig_path, 1); #endif if (!ret) { DWORD err = GetLastError (); error ("Error creating process \"%s%s\", (error %d): %s\n", program, args, (int) err, strwinerror (err)); } else { OUTMSG2 (("Process created: %s\n", (char *) args)); } #ifndef _WIN32_WCE /* On Windows CE this handle can't be closed. The OS reuses it in the debug events, while the 9x/NT versions of Windows probably use a DuplicateHandle'd one. */ CloseHandle (pi.hThread); #endif current_process_handle = pi.hProcess; current_process_id = pi.dwProcessId; do_initial_child_stuff (current_process_id); return current_process_id; } /* Attach to a running process. PID is the process ID to attach to, specified by the user or a higher layer. */ static int win32_attach (unsigned long pid) { winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL; winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL; #ifdef _WIN32_WCE HMODULE dll = GetModuleHandle (_T("COREDLL.DLL")); #else HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL")); #endif DebugActiveProcessStop = GETPROCADDRESS (dll, DebugActiveProcessStop); DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit); if (DebugActiveProcess (pid)) { if (DebugSetProcessKillOnExit != NULL) DebugSetProcessKillOnExit (FALSE); current_process_handle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); if (current_process_handle != NULL) { current_process_id = pid; do_initial_child_stuff (pid); return 0; } if (DebugActiveProcessStop != NULL) DebugActiveProcessStop (current_process_id); } error ("Attach to process failed."); } /* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */ static void handle_output_debug_string (struct target_waitstatus *ourstatus) { #define READ_BUFFER_LEN 1024 CORE_ADDR addr; char s[READ_BUFFER_LEN + 1] = { 0 }; DWORD nbytes = current_event.u.DebugString.nDebugStringLength; if (nbytes == 0) return; if (nbytes > READ_BUFFER_LEN) nbytes = READ_BUFFER_LEN; addr = (CORE_ADDR) (size_t) current_event.u.DebugString.lpDebugStringData; if (current_event.u.DebugString.fUnicode) { /* The event tells us how many bytes, not chars, even in Unicode. */ WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 }; if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0) return; wcstombs (s, buffer, (nbytes + 1) / sizeof (WCHAR)); } else { if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0) return; } if (strncmp (s, "cYg", 3) != 0) { if (!server_waiting) { OUTMSG2(("%s", s)); return; } monitor_output (s); } #undef READ_BUFFER_LEN } /* Kill all inferiors. */ static void win32_kill (void) { win32_thread_info *current_thread; if (current_process_handle == NULL) return; TerminateProcess (current_process_handle, 0); for (;;) { if (!child_continue (DBG_CONTINUE, -1)) break; if (!WaitForDebugEvent (¤t_event, INFINITE)) break; if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { struct target_waitstatus our_status = { 0 }; handle_output_debug_string (&our_status); } } CloseHandle (current_process_handle); current_thread = inferior_target_data (current_inferior); if (current_thread && current_thread->h) { /* This may fail in an attached process, so don't check. */ (void) CloseHandle (current_thread->h); } } /* Detach from all inferiors. */ static int win32_detach (void) { HANDLE h; winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL; winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL; #ifdef _WIN32_WCE HMODULE dll = GetModuleHandle (_T("COREDLL.DLL")); #else HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL")); #endif DebugActiveProcessStop = GETPROCADDRESS (dll, DebugActiveProcessStop); DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit); if (DebugSetProcessKillOnExit == NULL || DebugActiveProcessStop == NULL) return -1; /* We need a new handle, since DebugActiveProcessStop closes all the ones that came through the events. */ if ((h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, current_process_id)) == NULL) { /* The process died. */ return -1; } { struct thread_resume resume; resume.thread = -1; resume.step = 0; resume.sig = 0; resume.leave_stopped = 0; win32_resume (&resume); } if (!DebugActiveProcessStop (current_process_id)) { CloseHandle (h); return -1; } DebugSetProcessKillOnExit (FALSE); current_process_handle = h; return 0; } /* Wait for inferiors to end. */ static void win32_join (void) { if (current_process_id == 0 || current_process_handle == NULL) return; WaitForSingleObject (current_process_handle, INFINITE); CloseHandle (current_process_handle); current_process_handle = NULL; current_process_id = 0; } /* Return 1 iff the thread with thread ID TID is alive. */ static int win32_thread_alive (unsigned long tid) { int res; /* Our thread list is reliable; don't bother to poll target threads. */ if (find_inferior_id (&all_threads, tid) != NULL) res = 1; else res = 0; return res; } /* Resume the inferior process. RESUME_INFO describes how we want to resume. */ static void win32_resume (struct thread_resume *resume_info) { DWORD tid; enum target_signal sig; int step; win32_thread_info *th; DWORD continue_status = DBG_CONTINUE; /* This handles the very limited set of resume packets that GDB can currently produce. */ if (resume_info[0].thread == -1) tid = -1; else if (resume_info[1].thread == -1 && !resume_info[1].leave_stopped) tid = -1; else /* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make the Windows resume code do the right thing for thread switching. */ tid = current_event.dwThreadId; if (resume_info[0].thread != -1) { sig = resume_info[0].sig; step = resume_info[0].step; } else { sig = 0; step = 0; } if (sig != TARGET_SIGNAL_0) { if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) { OUTMSG (("Cannot continue with signal %d here.\n", sig)); } else if (sig == last_sig) continue_status = DBG_EXCEPTION_NOT_HANDLED; else OUTMSG (("Can only continue with recieved signal %d.\n", last_sig)); } last_sig = TARGET_SIGNAL_0; /* Get context for the currently selected thread. */ th = thread_rec (current_event.dwThreadId, FALSE); if (th) { if (th->context.ContextFlags) { /* Move register values from the inferior into the thread context structure. */ regcache_invalidate (); if (step) { if (the_low_target.single_step != NULL) (*the_low_target.single_step) (th); else error ("Single stepping is not supported " "in this configuration.\n"); } (*the_low_target.set_thread_context) (th, ¤t_event); th->context.ContextFlags = 0; } } /* Allow continuing with the same signal that interrupted us. Otherwise complain. */ child_continue (continue_status, tid); } static void win32_add_one_solib (const char *name, CORE_ADDR load_addr) { char buf[MAX_PATH + 1]; char buf2[MAX_PATH + 1]; #ifdef _WIN32_WCE WIN32_FIND_DATA w32_fd; WCHAR wname[MAX_PATH + 1]; mbstowcs (wname, name, MAX_PATH); HANDLE h = FindFirstFile (wname, &w32_fd); #else WIN32_FIND_DATAA w32_fd; HANDLE h = FindFirstFileA (name, &w32_fd); #endif if (h == INVALID_HANDLE_VALUE) strcpy (buf, name); else { FindClose (h); strcpy (buf, name); #ifndef _WIN32_WCE { char cwd[MAX_PATH + 1]; char *p; if (GetCurrentDirectoryA (MAX_PATH + 1, cwd)) { p = strrchr (buf, '\\'); if (p) p[1] = '\0'; SetCurrentDirectoryA (buf); GetFullPathNameA (w32_fd.cFileName, MAX_PATH, buf, &p); SetCurrentDirectoryA (cwd); } } #endif } #ifdef __CYGWIN__ cygwin_conv_to_posix_path (buf, buf2); #else strcpy (buf2, buf); #endif loaded_dll (buf2, load_addr); } static char * get_image_name (HANDLE h, void *address, int unicode) { static char buf[(2 * MAX_PATH) + 1]; DWORD size = unicode ? sizeof (WCHAR) : sizeof (char); char *address_ptr; int len = 0; char b[2]; DWORD done; /* Attempt to read the name of the dll that was detected. This is documented to work only when actively debugging a program. It will not work for attached processes. */ if (address == NULL) return NULL; #ifdef _WIN32_WCE /* Windows CE reports the address of the image name, instead of an address of a pointer into the image name. */ address_ptr = address; #else /* See if we could read the address of a string, and that the address isn't null. */ if (!ReadProcessMemory (h, address, &address_ptr, sizeof (address_ptr), &done) || done != sizeof (address_ptr) || !address_ptr) return NULL; #endif /* Find the length of the string */ while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done) && (b[0] != 0 || b[size - 1] != 0) && done == size) continue; if (!unicode) ReadProcessMemory (h, address_ptr, buf, len, &done); else { WCHAR *unicode_address = (WCHAR *) alloca (len * sizeof (WCHAR)); ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR), &done); WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0); } return buf; } typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *, DWORD, LPDWORD); typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE, LPMODULEINFO, DWORD); typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE, LPSTR, DWORD); static winapi_EnumProcessModules win32_EnumProcessModules; static winapi_GetModuleInformation win32_GetModuleInformation; static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA; static BOOL load_psapi (void) { static int psapi_loaded = 0; static HMODULE dll = NULL; if (!psapi_loaded) { psapi_loaded = 1; dll = LoadLibrary (TEXT("psapi.dll")); if (!dll) return FALSE; win32_EnumProcessModules = GETPROCADDRESS (dll, EnumProcessModules); win32_GetModuleInformation = GETPROCADDRESS (dll, GetModuleInformation); win32_GetModuleFileNameExA = GETPROCADDRESS (dll, GetModuleFileNameExA); } return (win32_EnumProcessModules != NULL && win32_GetModuleInformation != NULL && win32_GetModuleFileNameExA != NULL); } static int psapi_get_dll_name (DWORD BaseAddress, char *dll_name_ret) { DWORD len; MODULEINFO mi; size_t i; HMODULE dh_buf[1]; HMODULE *DllHandle = dh_buf; DWORD cbNeeded; BOOL ok; if (!load_psapi ()) goto failed; cbNeeded = 0; ok = (*win32_EnumProcessModules) (current_process_handle, DllHandle, sizeof (HMODULE), &cbNeeded); if (!ok || !cbNeeded) goto failed; DllHandle = (HMODULE *) alloca (cbNeeded); if (!DllHandle) goto failed; ok = (*win32_EnumProcessModules) (current_process_handle, DllHandle, cbNeeded, &cbNeeded); if (!ok) goto failed; for (i = 0; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++) { if (!(*win32_GetModuleInformation) (current_process_handle, DllHandle[i], &mi, sizeof (mi))) { DWORD err = GetLastError (); error ("Can't get module info: (error %d): %s\n", (int) err, strwinerror (err)); } if ((DWORD) (mi.lpBaseOfDll) == BaseAddress) { len = (*win32_GetModuleFileNameExA) (current_process_handle, DllHandle[i], dll_name_ret, MAX_PATH); if (len == 0) { DWORD err = GetLastError (); error ("Error getting dll name: (error %d): %s\n", (int) err, strwinerror (err)); } return 1; } } failed: dll_name_ret[0] = '\0'; return 0; } typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD); typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32); typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32); static winapi_CreateToolhelp32Snapshot win32_CreateToolhelp32Snapshot; static winapi_Module32First win32_Module32First; static winapi_Module32Next win32_Module32Next; #ifdef _WIN32_WCE typedef BOOL (WINAPI *winapi_CloseToolhelp32Snapshot) (HANDLE); static winapi_CloseToolhelp32Snapshot win32_CloseToolhelp32Snapshot; #endif static BOOL load_toolhelp (void) { static int toolhelp_loaded = 0; static HMODULE dll = NULL; if (!toolhelp_loaded) { toolhelp_loaded = 1; #ifndef _WIN32_WCE dll = GetModuleHandle (_T("KERNEL32.DLL")); #else dll = LoadLibrary (L"TOOLHELP.DLL"); #endif if (!dll) return FALSE; win32_CreateToolhelp32Snapshot = GETPROCADDRESS (dll, CreateToolhelp32Snapshot); win32_Module32First = GETPROCADDRESS (dll, Module32First); win32_Module32Next = GETPROCADDRESS (dll, Module32Next); #ifdef _WIN32_WCE win32_CloseToolhelp32Snapshot = GETPROCADDRESS (dll, CloseToolhelp32Snapshot); #endif } return (win32_CreateToolhelp32Snapshot != NULL && win32_Module32First != NULL && win32_Module32Next != NULL #ifdef _WIN32_WCE && win32_CloseToolhelp32Snapshot != NULL #endif ); } static int toolhelp_get_dll_name (DWORD BaseAddress, char *dll_name_ret) { HANDLE snapshot_module; MODULEENTRY32 modEntry = { sizeof (MODULEENTRY32) }; int found = 0; if (!load_toolhelp ()) return 0; snapshot_module = win32_CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, current_event.dwProcessId); if (snapshot_module == INVALID_HANDLE_VALUE) return 0; /* Ignore the first module, which is the exe. */ if (win32_Module32First (snapshot_module, &modEntry)) while (win32_Module32Next (snapshot_module, &modEntry)) if ((DWORD) modEntry.modBaseAddr == BaseAddress) { #ifdef UNICODE wcstombs (dll_name_ret, modEntry.szExePath, MAX_PATH + 1); #else strcpy (dll_name_ret, modEntry.szExePath); #endif found = 1; break; } #ifdef _WIN32_WCE win32_CloseToolhelp32Snapshot (snapshot_module); #else CloseHandle (snapshot_module); #endif return found; } static void handle_load_dll (void) { LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll; char dll_buf[MAX_PATH + 1]; char *dll_name = NULL; DWORD load_addr; dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0'; if (!psapi_get_dll_name ((DWORD) (event->lpBaseOfDll), dll_buf) && !toolhelp_get_dll_name ((DWORD) (event->lpBaseOfDll), dll_buf)) dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0'; dll_name = dll_buf; if (*dll_name == '\0') dll_name = get_image_name (current_process_handle, event->lpImageName, event->fUnicode); if (!dll_name) return; /* The symbols in a dll are offset by 0x1000, which is the the offset from 0 of the first byte in an image - because of the file header and the section alignment. */ load_addr = (DWORD) event->lpBaseOfDll + 0x1000; win32_add_one_solib (dll_name, load_addr); } static void handle_unload_dll (void) { CORE_ADDR load_addr = (CORE_ADDR) (DWORD) current_event.u.UnloadDll.lpBaseOfDll; load_addr += 0x1000; unloaded_dll (NULL, load_addr); } static void handle_exception (struct target_waitstatus *ourstatus) { DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode; ourstatus->kind = TARGET_WAITKIND_STOPPED; switch (code) { case EXCEPTION_ACCESS_VIOLATION: OUTMSG2 (("EXCEPTION_ACCESS_VIOLATION")); ourstatus->value.sig = TARGET_SIGNAL_SEGV; break; case STATUS_STACK_OVERFLOW: OUTMSG2 (("STATUS_STACK_OVERFLOW")); ourstatus->value.sig = TARGET_SIGNAL_SEGV; break; case STATUS_FLOAT_DENORMAL_OPERAND: OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case STATUS_FLOAT_INEXACT_RESULT: OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case STATUS_FLOAT_INVALID_OPERATION: OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case STATUS_FLOAT_OVERFLOW: OUTMSG2 (("STATUS_FLOAT_OVERFLOW")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case STATUS_FLOAT_STACK_CHECK: OUTMSG2 (("STATUS_FLOAT_STACK_CHECK")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case STATUS_FLOAT_UNDERFLOW: OUTMSG2 (("STATUS_FLOAT_UNDERFLOW")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case STATUS_FLOAT_DIVIDE_BY_ZERO: OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case STATUS_INTEGER_DIVIDE_BY_ZERO: OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case STATUS_INTEGER_OVERFLOW: OUTMSG2 (("STATUS_INTEGER_OVERFLOW")); ourstatus->value.sig = TARGET_SIGNAL_FPE; break; case EXCEPTION_BREAKPOINT: OUTMSG2 (("EXCEPTION_BREAKPOINT")); ourstatus->value.sig = TARGET_SIGNAL_TRAP; #ifdef _WIN32_WCE /* Remove the initial breakpoint. */ check_breakpoints ((CORE_ADDR) (long) current_event .u.Exception.ExceptionRecord.ExceptionAddress); #endif break; case DBG_CONTROL_C: OUTMSG2 (("DBG_CONTROL_C")); ourstatus->value.sig = TARGET_SIGNAL_INT; break; case DBG_CONTROL_BREAK: OUTMSG2 (("DBG_CONTROL_BREAK")); ourstatus->value.sig = TARGET_SIGNAL_INT; break; case EXCEPTION_SINGLE_STEP: OUTMSG2 (("EXCEPTION_SINGLE_STEP")); ourstatus->value.sig = TARGET_SIGNAL_TRAP; break; case EXCEPTION_ILLEGAL_INSTRUCTION: OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION")); ourstatus->value.sig = TARGET_SIGNAL_ILL; break; case EXCEPTION_PRIV_INSTRUCTION: OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION")); ourstatus->value.sig = TARGET_SIGNAL_ILL; break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION")); ourstatus->value.sig = TARGET_SIGNAL_ILL; break; default: if (current_event.u.Exception.dwFirstChance) { ourstatus->kind = TARGET_WAITKIND_SPURIOUS; return; } OUTMSG2 (("gdbserver: unknown target exception 0x%08lx at 0x%08lx", current_event.u.Exception.ExceptionRecord.ExceptionCode, (DWORD) current_event.u.Exception.ExceptionRecord. ExceptionAddress)); ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; break; } OUTMSG2 (("\n")); last_sig = ourstatus->value.sig; } /* Get the next event from the child. */ static void get_child_debug_event (struct target_waitstatus *ourstatus) { BOOL debug_event; last_sig = TARGET_SIGNAL_0; ourstatus->kind = TARGET_WAITKIND_SPURIOUS; /* Keep the wait time low enough for confortable remote interruption, but high enough so gdbserver doesn't become a bottleneck. */ if (!(debug_event = WaitForDebugEvent (¤t_event, 250))) return; current_inferior = (struct thread_info *) find_inferior_id (&all_threads, current_event.dwThreadId); switch (current_event.dwDebugEventCode) { case CREATE_THREAD_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT " "for pid=%d tid=%x)\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); /* Record the existence of this thread. */ child_add_thread (current_event.dwThreadId, current_event.u.CreateThread.hThread); break; case EXIT_THREAD_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); child_delete_thread (current_event.dwThreadId); break; case CREATE_PROCESS_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); CloseHandle (current_event.u.CreateProcessInfo.hFile); current_process_handle = current_event.u.CreateProcessInfo.hProcess; main_thread_id = current_event.dwThreadId; ourstatus->kind = TARGET_WAITKIND_EXECD; ourstatus->value.execd_pathname = "Main executable"; /* Add the main thread. */ child_add_thread (main_thread_id, current_event.u.CreateProcessInfo.hThread); ourstatus->value.related_pid = current_event.dwThreadId; #ifdef _WIN32_WCE /* Windows CE doesn't set the initial breakpoint automatically like the desktop versions of Windows do. We add it explicitly here. It will be removed as soon as it is hit. */ set_breakpoint_at ((CORE_ADDR) (long) current_event.u .CreateProcessInfo.lpStartAddress, delete_breakpoint_at); #endif break; case EXIT_PROCESS_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); ourstatus->kind = TARGET_WAITKIND_EXITED; ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode; CloseHandle (current_process_handle); current_process_handle = NULL; break; case LOAD_DLL_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); CloseHandle (current_event.u.LoadDll.hFile); handle_load_dll (); ourstatus->kind = TARGET_WAITKIND_LOADED; ourstatus->value.sig = TARGET_SIGNAL_TRAP; break; case UNLOAD_DLL_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); handle_unload_dll (); ourstatus->kind = TARGET_WAITKIND_LOADED; ourstatus->value.sig = TARGET_SIGNAL_TRAP; break; case EXCEPTION_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); handle_exception (ourstatus); break; case OUTPUT_DEBUG_STRING_EVENT: /* A message from the kernel (or Cygwin). */ OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); handle_output_debug_string (ourstatus); break; default: OUTMSG2 (("gdbserver: kernel event unknown " "for pid=%d tid=%x code=%ld\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId, current_event.dwDebugEventCode)); break; } current_inferior = (struct thread_info *) find_inferior_id (&all_threads, current_event.dwThreadId); } /* Wait for the inferior process to change state. STATUS will be filled in with a response code to send to GDB. Returns the signal which caused the process to stop. */ static unsigned char win32_wait (char *status) { struct target_waitstatus our_status; *status = 'T'; while (1) { /* Check if GDB sent us an interrupt request. */ check_remote_input_interrupt_request (); get_child_debug_event (&our_status); switch (our_status.kind) { case TARGET_WAITKIND_EXITED: OUTMSG2 (("Child exited with retcode = %x\n", our_status.value.integer)); *status = 'W'; child_fetch_inferior_registers (-1); return our_status.value.integer; case TARGET_WAITKIND_STOPPED: case TARGET_WAITKIND_LOADED: OUTMSG2 (("Child Stopped with signal = %d \n", our_status.value.sig)); *status = 'T'; child_fetch_inferior_registers (-1); if (our_status.kind == TARGET_WAITKIND_LOADED && !server_waiting) { /* When gdb connects, we want to be stopped at the initial breakpoint, not in some dll load event. */ child_continue (DBG_CONTINUE, -1); break; } return our_status.value.sig; default: OUTMSG (("Ignoring unknown internal event, %d\n", our_status.kind)); /* fall-through */ case TARGET_WAITKIND_SPURIOUS: case TARGET_WAITKIND_EXECD: /* do nothing, just continue */ child_continue (DBG_CONTINUE, -1); break; } } } /* Fetch registers from the inferior process. If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */ static void win32_fetch_inferior_registers (int regno) { child_fetch_inferior_registers (regno); } /* Store registers to the inferior process. If REGNO is -1, store all registers; otherwise, store at least REGNO. */ static void win32_store_inferior_registers (int regno) { child_store_inferior_registers (regno); } /* Read memory from the inferior process. This should generally be called through read_inferior_memory, which handles breakpoint shadowing. Read LEN bytes at MEMADDR into a buffer at MYADDR. */ static int win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) { return child_xfer_memory (memaddr, (char *) myaddr, len, 0, 0) != len; } /* Write memory to the inferior process. This should generally be called through write_inferior_memory, which handles breakpoint shadowing. Write LEN bytes from the buffer at MYADDR to MEMADDR. Returns 0 on success and errno on failure. */ static int win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) { return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len; } /* Send an interrupt request to the inferior process. */ static void win32_request_interrupt (void) { winapi_DebugBreakProcess DebugBreakProcess; winapi_GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent; #ifdef _WIN32_WCE HMODULE dll = GetModuleHandle (_T("COREDLL.DLL")); #else HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL")); #endif GenerateConsoleCtrlEvent = GETPROCADDRESS (dll, GenerateConsoleCtrlEvent); if (GenerateConsoleCtrlEvent != NULL && GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, current_process_id)) return; /* GenerateConsoleCtrlEvent can fail if process id being debugged is not a process group id. Fallback to XP/Vista 'DebugBreakProcess', which generates a breakpoint exception in the interior process. */ DebugBreakProcess = GETPROCADDRESS (dll, DebugBreakProcess); if (DebugBreakProcess != NULL && DebugBreakProcess (current_process_handle)) return; OUTMSG (("Could not interrupt process.\n")); } static const char * win32_arch_string (void) { return the_low_target.arch_string; } static struct target_ops win32_target_ops = { win32_create_inferior, win32_attach, win32_kill, win32_detach, win32_join, win32_thread_alive, win32_resume, win32_wait, win32_fetch_inferior_registers, win32_store_inferior_registers, win32_read_inferior_memory, win32_write_inferior_memory, NULL, win32_request_interrupt, NULL, NULL, NULL, NULL, NULL, NULL, NULL, win32_arch_string }; /* Initialize the Win32 backend. */ void initialize_low (void) { set_target_ops (&win32_target_ops); if (the_low_target.breakpoint != NULL) set_breakpoint_data (the_low_target.breakpoint, the_low_target.breakpoint_len); init_registers (); }