From 4d635be2dbadc77522eddc9668697385a3b9f8b4 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 2 Mar 2023 13:28:34 -0800 Subject: [PATCH] Add SBValue::GetValueAsAddress API for removing non-addressing metadata On target where metadata is stored in bits that aren't used for virtual addressing -- AArch64 Top Byte Ignore and pointer authentication are two examples -- an SBValue object representing a pointer will return the address with metadata for SBValue::GetValueAsUnsigned. Users may want to get the virtual address without the metadata; this new method gives them a way to do this. Differential Revision: https://reviews.llvm.org/D142792 --- lldb/bindings/interface/SBValueDocstrings.i | 18 +++++++ lldb/include/lldb/API/SBValue.h | 2 + lldb/include/lldb/Core/ValueObject.h | 4 ++ lldb/source/API/SBValue.cpp | 20 ++++++++ lldb/source/Core/ValueObject.cpp | 22 +++++++- lldb/source/DataFormatters/ValueObjectPrinter.cpp | 7 +++ .../api/clear-sbvalue-nonadressable-bits/Makefile | 3 ++ .../TestClearSBValueNonAddressableBits.py | 58 ++++++++++++++++++++++ .../api/clear-sbvalue-nonadressable-bits/main.c | 25 ++++++++++ 9 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 lldb/test/API/api/clear-sbvalue-nonadressable-bits/Makefile create mode 100644 lldb/test/API/api/clear-sbvalue-nonadressable-bits/TestClearSBValueNonAddressableBits.py create mode 100644 lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c diff --git a/lldb/bindings/interface/SBValueDocstrings.i b/lldb/bindings/interface/SBValueDocstrings.i index 6bab923..0bdd386 100644 --- a/lldb/bindings/interface/SBValueDocstrings.i +++ b/lldb/bindings/interface/SBValueDocstrings.i @@ -135,6 +135,24 @@ linked list." %feature("docstring", "Expands nested expressions like .a->b[0].c[1]->d." ) lldb::SBValue::GetValueForExpressionPath; +%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/Top Byte Ignore/Memory Tagging Extension bits. + + Calling this on a random value which is not a pointer is + 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.") lldb::SBValue::GetValueAsAddress; + %feature("doctstring", " Returns the number for children. diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h index 1cba492..6409bb1 100644 --- a/lldb/include/lldb/API/SBValue.h +++ b/lldb/include/lldb/API/SBValue.h @@ -62,6 +62,8 @@ public: 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 diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index a666d0b..d249fc2 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -564,6 +564,10 @@ public: 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); diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index ecd16cc..2c853c7 100644 --- a/lldb/source/API/SBValue.cpp +++ b/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" @@ -924,6 +925,25 @@ uint64_t SBValue::GetValueAsUnsigned(uint64_t fail_value) { 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; + if (ProcessSP process_sp = m_opaque_sp->GetProcessSP()) + if (ABISP abi_sp = process_sp->GetABI()) + return abi_sp->FixCodeAddress(ret_val); + return ret_val; + } + + return fail_value; +} + bool SBValue::MightHaveChildren() { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 6e79a04..821f2b3 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/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" @@ -1453,6 +1454,14 @@ addr_t ValueObject::GetAddressOf(bool scalar_is_load_address, 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) @@ -1468,11 +1477,15 @@ addr_t ValueObject::GetPointerValue(AddressType *address_type) { address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); break; - case Value::ValueType::HostAddress: + case Value::ValueType::HostAddress: { + lldb::offset_t data_offset = 0; + address = m_data.GetAddress(&data_offset); + } break; + 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; } @@ -3011,6 +3024,11 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress( 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( diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp index 17e9877..4f73cee 100644 --- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -440,6 +440,13 @@ bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed, if (!m_options.m_hide_name) m_stream->PutChar(' '); m_stream->PutCString(m_value); + if (IsPointerValue(m_valobj->GetCompilerType())) { + uint64_t orig_value = m_valobj->GetValueAsUnsigned(0); + addr_t stripped = m_valobj->GetPointerValue(); + if (stripped != orig_value) { + m_stream->Printf(" (actual=0x%" PRIx64 ")", stripped); + } + } value_printed = true; } } diff --git a/lldb/test/API/api/clear-sbvalue-nonadressable-bits/Makefile b/lldb/test/API/api/clear-sbvalue-nonadressable-bits/Makefile new file mode 100644 index 0000000..1049594 --- /dev/null +++ b/lldb/test/API/api/clear-sbvalue-nonadressable-bits/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/api/clear-sbvalue-nonadressable-bits/TestClearSBValueNonAddressableBits.py b/lldb/test/API/api/clear-sbvalue-nonadressable-bits/TestClearSBValueNonAddressableBits.py new file mode 100644 index 0000000..0de98ec --- /dev/null +++ b/lldb/test/API/api/clear-sbvalue-nonadressable-bits/TestClearSBValueNonAddressableBits.py @@ -0,0 +1,58 @@ +"""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'])) + + # Only run this test on systems where TBI is known to be + # enabled, so the address mask will clear the TBI bits. + @skipUnlessPlatform(["linux"]+lldbplatformutil.getDarwinOSTriples()) + 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 ("frame variable") + self.runCmd ("frame variable &count &global") + + frame = thread.GetFrameAtIndex(0) + + count_p = frame.FindVariable("count_p") + count_invalid_p = frame.FindVariable("count_invalid_p") + self.assertEqual(count_p.GetValueAsUnsigned(), count_invalid_p.GetValueAsAddress()) + self.assertNotEqual(count_invalid_p.GetValueAsUnsigned(), count_invalid_p.GetValueAsAddress()) + self.assertEqual(5, count_p.Dereference().GetValueAsUnsigned()) + self.assertEqual(5, count_invalid_p.Dereference().GetValueAsUnsigned()) + strm = lldb.SBStream() + count_invalid_p.GetDescription(strm) + self.assertIn("actual=0x", strm.GetData()) + + global_p = frame.FindVariable("global_p") + global_invalid_p = frame.FindVariable("global_invalid_p") + self.assertEqual(global_p.GetValueAsUnsigned(), global_invalid_p.GetValueAsAddress()) + self.assertNotEqual(global_invalid_p.GetValueAsUnsigned(), global_invalid_p.GetValueAsAddress()) + self.assertEqual(10, global_p.Dereference().GetValueAsUnsigned()) + self.assertEqual(10, global_invalid_p.Dereference().GetValueAsUnsigned()) + strm = lldb.SBStream() + global_invalid_p.GetDescription(strm) + self.assertIn("actual=0x", strm.GetData()) + + main_p = frame.FindVariable("main_p") + main_invalid_p = frame.FindVariable("main_invalid_p") + self.assertEqual(main_p.GetValueAsUnsigned(), main_invalid_p.GetValueAsAddress()) + strm = lldb.SBStream() + main_invalid_p.GetDescription(strm) + self.assertIn("main at main.c:", strm.GetData()) + self.assertIn("actual=0x", strm.GetData()) diff --git a/lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c b/lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c new file mode 100644 index 0000000..4eaca41 --- /dev/null +++ b/lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c @@ -0,0 +1,25 @@ +int global = 10; + +int main() { + int count = 5; + int *count_p = &count; + + // 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)count_p; + scratch |= (3ULL << 60); + int *count_invalid_p = (int *)scratch; + + int (*main_p)() = main; + scratch = (intptr_t)main_p; + scratch |= (3ULL << 60); + int (*main_invalid_p)() = (int (*)())scratch; + + int *global_p = &global; + scratch = (intptr_t)global_p; + scratch |= (3ULL << 60); + int *global_invalid_p = (int *)scratch; + + return count; // break here +} -- 2.7.4