jasonmolenda created this revision.
jasonmolenda added a reviewer: DavidSpickett.
jasonmolenda added a project: LLDB.
Herald added subscribers: omjavaid, JDevlieghere, kristof.beyls.
Herald added a project: All.
jasonmolenda requested review of this revision.
Herald added a subscriber: lldb-commits.

This patch adds a new method to SBValue, `GetValueAsAddress()`, which will take 
the uint64_t value in the SBValue and run it through the ABI's FixAddress 
method to clear any TBI/MTE/ptrauth bits on AArch64 targets.  Script authors 
may want access to both the actual uint64_t value, and the address that will be 
accessed, in an SBValue, so I added a new method in addition to 
GetValueAsUnsigned to provide this.

I currently have SBValue::GetValueAsAddress NOT perform a type check, and 
possibly I should have it check the type's IsPointerType() before doing this, 
but at the same time if the script/driver is calling this method, it's probably 
best to just do that.

There's also changes to methods like 
`ValueObject::CreateValueObjectFromAddress` so we can get SBValue::Dereference 
and such to behave correct when you have an SBValue created from a signed 
pointer.

I have the attached test case set to run on any AArch64 system; on Darwin we do 
the same pointer stripping on any process regardless if it is using pointer 
auth (that is, for both "arm64" and "arm64e").  On Linux, a non-ptrauth process 
may not have an address mask and this test may fail because the bits I mask 
into the top nibble in the test program are not removed by GetValueAsAddress(). 
 I'm not sure exactly, but I can remove this test from running on Linux, or add 
a check for `isAArch64PAuth` or something.  This program never actually 
dereferences this pointer value with bits in the high nibble set, it's only set 
up for lldb to manipulate.

We've used this API inside Apple for a bit and it has worked well for our API 
users; of course if there is consensus that it should be done differently we'll 
find a way to handle that internally.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D142792

Files:
  lldb/bindings/interface/SBValue.i
  lldb/include/lldb/API/SBValue.h
  lldb/include/lldb/Core/ValueObject.h
  lldb/source/API/SBValue.cpp
  lldb/source/Core/ValueObject.cpp
  lldb/source/DataFormatters/ValueObjectPrinter.cpp
  lldb/test/API/api/clear-sbvalue-nonadressable-bits/Makefile
  
lldb/test/API/api/clear-sbvalue-nonadressable-bits/TestClearSBValueNonAddressableBits.py
  lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c

Index: lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c
@@ -0,0 +1,15 @@
+#include <stdint.h>
+int main()
+{
+  int v = 5;
+  int *v_p = &v;
+
+  // Add some metadata in the top byte (this will crash unless the
+  // test is running with TBI enabled, but we won't dereference it)
+
+  intptr_t scratch = (intptr_t) v_p;
+  scratch |= (3ULL << 60);
+  int *v_invalid_p = (int *)scratch;
+
+  return v; // break here
+}
Index: lldb/test/API/api/clear-sbvalue-nonadressable-bits/TestClearSBValueNonAddressableBits.py
===================================================================
--- /dev/null
+++ lldb/test/API/api/clear-sbvalue-nonadressable-bits/TestClearSBValueNonAddressableBits.py
@@ -0,0 +1,35 @@
+"""Test that SBValue clears non-addressable bits"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestClearSBValueNonAddressableBits(TestBase):
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    # On AArch64 systems, the top bits that are not used for
+    # addressing may be used for TBI, MTE, and/or pointer 
+    # authentication.
+    @skipIf(archs=no_match(['aarch64', 'arm64', 'arm64e']))
+
+    def test(self):
+        self.source = 'main.c'
+        self.build()
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
+                           "break here", lldb.SBFileSpec(self.source, False))
+
+        if self.TraceOn():
+            self.runCmd ("v v v_p v_invalid_p")
+            self.runCmd ("v &v &v_p &v_invalid_p")
+
+        frame = thread.GetFrameAtIndex(0)
+        v_p = frame.FindVariable("v_p")
+        v_invalid_p = frame.FindVariable("v_invalid_p")
+
+        self.assertEqual(v_p.GetValueAsUnsigned(), v_invalid_p.GetValueAsAddress())
+        self.assertNotEqual(v_invalid_p.GetValueAsUnsigned(), v_invalid_p.GetValueAsAddress())
+
+        self.assertEqual(5, v_p.Dereference().GetValueAsUnsigned())
+        self.assertEqual(5, v_invalid_p.Dereference().GetValueAsUnsigned())
Index: lldb/test/API/api/clear-sbvalue-nonadressable-bits/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/api/clear-sbvalue-nonadressable-bits/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
Index: lldb/source/DataFormatters/ValueObjectPrinter.cpp
===================================================================
--- lldb/source/DataFormatters/ValueObjectPrinter.cpp
+++ lldb/source/DataFormatters/ValueObjectPrinter.cpp
@@ -426,6 +426,11 @@
             IsPointerValue(m_valobj->GetCompilerType())) {
         } else {
           m_stream->Printf(" %s", m_value.c_str());
+          addr_t stripped =
+              m_valobj->GetStrippedPointerValue(m_valobj->GetPointerValue());
+          if (stripped != m_valobj->GetPointerValue()) {
+            m_stream->Printf(" (actual=0x%" PRIx64 ")", stripped);
+          }
           value_printed = true;
         }
       }
Index: lldb/source/Core/ValueObject.cpp
===================================================================
--- lldb/source/Core/ValueObject.cpp
+++ lldb/source/Core/ValueObject.cpp
@@ -31,6 +31,7 @@
 #include "lldb/Symbol/SymbolContext.h"
 #include "lldb/Symbol/Type.h"
 #include "lldb/Symbol/Variable.h"
+#include "lldb/Target/ABI.h"
 #include "lldb/Target/ExecutionContext.h"
 #include "lldb/Target/Language.h"
 #include "lldb/Target/LanguageRuntime.h"
@@ -1442,6 +1443,14 @@
   return LLDB_INVALID_ADDRESS;
 }
 
+addr_t ValueObject::GetStrippedPointerValue(addr_t address) {
+  ExecutionContext exe_ctx(GetExecutionContextRef());
+  if (Process *process = exe_ctx.GetProcessPtr())
+    if (ABISP abi_sp = process->GetABI())
+      return abi_sp->FixCodeAddress(address);
+  return address;
+}
+
 addr_t ValueObject::GetPointerValue(AddressType *address_type) {
   addr_t address = LLDB_INVALID_ADDRESS;
   if (address_type)
@@ -1461,7 +1470,7 @@
   case Value::ValueType::LoadAddress:
   case Value::ValueType::FileAddress: {
     lldb::offset_t data_offset = 0;
-    address = m_data.GetAddress(&data_offset);
+    address = GetStrippedPointerValue(m_data.GetAddress(&data_offset));
   } break;
   }
 
@@ -3000,6 +3009,11 @@
   if (type) {
     CompilerType pointer_type(type.GetPointerType());
     if (pointer_type) {
+      if (Process *process = exe_ctx.GetProcessPtr()) {
+        if (ABISP abi_sp = process->GetABI()) {
+          address = abi_sp->FixCodeAddress(address);
+        }
+      }
       lldb::DataBufferSP buffer(
           new lldb_private::DataBufferHeap(&address, sizeof(lldb::addr_t)));
       lldb::ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create(
Index: lldb/source/API/SBValue.cpp
===================================================================
--- lldb/source/API/SBValue.cpp
+++ lldb/source/API/SBValue.cpp
@@ -30,6 +30,7 @@
 #include "lldb/Symbol/Type.h"
 #include "lldb/Symbol/Variable.h"
 #include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
 #include "lldb/Target/ExecutionContext.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/StackFrame.h"
@@ -920,6 +921,29 @@
   return fail_value;
 }
 
+lldb::addr_t SBValue::GetValueAsAddress() {
+  addr_t fail_value = LLDB_INVALID_ADDRESS;
+  ValueLocker locker;
+  lldb::ValueObjectSP value_sp(GetSP(locker));
+  if (value_sp) {
+    bool success = true;
+    uint64_t ret_val = fail_value;
+    ret_val = value_sp->GetValueAsUnsigned(fail_value, &success);
+    if (!success) {
+      return fail_value;
+    }
+    ProcessSP process_sp = m_opaque_sp->GetProcessSP();
+    if (!process_sp)
+      return ret_val;
+    ABISP abi_sp = process_sp->GetABI();
+    if (abi_sp)
+      return abi_sp->FixCodeAddress(ret_val);
+    return ret_val;
+  }
+
+  return fail_value;
+}
+
 bool SBValue::MightHaveChildren() {
   LLDB_INSTRUMENT_VA(this);
 
Index: lldb/include/lldb/Core/ValueObject.h
===================================================================
--- lldb/include/lldb/Core/ValueObject.h
+++ lldb/include/lldb/Core/ValueObject.h
@@ -564,6 +564,10 @@
 
   lldb::addr_t GetPointerValue(AddressType *address_type = nullptr);
 
+  /// Remove TBI/MTE/ptrauth bits from address, if those are defined on this
+  /// target/ABI.
+  lldb::addr_t GetStrippedPointerValue(lldb::addr_t address);
+
   lldb::ValueObjectSP GetSyntheticChild(ConstString key) const;
 
   lldb::ValueObjectSP GetSyntheticArrayMember(size_t index, bool can_create);
Index: lldb/include/lldb/API/SBValue.h
===================================================================
--- lldb/include/lldb/API/SBValue.h
+++ lldb/include/lldb/API/SBValue.h
@@ -62,6 +62,8 @@
 
   uint64_t GetValueAsUnsigned(uint64_t fail_value = 0);
 
+  lldb::addr_t GetValueAsAddress();
+
   ValueType GetValueType();
 
   // If you call this on a newly created ValueObject, it will always return
Index: lldb/bindings/interface/SBValue.i
===================================================================
--- lldb/bindings/interface/SBValue.i
+++ lldb/bindings/interface/SBValue.i
@@ -113,6 +113,28 @@
     uint64_t
     GetValueAsUnsigned(uint64_t fail_value=0);
 
+    %feature("docstring", 
+"     // Return the value as an address.  On failure, LLDB_INVALID_ADDRESS 
+      // will be returned.  On architectures like AArch64, where the top 
+      // (unaddressable) bits can be used for authentication, memory tagging, 
+      // or top byte ignore,  this method will return the value with those 
+      // top bits cleared.  
+      //
+      // GetValueAsUnsigned returns the actual value, with the 
+      // authentication/TBI/MTE bits.  
+      //
+      // Calling this on a random value which is not a pointer is an
+      // incorrect.  Call GetType().IsPointerType() if in doubt.  
+      //
+      // An SB API program may want to show both the literal byte value
+      // and the address it refers to in memory.  These two SBValue methods
+      // allow SB API writers to behave appropriately for their 
+      // interface."
+    ) GetValueAsAddress;
+
+    lldb::addr_t
+    GetValueAsAddress();
+
     ValueType
     GetValueType ();
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to