This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rL327970: Re-land: [lldb] Use vFlash commands when writing to 
target's flash memory… (authored by labath, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D42145?vs=137827&id=139106#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D42145

Files:
  lldb/trunk/include/lldb/Core/Module.h
  lldb/trunk/include/lldb/Host/XML.h
  lldb/trunk/include/lldb/Symbol/ObjectFile.h
  lldb/trunk/include/lldb/Target/MemoryRegionInfo.h
  lldb/trunk/include/lldb/Target/Process.h
  
lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteLoad.py
  
lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py
  lldb/trunk/source/Commands/CommandObjectTarget.cpp
  lldb/trunk/source/Core/Module.cpp
  lldb/trunk/source/Host/common/XML.cpp
  lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
  lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
  lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
  lldb/trunk/source/Symbol/ObjectFile.cpp
  lldb/trunk/source/Target/Process.cpp

Index: lldb/trunk/source/Host/common/XML.cpp
===================================================================
--- lldb/trunk/source/Host/common/XML.cpp
+++ lldb/trunk/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: lldb/trunk/source/Core/Module.cpp
===================================================================
--- lldb/trunk/source/Core/Module.cpp
+++ lldb/trunk/source/Core/Module.cpp
@@ -1685,7 +1685,3 @@
 
   return false;
 }
-
-Status Module::LoadInMemory(Target &target, bool set_pc) {
-  return m_objfile_sp->LoadInMemory(target, set_pc);
-}
Index: lldb/trunk/source/Target/Process.cpp
===================================================================
--- lldb/trunk/source/Target/Process.cpp
+++ lldb/trunk/source/Target/Process.cpp
@@ -2533,6 +2533,17 @@
   return 0;
 }
 
+Status Process::WriteObjectFile(std::vector<ObjectFile::LoadableData> 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
 addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
                                Status &error) {
Index: lldb/trunk/source/Symbol/ObjectFile.cpp
===================================================================
--- lldb/trunk/source/Symbol/ObjectFile.cpp
+++ lldb/trunk/source/Symbol/ObjectFile.cpp
@@ -16,7 +16,6 @@
 #include "lldb/Symbol/ObjectContainer.h"
 #include "lldb/Symbol/SymbolFile.h"
 #include "lldb/Target/Process.h"
-#include "lldb/Target/RegisterContext.h"
 #include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/DataBuffer.h"
@@ -648,41 +647,31 @@
   return ConstString(ss.GetString());
 }
 
-Status ObjectFile::LoadInMemory(Target &target, bool set_pc) {
-  Status error;
-  ProcessSP process = target.CalculateProcess();
-  if (!process)
-    return Status("No Process");
-  if (set_pc && !GetEntryPointAddress().IsValid())
-    return Status("No entry address in object file");
-
+std::vector<ObjectFile::LoadableData>
+ObjectFile::GetLoadableData(Target &target) {
+  std::vector<LoadableData> loadables;
   SectionList *section_list = GetSectionList();
   if (!section_list)
-    return Status("No section in object file");
+    return loadables;
+  // Create a list of loadable data from loadable sections
   size_t section_count = section_list->GetNumSections(0);
   for (size_t i = 0; i < section_count; ++i) {
+    LoadableData loadable;
     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 (set_pc) {
-    ThreadList &thread_list = process->GetThreadList();
-    ThreadSP curr_thread(thread_list.GetSelectedThread());
-    RegisterContextSP reg_context(curr_thread->GetRegisterContext());
-    Address file_entry = GetEntryPointAddress();
-    reg_context->SetPC(file_entry.GetLoadAddress(&target));
+    loadable.Dest =
+        target.GetSectionLoadList().GetSectionLoadAddress(section_sp);
+    if (loadable.Dest == LLDB_INVALID_ADDRESS)
+      continue;
+    // We can skip sections like bss
+    if (section_sp->GetFileSize() == 0)
+      continue;
+    DataExtractor section_data;
+    section_sp->GetSectionData(section_data);
+    loadable.Contents = llvm::ArrayRef<uint8_t>(section_data.GetDataStart(),
+                                                section_data.GetByteSize());
+    loadables.push_back(loadable);
   }
-  return error;
+  return loadables;
 }
 
 void ObjectFile::RelocateSection(lldb_private::Section *section)
Index: lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
===================================================================
--- lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
+++ lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
@@ -161,6 +161,11 @@
 
   void RelocateSection(lldb_private::Section *section) override;
 
+protected:
+
+  std::vector<LoadableData>
+  GetLoadableData(lldb_private::Target &target) override;
+
 private:
   ObjectFileELF(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
                 lldb::offset_t data_offset, const lldb_private::FileSpec *file,
@@ -383,6 +388,8 @@
   RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
                               lldb_private::ArchSpec &arch_spec,
                               lldb_private::UUID &uuid);
+
+  bool AnySegmentHasPhysicalAddress();
 };
 
 #endif // liblldb_ObjectFileELF_h_
Index: lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
===================================================================
--- lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -3442,3 +3442,38 @@
   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;
+}
+
+std::vector<ObjectFile::LoadableData>
+ObjectFileELF::GetLoadableData(Target &target) {
+  // Create a list of loadable data from loadable segments,
+  // using physical addresses if they aren't all null
+  std::vector<LoadableData> loadables;
+  size_t header_count = ParseProgramHeaders();
+  bool should_use_paddr = AnySegmentHasPhysicalAddress();
+  for (size_t i = 1; i <= header_count; ++i) {
+    LoadableData loadable;
+    auto header = GetProgramHeaderByIndex(i);
+    if (header->p_type != llvm::ELF::PT_LOAD)
+      continue;
+    loadable.Dest = should_use_paddr ? header->p_paddr : header->p_vaddr;
+    if (loadable.Dest == LLDB_INVALID_ADDRESS)
+      continue;
+    if (header->p_filesz == 0)
+      continue;
+    auto segment_data = GetSegmentDataByIndex(i);
+    loadable.Contents = llvm::ArrayRef<uint8_t>(segment_data.GetDataStart(),
+                                                segment_data.GetByteSize());
+    loadables.push_back(loadable);
+  }
+  return loadables;
+}
Index: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -144,6 +144,9 @@
   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
                       Status &error) override;
 
+  Status
+  WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override;
+
   size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
                        Status &error) override;
 
@@ -302,6 +305,11 @@
   int64_t m_breakpoint_pc_offset;
   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
   //----------------------------------------------------------------------
@@ -408,6 +416,12 @@
 
   Status UpdateAutomaticSignalFiltering() override;
 
+  Status FlashErase(lldb::addr_t addr, size_t size);
+
+  Status FlashDone();
+
+  bool HasErased(FlashRange range);
+
 private:
   //------------------------------------------------------------------
   // For ProcessGDBRemote only
Index: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -59,6 +59,7 @@
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Target/ABI.h"
 #include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/MemoryRegionInfo.h"
 #include "lldb/Target/SystemRuntime.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Target/TargetList.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_allow_flash_writes(false),
+      m_erased_flash_ranges() {
   m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
                                    "async thread should exit");
   m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
@@ -2798,6 +2800,145 @@
   return 0;
 }
 
+Status ProcessGDBRemote::WriteObjectFile(
+    std::vector<ObjectFile::LoadableData> 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 ObjectFile::LoadableData a, const ObjectFile::LoadableData 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 size, Status &error) {
   GetMaxMemorySize();
@@ -2810,10 +2951,33 @@
     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) {
+    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.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(),
+                             endian::InlHostByteOrder());
+  }
   StringExtractorGDBRemote response;
   if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
                                               true) ==
Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/trunk/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<MemoryRegionInfo> 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<uint8_t> &buffer,
                                 size_t offset);
 
+  Status LoadQXferMemoryMap();
+
+  Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr,
+                                     MemoryRegionInfo &region);
+
 private:
   DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient);
 };
Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/trunk/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 &region) {
+  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(
+          [&region](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: lldb/trunk/source/Commands/CommandObjectTarget.cpp
===================================================================
--- lldb/trunk/source/Commands/CommandObjectTarget.cpp
+++ lldb/trunk/source/Commands/CommandObjectTarget.cpp
@@ -45,6 +45,7 @@
 #include "lldb/Symbol/VariableList.h"
 #include "lldb/Target/ABI.h"
 #include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
 #include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/StackFrame.h"
 #include "lldb/Target/Thread.h"
@@ -2746,11 +2747,34 @@
                     process->Flush();
                 }
                 if (load) {
-                  Status error = module->LoadInMemory(*target, set_pc);
+                  ProcessSP process = target->CalculateProcess();
+                  Address file_entry = objfile->GetEntryPointAddress();
+                  if (!process) {
+                    result.AppendError("No process");
+                    return false;
+                  }
+                  if (set_pc && !file_entry.IsValid()) {
+                    result.AppendError("No entry address in object file");
+                    return false;
+                  }
+                  std::vector<ObjectFile::LoadableData> loadables(
+                      objfile->GetLoadableData(*target));
+                  if (loadables.size() == 0) {
+                    result.AppendError("No loadable sections");
+                    return false;
+                  }
+                  Status error = process->WriteObjectFile(std::move(loadables));
                   if (error.Fail()) {
                     result.AppendError(error.AsCString());
                     return false;
                   }
+                  if (set_pc) {
+                    ThreadList &thread_list = process->GetThreadList();
+                    ThreadSP curr_thread(thread_list.GetSelectedThread());
+                    RegisterContextSP reg_context(
+                        curr_thread->GetRegisterContext());
+                    reg_context->SetPC(file_entry.GetLoadAddress(target));
+                  }
                 }
               } else {
                 module->GetFileSpec().GetPath(path, sizeof(path));
Index: lldb/trunk/include/lldb/Symbol/ObjectFile.h
===================================================================
--- lldb/trunk/include/lldb/Symbol/ObjectFile.h
+++ lldb/trunk/include/lldb/Symbol/ObjectFile.h
@@ -88,6 +88,11 @@
     eStrataJIT
   } Strata;
 
+  struct LoadableData {
+    lldb::addr_t Dest;
+    llvm::ArrayRef<uint8_t> Contents;
+  };
+
   //------------------------------------------------------------------
   /// Construct with a parent module, offset, and header data.
   ///
@@ -838,7 +843,7 @@
   ///
   /// @return
   //------------------------------------------------------------------
-  virtual Status LoadInMemory(Target &target, bool set_pc);
+  virtual std::vector<LoadableData> GetLoadableData(Target &target);
 
 protected:
   //------------------------------------------------------------------
Index: lldb/trunk/include/lldb/Core/Module.h
===================================================================
--- lldb/trunk/include/lldb/Core/Module.h
+++ lldb/trunk/include/lldb/Core/Module.h
@@ -1021,20 +1021,6 @@
   bool RemapSourceFile(llvm::StringRef path, std::string &new_path) const;
   bool RemapSourceFile(const char *, std::string &) const = delete;
 
-  //------------------------------------------------------------------
-  /// Loads this module to memory.
-  ///
-  /// Loads the bits needed to create an executable image to the memory.
-  /// It is useful with bare-metal targets where target does not have the
-  /// ability to start a process itself.
-  ///
-  /// @param[in] target
-  ///     Target where to load the module.
-  ///
-  /// @return
-  //------------------------------------------------------------------
-  Status LoadInMemory(Target &target, bool set_pc);
-
   //----------------------------------------------------------------------
   /// @class LookupInfo Module.h "lldb/Core/Module.h"
   /// @brief A class that encapsulates name lookup information.
Index: lldb/trunk/include/lldb/Target/Process.h
===================================================================
--- lldb/trunk/include/lldb/Target/Process.h
+++ lldb/trunk/include/lldb/Target/Process.h
@@ -38,6 +38,7 @@
 #include "lldb/Host/HostThread.h"
 #include "lldb/Host/ProcessRunLock.h"
 #include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Target/ExecutionContextScope.h"
 #include "lldb/Target/InstrumentationRuntime.h"
 #include "lldb/Target/Memory.h"
@@ -1950,6 +1951,8 @@
     return LLDB_INVALID_ADDRESS;
   }
 
+  virtual Status WriteObjectFile(std::vector<ObjectFile::LoadableData> entries);
+
   //------------------------------------------------------------------
   /// The public interface to allocating memory in the process.
   ///
Index: lldb/trunk/include/lldb/Target/MemoryRegionInfo.h
===================================================================
--- lldb/trunk/include/lldb/Target/MemoryRegionInfo.h
+++ lldb/trunk/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: lldb/trunk/include/lldb/Host/XML.h
===================================================================
--- lldb/trunk/include/lldb/Host/XML.h
+++ lldb/trunk/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: lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py
===================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py
@@ -453,5 +453,5 @@
                 i += 1
             j += 1
         if i < len(packets):
-            self.fail("Did not receive: %s\nLast 10 packets:\n\t%s" %
-                    (packets[i], '\n\t'.join(log[-10:])))
+            self.fail(u"Did not receive: %s\nLast 10 packets:\n\t%s" %
+                    (packets[i], u'\n\t'.join(log[-10:])))
Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteLoad.py
===================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteLoad.py
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteLoad.py
@@ -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"
+                ])
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to