[Lldb-commits] [lldb] e8f998c - AArch64 Linux and elf-core PAC stack unwinder support
Muhammad Omair Javaid via lldb-commits
lldb-commits at lists.llvm.org
Tue Jun 15 14:10:05 PDT 2021
Author: Muhammad Omair Javaid
Date: 2021-06-16T02:09:46+05:00
New Revision: e8f998c0c5edda3d6bad9b70e60975296df3d9fb
URL: https://github.com/llvm/llvm-project/commit/e8f998c0c5edda3d6bad9b70e60975296df3d9fb
DIFF: https://github.com/llvm/llvm-project/commit/e8f998c0c5edda3d6bad9b70e60975296df3d9fb.diff
LOG: AArch64 Linux and elf-core PAC stack unwinder support
This patch builds on D100521 and other related patches to add support
for unwinding stack on AArch64 systems with pointer authentication
feature enabled.
We override FixCodeAddress and FixDataAddress function in ABISysV_arm64
class. We now try to calculate and set code and data masks after reading
data_mask and code_mask registers exposed by AArch64 targets running Linux.
This patch utilizes core file linux-aarch64-pac.core for testing that
LLDB can successfully unwind stack frames in the presence of signed
return address after masking off ignored bits.
This patch also includes a AArch64 Linux native test case to demonstrate
successful back trace calculation in presence of pointer authentication
feature.
Differential Revision: https://reviews.llvm.org/D99944
Added:
lldb/test/API/functionalities/postmortem/elf-core/linux-aarch64-pac.out
lldb/test/API/functionalities/unwind/aarch64_unwind_pac/Makefile
lldb/test/API/functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py
lldb/test/API/functionalities/unwind/aarch64_unwind_pac/main.c
Modified:
lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h
lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
Removed:
################################################################################
diff --git a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
index 312b5620262d..16fb38e107c3 100644
--- a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
+++ b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
@@ -787,6 +787,56 @@ lldb::addr_t ABISysV_arm64::FixAddress(addr_t pc, addr_t mask) {
return (pc & pac_sign_extension) ? pc | mask : pc & (~mask);
}
+// Reads code or data address mask for the current Linux process.
+static lldb::addr_t ReadLinuxProcessAddressMask(lldb::ProcessSP process_sp,
+ llvm::StringRef reg_name) {
+ // Linux configures user-space virtual addresses with top byte ignored.
+ // We set default value of mask such that top byte is masked out.
+ uint64_t address_mask = ~((1ULL << 56) - 1);
+ // If Pointer Authentication feature is enabled then Linux exposes
+ // PAC data and code mask register. Try reading relevant register
+ // below and merge it with default address mask calculated above.
+ lldb::ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread();
+ if (thread_sp) {
+ lldb::RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext();
+ if (reg_ctx_sp) {
+ const RegisterInfo *reg_info =
+ reg_ctx_sp->GetRegisterInfoByName(reg_name, 0);
+ if (reg_info) {
+ lldb::addr_t mask_reg_val = reg_ctx_sp->ReadRegisterAsUnsigned(
+ reg_info->kinds[eRegisterKindLLDB], LLDB_INVALID_ADDRESS);
+ if (mask_reg_val != LLDB_INVALID_ADDRESS)
+ address_mask |= mask_reg_val;
+ }
+ }
+ }
+ return address_mask;
+}
+
+lldb::addr_t ABISysV_arm64::FixCodeAddress(lldb::addr_t pc) {
+ if (lldb::ProcessSP process_sp = GetProcessSP()) {
+ if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSLinux() &&
+ !process_sp->GetCodeAddressMask())
+ process_sp->SetCodeAddressMask(
+ ReadLinuxProcessAddressMask(process_sp, "code_mask"));
+
+ return FixAddress(pc, process_sp->GetCodeAddressMask());
+ }
+ return pc;
+}
+
+lldb::addr_t ABISysV_arm64::FixDataAddress(lldb::addr_t pc) {
+ if (lldb::ProcessSP process_sp = GetProcessSP()) {
+ if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSLinux() &&
+ !process_sp->GetDataAddressMask())
+ process_sp->SetDataAddressMask(
+ ReadLinuxProcessAddressMask(process_sp, "data_mask"));
+
+ return FixAddress(pc, process_sp->GetDataAddressMask());
+ }
+ return pc;
+}
+
void ABISysV_arm64::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
"SysV ABI for AArch64 targets", CreateInstance);
diff --git a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h
index 4c88ee2cb63e..3428a7ad9418 100644
--- a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h
+++ b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h
@@ -85,6 +85,9 @@ class ABISysV_arm64 : public ABIAArch64 {
uint32_t GetPluginVersion() override;
+ lldb::addr_t FixCodeAddress(lldb::addr_t pc) override;
+ lldb::addr_t FixDataAddress(lldb::addr_t pc) override;
+
protected:
lldb::ValueObjectSP
GetReturnValueObjectImpl(lldb_private::Thread &thread,
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
index 6f8d05101baa..3dfc7a0d4405 100644
--- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
+++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
@@ -20,6 +20,7 @@ class LinuxCoreTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
_aarch64_pid = 37688
+ _aarch64_pac_pid = 387
_i386_pid = 32306
_x86_64_pid = 32259
_s390x_pid = 1045
@@ -257,6 +258,18 @@ def test_x86_64_sysroot(self):
self.dbg.DeleteTarget(target)
+ @skipIfLLVMTargetMissing("AArch64")
+ def test_aarch64_pac(self):
+ """Test that lldb can unwind stack for AArch64 elf core file with PAC enabled."""
+
+ target = self.dbg.CreateTarget("linux-aarch64-pac.out")
+ self.assertTrue(target, VALID_TARGET)
+ process = target.LoadCore("linux-aarch64-pac.core")
+
+ self.check_all(process, self._aarch64_pac_pid, self._aarch64_regions, "a.out")
+
+ self.dbg.DeleteTarget(target)
+
@skipIfLLVMTargetMissing("AArch64")
@expectedFailureAll(archs=["aarch64"], oslist=["freebsd"],
bugnumber="llvm.org/pr49415")
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/linux-aarch64-pac.out b/lldb/test/API/functionalities/postmortem/elf-core/linux-aarch64-pac.out
new file mode 100755
index 000000000000..6df8e1c9e302
Binary files /dev/null and b/lldb/test/API/functionalities/postmortem/elf-core/linux-aarch64-pac.out
diff er
diff --git a/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/Makefile b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/Makefile
new file mode 100644
index 000000000000..18bb607eda55
--- /dev/null
+++ b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/Makefile
@@ -0,0 +1,5 @@
+C_SOURCES := main.c
+
+CFLAGS := -g -Os -march=armv8.3-a -mbranch-protection=pac-ret+leaf
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py
new file mode 100644
index 000000000000..8f88e644b653
--- /dev/null
+++ b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py
@@ -0,0 +1,44 @@
+"""
+Test that we can backtrace correctly when AArch64 PAC is enabled
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AArch64UnwindPAC(TestBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipIf(archs=no_match(["aarch64"]))
+ @skipIf(oslist=no_match(['linux']))
+ def test(self):
+ """Test that we can backtrace correctly when AArch64 PAC is enabled"""
+ self.build()
+
+ self.line = line_number('main.c', '// Frame func_c')
+
+ exe = self.getBuildArtifact("a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.c", self.line, num_expected_locations=1)
+ self.runCmd("run", RUN_SUCCEEDED)
+ self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=["stop reason = breakpoint 1."])
+
+ target = self.dbg.GetSelectedTarget()
+ process = target.GetProcess()
+ thread = process.GetThreadAtIndex(0)
+
+ backtrace = ["func_c", "func_b", "func_a", "main", "__libc_start_main", "_start"]
+ self.assertEqual(thread.GetNumFrames(), len(backtrace))
+ for frame_idx, frame in enumerate(thread.frames):
+ frame = thread.GetFrameAtIndex(frame_idx)
+ self.assertTrue(frame)
+ self.assertEqual(frame.GetFunctionName(), backtrace[frame_idx])
+ # Check line number for functions in main.c
+ if (frame_idx < 4):
+ self.assertEqual(frame.GetLineEntry().GetLine(),
+ line_number("main.c", "Frame " + backtrace[frame_idx]))
diff --git a/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/main.c b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/main.c
new file mode 100644
index 000000000000..7de8fc0e1834
--- /dev/null
+++ b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/main.c
@@ -0,0 +1,24 @@
+// This program makes a multi tier nested function call to test AArch64
+// Pointer Authentication feature.
+
+// To enable PAC return address signing compile with following clang arguments:
+// -march=armv8.3-a -mbranch-protection=pac-ret+leaf
+
+#include <stdlib.h>
+
+static void __attribute__((noinline)) func_c(void) {
+ exit(0); // Frame func_c
+}
+
+static void __attribute__((noinline)) func_b(void) {
+ func_c(); // Frame func_b
+}
+
+static void __attribute__((noinline)) func_a(void) {
+ func_b(); // Frame func_a
+}
+
+int main(int argc, char *argv[]) {
+ func_a(); // Frame main
+ return 0;
+}
More information about the lldb-commits
mailing list