Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -75,6 +75,8 @@ size_t GetFPRSize() override { return sizeof(m_fpr); } + lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) override; + private: bool m_gpr_is_valid; bool m_fpu_is_valid; Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -890,4 +890,19 @@ "Unknown AArch64 memory tag type %d", type); } +lldb::addr_t NativeRegisterContextLinux_arm64::FixWatchpointHitAddress( + lldb::addr_t hit_addr) { + // Linux configures user-space virtual addresses with top byte ignored. + // We set default value of mask such that top byte is masked out. + lldb::addr_t mask = ~((1ULL << 56) - 1); + + // Try to read pointer authentication data_mask register and calculate a + // consolidated data address mask after ignoring the top byte. + if (ReadPAuthMask().Success()) + mask |= m_pac_mask.data_mask; + + return hit_addr & ~mask; + ; +} + #endif // defined (__arm64__) || defined (__aarch64__) Index: lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h =================================================================== --- lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h +++ lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h @@ -72,6 +72,9 @@ virtual llvm::Error ReadHardwareDebugInfo() = 0; virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0; + virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) { + return hit_addr; + } }; } // namespace lldb_private Index: lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp =================================================================== --- lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp +++ lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp @@ -421,6 +421,9 @@ if (error) return Status(std::move(error)); + // Mask off ignored bits from watchpoint trap address. + trap_addr = FixWatchpointHitAddress(trap_addr); + uint32_t watch_size; lldb::addr_t watch_addr; Index: lldb/source/Target/Target.cpp =================================================================== --- lldb/source/Target/Target.cpp +++ lldb/source/Target/Target.cpp @@ -41,6 +41,7 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ABI.h" #include "lldb/Target/Language.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Process.h" @@ -823,6 +824,11 @@ // Grab the list mutex while doing operations. const bool notify = false; // Don't notify about all the state changes we do // on creating the watchpoint. + + // Mask off ignored bits from watchpoint address. + if (ABISP abi = m_process_sp->GetABI()) + addr = abi->FixDataAddress(addr); + std::unique_lock lock; this->GetWatchpointList().GetListMutex(lock); WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr); Index: lldb/test/API/commands/watchpoints/watch_tagged_addr/Makefile =================================================================== --- /dev/null +++ lldb/test/API/commands/watchpoints/watch_tagged_addr/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules Index: lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py =================================================================== --- /dev/null +++ lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py @@ -0,0 +1,130 @@ +""" +Test LLDB can set and hit watchpoints on tagged addresses +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestWatchTaggedAddresses(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Set source filename. + self.source = 'main.c' + + # Invoke the default build rule. + self.build() + + # Get the path of the executable + exe = self.getBuildArtifact("a.out") + + # Create a target by the debugger. + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + @skipIf(archs=no_match(["aarch64"])) + @skipIf(oslist=no_match(['linux'])) + def test_watch_hit_tagged_ptr_access(self): + """ + Test that LLDB hits watchpoint installed on an untagged address with + memory access by a tagged pointer. + """ + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_symbol(self, 'main') + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped due to the breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + # Set the watchpoint variable declaration line number. + self.decl = line_number(self.source, + '// Watchpoint variable declaration.') + + # Now let's set a watchpoint on 'global_var'. + self.expect( + "watchpoint set variable global_var", + WATCHPOINT_CREATED, + substrs=[ + 'Watchpoint created', + 'size = 4', + 'type = w', + '%s:%d' % + (self.source, + self.decl)]) + + self.verify_watch_hits() + + @skipIf(archs=no_match(["aarch64"])) + @skipIf(oslist=no_match(['linux'])) + def test_watch_set_on_tagged_ptr(self): + """Test that LLDB can install and hit watchpoint on a tagged address""" + + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + lldbutil.run_break_set_by_file_and_line( + self, None, self.line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped due to the breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', + 'stop reason = breakpoint']) + + # Now let's set a expression watchpoint on 'tagged_ptr'. + self.expect( + "watchpoint set expression -s 4 -- tagged_ptr", + WATCHPOINT_CREATED, + substrs=[ + 'Watchpoint created', + 'size = 4', + 'type = w']) + + self.verify_watch_hits() + + def verify_watch_hits(self): + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs=['Number of supported hardware watchpoints:', + 'hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (read_write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stop reason = watchpoint']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (read_write type). + # The stop reason of the thread should be watchpoint. + self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT, + substrs=['stop reason = watchpoint']) + + self.runCmd("process continue") + + # There should be no more watchpoint hit and the process status should + # be 'exited'. + self.expect("process status", + substrs=['exited']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 2. + self.expect("watchpoint list -v", + substrs=['hit_count = 2']) Index: lldb/test/API/commands/watchpoints/watch_tagged_addr/main.c =================================================================== --- /dev/null +++ lldb/test/API/commands/watchpoints/watch_tagged_addr/main.c @@ -0,0 +1,22 @@ +#include + +uint32_t global_var = 0; // Watchpoint variable declaration. + +int main(int argc, char **argv) { + int dummy = 0; + // Move address of global variable into tagged_ptr after tagging + // Simple tagging scheme where 62nd bit of tagged address is set + uint32_t *tagged_ptr = (uint32_t *)((uint64_t)&global_var | (1ULL << 62)); + + ++dummy; // Set break point at this line. + + // Increment global_var + ++global_var; + + ++dummy; + + // Increment global_var using tagged_ptr + ++*tagged_ptr; + + return 0; +}