mirror of
https://github.com/intel/llvm.git
synced 2026-01-15 12:25:46 +08:00
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
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
C_SOURCES := main.c
|
||||
|
||||
include Makefile.rules
|
||||
@@ -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())
|
||||
25
lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c
Normal file
25
lldb/test/API/api/clear-sbvalue-nonadressable-bits/main.c
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user