Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Itanium Unwind ABI in place of HP's #4190

Closed
jsonn opened this issue Apr 28, 2015 · 13 comments
Closed

Use Itanium Unwind ABI in place of HP's #4190

jsonn opened this issue Apr 28, 2015 · 13 comments
Labels
area-VM-coreclr question Answer questions and provide assistance, not an issue with source code or documentation.
Milestone

Comments

@jsonn
Copy link

jsonn commented Apr 28, 2015

Exception Handling on ELF platforms (except ARM) and on Mac OSX is based on the Itanium ABI. From a cursory look, it should be possible to implement the EH dispatch directly on top without having to use the non-standard HP interface. Am I missing something fundamental?

@MattWhilden
Copy link
Contributor

/cc @jkotas @sergiy-k

@jkotas
Copy link
Member

jkotas commented Apr 28, 2015

/cc @janvorli

The current EH design built upon the existing (Windows) code as much as possible, to avoid introducing unnecessary complexity. Check the design discussions in #3942 and #3946.

Implementing the EH differently is certainly possible, but it is unlikely to be an improvement. Alternative measurably better implementations are welcomed to prove me wrong.

@janvorli
Copy link
Member

@jsonn - what do you mean by the HP interface? The low level libunwind? We need that one for stack walking over native frames and its use in the exception handling itself is to unwind to the first managed frame. We use it for stack walking for GC purposes too.
The problem with the higher level unwind functions is that you cannot start the unwind at an arbitrary frame. We need to scan stacks of other threads too.

@jsonn
Copy link
Author

jsonn commented Apr 29, 2015

"Low" and "high" level is a bit misleading as the interfaces (libunwind.h vs unwind.h) are pretty much orthogonal. libunwind.h is the HP interface, unwind.h is standard Itanium ABI. If all you want to do is stack walking, _Unwind_Backtrace is a wide supported extension of the plain Itanium ABI to just do a forced unwind. The same trick with adjusting the context with known registers can be used here.

What I don't understand is how the current code is supposed to interact with foreign exception handlers, i.e. if the call stack contains C++ code.

@jkotas
Copy link
Member

jkotas commented Apr 29, 2015

We use libunwind to walk the stack for GC (and other purposes). We do not use to run the actual handlers.

Does the Itanium ABI have equivalent of unw_get_save_loc?

@janvorli
Copy link
Member

@jkotas No, it doesn't have that.
@jsonn The Itanium ABI implementation on OSX and in the clang libc++ is built on top of the other one. That's why I was referring to it as "lower level API". I don't know how that's done in the GCC's libstdc++ library though.

What I don't understand is how the current code is supposed to interact with foreign exception handlers, i.e. if the call stack contains C++ code.

We are not using the API for exception unwinding itself. In the exception handling, we just use it to scan the stack to find the first managed frame. The exception unwinding of native frames on the call stack is performed just by throwing C++ exception and catching it at the last native frame before a managed frame or possibly in the native code itself.

@jsonn
Copy link
Author

jsonn commented Apr 29, 2015

But you don't run exception handlers if there is any non-CLR frame between the top of the stack and the first managed frame, right? Or is that case impossible?

unw_get_save_loc is currently not supported by LLVM (and Apple's) unwind implementation. I'm not sure how it can work reliable. I believe it is valid for one frame explicitly restore the register if it guarantees that all exceptions are handled. But this is a topic that should be brought up on the LLVM lists for input from other involved parties. It would seem to me that the ability to intercept the return to a frame would provide a more reliable mechanism. It would still require extending the interface for that, but it seems to be a better primitive.

We explicitly don't support the libunwind.h interface in NetBSD as it exposes interfaces in a bad way. The size of the unwind context needs to be statically sized large enough to hold all possible target state, which is a bad idea for long term maintenance. There are other flaws in the interface making it problematic. That said, I am strongly interested in finding and fixing issues with the Itanium ABI.

@janvorli
Copy link
Member

But you don't run exception handlers if there is any non-CLR frame between the top of the stack and the first managed frame, right? Or is that case impossible?

No, we don't. The native frames that are there are always frames stemming from a catch handler of managed exception thrown as PAL_SEHException by coreclr (which includes IL_Throw / IL_Rethrow helpers that are called when managed code throws / rethrows an exception). So we know we can just skip such frames without performing full exception unwinding.

Regarding the unw_get_save_loc can you please be more specific on why you are not sure how it can be made reliable? Do you mean that in the context of the actual implementation in LLVM / Apple's library?
To give you some more detail, we need to track locations of callee saved registers that contain pointers to GC objects so that we can update them in place on the stack when GC decides to move that object. Otherwise when the code returns to the place where the register is restored from the stack location, it would contain a pointer that is no longer valid.
On OSX where the libunwind doesn't support the unw_get_save_loc, we workaround this issue by pinning the objects that are pointed to by those registers, but it obviously is not ideal.

@jsonn
Copy link
Author

jsonn commented Apr 29, 2015

How do you ensure that the spilling is really annotated? I mean I can write an assembler function with a catch all block and spill/restore the registers manually without adding .cfi annotation. That would still be correct for normal EH operation.

@jsonn
Copy link
Author

jsonn commented Apr 30, 2015

Thinking a bit more about unw_get_save_loc, the biggest issue I see is the combination of one-frame-at-a-time operation with toward-the-bottom approach. While the return address can be reasonably expected to be spilled in the immediate next frame for anything but very special edge cases, general registers don't have that property.

@janvorli
Copy link
Member

@jsonn Regarding your former question, for the GC purposes, we care only for native code in the coreclr. We have all the assembler functions properly annotated. And we spill all callee saved registers to stack in an asm helper when we call external native code.

@krytarowski
Copy link
Contributor

Can we close it?

@jkotas
Copy link
Member

jkotas commented Mar 5, 2016

I think so.

@jkotas jkotas closed this as completed Mar 5, 2016
@msftgits msftgits transferred this issue from dotnet/coreclr Jan 30, 2020
@msftgits msftgits added this to the Future milestone Jan 30, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Jan 6, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-VM-coreclr question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests

6 participants