From: Muhammad Omair Javaid Date: Tue, 15 Jun 2021 10:41:38 +0000 (+0500) Subject: AArch64 Linux and elf-core PAC stack unwinder support X-Git-Tag: llvmorg-14-init~3883 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e8f998c0c5edda3d6bad9b70e60975296df3d9fb;p=platform%2Fupstream%2Fllvm.git 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 --- diff --git a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp index 312b562..16fb38e1 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 4c88ee2..3428a7a 100644 --- a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h +++ b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h @@ -85,6 +85,9 @@ public: 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 6f8d051..3dfc7a0 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 @@ -258,6 +259,18 @@ class LinuxCoreTestCase(TestBase): 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") def test_aarch64_regs(self): 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 0000000..6df8e1c Binary files /dev/null and b/lldb/test/API/functionalities/postmortem/elf-core/linux-aarch64-pac.out differ 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 0000000..18bb607e --- /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 0000000..8f88e64 --- /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 0000000..7de8fc0 --- /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 + +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; +}