-
Notifications
You must be signed in to change notification settings - Fork 119
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
Exception handling on Windows causing access violation with ARC #222
Comments
Normally I'd debug this kind of thing in WinDbg using its time-travel mode: get to the crash, look where the pointer to the faulting address came from, and then run in reverse with a watchpoint set to that address and see where it happened. It may be that we're failing to retain / release over exception throwing (though I thought the compiler did that in ARC mode?), but if you can find out what 0x00007FFA9FC25280 used to point to with the time-travel debugger then you can see where it was deallocated. |
That sounds like a good plan. Unfortunately I haven’t really used WinDbg before and are not really getting anywhere with it... For starters, could you tell me how to find out where the pointer to the faulting address came from? I used this tutorial as guideline and are getting a list of events that shows the C++ exception that is thrown directly followed by the access violation exception. It would be great if you could give me some pointers where to go from there. One interesting tidbit maybe is that on x86 the faulting address is always 0x100:
Whereas on x64 it’s always some actual memory address:
|
Unwinding ObjC code with automatic reference counting involves calls to intrinsics like `llvm.obj.retain` and `llvm.objc.destroyWeak`. Exception handling on Windows is based on funclets and WinEHPrepare only allows calls to `nounwind` intrinsics. This works just fine, except for ObjC `nounwind` intrinsics, because these are lowered into regular function calls in an earlier stage already (i.e. PreISelIntrinsicLoweringPass). Thus, WinEHPrepare accidentally drops them as implausible instructions and silently truncates certain funclets. Eventually, this causes crashes during unwinding on Windows with the GNUstep ObjC runtime: gnustep/libobjc2#222 This patch forces the emission of a "funclet" bundle operand for calls to ObjC ARC intrinsics during codegen and lets PreISelIntrinsicLowering carry it over onto lowered replacement calls. This way all ObjC ARC calls survive WinEHPrepare through the FuncletBundleOperand check. The latter had to be adjusted in order to allow funclet pads to get optimized away. Differential Revision: https://reviews.llvm.org/D124762
…er to function calls WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic and has no proper funclet bundle operand. This affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222 This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs. * Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand. * PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers. * LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites. Differential Revision: https://reviews.llvm.org/D128190
…er to function calls in the course of IR transforms WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic or has no proper funclet bundle operand. This affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222 This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs. * Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand. * PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers. * LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites. Reviewed By: theraven Differential Revision: https://reviews.llvm.org/D128190
We got this fixed in Clang 15: |
Known issues: #7 llvm/llvm-project#43828 llvm/llvm-project#51899 llvm/llvm-project#54556 llvm/llvm-project#56952 gnustep/libobjc2#222 Also removed copy about GNUstep test failures, as they have been fixed.
I was about to propose a change that enabled the test on Windows when using Clang 15+ as the host compiler, when I realized that it still crashes! The nested try-catch in this test actually triggers another problem that was not covered in my fix from https://reviews.llvm.org/D128190 |
Review for a fix/workaround candidate: https://reviews.llvm.org/D134866 |
I was working on a PR to enable the ObjCXXEHInterop_arc tests on Windows in Clang 15+ when I realized that there is a second side-effect that we didn't fix with the above patch. It does affect the test here, so I didn't sent the PR yet. More info and the start of what may become a more general discussion on the concepts behind WinEH: https://reviews.llvm.org/D134866 |
…er to function calls in the course of IR transforms WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic or has no proper funclet bundle operand. This affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222 This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs. * Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand. * PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers. * LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites. Reviewed By: theraven Differential Revision: https://reviews.llvm.org/D128190
This was fully fixed in Clang 16. |
…er to function calls in the course of IR transforms WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic or has no proper funclet bundle operand. This affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222 This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs. * Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand. * PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers. * LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites. Reviewed By: theraven Differential Revision: https://reviews.llvm.org/D128190
Throwing/catching an exception in ARC-enabled code on Windows causes an access violation. I’ve reproduced this in various environments/projects and both x64 and x86 with the same results. While debug builds will usually crash almost immediately, release builds can sometimes continue execution for a bit before crashing.
I also tried using Application Verifier and Page Heap to find the cause, but unfortunately I never got any more useful info. The stack trace is usually corrupt and/or unhelpful.
This can for example be reproduced with the ObjCXXEHInterop_arc test, which is currently disabled for Windows due to the failure:
Any thoughts on how to debug this further would be appreciated.
The text was updated successfully, but these errors were encountered: