diff --git a/lldb/bindings/interface/SBValue.i b/lldb/bindings/interface/SBValue.i --- a/lldb/bindings/interface/SBValue.i +++ b/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/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." + ) GetValueAsAddress; + + lldb::addr_t + GetValueAsAddress(); + ValueType GetValueType (); diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h --- a/lldb/include/lldb/API/SBValue.h +++ b/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 diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h --- a/lldb/include/lldb/Core/ValueObject.h +++ b/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); diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp --- 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" @@ -920,6 +921,25 @@ 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 --- 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" @@ -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) @@ -1457,11 +1466,15 @@ 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; } @@ -3000,6 +3013,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( diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp --- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -426,6 +426,11 @@ IsPointerValue(m_valobj->GetCompilerType())) { } else { m_stream->Printf(" %s", m_value.c_str()); + 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 --- /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 --- /dev/null +++ b/lldb/test/API/api/clear-sbvalue-nonadressable-bits/TestClearSBValueNonAddressableBits.py @@ -0,0 +1,43 @@ +"""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 ("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()) + + strm = lldb.SBStream() + v_invalid_p.GetDescription(strm) + 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 --- /dev/null +++ b/lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c @@ -0,0 +1,25 @@ +#include +#include +int global = 10; + +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; + + scratch = (intptr_t) dlsym(RTLD_DEFAULT, "main"); + scratch |= (3ULL << 60); + int (*main_invalid_p)() = (int (*)()) scratch; + + scratch = (intptr_t) &global; + scratch |= (3ULL << 60); + int *global_invalid_p = (int*) scratch; + + return v; // break here +}