-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Remove 'set frame pointer' unwind code from Windows x64 unwind. #1983
Conversation
This commit removes the "set frame pointer" unwind code and frame pointer information from Windows x64 unwind information. In Windows x64 unwind information, a "frame pointer" is actually the *base address* of the static part of the local frame and would be at some negative offset to RSP upon establishing the frame pointer. Currently Cranelift uses a "traditional" notion of a frame pointer, one that is the highest address in the local frame (i.e. pointing at the previous frame pointer on the stack). Windows x64 unwind doesn't describe such frame pointers and only needs one described if the frame contains a dynamic stack allocation. Fixes bytecodealliance#1967.
This commit adds a simple test case that reproduces the problem in
ece20a3
to
b391817
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice find on this, looks like some gnarly debugging to get here! I'm not super familiar with Windows unwinding though so I'd defer to @iximeow's review as well.
@iximeow the gist of the problem is that a "frame pointer" in Win64 unwind information should point at the base of the static part of the local frame and not what we discussed earlier when you originally implemented the FPR saves for Windows; that being a "normal" frame pointer with the unwind information describing how to offset it to get that base address for unwinding only. Windows actually expects the "frame pointer" to be offset from RSP when established and Cranelift doesn't do that. To eliminate the potential for confusion regarding what a "frame pointer" is, I've opted to simply remove encoding the "set frame pointer" unwind code in the unwind information for now. In the future, if we ever need to support dynamically sized frames, we can add it back in the right way (i.e. the frame pointer offset should be discovered via the establishing instruction and not based on the register saves). |
Subscribe to Label Actioncc @bnjbvr
This issue or pull request has been labeled: "cranelift"
Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
I'm pretty sure I understand how this fits in with Windows x6_64 unwinding generally, but I want to repeat my understanding back to make sure we're on the same page: this change doesn't interact with saved general-purpose registers because they were never frame pointer-relative in the first place. Offsets for FPR-saves were correct all along, but by setting a frame pointer we instructed the Windows unwinder to treat them as offsets an incorrect location, where things go sideways?
So by not setting an explicit frame pointer, the Windows unwinder assumes the frame base is RSP at function entry, and everything is relative to that?
That's correct. Windows doesn't need a traditional frame pointer at all to figure out where the frame starts because every function that modifies the SP, in any way, must have unwind information that describes the adjustments. For frames that can't describe that adjustment statically (e.g. a call to We were calculating the "frame pointer" and describing it correctly in the unwind information, but Cranelift establishes the frame pointer as one would one expect: always using the start (highest address) of the local frame. I thought that the Windows unwind information also expected this traditional notion of frame pointer and the encoded offset would be subtracted from the frame pointer during unwinding to determine the base address that FPR saves are relative to. Instead it was actually the opposite: the unwinder would add the offset to the frame pointer to find the top of the frame. This obviously caused bad stack walks during unwind, not to mention it restored the saved FPRs from the wrong addresses as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that the Windows unwind information also expected this traditional notion of frame pointer and the encoded offset would be subtracted from the frame pointer during unwinding to determine the base address that FPR saves are relative to.
Needless to say, I'd thought this too :) Thank you for figuring this out, and the test to guard against breaking this in the future.
I just noticed a subtle bug in how we're encoding I'll push up a fix. |
The `SaveXmm128Far` unwind op should have a 32-bit unscaled value. The value was accidentally scaled down by 16 while calculating whether or not the `SaveXmm128` or `SaveXmm128Far` unwind op should be used.
That was an impressively fast fix for such a complex issue! Thanks everyone. |
Pure speed! Thanks heaps for getting this in main so quick! |
This PR removes the "set frame pointer" unwind code and frame
pointer information from Windows x64 unwind information.
In Windows x64 unwind information, a "frame pointer" is actually the
base address of the static part of the local frame and would be at some
negative offset to RSP upon establishing the frame pointer.
Currently Cranelift uses a "traditional" notion of a frame pointer, one
that is the highest address in the local frame (i.e. pointing at the
previous frame pointer on the stack).
Windows x64 unwind doesn't describe such frame pointers and only needs
one described if the frame contains a dynamic stack allocation.
Fixes #1967.