llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Charles Zablit (charles-zablit)

<details>
<summary>Changes</summary>

`ABIWindows_x86_64::CreateDefaultUnwindPlan()` uses a "CFA = rbp + 16" as a 
placeholder, however, `Windows-x86_64` does not use rbp as a frame pointer. 
With this bogus rbp based CFA, the unwinder could not produce the caller frame, 
so stepping out of such a function fails with "Could not create return address 
breakpoint".

This patch uses the `Windows-x86_64` correct rule for the fallback: [the return 
address is at the top of the 
stack](https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions?view=msvc-170),
 so `CFA = rsp + 8` and `pc = [CFA - 8]`.

 This patch also ports `lang/c/trampoline_stepping` from Swiftlang.

---
Full diff: https://github.com/llvm/llvm-project/pull/210076.diff


4 Files Affected:

- (modified) lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp (+2-8) 
- (added) lldb/test/API/lang/c/trampoline_stepping/Makefile (+3) 
- (added) lldb/test/API/lang/c/trampoline_stepping/TestTrampolineStepping.py 
(+100) 
- (added) lldb/test/API/lang/c/trampoline_stepping/main.c (+52) 


``````````diff
diff --git a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp 
b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp
index 079b22a307602..be3c83880bd60 100644
--- a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp
+++ b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp
@@ -751,21 +751,15 @@ UnwindPlanSP 
ABIWindows_x86_64::CreateFunctionEntryUnwindPlan() {
 
 // Windows-x86_64 doesn't use %rbp
 // No available Unwind information for Windows-x86_64 (section .pdata)
-// Let's use SysV-x86_64 one for now
 UnwindPlanSP ABIWindows_x86_64::CreateDefaultUnwindPlan() {
-  uint32_t fp_reg_num = dwarf_rbp;
   uint32_t sp_reg_num = dwarf_rsp;
   uint32_t pc_reg_num = dwarf_rip;
 
   UnwindPlan::Row row;
-
-  const int32_t ptr_size = 8;
-  row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp, 2 * ptr_size);
   row.SetOffset(0);
   row.SetUnspecifiedRegistersAreUndefined(true);
-
-  row.SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
-  row.SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+  row.GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 8);
+  row.SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false);
   row.SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
 
   auto plan_sp = std::make_shared<UnwindPlan>(eRegisterKindDWARF);
diff --git a/lldb/test/API/lang/c/trampoline_stepping/Makefile 
b/lldb/test/API/lang/c/trampoline_stepping/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/lang/c/trampoline_stepping/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/c/trampoline_stepping/TestTrampolineStepping.py 
b/lldb/test/API/lang/c/trampoline_stepping/TestTrampolineStepping.py
new file mode 100644
index 0000000000000..bbf1dc8cb7fea
--- /dev/null
+++ b/lldb/test/API/lang/c/trampoline_stepping/TestTrampolineStepping.py
@@ -0,0 +1,100 @@
+"""Test that stepping in/out of trampolines works as expected."""
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestTrampoline(TestBase):
+    def setup(self, bkpt_str):
+        self.build()
+
+        _, _, thread, _ = lldbutil.run_to_source_breakpoint(
+            self, bkpt_str, lldb.SBFileSpec("main.c")
+        )
+        return thread
+
+    def test_direct_call(self):
+        thread = self.setup("Break here for direct")
+
+        # Sanity check that we start out in the correct function.
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("direct_trampoline_call", name)
+
+        # Check that stepping in will take us directly to the trampoline 
target.
+        thread.StepInto()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("foo", name)
+
+        # Check that stepping out takes us back to the trampoline caller.
+        thread.StepOut()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("direct_trampoline_call", name)
+
+        # Check that stepping over the end of the trampoline target
+        # takes us back to the trampoline caller.
+        thread.StepInto()
+        thread.StepOver()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("direct_trampoline_call", name)
+
+    def test_chained_call(self):
+        thread = self.setup("Break here for chained")
+
+        # Sanity check that we start out in the correct function.
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("chained_trampoline_call", name)
+
+        # Check that stepping in will take us directly to the trampoline 
target.
+        thread.StepInto()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("foo", name)
+
+        # Check that stepping out takes us back to the trampoline caller.
+        thread.StepOut()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("chained_trampoline_call", name)
+
+        # Check that stepping over the end of the trampoline target
+        # takes us back to the trampoline caller.
+        thread.StepInto()
+        thread.StepOver()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("chained_trampoline_call", name)
+
+    def test_trampoline_after_nodebug(self):
+        thread = self.setup("Break here for nodebug then trampoline")
+
+        # Sanity check that we start out in the correct function.
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("trampoline_after_nodebug", name)
+
+        # Check that stepping in will take us directly to the trampoline 
target.
+        thread.StepInto()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("foo", name)
+
+        # Check that stepping out takes us back to the trampoline caller.
+        thread.StepOut()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("trampoline_after_nodebug", name)
+
+        # Check that stepping over the end of the trampoline target
+        # takes us back to the trampoline caller.
+        thread.StepInto()
+        thread.StepOver()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("trampoline_after_nodebug", name)
+
+    def test_unused_target(self):
+        thread = self.setup("Break here for unused")
+
+        # Sanity check that we start out in the correct function.
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("unused_target", name)
+
+        # Check that stepping into a trampoline that doesn't call its target
+        # jumps back to its caller.
+        thread.StepInto()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn("unused_target", name)
diff --git a/lldb/test/API/lang/c/trampoline_stepping/main.c 
b/lldb/test/API/lang/c/trampoline_stepping/main.c
new file mode 100644
index 0000000000000..cb98be00ca1f6
--- /dev/null
+++ b/lldb/test/API/lang/c/trampoline_stepping/main.c
@@ -0,0 +1,52 @@
+void foo(void) {}
+
+__attribute__((transparent_stepping))
+void bar(void) {
+  foo();
+}
+
+__attribute__((transparent_stepping))
+void baz(void) {
+  bar();
+}
+
+__attribute__((nodebug))
+void nodebug(void) {}
+
+__attribute__((transparent_stepping))
+void nodebug_then_trampoline(void) {
+  nodebug();
+  baz();
+}
+
+__attribute__((transparent_stepping))
+void doesnt_call_trampoline(void) {}
+
+void direct_trampoline_call(void) {
+  bar(); // Break here for direct 
+  bar();
+}
+
+void chained_trampoline_call(void) {
+  baz(); // Break here for chained
+  baz();
+}
+
+void trampoline_after_nodebug(void) {
+  nodebug_then_trampoline(); // Break here for nodebug then trampoline
+  nodebug_then_trampoline();
+}
+
+void unused_target(void) {
+  doesnt_call_trampoline(); // Break here for unused
+}
+
+
+int main(void) {
+  direct_trampoline_call();
+  chained_trampoline_call();
+  trampoline_after_nodebug();
+  unused_target();
+  return 0;
+}
+

``````````

</details>


https://github.com/llvm/llvm-project/pull/210076
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to