[lldb] Improve mid-function epilogue scanning for x86 (#110965) #9376
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The x86 assembly instruction scanner creates incorrect UnwindPlans when a mid-function epilogue has a non-epilogue instruction in it.
The x86 instruction analysis which creates an UnwindPlan handles mid-function epilogues by tracking "epilogue instructions" (register loads from stack, stack pointer increasing, etc) and any UnwindPlan updates which are NOT epilogue instructions update the "prologue UnwindPlan" saved row. It detects a LEAVE/RET/unconditional JMP out of the function and after that instruction, re-instates the "prologue Row".
There's a parallel piece of data tracked across the duration of the function, current_sp_bytes_offset_from_fa, and we reflect the "value after prologue instructions" in
prologue_completed_sp_bytes_offset_from_cfa. When the CFA is calculated in terms of the frame pointer ($ebp/$rbp), we don't add changes to the stack pointer to the UnwindPlan, so this separate mechanism is used for the "current value" and the "last value at prologue setup".
(the x86 UnwindPlan generated writes "sp=CFA+0" as a register rule which is formally correct, but it could also track the stack pointer value as sp=$rsp+ and update this register rule every time $rsp is modified.)
This leads to a bug when there is an instruction in an epilogue which isn't recognzied as an epilogue instruction.
prologue_completed_sp_bytes_offset_from_cfa is always set to the value of current_sp_bytes_offset_from_fa unless the current instruction is an epilogue instruction. With a non-epilogue instruction in the middle of the epilogue, we suddenly copy a current_sp_bytes_offset_from_fa value from the middle of the epilogue into this
prologue_completed_sp_bytes_offset_from_cfa. Once the epilogue is finished, we restore the "prologue Row" and
prologue_completed_sp_bytes_offset_from_cfa. But now $rsp has a very incorrect value in it.
This patch tracks when we've updated current_sp_bytes_offset_from_fa in the current instruction analysis. If it was updated looking at an epilogue instruction,
is_epilogue
will be set correctly. Otherwise it's a "prologue" instruction and we should update prologue_completed_sp_bytes_offset_from_cfa. Any instruction that is unrecognized will leave prologue_completed_sp_bytes_offset_from_cfa unmodified.The actual instruction we hit this with was a BTRQ but I added a NOP to the unit test which is only 1 byte and made the update to the unit test a little simpler. This bug is hit with a NOP just as well.
UnwindAssemblyInstEmulation has a much better algorithm for handling mid-function epilogues, which "forward" the current unwind state Row when it sees branches within the function, to the target instruction offset. This avoids detecting prologue/epilogue instructions altogether.
rdar://137153323
(cherry picked from commit 00c1989)