GCC Improvements on Windows

Google Summer of Code 2008 Project

Student: Aaron W. LaFramboise

Mentor: Steven Bosscher

Abstract

The goal of this proposal is to improve the usability of GCC on the Microsoft Windows targets of GCC, primarily mingw32 and mingw64. There are three challenges facing these Windows targets.

To this end, a number of small projects have been identified, each of a research and implementation time of between a week and a month. Completion of these projects will cause a dramatic increase in the overall usability of the compiler on Windows.

  1. Dwarf2 exception propagation between DLLs and across unknown frames
  2. Thread and module constructors and destructors
  3. DLL-ification of GCC libraries
  4. Better dynamic symbol support
  5. Modernize Windows platform support
  6. Tool support or improvements for gcov, OpenMP, and possibly mudflap

Status

Last updated: February 27, 2009: The project ended in August of 2008.

For any inquiries regarding work I did on this project, please contact me by email: aaronavay62@aaronwl.com . If you're interested in improvements related to any of the below, I'd be happy to share with you anything I know. I am continuing my involvement in GCC; however, due to time pressure from university, my research assistant position, and my GCC-related duties for MinGW, the amount of time I have to spend on GCC improvements is limited. I believe the best people to talk to at present to get a feel for what is happening with GCC on Windows are Danny Smith, Kai Tietz, and Dave Korn.

The best place to collaborate on GCC-related issues is the WindowsGCCImprovements page and the GCC mailing list. I'll be moving materials to that page as times allows.

Component Completion

Component

Status

Notes

1. Exceptions across foreign frames

Incomplete

A large amount of work was spent here, but there are hard problems that need more work to resolve. I wrote a few prototype unwinders, which I intended to submit, but they turned out to have problems that I have not yet resolved. I will continue working on this issue.

1. Exceptions across DLLs

Partial

Item (3 libgcc_s) resolves this partially; a later subproject will extend this to non-libgcc_s cases as discussed with KT and VR

2. Exception thread cleanup

Submitted

3. DLL: libgcc_s

Submitted

3. DLL: libstdc++

Testing

Making general classes with dllexport is something that still needs to be examined

3. DLL: libjava/libgcj/libgij/libffi

Testing

3. DLL: libgfortran

Incomplete

Something is broken here

3. DLL: libobjc

Testing

3. DLL: libgomp

Submitted

3. DLL: libssp

Submitted

4. Dynamic symbols

Cancelled

This was the most vague part of the original proposal, and later in the project, it became clear that there wouldn't be time to explore this component.

5. Modernize

No GCC changes

xxx

6. Tool: gcov

Submitted

6. Tool: OpenMP

OK

The current OpenMP situation with libgomp/pthreads-win32 is satisfactory.

6. Tool: libmudflap

Cancelled

Other Activities

Opened PRs: 36207, 36218

Resolved PRs: 36968

Other PRs involved in: 25502, 36057, 36654, 37094

I've worked on miscellaneous issues to fix minor bugs. I've submitted about three minor patches for these to gcc-patches@, and more are forthcoming when I get a chance. Also, I've written some documentation, organizing the existing Windows information on the Wiki, and adding some more; and I've updated the target-specific build instructions.

Midterm: July 7, 2008

Status at midterm: I haven't produced very much code. The background research and fiddling time has turned out to be massively more time-consuming than I anticipated, and I've been overly optimistic about the rate at which I can work.

About two thirds of my time so far has been spent trying to figure out how to improve exceptions on Win32 and Win64.

I've found belatedly thats whats really needed to resolve the exception issues on Win32 and Win64 is a partial SEH implementation--nontrivial in the latter case (for Win64).

Planning Information

This was the original plan, but unfortunately I didn't end up following very closely. The initial issues with foreign frames and exceptions ended up dragging on for some time.

gantt20080603.gif

Important Dates

Problem Issues

General Information

For general GCC on Windows information, see Windows.

SEH Support

Since some components of my project intersect with SEH support, it's helpful to lay out the independent subcomponents of such support.

Component

Present Support

Notes

1. Throwing SEH exceptions from GCC frames

RaiseException() works

RaiseException is not really acceptable in user-level code, being more of a system-level interface. What's needed is an equivalent of _try that will let us throw an actual language object.

2. Properly propagating SEH exceptions through a GCC frame

None

Two possibilities: 1. Generate SEH frames in every function with destructors. This negates the performance benefits of DW2. 2. Do something special to the frame before calling functions that might throw SEH exceptions. This would probably be some sort of annotation on the called function. The callee would then catch any SEH exceptions, and unwind all DW2 frames up to the next foreign frame.

3. Catching SEH exceptions in a GCC frame

MinGW <expt.h> provides limited support, but is experimental and broken

Installing and removing a SEH handler is relatively easy, and doesn't even require compiler-level support; it does require a write to the TIB, which needs inline assembler at least. Translations to a GCC language object type will require some special mechanism. Microsoft's compiler has various semi-documented mechanisms designed to do these sort of translations, both with an explicit translator and implicit translation to the same object type that was thrown.

4. try/catch use SEH instead of DW2/SJLJ

None

This would be a third exception mode, independent of SJLJ and DW2. There's really no reason for try to support more than one of these, as SEH negates the performance benefit of DW2, and is essentially the same thing as SJLJ.

5. Unwinding foreign frames using SEH when throwing with DW2/SJLJ

None

This is part of my project; We need this to be able to throw out of a Win32 event handler to the top level of an application. See below for more details.

6. Use SEH for -ftrapv

Unknown

I'm not sure how this works at present; it's possible this already does the right thing.

7. -fasynchronous-unwind-tables support for SEH

Unknown

I'm not exactly sure how this would work, or if it even needs anything beyond the above. It's definitely desirable though, because Microsoft's compiler can do the equivalent.

8. llama

X

Y

9. llama

X

Y

Foreign Frames

On 32-bit Windows, developers have been accustomed to throwing through various callbacks, most notably DispatchMessage in the main event loop. Strictly speaking, throwing through foreign frames is not valid, because it can put data associated with caller frames into an inconsistent state. I've discovered two instances in Microsoft documentation that says that throwing outside of local code is not allowed, and throwing through DispatchMessage() in particular crashed Win16 and is silently discarded in WinCE 5.0 for Palm. Nonetheless, its obvious that some sort of way SEH exceptions need to be able to get through DispatchMessage(), if only to propagate hardware faults to the top level.

With MS exceptions, this works just fine, unwinding SEH frames along side C++ exceptions if the proper mode is enabled. With GCC SJLJ, unwinding would work; somewhat vacuously, because SJLJ will unwind through pretty much anything as long as the SJLJ chain is intact. DispatchMessage appears to be quite resilient to this sort of unwinding, even though its probably not the right thing.

The goal is to make DW2 be able to propagate safely through some foreign frames, where the programmer knows it is safe, and make it somewhat safer by unwinding SEH mode at the same time, so its not any worse than propagating a normal SEH exception. A general solution is not possible, because there's no way to know a posteri where a foreign frame may have stashed the callee-saved registers, and we don't want to disrupt the zero-cost nature of DW2 in the general case by storing overzealously.

The compromise here is to allow the user to create special source-level annotations to allow exceptions to propagate through foreign frames higher up the call stack. The annotation is actually a SEH registration record, which indicates that this frame is a safe landing pad. When the GCC unwinder is unable to unwind by normal means, the fallback handler will invoked. The fallback handler will search the SEH chain for any of these special registration records, and generate the proper frame unwind information to allow the unwinder to unwind to it.

Registration record: There are two ways to make the landing pad safe. One is to force all registers to be clobbered over the call. This is very easy, but may have a performance cost; however, in the DispatchMessage() case, the penalty is probably minimal. (Note: I need to benchmark this to be sure.) This is the approach I've taken for now.

The better approach is to save the callee-saved registers, and only restore them when we need them; this has a much smaller performance cost, as the registers aren't actually invalidated, so this won't inhibit optimization, require memory reads, or stall the pipeline. To support future compatibility for this, the registration records set aside space for this, and the unwinder will restore them. Actually getting this to work right requires a special function decorator which is not yet implemented, but since this is only a performance issue, this can be implemented later. The present version will still restore the registers on unwind, but since they are not actually being saved, this doesn't really do anything; but it doesn't hurt either.

Intermediate SEH frames: We want to properly unwind these as well. However, this is a little bit harder. The way to do it is to create a separate unwind frame for each SEH registration, with a special personality routine that will support unwinding it. This should be straightforward as long as GCC is properly conforming to the Itanium ABI.

I have not done this yet; while unwinding SEH frames is a design goal, leaving this out is not any worse than the SJLJ situation was.

SEH Generally: MinGW has a special experimental implemention of library-level SEH in <excpt.h>. Unfortunately, it appears to be somewhat broken. However, it should to generalize the above landing pad annotations to general SEH handlers; combined with the above SEH support, this will give some measure of SEH support to GCC. Future work could turn these library-level primatives into language-level primatives for source-level compatibility with MSVC.

Constructors

What I've done is extend the Itanium ABI "DSO Object Destruction API" to also deliver thread termination information, yielding a general compiler-level atexit implementation that can give information about both module loading/unloading, thread creation/deletion, and process creation/deletion. The primary consumer of this information is the GCC exception machinery, which needs to know about these events for proper exception maintenance. The old mechanism has been revised to use the new API. This also changes mingw-runtime, as mingwm10.dll is no longer needed.

The primary blocking issue is that binutils doesn't support the TLS constructor information, but this is a minor issue to add. If a shared libgcc_s is used, this is not needed, as the DllMain from libgcc_s can provide the proper information.

Exceptions across DLL

Eliminating the need for a shared libgcc_s is obviously desirable here. With the above ctor support, the shared libgcc_s addresses all known issues. Without libgcc_s, we need the equivalent of .eh_frame in ELF for PECOFF. This is easy to do, but we need some binutils support. I'm going to work on this binutils support this week.

DLLification

For some simple libraries, this was a matter of enabling the proper automake/libtool machinery, and fixing any resultant problems. For others, more extensive work is needed.

References

Dwarf-2 Exceptions

Itanium C++ ABI: Exception Handling: Formal specification of how the Dwarf2-based exception mechanism is supposed to work

Dwarf2 Exception Handler HOWTO: Description of GCC's Dwarf2 unwinding mechanism

C++ Exception Handling for IA-64: Description of HP-UX's similar table-based unwinding mechanism

SEH Exceptions

A Crash Course on the Depths of Win32™ Structured Exception Handling: Overview of SEH internals

Reversing Microsoft Visual C++ Part I: Exception Handling: Detailed information on the stack frame layout when SEH is used

The Exception Model: Interactions between SEH and other frameworks

Structured Exception Handling Reference: Reference documentation to some SEH functions; this is the only documented part of SEH.

Underneath Structured Exceptions & C++ Exceptions: Description of Borland's SEH mechanism, precursor to Microsoft's implementation, with many obvious similarities.

ABI

http://www.codesourcery.com/cxx-abi/abi.html

Thread Information Block

Note: The TIB is also occasionally called the TEB, Thread Environment Block, in the context of Windows NT.

Under the Hood, May 1996: A general overview of the TIB and useful SHOWTIB program

Win32 Thread Information Block: A fairly complete reference of the undocumented TIB structure

None: WindowsGCCImprovementsGSoC2008 (last edited 2009-02-27 09:06:51 by AaronLaFramboise)