omjavaid created this revision. omjavaid added reviewers: labath, DavidSpickett. Herald added subscribers: danielkiss, kristof.beyls. omjavaid requested review of this revision.
AArch64 architecture support virtual addresses with some of the top bits ignored. These ignored bits can host memory tags or bit masks that can serve to for authentication of address integrity. We need to clear away the top ignored bits from watchpoint address to reliably hit watchpoints as well set watchpoints on addresses containing tags or masks in their top bits. This patch adds support to watch tagged addresses on AArch64/Linux. https://reviews.llvm.org/D101361 Files: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h lldb/source/Target/Target.cpp lldb/test/API/commands/watchpoints/watch_tagged_addr/Makefile lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py lldb/test/API/commands/watchpoints/watch_tagged_addr/main.c
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,17 @@ +#include <stdint.h> + +uint32_t global_var = 0; // Watchpoint variable declaration. + +int main(int argc, char **argv) { + // 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)); + + // Increment global_var + ++global_var; // Set break point at this line. + + // Increment global_var using tagged_ptr + ++*tagged_ptr; + + return 0; +} 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,127 @@ +""" +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 LLDB hits watchpoints when tagged pointer is used for memory access""" + + # 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 LLDB hits watchpoints when tagged pointer is used for memory access""" + + # 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/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/source/Target/Target.cpp =================================================================== --- lldb/source/Target/Target.cpp +++ lldb/source/Target/Target.cpp @@ -42,6 +42,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" @@ -819,6 +820,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<std::recursive_mutex> lock; this->GetWatchpointList().GetListMutex(lock); WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr); 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/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -72,6 +72,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 @@ -877,4 +877,18 @@ return expedited_reg_nums; } +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().Fail()) + mask |= m_pac_mask.data_mask; + + return hit_addr & ~mask; +} + #endif // defined (__arm64__) || defined (__aarch64__)
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits