[2/4] libbacktrace: detect executable path on windows

Message ID 20230120105409.54949-2-gcc@hazardy.de
State New
Headers
Series [1/4] libbacktrace: change all pc related variables to uintptr_t |

Commit Message

Björn Schäpers Jan. 20, 2023, 10:54 a.m. UTC
  From: Björn Schäpers <bjoern@hazardy.de>

This is actually needed so that libstdc++'s <stacktrace> implementation
to be able to work on windows.

Tested on x86_64-linux and i686-w64-mingw32.

-- >8 --

	* configure.ac: Add a check for windows.h.
	* configure, config.h.in: Regenerate.
	* fileline.c: Add windows_get_executable_path.
	* fileline.c (fileline_initialiez): Add a pass using
	windows_get_executable_path.

Signed-off-by: Björn Schäpers <bjoern@hazardy.de>
---
 libbacktrace/config.h.in  |  3 +++
 libbacktrace/configure    | 13 ++++++++++++
 libbacktrace/configure.ac |  2 ++
 libbacktrace/fileline.c   | 43 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 60 insertions(+), 1 deletion(-)
  

Comments

Ian Lance Taylor Jan. 23, 2023, 11 p.m. UTC | #1
On Fri, Jan 20, 2023 at 2:56 AM Björn Schäpers <gcc@hazardy.de> wrote:
>
> From: Björn Schäpers <bjoern@hazardy.de>
>
> This is actually needed so that libstdc++'s <stacktrace> implementation
> to be able to work on windows.
>
> Tested on x86_64-linux and i686-w64-mingw32.
>
> -- >8 --
>
>         * configure.ac: Add a check for windows.h.
>         * configure, config.h.in: Regenerate.
>         * fileline.c: Add windows_get_executable_path.
>         * fileline.c (fileline_initialiez): Add a pass using
>         windows_get_executable_path.
>
> +#ifdef HAVE_WINDOWS_H
> +
> +static char *
> +windows_get_executable_path (char *buf, backtrace_error_callback error_callback,
> +                            void *data)
> +{
> +  if (GetModuleFileNameA (NULL, buf, MAX_PATH - 1) == 0)
> +    {
> +      error_callback (data,
> +                     "could not get the filename of the current executable",
> +                     (int) GetLastError ());
> +      return NULL;
> +    }
> +  return buf;
> +}

Thanks, but this seems incomplete.  The docs for GetModuleFileNameA
say that if the pathname is too long to fit into the buffer it returns
the size of the buffer and sets the error to
ERROR_INSUFFICIENT_BUFFER.  It seems to me that in that case we should
allocate a larger buffer and try again.  And, in general, it will be
simpler if we always allocate the buffer, as macho_get_executable_path
does.  Unfortunately it appears that Windows does not provide a way to
ask for the required length.  On Windows it seems that MAX_PATH is not
a true limit, as an extended length path may be up to 32767 bytes.

So probably something like (untested)

static char *
windows_get_executable_path (struct backtrace_state *state,
     backtrace_error_callback error_callback,
     void *data)
{
  uint32_t len;
  char *buf;

  len = MAX_PATH;
  while (1)
    {
      uint32_t got;

      name = (char *) backtrace_alloc (state, len, error_callback, data);
      if (name == NULL)
return NULL;
      got = GetModuleFileNameA (NULL, name, len);
      if (got < len - 1) /* -1 because NULB is not counted */
return name;
      backtrace_free (state, name, len, error_callback, data);
      if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
return NULL;
      len *= 2;
    }
}

Ian
  
Eli Zaretskii Jan. 24, 2023, 1:11 p.m. UTC | #2
> Date: Mon, 23 Jan 2023 15:00:56 -0800
> Cc: gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
> From: Ian Lance Taylor via Gcc <gcc@gcc.gnu.org>
> 
> > +#ifdef HAVE_WINDOWS_H
> > +
> > +static char *
> > +windows_get_executable_path (char *buf, backtrace_error_callback error_callback,
> > +                            void *data)
> > +{
> > +  if (GetModuleFileNameA (NULL, buf, MAX_PATH - 1) == 0)
> > +    {
> > +      error_callback (data,
> > +                     "could not get the filename of the current executable",
> > +                     (int) GetLastError ());
> > +      return NULL;
> > +    }
> > +  return buf;
> > +}
> 
> Thanks, but this seems incomplete.  The docs for GetModuleFileNameA
> say that if the pathname is too long to fit into the buffer it returns
> the size of the buffer and sets the error to
> ERROR_INSUFFICIENT_BUFFER.  It seems to me that in that case we should
> allocate a larger buffer and try again.

This is correct in general, but not in this particular case.

> On Windows it seems that MAX_PATH is not
> a true limit, as an extended length path may be up to 32767 bytes.

The limit of 32767 characters (not bytes, AFAIK) is only applicable
when using the Unicode (a.k.a. "wide") versions of the Windows Win32
APIs, see

  https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation

Since the above code uses GetModuleFileNameA, which is an "ANSI"
single-byte API, it is still subject to the MAX_PATH limitation, and
MAX_PATH is defined as 260 on Windows headers.
  
Ian Lance Taylor Jan. 24, 2023, 2:35 p.m. UTC | #3
On Tue, Jan 24, 2023 at 5:11 AM Eli Zaretskii via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> > Date: Mon, 23 Jan 2023 15:00:56 -0800
> > Cc: gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
> > From: Ian Lance Taylor via Gcc <gcc@gcc.gnu.org>
> >
> > > +#ifdef HAVE_WINDOWS_H
> > > +
> > > +static char *
> > > +windows_get_executable_path (char *buf, backtrace_error_callback error_callback,
> > > +                            void *data)
> > > +{
> > > +  if (GetModuleFileNameA (NULL, buf, MAX_PATH - 1) == 0)
> > > +    {
> > > +      error_callback (data,
> > > +                     "could not get the filename of the current executable",
> > > +                     (int) GetLastError ());
> > > +      return NULL;
> > > +    }
> > > +  return buf;
> > > +}
> >
> > Thanks, but this seems incomplete.  The docs for GetModuleFileNameA
> > say that if the pathname is too long to fit into the buffer it returns
> > the size of the buffer and sets the error to
> > ERROR_INSUFFICIENT_BUFFER.  It seems to me that in that case we should
> > allocate a larger buffer and try again.
>
> This is correct in general, but not in this particular case.
>
> > On Windows it seems that MAX_PATH is not
> > a true limit, as an extended length path may be up to 32767 bytes.
>
> The limit of 32767 characters (not bytes, AFAIK) is only applicable
> when using the Unicode (a.k.a. "wide") versions of the Windows Win32
> APIs, see
>
>   https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
>
> Since the above code uses GetModuleFileNameA, which is an "ANSI"
> single-byte API, it is still subject to the MAX_PATH limitation, and
> MAX_PATH is defined as 260 on Windows headers.

Thanks.  Should this code be using GetModuleFileNameW?  Or would that
mean that the later call to open will fail?

260 bytes does not seem like very much for a path name these days.

Ian
  
Eli Zaretskii Jan. 24, 2023, 4:52 p.m. UTC | #4
> From: Ian Lance Taylor <iant@golang.org>
> Date: Tue, 24 Jan 2023 06:35:21 -0800
> Cc: gcc@hazardy.de, gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
> 
> > > On Windows it seems that MAX_PATH is not
> > > a true limit, as an extended length path may be up to 32767 bytes.
> >
> > The limit of 32767 characters (not bytes, AFAIK) is only applicable
> > when using the Unicode (a.k.a. "wide") versions of the Windows Win32
> > APIs, see
> >
> >   https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
> >
> > Since the above code uses GetModuleFileNameA, which is an "ANSI"
> > single-byte API, it is still subject to the MAX_PATH limitation, and
> > MAX_PATH is defined as 260 on Windows headers.
> 
> Thanks.  Should this code be using GetModuleFileNameW?  Or would that
> mean that the later call to open will fail?

We'd need to use _wopen or somesuch, and the file name will have to be
a wchar_t array, not a char array, yes.  So this is not very practical
when file names need to be passed between functions, unless they are
converted to UTF-8 (and back again before using them in Windows APIs).

And note that even then, the 260-byte limit could be lifted only if
the user has a new enough Windows version _and_ has opted in to the
long-name feature by turning it on in the Registry.  Otherwise, file
names used in "wide" APIs can only break the 260-byte limit if they
use the special format "\\?\D:\foo\bar", which means file names
specified by user outside of the program or file names that come from
other programs will need to be reformatted to this special format.

> 260 bytes does not seem like very much for a path name these days.

That's true.  But complications with using longer file names are still
a PITA on Windows, even though they are a step closer to practically
possible.
  
Ian Lance Taylor Jan. 24, 2023, 5:58 p.m. UTC | #5
On Tue, Jan 24, 2023 at 8:53 AM Eli Zaretskii via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> > From: Ian Lance Taylor <iant@golang.org>
> > Date: Tue, 24 Jan 2023 06:35:21 -0800
> > Cc: gcc@hazardy.de, gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
> >
> > > > On Windows it seems that MAX_PATH is not
> > > > a true limit, as an extended length path may be up to 32767 bytes.
> > >
> > > The limit of 32767 characters (not bytes, AFAIK) is only applicable
> > > when using the Unicode (a.k.a. "wide") versions of the Windows Win32
> > > APIs, see
> > >
> > >   https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
> > >
> > > Since the above code uses GetModuleFileNameA, which is an "ANSI"
> > > single-byte API, it is still subject to the MAX_PATH limitation, and
> > > MAX_PATH is defined as 260 on Windows headers.
> >
> > Thanks.  Should this code be using GetModuleFileNameW?  Or would that
> > mean that the later call to open will fail?
>
> We'd need to use _wopen or somesuch, and the file name will have to be
> a wchar_t array, not a char array, yes.  So this is not very practical
> when file names need to be passed between functions, unless they are
> converted to UTF-8 (and back again before using them in Windows APIs).
>
> And note that even then, the 260-byte limit could be lifted only if
> the user has a new enough Windows version _and_ has opted in to the
> long-name feature by turning it on in the Registry.  Otherwise, file
> names used in "wide" APIs can only break the 260-byte limit if they
> use the special format "\\?\D:\foo\bar", which means file names
> specified by user outside of the program or file names that come from
> other programs will need to be reformatted to this special format.
>
> > 260 bytes does not seem like very much for a path name these days.
>
> That's true.  But complications with using longer file names are still
> a PITA on Windows, even though they are a step closer to practically
> possible.


OK, thanks.

I'd rather that the patch look like the appended.  Can someone with a
Windows system test to see what that builds and passes the tests?
Thanks.

Ian
  
Eli Zaretskii Jan. 24, 2023, 6:11 p.m. UTC | #6
> From: Ian Lance Taylor <iant@golang.org>
> Date: Tue, 24 Jan 2023 09:58:10 -0800
> Cc: gcc@hazardy.de, gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
> 
> I'd rather that the patch look like the appended.  Can someone with a
> Windows system test to see what that builds and passes the tests?

ENOPATCH
  
Ian Lance Taylor Jan. 24, 2023, 6:32 p.m. UTC | #7
On Tue, Jan 24, 2023 at 10:12 AM Eli Zaretskii via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> > From: Ian Lance Taylor <iant@golang.org>
> > Date: Tue, 24 Jan 2023 09:58:10 -0800
> > Cc: gcc@hazardy.de, gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
> >
> > I'd rather that the patch look like the appended.  Can someone with a
> > Windows system test to see what that builds and passes the tests?
>
> ENOPATCH

Gah.

Ian
diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in
index 94621c2e385..29d1ad3911a 100644
--- a/libbacktrace/config.h.in
+++ b/libbacktrace/config.h.in
@@ -100,6 +100,9 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the <windows.h> header file. */
+#undef HAVE_WINDOWS_H
+
 /* Define if -lz is available. */
 #undef HAVE_ZLIB
 
diff --git a/libbacktrace/configure b/libbacktrace/configure
index 6af2c04c81a..0a27cfb7799 100755
--- a/libbacktrace/configure
+++ b/libbacktrace/configure
@@ -13409,6 +13409,19 @@ $as_echo "#define HAVE_LOADQUERY 1" >>confdefs.h
 
 fi
 
+for ac_header in windows.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "windows.h" "ac_cv_header_windows_h" "$ac_includes_default"
+if test "x$ac_cv_header_windows_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_WINDOWS_H 1
+_ACEOF
+
+fi
+
+done
+
+
 # Check for the fcntl function.
 if test -n "${with_target_subdir}"; then
    case "${host}" in
diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac
index 39e6bf41e35..e3e10abd7b5 100644
--- a/libbacktrace/configure.ac
+++ b/libbacktrace/configure.ac
@@ -377,6 +377,8 @@ if test "$have_loadquery" = "yes"; then
   AC_DEFINE(HAVE_LOADQUERY, 1, [Define if AIX loadquery is available.])
 fi
 
+AC_CHECK_HEADERS(windows.h)
+
 # Check for the fcntl function.
 if test -n "${with_target_subdir}"; then
    case "${host}" in
diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c
index 674bf33cdcf..e110b54ee24 100644
--- a/libbacktrace/fileline.c
+++ b/libbacktrace/fileline.c
@@ -47,6 +47,18 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include <mach-o/dyld.h>
 #endif
 
+#ifdef HAVE_WINDOWS_H
+#ifndef WIN32_MEAN_AND_LEAN
+#define WIN32_MEAN_AND_LEAN
+#endif
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#include <windows.h>
+#endif
+
 #include "backtrace.h"
 #include "internal.h"
 
@@ -155,6 +167,27 @@ macho_get_executable_path (struct backtrace_state *state,
 
 #endif /* !defined (HAVE_MACH_O_DYLD_H) */
 
+#ifdef HAVE_WINDOWS_H
+
+static char *
+windows_get_executable_path (char *buf, backtrace_error_callback error_callback,
+			     void *data)
+{
+  size_t got;
+
+  got = GetModuleFileNameA (NULL, buf, MAX_PATH - 1);
+  if (got == 0
+      || (got == MAX_PATH - 1 && GetLastError () == ERROR_INSUFFICIENT_BUFFER))
+    return NULL;
+  return buf;
+}
+
+#else /* !defined (HAVE_WINDOWS_H) */
+
+#define windows_get_executable_path(buf, error_callback, data) NULL
+
+#endif /* !defined (HAVE_WINDOWS_H) */
+
 /* Initialize the fileline information from the executable.  Returns 1
    on success, 0 on failure.  */
 
@@ -168,7 +201,11 @@ fileline_initialize (struct backtrace_state *state,
   int called_error_callback;
   int descriptor;
   const char *filename;
+#ifdef HAVE_WINDOWS_H
+  char buf[MAX_PATH];
+#else
   char buf[64];
+#endif
 
   if (!state->threaded)
     failed = state->fileline_initialization_failed;
@@ -192,7 +229,7 @@ fileline_initialize (struct backtrace_state *state,
 
   descriptor = -1;
   called_error_callback = 0;
-  for (pass = 0; pass < 8; ++pass)
+  for (pass = 0; pass < 9; ++pass)
     {
       int does_not_exist;
 
@@ -224,6 +261,9 @@ fileline_initialize (struct backtrace_state *state,
 	case 7:
 	  filename = macho_get_executable_path (state, error_callback, data);
 	  break;
+	case 8:
+	  filename = windows_get_executable_path (buf, error_callback, data);
+	  break;
 	default:
 	  abort ();
 	}
  
Björn Schäpers Jan. 24, 2023, 9 p.m. UTC | #8
Am 24.01.2023 um 17:52 schrieb Eli Zaretskii:
>> From: Ian Lance Taylor <iant@golang.org>
>> Date: Tue, 24 Jan 2023 06:35:21 -0800
>> Cc: gcc@hazardy.de, gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
>>
>>>> On Windows it seems that MAX_PATH is not
>>>> a true limit, as an extended length path may be up to 32767 bytes.
>>>
>>> The limit of 32767 characters (not bytes, AFAIK) is only applicable
>>> when using the Unicode (a.k.a. "wide") versions of the Windows Win32
>>> APIs, see
>>>
>>>    https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
>>>
>>> Since the above code uses GetModuleFileNameA, which is an "ANSI"
>>> single-byte API, it is still subject to the MAX_PATH limitation, and
>>> MAX_PATH is defined as 260 on Windows headers.
>>
>> Thanks.  Should this code be using GetModuleFileNameW?  Or would that
>> mean that the later call to open will fail?
> 
> We'd need to use _wopen or somesuch, and the file name will have to be
> a wchar_t array, not a char array, yes.  So this is not very practical
> when file names need to be passed between functions, unless they are
> converted to UTF-8 (and back again before using them in Windows APIs).
> 
> And note that even then, the 260-byte limit could be lifted only if
> the user has a new enough Windows version _and_ has opted in to the
> long-name feature by turning it on in the Registry.  Otherwise, file
> names used in "wide" APIs can only break the 260-byte limit if they
> use the special format "\\?\D:\foo\bar", which means file names
> specified by user outside of the program or file names that come from
> other programs will need to be reformatted to this special format.
> 
>> 260 bytes does not seem like very much for a path name these days.
> 
> That's true.  But complications with using longer file names are still
> a PITA on Windows, even though they are a step closer to practically
> possible.

That was basically also my reasoning for choosing the A variant instead of W.
  
Björn Schäpers Feb. 5, 2023, 9:20 a.m. UTC | #9
Am 24.01.2023 um 19:32 schrieb Ian Lance Taylor:
> On Tue, Jan 24, 2023 at 10:12 AM Eli Zaretskii via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
>>
>>> From: Ian Lance Taylor <iant@golang.org>
>>> Date: Tue, 24 Jan 2023 09:58:10 -0800
>>> Cc: gcc@hazardy.de, gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
>>>
>>> I'd rather that the patch look like the appended.  Can someone with a
>>> Windows system test to see what that builds and passes the tests?
>>
>> ENOPATCH
> 
> Gah.
> 
> Ian
> 
That seems to be my original patch, right? That one I have tested (and 
am actually using) on x86 and x64 windows.
  
Ian Lance Taylor Feb. 6, 2023, 12:22 a.m. UTC | #10
On Sun, Feb 5, 2023 at 1:21 AM Björn Schäpers <gcc@hazardy.de> wrote:
>
> Am 24.01.2023 um 19:32 schrieb Ian Lance Taylor:
> > On Tue, Jan 24, 2023 at 10:12 AM Eli Zaretskii via Gcc-patches
> > <gcc-patches@gcc.gnu.org> wrote:
> >>
> >>> From: Ian Lance Taylor <iant@golang.org>
> >>> Date: Tue, 24 Jan 2023 09:58:10 -0800
> >>> Cc: gcc@hazardy.de, gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
> >>>
> >>> I'd rather that the patch look like the appended.  Can someone with a
> >>> Windows system test to see what that builds and passes the tests?
> >>
> >> ENOPATCH
> >
> > Gah.
> >
> > Ian
> >
> That seems to be my original patch, right? That one I have tested (and
> am actually using) on x86 and x64 windows.

It's very similar but I changed the windows_get_executable_path function.

Ian
  
Björn Schäpers Nov. 20, 2023, 7:56 p.m. UTC | #11
Hi,

this is what I'm using with GCC 12 and 13 on my windows machines, rebased onto 
the current HEAD.

Kind regards,
Björn.

Am 06.02.2023 um 01:22 schrieb Ian Lance Taylor:
> On Sun, Feb 5, 2023 at 1:21 AM Björn Schäpers <gcc@hazardy.de> wrote:
>>
>> Am 24.01.2023 um 19:32 schrieb Ian Lance Taylor:
>>> On Tue, Jan 24, 2023 at 10:12 AM Eli Zaretskii via Gcc-patches
>>> <gcc-patches@gcc.gnu.org> wrote:
>>>>
>>>>> From: Ian Lance Taylor <iant@golang.org>
>>>>> Date: Tue, 24 Jan 2023 09:58:10 -0800
>>>>> Cc: gcc@hazardy.de, gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org
>>>>>
>>>>> I'd rather that the patch look like the appended.  Can someone with a
>>>>> Windows system test to see what that builds and passes the tests?
>>>>
>>>> ENOPATCH
>>>
>>> Gah.
>>>
>>> Ian
>>>
>> That seems to be my original patch, right? That one I have tested (and
>> am actually using) on x86 and x64 windows.
> 
> It's very similar but I changed the windows_get_executable_path function.
> 
> Ian
From e0ee58b71f726606205aa1f0168a724859162c21 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Sch=C3=A4pers?= <bjoern@hazardy.de>
Date: Sun, 30 Apr 2023 23:48:18 +0200
Subject: [PATCH 1/3] libbacktrace: detect executable path on windows
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Tested on x86_64-linux with GCC 12, i686-w64-mingw32 and
x86_64-w64-mingw32 with GCC 12 & 13. This patch is rebased onto the
current HEAD.

-- >8 --

	* configure.ac: Add a check for windows.h.
	* configure, config.h.in: Regenerate.
	* fileline.c: Add windows_get_executable_path.
	* fileline.c (fileline_initialize): Add a pass using
	windows_get_executable_path.

Signed-off-by: Björn Schäpers <bjoern@hazardy.de>
---
 libbacktrace/config.h.in  |  3 +++
 libbacktrace/configure    | 13 +++++++++++
 libbacktrace/configure.ac |  2 ++
 libbacktrace/fileline.c   | 49 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in
index a4f5bddddf6..ee2616335c7 100644
--- a/libbacktrace/config.h.in
+++ b/libbacktrace/config.h.in
@@ -104,6 +104,9 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the <windows.h> header file. */
+#undef HAVE_WINDOWS_H
+
 /* Define if -lz is available. */
 #undef HAVE_ZLIB
 
diff --git a/libbacktrace/configure b/libbacktrace/configure
index 0ccc060901d..7ade966b54d 100755
--- a/libbacktrace/configure
+++ b/libbacktrace/configure
@@ -13509,6 +13509,19 @@ $as_echo "#define HAVE_LOADQUERY 1" >>confdefs.h
 
 fi
 
+for ac_header in windows.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "windows.h" "ac_cv_header_windows_h" "$ac_includes_default"
+if test "x$ac_cv_header_windows_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_WINDOWS_H 1
+_ACEOF
+
+fi
+
+done
+
+
 # Check for the fcntl function.
 if test -n "${with_target_subdir}"; then
    case "${host}" in
diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac
index 71cd50f8cdf..00acb42eb6d 100644
--- a/libbacktrace/configure.ac
+++ b/libbacktrace/configure.ac
@@ -379,6 +379,8 @@ if test "$have_loadquery" = "yes"; then
   AC_DEFINE(HAVE_LOADQUERY, 1, [Define if AIX loadquery is available.])
 fi
 
+AC_CHECK_HEADERS(windows.h)
+
 # Check for the fcntl function.
 if test -n "${with_target_subdir}"; then
    case "${host}" in
diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c
index 0e560b44e7a..28d752e2625 100644
--- a/libbacktrace/fileline.c
+++ b/libbacktrace/fileline.c
@@ -47,6 +47,18 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include <mach-o/dyld.h>
 #endif
 
+#ifdef HAVE_WINDOWS_H
+#ifndef WIN32_MEAN_AND_LEAN
+#define WIN32_MEAN_AND_LEAN
+#endif
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#include <windows.h>
+#endif
+
 #include "backtrace.h"
 #include "internal.h"
 
@@ -165,6 +177,34 @@ macho_get_executable_path (struct backtrace_state *state,
 
 #endif /* !HAVE_DECL__PGMPTR */
 
+#ifdef HAVE_WINDOWS_H
+
+static char *
+windows_get_executable_path (char *buf, backtrace_error_callback error_callback,
+			     void *data)
+{
+  size_t got;
+  int error;
+
+  got = GetModuleFileNameA (NULL, buf, MAX_PATH - 1);
+  error = GetLastError ();
+  if (got == 0
+      || (got == MAX_PATH - 1 && error == ERROR_INSUFFICIENT_BUFFER))
+    {
+      error_callback (data,
+		      "could not get the filename of the current executable",
+		      error);
+      return NULL;
+    }
+  return buf;
+}
+
+#else /* !defined (HAVE_WINDOWS_H) */
+
+#define windows_get_executable_path(buf, error_callback, data) NULL
+
+#endif /* !defined (HAVE_WINDOWS_H) */
+
 /* Initialize the fileline information from the executable.  Returns 1
    on success, 0 on failure.  */
 
@@ -178,7 +218,11 @@ fileline_initialize (struct backtrace_state *state,
   int called_error_callback;
   int descriptor;
   const char *filename;
+#ifdef HAVE_WINDOWS_H
+  char buf[MAX_PATH];
+#else
   char buf[64];
+#endif
 
   if (!state->threaded)
     failed = state->fileline_initialization_failed;
@@ -202,7 +246,7 @@ fileline_initialize (struct backtrace_state *state,
 
   descriptor = -1;
   called_error_callback = 0;
-  for (pass = 0; pass < 9; ++pass)
+  for (pass = 0; pass < 10; ++pass)
     {
       int does_not_exist;
 
@@ -239,6 +283,9 @@ fileline_initialize (struct backtrace_state *state,
 	case 8:
 	  filename = macho_get_executable_path (state, error_callback, data);
 	  break;
+	case 9:
+	  filename = windows_get_executable_path (buf, error_callback, data);
+	  break;
 	default:
 	  abort ();
 	}
  
Ian Lance Taylor Nov. 29, 2023, 10:05 p.m. UTC | #12
On Mon, Nov 20, 2023 at 11:57 AM Björn Schäpers <gcc@hazardy.de> wrote:
>
> this is what I'm using with GCC 12 and 13 on my windows machines, rebased onto
> the current HEAD.

Thanks.  Committed as follows.

Ian

            * fileline.c: Include <windows.h> if available.
            (windows_get_executable_path): New static function.
            (fileline_initialize): Call windows_get_executable_path.
            * configure.ac: Checked for windows.h
            * configure: Regenerate.
            * config.h.in: Regenerate.
0ee01dfacbcc9bc05d11433a69c0a0ac13afa42f
diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in
index a4f5bddddf6..ee2616335c7 100644
--- a/libbacktrace/config.h.in
+++ b/libbacktrace/config.h.in
@@ -104,6 +104,9 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the <windows.h> header file. */
+#undef HAVE_WINDOWS_H
+
 /* Define if -lz is available. */
 #undef HAVE_ZLIB
 
diff --git a/libbacktrace/configure b/libbacktrace/configure
index 0ccc060901d..7ade966b54d 100755
--- a/libbacktrace/configure
+++ b/libbacktrace/configure
@@ -13509,6 +13509,19 @@ $as_echo "#define HAVE_LOADQUERY 1" >>confdefs.h
 
 fi
 
+for ac_header in windows.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "windows.h" "ac_cv_header_windows_h" "$ac_includes_default"
+if test "x$ac_cv_header_windows_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_WINDOWS_H 1
+_ACEOF
+
+fi
+
+done
+
+
 # Check for the fcntl function.
 if test -n "${with_target_subdir}"; then
    case "${host}" in
diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac
index 71cd50f8cdf..00acb42eb6d 100644
--- a/libbacktrace/configure.ac
+++ b/libbacktrace/configure.ac
@@ -379,6 +379,8 @@ if test "$have_loadquery" = "yes"; then
   AC_DEFINE(HAVE_LOADQUERY, 1, [Define if AIX loadquery is available.])
 fi
 
+AC_CHECK_HEADERS(windows.h)
+
 # Check for the fcntl function.
 if test -n "${with_target_subdir}"; then
    case "${host}" in
diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c
index 0e560b44e7a..773f3a92969 100644
--- a/libbacktrace/fileline.c
+++ b/libbacktrace/fileline.c
@@ -47,6 +47,18 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include <mach-o/dyld.h>
 #endif
 
+#ifdef HAVE_WINDOWS_H
+#ifndef WIN32_MEAN_AND_LEAN
+#define WIN32_MEAN_AND_LEAN
+#endif
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#include <windows.h>
+#endif
+
 #include "backtrace.h"
 #include "internal.h"
 
@@ -165,6 +177,37 @@ macho_get_executable_path (struct backtrace_state *state,
 
 #endif /* !HAVE_DECL__PGMPTR */
 
+#ifdef HAVE_WINDOWS_H
+
+#define FILENAME_BUF_SIZE (MAX_PATH)
+
+static char *
+windows_get_executable_path (char *buf, backtrace_error_callback error_callback,
+			     void *data)
+{
+  size_t got;
+  int error;
+
+  got = GetModuleFileNameA (NULL, buf, FILENAME_BUF_SIZE - 1);
+  error = GetLastError ();
+  if (got == 0
+      || (got == FILENAME_BUF_SIZE - 1 && error == ERROR_INSUFFICIENT_BUFFER))
+    {
+      error_callback (data,
+		      "could not get the filename of the current executable",
+		      error);
+      return NULL;
+    }
+  return buf;
+}
+
+#else /* !defined (HAVE_WINDOWS_H) */
+
+#define windows_get_executable_path(buf, error_callback, data) NULL
+#define FILENAME_BUF_SIZE 64
+
+#endif /* !defined (HAVE_WINDOWS_H) */
+
 /* Initialize the fileline information from the executable.  Returns 1
    on success, 0 on failure.  */
 
@@ -178,7 +221,7 @@ fileline_initialize (struct backtrace_state *state,
   int called_error_callback;
   int descriptor;
   const char *filename;
-  char buf[64];
+  char buf[FILENAME_BUF_SIZE];
 
   if (!state->threaded)
     failed = state->fileline_initialization_failed;
@@ -202,7 +245,7 @@ fileline_initialize (struct backtrace_state *state,
 
   descriptor = -1;
   called_error_callback = 0;
-  for (pass = 0; pass < 9; ++pass)
+  for (pass = 0; pass < 10; ++pass)
     {
       int does_not_exist;
 
@@ -239,6 +282,9 @@ fileline_initialize (struct backtrace_state *state,
 	case 8:
 	  filename = macho_get_executable_path (state, error_callback, data);
 	  break;
+	case 9:
+	  filename = windows_get_executable_path (buf, error_callback, data);
+	  break;
 	default:
 	  abort ();
 	}
  

Patch

diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in
index a21e2eaf525..355e820741b 100644
--- a/libbacktrace/config.h.in
+++ b/libbacktrace/config.h.in
@@ -100,6 +100,9 @@ 
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the <windows.h> header file. */
+#undef HAVE_WINDOWS_H
+
 /* Define if -lz is available. */
 #undef HAVE_ZLIB
 
diff --git a/libbacktrace/configure b/libbacktrace/configure
index a5bd133f4e4..ef677423733 100755
--- a/libbacktrace/configure
+++ b/libbacktrace/configure
@@ -13403,6 +13403,19 @@  $as_echo "#define HAVE_LOADQUERY 1" >>confdefs.h
 
 fi
 
+for ac_header in windows.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "windows.h" "ac_cv_header_windows_h" "$ac_includes_default"
+if test "x$ac_cv_header_windows_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_WINDOWS_H 1
+_ACEOF
+
+fi
+
+done
+
+
 # Check for the fcntl function.
 if test -n "${with_target_subdir}"; then
    case "${host}" in
diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac
index 1daaa2f62d2..b5feb29bcdc 100644
--- a/libbacktrace/configure.ac
+++ b/libbacktrace/configure.ac
@@ -377,6 +377,8 @@  if test "$have_loadquery" = "yes"; then
   AC_DEFINE(HAVE_LOADQUERY, 1, [Define if AIX loadquery is available.])
 fi
 
+AC_CHECK_HEADERS(windows.h)
+
 # Check for the fcntl function.
 if test -n "${with_target_subdir}"; then
    case "${host}" in
diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c
index a40cd498114..73c2c8e8bc9 100644
--- a/libbacktrace/fileline.c
+++ b/libbacktrace/fileline.c
@@ -47,6 +47,18 @@  POSSIBILITY OF SUCH DAMAGE.  */
 #include <mach-o/dyld.h>
 #endif
 
+#ifdef HAVE_WINDOWS_H
+#ifndef WIN32_MEAN_AND_LEAN
+#define WIN32_MEAN_AND_LEAN
+#endif
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#include <windows.h>
+#endif
+
 #include "backtrace.h"
 #include "internal.h"
 
@@ -155,6 +167,28 @@  macho_get_executable_path (struct backtrace_state *state,
 
 #endif /* !defined (HAVE_MACH_O_DYLD_H) */
 
+#ifdef HAVE_WINDOWS_H
+
+static char *
+windows_get_executable_path (char *buf, backtrace_error_callback error_callback,
+			     void *data)
+{
+  if (GetModuleFileNameA (NULL, buf, MAX_PATH - 1) == 0)
+    {
+      error_callback (data,
+		      "could not get the filename of the current executable",
+		      (int) GetLastError ());
+      return NULL;
+    }
+  return buf;
+}
+
+#else /* !defined (HAVE_WINDOWS_H) */
+
+#define windows_get_executable_path(buf, error_callback, data) NULL
+
+#endif /* !defined (HAVE_WINDOWS_H) */
+
 /* Initialize the fileline information from the executable.  Returns 1
    on success, 0 on failure.  */
 
@@ -168,7 +202,11 @@  fileline_initialize (struct backtrace_state *state,
   int called_error_callback;
   int descriptor;
   const char *filename;
+#ifdef HAVE_WINDOWS_H
+  char buf[MAX_PATH];
+#else
   char buf[64];
+#endif
 
   if (!state->threaded)
     failed = state->fileline_initialization_failed;
@@ -192,7 +230,7 @@  fileline_initialize (struct backtrace_state *state,
 
   descriptor = -1;
   called_error_callback = 0;
-  for (pass = 0; pass < 8; ++pass)
+  for (pass = 0; pass < 9; ++pass)
     {
       int does_not_exist;
 
@@ -224,6 +262,9 @@  fileline_initialize (struct backtrace_state *state,
 	case 7:
 	  filename = macho_get_executable_path (state, error_callback, data);
 	  break;
+	case 8:
+	  filename = windows_get_executable_path (buf, error_callback, data);
+	  break;
 	default:
 	  abort ();
 	}