[lldb] Use vFlash commands when writing to target's flash memory regions
Summary: When writing an object file over gdb-remote, use the vFlashErase, vFlashWrite, and vFlashDone commands if the write address is in a flash memory region. A bare metal target may have this kind of setup. - Update ObjectFileELF to set load addresses using physical addresses. A typical case may be a data section with a physical address in ROM and a virtual address in RAM, which should be loaded to the ROM address. - Add support for querying the target's qXfer:memory-map, which contains information about flash memory regions, leveraging MemoryRegionInfo data structures with minor modifications - Update ProcessGDBRemote to use vFlash commands in DoWriteMemory when the target address is in a flash region Original discussion at http://lists.llvm.org/pipermail/lldb-dev/2018-January/013093.html Reviewers: clayborg, labath Reviewed By: labath Subscribers: arichardson, emaste, mgorny, lldb-commits Differential Revision: https://reviews.llvm.org/D42145 Patch by Owen Shaw <llvm@owenpshaw.net> llvm-svn: 326261
This commit is contained in:
parent
3acdc67734
commit
029fb69372
|
@ -82,6 +82,9 @@ public:
|
||||||
llvm::StringRef GetAttributeValue(const char *name,
|
llvm::StringRef GetAttributeValue(const char *name,
|
||||||
const char *fail_value = nullptr) const;
|
const char *fail_value = nullptr) const;
|
||||||
|
|
||||||
|
bool GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
|
||||||
|
uint64_t fail_value = 0, int base = 0) const;
|
||||||
|
|
||||||
XMLNode FindFirstChildElementWithName(const char *name) const;
|
XMLNode FindFirstChildElementWithName(const char *name) const;
|
||||||
|
|
||||||
XMLNode GetElementForPath(const NamePath &path);
|
XMLNode GetElementForPath(const NamePath &path);
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
|
|
||||||
MemoryRegionInfo()
|
MemoryRegionInfo()
|
||||||
: m_range(), m_read(eDontKnow), m_write(eDontKnow), m_execute(eDontKnow),
|
: m_range(), m_read(eDontKnow), m_write(eDontKnow), m_execute(eDontKnow),
|
||||||
m_mapped(eDontKnow) {}
|
m_mapped(eDontKnow), m_flash(eDontKnow), m_blocksize(0) {}
|
||||||
|
|
||||||
~MemoryRegionInfo() {}
|
~MemoryRegionInfo() {}
|
||||||
|
|
||||||
|
@ -58,6 +58,14 @@ public:
|
||||||
|
|
||||||
void SetName(const char *name) { m_name = ConstString(name); }
|
void SetName(const char *name) { m_name = ConstString(name); }
|
||||||
|
|
||||||
|
OptionalBool GetFlash() const { return m_flash; }
|
||||||
|
|
||||||
|
void SetFlash(OptionalBool val) { m_flash = val; }
|
||||||
|
|
||||||
|
lldb::offset_t GetBlocksize() const { return m_blocksize; }
|
||||||
|
|
||||||
|
void SetBlocksize(lldb::offset_t blocksize) { m_blocksize = blocksize; }
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// Get permissions as a uint32_t that is a mask of one or more bits from
|
// Get permissions as a uint32_t that is a mask of one or more bits from
|
||||||
// the lldb::Permissions
|
// the lldb::Permissions
|
||||||
|
@ -98,6 +106,8 @@ protected:
|
||||||
OptionalBool m_execute;
|
OptionalBool m_execute;
|
||||||
OptionalBool m_mapped;
|
OptionalBool m_mapped;
|
||||||
ConstString m_name;
|
ConstString m_name;
|
||||||
|
OptionalBool m_flash;
|
||||||
|
lldb::offset_t m_blocksize;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -556,6 +556,11 @@ public:
|
||||||
return GetStaticBroadcasterClass();
|
return GetStaticBroadcasterClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct WriteEntry {
|
||||||
|
lldb::addr_t Dest;
|
||||||
|
llvm::ArrayRef<uint8_t> Contents;
|
||||||
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
/// A notification structure that can be used by clients to listen
|
/// A notification structure that can be used by clients to listen
|
||||||
/// for changes in a process's lifetime.
|
/// for changes in a process's lifetime.
|
||||||
|
@ -1950,6 +1955,8 @@ public:
|
||||||
return LLDB_INVALID_ADDRESS;
|
return LLDB_INVALID_ADDRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual Status WriteObjectFile(std::vector<WriteEntry> entries);
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
/// The public interface to allocating memory in the process.
|
/// The public interface to allocating memory in the process.
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import lldb
|
||||||
|
from lldbsuite.test.lldbtest import *
|
||||||
|
from lldbsuite.test.decorators import *
|
||||||
|
from gdbclientutils import *
|
||||||
|
|
||||||
|
|
||||||
|
class TestGDBRemoteLoad(GDBRemoteTestBase):
|
||||||
|
|
||||||
|
def test_ram_load(self):
|
||||||
|
"""Test loading an object file to a target's ram"""
|
||||||
|
target = self.createTarget("a.yaml")
|
||||||
|
process = self.connect(target)
|
||||||
|
self.dbg.HandleCommand("target modules load -l -s0")
|
||||||
|
self.assertPacketLogContains([
|
||||||
|
"M1000,4:c3c3c3c3",
|
||||||
|
"M1004,2:3232"
|
||||||
|
])
|
||||||
|
|
||||||
|
@skipIfXmlSupportMissing
|
||||||
|
def test_flash_load(self):
|
||||||
|
"""Test loading an object file to a target's flash memory"""
|
||||||
|
|
||||||
|
class Responder(MockGDBServerResponder):
|
||||||
|
def qSupported(self, client_supported):
|
||||||
|
return "PacketSize=3fff;QStartNoAckMode+;qXfer:memory-map:read+"
|
||||||
|
|
||||||
|
def qXferRead(self, obj, annex, offset, length):
|
||||||
|
if obj == "memory-map":
|
||||||
|
return (self.MEMORY_MAP[offset:offset + length],
|
||||||
|
offset + length < len(self.MEMORY_MAP))
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
def other(self, packet):
|
||||||
|
if packet[0:11] == "vFlashErase":
|
||||||
|
return "OK"
|
||||||
|
if packet[0:11] == "vFlashWrite":
|
||||||
|
return "OK"
|
||||||
|
if packet == "vFlashDone":
|
||||||
|
return "OK"
|
||||||
|
return ""
|
||||||
|
|
||||||
|
MEMORY_MAP = """<?xml version="1.0"?>
|
||||||
|
<memory-map>
|
||||||
|
<memory type="ram" start="0x0" length="0x1000"/>
|
||||||
|
<memory type="flash" start="0x1000" length="0x1000">
|
||||||
|
<property name="blocksize">0x100</property>
|
||||||
|
</memory>
|
||||||
|
<memory type="ram" start="0x2000" length="0x1D400"/>
|
||||||
|
</memory-map>
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.server.responder = Responder()
|
||||||
|
target = self.createTarget("a.yaml")
|
||||||
|
process = self.connect(target)
|
||||||
|
self.dbg.HandleCommand("target modules load -l -s0")
|
||||||
|
self.assertPacketLogContains([
|
||||||
|
"vFlashErase:1000,100",
|
||||||
|
"vFlashWrite:1000:\xc3\xc3\xc3\xc3",
|
||||||
|
"vFlashWrite:1004:\x32\x32",
|
||||||
|
"vFlashDone"
|
||||||
|
])
|
|
@ -151,6 +151,18 @@ llvm::StringRef XMLNode::GetAttributeValue(const char *name,
|
||||||
return llvm::StringRef();
|
return llvm::StringRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
|
||||||
|
uint64_t fail_value, int base) const {
|
||||||
|
#if defined(LIBXML2_DEFINED)
|
||||||
|
llvm::StringRef str_value = GetAttributeValue(name, "");
|
||||||
|
#else
|
||||||
|
llvm::StringRef str_value;
|
||||||
|
#endif
|
||||||
|
bool success = false;
|
||||||
|
value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
|
void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
|
||||||
#if defined(LIBXML2_DEFINED)
|
#if defined(LIBXML2_DEFINED)
|
||||||
if (IsValid())
|
if (IsValid())
|
||||||
|
|
|
@ -827,7 +827,7 @@ bool ObjectFileELF::SetLoadAddress(Target &target, lldb::addr_t value,
|
||||||
// of the sections that have SHF_ALLOC in their flag bits.
|
// of the sections that have SHF_ALLOC in their flag bits.
|
||||||
SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
|
SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
|
||||||
if (section_sp && section_sp->Test(SHF_ALLOC)) {
|
if (section_sp && section_sp->Test(SHF_ALLOC)) {
|
||||||
lldb::addr_t load_addr = section_sp->GetFileAddress();
|
lldb::addr_t load_addr = GetSectionPhysicalAddress(section_sp);
|
||||||
// We don't want to update the load address of a section with type
|
// We don't want to update the load address of a section with type
|
||||||
// eSectionTypeAbsoluteAddress as they already have the absolute load
|
// eSectionTypeAbsoluteAddress as they already have the absolute load
|
||||||
// address
|
// address
|
||||||
|
@ -3470,3 +3470,40 @@ size_t ObjectFileELF::ReadSectionData(Section *section,
|
||||||
section_data.SetData(buffer_sp);
|
section_data.SetData(buffer_sp);
|
||||||
return buffer_sp->GetByteSize();
|
return buffer_sp->GetByteSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ObjectFileELF::AnySegmentHasPhysicalAddress() {
|
||||||
|
size_t header_count = ParseProgramHeaders();
|
||||||
|
for (size_t i = 1; i <= header_count; ++i) {
|
||||||
|
auto header = GetProgramHeaderByIndex(i);
|
||||||
|
if (header->p_paddr != 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elf::ELFProgramHeader *
|
||||||
|
ObjectFileELF::GetSectionSegment(SectionSP section_sp) {
|
||||||
|
auto section_size = section_sp->GetFileSize();
|
||||||
|
if (section_size == 0)
|
||||||
|
section_size = 1;
|
||||||
|
size_t header_count = ParseProgramHeaders();
|
||||||
|
for (size_t i = 1; i <= header_count; ++i) {
|
||||||
|
auto header = GetProgramHeaderByIndex(i);
|
||||||
|
if (section_sp->GetFileOffset() >= header->p_offset &&
|
||||||
|
section_sp->GetFileOffset() + section_size <=
|
||||||
|
header->p_offset + header->p_filesz)
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t ObjectFileELF::GetSectionPhysicalAddress(SectionSP section_sp) {
|
||||||
|
auto segment = GetSectionSegment(section_sp);
|
||||||
|
if (segment == nullptr)
|
||||||
|
return section_sp->GetFileAddress();
|
||||||
|
if (segment->p_type != PT_LOAD)
|
||||||
|
return LLDB_INVALID_ADDRESS;
|
||||||
|
auto base_address =
|
||||||
|
AnySegmentHasPhysicalAddress() ? segment->p_paddr : segment->p_vaddr;
|
||||||
|
return base_address + (section_sp->GetFileOffset() - segment->p_offset);
|
||||||
|
}
|
||||||
|
|
|
@ -383,6 +383,12 @@ private:
|
||||||
RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
|
RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
|
||||||
lldb_private::ArchSpec &arch_spec,
|
lldb_private::ArchSpec &arch_spec,
|
||||||
lldb_private::UUID &uuid);
|
lldb_private::UUID &uuid);
|
||||||
|
|
||||||
|
bool AnySegmentHasPhysicalAddress();
|
||||||
|
|
||||||
|
const elf::ELFProgramHeader *GetSectionSegment(lldb::SectionSP section_sp);
|
||||||
|
|
||||||
|
lldb::addr_t GetSectionPhysicalAddress(lldb::SectionSP section_sp);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // liblldb_ObjectFileELF_h_
|
#endif // liblldb_ObjectFileELF_h_
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "lldb/Core/ModuleSpec.h"
|
#include "lldb/Core/ModuleSpec.h"
|
||||||
#include "lldb/Core/State.h"
|
#include "lldb/Core/State.h"
|
||||||
#include "lldb/Host/HostInfo.h"
|
#include "lldb/Host/HostInfo.h"
|
||||||
|
#include "lldb/Host/XML.h"
|
||||||
#include "lldb/Interpreter/Args.h"
|
#include "lldb/Interpreter/Args.h"
|
||||||
#include "lldb/Symbol/Symbol.h"
|
#include "lldb/Symbol/Symbol.h"
|
||||||
#include "lldb/Target/MemoryRegionInfo.h"
|
#include "lldb/Target/MemoryRegionInfo.h"
|
||||||
|
@ -81,6 +82,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
|
||||||
m_supports_qXfer_libraries_read(eLazyBoolCalculate),
|
m_supports_qXfer_libraries_read(eLazyBoolCalculate),
|
||||||
m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate),
|
m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate),
|
||||||
m_supports_qXfer_features_read(eLazyBoolCalculate),
|
m_supports_qXfer_features_read(eLazyBoolCalculate),
|
||||||
|
m_supports_qXfer_memory_map_read(eLazyBoolCalculate),
|
||||||
m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate),
|
m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate),
|
||||||
m_supports_jThreadExtendedInfo(eLazyBoolCalculate),
|
m_supports_jThreadExtendedInfo(eLazyBoolCalculate),
|
||||||
m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate),
|
m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate),
|
||||||
|
@ -103,7 +105,8 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
|
||||||
m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX),
|
m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX),
|
||||||
m_default_packet_timeout(0), m_max_packet_size(0),
|
m_default_packet_timeout(0), m_max_packet_size(0),
|
||||||
m_qSupported_response(), m_supported_async_json_packets_is_valid(false),
|
m_qSupported_response(), m_supported_async_json_packets_is_valid(false),
|
||||||
m_supported_async_json_packets_sp() {}
|
m_supported_async_json_packets_sp(), m_qXfer_memory_map(),
|
||||||
|
m_qXfer_memory_map_loaded(false) {}
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// Destructor
|
// Destructor
|
||||||
|
@ -192,6 +195,13 @@ bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() {
|
||||||
return m_supports_qXfer_features_read == eLazyBoolYes;
|
return m_supports_qXfer_features_read == eLazyBoolYes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GDBRemoteCommunicationClient::GetQXferMemoryMapReadSupported() {
|
||||||
|
if (m_supports_qXfer_memory_map_read == eLazyBoolCalculate) {
|
||||||
|
GetRemoteQSupported();
|
||||||
|
}
|
||||||
|
return m_supports_qXfer_memory_map_read == eLazyBoolYes;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() {
|
uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() {
|
||||||
if (m_max_packet_size == 0) {
|
if (m_max_packet_size == 0) {
|
||||||
GetRemoteQSupported();
|
GetRemoteQSupported();
|
||||||
|
@ -296,6 +306,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
|
||||||
m_supports_qXfer_libraries_read = eLazyBoolCalculate;
|
m_supports_qXfer_libraries_read = eLazyBoolCalculate;
|
||||||
m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
|
m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
|
||||||
m_supports_qXfer_features_read = eLazyBoolCalculate;
|
m_supports_qXfer_features_read = eLazyBoolCalculate;
|
||||||
|
m_supports_qXfer_memory_map_read = eLazyBoolCalculate;
|
||||||
m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate;
|
m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate;
|
||||||
m_supports_qProcessInfoPID = true;
|
m_supports_qProcessInfoPID = true;
|
||||||
m_supports_qfProcessInfo = true;
|
m_supports_qfProcessInfo = true;
|
||||||
|
@ -342,6 +353,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
|
||||||
m_supports_qXfer_libraries_svr4_read = eLazyBoolNo;
|
m_supports_qXfer_libraries_svr4_read = eLazyBoolNo;
|
||||||
m_supports_augmented_libraries_svr4_read = eLazyBoolNo;
|
m_supports_augmented_libraries_svr4_read = eLazyBoolNo;
|
||||||
m_supports_qXfer_features_read = eLazyBoolNo;
|
m_supports_qXfer_features_read = eLazyBoolNo;
|
||||||
|
m_supports_qXfer_memory_map_read = eLazyBoolNo;
|
||||||
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
|
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
|
||||||
// not, we assume no limit
|
// not, we assume no limit
|
||||||
|
|
||||||
|
@ -377,6 +389,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
|
||||||
m_supports_qXfer_libraries_read = eLazyBoolYes;
|
m_supports_qXfer_libraries_read = eLazyBoolYes;
|
||||||
if (::strstr(response_cstr, "qXfer:features:read+"))
|
if (::strstr(response_cstr, "qXfer:features:read+"))
|
||||||
m_supports_qXfer_features_read = eLazyBoolYes;
|
m_supports_qXfer_features_read = eLazyBoolYes;
|
||||||
|
if (::strstr(response_cstr, "qXfer:memory-map:read+"))
|
||||||
|
m_supports_qXfer_memory_map_read = eLazyBoolYes;
|
||||||
|
|
||||||
// Look for a list of compressions in the features list e.g.
|
// Look for a list of compressions in the features list e.g.
|
||||||
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma
|
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma
|
||||||
|
@ -1460,7 +1474,8 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
|
||||||
UNUSED_IF_ASSERT_DISABLED(packet_len);
|
UNUSED_IF_ASSERT_DISABLED(packet_len);
|
||||||
StringExtractorGDBRemote response;
|
StringExtractorGDBRemote response;
|
||||||
if (SendPacketAndWaitForResponse(packet, response, false) ==
|
if (SendPacketAndWaitForResponse(packet, response, false) ==
|
||||||
PacketResult::Success) {
|
PacketResult::Success &&
|
||||||
|
response.GetResponseType() == StringExtractorGDBRemote::eResponse) {
|
||||||
llvm::StringRef name;
|
llvm::StringRef name;
|
||||||
llvm::StringRef value;
|
llvm::StringRef value;
|
||||||
addr_t addr_value = LLDB_INVALID_ADDRESS;
|
addr_t addr_value = LLDB_INVALID_ADDRESS;
|
||||||
|
@ -1536,8 +1551,134 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
|
||||||
if (m_supports_memory_region_info == eLazyBoolNo) {
|
if (m_supports_memory_region_info == eLazyBoolNo) {
|
||||||
error.SetErrorString("qMemoryRegionInfo is not supported");
|
error.SetErrorString("qMemoryRegionInfo is not supported");
|
||||||
}
|
}
|
||||||
if (error.Fail())
|
|
||||||
|
// Try qXfer:memory-map:read to get region information not included in
|
||||||
|
// qMemoryRegionInfo
|
||||||
|
MemoryRegionInfo qXfer_region_info;
|
||||||
|
Status qXfer_error = GetQXferMemoryMapRegionInfo(addr, qXfer_region_info);
|
||||||
|
|
||||||
|
if (error.Fail()) {
|
||||||
|
// If qMemoryRegionInfo failed, but qXfer:memory-map:read succeeded,
|
||||||
|
// use the qXfer result as a fallback
|
||||||
|
if (qXfer_error.Success()) {
|
||||||
|
region_info = qXfer_region_info;
|
||||||
|
error.Clear();
|
||||||
|
} else {
|
||||||
region_info.Clear();
|
region_info.Clear();
|
||||||
|
}
|
||||||
|
} else if (qXfer_error.Success()) {
|
||||||
|
// If both qMemoryRegionInfo and qXfer:memory-map:read succeeded, and if
|
||||||
|
// both regions are the same range, update the result to include the
|
||||||
|
// flash-memory information that is specific to the qXfer result.
|
||||||
|
if (region_info.GetRange() == qXfer_region_info.GetRange()) {
|
||||||
|
region_info.SetFlash(qXfer_region_info.GetFlash());
|
||||||
|
region_info.SetBlocksize(qXfer_region_info.GetBlocksize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo(
|
||||||
|
lldb::addr_t addr, MemoryRegionInfo ®ion) {
|
||||||
|
Status error = LoadQXferMemoryMap();
|
||||||
|
if (!error.Success())
|
||||||
|
return error;
|
||||||
|
for (const auto &map_region : m_qXfer_memory_map) {
|
||||||
|
if (map_region.GetRange().Contains(addr)) {
|
||||||
|
region = map_region;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error.SetErrorString("Region not found");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() {
|
||||||
|
|
||||||
|
Status error;
|
||||||
|
|
||||||
|
if (m_qXfer_memory_map_loaded)
|
||||||
|
// Already loaded, return success
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (!XMLDocument::XMLEnabled()) {
|
||||||
|
error.SetErrorString("XML is not supported");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetQXferMemoryMapReadSupported()) {
|
||||||
|
error.SetErrorString("Memory map is not supported");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string xml;
|
||||||
|
lldb_private::Status lldberr;
|
||||||
|
if (!ReadExtFeature(ConstString("memory-map"), ConstString(""), xml,
|
||||||
|
lldberr)) {
|
||||||
|
error.SetErrorString("Failed to read memory map");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLDocument xml_document;
|
||||||
|
|
||||||
|
if (!xml_document.ParseMemory(xml.c_str(), xml.size())) {
|
||||||
|
error.SetErrorString("Failed to parse memory map xml");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLNode map_node = xml_document.GetRootElement("memory-map");
|
||||||
|
if (!map_node) {
|
||||||
|
error.SetErrorString("Invalid root node in memory map xml");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_qXfer_memory_map.clear();
|
||||||
|
|
||||||
|
map_node.ForEachChildElement([this](const XMLNode &memory_node) -> bool {
|
||||||
|
if (!memory_node.IsElement())
|
||||||
|
return true;
|
||||||
|
if (memory_node.GetName() != "memory")
|
||||||
|
return true;
|
||||||
|
auto type = memory_node.GetAttributeValue("type", "");
|
||||||
|
uint64_t start;
|
||||||
|
uint64_t length;
|
||||||
|
if (!memory_node.GetAttributeValueAsUnsigned("start", start))
|
||||||
|
return true;
|
||||||
|
if (!memory_node.GetAttributeValueAsUnsigned("length", length))
|
||||||
|
return true;
|
||||||
|
MemoryRegionInfo region;
|
||||||
|
region.GetRange().SetRangeBase(start);
|
||||||
|
region.GetRange().SetByteSize(length);
|
||||||
|
if (type == "rom") {
|
||||||
|
region.SetReadable(MemoryRegionInfo::eYes);
|
||||||
|
this->m_qXfer_memory_map.push_back(region);
|
||||||
|
} else if (type == "ram") {
|
||||||
|
region.SetReadable(MemoryRegionInfo::eYes);
|
||||||
|
region.SetWritable(MemoryRegionInfo::eYes);
|
||||||
|
this->m_qXfer_memory_map.push_back(region);
|
||||||
|
} else if (type == "flash") {
|
||||||
|
region.SetFlash(MemoryRegionInfo::eYes);
|
||||||
|
memory_node.ForEachChildElement(
|
||||||
|
[®ion](const XMLNode &prop_node) -> bool {
|
||||||
|
if (!prop_node.IsElement())
|
||||||
|
return true;
|
||||||
|
if (prop_node.GetName() != "property")
|
||||||
|
return true;
|
||||||
|
auto propname = prop_node.GetAttributeValue("name", "");
|
||||||
|
if (propname == "blocksize") {
|
||||||
|
uint64_t blocksize;
|
||||||
|
if (prop_node.GetElementTextAsUnsigned(blocksize))
|
||||||
|
region.SetBlocksize(blocksize);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
this->m_qXfer_memory_map.push_back(region);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_qXfer_memory_map_loaded = true;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -355,6 +355,8 @@ public:
|
||||||
|
|
||||||
bool GetQXferFeaturesReadSupported();
|
bool GetQXferFeaturesReadSupported();
|
||||||
|
|
||||||
|
bool GetQXferMemoryMapReadSupported();
|
||||||
|
|
||||||
LazyBool SupportsAllocDeallocMemory() // const
|
LazyBool SupportsAllocDeallocMemory() // const
|
||||||
{
|
{
|
||||||
// Uncomment this to have lldb pretend the debug server doesn't respond to
|
// Uncomment this to have lldb pretend the debug server doesn't respond to
|
||||||
|
@ -545,6 +547,7 @@ protected:
|
||||||
LazyBool m_supports_qXfer_libraries_read;
|
LazyBool m_supports_qXfer_libraries_read;
|
||||||
LazyBool m_supports_qXfer_libraries_svr4_read;
|
LazyBool m_supports_qXfer_libraries_svr4_read;
|
||||||
LazyBool m_supports_qXfer_features_read;
|
LazyBool m_supports_qXfer_features_read;
|
||||||
|
LazyBool m_supports_qXfer_memory_map_read;
|
||||||
LazyBool m_supports_augmented_libraries_svr4_read;
|
LazyBool m_supports_augmented_libraries_svr4_read;
|
||||||
LazyBool m_supports_jThreadExtendedInfo;
|
LazyBool m_supports_jThreadExtendedInfo;
|
||||||
LazyBool m_supports_jLoadedDynamicLibrariesInfos;
|
LazyBool m_supports_jLoadedDynamicLibrariesInfos;
|
||||||
|
@ -588,6 +591,9 @@ protected:
|
||||||
bool m_supported_async_json_packets_is_valid;
|
bool m_supported_async_json_packets_is_valid;
|
||||||
lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp;
|
lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp;
|
||||||
|
|
||||||
|
std::vector<MemoryRegionInfo> m_qXfer_memory_map;
|
||||||
|
bool m_qXfer_memory_map_loaded;
|
||||||
|
|
||||||
bool GetCurrentProcessInfo(bool allow_lazy_pid = true);
|
bool GetCurrentProcessInfo(bool allow_lazy_pid = true);
|
||||||
|
|
||||||
bool GetGDBServerVersion();
|
bool GetGDBServerVersion();
|
||||||
|
@ -610,6 +616,11 @@ protected:
|
||||||
llvm::MutableArrayRef<uint8_t> &buffer,
|
llvm::MutableArrayRef<uint8_t> &buffer,
|
||||||
size_t offset);
|
size_t offset);
|
||||||
|
|
||||||
|
Status LoadQXferMemoryMap();
|
||||||
|
|
||||||
|
Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr,
|
||||||
|
MemoryRegionInfo ®ion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient);
|
DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient);
|
||||||
};
|
};
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
#include "lldb/Symbol/ObjectFile.h"
|
#include "lldb/Symbol/ObjectFile.h"
|
||||||
#include "lldb/Target/ABI.h"
|
#include "lldb/Target/ABI.h"
|
||||||
#include "lldb/Target/DynamicLoader.h"
|
#include "lldb/Target/DynamicLoader.h"
|
||||||
|
#include "lldb/Target/MemoryRegionInfo.h"
|
||||||
#include "lldb/Target/SystemRuntime.h"
|
#include "lldb/Target/SystemRuntime.h"
|
||||||
#include "lldb/Target/Target.h"
|
#include "lldb/Target/Target.h"
|
||||||
#include "lldb/Target/TargetList.h"
|
#include "lldb/Target/TargetList.h"
|
||||||
|
@ -256,7 +257,8 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp,
|
||||||
m_addr_to_mmap_size(), m_thread_create_bp_sp(),
|
m_addr_to_mmap_size(), m_thread_create_bp_sp(),
|
||||||
m_waiting_for_attach(false), m_destroy_tried_resuming(false),
|
m_waiting_for_attach(false), m_destroy_tried_resuming(false),
|
||||||
m_command_sp(), m_breakpoint_pc_offset(0),
|
m_command_sp(), m_breakpoint_pc_offset(0),
|
||||||
m_initial_tid(LLDB_INVALID_THREAD_ID) {
|
m_initial_tid(LLDB_INVALID_THREAD_ID), m_allow_flash_writes(false),
|
||||||
|
m_erased_flash_ranges() {
|
||||||
m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
|
m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
|
||||||
"async thread should exit");
|
"async thread should exit");
|
||||||
m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
|
m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
|
||||||
|
@ -2798,6 +2800,142 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status ProcessGDBRemote::WriteObjectFile(std::vector<WriteEntry> entries) {
|
||||||
|
Status error;
|
||||||
|
// Sort the entries by address because some writes, like those to flash
|
||||||
|
// memory, must happen in order of increasing address.
|
||||||
|
std::stable_sort(
|
||||||
|
std::begin(entries), std::end(entries),
|
||||||
|
[](const WriteEntry a, const WriteEntry b) { return a.Dest < b.Dest; });
|
||||||
|
m_allow_flash_writes = true;
|
||||||
|
error = Process::WriteObjectFile(entries);
|
||||||
|
if (error.Success())
|
||||||
|
error = FlashDone();
|
||||||
|
else
|
||||||
|
// Even though some of the writing failed, try to send a flash done if
|
||||||
|
// some of the writing succeeded so the flash state is reset to normal,
|
||||||
|
// but don't stomp on the error status that was set in the write failure
|
||||||
|
// since that's the one we want to report back.
|
||||||
|
FlashDone();
|
||||||
|
m_allow_flash_writes = false;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessGDBRemote::HasErased(FlashRange range) {
|
||||||
|
auto size = m_erased_flash_ranges.GetSize();
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
if (m_erased_flash_ranges.GetEntryAtIndex(i)->Contains(range))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status ProcessGDBRemote::FlashErase(lldb::addr_t addr, size_t size) {
|
||||||
|
Status status;
|
||||||
|
|
||||||
|
MemoryRegionInfo region;
|
||||||
|
status = GetMemoryRegionInfo(addr, region);
|
||||||
|
if (!status.Success())
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// The gdb spec doesn't say if erasures are allowed across multiple regions,
|
||||||
|
// but we'll disallow it to be safe and to keep the logic simple by worring
|
||||||
|
// about only one region's block size. DoMemoryWrite is this function's
|
||||||
|
// primary user, and it can easily keep writes within a single memory region
|
||||||
|
if (addr + size > region.GetRange().GetRangeEnd()) {
|
||||||
|
status.SetErrorString("Unable to erase flash in multiple regions");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blocksize = region.GetBlocksize();
|
||||||
|
if (blocksize == 0) {
|
||||||
|
status.SetErrorString("Unable to erase flash because blocksize is 0");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erasures can only be done on block boundary adresses, so round down addr
|
||||||
|
// and round up size
|
||||||
|
lldb::addr_t block_start_addr = addr - (addr % blocksize);
|
||||||
|
size += (addr - block_start_addr);
|
||||||
|
if ((size % blocksize) != 0)
|
||||||
|
size += (blocksize - size % blocksize);
|
||||||
|
|
||||||
|
FlashRange range(block_start_addr, size);
|
||||||
|
|
||||||
|
if (HasErased(range))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// We haven't erased the entire range, but we may have erased part of it.
|
||||||
|
// (e.g., block A is already erased and range starts in A and ends in B).
|
||||||
|
// So, adjust range if necessary to exclude already erased blocks.
|
||||||
|
if (!m_erased_flash_ranges.IsEmpty()) {
|
||||||
|
// Assuming that writes and erasures are done in increasing addr order,
|
||||||
|
// because that is a requirement of the vFlashWrite command. Therefore,
|
||||||
|
// we only need to look at the last range in the list for overlap.
|
||||||
|
const auto &last_range = *m_erased_flash_ranges.Back();
|
||||||
|
if (range.GetRangeBase() < last_range.GetRangeEnd()) {
|
||||||
|
auto overlap = last_range.GetRangeEnd() - range.GetRangeBase();
|
||||||
|
// overlap will be less than range.GetByteSize() or else HasErased() would
|
||||||
|
// have been true
|
||||||
|
range.SetByteSize(range.GetByteSize() - overlap);
|
||||||
|
range.SetRangeBase(range.GetRangeBase() + overlap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamString packet;
|
||||||
|
packet.Printf("vFlashErase:%" PRIx64 ",%" PRIx64, range.GetRangeBase(),
|
||||||
|
(uint64_t)range.GetByteSize());
|
||||||
|
|
||||||
|
StringExtractorGDBRemote response;
|
||||||
|
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
|
||||||
|
true) ==
|
||||||
|
GDBRemoteCommunication::PacketResult::Success) {
|
||||||
|
if (response.IsOKResponse()) {
|
||||||
|
m_erased_flash_ranges.Insert(range, true);
|
||||||
|
} else {
|
||||||
|
if (response.IsErrorResponse())
|
||||||
|
status.SetErrorStringWithFormat("flash erase failed for 0x%" PRIx64,
|
||||||
|
addr);
|
||||||
|
else if (response.IsUnsupportedResponse())
|
||||||
|
status.SetErrorStringWithFormat("GDB server does not support flashing");
|
||||||
|
else
|
||||||
|
status.SetErrorStringWithFormat(
|
||||||
|
"unexpected response to GDB server flash erase packet '%s': '%s'",
|
||||||
|
packet.GetData(), response.GetStringRef().c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status.SetErrorStringWithFormat("failed to send packet: '%s'",
|
||||||
|
packet.GetData());
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status ProcessGDBRemote::FlashDone() {
|
||||||
|
Status status;
|
||||||
|
// If we haven't erased any blocks, then we must not have written anything
|
||||||
|
// either, so there is no need to actually send a vFlashDone command
|
||||||
|
if (m_erased_flash_ranges.IsEmpty())
|
||||||
|
return status;
|
||||||
|
StringExtractorGDBRemote response;
|
||||||
|
if (m_gdb_comm.SendPacketAndWaitForResponse("vFlashDone", response, true) ==
|
||||||
|
GDBRemoteCommunication::PacketResult::Success) {
|
||||||
|
if (response.IsOKResponse()) {
|
||||||
|
m_erased_flash_ranges.Clear();
|
||||||
|
} else {
|
||||||
|
if (response.IsErrorResponse())
|
||||||
|
status.SetErrorStringWithFormat("flash done failed");
|
||||||
|
else if (response.IsUnsupportedResponse())
|
||||||
|
status.SetErrorStringWithFormat("GDB server does not support flashing");
|
||||||
|
else
|
||||||
|
status.SetErrorStringWithFormat(
|
||||||
|
"unexpected response to GDB server flash done packet: '%s'",
|
||||||
|
response.GetStringRef().c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status.SetErrorStringWithFormat("failed to send flash done packet");
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf,
|
size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf,
|
||||||
size_t size, Status &error) {
|
size_t size, Status &error) {
|
||||||
GetMaxMemorySize();
|
GetMaxMemorySize();
|
||||||
|
@ -2810,10 +2948,33 @@ size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf,
|
||||||
size = max_memory_size;
|
size = max_memory_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamString packet;
|
StreamGDBRemote packet;
|
||||||
|
|
||||||
|
MemoryRegionInfo region;
|
||||||
|
Status region_status = GetMemoryRegionInfo(addr, region);
|
||||||
|
|
||||||
|
bool is_flash =
|
||||||
|
region_status.Success() && region.GetFlash() == MemoryRegionInfo::eYes;
|
||||||
|
|
||||||
|
if (is_flash) {
|
||||||
|
if (!m_allow_flash_writes) {
|
||||||
|
error.SetErrorString("Writing to flash memory is not allowed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// Keep the write within a flash memory region
|
||||||
|
if (addr + size > region.GetRange().GetRangeEnd())
|
||||||
|
size = region.GetRange().GetRangeEnd() - addr;
|
||||||
|
// Flash memory must be erased before it can be written
|
||||||
|
error = FlashErase(addr, size);
|
||||||
|
if (!error.Success())
|
||||||
|
return 0;
|
||||||
|
packet.Printf("vFlashWrite:%" PRIx64 ":", addr);
|
||||||
|
packet.PutEscapedBytes(buf, size);
|
||||||
|
} else {
|
||||||
packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size);
|
packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size);
|
||||||
packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(),
|
packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(),
|
||||||
endian::InlHostByteOrder());
|
endian::InlHostByteOrder());
|
||||||
|
}
|
||||||
StringExtractorGDBRemote response;
|
StringExtractorGDBRemote response;
|
||||||
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
|
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
|
||||||
true) ==
|
true) ==
|
||||||
|
|
|
@ -144,6 +144,8 @@ public:
|
||||||
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
||||||
Status &error) override;
|
Status &error) override;
|
||||||
|
|
||||||
|
Status WriteObjectFile(std::vector<WriteEntry> entries) override;
|
||||||
|
|
||||||
size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
|
size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
|
||||||
Status &error) override;
|
Status &error) override;
|
||||||
|
|
||||||
|
@ -302,6 +304,11 @@ protected:
|
||||||
int64_t m_breakpoint_pc_offset;
|
int64_t m_breakpoint_pc_offset;
|
||||||
lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
|
lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
|
||||||
|
|
||||||
|
bool m_allow_flash_writes;
|
||||||
|
using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>;
|
||||||
|
using FlashRange = FlashRangeVector::Entry;
|
||||||
|
FlashRangeVector m_erased_flash_ranges;
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// Accessors
|
// Accessors
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
@ -408,6 +415,12 @@ protected:
|
||||||
|
|
||||||
Status UpdateAutomaticSignalFiltering() override;
|
Status UpdateAutomaticSignalFiltering() override;
|
||||||
|
|
||||||
|
Status FlashErase(lldb::addr_t addr, size_t size);
|
||||||
|
|
||||||
|
Status FlashDone();
|
||||||
|
|
||||||
|
bool HasErased(FlashRange range);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// For ProcessGDBRemote only
|
// For ProcessGDBRemote only
|
||||||
|
|
|
@ -659,22 +659,31 @@ Status ObjectFile::LoadInMemory(Target &target, bool set_pc) {
|
||||||
SectionList *section_list = GetSectionList();
|
SectionList *section_list = GetSectionList();
|
||||||
if (!section_list)
|
if (!section_list)
|
||||||
return Status("No section in object file");
|
return Status("No section in object file");
|
||||||
|
|
||||||
|
std::vector<Process::WriteEntry> writeEntries;
|
||||||
|
|
||||||
|
// Create a list of write entries from loadable sections
|
||||||
size_t section_count = section_list->GetNumSections(0);
|
size_t section_count = section_list->GetNumSections(0);
|
||||||
for (size_t i = 0; i < section_count; ++i) {
|
for (size_t i = 0; i < section_count; ++i) {
|
||||||
|
Process::WriteEntry entry;
|
||||||
SectionSP section_sp = section_list->GetSectionAtIndex(i);
|
SectionSP section_sp = section_list->GetSectionAtIndex(i);
|
||||||
addr_t addr = target.GetSectionLoadList().GetSectionLoadAddress(section_sp);
|
entry.Dest = target.GetSectionLoadList().GetSectionLoadAddress(section_sp);
|
||||||
if (addr != LLDB_INVALID_ADDRESS) {
|
if (entry.Dest == LLDB_INVALID_ADDRESS)
|
||||||
DataExtractor section_data;
|
continue;
|
||||||
// We can skip sections like bss
|
// We can skip sections like bss
|
||||||
if (section_sp->GetFileSize() == 0)
|
if (section_sp->GetFileSize() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
DataExtractor section_data;
|
||||||
section_sp->GetSectionData(section_data);
|
section_sp->GetSectionData(section_data);
|
||||||
lldb::offset_t written = process->WriteMemory(
|
entry.Contents = llvm::ArrayRef<uint8_t>(section_data.GetDataStart(),
|
||||||
addr, section_data.GetDataStart(), section_data.GetByteSize(), error);
|
section_data.GetByteSize());
|
||||||
if (written != section_data.GetByteSize())
|
writeEntries.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
error = process->WriteObjectFile(std::move(writeEntries));
|
||||||
|
if (!error.Success())
|
||||||
return error;
|
return error;
|
||||||
}
|
|
||||||
}
|
|
||||||
if (set_pc) {
|
if (set_pc) {
|
||||||
ThreadList &thread_list = process->GetThreadList();
|
ThreadList &thread_list = process->GetThreadList();
|
||||||
ThreadSP curr_thread(thread_list.GetSelectedThread());
|
ThreadSP curr_thread(thread_list.GetSelectedThread());
|
||||||
|
|
|
@ -2533,6 +2533,17 @@ size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status Process::WriteObjectFile(std::vector<WriteEntry> entries) {
|
||||||
|
Status error;
|
||||||
|
for (const auto &Entry : entries) {
|
||||||
|
WriteMemory(Entry.Dest, Entry.Contents.data(), Entry.Contents.size(),
|
||||||
|
error);
|
||||||
|
if (!error.Success())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
#define USE_ALLOCATE_MEMORY_CACHE 1
|
#define USE_ALLOCATE_MEMORY_CACHE 1
|
||||||
addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
|
addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
|
||||||
Status &error) {
|
Status &error) {
|
||||||
|
|
Loading…
Reference in New Issue