Re: Support of gdb for Windows 64 native systems

Joel Brobecker
2007-10-13T02:41:29Z

[-- Attachment #1: Type: text/plain, Size: 2592 bytes --]

Hello Eli,

I was able to get rid of i386-win32-tdep.c and use i386-cygwin-tdep.c
instead. So one more difference gone. I fixed the problem with
including <sys/procfs.h> trivially, since we already check for
this include file during the configure phase. The size of the
patch has reduced quite a bit thanks to our discussing this together.

I'm attaching a rough sketch of the changes I think are needed
to adapt the cygwin port to also support mingw. It turns out
that there are more changes that I was claiming. My approach
of serching for ifdefs was a bit over simplistic. Since we have
copyright assignment issues anyway, I haven't tried to be extremely
precise in the patch, as I can't officially submit it for inclusion.
However, this gives a rough idea of the magnitude of the task.

Please ignore the TO_HOST_DIR_SPEC etc changes. These are relics
from the times when we used to provide a cygwin debugger that
pretended to be mingw debugger by outputing/accepting mingw paths.
We should really clean these out of our tree someday...

Back when I worked on this, my approach was to patch only the
files that I thought needed to be patched, rather than importing
the entire mega patch. However, I did tend to apply blindly
the pieces I took, so I don't necessarily know that all the
changes in win32-nat.c are necessary. That's a cleanup left
for later.

I haven't included the configure.tgt change, since it is trivial.
I might be missing some configure.ac changes, I'm not sure.

Here is the mingw.mt makefile fragment:

# Target: Intel x86 running Win32 (MinGW)
TDEPFILES= i386-tdep.o i386-cygwin-tdep.o i387-tdep.o solib-target.o
DEPRECATED_TM_FILE= tm-mingw32.h
GDBSERVER_DEPFILES=

Unfortunately, as you can see, we're still using a tm file, which
only contains the following definition:

#define ATTACH_NO_WAIT

It should be pretty easy to work around this. It's strange that
mingw would require it and not cygwin, but I haven't looked deaper
into this. Perhaps cygwin does some magic to fake an event.
It just happens that I had to do the same in my wait loop on
my WTX backend (for VxWorks).

> > > #ifdef __MINGW32__
> > > #define MAXPATHLEN PATH_MAX
> > > #endif
> >
> > This, for example, is strictly speaking wrong on Windows: Windows
> > supports much longer file names (up to 32K), if you use Unicode APIs.
>
> You are probably correct, I really know very little about Windows.

This, I still haven't fixed, because I don't know what the proper
thing to do is. What do you think I should use on Windows? I'll
happily fix it.

--
Joel

[-- Attachment #2: mingw.diff --]
[-- Type: text/plain, Size: 15359 bytes --]

Index: win32-nat.c
===================================================================
--- win32-nat.c (.../branches/gdb/FSF/current/gdb) (revision 15044)
+++ win32-nat.c (.../trunk/gdb/gdb-head/gdb) (revision 15044)
@@ -22,8 +22,6 @@

/* Originally by Steve Chamberlain, sac@cygnus.com */

-/* We assume we're being built with and will be used for cygwin. */
-
#include "defs.h"
#include "frame.h" /* required by inferior.h */
#include "inferior.h"
@@ -40,7 +38,9 @@
#include <stdlib.h>
#include <windows.h>
#include <imagehlp.h>
+#ifdef HAVE_SYS_CYGWIN_H
#include <sys/cygwin.h>
+#endif
#include <signal.h>

#include "buildsym.h"
@@ -51,6 +51,9 @@
#include "gdbthread.h"
#include "gdbcmd.h"
#include <sys/param.h>
+#ifdef __MINGW32__
+ #define MAXPATHLEN PATH_MAX
+#endif
#include <unistd.h>
#include "exec.h"
#include "solist.h"
@@ -81,7 +84,9 @@ enum
CONTEXT_DEBUGGER = (CONTEXT_FULL | CONTEXT_FLOATING_POINT)
};
#endif
+#ifdef HAVE_SYS_PROCFS_H
#include <sys/procfs.h>
+#endif
#include <psapi.h>

#define CONTEXT_DEBUGGER_DR CONTEXT_DEBUGGER | CONTEXT_DEBUG_REGISTERS \
@@ -105,6 +110,7 @@ static int debug_registers_used;

static void win32_stop (void);
static int win32_win32_thread_alive (ptid_t);
+static char *win32_pid_to_str (ptid_t ptid);
static void win32_kill_inferior (void);

static enum target_signal last_sig = TARGET_SIGNAL_0;
@@ -237,10 +243,52 @@ static void
check (BOOL ok, const char *file, int line)
{
if (!ok)
- printf_filtered ("error return %s:%d was %lu\n", file, line,
- GetLastError ());
+ {
+ LPVOID lpMsgBuf;
+ DWORD last_error = GetLastError ();
+ if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ last_error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL))
+ {
+ printf_filtered ("error return %s:%d was %lu\n", file, line,
+ last_error);
+ }
+ else
+ {
+ printf_filtered ("error return %s:%d: [%lu] %s\n", file, line,
+ last_error, (LPCTSTR) lpMsgBuf);
+ LocalFree (lpMsgBuf);
+ }
+ }
}

+#ifndef HAVE_CYGWIN_CONV_TO_POSIX_PATH
+/* A basic replacement implementation for cygwin_conv_to_posix_path ()
+ when not available (basically, when building using a MinGW compiler). */
+
+static void
+cygwin_conv_to_posix_path (const char *path, char *posix_path)
+{
+ strcpy (posix_path, path);
+}
+#endif
+
+#ifndef HAVE_CYGWIN_CONV_TO_WIN32_PATH
+/* A basic replacement implementation for cygwin_conv_to_win32_path ()
+ when not available (basically, when building using a MinGW compiler). */
+static void
+cygwin_conv_to_win32_path (const char *path, char *win32_path)
+{
+ strcpy (win32_path, path);
+}
+#endif
+
/* Find a thread record given a thread id.
If get_context then also retrieve the context for this
thread. */
@@ -446,30 +494,22 @@ static BOOL WINAPI (*psapi_EnumProcessMo
static BOOL WINAPI (*psapi_GetModuleInformation) (HANDLE, HMODULE, LPMODULEINFO, DWORD) = NULL;
static DWORD WINAPI (*psapi_GetModuleFileNameExA) (HANDLE, HMODULE, LPSTR, DWORD) = NULL;

-static int
-psapi_get_dll_name (DWORD BaseAddress, char *dll_name_ret)
+BOOL
+load_psapi ()
{
- DWORD len;
- MODULEINFO mi;
- int i;
- HMODULE dh_buf[1];
- HMODULE *DllHandle = dh_buf;
- DWORD cbNeeded;
- BOOL ok;
-
if (!psapi_loaded ||
psapi_EnumProcessModules == NULL ||
psapi_GetModuleInformation == NULL ||
psapi_GetModuleFileNameExA == NULL)
{
if (psapi_loaded)
- goto failed;
+ return FALSE;
psapi_loaded = 1;
psapi_module_handle = LoadLibrary ("psapi.dll");
if (!psapi_module_handle)
{
/* printf_unfiltered ("error loading psapi.dll: %u", GetLastError ()); */
- goto failed;
+ return FALSE;
}
psapi_EnumProcessModules = GetProcAddress (psapi_module_handle, "EnumProcessModules");
psapi_GetModuleInformation = GetProcAddress (psapi_module_handle, "GetModuleInformation");
@@ -478,9 +518,25 @@ psapi_get_dll_name (DWORD BaseAddress, c
if (psapi_EnumProcessModules == NULL ||
psapi_GetModuleInformation == NULL ||
psapi_GetModuleFileNameExA == NULL)
- goto failed;
+ return FALSE;
}
+ return TRUE;
+}

+int
+psapi_get_dll_name (DWORD BaseAddress, char *dll_name_ret)
+{
+ DWORD len;
+ MODULEINFO mi;
+ int i;
+ HMODULE dh_buf[1];
+ HMODULE *DllHandle = dh_buf;
+ DWORD cbNeeded;
+ BOOL ok;
+
+ if (!load_psapi ())
+ goto failed;
+
cbNeeded = 0;
ok = (*psapi_EnumProcessModules) (current_process_handle,
DllHandle,
@@ -525,6 +581,29 @@ failed:
return 0;
}

+int
+psapi_get_module_filename (HANDLE hProcess,
+ HMODULE hModule,
+ LPTSTR lpFilename,
+ DWORD nSize)
+{
+ DWORD len;
+
+ if (!load_psapi ())
+ goto failed;
+
+ len = (*psapi_GetModuleFileNameExA) (hProcess,
+ hModule,
+ lpFilename,
+ nSize);
+ if (len == 0)
+ error ("Error getting file name: %u\n", (unsigned) GetLastError ());
+
+failed:
+ lpFilename[0] = '\0';
+ return 0;
+}
+
/* Encapsulate the information required in a call to
symbol_file_add_args */
struct safe_symbol_file_add_args
@@ -542,6 +621,9 @@ struct safe_symbol_file_add_args
struct lm_info
{
DWORD load_addr;
+
+ /* The ImageBase, aka the prefered load address. */
+ CORE_ADDR image_base;
};

static struct so_list solib_start, *solib_end;
@@ -637,6 +719,7 @@ win32_make_so (const char *name, DWORD l
so = XZALLOC (struct so_list);
so->lm_info = (struct lm_info *) xmalloc (sizeof (struct lm_info));
so->lm_info->load_addr = load_addr;
+ so->lm_info->image_base = 0; /* Will be filled in later. */
cygwin_conv_to_posix_path (buf, so->so_name);
strcpy (so->so_original_name, name);

@@ -769,7 +852,14 @@ handle_unload_dll (void *dummy)
return 1;
}

- error (_("Error: dll starting at 0x%lx not found."), (DWORD) lpBaseOfDll);
+ /* brobecker/2006-06-15: If we reach that point, that means that
+ the debugger doesn't know of any DLL loaded at this particular
+ address. The reason for this discrepancy is unknown, but what
+ we have found out is that all the users have found an error
+ message to be confusing, and useless (no real information as
+ to what is happening such as the DLL name for instance can be
+ computed). In any case, it's just fine to ignore this unload
+ event and continue silently. */

return 0;
}
@@ -1227,6 +1317,10 @@ win32_resume (ptid_t ptid, int step, enu
FIXME: should we set dr6 also ?? */
th->context.Dr7 = dr[7];
}
+
+ DEBUG_EVENTS (("gdb: child_resume.SetThreadContext: %s\n",
+ target_pid_to_str (pid_to_ptid (th->id))));
+
CHECK (SetThreadContext (th->h, &th->context));
th->context.ContextFlags = 0;
}
@@ -1465,6 +1559,9 @@ do_initial_win32_stuff (DWORD pid)
extern int stop_after_trap;
int i;

+ DEBUG_EVENTS (("gdb: do_initial_child_stuff: %s\n",
+ target_pid_to_str (pid_to_ptid (pid))));
+
last_sig = TARGET_SIGNAL_0;
event_count = 0;
exception_count = 0;
@@ -1627,6 +1724,10 @@ win32_attach (char *args, int from_tty)

if (!ok)
{
+#ifdef __CYGWIN__
+ /* FIXME: This can be implemented better. Just moved the last
+ if (!ok) block out of this if block, and put this entire
+ if block under #ifdef __CYGWIN__. */
/* Try fall back to Cygwin pid */
pid = cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid);

@@ -1634,6 +1735,7 @@ win32_attach (char *args, int from_tty)
ok = DebugActiveProcess (pid);

if (!ok)
+#endif /* __CYGWIN__ */
error (_("Can't attach to process."));
}

@@ -1694,13 +1796,14 @@ win32_detach (char *args, int from_tty)
static char *
win32_pid_to_exec_file (int pid)
{
+ static char path[MAX_PATH + 1];
+ char *path_ptr = NULL;
+#ifdef __CYGWIN__
/* Try to find the process path using the Cygwin internal process list
pid isn't a valid pid, unfortunately. Use current_event.dwProcessId
instead. */
/* TODO: Also find native Windows processes using CW_GETPINFO_FULL. */

- static char path[MAX_PATH + 1];
- char *path_ptr = NULL;
int cpid;
struct external_pinfo *pinfo;

@@ -1718,7 +1821,15 @@ win32_pid_to_exec_file (int pid)
}
}
cygwin_internal (CW_UNLOCK_PINFO);
+#else
+ if (!psapi_get_module_filename (current_process_handle, NULL, path, MAX_PATH))
+ printf_unfiltered ("error reading the process's file name: %lu",
+ GetLastError ());
+ else
+ path_ptr = path;
+#endif
return path_ptr;
+
}

/* Print status information about what we're accessing. */
@@ -1736,6 +1847,15 @@ win32_open (char *arg, int from_tty)
error (_("Use the \"run\" command to start a Unix child process."));
}

+/* Function called by qsort to sort environment strings. */
+static int
+env_sort (const void *a, const void *b)
+{
+ const char **p = (const char **) a;
+ const char **q = (const char **) b;
+ return strcasecmp (*p, *q);
+}
+
/* Start an inferior win32 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.
@@ -1754,8 +1874,16 @@ win32_create_inferior (char *exec_file,
char *toexec;
char shell[MAX_PATH + 1]; /* Path to shell */
const char *sh;
+#if defined (__MINGW32__)
+ /* BEGIN: Fragment of Al Stevens's patch for GDB on Win9x */
+ HANDLE hStdInput = 0;
+ HANDLE hStdOutput = 0;
+ HANDLE hStdError = 0;
+ /* END: Fragment of Al Stevens's patch for GDB on Win9x */
+#else /* !__MINGW32__ */
int tty;
int ostdin, ostdout, ostderr;
+#endif /* !__MINGW32__ */
const char *inferior_io_terminal = get_inferior_io_terminal ();

if (!exec_file)
@@ -1799,8 +1927,90 @@ win32_create_inferior (char *exec_file,
strcat (args, allargs);

/* Prepare the environment vars for CreateProcess. */
+#ifdef __CYGWIN__
cygwin_internal (CW_SYNC_WINENV);
+#else
+ {
+ static const char *conv_path_names[] =
+ {
+ "PATH=",
+ 0
+ };
+ int envlen;
+ int i;
+ size_t envsize;
+ char **env;
+ char *winenv;
+ char *temp;
+
+ /* CreateProcess takes the environment list as a null terminated set of
+ strings (i.e. two nulls terminate the list). */
+
+ /* Get total size for env strings. */
+ for (envlen = 0, i = 0; in_env[i] && *in_env[i]; i++)
+ {
+ int j, len;
+
+ for (j = 0; conv_path_names[j]; j++)
+ {
+ len = strlen (conv_path_names[j]);
+ if (strncmp (conv_path_names[j], in_env[i], len) == 0)
+ {
+ envlen += strlen (in_env[i]) + 1;
+ break;
+ }
+ }
+ if (conv_path_names[j] == NULL)
+ envlen += strlen (in_env[i]) + 1;
+ }
+
+ envsize = sizeof (in_env[0]) * (i + 1);
+ env = (char **) alloca (envsize);
+ memcpy (env, in_env, envsize);
+ /* Windows programs expect the environment block to be sorted. */
+ qsort (env, i, sizeof (char *), env_sort);
+
+ winenv = alloca (envlen + 1);
+
+ /* Copy env strings into new buffer. */
+ for (temp = winenv, i = 0; env[i] && *env[i]; i++)
+ {
+ int j, len;
+
+ for (j = 0; conv_path_names[j]; j++)
+ {
+ len = strlen (conv_path_names[j]);
+ if (strncmp (conv_path_names[j], env[i], len) == 0)
+ {
+ strcpy (temp, env[i]);
+ break;
+ }
+ }
+ if (conv_path_names[j] == NULL)
+ strcpy (temp, env[i]);

+ temp += strlen (temp) + 1;
+ }
+
+ /* Final nil string to terminate new env. */
+ *temp = 0;
+ }
+#endif
+
+#if defined (__MINGW32__)
+ /* BEGIN: Fragment of Al Stevens's patch for GDB on Win9x */
+ if ( new_console)
+ {
+ hStdInput = GetStdHandle( STD_INPUT_HANDLE);
+ hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE);
+ hStdError = GetStdHandle( STD_ERROR_HANDLE);
+
+ SetStdHandle( STD_INPUT_HANDLE, INVALID_HANDLE_VALUE);
+ SetStdHandle( STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
+ SetStdHandle( STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
+ }
+ /* END: Fragment of Al Stevens's patch for GDB on Win9x */
+#else /* !__MINGW32__ */
if (!inferior_io_terminal)
tty = ostdin = ostdout = ostderr = -1;
else
@@ -1821,6 +2031,7 @@ win32_create_inferior (char *exec_file,
dup2 (tty, 2);
}
}
+#endif /* !__MINGW32__ */

win32_init_thread_list ();
ret = CreateProcess (0,
@@ -1833,6 +2044,16 @@ win32_create_inferior (char *exec_file,
NULL, /* current directory */
&si,
&pi);
+#if defined (__MINGW32__)
+ /* BEGIN: Fragment of Al Stevens's patch for GDB on Win9x */
+ if ( new_console)
+ {
+ SetStdHandle( STD_INPUT_HANDLE, hStdInput);
+ SetStdHandle( STD_OUTPUT_HANDLE, hStdOutput);
+ SetStdHandle( STD_ERROR_HANDLE, hStdError);
+ }
+ /* END: Fragment of Al Stevens's patch for GDB on Win9x */
+#else /* !__MINGW32__ */
if (tty >= 0)
{
close (tty);
@@ -1843,7 +2064,7 @@ win32_create_inferior (char *exec_file,
close (ostdout);
close (ostderr);
}
-
+#endif /* !__MINGW32__ */
if (!ret)
error (_("Error creating process %s, (error %d)."),
exec_file, (unsigned) GetLastError ());
@@ -1947,11 +2168,15 @@ win32_close (int x)
{
DEBUG_EVENTS (("gdb: win32_close, inferior_ptid=%d\n",
PIDGET (inferior_ptid)));
+ win32_init_thread_list ();
+ disable_breakpoints_in_shlibs ();
+ win32_clear_solib ();
+ clear_proceed_status ();
}

/* Convert pid to printable format. */
static char *
-cygwin_pid_to_str (ptid_t ptid)
+win32_pid_to_str (ptid_t ptid)
{
static char buf[80];
int pid = PIDGET (ptid);
@@ -2055,7 +2280,7 @@ init_win32_ops (void)
win32_ops.to_mourn_inferior = win32_mourn_inferior;
win32_ops.to_can_run = win32_can_run;
win32_ops.to_thread_alive = win32_win32_thread_alive;
- win32_ops.to_pid_to_str = cygwin_pid_to_str;
+ win32_ops.to_pid_to_str = win32_pid_to_str;
win32_ops.to_stop = win32_stop;
win32_ops.to_stratum = process_stratum;
win32_ops.to_has_all_memory = 1;
Index: symfile.c
===================================================================
--- symfile.c (.../branches/gdb/FSF/current/gdb) (revision 15044)
+++ symfile.c (.../trunk/gdb/gdb-head/gdb) (revision 15044)
@@ -1641,10 +1674,21 @@ symfile_bfd_open (char *name)
{
close (desc);
make_cleanup (xfree, name);
- error (_("\"%s\": can't open to read symbols: %s."), name,
+ error (_("\"%s\": can't open to read symbols: %s."),
+ TO_HOST_FILE_SPEC (name),
bfd_errmsg (bfd_get_error ()));
}
- bfd_set_cacheable (sym_bfd, 1);
+
+#ifdef _WIN32
+ /* The executable must not be closed because it will not been possible to
+ reopen it later under Windows NT if this executable is the one being
+ debugged. */
+
+ if (strstr (name, ".exe") != NULL)
+ sym_bfd->cacheable = FALSE;
+ else
+#endif
+ bfd_set_cacheable (sym_bfd, 1);

if (!bfd_check_format (sym_bfd, bfd_object))
{