This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5e6aabd48e35: Support AArch64/Linux watchpoint on tagged
addresses (authored by omjavaid).
Herald added a project: LLDB.
Changed prior to commit:
https://reviews.llvm.org/D101361?vs=357101&id=357831#toc
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D101361/new/
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,29 @@
+#include <stdint.h>
+
+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));
+
+ // pacdza computes and inserts a pointer authentication code for address
+ // stored in tagged_ptr using PAC key A.
+ __asm__ __volatile__("pacdza %0" : "=r"(tagged_ptr) : "r"(tagged_ptr));
+
+ ++dummy; // Set break point at this line.
+
+ // Increment global_var
+ ++global_var;
+
+ ++dummy;
+
+ // autdza authenticates tagged_ptr using PAC key A.
+ __asm__ __volatile__("autdza %0" : "=r"(tagged_ptr) : "r"(tagged_ptr));
+
+ // 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,135 @@
+"""
+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.
+ """
+ if not self.isAArch64PAuth():
+ self.skipTest('Target must support pointer authentication.')
+
+ # 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"""
+
+ if not self.isAArch64PAuth():
+ self.skipTest('Target must support pointer authentication.')
+
+ # 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,5 @@
+C_SOURCES := main.c
+
+CFLAGS_EXTRAS := -march=armv8.3-a
+
+include Makefile.rules
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<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
@@ -76,6 +76,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
@@ -892,4 +892,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__)
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits