owenpshaw created this revision.
owenpshaw added reviewers: clayborg, labath.
Herald added subscribers: mgorny, emaste.

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
- Add a new foundation for testing gdb-remote behaviors by using a mock server 
that can respond however the test requires

Original discussion at 
http://lists.llvm.org/pipermail/lldb-dev/2018-January/013093.html

---

A few questions...

1. Leveraging MemoryRegionInfo seemed like the way to go, since 
qXfer:memory-map results are similar to qMemoryRegionInfo results.  But should 
GetMemoryRegionInfo() be changed to use the qXfer results instead of having the 
separate new function I added?
2. Are the new gdb-remote python tests moving in the right direction?  I can 
add more cases, but wanted to first verify the foundation was acceptable.  It's 
similar to some code in the tools/lldb-server tests, but seemed different 
enough to justify its own base.


https://reviews.llvm.org/D42145

Files:
  include/lldb/Host/XML.h
  include/lldb/Target/MemoryRegionInfo.h
  include/lldb/Target/Process.h
  
packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteClient.py
  
packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteLoad.py
  packages/Python/lldbsuite/test/functionalities/gdb_remote_client/a.yaml
  
packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py
  source/Host/common/XML.cpp
  source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
  source/Plugins/ObjectFile/ELF/ObjectFileELF.h
  source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
  source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
  source/Symbol/ObjectFile.cpp
  unittests/Process/gdb-remote/CMakeLists.txt
  unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp

Index: unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp
===================================================================
--- /dev/null
+++ unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp
@@ -0,0 +1,54 @@
+//===-- GDBRemoteCommunicationTest.cpp --------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include <future>
+
+#include "GDBRemoteTestUtils.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private::process_gdb_remote;
+using namespace lldb_private;
+
+TEST(GDBRemoteCommunicationTest, WriteEscapedBinary) {
+  StreamString escaped;
+
+  // Nothing gets escaped
+  // Verify null and other control chars don't cause problems
+  const uint8_t data[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+  GDBRemoteCommunication::WriteEscapedBinary(escaped, data, sizeof(data));
+  ASSERT_EQ(sizeof(data), escaped.GetSize());
+  ASSERT_EQ(0x00, escaped.GetString().data()[0]);
+  ASSERT_EQ(0x03, escaped.GetString().data()[3]);
+  ASSERT_EQ(0x07, escaped.GetString().data()[7]);
+
+  // 0x23 and 0x24 should be escaped
+  escaped.Clear();
+  const uint8_t data2[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27};
+  GDBRemoteCommunication::WriteEscapedBinary(escaped, data2, sizeof(data));
+  ASSERT_EQ(sizeof(data) + 2, escaped.GetSize());
+  ASSERT_EQ(0x20, escaped.GetString().data()[0]);
+  ASSERT_EQ(0x7d, escaped.GetString().data()[3]);
+  ASSERT_EQ(0x23 ^ 0x20, escaped.GetString().data()[4]);
+  ASSERT_EQ(0x7d, escaped.GetString().data()[5]);
+  ASSERT_EQ(0x24 ^ 0x20, escaped.GetString().data()[6]);
+  ASSERT_EQ(0x25, escaped.GetString().data()[7]);
+  ASSERT_EQ(0x27, escaped.GetString().data()[9]);
+
+  // 0x7d should be escaped
+  escaped.Clear();
+  const uint8_t data3[] = {0x7b, 0x74, 0x65, 0x73, 0x74, 0x7d};
+  GDBRemoteCommunication::WriteEscapedBinary(escaped, data3, sizeof(data));
+  ASSERT_EQ(sizeof(data) + 1, escaped.GetSize());
+  ASSERT_EQ(0x7b, escaped.GetString().data()[0]);
+  ASSERT_EQ(0x74, escaped.GetString().data()[1]);
+  ASSERT_EQ(0x65, escaped.GetString().data()[2]);
+  ASSERT_EQ(0x73, escaped.GetString().data()[3]);
+  ASSERT_EQ(0x74, escaped.GetString().data()[4]);
+  ASSERT_EQ(0x7d, escaped.GetString().data()[5]);
+  ASSERT_EQ(0x7d ^ 0x20, escaped.GetString().data()[6]);
+}
Index: unittests/Process/gdb-remote/CMakeLists.txt
===================================================================
--- unittests/Process/gdb-remote/CMakeLists.txt
+++ unittests/Process/gdb-remote/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_unittest(ProcessGdbRemoteTests
   GDBRemoteClientBaseTest.cpp
   GDBRemoteCommunicationClientTest.cpp
+  GDBRemoteCommunicationTest.cpp
   GDBRemoteTestUtils.cpp
 
   LINK_LIBS
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<SectionSP> 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 &section_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());
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,12 @@
   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<lldb::addr_t, size_t>;
+  using FlashRange = FlashRangeVector::Entry;
+  FlashRangeVector m_erased_flash_ranges;
+  std::vector<lldb::MemoryRegionInfoSP> m_memory_map;
+
   //----------------------------------------------------------------------
   // Accessors
   //----------------------------------------------------------------------
@@ -408,6 +418,16 @@
 
   Status UpdateAutomaticSignalFiltering() override;
 
+  bool GetMemoryMap(std::vector<lldb::MemoryRegionInfoSP> &region_list);
+
+  Status FlashErase(lldb::addr_t addr, size_t size);
+
+  Status FlashDone();
+
+  bool HasErased(FlashRange range);
+
+  lldb::MemoryRegionInfoSP GetMemoryMapRegion(lldb::addr_t addr);
+
 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_memory_map() {
   m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
                                    "async thread should exit");
   m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
@@ -2798,6 +2800,138 @@
   return 0;
 }
 
+MemoryRegionInfoSP ProcessGDBRemote::GetMemoryMapRegion(lldb::addr_t addr) {
+  if (m_memory_map.empty())
+    GetMemoryMap(m_memory_map);
+  for (const auto &region : m_memory_map)
+    if (region->GetRange().Contains(addr))
+      return region;
+  return nullptr;
+}
+
+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;
+
+  MemoryRegionInfoSP region_sp = GetMemoryMapRegion(addr);
+
+  // 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_sp->GetRange().GetRangeEnd()){
+    status.SetErrorString("Unable to erase flash in multiple regions");
+    return status;
+  }
+
+  uint64_t blocksize = region_sp->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();
@@ -2811,15 +2945,37 @@
   }
 
   StreamString packet;
-  packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size);
-  packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(),
-                           endian::InlHostByteOrder());
+
+  MemoryRegionInfoSP region_sp = GetMemoryMapRegion(addr);
+
+  bool is_flash = region_sp != nullptr && region_sp->GetFlash() ==
+      MemoryRegionInfo::eYes;
+  if (is_flash) {
+    // Keep the write within a flash memory region
+    if (addr + size > region_sp->GetRange().GetRangeEnd())
+      size = region_sp->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);
+    GDBRemoteCommunication::WriteEscapedBinary(packet, 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,
@@ -4453,6 +4609,78 @@
   return m_register_info.GetNumRegisters() > 0;
 }
 
+bool ProcessGDBRemote::GetMemoryMap(
+    std::vector<lldb::MemoryRegionInfoSP> &region_list) {
+
+  if (!XMLDocument::XMLEnabled())
+    return false;
+
+  auto &comm = m_gdb_comm;
+
+  if (!comm.GetQXferMemoryMapReadSupported())
+    return false;
+
+  std::string xml;
+  lldb_private::Status lldberr;
+  if (!comm.ReadExtFeature(ConstString("memory-map"),
+                           ConstString(""),
+                           xml, lldberr))
+    return false;
+
+  XMLDocument xml_document;
+
+  if (!xml_document.ParseMemory(xml.c_str(), xml.size()))
+    return false;
+
+  XMLNode map_node = xml_document.GetRootElement("memory-map");
+  if (!map_node)
+    return false;
+
+  map_node.ForEachChildElement([&region_list](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;
+    MemoryRegionInfoSP region_sp(new MemoryRegionInfo());
+    region_sp->GetRange().SetRangeBase(start);
+    region_sp->GetRange().SetByteSize(length);
+    if (type == "rom") {
+      region_sp->SetReadable(MemoryRegionInfo::eYes);
+      region_list.push_back(region_sp);
+    } else if (type == "ram") {
+      region_sp->SetReadable(MemoryRegionInfo::eYes);
+      region_sp->SetWritable(MemoryRegionInfo::eYes);
+      region_list.push_back(region_sp);
+    } else if (type == "flash") {
+      region_sp->SetFlash(MemoryRegionInfo::eYes);
+      memory_node.ForEachChildElement([&region_sp](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_sp->SetBlocksize(blocksize);
+        }
+        return true;
+      });
+      region_list.push_back(region_sp);
+    }
+    return true;
+  });
+
+  return true;
+}
+
 Status ProcessGDBRemote::GetLoadedModuleList(LoadedModuleInfoList &list) {
   // Make sure LLDB has an XML parser it can use first
   if (!XMLDocument::XMLEnabled())
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;
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -81,6 +81,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),
@@ -192,6 +193,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 +304,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 +351,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 +387,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
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -141,6 +141,17 @@
 
   void DumpHistory(Stream &strm);
 
+  //------------------------------------------------------------------
+  // Write the bytes from buf into stream, including escape sequences
+  // for certain bytes as described by the gdb server protocol.
+  //
+  // Most commands escape using hex strings, but a few commands send
+  // nearly-raw binary, only escaping special characters such as #, $,
+  // and }.
+  //------------------------------------------------------------------
+  static void WriteEscapedBinary(StreamString &stream, const void *buf,
+                                 size_t size);
+
 protected:
   class History {
   public:
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -274,6 +274,23 @@
   return result;
 }
 
+void GDBRemoteCommunication::WriteEscapedBinary(StreamString &stream,
+                                                const void *buf,
+                                                size_t size) {
+  const uint8_t *bytes = (const uint8_t *)buf;
+  const uint8_t *end = bytes + size;
+  const uint8_t escape = 0x7d;
+  for (; bytes != end; ++bytes) {
+    if (*bytes == 0x23 || *bytes == 0x24 || *bytes == 0x7d) {
+      const uint8_t x = (*bytes) ^ 0x20;
+      stream.Write(&escape, 1);
+      stream.Write(&x, 1);
+    }else{
+      stream.Write(bytes, 1);
+    }
+  }
+}
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunication::ReadPacketWithOutputSupport(
     StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
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/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: packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py
@@ -0,0 +1,342 @@
+import os
+import os.path
+import subprocess
+import threading
+import socket
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbtest_config
+
+
+def yaml2obj_executable():
+    # Tries to find yaml2obj at the same folder as the lldb
+    path = os.path.join(os.path.dirname(lldbtest_config.lldbExec), "yaml2obj")
+    if os.path.exists(path):
+        return path
+    raise Exception("yaml2obj executable not found")
+
+
+def yaml2elf(yaml_path, elf_path):
+    yaml2obj = yaml2obj_executable()
+    command = [yaml2obj, "-o=%s" % elf_path, yaml_path]
+    system([command])
+
+
+def checksum(message):
+    check = 0
+    for c in message:
+        check += ord(c)
+    return check % 256
+
+
+def frame_packet(message):
+    return "$%s#%02x" % (message, checksum(message))
+
+
+def escape_binary(message):
+    out = ""
+    for c in message:
+        d = ord(c)
+        if d in (0x23, 0x24, 0x7d):
+            out += chr(0x7d)
+            out += chr(d ^ 0x20)
+        else:
+            out += c
+    return out
+
+
+def hex_encode_bytes(message):
+    out = ""
+    for c in message:
+        out += "%02x" % ord(c)
+    return out
+
+
+def hex_decode_bytes(hex_bytes):
+    out = ""
+    hex_len = len(hex_bytes)
+    while i < hex_len - 1:
+        out += chr(int(hex_bytes[i:i + 2]), 16)
+        i += 2
+    return out
+
+
+class MockGDBServerResponder:
+
+    registerCount = 40
+    packetLog = None
+
+    def __init__(self):
+        self.packetLog = []
+
+    def respond(self, packet):
+        self.packetLog.append(packet)
+        if packet == "g":
+            return self.readRegisters()
+        if packet[0] == "G":
+            return self.writeRegisters(packet[1:])
+        if packet[0] == "p":
+            return self.readRegister(int(packet[1:], 16))
+        if packet[0] == "P":
+            register, value = packet[1:].split("=")
+            return self.readRegister(int(register, 16), value)
+        if packet[0] == "m":
+            addr, length = [int(x, 16) for x in packet[1:].split(',')]
+            return self.readMemory(addr, length)
+        if packet[0] == "M":
+            location, encoded_data = packet[1:].split(":")
+            addr, length = [int(x, 16) for x in location.split(',')]
+            return self.writeMemory(addr, encoded_data)
+        if packet[0:7] == "qSymbol":
+            return self.qSymbol(packet[8:])
+        if packet[0:10] == "qSupported":
+            return self.qSupported(packet[11:].split(";"))
+        if packet == "qfThreadInfo":
+            return self.qfThreadInfo()
+        if packet == "qC":
+            return self.qC()
+        if packet == "?":
+            return self.haltReason()
+        if packet[0] == "H":
+            return self.selectThread(packet[1], int(packet[2:], 16))
+        if packet[0:6] == "qXfer:":
+            obj, read, annex, location = packet[6:].split(":")
+            offset, length = [int(x, 16) for x in location.split(',')]
+            data, has_more = self.qXferRead(obj, annex, offset, length)
+            if data is not None:
+                return self._qXferResponse(data, has_more)
+            return ""
+        return self.other(packet)
+
+    def readRegisters(self):
+        return "xxxxxxxx" * self.registerCount
+
+    def readRegister(self, register):
+        return "xxxxxxxx"
+
+    def writeRegisters(self, registers_hex):
+        return "OK"
+
+    def writeRegister(self, register, value_hex):
+        return "OK"
+
+    def readMemory(self, addr, length):
+        return "00" * length
+
+    def writeMemory(self, addr, data_hex):
+        return "OK"
+
+    def qSymbol(self, symbol_args):
+        return "OK"
+
+    def qSupported(self, client_supported):
+        return "PacketSize=3fff;QStartNoAckMode+"
+
+    def qfThreadInfo(self):
+        return "l"
+
+    def qC(self):
+        return "QC0"
+
+    def haltReason(self):
+        # SIGINT is 2, return type is 2 digit hex string
+        return "S02"
+
+    def qXferRead(self, obj, annex, offset, length):
+        return None, False
+
+    def _qXferResponse(self, data, has_more):
+        return "%s%s" % ("m" if has_more else "l", escape_binary(data))
+
+    def selectThread(self, op, thread_id):
+        return "OK"
+
+    def other(self, packet):
+        # empty string means unsupported
+        return ""
+
+
+class MockGDBServer:
+
+    responder = None
+    port = 0
+    _socket = None
+    _client = None
+    _thread = None
+    _incomingPacket = None
+    _incomingChecksum = None
+    _shouldSendAck = True
+    _isExpectingAck = False
+    
+    def __init__(self, port = 0):
+        self.responder = MockGDBServerResponder()
+        self.port = port
+        self._socket = socket.socket()
+
+    def start(self):
+        # Block until the socket is up, so self.port is available immediately.
+        # Then start a thread that waits for a client connection.
+        addr = ("127.0.0.1", self.port)
+        self._socket.bind(addr)
+        self.port = self._socket.getsockname()[1]
+        self._socket.listen(0)
+        self._thread = threading.Thread(target=self._run)
+        self._thread.start()
+
+    def stop(self):
+        if self._client is not None:
+            self._client.shutdown(socket.SHUT_RDWR)
+            self._client.close()
+        # Would call self._socket.shutdown, but it blocks forever for some
+        # unknown reason.  close() works just fine.
+        self._socket.close()
+        self._thread.join()
+        self._thread = None
+
+    def _run(self):
+        # For testing purposes, we only need to worry about one client
+        # connecting just one time.
+        try:
+            # accept() is stubborn and won't fail even when the socket is
+            # shutdown, so we'll use a timeout
+            self._socket.settimeout(2.0)
+            client, client_addr = self._socket.accept()
+            self._client = client
+            # The connected client inherits its timeout from self._socket,
+            # but we'll use a blocking socket for the client
+            self._client.settimeout(None)
+        except:
+            return
+        self._shouldSendAck = True
+        self._isExpectingAck = False
+        data = None
+        while True:
+            try:
+                data = self._client.recv(4096)
+                if data is None or len(data) == 0:
+                    break
+            except Exception as e:
+                self._client.close()
+                break
+            self._receive(data)
+
+    def _receive(self, data):
+        i = 0
+        data_len = len(data)
+        while i < data_len:
+            # If we haven't set _incomingPacket to anything yet, it means we're
+            # expecting the start of a new packet.
+            if self._incomingPacket is None:
+                if data[i] == '+':
+                    if self._isExpectingAck:
+                        # We're expecting an ack from the client, so ignore it.
+                        self._isExpectingAck = False
+                    else:
+                        # Not expecting an ack, so just ack back.
+                        self._client.sendall('+')
+                    i += 1
+                elif data[i] == '$':
+                    self._incomingPacket = ""
+                    i += 1
+                else:
+                    # Unexpected byte, closing connection to indicate error.
+                    self._client.close()
+                    return
+            # If we haven't set _incomingChecksum to anything yet, it means
+            # we're collecting bytes, waiting for a # to indicate the end of
+            # packet data.
+            elif self._incomingChecksum is None:
+                while i < data_len:
+                    if data[i] == '#':
+                        self._incomingChecksum = ""
+                        i += 1
+                        break
+                    self._incomingPacket += data[i]
+                    i += 1
+            # If we have set _incomingChecksum, then we're collecting the
+            # two bytes of the checksum string.
+            else:
+                while i < data_len and len(self._incomingChecksum) < 2:
+                    self._incomingChecksum += data[i]
+                    i += 1
+                if len(self._incomingChecksum) == 2:
+                    check = None
+                    try:
+                        check = int(self._incomingChecksum, 16)
+                    except ValueError:
+                        # Non-hex checksum, closing connection.
+                        self._client.close()
+                        return
+                    if check != checksum(self._incomingPacket):
+                        # Mismatching checksums, closing connection.
+                        # Since we're using TCP transport, the checksum can
+                        # only be wrong if the client did something wrong.
+                        self._client.close()
+                        return
+                    packet = self._incomingPacket
+                    self._incomingPacket = None
+                    self._incomingChecksum = None
+                    self._handlePacket(packet)
+
+    def _handlePacket(self, packet):
+        response = ""
+        # We'll handle the ack stuff here since it's not something any of the
+        # tests will be concerned about, and it'll get turned off quicly anyway.
+        if self._shouldSendAck:
+            self._client.sendall('+')
+            self._isExpectingAck = True
+        if packet == "QStartNoAckMode":
+            self._shouldSendAck = False
+            response = "OK"
+        elif self.responder is not None:
+            # Delegate everything else to our responder
+            response = self.responder.respond(packet)
+        # Handle packet framing since we don't want to bother tests with it.
+        framed = frame_packet(response)
+        self._client.sendall(framed)
+
+
+class GDBRemoteTestBase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    server = None
+    temp_files = None
+
+    def setUp(self):
+        TestBase.setUp(self)
+        self.temp_files = []
+        self.server = MockGDBServer()
+        self.server.start()
+
+    def tearDown(self):
+        for temp_file in self.temp_files:
+            self.RemoveTempFile(temp_file)
+        self.server.stop()
+        self.temp_files = []
+        TestBase.tearDown(self)
+
+    def createTarget(self, yaml_path):
+        yaml_base, ext = os.path.splitext(yaml_path)
+        elf_path = "%s.elf" % yaml_base
+        yaml2elf(yaml_path, elf_path)
+        self.temp_files.append(elf_path)
+        return self.dbg.CreateTarget(elf_path)
+
+    def connect(self, target):
+        listener = self.dbg.GetListener()
+        error = lldb.SBError()
+        url = "connect://localhost:%d" % self.server.port
+        process = target.ConnectRemote(listener, url, "gdb-remote", error)
+        self.assertTrue(error.Success(), error.description)
+        self.assertTrue(process, PROCESS_IS_VALID)
+
+    def assertPacketLogContains(self, packets):
+        i = 0
+        j = 0
+        log = self.server.responder.packetLog
+        while i < len(packets) and j < len(log):
+            if log[j] == packets[i]:
+                i += 1
+            j += 1
+        if i < len(packets):
+            self.fail("Did not receive: %s" % packets[i])
Index: packages/Python/lldbsuite/test/functionalities/gdb_remote_client/a.yaml
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/functionalities/gdb_remote_client/a.yaml
@@ -0,0 +1,34 @@
+!ELF
+FileHeader:
+  Class:           ELFCLASS32
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_ARM
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x4
+    Content:         "c3c3c3c3"
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x2000
+    AddressAlign:    0x4
+    Content:         "3232"
+ProgramHeaders:
+  - Type: PT_LOAD
+    Flags: [ PF_X, PF_R ]
+    VAddr: 0x1000
+    PAddr: 0x1000
+    Align: 0x4
+    Sections:
+      - Section: .text
+  - Type: PT_LOAD
+    Flags: [ PF_R, PF_W ]
+    VAddr: 0x2000
+    PAddr: 0x1004
+    Align: 0x4
+    Sections:
+      - Section: .data
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,62 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from gdbclientutils import *
+
+
+class TestGDBRemoteLoad(GDBRemoteTestBase):
+
+    @no_debug_info_test
+    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"
+                ])
+
+    @no_debug_info_test
+    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"
+                ])
Index: packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteClient.py
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestGDBRemoteClient.py
@@ -0,0 +1,13 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from gdbclientutils import *
+
+
+class TestGDBRemoteClient(GDBRemoteTestBase):
+
+    @no_debug_info_test
+    def test_connect(self):
+        """Test connecting to a remote gdb server"""
+        target = self.createTarget("a.yaml")
+        process = self.connect(target)
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: 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/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);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to