Improve process exit status macros on MinGW

When a Windows program is terminated by a fatal exception, its exit
code is the value of that exception, as defined by the various
EXCEPTION_* symbols in the Windows API headers.  This commit emulates
WTERMSIG etc. by translating the fatal exception codes to more-or-less
equivalent Posix signals.

gdb/ChangeLog:
2020-01-06  Eli Zaretskii  <eliz@gnu.org>
	    Pedro Alves  <palves@redhat.com>

	* Makefile.in (COMMON_SFILES): Add gdbsupport/gdb_wait.c.
	* windows-tdep.c: New enumeration of WINDOWS_SIG* signals.
	(windows_gdb_signal_to_target): New function, uses the above
	enumeration to convert GDB internal signal codes to equivalent
	Windows codes.
	(windows_init_abi): Call set_gdbarch_gdb_signal_to_target.
	* windows-nat.c: Include "gdb_wait.h".
	(get_windows_debug_event): Extract the fatal exception from the
	exit status and convert to the equivalent Posix signal number.
	* cli/cli-cmds.c (exit_status_set_internal_vars): Account for the
	possibility that WTERMSIG returns GDB_SIGNAL_UNKNOWN.
	* gdbsupport/gdb_wait.c: New file, implements
	windows_status_to_termsig.
	* gdbsupport/gdb_wait.h (WIFEXITED, WIFSIGNALED, WEXITSTATUS)
	(WTERMSIG) [__MINGW32__]: Separate definitions for MinGW.

gdb/gdbserver/ChangeLog:
2020-01-06  Eli Zaretskii  <eliz@gnu.org>
	    Pedro Alves  <palves@redhat.com>

	* win32-low.c (get_child_debug_event): Extract the fatal exception
	from the exit status and convert to the equivalent Posix signal
	number.
	(win32_wait): Allow TARGET_WAITKIND_SIGNALLED status as well.
	* Makefile.in (OBS, SFILES): Add gdb_wait.[co].
This commit is contained in:
Eli Zaretskii 2020-01-06 11:51:54 +00:00 committed by Pedro Alves
parent 49078ece04
commit 559e7e5056
10 changed files with 322 additions and 10 deletions

View File

@ -1,3 +1,22 @@
2020-01-06 Eli Zaretskii <eliz@gnu.org>
Pedro Alves <palves@redhat.com>
* Makefile.in (COMMON_SFILES): Add gdbsupport/gdb_wait.c.
* windows-tdep.c: New enumeration of WINDOWS_SIG* signals.
(windows_gdb_signal_to_target): New function, uses the above
enumeration to convert GDB internal signal codes to equivalent
Windows codes.
(windows_init_abi): Call set_gdbarch_gdb_signal_to_target.
* windows-nat.c: Include "gdb_wait.h".
(get_windows_debug_event): Extract the fatal exception from the
exit status and convert to the equivalent Posix signal number.
* cli/cli-cmds.c (exit_status_set_internal_vars): Account for the
possibility that WTERMSIG returns GDB_SIGNAL_UNKNOWN.
* gdbsupport/gdb_wait.c: New file, implements
windows_status_to_termsig.
* gdbsupport/gdb_wait.h (WIFEXITED, WIFSIGNALED, WEXITSTATUS)
(WTERMSIG) [__MINGW32__]: Separate definitions for MinGW.
2020-01-05 Andrew Burgess <andrew.burgess@embecosm.com>
* tui/tui-layout.c (tui_add_win_to_layout): Use tui_set_layout not

View File

@ -986,6 +986,7 @@ COMMON_SFILES = \
gdbsupport/gdb-dlfcn.c \
gdbsupport/gdb_tilde_expand.c \
gdbsupport/gdb_vecs.c \
gdbsupport/gdb_wait.c \
gdbsupport/netstuff.c \
gdbsupport/new-op.c \
gdbsupport/pathstuff.c \

View File

@ -800,6 +800,18 @@ exit_status_set_internal_vars (int exit_status)
clear_internalvar (var_signal);
if (WIFEXITED (exit_status))
set_internalvar_integer (var_code, WEXITSTATUS (exit_status));
#ifdef __MINGW32__
else if (WIFSIGNALED (exit_status) && WTERMSIG (exit_status) == -1)
{
/* The -1 condition can happen on MinGW, if we don't recognize
the fatal exception code encoded in the exit status; see
gdbsupport/gdb_wait.c. We don't want to lose information in
the exit status in that case. Record it as a normal exit
with the full exit status, including the higher 0xC0000000
bits. */
set_internalvar_integer (var_code, exit_status);
}
#endif
else if (WIFSIGNALED (exit_status))
set_internalvar_integer (var_signal, WTERMSIG (exit_status));
else

View File

@ -1,3 +1,12 @@
2020-01-06 Eli Zaretskii <eliz@gnu.org>
Pedro Alves <palves@redhat.com>
* win32-low.c (get_child_debug_event): Extract the fatal exception
from the exit status and convert to the equivalent Posix signal
number.
(win32_wait): Allow TARGET_WAITKIND_SIGNALLED status as well.
* Makefile.in (OBS, SFILES): Add gdb_wait.[co].
2020-01-01 Hannes Domani <ssbssa@yahoo.de>
* Makefile.in: Use INSTALL_PROGRAM_ENV.

View File

@ -217,6 +217,7 @@ SFILES = \
$(srcdir)/gdbsupport/gdb-dlfcn.c \
$(srcdir)/gdbsupport/gdb_tilde_expand.c \
$(srcdir)/gdbsupport/gdb_vecs.c \
$(srcdir)/gdbsupport/gdb_wait.c \
$(srcdir)/gdbsupport/netstuff.c \
$(srcdir)/gdbsupport/new-op.c \
$(srcdir)/gdbsupport/pathstuff.c \
@ -264,6 +265,7 @@ OBS = \
gdbsupport/gdb-dlfcn.o \
gdbsupport/gdb_tilde_expand.o \
gdbsupport/gdb_vecs.o \
gdbsupport/gdb_wait.o \
gdbsupport/netstuff.o \
gdbsupport/new-op.o \
gdbsupport/pathstuff.o \

View File

@ -34,6 +34,7 @@
#include <process.h>
#include "gdbsupport/gdb_tilde_expand.h"
#include "gdbsupport/common-inferior.h"
#include "gdbsupport/gdb_wait.h"
#ifndef USE_WIN32API
#include <sys/cygwin.h>
@ -1511,8 +1512,24 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
"for pid=%u tid=%x\n",
(unsigned) current_event.dwProcessId,
(unsigned) current_event.dwThreadId));
ourstatus->kind = TARGET_WAITKIND_EXITED;
ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode;
{
DWORD exit_status = current_event.u.ExitProcess.dwExitCode;
/* If the exit status looks like a fatal exception, but we
don't recognize the exception's code, make the original
exit status value available, to avoid losing information. */
int exit_signal
= WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1;
if (exit_signal == -1)
{
ourstatus->kind = TARGET_WAITKIND_EXITED;
ourstatus->value.integer = exit_status;
}
else
{
ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
ourstatus->value.sig = gdb_signal_from_host (exit_signal);
}
}
child_continue (DBG_CONTINUE, -1);
CloseHandle (current_process_handle);
current_process_handle = NULL;
@ -1607,6 +1624,7 @@ win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
win32_clear_inferiors ();
return ptid_t (current_event.dwProcessId);
case TARGET_WAITKIND_STOPPED:
case TARGET_WAITKIND_SIGNALLED:
case TARGET_WAITKIND_LOADED:
OUTMSG2 (("Child Stopped with signal = %d \n",
ourstatus->value.sig));

83
gdb/gdbsupport/gdb_wait.c Normal file
View File

@ -0,0 +1,83 @@
/* Support code for standard wait macros in gdb_wait.h.
Copyright (C) 2019 Free Software Foundation, Inc.
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 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "common-defs.h"
#ifdef __MINGW32__
/* The underlying idea is that when a Windows program is terminated by
a fatal exception, its exit code is the value of that exception, as
defined by the various EXCEPTION_* symbols in the Windows API
headers. We thus emulate WTERMSIG etc. by translating the fatal
exception codes to more-or-less equivalent Posix signals.
The translation below is not perfect, because a program could
legitimately exit normally with a status whose value happens to
have the high bits set, but that's extremely rare, to say the
least, and it is deemed such a negligibly small probability of
false positives is justified by the utility of reporting the
terminating signal in the "normal" cases. */
# include "gdb/signals.h" /* for enum gdb_signal */
# define WIN32_LEAN_AND_MEAN
# include <windows.h> /* for EXCEPTION_* constants */
struct xlate_status
{
/* The exit status (actually, fatal exception code). */
DWORD status;
/* The corresponding signal value. */
int sig;
};
int
windows_status_to_termsig (unsigned long status)
{
static const xlate_status status_xlate_tbl[] =
{
{EXCEPTION_ACCESS_VIOLATION, SIGSEGV},
{EXCEPTION_IN_PAGE_ERROR, SIGSEGV},
{EXCEPTION_INVALID_HANDLE, SIGSEGV},
{EXCEPTION_ILLEGAL_INSTRUCTION, SIGILL},
{EXCEPTION_NONCONTINUABLE_EXCEPTION, SIGILL},
{EXCEPTION_ARRAY_BOUNDS_EXCEEDED, SIGSEGV},
{EXCEPTION_FLT_DENORMAL_OPERAND, SIGFPE},
{EXCEPTION_FLT_DIVIDE_BY_ZERO, SIGFPE},
{EXCEPTION_FLT_INEXACT_RESULT, SIGFPE},
{EXCEPTION_FLT_INVALID_OPERATION, SIGFPE},
{EXCEPTION_FLT_OVERFLOW, SIGFPE},
{EXCEPTION_FLT_STACK_CHECK, SIGFPE},
{EXCEPTION_FLT_UNDERFLOW, SIGFPE},
{EXCEPTION_INT_DIVIDE_BY_ZERO, SIGFPE},
{EXCEPTION_INT_OVERFLOW, SIGFPE},
{EXCEPTION_PRIV_INSTRUCTION, SIGILL},
{EXCEPTION_STACK_OVERFLOW, SIGSEGV},
{CONTROL_C_EXIT, SIGTERM}
};
for (const xlate_status &x : status_xlate_tbl)
if (x.status == status)
return x.sig;
return -1;
}
#endif /* __MINGW32__ */

View File

@ -38,20 +38,33 @@
in POSIX.1. We fail to define WNOHANG and WUNTRACED, which POSIX.1
<sys/wait.h> defines, since our code does not use waitpid() (but
NOTE exception for GNU/Linux below). We also fail to declare
wait() and waitpid(). */
wait() and waitpid().
For MinGW, we use the fact that when a Windows program is
terminated by a fatal exception, its exit code is the value of that
exception, as defined by the various EXCEPTION_* symbols in the
Windows API headers. See also gdb_wait.c. */
#ifndef WIFEXITED
#define WIFEXITED(w) (((w)&0377) == 0)
# ifdef __MINGW32__
# define WIFEXITED(w) (((w) & 0xC0000000) == 0)
# else
# define WIFEXITED(w) (((w)&0377) == 0)
# endif
#endif
#ifndef WIFSIGNALED
#define WIFSIGNALED(w) (((w)&0377) != 0177 && ((w)&~0377) == 0)
# ifdef __MINGW32__
# define WIFSIGNALED(w) (((w) & 0xC0000000) == 0xC0000000)
# else
# define WIFSIGNALED(w) (((w)&0377) != 0177 && ((w)&~0377) == 0)
# endif
#endif
#ifndef WIFSTOPPED
#ifdef IBM6000
/* Unfortunately, the above comment (about being compatible in all Unix
/* Unfortunately, the above comment (about being compatible in all Unix
systems) is not quite correct for AIX, sigh. And AIX 3.2 can generate
status words like 0x57c (sigtrap received after load), and gdb would
choke on it. */
@ -64,11 +77,20 @@
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(w) (((w) >> 8) & 0377) /* same as WRETCODE */
# ifdef __MINGW32__
# define WEXITSTATUS(w) ((w) & ~0xC0000000)
# else
# define WEXITSTATUS(w) (((w) >> 8) & 0377) /* same as WRETCODE */
# endif
#endif
#ifndef WTERMSIG
#define WTERMSIG(w) ((w) & 0177)
# ifdef __MINGW32__
extern int windows_status_to_termsig (unsigned long);
# define WTERMSIG(w) windows_status_to_termsig (w)
# else
# define WTERMSIG(w) ((w) & 0177)
# endif
#endif
#ifndef WSTOPSIG

View File

@ -68,6 +68,7 @@
#include "inf-child.h"
#include "gdbsupport/gdb_tilde_expand.h"
#include "gdbsupport/pathstuff.h"
#include "gdbsupport/gdb_wait.h"
#define AdjustTokenPrivileges dyn_AdjustTokenPrivileges
#define DebugActiveProcessStop dyn_DebugActiveProcessStop
@ -1627,8 +1628,23 @@ get_windows_debug_event (struct target_ops *ops,
windows_delete_thread (ptid_t (current_event.dwProcessId, 0,
current_event.dwThreadId),
0, true /* main_thread_p */);
ourstatus->kind = TARGET_WAITKIND_EXITED;
ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode;
DWORD exit_status = current_event.u.ExitProcess.dwExitCode;
/* If the exit status looks like a fatal exception, but we
don't recognize the exception's code, make the original
exit status value available, to avoid losing
information. */
int exit_signal
= WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1;
if (exit_signal == -1)
{
ourstatus->kind = TARGET_WAITKIND_EXITED;
ourstatus->value.integer = exit_status;
}
else
{
ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
ourstatus->value.sig = gdb_signal_from_host (exit_signal);
}
thread_id = current_event.dwThreadId;
}
break;

View File

@ -35,6 +35,57 @@
#include "solib-target.h"
#include "gdbcore.h"
/* Windows signal numbers differ between MinGW flavors and between
those and Cygwin. The below enumeration was gleaned from the
respective headers; the ones marked with MinGW64/Cygwin are defined
only by MinGW64 and Cygwin, not by mingw.org's MinGW. FIXME: We
should really have distinct MinGW vs Cygwin OSABIs, and two
separate enums, selected at runtime. */
enum
{
WINDOWS_SIGHUP = 1, /* MinGW64/Cygwin */
WINDOWS_SIGINT = 2,
WINDOWS_SIGQUIT = 3, /* MinGW64/Cygwin */
WINDOWS_SIGILL = 4,
WINDOWS_SIGTRAP = 5, /* MinGW64/Cygwin */
#ifdef __CYGWIN__
WINDOWS_SGABRT = 6,
#else
WINDOWS_SIGIOT = 6, /* MinGW64 */
#endif
WINDOWS_SIGEMT = 7, /* MinGW64/Cygwin */
WINDOWS_SIGFPE = 8,
WINDOWS_SIGKILL = 9, /* MinGW64/Cygwin */
WINDOWS_SIGBUS = 10, /* MinGW64/Cygwin */
WINDOWS_SIGSEGV = 11,
WINDOWS_SIGSYS = 12, /* MinGW64/Cygwin */
WINDOWS_SIGPIPE = 13,/* MinGW64/Cygwin */
WINDOWS_SIGALRM = 14,/* MinGW64/Cygwin */
WINDOWS_SIGTERM = 15,
#ifdef __CYGWIN__
WINDOWS_SIGURG = 16,
WINDOWS_SIGSTOP = 17,
WINDOWS_SIGTSTP = 18,
WINDOWS_SIGCONT = 19,
WINDOWS_SIGCHLD = 20,
WINDOWS_SIGTTIN = 21,
WINDOWS_SIGTTOU = 22,
WINDOWS_SIGIO = 23,
WINDOWS_SIGXCPU = 24,
WINDOWS_SIGXFSZ = 25,
WINDOWS_SIGVTALRM = 26,
WINDOWS_SIGPROF = 27,
WINDOWS_SIGWINCH = 28,
WINDOWS_SIGLOST = 29,
WINDOWS_SIGUSR1 = 30,
WINDOWS_SIGUSR2 = 31
#else
WINDOWS_SIGBREAK = 21,
WINDOWS_SIGABRT = 22
#endif
};
struct cmd_list_element *info_w32_cmdlist;
typedef struct thread_information_block_32
@ -461,6 +512,83 @@ init_w32_command_list (void)
}
}
/* Implementation of `gdbarch_gdb_signal_to_target'. */
static int
windows_gdb_signal_to_target (struct gdbarch *gdbarch, enum gdb_signal signal)
{
switch (signal)
{
case GDB_SIGNAL_0:
return 0;
case GDB_SIGNAL_HUP:
return WINDOWS_SIGHUP;
case GDB_SIGNAL_INT:
return WINDOWS_SIGINT;
case GDB_SIGNAL_QUIT:
return WINDOWS_SIGQUIT;
case GDB_SIGNAL_ILL:
return WINDOWS_SIGILL;
case GDB_SIGNAL_TRAP:
return WINDOWS_SIGTRAP;
case GDB_SIGNAL_ABRT:
return WINDOWS_SIGABRT;
case GDB_SIGNAL_EMT:
return WINDOWS_SIGEMT;
case GDB_SIGNAL_FPE:
return WINDOWS_SIGFPE;
case GDB_SIGNAL_KILL:
return WINDOWS_SIGKILL;
case GDB_SIGNAL_BUS:
return WINDOWS_SIGBUS;
case GDB_SIGNAL_SEGV:
return WINDOWS_SIGSEGV;
case GDB_SIGNAL_SYS:
return WINDOWS_SIGSYS;
case GDB_SIGNAL_PIPE:
return WINDOWS_SIGPIPE;
case GDB_SIGNAL_ALRM:
return WINDOWS_SIGALRM;
case GDB_SIGNAL_TERM:
return WINDOWS_SIGTERM;
#ifdef __CYGWIN__
case GDB_SIGNAL_URG:
return WINDOWS_SIGURG;
case GDB_SIGNAL_STOP:
return WINDOWS_SIGSTOP;
case GDB_SIGNAL_TSTP:
return WINDOWS_SIGTSTP;
case GDB_SIGNAL_CONT:
return WINDOWS_SIGCONT;
case GDB_SIGNAL_CHLD:
return WINDOWS_SIGCHLD;
case GDB_SIGNAL_TTIN:
return WINDOWS_SIGTTIN;
case GDB_SIGNAL_TTOU:
return WINDOWS_SIGTTOU;
case GDB_SIGNAL_IO:
return WINDOWS_SIGIO;
case GDB_SIGNAL_XCPU:
return WINDOWS_SIGXCPU;
case GDB_SIGNAL_XFSZ:
return WINDOWS_SIGXFSZ;
case GDB_SIGNAL_VTALRM:
return WINDOWS_SIGVTALRM;
case GDB_SIGNAL_PROF:
return WINDOWS_SIGPROF;
case GDB_SIGNAL_WINCH:
return WINDOWS_SIGWINCH;
case GDB_SIGNAL_PWR:
return WINDOWS_SIGLOST;
case GDB_SIGNAL_USR1:
return WINDOWS_SIGUSR1;
case GDB_SIGNAL_USR2:
return WINDOWS_SIGUSR2;
#endif /* __CYGWIN__ */
}
return -1;
}
/* To be called from the various GDB_OSABI_CYGWIN handlers for the
various Windows architectures and machine types. */
@ -477,6 +605,8 @@ windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
set_gdbarch_iterate_over_objfiles_in_search_order
(gdbarch, windows_iterate_over_objfiles_in_search_order);
set_gdbarch_gdb_signal_to_target (gdbarch, windows_gdb_signal_to_target);
set_solib_ops (gdbarch, &solib_target_so_ops);
}