[lldb] Use vFlash commands when writing to target's flash memory regions

Summary:
When writing an object file over gdb-remote, use the vFlashErase, vFlashWrite, and vFlashDone commands if the write address is in a flash memory region.  A bare metal target may have this kind of setup.

- Update ObjectFileELF to set load addresses using physical addresses.  A typical case may be a data section with a physical address in ROM and a virtual address in RAM, which should be loaded to the ROM address.
- Add support for querying the target's qXfer:memory-map, which contains information about flash memory regions, leveraging MemoryRegionInfo data structures with minor modifications
- Update ProcessGDBRemote to use vFlash commands in DoWriteMemory when the target address is in a flash region

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

Reviewers: clayborg, labath

Reviewed By: labath

Subscribers: arichardson, emaste, mgorny, lldb-commits

Differential Revision: https://reviews.llvm.org/D42145
Patch by Owen Shaw <llvm@owenpshaw.net>

llvm-svn: 326261
This commit is contained in:
Pavel Labath 2018-02-27 22:14:33 +00:00
parent 3acdc67734
commit 029fb69372
13 changed files with 505 additions and 23 deletions

View File

@ -82,6 +82,9 @@ public:
llvm::StringRef GetAttributeValue(const char *name, llvm::StringRef GetAttributeValue(const char *name,
const char *fail_value = nullptr) const; const char *fail_value = nullptr) const;
bool GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
uint64_t fail_value = 0, int base = 0) const;
XMLNode FindFirstChildElementWithName(const char *name) const; XMLNode FindFirstChildElementWithName(const char *name) const;
XMLNode GetElementForPath(const NamePath &path); XMLNode GetElementForPath(const NamePath &path);

View File

@ -25,7 +25,7 @@ public:
MemoryRegionInfo() MemoryRegionInfo()
: m_range(), m_read(eDontKnow), m_write(eDontKnow), m_execute(eDontKnow), : m_range(), m_read(eDontKnow), m_write(eDontKnow), m_execute(eDontKnow),
m_mapped(eDontKnow) {} m_mapped(eDontKnow), m_flash(eDontKnow), m_blocksize(0) {}
~MemoryRegionInfo() {} ~MemoryRegionInfo() {}
@ -58,6 +58,14 @@ public:
void SetName(const char *name) { m_name = ConstString(name); } void SetName(const char *name) { m_name = ConstString(name); }
OptionalBool GetFlash() const { return m_flash; }
void SetFlash(OptionalBool val) { m_flash = val; }
lldb::offset_t GetBlocksize() const { return m_blocksize; }
void SetBlocksize(lldb::offset_t blocksize) { m_blocksize = blocksize; }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Get permissions as a uint32_t that is a mask of one or more bits from // Get permissions as a uint32_t that is a mask of one or more bits from
// the lldb::Permissions // the lldb::Permissions
@ -98,6 +106,8 @@ protected:
OptionalBool m_execute; OptionalBool m_execute;
OptionalBool m_mapped; OptionalBool m_mapped;
ConstString m_name; ConstString m_name;
OptionalBool m_flash;
lldb::offset_t m_blocksize;
}; };
} }

View File

@ -556,6 +556,11 @@ public:
return GetStaticBroadcasterClass(); return GetStaticBroadcasterClass();
} }
struct WriteEntry {
lldb::addr_t Dest;
llvm::ArrayRef<uint8_t> Contents;
};
//------------------------------------------------------------------ //------------------------------------------------------------------
/// A notification structure that can be used by clients to listen /// A notification structure that can be used by clients to listen
/// for changes in a process's lifetime. /// for changes in a process's lifetime.
@ -1950,6 +1955,8 @@ public:
return LLDB_INVALID_ADDRESS; return LLDB_INVALID_ADDRESS;
} }
virtual Status WriteObjectFile(std::vector<WriteEntry> entries);
//------------------------------------------------------------------ //------------------------------------------------------------------
/// The public interface to allocating memory in the process. /// The public interface to allocating memory in the process.
/// ///

View File

@ -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"
])

View File

@ -151,6 +151,18 @@ llvm::StringRef XMLNode::GetAttributeValue(const char *name,
return llvm::StringRef(); return llvm::StringRef();
} }
bool XMLNode::GetAttributeValueAsUnsigned(const char *name, uint64_t &value,
uint64_t fail_value, int base) const {
#if defined(LIBXML2_DEFINED)
llvm::StringRef str_value = GetAttributeValue(name, "");
#else
llvm::StringRef str_value;
#endif
bool success = false;
value = StringConvert::ToUInt64(str_value.data(), fail_value, base, &success);
return success;
}
void XMLNode::ForEachChildNode(NodeCallback const &callback) const { void XMLNode::ForEachChildNode(NodeCallback const &callback) const {
#if defined(LIBXML2_DEFINED) #if defined(LIBXML2_DEFINED)
if (IsValid()) if (IsValid())

View File

@ -827,7 +827,7 @@ bool ObjectFileELF::SetLoadAddress(Target &target, lldb::addr_t value,
// of the sections that have SHF_ALLOC in their flag bits. // of the sections that have SHF_ALLOC in their flag bits.
SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
if (section_sp && section_sp->Test(SHF_ALLOC)) { if (section_sp && section_sp->Test(SHF_ALLOC)) {
lldb::addr_t load_addr = section_sp->GetFileAddress(); lldb::addr_t load_addr = GetSectionPhysicalAddress(section_sp);
// We don't want to update the load address of a section with type // We don't want to update the load address of a section with type
// eSectionTypeAbsoluteAddress as they already have the absolute load // eSectionTypeAbsoluteAddress as they already have the absolute load
// address // address
@ -3470,3 +3470,40 @@ size_t ObjectFileELF::ReadSectionData(Section *section,
section_data.SetData(buffer_sp); section_data.SetData(buffer_sp);
return buffer_sp->GetByteSize(); return buffer_sp->GetByteSize();
} }
bool ObjectFileELF::AnySegmentHasPhysicalAddress() {
size_t header_count = ParseProgramHeaders();
for (size_t i = 1; i <= header_count; ++i) {
auto header = GetProgramHeaderByIndex(i);
if (header->p_paddr != 0)
return true;
}
return false;
}
const elf::ELFProgramHeader *
ObjectFileELF::GetSectionSegment(SectionSP section_sp) {
auto section_size = section_sp->GetFileSize();
if (section_size == 0)
section_size = 1;
size_t header_count = ParseProgramHeaders();
for (size_t i = 1; i <= header_count; ++i) {
auto header = GetProgramHeaderByIndex(i);
if (section_sp->GetFileOffset() >= header->p_offset &&
section_sp->GetFileOffset() + section_size <=
header->p_offset + header->p_filesz)
return header;
}
return nullptr;
}
addr_t ObjectFileELF::GetSectionPhysicalAddress(SectionSP section_sp) {
auto segment = GetSectionSegment(section_sp);
if (segment == nullptr)
return section_sp->GetFileAddress();
if (segment->p_type != PT_LOAD)
return LLDB_INVALID_ADDRESS;
auto base_address =
AnySegmentHasPhysicalAddress() ? segment->p_paddr : segment->p_vaddr;
return base_address + (section_sp->GetFileOffset() - segment->p_offset);
}

View File

@ -383,6 +383,12 @@ private:
RefineModuleDetailsFromNote(lldb_private::DataExtractor &data, RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
lldb_private::ArchSpec &arch_spec, lldb_private::ArchSpec &arch_spec,
lldb_private::UUID &uuid); lldb_private::UUID &uuid);
bool AnySegmentHasPhysicalAddress();
const elf::ELFProgramHeader *GetSectionSegment(lldb::SectionSP section_sp);
lldb::addr_t GetSectionPhysicalAddress(lldb::SectionSP section_sp);
}; };
#endif // liblldb_ObjectFileELF_h_ #endif // liblldb_ObjectFileELF_h_

View File

@ -21,6 +21,7 @@
#include "lldb/Core/ModuleSpec.h" #include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/State.h" #include "lldb/Core/State.h"
#include "lldb/Host/HostInfo.h" #include "lldb/Host/HostInfo.h"
#include "lldb/Host/XML.h"
#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/Args.h"
#include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/Symbol.h"
#include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/MemoryRegionInfo.h"
@ -81,6 +82,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
m_supports_qXfer_libraries_read(eLazyBoolCalculate), m_supports_qXfer_libraries_read(eLazyBoolCalculate),
m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate), m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate),
m_supports_qXfer_features_read(eLazyBoolCalculate), m_supports_qXfer_features_read(eLazyBoolCalculate),
m_supports_qXfer_memory_map_read(eLazyBoolCalculate),
m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate),
m_supports_jThreadExtendedInfo(eLazyBoolCalculate), m_supports_jThreadExtendedInfo(eLazyBoolCalculate),
m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate), m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate),
@ -103,7 +105,8 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX), m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX),
m_default_packet_timeout(0), m_max_packet_size(0), m_default_packet_timeout(0), m_max_packet_size(0),
m_qSupported_response(), m_supported_async_json_packets_is_valid(false), m_qSupported_response(), m_supported_async_json_packets_is_valid(false),
m_supported_async_json_packets_sp() {} m_supported_async_json_packets_sp(), m_qXfer_memory_map(),
m_qXfer_memory_map_loaded(false) {}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Destructor // Destructor
@ -192,6 +195,13 @@ bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() {
return m_supports_qXfer_features_read == eLazyBoolYes; return m_supports_qXfer_features_read == eLazyBoolYes;
} }
bool GDBRemoteCommunicationClient::GetQXferMemoryMapReadSupported() {
if (m_supports_qXfer_memory_map_read == eLazyBoolCalculate) {
GetRemoteQSupported();
}
return m_supports_qXfer_memory_map_read == eLazyBoolYes;
}
uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() {
if (m_max_packet_size == 0) { if (m_max_packet_size == 0) {
GetRemoteQSupported(); GetRemoteQSupported();
@ -296,6 +306,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
m_supports_qXfer_libraries_read = eLazyBoolCalculate; m_supports_qXfer_libraries_read = eLazyBoolCalculate;
m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
m_supports_qXfer_features_read = eLazyBoolCalculate; m_supports_qXfer_features_read = eLazyBoolCalculate;
m_supports_qXfer_memory_map_read = eLazyBoolCalculate;
m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate;
m_supports_qProcessInfoPID = true; m_supports_qProcessInfoPID = true;
m_supports_qfProcessInfo = true; m_supports_qfProcessInfo = true;
@ -342,6 +353,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; m_supports_qXfer_libraries_svr4_read = eLazyBoolNo;
m_supports_augmented_libraries_svr4_read = eLazyBoolNo; m_supports_augmented_libraries_svr4_read = eLazyBoolNo;
m_supports_qXfer_features_read = eLazyBoolNo; m_supports_qXfer_features_read = eLazyBoolNo;
m_supports_qXfer_memory_map_read = eLazyBoolNo;
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
// not, we assume no limit // not, we assume no limit
@ -377,6 +389,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
m_supports_qXfer_libraries_read = eLazyBoolYes; m_supports_qXfer_libraries_read = eLazyBoolYes;
if (::strstr(response_cstr, "qXfer:features:read+")) if (::strstr(response_cstr, "qXfer:features:read+"))
m_supports_qXfer_features_read = eLazyBoolYes; m_supports_qXfer_features_read = eLazyBoolYes;
if (::strstr(response_cstr, "qXfer:memory-map:read+"))
m_supports_qXfer_memory_map_read = eLazyBoolYes;
// Look for a list of compressions in the features list e.g. // Look for a list of compressions in the features list e.g.
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma
@ -1460,7 +1474,8 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
UNUSED_IF_ASSERT_DISABLED(packet_len); UNUSED_IF_ASSERT_DISABLED(packet_len);
StringExtractorGDBRemote response; StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse(packet, response, false) == if (SendPacketAndWaitForResponse(packet, response, false) ==
PacketResult::Success) { PacketResult::Success &&
response.GetResponseType() == StringExtractorGDBRemote::eResponse) {
llvm::StringRef name; llvm::StringRef name;
llvm::StringRef value; llvm::StringRef value;
addr_t addr_value = LLDB_INVALID_ADDRESS; addr_t addr_value = LLDB_INVALID_ADDRESS;
@ -1536,8 +1551,134 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
if (m_supports_memory_region_info == eLazyBoolNo) { if (m_supports_memory_region_info == eLazyBoolNo) {
error.SetErrorString("qMemoryRegionInfo is not supported"); error.SetErrorString("qMemoryRegionInfo is not supported");
} }
if (error.Fail())
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; return error;
} }

View File

@ -355,6 +355,8 @@ public:
bool GetQXferFeaturesReadSupported(); bool GetQXferFeaturesReadSupported();
bool GetQXferMemoryMapReadSupported();
LazyBool SupportsAllocDeallocMemory() // const LazyBool SupportsAllocDeallocMemory() // const
{ {
// Uncomment this to have lldb pretend the debug server doesn't respond to // Uncomment this to have lldb pretend the debug server doesn't respond to
@ -545,6 +547,7 @@ protected:
LazyBool m_supports_qXfer_libraries_read; LazyBool m_supports_qXfer_libraries_read;
LazyBool m_supports_qXfer_libraries_svr4_read; LazyBool m_supports_qXfer_libraries_svr4_read;
LazyBool m_supports_qXfer_features_read; LazyBool m_supports_qXfer_features_read;
LazyBool m_supports_qXfer_memory_map_read;
LazyBool m_supports_augmented_libraries_svr4_read; LazyBool m_supports_augmented_libraries_svr4_read;
LazyBool m_supports_jThreadExtendedInfo; LazyBool m_supports_jThreadExtendedInfo;
LazyBool m_supports_jLoadedDynamicLibrariesInfos; LazyBool m_supports_jLoadedDynamicLibrariesInfos;
@ -588,6 +591,9 @@ protected:
bool m_supported_async_json_packets_is_valid; bool m_supported_async_json_packets_is_valid;
lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp; lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp;
std::vector<MemoryRegionInfo> m_qXfer_memory_map;
bool m_qXfer_memory_map_loaded;
bool GetCurrentProcessInfo(bool allow_lazy_pid = true); bool GetCurrentProcessInfo(bool allow_lazy_pid = true);
bool GetGDBServerVersion(); bool GetGDBServerVersion();
@ -610,6 +616,11 @@ protected:
llvm::MutableArrayRef<uint8_t> &buffer, llvm::MutableArrayRef<uint8_t> &buffer,
size_t offset); size_t offset);
Status LoadQXferMemoryMap();
Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr,
MemoryRegionInfo &region);
private: private:
DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient); DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient);
}; };

View File

@ -59,6 +59,7 @@
#include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/ABI.h" #include "lldb/Target/ABI.h"
#include "lldb/Target/DynamicLoader.h" #include "lldb/Target/DynamicLoader.h"
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Target/SystemRuntime.h" #include "lldb/Target/SystemRuntime.h"
#include "lldb/Target/Target.h" #include "lldb/Target/Target.h"
#include "lldb/Target/TargetList.h" #include "lldb/Target/TargetList.h"
@ -256,7 +257,8 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp,
m_addr_to_mmap_size(), m_thread_create_bp_sp(), m_addr_to_mmap_size(), m_thread_create_bp_sp(),
m_waiting_for_attach(false), m_destroy_tried_resuming(false), m_waiting_for_attach(false), m_destroy_tried_resuming(false),
m_command_sp(), m_breakpoint_pc_offset(0), m_command_sp(), m_breakpoint_pc_offset(0),
m_initial_tid(LLDB_INVALID_THREAD_ID) { m_initial_tid(LLDB_INVALID_THREAD_ID), m_allow_flash_writes(false),
m_erased_flash_ranges() {
m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
"async thread should exit"); "async thread should exit");
m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
@ -2798,6 +2800,142 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
return 0; return 0;
} }
Status ProcessGDBRemote::WriteObjectFile(std::vector<WriteEntry> entries) {
Status error;
// Sort the entries by address because some writes, like those to flash
// memory, must happen in order of increasing address.
std::stable_sort(
std::begin(entries), std::end(entries),
[](const WriteEntry a, const WriteEntry b) { return a.Dest < b.Dest; });
m_allow_flash_writes = true;
error = Process::WriteObjectFile(entries);
if (error.Success())
error = FlashDone();
else
// Even though some of the writing failed, try to send a flash done if
// some of the writing succeeded so the flash state is reset to normal,
// but don't stomp on the error status that was set in the write failure
// since that's the one we want to report back.
FlashDone();
m_allow_flash_writes = false;
return error;
}
bool ProcessGDBRemote::HasErased(FlashRange range) {
auto size = m_erased_flash_ranges.GetSize();
for (size_t i = 0; i < size; ++i)
if (m_erased_flash_ranges.GetEntryAtIndex(i)->Contains(range))
return true;
return false;
}
Status ProcessGDBRemote::FlashErase(lldb::addr_t addr, size_t size) {
Status status;
MemoryRegionInfo region;
status = GetMemoryRegionInfo(addr, region);
if (!status.Success())
return status;
// The gdb spec doesn't say if erasures are allowed across multiple regions,
// but we'll disallow it to be safe and to keep the logic simple by worring
// about only one region's block size. DoMemoryWrite is this function's
// primary user, and it can easily keep writes within a single memory region
if (addr + size > region.GetRange().GetRangeEnd()) {
status.SetErrorString("Unable to erase flash in multiple regions");
return status;
}
uint64_t blocksize = region.GetBlocksize();
if (blocksize == 0) {
status.SetErrorString("Unable to erase flash because blocksize is 0");
return status;
}
// Erasures can only be done on block boundary adresses, so round down addr
// and round up size
lldb::addr_t block_start_addr = addr - (addr % blocksize);
size += (addr - block_start_addr);
if ((size % blocksize) != 0)
size += (blocksize - size % blocksize);
FlashRange range(block_start_addr, size);
if (HasErased(range))
return status;
// We haven't erased the entire range, but we may have erased part of it.
// (e.g., block A is already erased and range starts in A and ends in B).
// So, adjust range if necessary to exclude already erased blocks.
if (!m_erased_flash_ranges.IsEmpty()) {
// Assuming that writes and erasures are done in increasing addr order,
// because that is a requirement of the vFlashWrite command. Therefore,
// we only need to look at the last range in the list for overlap.
const auto &last_range = *m_erased_flash_ranges.Back();
if (range.GetRangeBase() < last_range.GetRangeEnd()) {
auto overlap = last_range.GetRangeEnd() - range.GetRangeBase();
// overlap will be less than range.GetByteSize() or else HasErased() would
// have been true
range.SetByteSize(range.GetByteSize() - overlap);
range.SetRangeBase(range.GetRangeBase() + overlap);
}
}
StreamString packet;
packet.Printf("vFlashErase:%" PRIx64 ",%" PRIx64, range.GetRangeBase(),
(uint64_t)range.GetByteSize());
StringExtractorGDBRemote response;
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
true) ==
GDBRemoteCommunication::PacketResult::Success) {
if (response.IsOKResponse()) {
m_erased_flash_ranges.Insert(range, true);
} else {
if (response.IsErrorResponse())
status.SetErrorStringWithFormat("flash erase failed for 0x%" PRIx64,
addr);
else if (response.IsUnsupportedResponse())
status.SetErrorStringWithFormat("GDB server does not support flashing");
else
status.SetErrorStringWithFormat(
"unexpected response to GDB server flash erase packet '%s': '%s'",
packet.GetData(), response.GetStringRef().c_str());
}
} else {
status.SetErrorStringWithFormat("failed to send packet: '%s'",
packet.GetData());
}
return status;
}
Status ProcessGDBRemote::FlashDone() {
Status status;
// If we haven't erased any blocks, then we must not have written anything
// either, so there is no need to actually send a vFlashDone command
if (m_erased_flash_ranges.IsEmpty())
return status;
StringExtractorGDBRemote response;
if (m_gdb_comm.SendPacketAndWaitForResponse("vFlashDone", response, true) ==
GDBRemoteCommunication::PacketResult::Success) {
if (response.IsOKResponse()) {
m_erased_flash_ranges.Clear();
} else {
if (response.IsErrorResponse())
status.SetErrorStringWithFormat("flash done failed");
else if (response.IsUnsupportedResponse())
status.SetErrorStringWithFormat("GDB server does not support flashing");
else
status.SetErrorStringWithFormat(
"unexpected response to GDB server flash done packet: '%s'",
response.GetStringRef().c_str());
}
} else {
status.SetErrorStringWithFormat("failed to send flash done packet");
}
return status;
}
size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf, size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf,
size_t size, Status &error) { size_t size, Status &error) {
GetMaxMemorySize(); GetMaxMemorySize();
@ -2810,10 +2948,33 @@ size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf,
size = max_memory_size; size = max_memory_size;
} }
StreamString packet; StreamGDBRemote packet;
packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size);
packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), MemoryRegionInfo region;
endian::InlHostByteOrder()); 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; StringExtractorGDBRemote response;
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
true) == true) ==

View File

@ -144,6 +144,8 @@ public:
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
Status &error) override; Status &error) override;
Status WriteObjectFile(std::vector<WriteEntry> entries) override;
size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
Status &error) override; Status &error) override;
@ -302,6 +304,11 @@ protected:
int64_t m_breakpoint_pc_offset; int64_t m_breakpoint_pc_offset;
lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
bool m_allow_flash_writes;
using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>;
using FlashRange = FlashRangeVector::Entry;
FlashRangeVector m_erased_flash_ranges;
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Accessors // Accessors
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -408,6 +415,12 @@ protected:
Status UpdateAutomaticSignalFiltering() override; Status UpdateAutomaticSignalFiltering() override;
Status FlashErase(lldb::addr_t addr, size_t size);
Status FlashDone();
bool HasErased(FlashRange range);
private: private:
//------------------------------------------------------------------ //------------------------------------------------------------------
// For ProcessGDBRemote only // For ProcessGDBRemote only

View File

@ -659,22 +659,31 @@ Status ObjectFile::LoadInMemory(Target &target, bool set_pc) {
SectionList *section_list = GetSectionList(); SectionList *section_list = GetSectionList();
if (!section_list) if (!section_list)
return Status("No section in object file"); return Status("No section in object file");
std::vector<Process::WriteEntry> writeEntries;
// Create a list of write entries from loadable sections
size_t section_count = section_list->GetNumSections(0); size_t section_count = section_list->GetNumSections(0);
for (size_t i = 0; i < section_count; ++i) { for (size_t i = 0; i < section_count; ++i) {
Process::WriteEntry entry;
SectionSP section_sp = section_list->GetSectionAtIndex(i); SectionSP section_sp = section_list->GetSectionAtIndex(i);
addr_t addr = target.GetSectionLoadList().GetSectionLoadAddress(section_sp); entry.Dest = target.GetSectionLoadList().GetSectionLoadAddress(section_sp);
if (addr != LLDB_INVALID_ADDRESS) { if (entry.Dest == LLDB_INVALID_ADDRESS)
DataExtractor section_data; continue;
// We can skip sections like bss // We can skip sections like bss
if (section_sp->GetFileSize() == 0) if (section_sp->GetFileSize() == 0)
continue; continue;
section_sp->GetSectionData(section_data); DataExtractor section_data;
lldb::offset_t written = process->WriteMemory( section_sp->GetSectionData(section_data);
addr, section_data.GetDataStart(), section_data.GetByteSize(), error); entry.Contents = llvm::ArrayRef<uint8_t>(section_data.GetDataStart(),
if (written != section_data.GetByteSize()) section_data.GetByteSize());
return error; writeEntries.push_back(entry);
}
} }
error = process->WriteObjectFile(std::move(writeEntries));
if (!error.Success())
return error;
if (set_pc) { if (set_pc) {
ThreadList &thread_list = process->GetThreadList(); ThreadList &thread_list = process->GetThreadList();
ThreadSP curr_thread(thread_list.GetSelectedThread()); ThreadSP curr_thread(thread_list.GetSelectedThread());

View File

@ -2533,6 +2533,17 @@ size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size,
return 0; return 0;
} }
Status Process::WriteObjectFile(std::vector<WriteEntry> entries) {
Status error;
for (const auto &Entry : entries) {
WriteMemory(Entry.Dest, Entry.Contents.data(), Entry.Contents.size(),
error);
if (!error.Success())
break;
}
return error;
}
#define USE_ALLOCATE_MEMORY_CACHE 1 #define USE_ALLOCATE_MEMORY_CACHE 1
addr_t Process::AllocateMemory(size_t size, uint32_t permissions, addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
Status &error) { Status &error) {