[lldb][Target] RunThreadPlan to save/restore the ExecutionContext's frame if one exists (#134097)

When using `SBFrame::EvaluateExpression` on a frame that's not the
currently selected frame, we would sometimes run into errors such as:
```
error: error: The context has changed before we could JIT the expression!
error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression
```

During expression parsing, we call `RunStaticInitializers`. On our
internal fork this happens quite frequently because any usage of, e.g.,
function pointers, will inject ptrauth fixup code into the expression.
The static initializers are run using `RunThreadPlan`. The
`ExecutionContext::m_frame_sp` going into the `RunThreadPlan` is the
`SBFrame` that we called `EvaluateExpression` on. LLDB then tries to
save this frame to restore it after the thread-plan ran (the restore
occurs by unconditionally overwriting whatever is in
`ExecutionContext::m_frame_sp`). However, if the `selected_frame_sp` is
not the same as the `SBFrame`, then `RunThreadPlan` would set the
`ExecutionContext`'s frame to a different frame than what we started
with. When we `PrepareToExecuteJITExpression`, LLDB checks whether the
`ExecutionContext` frame changed from when we initially
`EvaluateExpression`, and if did, bails out with the error above.

One such test-case is attached. This currently passes regardless of the
fix because our ptrauth static initializers code isn't upstream yet. But
the plan is to upstream it soon.

This patch addresses the issue by saving/restoring the frame of the
incoming `ExecutionContext`, if such frame exists. Otherwise, fall back
to using the selected frame.

rdar://147456589
This commit is contained in:
Michael Buch
2025-04-03 11:10:16 +01:00
committed by GitHub
parent 61907ebd76
commit 554f4d1a57
4 changed files with 46 additions and 1 deletions

View File

@@ -5080,7 +5080,13 @@ Process::RunThreadPlan(ExecutionContext &exe_ctx,
return eExpressionSetupError;
}
StackID ctx_frame_id = selected_frame_sp->GetStackID();
// If the ExecutionContext has a frame, we want to make sure to save/restore
// that frame into exe_ctx. This can happen when we run expressions from a
// non-selected SBFrame, in which case we don't want some thread-plan
// to overwrite the ExecutionContext frame.
StackID ctx_frame_id = exe_ctx.HasFrameScope()
? exe_ctx.GetFrameRef().GetStackID()
: selected_frame_sp->GetStackID();
// N.B. Running the target may unset the currently selected thread and frame.
// We don't want to do that either, so we should arrange to reset them as