| //===-- 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/RegisterType.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 *)); | |
| if (!type_size) | |
| return Status::FromErrorString("type does not have a size"); | |
| 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()) { | |
| ByteOrder byte_order = target->GetArchitecture().GetByteOrder(); | |
| // ValueObjectRegister stores structured register types in target byte | |
| // order and all other register buffers in host byte order. | |
| if (m_context_type == ContextType::RegisterInfo && | |
| !llvm::isa_and_present<RegisterTypeComposite>( | |
| GetRegisterInfo()->register_type)) | |
| byte_order = endian::InlHostByteOrder(); | |
| data.SetByteOrder(byte_order); | |
| 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(); } |