Skip to content

Commit 554f4d1

Browse files
authored
[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
1 parent 61907eb commit 554f4d1

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

lldb/source/Target/Process.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -5080,7 +5080,13 @@ Process::RunThreadPlan(ExecutionContext &exe_ctx,
50805080
return eExpressionSetupError;
50815081
}
50825082

5083-
StackID ctx_frame_id = selected_frame_sp->GetStackID();
5083+
// If the ExecutionContext has a frame, we want to make sure to save/restore
5084+
// that frame into exe_ctx. This can happen when we run expressions from a
5085+
// non-selected SBFrame, in which case we don't want some thread-plan
5086+
// to overwrite the ExecutionContext frame.
5087+
StackID ctx_frame_id = exe_ctx.HasFrameScope()
5088+
? exe_ctx.GetFrameRef().GetStackID()
5089+
: selected_frame_sp->GetStackID();
50845090

50855091
// N.B. Running the target may unset the currently selected thread and frame.
50865092
// We don't want to do that either, so we should arrange to reset them as
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
6+
7+
class ExprFromNonZeroFrame(TestBase):
8+
NO_DEBUG_INFO_TESTCASE = True
9+
10+
def test(self):
11+
"""
12+
Tests that we can use SBFrame::EvaluateExpression on a frame
13+
that we're not stopped in, even if thread-plans run as part of
14+
parsing the expression (e.g., when running static initializers).
15+
"""
16+
self.build()
17+
18+
(_, _, thread, _) = lldbutil.run_to_source_breakpoint(
19+
self, "Break here", lldb.SBFileSpec("main.c")
20+
)
21+
frame = thread.GetFrameAtIndex(1)
22+
23+
# Using a function pointer inside the expression ensures we
24+
# emit a ptrauth static initializer on arm64e into the JITted
25+
# expression. The thread-plan that runs for this static
26+
# initializer should save/restore the current execution context
27+
# frame (which in this test is frame #1).
28+
result = frame.EvaluateExpression("int (*fptr)() = &func; fptr()")
29+
self.assertTrue(result.GetError().Success())
30+
self.assertEqual(result.GetValueAsSigned(), 5)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
int func(void) {
2+
__builtin_printf("Break here");
3+
return 5;
4+
}
5+
6+
int main(int argc, const char *argv[]) { return func(); }

0 commit comments

Comments
 (0)