Opened 6 years ago

Closed 3 years ago

Last modified 2 years ago

#6079 closed bug (fixed)

SEH exception handler not implemented on Win64

Reported by: igloo Owned by: Phyx-
Priority: normal Milestone: 8.0.1
Component: Runtime System Version: 7.5
Keywords: Cc: simonmar
Operating System: Windows Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case: derefnull, divbyzero
Blocked By: Blocking:
Related Tickets: #1820 Differential Rev(s): Phab:D691
Wiki Page:

Description

In RtsMain.c we only enable BEGIN_CATCH/END_CATCH on Win32. I think we need a completely different implementation for Win64.

Change History (17)

comment:1 Changed 6 years ago by igloo

Test Case: derefnull, divbyzero

comment:2 Changed 5 years ago by igloo

Milestone: 7.6.17.6.2

comment:3 Changed 4 years ago by ezyang

http://www.osronline.com/article.cfm?article=469 explains what has changed in Win64.

comment:4 Changed 4 years ago by ezyang

Architecture: x86_64 (amd64)Unknown/Multiple

comment:5 Changed 4 years ago by ezyang

Architecture: Unknown/Multiplex86_64 (amd64)

comment:6 Changed 4 years ago by ezyang

Remark: GHC does not have to be built for 64-bit for this to occur; all you need to be doing is running a 64-bit version of Windows itself.

comment:7 Changed 3 years ago by thoughtpolice

Milestone: 7.6.27.10.1

Moving to 7.10.1.

comment:8 Changed 3 years ago by thoughtpolice

Milestone: 7.10.17.12.1

Moving to 7.12.1 milestone; if you feel this is an error and should be addressed sooner, please move it back to the 7.10.1 milestone.

comment:9 Changed 3 years ago by Phyx-

Cc: simonmar added
Owner: set to Phyx-

comment:10 Changed 3 years ago by Phyx-

Summary of what's changed and how It affects the GHC side of things:

on x86 SEH handling was done via the stack. On a thread FS[0] would always point to the SEH linked list. It was also prone to buffer overflows and made it hard to share error handlers.

on x64 it was cleaned up a bit and the EH information is now stored in the the PE headers in tables. instead of an EH covering an entire thread until popped, you know have to explicitly specify the rva (relative virtual address) of the region to which the EH should apply.

Microsoft compilers calculate this information at compile time from the __try, __catch and __except1 symbols and this information is statically added to the PE file. GCC's implementation works by dynamically manipulating the table (using RtlAddFunctionTable and related functions).

It's implemented in MingW64 from version 4.6.2 (current version of msys2 ships with newer version so should be fine) and is based on GAS's implementation http://code.google.com/p/propgcc/source/browse/binutils/gas/config/obj-coff-seh.h

The only way to use this is inline assembly in gcc:

BEGIN_CATCH and END_CATCH theoretically become:

long CALLBACK __hs_exception_handler(EXCEPTION_POINTERS *exception_data);

#define BEGIN_CATCH asm (".l_start:\n" \

"\t.seh_handler __C_specific_handler, @except\n" \ "\t.seh_handlerdata\n" \ "\t.long 1\n" \ "\t.rva .l_start, .l_end, __hs_exception_handler, .l_end\n" \ "\t.text" \

);

#define END_CATCH asm ("nop\n" \

"\t.l_end:\n" \ "\tnop\n" \ );

everything between the .l_start and .l_end label is covered by the function __hs_exception_handler.

The interesting part here is the line "\t.rva .l_startw, .l_endw,
_gnu_exception_handler ,.l_endw\n".  The first rva is the
start-address of the first instruction within try-block,
the next is the end-address of the try-block (be aware that it needs
to +1 after end of last instruction in try-block). The third rva is
the handler to be called, and the fourth is the address of the
finally-block.

(NOTE: the \t are only for indenting if viewing the dissassembly generated from gcc with -S)

---

Implementation wise, a few things make it difficult: 1) the function real_main() in RtsMain is decorated with __noreturn__, this makes GCC perform dead code elimination and remove the END_CATCH, the linker will then refuse to link as the .l_end label will be missing.

Instead moving the BEGIN_CATCH and END_CATCH blocks inside the real_main to appropriate positions allows it all to work.

---

I have the changes implemented but the handler is not being called for some reason. I will figure out what's wrong and create a patch. I'm concerned that when linking, the .l_end label is put in a position that does not cover the user code, in which case this implementation would probably get more complex

comment:11 Changed 3 years ago by Phyx-

Architecture: x86_64 (amd64)Unknown/Multiple
Differential Rev(s): Phab:691
Status: newpatch

Implementation has been mostly rewritten and a more uniform approach used for both x64 and x86. This also allowed the removal of a lot of the build hacks for windows and allows RtsMain to be able to be built with optimizations again.

See Phrabricator differential for more information.

comment:12 Changed 3 years ago by Phyx-

Differential Rev(s): Phab:691Phab:D691

comment:13 Changed 3 years ago by Austin Seipp <austin@…>

In 5200bdeb26c5ec98739b14b10fc8907296bceeb9/ghc:

Replaced SEH handles with VEH handlers which should work uniformly across x86 and x64

Summary:
On Windows, the default action for things like division by zero and
segfaults is to pop up a Dr. Watson error reporting dialog if the exception
is unhandled by the user code.

This is a pain when we are SSHed into a Windows machine, or when we
want to debug a problem with gdb (gdb will get a first and second chance to
handle the exception, but if it doesn't the pop-up will show).

veh_excn provides two macros, `BEGIN_CATCH` and `END_CATCH`, which
will catch such exceptions in the entire process and die by
printing a message and calling `stg_exit(1)`.

Previously this code was handled using SEH (Structured Exception Handlers)
however each compiler and platform have different ways of dealing with SEH.

`MSVC` compilers have the keywords `__try`, `__catch` and `__except` to have the
compiler generate the appropriate SEH handler code for you.

`MinGW` compilers have no such keywords and require you to manually set the
SEH Handlers, however because SEH is implemented differently in x86 and x64
the methods to use them in GCC differs.

`x86`: SEH is based on the stack, the SEH handlers are available at `FS[0]`.
     On startup one would only need to add a new handler there. This has
     a number of issues such as hard to share handlers and it can be exploited.

`x64`: In order to fix the issues with the way SEH worked in x86, on x64 SEH handlers
     are statically compiled and added to the .pdata section by the compiler.
     Instead of being thread global they can now be Image global since you have to
     specify the `RVA` of the region of code that the handlers govern.

You can on x64 Dynamically allocate SEH handlers, but it seems that (based on
experimentation and it's very under-documented) that the dynamic calls cannot override
static SEH handlers in the .pdata section.

Because of this and because GHC no longer needs to support < windows XP, the better
alternative for handling errors would be using the in XP introduced VEH.

The bonus is because VEH (Vectored Exception Handler) are a runtime construct the API
is the same for both x86 and x64 (note that the Context object does contain CPU specific
structures) and the calls are the same cross compilers. Which means this file can be
simplified quite a bit.
Using VEH also means we don't have to worry about the dynamic code generated by GHCi.

Test Plan:
Prior to this diff the tests for `derefnull` and `divbyzero` seem to have been disabled for windows.
To reproduce the issue on x64:
1) open ghci
2) import GHC.Base
3) run: 1 `divInt` 0

which should lead to ghci crashing an a watson error box displaying.

After applying the patch, run:

make TEST="derefnull divbyzero"

on both x64 and x86 builds of ghc to verify fix.

Reviewers: simonmar, austin

Reviewed By: austin

Subscribers: thomie

Differential Revision: https://phabricator.haskell.org/D691

GHC Trac Issues: #6079

comment:14 Changed 3 years ago by Phyx-

Resolution: fixed
Status: patchclosed

comment:15 Changed 3 years ago by Phyx-

comment:16 Changed 3 years ago by Phyx-

comment:17 Changed 2 years ago by thoughtpolice

Milestone: 7.12.18.0.1

Milestone renamed

Note: See TracTickets for help on using tickets.