/* Target-vector operations for controlling windows child processes, for GDB.
- Copyright (C) 1995-2015 Free Software Foundation, Inc.
+ Copyright (C) 1995-2017 Free Software Foundation, Inc.
Contributed by Cygnus Solutions, A Red Hat Company.
#include "solist.h"
#include "solib.h"
#include "xml-support.h"
+#include "inttypes.h"
#include "i386-tdep.h"
#include "i387-tdep.h"
#define GetConsoleFontSize dyn_GetConsoleFontSize
#define GetCurrentConsoleFont dyn_GetCurrentConsoleFont
-static BOOL WINAPI (*AdjustTokenPrivileges)(HANDLE, BOOL, PTOKEN_PRIVILEGES,
- DWORD, PTOKEN_PRIVILEGES, PDWORD);
-static BOOL WINAPI (*DebugActiveProcessStop) (DWORD);
-static BOOL WINAPI (*DebugBreakProcess) (HANDLE);
-static BOOL WINAPI (*DebugSetProcessKillOnExit) (BOOL);
-static BOOL WINAPI (*EnumProcessModules) (HANDLE, HMODULE *, DWORD,
- LPDWORD);
-static BOOL WINAPI (*GetModuleInformation) (HANDLE, HMODULE, LPMODULEINFO,
- DWORD);
-static BOOL WINAPI (*LookupPrivilegeValueA)(LPCSTR, LPCSTR, PLUID);
-static BOOL WINAPI (*OpenProcessToken)(HANDLE, DWORD, PHANDLE);
-static BOOL WINAPI (*GetCurrentConsoleFont) (HANDLE, BOOL,
- CONSOLE_FONT_INFO *);
-static COORD WINAPI (*GetConsoleFontSize) (HANDLE, DWORD);
+typedef BOOL WINAPI (AdjustTokenPrivileges_ftype) (HANDLE, BOOL,
+ PTOKEN_PRIVILEGES,
+ DWORD, PTOKEN_PRIVILEGES,
+ PDWORD);
+static AdjustTokenPrivileges_ftype *AdjustTokenPrivileges;
+
+typedef BOOL WINAPI (DebugActiveProcessStop_ftype) (DWORD);
+static DebugActiveProcessStop_ftype *DebugActiveProcessStop;
+
+typedef BOOL WINAPI (DebugBreakProcess_ftype) (HANDLE);
+static DebugBreakProcess_ftype *DebugBreakProcess;
+
+typedef BOOL WINAPI (DebugSetProcessKillOnExit_ftype) (BOOL);
+static DebugSetProcessKillOnExit_ftype *DebugSetProcessKillOnExit;
+
+typedef BOOL WINAPI (EnumProcessModules_ftype) (HANDLE, HMODULE *, DWORD,
+ LPDWORD);
+static EnumProcessModules_ftype *EnumProcessModules;
+
+typedef BOOL WINAPI (GetModuleInformation_ftype) (HANDLE, HMODULE,
+ LPMODULEINFO, DWORD);
+static GetModuleInformation_ftype *GetModuleInformation;
+
+typedef BOOL WINAPI (LookupPrivilegeValueA_ftype) (LPCSTR, LPCSTR, PLUID);
+static LookupPrivilegeValueA_ftype *LookupPrivilegeValueA;
+
+typedef BOOL WINAPI (OpenProcessToken_ftype) (HANDLE, DWORD, PHANDLE);
+static OpenProcessToken_ftype *OpenProcessToken;
+
+typedef BOOL WINAPI (GetCurrentConsoleFont_ftype) (HANDLE, BOOL,
+ CONSOLE_FONT_INFO *);
+static GetCurrentConsoleFont_ftype *GetCurrentConsoleFont;
+
+typedef COORD WINAPI (GetConsoleFontSize_ftype) (HANDLE, DWORD);
+static GetConsoleFontSize_ftype *GetConsoleFontSize;
#undef STARTUPINFO
#undef CreateProcess
#ifndef __CYGWIN__
# define __PMAX (MAX_PATH + 1)
- static DWORD WINAPI (*GetModuleFileNameEx) (HANDLE, HMODULE, LPSTR, DWORD);
+ typedef DWORD WINAPI (GetModuleFileNameEx_ftype) (HANDLE, HMODULE, LPSTR, DWORD);
+ static GetModuleFileNameEx_ftype *GetModuleFileNameEx;
# define STARTUPINFO STARTUPINFOA
# define CreateProcess CreateProcessA
# define GetModuleFileNameEx_name "GetModuleFileNameExA"
static CORE_ADDR cygwin_load_end;
# define __USEWIDE
typedef wchar_t cygwin_buf_t;
- static DWORD WINAPI (*GetModuleFileNameEx) (HANDLE, HMODULE,
- LPWSTR, DWORD);
+ typedef DWORD WINAPI (GetModuleFileNameEx_ftype) (HANDLE, HMODULE,
+ LPWSTR, DWORD);
+ static GetModuleFileNameEx_ftype *GetModuleFileNameEx;
# define STARTUPINFO STARTUPINFOW
# define CreateProcess CreateProcessW
# define GetModuleFileNameEx_name "GetModuleFileNameExW"
static int windows_initialization_done;
#define DR6_CLEAR_VALUE 0xffff0ff0
+/* The exception thrown by a program to tell the debugger the name of
+ a thread. The exception record contains an ID of a thread and a
+ name to give it. This exception has no documented name, but MSDN
+ dubs it "MS_VC_EXCEPTION" in one code example. */
+#define MS_VC_EXCEPTION 0x406d1388
+
+typedef enum
+{
+ HANDLE_EXCEPTION_UNHANDLED = 0,
+ HANDLE_EXCEPTION_HANDLED,
+ HANDLE_EXCEPTION_IGNORED
+} handle_exception_result;
+
/* The string sent by cygwin when it processes a signal.
FIXME: This should be in a cygwin include file. */
#ifndef _CYGWIN_SIGNAL_STRING
{DBG_CONTROL_C, GDB_SIGNAL_INT},
{EXCEPTION_SINGLE_STEP, GDB_SIGNAL_TRAP},
{STATUS_FLOAT_DIVIDE_BY_ZERO, GDB_SIGNAL_FPE},
- {-1, -1}};
+ {-1, GDB_SIGNAL_UNKNOWN}};
/* Set the MAPPINGS static global to OFFSETS.
See the description of MAPPINGS for more details. */
return th;
}
-/* Clear out any old thread list and reintialize it to a
+/* Clear out any old thread list and reinitialize it to a
pristine state. */
static void
windows_init_thread_list (void)
{
windows_thread_info *here = th->next;
th->next = here->next;
+ xfree (here->name);
xfree (here);
}
}
static void
-do_windows_fetch_inferior_registers (struct regcache *regcache, int r)
+do_windows_fetch_inferior_registers (struct regcache *regcache,
+ windows_thread_info *th, int r)
{
- char *context_offset = ((char *) ¤t_thread->context) + mappings[r];
+ char *context_offset = ((char *) &th->context) + mappings[r];
struct gdbarch *gdbarch = get_regcache_arch (regcache);
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
long l;
- if (!current_thread)
- return; /* Windows sometimes uses a non-existent thread id in its
- events. */
-
- if (current_thread->reload_context)
+ if (th->reload_context)
{
#ifdef __CYGWIN__
if (have_saved_context)
cygwin has informed us that we should consider the signal
to have occurred at another location which is stored in
"saved_context. */
- memcpy (¤t_thread->context, &saved_context,
+ memcpy (&th->context, &saved_context,
__COPY_CONTEXT_SIZE);
have_saved_context = 0;
}
else
#endif
{
- windows_thread_info *th = current_thread;
th->context.ContextFlags = CONTEXT_DEBUGGER_DR;
CHECK (GetThreadContext (th->h, &th->context));
/* Copy dr values from that thread.
dr[7] = th->context.Dr7;
}
}
- current_thread->reload_context = 0;
+ th->reload_context = 0;
}
if (r == I387_FISEG_REGNUM (tdep))
else
{
for (r = 0; r < gdbarch_num_regs (gdbarch); r++)
- do_windows_fetch_inferior_registers (regcache, r);
+ do_windows_fetch_inferior_registers (regcache, th, r);
}
}
windows_fetch_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int r)
{
- current_thread = thread_rec (ptid_get_tid (inferior_ptid), TRUE);
- /* Check if current_thread exists. Windows sometimes uses a non-existent
+ DWORD pid = ptid_get_tid (regcache_get_ptid (regcache));
+ windows_thread_info *th = thread_rec (pid, TRUE);
+
+ /* Check if TH exists. Windows sometimes uses a non-existent
thread id in its events. */
- if (current_thread)
- do_windows_fetch_inferior_registers (regcache, r);
+ if (th != NULL)
+ do_windows_fetch_inferior_registers (regcache, th, r);
}
static void
-do_windows_store_inferior_registers (const struct regcache *regcache, int r)
+do_windows_store_inferior_registers (const struct regcache *regcache,
+ windows_thread_info *th, int r)
{
- if (!current_thread)
- /* Windows sometimes uses a non-existent thread id in its events. */;
- else if (r >= 0)
+ if (r >= 0)
regcache_raw_collect (regcache, r,
- ((char *) ¤t_thread->context) + mappings[r]);
+ ((char *) &th->context) + mappings[r]);
else
{
for (r = 0; r < gdbarch_num_regs (get_regcache_arch (regcache)); r++)
- do_windows_store_inferior_registers (regcache, r);
+ do_windows_store_inferior_registers (regcache, th, r);
}
}
-/* Store a new register value into the current thread context. */
+/* Store a new register value into the context of the thread tied to
+ REGCACHE. */
static void
windows_store_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int r)
{
- current_thread = thread_rec (ptid_get_tid (inferior_ptid), TRUE);
- /* Check if current_thread exists. Windows sometimes uses a non-existent
+ DWORD pid = ptid_get_tid (regcache_get_ptid (regcache));
+ windows_thread_info *th = thread_rec (pid, TRUE);
+
+ /* Check if TH exists. Windows sometimes uses a non-existent
thread id in its events. */
- if (current_thread)
- do_windows_store_inferior_registers (regcache, r);
+ if (th != NULL)
+ do_windows_store_inferior_registers (regcache, th, r);
}
/* Encapsulate the information required in a call to
};
/* Maintain a linked list of "so" information. */
-struct lm_info
+struct lm_info_windows : public lm_info_base
{
- LPVOID load_addr;
+ LPVOID load_addr = 0;
};
static struct so_list solib_start, *solib_end;
}
#endif
so = XCNEW (struct so_list);
- so->lm_info = (struct lm_info *) xmalloc (sizeof (struct lm_info));
- so->lm_info->load_addr = load_addr;
+ lm_info_windows *li = new lm_info_windows;
+ so->lm_info = li;
+ li->load_addr = load_addr;
strcpy (so->so_original_name, name);
#ifndef __CYGWIN__
strcpy (so->so_name, buf);
p = strchr (so->so_name, '\0') - (sizeof ("/cygwin1.dll") - 1);
if (p >= so->so_name && strcasecmp (p, "/cygwin1.dll") == 0)
{
- bfd *abfd;
asection *text = NULL;
CORE_ADDR text_vma;
- abfd = gdb_bfd_open (so->so_name, "pei-i386", -1);
+ gdb_bfd_ref_ptr abfd (gdb_bfd_open (so->so_name, "pei-i386", -1));
- if (!abfd)
+ if (abfd == NULL)
return so;
- if (bfd_check_format (abfd, bfd_object))
- text = bfd_get_section_by_name (abfd, ".text");
+ if (bfd_check_format (abfd.get (), bfd_object))
+ text = bfd_get_section_by_name (abfd.get (), ".text");
if (!text)
- {
- gdb_bfd_unref (abfd);
- return so;
- }
+ return so;
/* The symbols in a dll are offset by 0x1000, which is the
offset from 0 of the first byte in an image - because of the
file header and the section alignment. */
cygwin_load_start = (CORE_ADDR) (uintptr_t) ((char *)
load_addr + 0x1000);
- cygwin_load_end = cygwin_load_start + bfd_section_size (abfd, text);
-
- gdb_bfd_unref (abfd);
+ cygwin_load_end = cygwin_load_start + bfd_section_size (abfd.get (),
+ text);
}
#endif
solib_end->next = windows_make_so (dll_name, event->lpBaseOfDll);
solib_end = solib_end->next;
+ lm_info_windows *li = (lm_info_windows *) solib_end->lm_info;
+
DEBUG_EVENTS (("gdb: Loading dll \"%s\" at %s.\n", solib_end->so_name,
- host_address_to_string (solib_end->lm_info->load_addr)));
+ host_address_to_string (li->load_addr)));
return 1;
}
static void
windows_free_so (struct so_list *so)
{
- if (so->lm_info)
- xfree (so->lm_info);
+ lm_info_windows *li = (lm_info_windows *) so->lm_info;
+
+ delete li;
xfree (so);
}
struct so_list *so;
for (so = &solib_start; so->next != NULL; so = so->next)
- if (so->next->lm_info->load_addr == lpBaseOfDll)
- {
- struct so_list *sodel = so->next;
+ {
+ lm_info_windows *li_next = (lm_info_windows *) so->next->lm_info;
+
+ if (li_next->load_addr == lpBaseOfDll)
+ {
+ struct so_list *sodel = so->next;
- so->next = sodel->next;
- if (!so->next)
- solib_end = so;
- DEBUG_EVENTS (("gdb: Unloading dll \"%s\".\n", sodel->so_name));
+ so->next = sodel->next;
+ if (!so->next)
+ solib_end = so;
+ DEBUG_EVENTS (("gdb: Unloading dll \"%s\".\n", sodel->so_name));
- windows_free_so (sodel);
- return 1;
- }
+ windows_free_so (sodel);
+ return 1;
+ }
+ }
/* We did not find any DLL that was previously loaded at this address,
so register a complaint. We do not report an error, because we have
solib_end = &solib_start;
}
+static void
+signal_event_command (char *args, int from_tty)
+{
+ uintptr_t event_id = 0;
+ char *endargs = NULL;
+
+ if (args == NULL)
+ error (_("signal-event requires an argument (integer event id)"));
+
+ event_id = strtoumax (args, &endargs, 10);
+
+ if ((errno == ERANGE) || (event_id == 0) || (event_id > UINTPTR_MAX) ||
+ ((HANDLE) event_id == INVALID_HANDLE_VALUE))
+ error (_("Failed to convert `%s' to event id"), args);
+
+ SetEvent ((HANDLE) event_id);
+ CloseHandle ((HANDLE) event_id);
+}
+
/* Handle DEBUG_STRING output from child process.
Cygwin prepends its messages with a "cygwin:". Interpret this as
a Cygwin signal. Otherwise just print the string as a warning. */
to treat this like a real signal. */
char *p;
int sig = strtol (s + sizeof (_CYGWIN_SIGNAL_STRING) - 1, &p, 0);
- int gotasig = gdb_signal_from_host (sig);
+ gdb_signal gotasig = gdb_signal_from_host (sig);
ourstatus->value.sig = gotasig;
if (gotasig)
host_address_to_string (\
current_event.u.Exception.ExceptionRecord.ExceptionAddress))
-static int
+static handle_exception_result
handle_exception (struct target_waitstatus *ourstatus)
{
- windows_thread_info *th;
- DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode;
+ EXCEPTION_RECORD *rec = ¤t_event.u.Exception.ExceptionRecord;
+ DWORD code = rec->ExceptionCode;
+ handle_exception_result result = HANDLE_EXCEPTION_HANDLED;
ourstatus->kind = TARGET_WAITKIND_STOPPED;
/* Record the context of the current thread. */
- th = thread_rec (current_event.dwThreadId, -1);
+ thread_rec (current_event.dwThreadId, -1);
switch (code)
{
cygwin-specific-signal. So, ignore SEGVs if they show up
within the text segment of the DLL itself. */
const char *fn;
- CORE_ADDR addr = (CORE_ADDR) (uintptr_t)
- current_event.u.Exception.ExceptionRecord.ExceptionAddress;
+ CORE_ADDR addr = (CORE_ADDR) (uintptr_t) rec->ExceptionAddress;
if ((!cygwin_exceptions && (addr >= cygwin_load_start
&& addr < cygwin_load_end))
|| (find_pc_partial_function (addr, &fn, NULL, NULL)
&& startswith (fn, "KERNEL32!IsBad")))
- return 0;
+ return HANDLE_EXCEPTION_UNHANDLED;
}
#endif
break;
DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_NONCONTINUABLE_EXCEPTION");
ourstatus->value.sig = GDB_SIGNAL_ILL;
break;
+ case MS_VC_EXCEPTION:
+ if (rec->NumberParameters >= 3
+ && (rec->ExceptionInformation[0] & 0xffffffff) == 0x1000)
+ {
+ DWORD named_thread_id;
+ windows_thread_info *named_thread;
+ CORE_ADDR thread_name_target;
+
+ DEBUG_EXCEPTION_SIMPLE ("MS_VC_EXCEPTION");
+
+ thread_name_target = rec->ExceptionInformation[1];
+ named_thread_id = (DWORD) (0xffffffff & rec->ExceptionInformation[2]);
+
+ if (named_thread_id == (DWORD) -1)
+ named_thread_id = current_event.dwThreadId;
+
+ named_thread = thread_rec (named_thread_id, 0);
+ if (named_thread != NULL)
+ {
+ int thread_name_len;
+ char *thread_name;
+
+ thread_name_len = target_read_string (thread_name_target,
+ &thread_name, 1025, NULL);
+ if (thread_name_len > 0)
+ {
+ thread_name[thread_name_len - 1] = '\0';
+ xfree (named_thread->name);
+ named_thread->name = thread_name;
+ }
+ else
+ xfree (thread_name);
+ }
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ result = HANDLE_EXCEPTION_IGNORED;
+ break;
+ }
+ /* treat improperly formed exception as unknown, fallthrough */
default:
/* Treat unhandled first chance exceptions specially. */
if (current_event.u.Exception.dwFirstChance)
- return -1;
+ return HANDLE_EXCEPTION_UNHANDLED;
printf_unfiltered ("gdb: unknown target exception 0x%08x at %s\n",
(unsigned) current_event.u.Exception.ExceptionRecord.ExceptionCode,
host_address_to_string (
}
exception_count++;
last_sig = ourstatus->value.sig;
- return 1;
+ return result;
}
/* Resume thread specified by ID, or all artificially suspended
if (!windows_initialization_done)
{
target_terminal_ours ();
- target_mourn_inferior ();
+ target_mourn_inferior (inferior_ptid);
error (_("During startup program exited with code 0x%x."),
(unsigned int) current_event.u.ExitProcess.dwExitCode);
}
break;
switch (handle_exception (ourstatus))
{
- case 0:
+ case HANDLE_EXCEPTION_UNHANDLED:
+ default:
continue_status = DBG_EXCEPTION_NOT_HANDLED;
break;
- case 1:
+ case HANDLE_EXCEPTION_HANDLED:
thread_id = current_event.dwThreadId;
break;
- case -1:
- last_sig = 1;
- continue_status = -1;
+ case HANDLE_EXCEPTION_IGNORED:
+ continue_status = DBG_CONTINUE;
break;
}
break;
if (!thread_id || saw_create != 1)
{
- if (continue_status == -1)
- windows_resume (ops, minus_one_ptid, 0, 1);
- else
- CHECK (windows_continue (continue_status, -1, 0));
+ CHECK (windows_continue (continue_status, -1, 0));
}
else
{
static void
do_initial_windows_stuff (struct target_ops *ops, DWORD pid, int attaching)
{
- extern int stop_after_trap;
int i;
struct inferior *inf;
struct thread_info *tp;
target_terminal_inferior ();
windows_initialization_done = 0;
- inf->control.stop_soon = STOP_QUIETLY;
+
while (1)
{
- stop_after_trap = 1;
- wait_for_inferior ();
- tp = inferior_thread ();
- if (tp->suspend.stop_signal != GDB_SIGNAL_TRAP)
- resume (tp->suspend.stop_signal);
- else
+ struct target_waitstatus status;
+
+ windows_wait (ops, minus_one_ptid, &status, 0);
+
+ /* Note windows_wait returns TARGET_WAITKIND_SPURIOUS for thread
+ events. */
+ if (status.kind != TARGET_WAITKIND_LOADED
+ && status.kind != TARGET_WAITKIND_SPURIOUS)
break;
+
+ windows_resume (ops, minus_one_ptid, 0, GDB_SIGNAL_0);
}
/* Now that the inferior has been started and all DLLs have been mapped,
windows_add_all_dlls ();
windows_initialization_done = 1;
- inf->control.stop_soon = NO_STOP_QUIETLY;
- stop_after_trap = 0;
return;
}
{
int detached = 1;
- ptid_t ptid = {-1};
+ ptid_t ptid = minus_one_ptid;
windows_resume (ops, ptid, 0, GDB_SIGNAL_0);
if (!DebugActiveProcessStop (current_event.dwProcessId))
if (detached && from_tty)
{
- char *exec_file = get_exec_file (0);
+ const char *exec_file = get_exec_file (0);
if (exec_file == 0)
exec_file = "";
printf_unfiltered ("Detaching from program: %s, Pid %u\n", exec_file,
/* Cygwin prefers that the path be in /x/y/z format, so extract
the filename into a temporary buffer first, and then convert it
to POSIX format into the destination buffer. */
- cygwin_buf_t *pathbuf = alloca (exe_name_max_len * sizeof (cygwin_buf_t));
+ cygwin_buf_t *pathbuf = (cygwin_buf_t *) alloca (exe_name_max_len * sizeof (cygwin_buf_t));
len = GetModuleFileNameEx (current_process_handle,
dh_buf, pathbuf, exe_name_max_len);
}
#endif
+#ifndef __CYGWIN__
+
+/* Redirection of inferior I/O streams for native MS-Windows programs.
+ Unlike on Unix, where this is handled by invoking the inferior via
+ the shell, on MS-Windows we need to emulate the cmd.exe shell.
+
+ The official documentation of the cmd.exe redirection features is here:
+
+ http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx
+
+ (That page talks about Windows XP, but there's no newer
+ documentation, so we assume later versions of cmd.exe didn't change
+ anything.)
+
+ Caveat: the documentation on that page seems to include a few lies.
+ For example, it describes strange constructs 1<&2 and 2<&1, which
+ seem to work only when 1>&2 resp. 2>&1 would make sense, and so I
+ think the cmd.exe parser of the redirection symbols simply doesn't
+ care about the < vs > distinction in these cases. Therefore, the
+ supported features are explicitly documented below.
+
+ The emulation below aims at supporting all the valid use cases
+ supported by cmd.exe, which include:
+
+ < FILE redirect standard input from FILE
+ 0< FILE redirect standard input from FILE
+ <&N redirect standard input from file descriptor N
+ 0<&N redirect standard input from file descriptor N
+ > FILE redirect standard output to FILE
+ >> FILE append standard output to FILE
+ 1>> FILE append standard output to FILE
+ >&N redirect standard output to file descriptor N
+ 1>&N redirect standard output to file descriptor N
+ >>&N append standard output to file descriptor N
+ 1>>&N append standard output to file descriptor N
+ 2> FILE redirect standard error to FILE
+ 2>> FILE append standard error to FILE
+ 2>&N redirect standard error to file descriptor N
+ 2>>&N append standard error to file descriptor N
+
+ Note that using N > 2 in the above construct is supported, but
+ requires that the corresponding file descriptor be open by some
+ means elsewhere or outside GDB. Also note that using ">&0" or
+ "<&2" will generally fail, because the file descriptor redirected
+ from is normally open in an incompatible mode (e.g., FD 0 is open
+ for reading only). IOW, use of such tricks is not recommended;
+ you are on your own.
+
+ We do NOT support redirection of file descriptors above 2, as in
+ "3>SOME-FILE", because MinGW compiled programs don't (supporting
+ that needs special handling in the startup code that MinGW
+ doesn't have). Pipes are also not supported.
+
+ As for invalid use cases, where the redirection contains some
+ error, the emulation below will detect that and produce some
+ error and/or failure. But the behavior in those cases is not
+ bug-for-bug compatible with what cmd.exe does in those cases.
+ That's because what cmd.exe does then is not well defined, and
+ seems to be a side effect of the cmd.exe parsing of the command
+ line more than anything else. For example, try redirecting to an
+ invalid file name, as in "> foo:bar".
+
+ There are also minor syntactic deviations from what cmd.exe does
+ in some corner cases. For example, it doesn't support the likes
+ of "> &foo" to mean redirect to file named literally "&foo"; we
+ do support that here, because that, too, sounds like some issue
+ with the cmd.exe parser. Another nicety is that we support
+ redirection targets that use file names with forward slashes,
+ something cmd.exe doesn't -- this comes in handy since GDB
+ file-name completion can be used when typing the command line for
+ the inferior. */
+
+/* Support routines for redirecting standard handles of the inferior. */
+
+/* Parse a single redirection spec, open/duplicate the specified
+ file/fd, and assign the appropriate value to one of the 3 standard
+ file descriptors. */
+static int
+redir_open (const char *redir_string, int *inp, int *out, int *err)
+{
+ int *fd, ref_fd = -2;
+ int mode;
+ const char *fname = redir_string + 1;
+ int rc = *redir_string;
+
+ switch (rc)
+ {
+ case '0':
+ fname++;
+ /* FALLTHROUGH */
+ case '<':
+ fd = inp;
+ mode = O_RDONLY;
+ break;
+ case '1': case '2':
+ fname++;
+ /* FALLTHROUGH */
+ case '>':
+ fd = (rc == '2') ? err : out;
+ mode = O_WRONLY | O_CREAT;
+ if (*fname == '>')
+ {
+ fname++;
+ mode |= O_APPEND;
+ }
+ else
+ mode |= O_TRUNC;
+ break;
+ default:
+ return -1;
+ }
+
+ if (*fname == '&' && '0' <= fname[1] && fname[1] <= '9')
+ {
+ /* A reference to a file descriptor. */
+ char *fdtail;
+ ref_fd = (int) strtol (fname + 1, &fdtail, 10);
+ if (fdtail > fname + 1 && *fdtail == '\0')
+ {
+ /* Don't allow redirection when open modes are incompatible. */
+ if ((ref_fd == 0 && (fd == out || fd == err))
+ || ((ref_fd == 1 || ref_fd == 2) && fd == inp))
+ {
+ errno = EPERM;
+ return -1;
+ }
+ if (ref_fd == 0)
+ ref_fd = *inp;
+ else if (ref_fd == 1)
+ ref_fd = *out;
+ else if (ref_fd == 2)
+ ref_fd = *err;
+ }
+ else
+ {
+ errno = EBADF;
+ return -1;
+ }
+ }
+ else
+ fname++; /* skip the separator space */
+ /* If the descriptor is already open, close it. This allows
+ multiple specs of redirections for the same stream, which is
+ somewhat nonsensical, but still valid and supported by cmd.exe.
+ (But cmd.exe only opens a single file in this case, the one
+ specified by the last redirection spec on the command line.) */
+ if (*fd >= 0)
+ _close (*fd);
+ if (ref_fd == -2)
+ {
+ *fd = _open (fname, mode, _S_IREAD | _S_IWRITE);
+ if (*fd < 0)
+ return -1;
+ }
+ else if (ref_fd == -1)
+ *fd = -1; /* reset to default destination */
+ else
+ {
+ *fd = _dup (ref_fd);
+ if (*fd < 0)
+ return -1;
+ }
+ /* _open just sets a flag for O_APPEND, which won't be passed to the
+ inferior, so we need to actually move the file pointer. */
+ if ((mode & O_APPEND) != 0)
+ _lseek (*fd, 0L, SEEK_END);
+ return 0;
+}
+
+/* Canonicalize a single redirection spec and set up the corresponding
+ file descriptor as specified. */
+static int
+redir_set_redirection (const char *s, int *inp, int *out, int *err)
+{
+ char buf[__PMAX + 2 + 5]; /* extra space for quotes & redirection string */
+ char *d = buf;
+ const char *start = s;
+ int quote = 0;
+
+ *d++ = *s++; /* copy the 1st character, < or > or a digit */
+ if ((*start == '>' || *start == '1' || *start == '2')
+ && *s == '>')
+ {
+ *d++ = *s++;
+ if (*s == '>' && *start != '>')
+ *d++ = *s++;
+ }
+ else if (*start == '0' && *s == '<')
+ *d++ = *s++;
+ /* cmd.exe recognizes "&N" only immediately after the redirection symbol. */
+ if (*s != '&')
+ {
+ while (isspace (*s)) /* skip whitespace before file name */
+ s++;
+ *d++ = ' '; /* separate file name with a single space */
+ }
+
+ /* Copy the file name. */
+ while (*s)
+ {
+ /* Remove quoting characters from the file name in buf[]. */
+ if (*s == '"') /* could support '..' quoting here */
+ {
+ if (!quote)
+ quote = *s++;
+ else if (*s == quote)
+ {
+ quote = 0;
+ s++;
+ }
+ else
+ *d++ = *s++;
+ }
+ else if (*s == '\\')
+ {
+ if (s[1] == '"') /* could support '..' here */
+ s++;
+ *d++ = *s++;
+ }
+ else if (isspace (*s) && !quote)
+ break;
+ else
+ *d++ = *s++;
+ if (d - buf >= sizeof (buf) - 1)
+ {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+ }
+ *d = '\0';
+
+ /* Windows doesn't allow redirection characters in file names, so we
+ can bail out early if they use them, or if there's no target file
+ name after the redirection symbol. */
+ if (d[-1] == '>' || d[-1] == '<')
+ {
+ errno = ENOENT;
+ return 0;
+ }
+ if (redir_open (buf, inp, out, err) == 0)
+ return s - start;
+ return 0;
+}
+
+/* Parse the command line for redirection specs and prepare the file
+ descriptors for the 3 standard streams accordingly. */
+static bool
+redirect_inferior_handles (const char *cmd_orig, char *cmd,
+ int *inp, int *out, int *err)
+{
+ const char *s = cmd_orig;
+ char *d = cmd;
+ int quote = 0;
+ bool retval = false;
+
+ while (isspace (*s))
+ *d++ = *s++;
+
+ while (*s)
+ {
+ if (*s == '"') /* could also support '..' quoting here */
+ {
+ if (!quote)
+ quote = *s;
+ else if (*s == quote)
+ quote = 0;
+ }
+ else if (*s == '\\')
+ {
+ if (s[1] == '"') /* escaped quote char */
+ s++;
+ }
+ else if (!quote)
+ {
+ /* Process a single redirection candidate. */
+ if (*s == '<' || *s == '>'
+ || ((*s == '1' || *s == '2') && s[1] == '>')
+ || (*s == '0' && s[1] == '<'))
+ {
+ int skip = redir_set_redirection (s, inp, out, err);
+
+ if (skip <= 0)
+ return false;
+ retval = true;
+ s += skip;
+ }
+ }
+ if (*s)
+ *d++ = *s++;
+ }
+ *d = '\0';
+ return retval;
+}
+#endif /* !__CYGWIN__ */
+
/* Start an inferior windows child process and sets inferior_ptid to its pid.
EXEC_FILE is the file to run.
ALLARGS is a string containing the arguments to the program.
ENV is the environment vector to pass. Errors reported with error(). */
static void
-windows_create_inferior (struct target_ops *ops, char *exec_file,
- char *allargs, char **in_env, int from_tty)
+windows_create_inferior (struct target_ops *ops, const char *exec_file,
+ const std::string &origallargs, char **in_env,
+ int from_tty)
{
STARTUPINFO si;
#ifdef __CYGWIN__
size_t len;
int tty;
int ostdin, ostdout, ostderr;
-#else
+#else /* !__CYGWIN__ */
char real_path[__PMAX];
char shell[__PMAX]; /* Path to shell */
- char *toexec;
- char *args;
- size_t args_len;
- HANDLE tty;
+ const char *toexec;
+ char *args, *allargs_copy;
+ size_t args_len, allargs_len;
+ int fd_inp = -1, fd_out = -1, fd_err = -1;
+ HANDLE tty = INVALID_HANDLE_VALUE;
+ HANDLE inf_stdin = INVALID_HANDLE_VALUE;
+ HANDLE inf_stdout = INVALID_HANDLE_VALUE;
+ HANDLE inf_stderr = INVALID_HANDLE_VALUE;
+ bool redirected = false;
char *w32env;
char *temp;
size_t envlen;
int i;
size_t envsize;
char **env;
-#endif
+#endif /* !__CYGWIN__ */
+ const char *allargs = origallargs.c_str ();
PROCESS_INFORMATION pi;
BOOL ret;
DWORD flags = 0;
error (_("Error starting executable: %d"), errno);
cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
mbstowcs (cygallargs, allargs, len);
-#else
+#else /* !__USEWIDE */
cygallargs = allargs;
#endif
}
+ mbstowcs (NULL, allargs, 0) + 2;
cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
swprintf (cygallargs, len, L" -c 'exec %s %s'", exec_file, allargs);
-#else
+#else /* !__USEWIDE */
len = (sizeof (" -c 'exec '") + strlen (exec_file)
+ strlen (allargs) + 2);
cygallargs = (char *) alloca (len);
xsnprintf (cygallargs, len, " -c 'exec %s %s'", exec_file, allargs);
-#endif
+#endif /* __USEWIDE */
toexec = shell;
flags |= DEBUG_PROCESS;
}
wcscpy (args, toexec);
wcscat (args, L" ");
wcscat (args, cygallargs);
-#else
+#else /* !__USEWIDE */
args = (cygwin_buf_t *) alloca (strlen (toexec) + strlen (cygallargs) + 2);
strcpy (args, toexec);
strcat (args, " ");
strcat (args, cygallargs);
-#endif
+#endif /* !__USEWIDE */
#ifdef CW_CVT_ENV_TO_WINENV
/* First try to create a direct Win32 copy of the POSIX environment. */
flags |= CREATE_UNICODE_ENVIRONMENT;
else
/* If that fails, fall back to old method tweaking GDB's environment. */
-#endif
+#endif /* CW_CVT_ENV_TO_WINENV */
{
/* Reset all Win32 environment variables to avoid leftover on next run. */
clear_win32_environment (environ);
close (ostdout);
close (ostderr);
}
-#else
- toexec = exec_file;
- /* Build the command line, a space-separated list of tokens where
- the first token is the name of the module to be executed.
- To avoid ambiguities introduced by spaces in the module name,
- we quote it. */
- args_len = strlen (toexec) + 2 /* quotes */ + strlen (allargs) + 2;
- args = alloca (args_len);
- xsnprintf (args, args_len, "\"%s\" %s", toexec, allargs);
-
- flags |= DEBUG_ONLY_THIS_PROCESS;
-
- if (!inferior_io_terminal)
- tty = INVALID_HANDLE_VALUE;
- else
+#else /* !__CYGWIN__ */
+ allargs_len = strlen (allargs);
+ allargs_copy = strcpy ((char *) alloca (allargs_len + 1), allargs);
+ if (strpbrk (allargs_copy, "<>") != NULL)
+ {
+ int e = errno;
+ errno = 0;
+ redirected =
+ redirect_inferior_handles (allargs, allargs_copy,
+ &fd_inp, &fd_out, &fd_err);
+ if (errno)
+ warning (_("Error in redirection: %s."), strerror (errno));
+ else
+ errno = e;
+ allargs_len = strlen (allargs_copy);
+ }
+ /* If not all the standard streams are redirected by the command
+ line, use inferior_io_terminal for those which aren't. */
+ if (inferior_io_terminal
+ && !(fd_inp >= 0 && fd_out >= 0 && fd_err >= 0))
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
if (tty == INVALID_HANDLE_VALUE)
warning (_("Warning: Failed to open TTY %s, error %#x."),
inferior_io_terminal, (unsigned) GetLastError ());
+ }
+ if (redirected || tty != INVALID_HANDLE_VALUE)
+ {
+ if (fd_inp >= 0)
+ si.hStdInput = (HANDLE) _get_osfhandle (fd_inp);
+ else if (tty != INVALID_HANDLE_VALUE)
+ si.hStdInput = tty;
else
- {
- si.hStdInput = tty;
- si.hStdOutput = tty;
- si.hStdError = tty;
- si.dwFlags |= STARTF_USESTDHANDLES;
- }
+ si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
+ if (fd_out >= 0)
+ si.hStdOutput = (HANDLE) _get_osfhandle (fd_out);
+ else if (tty != INVALID_HANDLE_VALUE)
+ si.hStdOutput = tty;
+ else
+ si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+ if (fd_err >= 0)
+ si.hStdError = (HANDLE) _get_osfhandle (fd_err);
+ else if (tty != INVALID_HANDLE_VALUE)
+ si.hStdError = tty;
+ else
+ si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+ si.dwFlags |= STARTF_USESTDHANDLES;
}
+ toexec = exec_file;
+ /* Build the command line, a space-separated list of tokens where
+ the first token is the name of the module to be executed.
+ To avoid ambiguities introduced by spaces in the module name,
+ we quote it. */
+ args_len = strlen (toexec) + 2 /* quotes */ + allargs_len + 2;
+ args = (char *) alloca (args_len);
+ xsnprintf (args, args_len, "\"%s\" %s", toexec, allargs_copy);
+
+ flags |= DEBUG_ONLY_THIS_PROCESS;
+
/* CreateProcess takes the environment list as a null terminated set of
strings (i.e. two nulls terminate the list). */
/* Windows programs expect the environment block to be sorted. */
qsort (env, i, sizeof (char *), envvar_cmp);
- w32env = alloca (envlen + 1);
+ w32env = (char *) alloca (envlen + 1);
/* Copy env strings into new buffer. */
for (temp = w32env, i = 0; env[i] && *env[i]; i++)
&pi);
if (tty != INVALID_HANDLE_VALUE)
CloseHandle (tty);
-#endif
+ if (fd_inp >= 0)
+ _close (fd_inp);
+ if (fd_out >= 0)
+ _close (fd_out);
+ if (fd_err >= 0)
+ _close (fd_err);
+#endif /* !__CYGWIN__ */
if (!ret)
error (_("Error creating process %s, (error %u)."),
break;
}
- target_mourn_inferior (); /* Or just windows_mourn_inferior? */
+ target_mourn_inferior (inferior_ptid); /* Or just windows_mourn_inferior? */
}
static void
}
/* Convert pid to printable format. */
-static char *
+static const char *
windows_pid_to_str (struct target_ops *ops, ptid_t ptid)
{
static char buf[80];
obstack_init (&obstack);
obstack_grow_str (&obstack, "<library-list>\n");
for (so = solib_start.next; so; so = so->next)
- windows_xfer_shared_library (so->so_name, (CORE_ADDR)
- (uintptr_t) so->lm_info->load_addr,
- target_gdbarch (), &obstack);
+ {
+ lm_info_windows *li = (lm_info_windows *) so->lm_info;
+
+ windows_xfer_shared_library (so->so_name, (CORE_ADDR)
+ (uintptr_t) li->load_addr,
+ target_gdbarch (), &obstack);
+ }
obstack_grow_str0 (&obstack, "</library-list>\n");
- buf = obstack_finish (&obstack);
+ buf = (const char *) obstack_finish (&obstack);
len_avail = strlen (buf);
if (offset >= len_avail)
len= 0;
return ptid_build (ptid_get_pid (inferior_ptid), 0, lwp);
}
+/* Implementation of the to_thread_name method. */
+
+static const char *
+windows_thread_name (struct target_ops *self, struct thread_info *thr)
+{
+ return thread_rec (ptid_get_tid (thr->ptid), 0)->name;
+}
+
static struct target_ops *
windows_target (void)
{
t->to_pid_to_exec_file = windows_pid_to_exec_file;
t->to_get_ada_task_ptid = windows_get_ada_task_ptid;
t->to_get_tib_address = windows_get_tib_address;
+ t->to_thread_name = windows_thread_name;
return t;
}
cygwin_internal (CW_SET_DOS_FILE_WARNING, 0);
#endif
+ add_com ("signal-event", class_run, signal_event_command, _("\
+Signal a crashed process with event ID, to allow its debugging.\n\
+This command is needed in support of setting up GDB as JIT debugger on \
+MS-Windows. The command should be invoked from the GDB command line using \
+the '-ex' command-line option. The ID of the event that blocks the \
+crashed process will be supplied by the Windows JIT debugging mechanism."));
+
#ifdef __CYGWIN__
add_setshow_boolean_cmd ("shell", class_support, &useshell, _("\
Set use of shell to start subprocess."), _("\
{
char *p;
char *oldini = (char *) alloca (strlen (homedir) +
- sizeof ("/gdb.ini"));
+ sizeof ("gdb.ini") + 1);
strcpy (oldini, homedir);
p = strchr (oldini, '\0');
if (p > oldini && !IS_DIR_SEPARATOR (p[-1]))
if (access (oldini, 0) == 0)
{
int len = strlen (oldini);
- char *newini = alloca (len + 1);
+ char *newini = (char *) alloca (len + 2);
- xsnprintf (newini, len + 1, "%.*s.gdbinit",
+ xsnprintf (newini, len + 2, "%.*s.gdbinit",
(int) (len - (sizeof ("gdb.ini") - 1)), oldini);
warning (_("obsolete '%s' found. Rename to '%s'."), oldini, newini);
}
{
HMODULE hm = NULL;
+#define GPA(m, func) \
+ func = (func ## _ftype *) GetProcAddress (m, #func)
+
hm = LoadLibrary ("kernel32.dll");
if (hm)
{
- DebugActiveProcessStop = (void *)
- GetProcAddress (hm, "DebugActiveProcessStop");
- DebugBreakProcess = (void *)
- GetProcAddress (hm, "DebugBreakProcess");
- DebugSetProcessKillOnExit = (void *)
- GetProcAddress (hm, "DebugSetProcessKillOnExit");
- GetConsoleFontSize = (void *)
- GetProcAddress (hm, "GetConsoleFontSize");
- GetCurrentConsoleFont = (void *)
- GetProcAddress (hm, "GetCurrentConsoleFont");
+ GPA (hm, DebugActiveProcessStop);
+ GPA (hm, DebugBreakProcess);
+ GPA (hm, DebugSetProcessKillOnExit);
+ GPA (hm, GetConsoleFontSize);
+ GPA (hm, DebugActiveProcessStop);
+ GPA (hm, GetCurrentConsoleFont);
}
/* Set variables to dummy versions of these processes if the function
hm = LoadLibrary ("psapi.dll");
if (hm)
{
- EnumProcessModules = (void *)
- GetProcAddress (hm, "EnumProcessModules");
- GetModuleInformation = (void *)
- GetProcAddress (hm, "GetModuleInformation");
- GetModuleFileNameEx = (void *)
- GetProcAddress (hm, GetModuleFileNameEx_name);
+ GPA (hm, EnumProcessModules);
+ GPA (hm, GetModuleInformation);
+ GetModuleFileNameEx = (GetModuleFileNameEx_ftype *)
+ GetProcAddress (hm, GetModuleFileNameEx_name);
}
if (!EnumProcessModules || !GetModuleInformation || !GetModuleFileNameEx)
hm = LoadLibrary ("advapi32.dll");
if (hm)
{
- OpenProcessToken = (void *) GetProcAddress (hm, "OpenProcessToken");
- LookupPrivilegeValueA = (void *)
- GetProcAddress (hm, "LookupPrivilegeValueA");
- AdjustTokenPrivileges = (void *)
- GetProcAddress (hm, "AdjustTokenPrivileges");
+ GPA (hm, OpenProcessToken);
+ GPA (hm, LookupPrivilegeValueA);
+ GPA (hm, AdjustTokenPrivileges);
/* Only need to set one of these since if OpenProcessToken fails nothing
else is needed. */
if (!OpenProcessToken || !LookupPrivilegeValueA
|| !AdjustTokenPrivileges)
OpenProcessToken = bad_OpenProcessToken;
}
+
+#undef GPA
}