Skip to content

Conversation

@labath
Copy link
Collaborator

@labath labath commented Jun 10, 2025

This fixes a regression caused by #139817, where we would fail to unwind (using eh_frame) when using debug stubs (like qemu) which do not provide eh_frame register numbers.

Unwinding fails because the unwinder tries to convert pc register number to the "eh_frame" scheme (and fails). Previously that worked because the code used a slightly different pattern which ended up comparing the number by converting it to the "generic" scheme (which we do provide).

I don't think that's a bug in the unwinder -- we just need to make sure we provide the register number all the time.

The associated test is not particularly useful, but I'm hoping it could be used to test other changes like this as well. Other register augmentation changes are tested in an end-to-end fashion (by injecting gdb-remote packets and then trying to read registers). That works well for testing the addition of subregisters, but it isn't particularly suitable for testing register numbers, as the only place (that I know of) where this manifests is during unwinding, and recreating an unwindable process in a test like this is fairly challenging.

Probably the best way to ensure coverage for this scenario would be to have lldb-server stop sending eh_frame register numbers (align it with other gdb-like stubs).

This fixes a regression caused by llvm#139817, where we would fail to unwind
(using eh_frame) when using debug stubs (like qemu) which do not provide
eh_frame register numbers.

Unwinding fails because the unwinder tries to convert pc register number
to the "eh_frame" scheme (and fails). Previously that worked because the
code used a slightly different pattern which ended up comparing the
number by converting it to the "generic" scheme (which we do provide).

I don't think that's a bug in the unwinder -- we just need to make sure
we provide the register number all the time.

The associated test is not particularly useful, but I'm hoping it could
be used to test other changes like this as well. Other register
augmentation changes are tested in an end-to-end fashion (by injecting
gdb-remote packets and then trying to read registers). That works well
for testing the addition of subregisters, but it isn't particularly
suitable for testing register numbers, as the only place (that I know
of) where this manifests is during unwinding, and recreating an
unwindable process in a test like this is fairly challenging.
@llvmbot
Copy link
Member

llvmbot commented Jun 10, 2025

@llvm/pr-subscribers-lldb

Author: Pavel Labath (labath)

Changes

This fixes a regression caused by #139817, where we would fail to unwind (using eh_frame) when using debug stubs (like qemu) which do not provide eh_frame register numbers.

Unwinding fails because the unwinder tries to convert pc register number to the "eh_frame" scheme (and fails). Previously that worked because the code used a slightly different pattern which ended up comparing the number by converting it to the "generic" scheme (which we do provide).

I don't think that's a bug in the unwinder -- we just need to make sure we provide the register number all the time.

The associated test is not particularly useful, but I'm hoping it could be used to test other changes like this as well. Other register augmentation changes are tested in an end-to-end fashion (by injecting gdb-remote packets and then trying to read registers). That works well for testing the addition of subregisters, but it isn't particularly suitable for testing register numbers, as the only place (that I know of) where this manifests is during unwinding, and recreating an unwindable process in a test like this is fairly challenging.

Probably the best way to ensure coverage for this scenario would be to have lldb-server stop sending eh_frame register numbers (align it with other gdb-like stubs).


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

5 Files Affected:

  • (modified) lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp (+3-2)
  • (added) lldb/unittests/ABI/AArch64/ABIAArch64Test.cpp (+68)
  • (added) lldb/unittests/ABI/AArch64/CMakeLists.txt (+9)
  • (added) lldb/unittests/ABI/CMakeLists.txt (+3)
  • (modified) lldb/unittests/CMakeLists.txt (+1)
diff --git a/lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp b/lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp
index b9e7c698cdec0..e40d2c5fc121a 100644
--- a/lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp
+++ b/lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp
@@ -12,6 +12,7 @@
 #include "ABIMacOSX_arm64.h"
 #include "ABISysV_arm64.h"
 #include "Utility/ARM64_DWARF_Registers.h"
+#include "Utility/ARM64_ehframe_Registers.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Target/Process.h"
 
@@ -69,9 +70,9 @@ lldb::addr_t ABIAArch64::FixDataAddress(lldb::addr_t pc) {
 std::pair<uint32_t, uint32_t>
 ABIAArch64::GetEHAndDWARFNums(llvm::StringRef name) {
   if (name == "pc")
-    return {LLDB_INVALID_REGNUM, arm64_dwarf::pc};
+    return {arm64_ehframe::pc, arm64_dwarf::pc};
   if (name == "cpsr")
-    return {LLDB_INVALID_REGNUM, arm64_dwarf::cpsr};
+    return {arm64_ehframe::cpsr, arm64_dwarf::cpsr};
   return MCBasedABI::GetEHAndDWARFNums(name);
 }
 
diff --git a/lldb/unittests/ABI/AArch64/ABIAArch64Test.cpp b/lldb/unittests/ABI/AArch64/ABIAArch64Test.cpp
new file mode 100644
index 0000000000000..7ae175a0d3a70
--- /dev/null
+++ b/lldb/unittests/ABI/AArch64/ABIAArch64Test.cpp
@@ -0,0 +1,68 @@
+//===-- ABIAArch64Test.cpp ------------------------------------------------===//
+
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ABI/AArch64/ABIMacOSX_arm64.h"
+#include "Plugins/ABI/AArch64/ABISysV_arm64.h"
+#include "Utility/ARM64_DWARF_Registers.h"
+#include "Utility/ARM64_ehframe_Registers.h"
+#include "lldb/Target/DynamicRegisterInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gtest/gtest.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+class ABIAArch64TestFixture : public testing::TestWithParam<llvm::StringRef> {
+public:
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+
+  //  virtual void SetUp() override { }
+  //  virtual void TearDown() override { }
+
+protected:
+};
+
+void ABIAArch64TestFixture::SetUpTestCase() {
+  LLVMInitializeAArch64TargetInfo();
+  LLVMInitializeAArch64TargetMC();
+  ABISysV_arm64::Initialize();
+  ABIMacOSX_arm64::Initialize();
+}
+
+void ABIAArch64TestFixture::TearDownTestCase() {
+  ABISysV_arm64::Terminate();
+  ABIMacOSX_arm64::Terminate();
+  llvm::llvm_shutdown();
+}
+
+TEST_P(ABIAArch64TestFixture, AugmentRegisterInfo) {
+  ABISP abi_sp = ABI::FindPlugin(ProcessSP(), ArchSpec(GetParam()));
+  ASSERT_TRUE(abi_sp);
+  using Register = DynamicRegisterInfo::Register;
+
+  Register pc{ConstString("pc"), ConstString(), ConstString("GPR")};
+  std::vector<Register> regs{pc};
+
+  abi_sp->AugmentRegisterInfo(regs);
+
+  ASSERT_EQ(regs.size(), 1);
+  Register new_pc = regs[0];
+  EXPECT_EQ(new_pc.name, pc.name);
+  EXPECT_EQ(new_pc.set_name, pc.set_name);
+  EXPECT_EQ(new_pc.regnum_ehframe, arm64_ehframe::pc);
+  EXPECT_EQ(new_pc.regnum_dwarf, arm64_dwarf::pc);
+}
+
+INSTANTIATE_TEST_SUITE_P(ABIAArch64Tests, ABIAArch64TestFixture,
+                         testing::Values("aarch64-pc-linux",
+                                         "arm64-apple-macosx"));
diff --git a/lldb/unittests/ABI/AArch64/CMakeLists.txt b/lldb/unittests/ABI/AArch64/CMakeLists.txt
new file mode 100644
index 0000000000000..592701fc40753
--- /dev/null
+++ b/lldb/unittests/ABI/AArch64/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_lldb_unittest(ABIAArch64Tests
+  ABIAArch64Test.cpp
+  LINK_COMPONENTS
+    Support
+    AArch64
+  LINK_LIBS
+    lldbTarget
+    lldbPluginABIAArch64
+  )
diff --git a/lldb/unittests/ABI/CMakeLists.txt b/lldb/unittests/ABI/CMakeLists.txt
new file mode 100644
index 0000000000000..8ad7474e9444a
--- /dev/null
+++ b/lldb/unittests/ABI/CMakeLists.txt
@@ -0,0 +1,3 @@
+if ("AArch64" IN_LIST LLVM_TARGETS_TO_BUILD)
+  add_subdirectory(AArch64)
+endif()
diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt
index d8f9cc747986e..6eaaa4f4c8c98 100644
--- a/lldb/unittests/CMakeLists.txt
+++ b/lldb/unittests/CMakeLists.txt
@@ -52,6 +52,7 @@ if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
   add_subdirectory(API)
   add_subdirectory(DAP)
 endif()
+add_subdirectory(ABI)
 add_subdirectory(Breakpoint)
 add_subdirectory(Callback)
 add_subdirectory(Core)

@labath
Copy link
Collaborator Author

labath commented Jun 10, 2025

I'd appreciate if some one could take a look at this, as one of our internal debug stubs is currently broken.

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I don't have the functional expertise to review this, but this makes sense and if there ends up being feedback it can be address post-merge.

@labath
Copy link
Collaborator Author

labath commented Jun 10, 2025

Thanks.

@labath labath merged commit 74a012e into llvm:main Jun 10, 2025
9 checks passed
@labath labath deleted the abi branch June 10, 2025 17:50
@kazutakahirata
Copy link
Contributor

@labath I've landed 50313a5 to fix warnings from this PR. Thanks!

@jasonmolenda
Copy link
Collaborator

Thanks for help, I didn't realize I regressed this with my NFC reorg.

@jasonmolenda
Copy link
Collaborator

I thought we augmented the target.xml from the remote gdb stub via ABI::AugmentRegisterInfo -- adding eh_frame/dwarf register numbers that were not supplied by the stub, for instance -- but looking at AArch64 & x86, it looks like it's just adding the slice registers like w0 / x0 and eip / rip to the registers provided. Ideally the remote stub should only tell us, like, "I have a register x1 and I will use number 1 to refer to it" and we should know everything else, the size, default format, ABI register numberings for x1 from internal knowledge. In the beginning we had the model of "debugserver tells us everything about registers, lldb knows nothing built-in" but that was never going to work for wider interop and it didn't last long.

@labath
Copy link
Collaborator Author

labath commented Jun 11, 2025

I thought we augmented the target.xml from the remote gdb stub via ABI::AugmentRegisterInfo -- adding eh_frame/dwarf register numbers that were not supplied by the stub, for instance -- but looking at AArch64 & x86, it looks like it's just adding the slice registers like w0 / x0 and eip / rip to the registers provided.

No, it's doing both. You see the slice registers because that's completely architecture specific. The register numbers are being added in the base class (MCBasedABI::AugmentRegisterInfo), which calls into this function (ABIAArch64::GetEHAndDWARFNums)

tomtor pushed a commit to tomtor/llvm-project that referenced this pull request Jun 14, 2025
This fixes a regression caused by llvm#139817, where we would fail to unwind
(using eh_frame) when using debug stubs (like qemu) which do not provide
eh_frame register numbers.

Unwinding fails because the unwinder tries to convert pc register number
to the "eh_frame" scheme (and fails). Previously that worked because the
code used a slightly different pattern which ended up comparing the
number by converting it to the "generic" scheme (which we do provide).

I don't think that's a bug in the unwinder -- we just need to make sure
we provide the register number all the time.

The associated test is not particularly useful, but I'm hoping it could
be used to test other changes like this as well. Other register
augmentation changes are tested in an end-to-end fashion (by injecting
gdb-remote packets and then trying to read registers). That works well
for testing the addition of subregisters, but it isn't particularly
suitable for testing register numbers, as the only place (that I know
of) where this manifests is during unwinding, and recreating an
unwindable process in a test like this is fairly challenging.

Probably the best way to ensure coverage for this scenario would be to
have lldb-server stop sending eh_frame register numbers (align it with
other gdb-like stubs).
akuhlens pushed a commit to akuhlens/llvm-project that referenced this pull request Jun 24, 2025
This fixes a regression caused by llvm#139817, where we would fail to unwind
(using eh_frame) when using debug stubs (like qemu) which do not provide
eh_frame register numbers.

Unwinding fails because the unwinder tries to convert pc register number
to the "eh_frame" scheme (and fails). Previously that worked because the
code used a slightly different pattern which ended up comparing the
number by converting it to the "generic" scheme (which we do provide).

I don't think that's a bug in the unwinder -- we just need to make sure
we provide the register number all the time.

The associated test is not particularly useful, but I'm hoping it could
be used to test other changes like this as well. Other register
augmentation changes are tested in an end-to-end fashion (by injecting
gdb-remote packets and then trying to read registers). That works well
for testing the addition of subregisters, but it isn't particularly
suitable for testing register numbers, as the only place (that I know
of) where this manifests is during unwinding, and recreating an
unwindable process in a test like this is fairly challenging.

Probably the best way to ensure coverage for this scenario would be to
have lldb-server stop sending eh_frame register numbers (align it with
other gdb-like stubs).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants