Fix crash backtraces on MS-Windows, broken by ASLR

* src/w32fns.c (DEFAULT_IMAGE_BASE): Define for 64-bit and 32-bit
MinGW builds.
(emacs_abort): Correct the callstack addresses for potential
relocation of the image base due to ASLR.  This makes 'addr2line'
be able to interpret emacs_backtrace.txt when ASLR is in effect,
which it is on every modern version of MS-Windows.  (Bug#63365)

* configure.ac (LD_SWITCH_SYSTEM_TEMACS) [mingw32]: Add comment
about keeping the image-base values in sync with w32fns.c.

* etc/DEBUG (How to disable ASLR): New section.
This commit is contained in:
Eli Zaretskii 2024-01-26 15:01:51 +02:00
parent 723b097351
commit de020255a5
3 changed files with 57 additions and 4 deletions

View file

@ -7463,6 +7463,8 @@ case "$opsys" in
mingw32)
## Is it any better under MinGW64 to relocate emacs into higher addresses?
## If the values of -image-base are modified, the corresponding
## values of DEFAULT_IMAGE_BASE in w32fns.c should be kept in sync.
case "$canonical" in
x86_64-*-*) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x400000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;;
*) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x01000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;;

View file

@ -928,7 +928,10 @@ data that is modified only very rarely.)
It is also useful to look at the corrupted object or data structure in
a fresh Emacs session and compare its contents with a session that you
are debugging.
are debugging. This might be somewhat harder on modern systems which
randomize addresses of running executables (the so-called Address
Space Layout Randomization, or ASLR, feature). If you have this
problem, see below under "How to disable ASLR".
** Debugging the TTY (non-windowed) version
@ -1080,6 +1083,34 @@ suppresses some Valgrind false alarms during Emacs garbage collection:
Unfortunately Valgrind suppression files tend to be system-dependent,
so you will need to keep one around that matches your system.
** How to disable ASLR
Modern systems use the so-called Address Space Layout Randomization,
(ASLR) feature, which randomizes the base address of running programs,
making it harder for malicious software or hackers to find the address
of some function or variable in a running program by looking at its
executable file. This causes the address of the same symbol to be
different across rerunning of the same program. Sometimes, it can be
useful to disable ASLR, for example, if you want to compare objects in
two different Emacs sessions.
On GNU/Linux, you can disable ASLR temporarily with the following
shell command:
echo 0 > /proc/sys/kernel/randomize_va_space
or by running Emacs in an environment where ASLR is temporarily
disabled:
setarch -R emacs [args...]
To disable ASLR in Emacs on MS-Windows, you will have to rebuild Emacs
while adding '-Wl,-disable-dynamicbase' to LD_SWITCH_SYSTEM_TEMACS
variable defined in src/Makefile. Alternatively, use some tool to
edit the PE header of the Emacs executable file and reset the
DYNAMIC_BASE (0x40) flag in the DllCharacteristics flags recorded by
the PE header.
** How to recover buffer contents from an Emacs core dump file
The file etc/emacs-buffer.gdb defines a set of GDB commands for

View file

@ -11121,12 +11121,20 @@ my_exception_handler (EXCEPTION_POINTERS * exception_data)
return prev_exception_handler (exception_data);
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
#endif /* !CYGWIN */
typedef USHORT (WINAPI * CaptureStackBackTrace_proc) (ULONG, ULONG, PVOID *,
PULONG);
#define BACKTRACE_LIMIT_MAX 62
/* The below must be kept in sync with the value of the
-Wl,-image-base switch we use in LD_SWITCH_SYSTEM_TEMACS, see
configure.ac. */
#if defined MINGW_W64 && EMACS_INT_MAX > LONG_MAX
# define DEFAULT_IMAGE_BASE (ptrdiff_t)0x400000000
#else /* 32-bit MinGW build */
# define DEFAULT_IMAGE_BASE (ptrdiff_t)0x01000000
#endif
static int
w32_backtrace (void **buffer, int limit)
@ -11181,6 +11189,13 @@ emacs_abort (void)
{
void *stack[BACKTRACE_LIMIT_MAX + 1];
int i = w32_backtrace (stack, BACKTRACE_LIMIT_MAX + 1);
#ifdef CYGWIN
ptrdiff_t addr_offset = 0;
#else /* MinGW */
/* The offset below is zero unless ASLR is in effect. */
ptrdiff_t addr_offset
= DEFAULT_IMAGE_BASE - (ptrdiff_t)GetModuleHandle (NULL);
#endif /* MinGW */
if (i)
{
@ -11231,8 +11246,13 @@ emacs_abort (void)
{
/* stack[] gives the return addresses, whereas we want
the address of the call, so decrease each address
by approximate size of 1 CALL instruction. */
sprintf (buf, "%p\r\n", (char *)stack[j] - sizeof(void *));
by approximate size of 1 CALL instruction. We add
ADDR_OFFSET to account for ASLR which changes the
base address of the program's image in memory,
whereas 'addr2line' needs to see addresses relative
to the fixed base recorded in the PE header. */
sprintf (buf, "%p\r\n",
(char *)stack[j] - sizeof(void *) + addr_offset);
if (stderr_fd >= 0)
write (stderr_fd, buf, strlen (buf));
if (errfile_fd >= 0)