diff --git a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp --- a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp +++ b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp @@ -791,6 +791,31 @@ return (pc & pac_sign_extension) ? pc | mask : pc & (~mask); } +// Reads code or data address mask for the current Linux process. +static lldb::addr_t ReadFreeBSDProcessAddressMask(lldb::ProcessSP process_sp, + llvm::StringRef reg_name) { + uint64_t address_mask = 0; + + // If Pointer Authentication feature is enabled then FreeBSD 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; +} + // Reads code or data address mask for the current Linux process. static lldb::addr_t ReadLinuxProcessAddressMask(lldb::ProcessSP process_sp, llvm::StringRef reg_name) { @@ -823,6 +848,10 @@ !process_sp->GetCodeAddressMask()) process_sp->SetCodeAddressMask( ReadLinuxProcessAddressMask(process_sp, "code_mask")); + else if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSFreeBSD() && + !process_sp->GetCodeAddressMask()) + process_sp->SetCodeAddressMask( + ReadFreeBSDProcessAddressMask(process_sp, "code_mask")); return FixAddress(pc, process_sp->GetCodeAddressMask()); } @@ -835,6 +864,10 @@ !process_sp->GetDataAddressMask()) process_sp->SetDataAddressMask( ReadLinuxProcessAddressMask(process_sp, "data_mask")); + else if (process_sp->GetTarget().GetArchitecture().GetTriple().isOSFreeBSD() && + !process_sp->GetDataAddressMask()) + process_sp->SetDataAddressMask( + ReadFreeBSDProcessAddressMask(process_sp, "data_mask")); return FixAddress(pc, process_sp->GetDataAddressMask()); } diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.h b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.h --- a/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.h +++ b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.h @@ -36,8 +36,10 @@ : public NativeRegisterContextFreeBSD, public NativeRegisterContextDBReg_arm64 { public: - NativeRegisterContextFreeBSD_arm64(const ArchSpec &target_arch, - NativeThreadProtocol &native_thread); + NativeRegisterContextFreeBSD_arm64( + const ArchSpec &target_arch, + NativeThreadProtocol &native_thread, + std::unique_ptr register_info_up); uint32_t GetRegisterSetCount() const override; @@ -65,6 +67,15 @@ // a unittest to assert that). std::array m_reg_data; std::array m_fpreg_data; + + struct arm64_addr_mask { + uint64_t data_mask; + uint64_t insn_mask; + }; + + bool m_addr_mask_is_valid; + struct arm64_addr_mask m_addr_mask; + #ifdef LLDB_HAS_FREEBSD_WATCHPOINT dbreg m_dbreg; bool m_read_dbreg; @@ -76,16 +87,22 @@ void *GetFPRBuffer() { return &m_fpreg_data; } size_t GetFPRSize() { return sizeof(m_fpreg_data); } + void *GetAddrMaskBuffer() { return &m_addr_mask; } + size_t GetAddrMaskBufferSize() { return sizeof(m_addr_mask); } + bool IsGPR(unsigned reg) const; bool IsFPR(unsigned reg) const; + bool IsAddrMask(unsigned reg) const; Status ReadGPR(); Status WriteGPR(); + Status ReadAddrMask(); Status ReadFPR(); Status WriteFPR(); uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; + uint32_t CalculateAddrMaskOffset(const RegisterInfo *reg_info) const; llvm::Error ReadHardwareDebugInfo() override; llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.cpp b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.cpp --- a/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.cpp +++ b/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.cpp @@ -19,9 +19,11 @@ #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" // clang-format off +#include #include #include #include +#include // clang-format on #define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize()) @@ -33,13 +35,19 @@ NativeRegisterContextFreeBSD * NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { - return new NativeRegisterContextFreeBSD_arm64(target_arch, native_thread); + Flags opt_regsets; + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskPAuth); + auto register_info_up = + std::make_unique(target_arch, opt_regsets); + return new NativeRegisterContextFreeBSD_arm64(target_arch, native_thread, + std::move(register_info_up)); } NativeRegisterContextFreeBSD_arm64::NativeRegisterContextFreeBSD_arm64( - const ArchSpec &target_arch, NativeThreadProtocol &native_thread) - : NativeRegisterContextRegisterInfo( - native_thread, new RegisterInfoPOSIX_arm64(target_arch, 0)) + const ArchSpec &target_arch, NativeThreadProtocol &native_thread, + std::unique_ptr register_info_up) + : NativeRegisterContextRegisterInfo(native_thread, + register_info_up.release()) #ifdef LLDB_HAS_FREEBSD_WATCHPOINT , m_read_dbreg(false) @@ -47,6 +55,9 @@ { ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); + ::memset(&m_addr_mask, 0, sizeof(m_addr_mask)); + + m_addr_mask_is_valid = false; } RegisterInfoPOSIX_arm64 & @@ -84,6 +95,12 @@ return false; } +bool NativeRegisterContextFreeBSD_arm64::IsAddrMask(unsigned reg) const { + // Reuse the Linux PAuth mask register for now. On FreeBSD it can be + // used when PAC isn't available, e.g. with TBI. + return GetRegisterInfo().IsPAuthReg(reg); +} + Status NativeRegisterContextFreeBSD_arm64::ReadGPR() { return NativeProcessFreeBSD::PtraceWrapper( PT_GETREGS, m_thread.GetID(), m_reg_data.data()); @@ -104,11 +121,37 @@ PT_SETFPREGS, m_thread.GetID(), m_fpreg_data.data()); } +Status NativeRegisterContextFreeBSD_arm64::ReadAddrMask() { + Status error; + +#ifdef NT_ARM_ADDR_MASK + if (m_addr_mask_is_valid) + return error; + + struct iovec ioVec; + ioVec.iov_base = GetAddrMaskBuffer(); + ioVec.iov_len = GetAddrMaskBufferSize(); + + error = NativeProcessFreeBSD::PtraceWrapper( + PT_GETREGSET, m_thread.GetID(), &ioVec, NT_ARM_ADDR_MASK); + + if (error.Success()) + m_addr_mask_is_valid = true; +#endif + + return error; +} + uint32_t NativeRegisterContextFreeBSD_arm64::CalculateFprOffset( const RegisterInfo *reg_info) const { return reg_info->byte_offset - GetGPRSize(); } +uint32_t NativeRegisterContextFreeBSD_arm64::CalculateAddrMaskOffset( + const RegisterInfo *reg_info) const { + return reg_info->byte_offset - GetRegisterInfo().GetPAuthOffset(); +} + Status NativeRegisterContextFreeBSD_arm64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) { @@ -145,6 +188,14 @@ offset = CalculateFprOffset(reg_info); assert(offset < GetFPRSize()); src = (uint8_t *)GetFPRBuffer() + offset; + } else if (IsAddrMask(reg)) { + error = ReadAddrMask(); + if (error.Fail()) + return error; + + offset = CalculateAddrMaskOffset(reg_info); + assert(offset < GetAddrMaskBufferSize()); + src = (uint8_t *)GetAddrMaskBuffer() + offset; } else return Status("Failed to read register value"); diff --git a/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h b/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h --- a/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h +++ b/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h @@ -120,6 +120,7 @@ }; constexpr RegsetDesc AARCH64_PAC_Desc[] = { + {llvm::Triple::FreeBSD, llvm::Triple::aarch64, llvm::ELF::NT_ARM_PAC_MASK}, {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_PAC_MASK}, }; diff --git a/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py --- a/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py +++ b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/TestAArch64UnwindPAC.py @@ -6,28 +6,26 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil +import re 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""" - if not self.isAArch64PAuth(): - self.skipTest('Target must support Pointer Authentication.') - + def common(self, backtrace_tail): 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) + if re.match('Process .* exited with status = 1[^0-9]', self.res.GetOutput()): + # No PAC support + self.skipTest('Target must support Pointer Authentication.') + self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, substrs=["stop reason = breakpoint 1."]) @@ -35,13 +33,33 @@ 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)) + backtrace = ["func_c", "func_b", "func_a", "main"] + backtrace_tail for frame_idx, frame in enumerate(thread.frames): frame = thread.GetFrameAtIndex(frame_idx) + # On FreeBSD we get ___lldb_unnamed_symbol symbols at the bottom + # of the stack. Ignore these and assume we have walked the entire + # stack, however check this assumption is correct. + if frame.GetFunctionName().startswith("___lldb_unnamed_symbol"): + self.assertEqual(len(backtrace), frame_idx) + break 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])) + + @skipIf(archs=no_match(["aarch64"])) + @skipIf(oslist=no_match(['freebsd'])) + def test_freebsd(self): + """Test that we can backtrace correctly when AArch64 PAC is enabled""" + self.common(["__start"]) + + @skipIf(archs=no_match(["aarch64"])) + @skipIf(oslist=no_match(['linux'])) + def test_linux(self): + """Test that we can backtrace correctly when AArch64 PAC is enabled""" + if not self.isAArch64PAuth(): + self.skipTest('Target must support Pointer Authentication.') + + self.common(["__libc_start_main", "_start"]) diff --git a/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/main.c b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/main.c --- a/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/main.c +++ b/lldb/test/API/functionalities/unwind/aarch64_unwind_pac/main.c @@ -4,8 +4,32 @@ // To enable PAC return address signing compile with following clang arguments: // -march=armv8.3-a -mbranch-protection=pac-ret+leaf +#include #include +#if defined(__FreeBSD__) +#include + +// Added in FreeBSD 12.2, can be removed when all supported branches define it. +#if !defined(HWCAP_PACA) +#define HWCAP_PACA 0x40000000 +#endif + +static bool pac_supported(void) { + unsigned long hwcap; + + if (elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap)) != 0) + return false; + + return (hwcap & HWCAP_PACA) != 0; +} +#else +static bool pac_supported(void) { + // We expect Linux to check for PAC up front using /proc/cpuinfo. + return true; +} +#endif + static void __attribute__((noinline)) func_c(void) { exit(0); // Frame func_c } @@ -19,6 +43,9 @@ } int main(int argc, char *argv[]) { + if (!pac_supported()) + return 1; + func_a(); // Frame main return 0; } diff --git a/lldb/test/API/python_api/sbvalue_const_addrof/main.cpp b/lldb/test/API/python_api/sbvalue_const_addrof/main.cpp --- a/lldb/test/API/python_api/sbvalue_const_addrof/main.cpp +++ b/lldb/test/API/python_api/sbvalue_const_addrof/main.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include struct RegisterContext {