diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 9060a43deac31..59ccda22c2739 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -5200,7 +5200,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 diff --git a/lldb/test/API/commands/expression/expr-from-non-zero-frame/Makefile b/lldb/test/API/commands/expression/expr-from-non-zero-frame/Makefile new file mode 100644 index 0000000000000..10495940055b6 --- /dev/null +++ b/lldb/test/API/commands/expression/expr-from-non-zero-frame/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/commands/expression/expr-from-non-zero-frame/TestExprFromNonZeroFrame.py b/lldb/test/API/commands/expression/expr-from-non-zero-frame/TestExprFromNonZeroFrame.py new file mode 100644 index 0000000000000..623c5b87f14c7 --- /dev/null +++ b/lldb/test/API/commands/expression/expr-from-non-zero-frame/TestExprFromNonZeroFrame.py @@ -0,0 +1,30 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class ExprFromNonZeroFrame(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test(self): + """ + Tests that we can use SBFrame::EvaluateExpression on a frame + that we're not stopped in, even if thread-plans run as part of + parsing the expression (e.g., when running static initializers). + """ + self.build() + + (_, _, thread, _) = lldbutil.run_to_source_breakpoint( + self, "return 5", lldb.SBFileSpec("main.c") + ) + frame = thread.GetFrameAtIndex(1) + + # Using a function pointer inside the expression ensures we + # emit a ptrauth static initializer on arm64e into the JITted + # expression. The thread-plan that runs for this static + # initializer should save/restore the current execution context + # frame (which in this test is frame #1). + result = frame.EvaluateExpression("int (*fptr)() = &func; fptr()") + self.assertTrue(result.GetError().Success()) + self.assertEqual(result.GetValueAsSigned(), 5) diff --git a/lldb/test/API/commands/expression/expr-from-non-zero-frame/main.c b/lldb/test/API/commands/expression/expr-from-non-zero-frame/main.c new file mode 100644 index 0000000000000..abd52aeeb5b0b --- /dev/null +++ b/lldb/test/API/commands/expression/expr-from-non-zero-frame/main.c @@ -0,0 +1,3 @@ +int func(void) { return 5; } + +int main(int argc, const char *argv[]) { return func(); }