
Fixes https://github.com/llvm/llvm-project/issues/135707 Follow up to https://github.com/llvm/llvm-project/pull/148836 which fixed some of this issue but not all of it. Our Value/ValueObject system does not store the endian directly in the values. Instead it assumes that the endian of the result of a cast can be assumed to be the target's endian, or the host but only as a fallback. It assumes the place it is copying from is also that endian. This breaks down when you have register values. These are always host endian and continue to be when cast. Casting them to big endian when on a little endian host breaks certain calls like GetValueAsUnsigned. To fix this, check the context of the value. If it has a register context, always treat it as host endian and make the result host endian. I had an alternative where I passed an "is_register" flag into all calls to this, but it felt like a layering violation and changed many more lines. This solution isn't much more robust, but it works for all the test cases I know of. Perhaps you can create a register value without a RegisterInfo backing it, but I don't know of a way myself. For testing, I had to add a minimal program file for each arch so that there is a type system to support the casting. This is generated from YAML since we only need the machine and endian to be set.
703 lines
22 KiB
C++
703 lines
22 KiB
C++
//===-- Value.cpp ---------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Core/Value.h"
|
|
|
|
#include "lldb/Core/Address.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Symbol/CompilerType.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/SymbolContext.h"
|
|
#include "lldb/Symbol/Type.h"
|
|
#include "lldb/Symbol/Variable.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/SectionLoadList.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Utility/ConstString.h"
|
|
#include "lldb/Utility/DataBufferHeap.h"
|
|
#include "lldb/Utility/DataExtractor.h"
|
|
#include "lldb/Utility/Endian.h"
|
|
#include "lldb/Utility/FileSpec.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/State.h"
|
|
#include "lldb/Utility/Stream.h"
|
|
#include "lldb/lldb-defines.h"
|
|
#include "lldb/lldb-forward.h"
|
|
#include "lldb/lldb-types.h"
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
|
|
#include <cinttypes>
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
Value::Value() : m_value(), m_compiler_type(), m_data_buffer() {}
|
|
|
|
Value::Value(const Scalar &scalar)
|
|
: m_value(scalar), m_compiler_type(), m_data_buffer() {}
|
|
|
|
Value::Value(const void *bytes, int len)
|
|
: m_value(), m_compiler_type(), m_value_type(ValueType::HostAddress),
|
|
m_data_buffer() {
|
|
SetBytes(bytes, len);
|
|
}
|
|
|
|
Value::Value(const Value &v)
|
|
: m_value(v.m_value), m_compiler_type(v.m_compiler_type),
|
|
m_context(v.m_context), m_value_type(v.m_value_type),
|
|
m_context_type(v.m_context_type), m_data_buffer() {
|
|
const uintptr_t rhs_value =
|
|
(uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS);
|
|
if ((rhs_value != 0) &&
|
|
(rhs_value == (uintptr_t)v.m_data_buffer.GetBytes())) {
|
|
m_data_buffer.CopyData(v.m_data_buffer.GetBytes(),
|
|
v.m_data_buffer.GetByteSize());
|
|
|
|
m_value = (uintptr_t)m_data_buffer.GetBytes();
|
|
}
|
|
}
|
|
|
|
Value &Value::operator=(const Value &rhs) {
|
|
if (this != &rhs) {
|
|
m_value = rhs.m_value;
|
|
m_compiler_type = rhs.m_compiler_type;
|
|
m_context = rhs.m_context;
|
|
m_value_type = rhs.m_value_type;
|
|
m_context_type = rhs.m_context_type;
|
|
const uintptr_t rhs_value =
|
|
(uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS);
|
|
if ((rhs_value != 0) &&
|
|
(rhs_value == (uintptr_t)rhs.m_data_buffer.GetBytes())) {
|
|
m_data_buffer.CopyData(rhs.m_data_buffer.GetBytes(),
|
|
rhs.m_data_buffer.GetByteSize());
|
|
|
|
m_value = (uintptr_t)m_data_buffer.GetBytes();
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void Value::SetBytes(const void *bytes, int len) {
|
|
m_value_type = ValueType::HostAddress;
|
|
m_data_buffer.CopyData(bytes, len);
|
|
m_value = (uintptr_t)m_data_buffer.GetBytes();
|
|
}
|
|
|
|
void Value::AppendBytes(const void *bytes, int len) {
|
|
m_value_type = ValueType::HostAddress;
|
|
m_data_buffer.AppendData(bytes, len);
|
|
m_value = (uintptr_t)m_data_buffer.GetBytes();
|
|
}
|
|
|
|
void Value::Dump(Stream *strm) {
|
|
if (!strm)
|
|
return;
|
|
m_value.GetValue(*strm, true);
|
|
strm->Printf(", value_type = %s, context = %p, context_type = %s",
|
|
Value::GetValueTypeAsCString(m_value_type), m_context,
|
|
Value::GetContextTypeAsCString(m_context_type));
|
|
}
|
|
|
|
Value::ValueType Value::GetValueType() const { return m_value_type; }
|
|
|
|
AddressType Value::GetValueAddressType() const {
|
|
switch (m_value_type) {
|
|
case ValueType::Invalid:
|
|
case ValueType::Scalar:
|
|
break;
|
|
case ValueType::LoadAddress:
|
|
return eAddressTypeLoad;
|
|
case ValueType::FileAddress:
|
|
return eAddressTypeFile;
|
|
case ValueType::HostAddress:
|
|
return eAddressTypeHost;
|
|
}
|
|
return eAddressTypeInvalid;
|
|
}
|
|
|
|
Value::ValueType Value::GetValueTypeFromAddressType(AddressType address_type) {
|
|
switch (address_type) {
|
|
case eAddressTypeFile:
|
|
return Value::ValueType::FileAddress;
|
|
case eAddressTypeLoad:
|
|
return Value::ValueType::LoadAddress;
|
|
case eAddressTypeHost:
|
|
return Value::ValueType::HostAddress;
|
|
case eAddressTypeInvalid:
|
|
return Value::ValueType::Invalid;
|
|
}
|
|
llvm_unreachable("Unexpected address type!");
|
|
}
|
|
|
|
RegisterInfo *Value::GetRegisterInfo() const {
|
|
if (m_context_type == ContextType::RegisterInfo)
|
|
return static_cast<RegisterInfo *>(m_context);
|
|
return nullptr;
|
|
}
|
|
|
|
Type *Value::GetType() {
|
|
if (m_context_type == ContextType::LLDBType)
|
|
return static_cast<Type *>(m_context);
|
|
return nullptr;
|
|
}
|
|
|
|
size_t Value::AppendDataToHostBuffer(const Value &rhs) {
|
|
if (this == &rhs)
|
|
return 0;
|
|
|
|
size_t curr_size = m_data_buffer.GetByteSize();
|
|
Status error;
|
|
switch (rhs.GetValueType()) {
|
|
case ValueType::Invalid:
|
|
return 0;
|
|
case ValueType::Scalar: {
|
|
const size_t scalar_size = rhs.m_value.GetByteSize();
|
|
if (scalar_size > 0) {
|
|
const size_t new_size = curr_size + scalar_size;
|
|
if (ResizeData(new_size) == new_size) {
|
|
rhs.m_value.GetAsMemoryData(m_data_buffer.GetBytes() + curr_size,
|
|
scalar_size, endian::InlHostByteOrder(),
|
|
error);
|
|
return scalar_size;
|
|
}
|
|
}
|
|
} break;
|
|
case ValueType::FileAddress:
|
|
case ValueType::LoadAddress:
|
|
case ValueType::HostAddress: {
|
|
const uint8_t *src = rhs.GetBuffer().GetBytes();
|
|
const size_t src_len = rhs.GetBuffer().GetByteSize();
|
|
if (src && src_len > 0) {
|
|
const size_t new_size = curr_size + src_len;
|
|
if (ResizeData(new_size) == new_size) {
|
|
::memcpy(m_data_buffer.GetBytes() + curr_size, src, src_len);
|
|
return src_len;
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
size_t Value::ResizeData(size_t len) {
|
|
m_value_type = ValueType::HostAddress;
|
|
m_data_buffer.SetByteSize(len);
|
|
m_value = (uintptr_t)m_data_buffer.GetBytes();
|
|
return m_data_buffer.GetByteSize();
|
|
}
|
|
|
|
bool Value::ValueOf(ExecutionContext *exe_ctx) {
|
|
switch (m_context_type) {
|
|
case ContextType::Invalid:
|
|
case ContextType::RegisterInfo: // RegisterInfo *
|
|
case ContextType::LLDBType: // Type *
|
|
break;
|
|
|
|
case ContextType::Variable: // Variable *
|
|
ResolveValue(exe_ctx);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint64_t Value::GetValueByteSize(Status *error_ptr, ExecutionContext *exe_ctx) {
|
|
switch (m_context_type) {
|
|
case ContextType::RegisterInfo: // RegisterInfo *
|
|
if (GetRegisterInfo()) {
|
|
if (error_ptr)
|
|
error_ptr->Clear();
|
|
return GetRegisterInfo()->byte_size;
|
|
}
|
|
break;
|
|
|
|
case ContextType::Invalid:
|
|
case ContextType::LLDBType: // Type *
|
|
case ContextType::Variable: // Variable *
|
|
{
|
|
auto *scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
|
|
auto size_or_err = GetCompilerType().GetByteSize(scope);
|
|
if (!size_or_err) {
|
|
if (error_ptr && error_ptr->Success())
|
|
*error_ptr = Status::FromError(size_or_err.takeError());
|
|
else
|
|
LLDB_LOG_ERRORV(GetLog(LLDBLog::Types), size_or_err.takeError(), "{0}");
|
|
} else {
|
|
if (error_ptr)
|
|
error_ptr->Clear();
|
|
return *size_or_err;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (error_ptr && error_ptr->Success())
|
|
*error_ptr = Status::FromErrorString("Unable to determine byte size.");
|
|
return 0;
|
|
}
|
|
|
|
const CompilerType &Value::GetCompilerType() {
|
|
if (!m_compiler_type.IsValid()) {
|
|
switch (m_context_type) {
|
|
case ContextType::Invalid:
|
|
break;
|
|
|
|
case ContextType::RegisterInfo:
|
|
break; // TODO: Eventually convert into a compiler type?
|
|
|
|
case ContextType::LLDBType: {
|
|
Type *lldb_type = GetType();
|
|
if (lldb_type)
|
|
m_compiler_type = lldb_type->GetForwardCompilerType();
|
|
} break;
|
|
|
|
case ContextType::Variable: {
|
|
Variable *variable = GetVariable();
|
|
if (variable) {
|
|
Type *variable_type = variable->GetType();
|
|
if (variable_type)
|
|
m_compiler_type = variable_type->GetForwardCompilerType();
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
return m_compiler_type;
|
|
}
|
|
|
|
void Value::SetCompilerType(const CompilerType &compiler_type) {
|
|
m_compiler_type = compiler_type;
|
|
}
|
|
|
|
lldb::Format Value::GetValueDefaultFormat() {
|
|
switch (m_context_type) {
|
|
case ContextType::RegisterInfo:
|
|
if (GetRegisterInfo())
|
|
return GetRegisterInfo()->format;
|
|
break;
|
|
|
|
case ContextType::Invalid:
|
|
case ContextType::LLDBType:
|
|
case ContextType::Variable: {
|
|
const CompilerType &ast_type = GetCompilerType();
|
|
if (ast_type.IsValid())
|
|
return ast_type.GetFormat();
|
|
} break;
|
|
}
|
|
|
|
// Return a good default in case we can't figure anything out
|
|
return eFormatHex;
|
|
}
|
|
|
|
bool Value::GetData(DataExtractor &data) {
|
|
switch (m_value_type) {
|
|
case ValueType::Invalid:
|
|
return false;
|
|
case ValueType::Scalar:
|
|
if (m_value.GetData(data))
|
|
return true;
|
|
break;
|
|
|
|
case ValueType::LoadAddress:
|
|
case ValueType::FileAddress:
|
|
case ValueType::HostAddress:
|
|
if (m_data_buffer.GetByteSize()) {
|
|
data.SetData(m_data_buffer.GetBytes(), m_data_buffer.GetByteSize(),
|
|
data.GetByteOrder());
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data,
|
|
Module *module) {
|
|
data.Clear();
|
|
|
|
Status error;
|
|
lldb::addr_t address = LLDB_INVALID_ADDRESS;
|
|
AddressType address_type = eAddressTypeFile;
|
|
Address file_so_addr;
|
|
const CompilerType &ast_type = GetCompilerType();
|
|
std::optional<uint64_t> type_size =
|
|
llvm::expectedToOptional(ast_type.GetByteSize(
|
|
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr));
|
|
// Nothing to be done for a zero-sized type.
|
|
if (type_size && *type_size == 0)
|
|
return error;
|
|
|
|
switch (m_value_type) {
|
|
case ValueType::Invalid:
|
|
error = Status::FromErrorString("invalid value");
|
|
break;
|
|
case ValueType::Scalar: {
|
|
data.SetByteOrder(endian::InlHostByteOrder());
|
|
if (ast_type.IsValid())
|
|
data.SetAddressByteSize(ast_type.GetPointerByteSize());
|
|
else
|
|
data.SetAddressByteSize(sizeof(void *));
|
|
|
|
uint32_t result_byte_size = *type_size;
|
|
if (m_value.GetData(data, result_byte_size))
|
|
return error; // Success;
|
|
|
|
error = Status::FromErrorString("extracting data from value failed");
|
|
break;
|
|
}
|
|
case ValueType::LoadAddress:
|
|
if (exe_ctx == nullptr) {
|
|
error = Status::FromErrorString(
|
|
"can't read load address (no execution context)");
|
|
} else {
|
|
Process *process = exe_ctx->GetProcessPtr();
|
|
if (process == nullptr || !process->IsAlive()) {
|
|
Target *target = exe_ctx->GetTargetPtr();
|
|
if (target) {
|
|
// Allow expressions to run and evaluate things when the target has
|
|
// memory sections loaded. This allows you to use "target modules
|
|
// load" to load your executable and any shared libraries, then
|
|
// execute commands where you can look at types in data sections.
|
|
if (target->HasLoadedSections()) {
|
|
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
|
|
if (target->ResolveLoadAddress(address, file_so_addr)) {
|
|
address_type = eAddressTypeLoad;
|
|
data.SetByteOrder(target->GetArchitecture().GetByteOrder());
|
|
data.SetAddressByteSize(
|
|
target->GetArchitecture().GetAddressByteSize());
|
|
} else
|
|
address = LLDB_INVALID_ADDRESS;
|
|
}
|
|
} else {
|
|
error = Status::FromErrorString(
|
|
"can't read load address (invalid process)");
|
|
}
|
|
} else {
|
|
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
|
|
address_type = eAddressTypeLoad;
|
|
data.SetByteOrder(
|
|
process->GetTarget().GetArchitecture().GetByteOrder());
|
|
data.SetAddressByteSize(
|
|
process->GetTarget().GetArchitecture().GetAddressByteSize());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ValueType::FileAddress:
|
|
if (exe_ctx == nullptr) {
|
|
error = Status::FromErrorString(
|
|
"can't read file address (no execution context)");
|
|
} else if (exe_ctx->GetTargetPtr() == nullptr) {
|
|
error =
|
|
Status::FromErrorString("can't read file address (invalid target)");
|
|
} else {
|
|
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
|
|
if (address == LLDB_INVALID_ADDRESS) {
|
|
error = Status::FromErrorString("invalid file address");
|
|
} else {
|
|
if (module == nullptr) {
|
|
// The only thing we can currently lock down to a module so that we
|
|
// can resolve a file address, is a variable.
|
|
Variable *variable = GetVariable();
|
|
if (variable) {
|
|
SymbolContext var_sc;
|
|
variable->CalculateSymbolContext(&var_sc);
|
|
module = var_sc.module_sp.get();
|
|
}
|
|
}
|
|
|
|
if (module) {
|
|
bool resolved = false;
|
|
ObjectFile *objfile = module->GetObjectFile();
|
|
if (objfile) {
|
|
Address so_addr(address, objfile->GetSectionList());
|
|
addr_t load_address =
|
|
so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
|
|
bool process_launched_and_stopped =
|
|
exe_ctx->GetProcessPtr()
|
|
? StateIsStoppedState(exe_ctx->GetProcessPtr()->GetState(),
|
|
true /* must_exist */)
|
|
: false;
|
|
// Don't use the load address if the process has exited.
|
|
if (load_address != LLDB_INVALID_ADDRESS &&
|
|
process_launched_and_stopped) {
|
|
resolved = true;
|
|
address = load_address;
|
|
address_type = eAddressTypeLoad;
|
|
data.SetByteOrder(
|
|
exe_ctx->GetTargetRef().GetArchitecture().GetByteOrder());
|
|
data.SetAddressByteSize(exe_ctx->GetTargetRef()
|
|
.GetArchitecture()
|
|
.GetAddressByteSize());
|
|
} else {
|
|
if (so_addr.IsSectionOffset()) {
|
|
resolved = true;
|
|
file_so_addr = so_addr;
|
|
data.SetByteOrder(objfile->GetByteOrder());
|
|
data.SetAddressByteSize(objfile->GetAddressByteSize());
|
|
}
|
|
}
|
|
}
|
|
if (!resolved) {
|
|
Variable *variable = GetVariable();
|
|
|
|
if (module) {
|
|
if (variable)
|
|
error = Status::FromErrorStringWithFormat(
|
|
"unable to resolve the module for file address 0x%" PRIx64
|
|
" for variable '%s' in %s",
|
|
address, variable->GetName().AsCString(""),
|
|
module->GetFileSpec().GetPath().c_str());
|
|
else
|
|
error = Status::FromErrorStringWithFormat(
|
|
"unable to resolve the module for file address 0x%" PRIx64
|
|
" in %s",
|
|
address, module->GetFileSpec().GetPath().c_str());
|
|
} else {
|
|
if (variable)
|
|
error = Status::FromErrorStringWithFormat(
|
|
"unable to resolve the module for file address 0x%" PRIx64
|
|
" for variable '%s'",
|
|
address, variable->GetName().AsCString(""));
|
|
else
|
|
error = Status::FromErrorStringWithFormat(
|
|
"unable to resolve the module for file address 0x%" PRIx64,
|
|
address);
|
|
}
|
|
}
|
|
} else {
|
|
// Can't convert a file address to anything valid without more
|
|
// context (which Module it came from)
|
|
error = Status::FromErrorString(
|
|
"can't read memory from file address without more context");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ValueType::HostAddress:
|
|
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
|
|
address_type = eAddressTypeHost;
|
|
if (exe_ctx) {
|
|
if (Target *target = exe_ctx->GetTargetPtr()) {
|
|
// Registers are always stored in host endian.
|
|
data.SetByteOrder(m_context_type == ContextType::RegisterInfo
|
|
? endian::InlHostByteOrder()
|
|
: target->GetArchitecture().GetByteOrder());
|
|
data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
|
|
break;
|
|
}
|
|
}
|
|
// fallback to host settings
|
|
data.SetByteOrder(endian::InlHostByteOrder());
|
|
data.SetAddressByteSize(sizeof(void *));
|
|
break;
|
|
}
|
|
|
|
// Bail if we encountered any errors
|
|
if (error.Fail())
|
|
return error;
|
|
|
|
if (address == LLDB_INVALID_ADDRESS) {
|
|
error = Status::FromErrorStringWithFormat(
|
|
"invalid %s address",
|
|
address_type == eAddressTypeHost ? "host" : "load");
|
|
return error;
|
|
}
|
|
|
|
// If we got here, we need to read the value from memory.
|
|
size_t byte_size = GetValueByteSize(&error, exe_ctx);
|
|
|
|
// Bail if we encountered any errors getting the byte size.
|
|
if (error.Fail())
|
|
return error;
|
|
|
|
// No memory to read for zero-sized types.
|
|
if (byte_size == 0)
|
|
return error;
|
|
|
|
// Make sure we have enough room within "data", and if we don't make
|
|
// something large enough that does
|
|
if (!data.ValidOffsetForDataOfSize(0, byte_size)) {
|
|
auto data_sp = std::make_shared<DataBufferHeap>(byte_size, '\0');
|
|
data.SetData(data_sp);
|
|
}
|
|
|
|
uint8_t *dst = const_cast<uint8_t *>(data.PeekData(0, byte_size));
|
|
if (dst != nullptr) {
|
|
if (address_type == eAddressTypeHost) {
|
|
// The address is an address in this process, so just copy it.
|
|
if (address == 0) {
|
|
error =
|
|
Status::FromErrorString("trying to read from host address of 0.");
|
|
return error;
|
|
}
|
|
memcpy(dst, reinterpret_cast<uint8_t *>(address), byte_size);
|
|
} else if ((address_type == eAddressTypeLoad) ||
|
|
(address_type == eAddressTypeFile)) {
|
|
if (file_so_addr.IsValid()) {
|
|
const bool force_live_memory = true;
|
|
if (exe_ctx->GetTargetRef().ReadMemory(file_so_addr, dst, byte_size,
|
|
error, force_live_memory) !=
|
|
byte_size) {
|
|
error = Status::FromErrorStringWithFormat(
|
|
"read memory from 0x%" PRIx64 " failed", (uint64_t)address);
|
|
}
|
|
} else {
|
|
// The execution context might have a NULL process, but it might have a
|
|
// valid process in the exe_ctx->target, so use the
|
|
// ExecutionContext::GetProcess accessor to ensure we get the process
|
|
// if there is one.
|
|
Process *process = exe_ctx->GetProcessPtr();
|
|
|
|
if (process) {
|
|
const size_t bytes_read =
|
|
process->ReadMemory(address, dst, byte_size, error);
|
|
if (bytes_read != byte_size)
|
|
error = Status::FromErrorStringWithFormat(
|
|
"read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
|
|
(uint64_t)address, (uint32_t)bytes_read, (uint32_t)byte_size);
|
|
} else {
|
|
error = Status::FromErrorStringWithFormat(
|
|
"read memory from 0x%" PRIx64 " failed (invalid process)",
|
|
(uint64_t)address);
|
|
}
|
|
}
|
|
} else {
|
|
error = Status::FromErrorStringWithFormat(
|
|
"unsupported AddressType value (%i)", address_type);
|
|
}
|
|
} else {
|
|
error = Status::FromErrorString("out of memory");
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
Scalar &Value::ResolveValue(ExecutionContext *exe_ctx, Module *module) {
|
|
const CompilerType &compiler_type = GetCompilerType();
|
|
if (compiler_type.IsValid()) {
|
|
switch (m_value_type) {
|
|
case ValueType::Invalid:
|
|
case ValueType::Scalar: // raw scalar value
|
|
break;
|
|
|
|
case ValueType::FileAddress:
|
|
case ValueType::LoadAddress: // load address value
|
|
case ValueType::HostAddress: // host address value (for memory in the process
|
|
// that is using liblldb)
|
|
{
|
|
DataExtractor data;
|
|
lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS);
|
|
Status error(GetValueAsData(exe_ctx, data, module));
|
|
if (error.Success()) {
|
|
Scalar scalar;
|
|
if (compiler_type.GetValueAsScalar(
|
|
data, 0, data.GetByteSize(), scalar,
|
|
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr)) {
|
|
m_value = scalar;
|
|
m_value_type = ValueType::Scalar;
|
|
} else {
|
|
if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) {
|
|
m_value.Clear();
|
|
m_value_type = ValueType::Scalar;
|
|
}
|
|
}
|
|
} else {
|
|
if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) {
|
|
m_value.Clear();
|
|
m_value_type = ValueType::Scalar;
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
return m_value;
|
|
}
|
|
|
|
Variable *Value::GetVariable() {
|
|
if (m_context_type == ContextType::Variable)
|
|
return static_cast<Variable *>(m_context);
|
|
return nullptr;
|
|
}
|
|
|
|
void Value::Clear() {
|
|
m_value.Clear();
|
|
m_compiler_type.Clear();
|
|
m_value_type = ValueType::Scalar;
|
|
m_context = nullptr;
|
|
m_context_type = ContextType::Invalid;
|
|
m_data_buffer.Clear();
|
|
}
|
|
|
|
const char *Value::GetValueTypeAsCString(ValueType value_type) {
|
|
switch (value_type) {
|
|
case ValueType::Invalid:
|
|
return "invalid";
|
|
case ValueType::Scalar:
|
|
return "scalar";
|
|
case ValueType::FileAddress:
|
|
return "file address";
|
|
case ValueType::LoadAddress:
|
|
return "load address";
|
|
case ValueType::HostAddress:
|
|
return "host address";
|
|
};
|
|
llvm_unreachable("enum cases exhausted.");
|
|
}
|
|
|
|
const char *Value::GetContextTypeAsCString(ContextType context_type) {
|
|
switch (context_type) {
|
|
case ContextType::Invalid:
|
|
return "invalid";
|
|
case ContextType::RegisterInfo:
|
|
return "RegisterInfo *";
|
|
case ContextType::LLDBType:
|
|
return "Type *";
|
|
case ContextType::Variable:
|
|
return "Variable *";
|
|
};
|
|
llvm_unreachable("enum cases exhausted.");
|
|
}
|
|
|
|
void Value::ConvertToLoadAddress(Module *module, Target *target) {
|
|
if (!module || !target || (GetValueType() != ValueType::FileAddress))
|
|
return;
|
|
|
|
lldb::addr_t file_addr = GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
if (file_addr == LLDB_INVALID_ADDRESS)
|
|
return;
|
|
|
|
Address so_addr;
|
|
if (!module->ResolveFileAddress(file_addr, so_addr))
|
|
return;
|
|
lldb::addr_t load_addr = so_addr.GetLoadAddress(target);
|
|
if (load_addr == LLDB_INVALID_ADDRESS)
|
|
return;
|
|
|
|
SetValueType(Value::ValueType::LoadAddress);
|
|
GetScalar() = load_addr;
|
|
}
|
|
|
|
void ValueList::PushValue(const Value &value) { m_values.push_back(value); }
|
|
|
|
size_t ValueList::GetSize() { return m_values.size(); }
|
|
|
|
Value *ValueList::GetValueAtIndex(size_t idx) {
|
|
if (idx < GetSize()) {
|
|
return &(m_values[idx]);
|
|
} else
|
|
return nullptr;
|
|
}
|
|
|
|
void ValueList::Clear() { m_values.clear(); }
|