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);
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,
mydir = TestBase.compute_mydir(__file__)
_aarch64_pid = 37688
+ _aarch64_pac_pid = 387
_i386_pid = 32306
_x86_64_pid = 32259
_s390x_pid = 1045
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):
--- /dev/null
+C_SOURCES := main.c
+
+CFLAGS := -g -Os -march=armv8.3-a -mbranch-protection=pac-ret+leaf
+
+include Makefile.rules
--- /dev/null
+"""
+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]))
--- /dev/null
+// 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;
+}