//===-- Value.cpp -----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Core/Value.h"
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/DataExtractor.h"
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/State.h"
#include "lldb/Core/Stream.h"
#include "lldb/Symbol/ClangASTType.h"
#include "lldb/Symbol/ClangASTContext.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/Target.h"
using namespace lldb;
using namespace lldb_private;
Value::Value() :
m_value (),
m_vector (),
m_clang_type (),
m_context (NULL),
m_value_type (eValueTypeScalar),
m_context_type (eContextTypeInvalid),
m_data_buffer ()
{
}
Value::Value(const Scalar& scalar) :
m_value (scalar),
m_vector (),
m_clang_type (),
m_context (NULL),
m_value_type (eValueTypeScalar),
m_context_type (eContextTypeInvalid),
m_data_buffer ()
{
}
Value::Value(const uint8_t *bytes, int len) :
m_value (),
m_vector (),
m_clang_type (),
m_context (NULL),
m_value_type (eValueTypeHostAddress),
m_context_type (eContextTypeInvalid),
m_data_buffer ()
{
m_data_buffer.CopyData(bytes, len);
m_value = (uintptr_t)m_data_buffer.GetBytes();
}
Value::Value(const Value &v) :
m_value (v.m_value),
m_vector (v.m_vector),
m_clang_type (v.m_clang_type),
m_context (v.m_context),
m_value_type (v.m_value_type),
m_context_type (v.m_context_type),
m_data_buffer ()
{
if ((uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS) == (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_vector = rhs.m_vector;
m_clang_type = rhs.m_clang_type;
m_context = rhs.m_context;
m_value_type = rhs.m_value_type;
m_context_type = rhs.m_context_type;
if ((uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS) == (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::Dump (Stream* strm)
{
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)
{
default:
case eValueTypeScalar:
break;
case eValueTypeLoadAddress: return eAddressTypeLoad;
case eValueTypeFileAddress: return eAddressTypeFile;
case eValueTypeHostAddress: return eAddressTypeHost;
}
return eAddressTypeInvalid;
}
RegisterInfo *
Value::GetRegisterInfo() const
{
if (m_context_type == eContextTypeRegisterInfo)
return static_cast<RegisterInfo *> (m_context);
return NULL;
}
Type *
Value::GetType()
{
if (m_context_type == eContextTypeLLDBType)
return static_cast<Type *> (m_context);
return NULL;
}
void
Value::ResizeData(size_t len)
{
m_value_type = eValueTypeHostAddress;
m_data_buffer.SetByteSize(len);
m_value = (uintptr_t)m_data_buffer.GetBytes();
}
bool
Value::ValueOf(ExecutionContext *exe_ctx)
{
switch (m_context_type)
{
case eContextTypeInvalid:
case eContextTypeRegisterInfo: // RegisterInfo *
case eContextTypeLLDBType: // Type *
break;
case eContextTypeVariable: // Variable *
ResolveValue(exe_ctx);
return true;
}
return false;
}
uint64_t
Value::GetValueByteSize (Error *error_ptr)
{
uint64_t byte_size = 0;
switch (m_context_type)
{
case eContextTypeRegisterInfo: // RegisterInfo *
if (GetRegisterInfo())
byte_size = GetRegisterInfo()->byte_size;
break;
case eContextTypeInvalid:
case eContextTypeLLDBType: // Type *
case eContextTypeVariable: // Variable *
{
const ClangASTType &ast_type = GetClangType();
if (ast_type.IsValid())
byte_size = ast_type.GetByteSize();
}
break;
}
if (error_ptr)
{
if (byte_size == 0)
{
if (error_ptr->Success())
error_ptr->SetErrorString("Unable to determine byte size.");
}
else
{
error_ptr->Clear();
}
}
return byte_size;
}
const ClangASTType &
Value::GetClangType ()
{
if (!m_clang_type.IsValid())
{
switch (m_context_type)
{
case eContextTypeInvalid:
break;
case eContextTypeRegisterInfo:
break; // TODO: Eventually convert into a clang type?
case eContextTypeLLDBType:
{
Type *lldb_type = GetType();
if (lldb_type)
m_clang_type = lldb_type->GetClangForwardType();
}
break;
case eContextTypeVariable:
{
Variable *variable = GetVariable();
if (variable)
{
Type *variable_type = variable->GetType();
if (variable_type)
m_clang_type = variable_type->GetClangForwardType();
}
}
break;
}
}
return m_clang_type;
}
void
Value::SetClangType (const ClangASTType &clang_type)
{
m_clang_type = clang_type;
}
lldb::Format
Value::GetValueDefaultFormat ()
{
switch (m_context_type)
{
case eContextTypeRegisterInfo:
if (GetRegisterInfo())
return GetRegisterInfo()->format;
break;
case eContextTypeInvalid:
case eContextTypeLLDBType:
case eContextTypeVariable:
{
const ClangASTType &ast_type = GetClangType();
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)
{
default:
break;
case eValueTypeScalar:
if (m_value.GetData (data))
return true;
break;
case eValueTypeLoadAddress:
case eValueTypeFileAddress:
case eValueTypeHostAddress:
if (m_data_buffer.GetByteSize())
{
data.SetData(m_data_buffer.GetBytes(), m_data_buffer.GetByteSize(), data.GetByteOrder());
return true;
}
break;
}
return false;
}
Error
Value::GetValueAsData (ExecutionContext *exe_ctx,
DataExtractor &data,
uint32_t data_offset,
Module *module)
{
data.Clear();
Error error;
lldb::addr_t address = LLDB_INVALID_ADDRESS;
AddressType address_type = eAddressTypeFile;
Address file_so_addr;
const ClangASTType &ast_type = GetClangType();
switch (m_value_type)
{
case eValueTypeVector:
if (ast_type.IsValid())
data.SetAddressByteSize (ast_type.GetPointerByteSize());
else
data.SetAddressByteSize(sizeof(void *));
data.SetData(m_vector.bytes, m_vector.length, m_vector.byte_order);
break;
case eValueTypeScalar:
data.SetByteOrder (lldb::endian::InlHostByteOrder());
if (ast_type.IsValid())
data.SetAddressByteSize (ast_type.GetPointerByteSize());
else
data.SetAddressByteSize(sizeof(void *));
if (m_value.GetData (data))
return error; // Success;
error.SetErrorStringWithFormat("extracting data from value failed");
break;
case eValueTypeLoadAddress:
if (exe_ctx == NULL)
{
error.SetErrorString ("can't read load address (no execution context)");
}
else
{
Process *process = exe_ctx->GetProcessPtr();
if (process == NULL || !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.
const SectionLoadList &target_sections = target->GetSectionLoadList();
if (!target_sections.IsEmpty())
{
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
if (target_sections.ResolveLoadAddress(address, file_so_addr))
{
address_type = eAddressTypeLoad;
data.SetByteOrder(target->GetArchitecture().GetByteOrder());
data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
}
else
address = LLDB_INVALID_ADDRESS;
}
// else
// {
// ModuleSP exe_module_sp (target->GetExecutableModule());
// if (exe_module_sp)
// {
// address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
// if (address != LLDB_INVALID_ADDRESS)
// {
// if (exe_module_sp->ResolveFileAddress(address, file_so_addr))
// {
// data.SetByteOrder(target->GetArchitecture().GetByteOrder());
// data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
// address_type = eAddressTypeFile;
// }
// else
// {
// address = LLDB_INVALID_ADDRESS;
// }
// }
// }
// }
}
else
{
error.SetErrorString ("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 eValueTypeFileAddress:
if (exe_ctx == NULL)
{
error.SetErrorString ("can't read file address (no execution context)");
}
else if (exe_ctx->GetTargetPtr() == NULL)
{
error.SetErrorString ("can't read file address (invalid target)");
}
else
{
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
if (address == LLDB_INVALID_ADDRESS)
{
error.SetErrorString ("invalid file address");
}
else
{
if (module == NULL)
{
// 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.SetErrorStringWithFormat ("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.SetErrorStringWithFormat ("unable to resolve the module for file address 0x%" PRIx64 " in %s",
address,
module->GetFileSpec().GetPath().c_str());
}
else
{
if (variable)
error.SetErrorStringWithFormat ("unable to resolve the module for file address 0x%" PRIx64 " for variable '%s'",
address,
variable->GetName().AsCString(""));
else
error.SetErrorStringWithFormat ("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.SetErrorString ("can't read memory from file address without more context");
}
}
}
break;
case eValueTypeHostAddress:
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
address_type = eAddressTypeHost;
if (exe_ctx)
{
Target *target = exe_ctx->GetTargetPtr();
if (target)
{
data.SetByteOrder(target->GetArchitecture().GetByteOrder());
data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
break;
}
}
// fallback to host settings
data.SetByteOrder(lldb::endian::InlHostByteOrder());
data.SetAddressByteSize(sizeof(void *));
break;
}
// Bail if we encountered any errors
if (error.Fail())
return error;
if (address == LLDB_INVALID_ADDRESS)
{
error.SetErrorStringWithFormat ("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);
// Bail if we encountered any errors getting the byte size
if (error.Fail())
return error;
// Make sure we have enough room within "data", and if we don't make
// something large enough that does
if (!data.ValidOffsetForDataOfSize (data_offset, byte_size))
{
DataBufferSP data_sp(new DataBufferHeap (data_offset + byte_size, '\0'));
data.SetData(data_sp);
}
uint8_t* dst = const_cast<uint8_t*>(data.PeekData (data_offset, byte_size));
if (dst != NULL)
{
if (address_type == eAddressTypeHost)
{
// The address is an address in this process, so just copy it
memcpy (dst, (uint8_t*)NULL + address, byte_size);
}
else if ((address_type == eAddressTypeLoad) || (address_type == eAddressTypeFile))
{
if (file_so_addr.IsValid())
{
// We have a file address that we were able to translate into a
// section offset address so we might be able to read this from
// the object files if we don't have a live process. Lets always
// try and read from the process if we have one though since we
// want to read the actual value by setting "prefer_file_cache"
// to false.
const bool prefer_file_cache = false;
if (exe_ctx->GetTargetRef().ReadMemory(file_so_addr, prefer_file_cache, dst, byte_size, error) != byte_size)
{
error.SetErrorStringWithFormat("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.SetErrorStringWithFormat("read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
(uint64_t)address,
(uint32_t)bytes_read,
(uint32_t)byte_size);
}
else
{
error.SetErrorStringWithFormat("read memory from 0x%" PRIx64 " failed (invalid process)", (uint64_t)address);
}
}
}
else
{
error.SetErrorStringWithFormat ("unsupported AddressType value (%i)", address_type);
}
}
else
{
error.SetErrorStringWithFormat ("out of memory");
}
return error;
}
Scalar &
Value::ResolveValue(ExecutionContext *exe_ctx)
{
const ClangASTType &clang_type = GetClangType();
if (clang_type.IsValid())
{
switch (m_value_type)
{
case eValueTypeScalar: // raw scalar value
break;
default:
case eValueTypeFileAddress:
case eValueTypeLoadAddress: // load address value
case eValueTypeHostAddress: // host address value (for memory in the process that is using liblldb)
{
DataExtractor data;
lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS);
Error error (GetValueAsData (exe_ctx, data, 0, NULL));
if (error.Success())
{
Scalar scalar;
if (clang_type.GetValueAsScalar (data, 0, data.GetByteSize(), scalar))
{
m_value = scalar;
m_value_type = eValueTypeScalar;
}
else
{
if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes())
{
m_value.Clear();
m_value_type = eValueTypeScalar;
}
}
}
else
{
if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes())
{
m_value.Clear();
m_value_type = eValueTypeScalar;
}
}
}
break;
}
}
return m_value;
}
Variable *
Value::GetVariable()
{
if (m_context_type == eContextTypeVariable)
return static_cast<Variable *> (m_context);
return NULL;
}
void
Value::Clear()
{
m_value.Clear();
m_vector.Clear();
m_clang_type.Clear();
m_value_type = eValueTypeScalar;
m_context = NULL;
m_context_type = eContextTypeInvalid;
m_data_buffer.Clear();
}
const char *
Value::GetValueTypeAsCString (ValueType value_type)
{
switch (value_type)
{
case eValueTypeScalar: return "scalar";
case eValueTypeVector: return "vector";
case eValueTypeFileAddress: return "file address";
case eValueTypeLoadAddress: return "load address";
case eValueTypeHostAddress: return "host address";
};
return "???";
}
const char *
Value::GetContextTypeAsCString (ContextType context_type)
{
switch (context_type)
{
case eContextTypeInvalid: return "invalid";
case eContextTypeRegisterInfo: return "RegisterInfo *";
case eContextTypeLLDBType: return "Type *";
case eContextTypeVariable: return "Variable *";
};
return "???";
}
ValueList::ValueList (const ValueList &rhs)
{
m_values = rhs.m_values;
}
const ValueList &
ValueList::operator= (const ValueList &rhs)
{
m_values = rhs.m_values;
return *this;
}
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 NULL;
}
void
ValueList::Clear ()
{
m_values.clear();
}