Index: include/lldb/Host/XML.h
===================================================================
--- include/lldb/Host/XML.h
+++ include/lldb/Host/XML.h
@@ -82,6 +82,9 @@
llvm::StringRef GetAttributeValue(const char *name,
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 GetElementForPath(const NamePath &path);
Index: include/lldb/Target/MemoryRegionInfo.h
===================================================================
--- include/lldb/Target/MemoryRegionInfo.h
+++ include/lldb/Target/MemoryRegionInfo.h
@@ -25,7 +25,7 @@
MemoryRegionInfo()
: m_range(), m_read(eDontKnow), m_write(eDontKnow), m_execute(eDontKnow),
- m_mapped(eDontKnow) {}
+ m_mapped(eDontKnow), m_flash(eDontKnow), m_blocksize(0) {}
~MemoryRegionInfo() {}
@@ -58,6 +58,14 @@
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
// the lldb::Permissions
@@ -98,6 +106,8 @@
OptionalBool m_execute;
OptionalBool m_mapped;
ConstString m_name;
+ OptionalBool m_flash;
+ lldb::offset_t m_blocksize;
};
}
Index: include/lldb/Target/Process.h
===================================================================
--- include/lldb/Target/Process.h
+++ include/lldb/Target/Process.h
@@ -1898,6 +1898,38 @@
bool is_signed, Scalar &scalar,
Status &error);
+ //------------------------------------------------------------------
+ /// Inform the process that several memory writes are about to
+ /// happen as a group.
+ ///
+ /// Allows a subclass to prepare for a batch of writes. Most times
+ /// this function will simply return true because no preparation is
+ /// required. However, cases such as flash memory writes over a gdb
+ /// connection require a sequence of erase/write/done commands, and
+ /// letting the process know a batch is coming allows it to dealy
+ /// issuing any done command until the EndWriteMemoryBatch() method
+ /// is called.
+ ///
+ /// @return
+ /// true on success, false on failure
+ //------------------------------------------------------------------
+ virtual bool BeginWriteMemoryBatch() { return true; }
+
+ //------------------------------------------------------------------
+ /// Inform the process that a group of memory writes is complete.
+ ///
+ /// Allows a subclass to finalize a batch of writes. Most times
+ /// this function will simply return true because no final operation
+ /// is required. However, cases such as flash memory writes over a
+ /// gdb connection require a sequence of erase/write/done commands,
+ /// and this method informs the process that a done command should
+ /// be issued.
+ ///
+ /// @return
+ /// true on success, false on failure
+ //------------------------------------------------------------------
+ virtual bool EndWriteMemoryBatch() { return true; }
+
//------------------------------------------------------------------
/// Write memory to a process.
///
Index: packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteLoad.py
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteLoad.py
@@ -0,0 +1,60 @@
+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"
+ ])
+
+ 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 = """
+
+
+
+ 0x100
+
+
+
+"""
+
+ 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"
+ ])
Index: source/Host/common/XML.cpp
===================================================================
--- source/Host/common/XML.cpp
+++ source/Host/common/XML.cpp
@@ -151,6 +151,18 @@
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 {
#if defined(LIBXML2_DEFINED)
if (IsValid())
Index: source/Plugins/ObjectFile/ELF/ObjectFileELF.h
===================================================================
--- source/Plugins/ObjectFile/ELF/ObjectFileELF.h
+++ source/Plugins/ObjectFile/ELF/ObjectFileELF.h
@@ -383,6 +383,12 @@
RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
lldb_private::ArchSpec &arch_spec,
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_
Index: source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
===================================================================
--- source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -865,7 +865,7 @@
// of the sections that have SHF_ALLOC in their flag bits.
SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
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
// eSectionTypeAbsoluteAddress as they already have the absolute load
// address
@@ -3507,3 +3507,41 @@
section_data.SetData(buffer_sp);
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);
+}
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -355,6 +355,8 @@
bool GetQXferFeaturesReadSupported();
+ bool GetQXferMemoryMapReadSupported();
+
LazyBool SupportsAllocDeallocMemory() // const
{
// Uncomment this to have lldb pretend the debug server doesn't respond to
@@ -545,6 +547,7 @@
LazyBool m_supports_qXfer_libraries_read;
LazyBool m_supports_qXfer_libraries_svr4_read;
LazyBool m_supports_qXfer_features_read;
+ LazyBool m_supports_qXfer_memory_map_read;
LazyBool m_supports_augmented_libraries_svr4_read;
LazyBool m_supports_jThreadExtendedInfo;
LazyBool m_supports_jLoadedDynamicLibrariesInfos;
@@ -588,6 +591,9 @@
bool m_supported_async_json_packets_is_valid;
lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp;
+ std::vector m_qXfer_memory_map;
+ bool m_qXfer_memory_map_loaded;
+
bool GetCurrentProcessInfo(bool allow_lazy_pid = true);
bool GetGDBServerVersion();
@@ -610,6 +616,11 @@
llvm::MutableArrayRef &buffer,
size_t offset);
+ Status LoadQXferMemoryMap();
+
+ Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr,
+ MemoryRegionInfo ®ion);
+
private:
DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient);
};
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -21,6 +21,7 @@
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/State.h"
#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/XML.h"
#include "lldb/Interpreter/Args.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/MemoryRegionInfo.h"
@@ -81,6 +82,7 @@
m_supports_qXfer_libraries_read(eLazyBoolCalculate),
m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate),
m_supports_qXfer_features_read(eLazyBoolCalculate),
+ m_supports_qXfer_memory_map_read(eLazyBoolCalculate),
m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate),
m_supports_jThreadExtendedInfo(eLazyBoolCalculate),
m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate),
@@ -103,7 +105,8 @@
m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX),
m_default_packet_timeout(0), m_max_packet_size(0),
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
@@ -192,6 +195,13 @@
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() {
if (m_max_packet_size == 0) {
GetRemoteQSupported();
@@ -296,6 +306,7 @@
m_supports_qXfer_libraries_read = eLazyBoolCalculate;
m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
m_supports_qXfer_features_read = eLazyBoolCalculate;
+ m_supports_qXfer_memory_map_read = eLazyBoolCalculate;
m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate;
m_supports_qProcessInfoPID = true;
m_supports_qfProcessInfo = true;
@@ -342,6 +353,7 @@
m_supports_qXfer_libraries_svr4_read = eLazyBoolNo;
m_supports_augmented_libraries_svr4_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
// not, we assume no limit
@@ -377,6 +389,8 @@
m_supports_qXfer_libraries_read = eLazyBoolYes;
if (::strstr(response_cstr, "qXfer:features:read+"))
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.
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma
@@ -1460,7 +1474,8 @@
UNUSED_IF_ASSERT_DISABLED(packet_len);
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse(packet, response, false) ==
- PacketResult::Success) {
+ PacketResult::Success && response.GetResponseType() ==
+ StringExtractorGDBRemote::eResponse) {
llvm::StringRef name;
llvm::StringRef value;
addr_t addr_value = LLDB_INVALID_ADDRESS;
@@ -1536,8 +1551,134 @@
if (m_supports_memory_region_info == eLazyBoolNo) {
error.SetErrorString("qMemoryRegionInfo is not supported");
}
- if (error.Fail())
- region_info.Clear();
+
+ // 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();
+ }
+ } 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;
}
Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -144,6 +144,10 @@
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
Status &error) override;
+ bool BeginWriteMemoryBatch() override;
+
+ bool EndWriteMemoryBatch() override;
+
size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
Status &error) override;
@@ -302,6 +306,11 @@
int64_t m_breakpoint_pc_offset;
lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
+ bool m_is_batched_memory_write;
+ using FlashRangeVector = lldb_private::RangeVector;
+ using FlashRange = FlashRangeVector::Entry;
+ FlashRangeVector m_erased_flash_ranges;
+
//----------------------------------------------------------------------
// Accessors
//----------------------------------------------------------------------
@@ -408,6 +417,12 @@
Status UpdateAutomaticSignalFiltering() override;
+ Status FlashErase(lldb::addr_t addr, size_t size);
+
+ Status FlashDone();
+
+ bool HasErased(FlashRange range);
+
private:
//------------------------------------------------------------------
// For ProcessGDBRemote only
Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -62,6 +62,7 @@
#include "lldb/Target/SystemRuntime.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/TargetList.h"
+#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Target/ThreadPlanCallFunction.h"
#include "lldb/Utility/CleanUp.h"
#include "lldb/Utility/FileSpec.h"
@@ -256,7 +257,8 @@
m_addr_to_mmap_size(), m_thread_create_bp_sp(),
m_waiting_for_attach(false), m_destroy_tried_resuming(false),
m_command_sp(), m_breakpoint_pc_offset(0),
- m_initial_tid(LLDB_INVALID_THREAD_ID) {
+ m_initial_tid(LLDB_INVALID_THREAD_ID), m_is_batched_memory_write(false),
+ m_erased_flash_ranges() {
m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
"async thread should exit");
m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
@@ -2798,6 +2800,132 @@
return 0;
}
+bool ProcessGDBRemote::BeginWriteMemoryBatch() {
+ m_is_batched_memory_write = true;
+ return true;
+}
+
+bool ProcessGDBRemote::EndWriteMemoryBatch() {
+ m_is_batched_memory_write = false;
+ auto status = FlashDone();
+ return status.Success();
+}
+
+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 size, Status &error) {
GetMaxMemorySize();
@@ -2810,16 +2938,40 @@
size = max_memory_size;
}
- StreamString packet;
- packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size);
- packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(),
- endian::InlHostByteOrder());
+ StreamGDBRemote packet;
+
+ MemoryRegionInfo region;
+ Status region_status = GetMemoryRegionInfo(addr, region);
+
+ bool is_flash = region_status.Success() && region.GetFlash() ==
+ MemoryRegionInfo::eYes;
+
+ if (is_flash) {
+ // 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.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+ }
StringExtractorGDBRemote response;
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
true) ==
GDBRemoteCommunication::PacketResult::Success) {
if (response.IsOKResponse()) {
error.Clear();
+ if (is_flash && !m_is_batched_memory_write) {
+ error = FlashDone();
+ if (!error.Success())
+ return 0;
+ }
return size;
} else if (response.IsErrorResponse())
error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64,
Index: source/Symbol/ObjectFile.cpp
===================================================================
--- source/Symbol/ObjectFile.cpp
+++ source/Symbol/ObjectFile.cpp
@@ -659,22 +659,47 @@
SectionList *section_list = GetSectionList();
if (!section_list)
return Status("No section in object file");
+
+ // Filter the list of loadable sections
+ std::vector loadable_sections;
size_t section_count = section_list->GetNumSections(0);
for (size_t i = 0; i < section_count; ++i) {
SectionSP section_sp = section_list->GetSectionAtIndex(i);
addr_t addr = target.GetSectionLoadList().GetSectionLoadAddress(section_sp);
- if (addr != LLDB_INVALID_ADDRESS) {
- DataExtractor section_data;
- // We can skip sections like bss
- if (section_sp->GetFileSize() == 0)
- continue;
- section_sp->GetSectionData(section_data);
- lldb::offset_t written = process->WriteMemory(
- addr, section_data.GetDataStart(), section_data.GetByteSize(), error);
- if (written != section_data.GetByteSize())
- return error;
+ if (addr == LLDB_INVALID_ADDRESS)
+ continue;
+ // We can skip sections like bss
+ if (section_sp->GetFileSize() == 0)
+ continue;
+ loadable_sections.push_back(section_sp);
+ }
+
+ // Sort the sections by address because some writes, like those to flash
+ // memory, must happen in order of increasing address.
+ std::stable_sort(std::begin(loadable_sections), std::end(loadable_sections),
+ [&target](const SectionSP a, const SectionSP b){
+ addr_t addr_a = target.GetSectionLoadList().GetSectionLoadAddress(a);
+ addr_t addr_b = target.GetSectionLoadList().GetSectionLoadAddress(b);
+ return addr_a < addr_b;
+ });
+
+ // Do a batch memory write to the process
+ if (!process->BeginWriteMemoryBatch())
+ return Status("Could not start write memory batch");
+ for (auto §ion_sp : loadable_sections) {
+ DataExtractor section_data;
+ section_sp->GetSectionData(section_data);
+ addr_t addr = target.GetSectionLoadList().GetSectionLoadAddress(section_sp);
+ lldb::offset_t written = process->WriteMemory(
+ addr, section_data.GetDataStart(), section_data.GetByteSize(), error);
+ if (written != section_data.GetByteSize()) {
+ process->EndWriteMemoryBatch();
+ return error;
}
}
+ if (!process->EndWriteMemoryBatch())
+ return Status("Could not end write memory batch");
+
if (set_pc) {
ThreadList &thread_list = process->GetThreadList();
ThreadSP curr_thread(thread_list.GetSelectedThread());