[lldb] Use vFlash commands when writing to target's flash memory regions
Summary: When writing an object file over gdb-remote, use the vFlashErase, vFlashWrite, and vFlashDone commands if the write address is in a flash memory region. A bare metal target may have this kind of setup. - Update ObjectFileELF to set load addresses using physical addresses. A typical case may be a data section with a physical address in ROM and a virtual address in RAM, which should be loaded to the ROM address. - Add support for querying the target's qXfer:memory-map, which contains information about flash memory regions, leveraging MemoryRegionInfo data structures with minor modifications - Update ProcessGDBRemote to use vFlash commands in DoWriteMemory when the target address is in a flash region Original discussion at http://lists.llvm.org/pipermail/lldb-dev/2018-January/013093.html Reviewers: clayborg, labath Reviewed By: labath Subscribers: arichardson, emaste, mgorny, lldb-commits Differential Revision: https://reviews.llvm.org/D42145 Patch by Owen Shaw <llvm@owenpshaw.net> llvm-svn: 326261
This commit is contained in:
parent
3acdc67734
commit
029fb69372
|
@ -82,6 +82,9 @@ public:
|
|||
llvm::StringRef GetAttributeValue(const char *name,
|
||||
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);
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
|
||||
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 @@ protected:
|
|||
OptionalBool m_execute;
|
||||
OptionalBool m_mapped;
|
||||
ConstString m_name;
|
||||
OptionalBool m_flash;
|
||||
lldb::offset_t m_blocksize;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -556,6 +556,11 @@ public:
|
|||
return GetStaticBroadcasterClass();
|
||||
}
|
||||
|
||||
struct WriteEntry {
|
||||
lldb::addr_t Dest;
|
||||
llvm::ArrayRef<uint8_t> Contents;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// A notification structure that can be used by clients to listen
|
||||
/// for changes in a process's lifetime.
|
||||
|
@ -1950,6 +1955,8 @@ public:
|
|||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
virtual Status WriteObjectFile(std::vector<WriteEntry> entries);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// The public interface to allocating memory in the process.
|
||||
///
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import lldb
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test.decorators import *
|
||||
from gdbclientutils import *
|
||||
|
||||
|
||||
class TestGDBRemoteLoad(GDBRemoteTestBase):
|
||||
|
||||
def test_ram_load(self):
|
||||
"""Test loading an object file to a target's ram"""
|
||||
target = self.createTarget("a.yaml")
|
||||
process = self.connect(target)
|
||||
self.dbg.HandleCommand("target modules load -l -s0")
|
||||
self.assertPacketLogContains([
|
||||
"M1000,4:c3c3c3c3",
|
||||
"M1004,2:3232"
|
||||
])
|
||||
|
||||
@skipIfXmlSupportMissing
|
||||
def test_flash_load(self):
|
||||
"""Test loading an object file to a target's flash memory"""
|
||||
|
||||
class Responder(MockGDBServerResponder):
|
||||
def qSupported(self, client_supported):
|
||||
return "PacketSize=3fff;QStartNoAckMode+;qXfer:memory-map:read+"
|
||||
|
||||
def qXferRead(self, obj, annex, offset, length):
|
||||
if obj == "memory-map":
|
||||
return (self.MEMORY_MAP[offset:offset + length],
|
||||
offset + length < len(self.MEMORY_MAP))
|
||||
return None, False
|
||||
|
||||
def other(self, packet):
|
||||
if packet[0:11] == "vFlashErase":
|
||||
return "OK"
|
||||
if packet[0:11] == "vFlashWrite":
|
||||
return "OK"
|
||||
if packet == "vFlashDone":
|
||||
return "OK"
|
||||
return ""
|
||||
|
||||
MEMORY_MAP = """<?xml version="1.0"?>
|
||||
<memory-map>
|
||||
<memory type="ram" start="0x0" length="0x1000"/>
|
||||
<memory type="flash" start="0x1000" length="0x1000">
|
||||
<property name="blocksize">0x100</property>
|
||||
</memory>
|
||||
<memory type="ram" start="0x2000" length="0x1D400"/>
|
||||
</memory-map>
|
||||
"""
|
||||
|
||||
self.server.responder = Responder()
|
||||
target = self.createTarget("a.yaml")
|
||||
process = self.connect(target)
|
||||
self.dbg.HandleCommand("target modules load -l -s0")
|
||||
self.assertPacketLogContains([
|
||||
"vFlashErase:1000,100",
|
||||
"vFlashWrite:1000:\xc3\xc3\xc3\xc3",
|
||||
"vFlashWrite:1004:\x32\x32",
|
||||
"vFlashDone"
|
||||
])
|
|
@ -151,6 +151,18 @@ llvm::StringRef XMLNode::GetAttributeValue(const char *name,
|
|||
return llvm::StringRef();
|
||||
}
|
||||
|
||||
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())
|
||||
|
|
|
@ -827,7 +827,7 @@ bool ObjectFileELF::SetLoadAddress(Target &target, lldb::addr_t value,
|
|||
// 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
|
||||
|
@ -3470,3 +3470,40 @@ size_t ObjectFileELF::ReadSectionData(Section *section,
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -383,6 +383,12 @@ private:
|
|||
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_
|
||||
|
|
|
@ -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 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
|
|||
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 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
|
|||
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 @@ bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() {
|
|||
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 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
|
|||
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 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
|
|||
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 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
|
|||
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 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
|
|||
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 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
|
|||
if (m_supports_memory_region_info == eLazyBoolNo) {
|
||||
error.SetErrorString("qMemoryRegionInfo is not supported");
|
||||
}
|
||||
if (error.Fail())
|
||||
region_info.Clear();
|
||||
|
||||
// Try qXfer:memory-map:read to get region information not included in
|
||||
// qMemoryRegionInfo
|
||||
MemoryRegionInfo qXfer_region_info;
|
||||
Status qXfer_error = GetQXferMemoryMapRegionInfo(addr, qXfer_region_info);
|
||||
|
||||
if (error.Fail()) {
|
||||
// If qMemoryRegionInfo failed, but qXfer:memory-map:read succeeded,
|
||||
// use the qXfer result as a fallback
|
||||
if (qXfer_error.Success()) {
|
||||
region_info = qXfer_region_info;
|
||||
error.Clear();
|
||||
} else {
|
||||
region_info.Clear();
|
||||
}
|
||||
} else if (qXfer_error.Success()) {
|
||||
// If both qMemoryRegionInfo and qXfer:memory-map:read succeeded, and if
|
||||
// both regions are the same range, update the result to include the
|
||||
// flash-memory information that is specific to the qXfer result.
|
||||
if (region_info.GetRange() == qXfer_region_info.GetRange()) {
|
||||
region_info.SetFlash(qXfer_region_info.GetFlash());
|
||||
region_info.SetBlocksize(qXfer_region_info.GetBlocksize());
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo(
|
||||
lldb::addr_t addr, MemoryRegionInfo ®ion) {
|
||||
Status error = LoadQXferMemoryMap();
|
||||
if (!error.Success())
|
||||
return error;
|
||||
for (const auto &map_region : m_qXfer_memory_map) {
|
||||
if (map_region.GetRange().Contains(addr)) {
|
||||
region = map_region;
|
||||
return error;
|
||||
}
|
||||
}
|
||||
error.SetErrorString("Region not found");
|
||||
return error;
|
||||
}
|
||||
|
||||
Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() {
|
||||
|
||||
Status error;
|
||||
|
||||
if (m_qXfer_memory_map_loaded)
|
||||
// Already loaded, return success
|
||||
return error;
|
||||
|
||||
if (!XMLDocument::XMLEnabled()) {
|
||||
error.SetErrorString("XML is not supported");
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!GetQXferMemoryMapReadSupported()) {
|
||||
error.SetErrorString("Memory map is not supported");
|
||||
return error;
|
||||
}
|
||||
|
||||
std::string xml;
|
||||
lldb_private::Status lldberr;
|
||||
if (!ReadExtFeature(ConstString("memory-map"), ConstString(""), xml,
|
||||
lldberr)) {
|
||||
error.SetErrorString("Failed to read memory map");
|
||||
return error;
|
||||
}
|
||||
|
||||
XMLDocument xml_document;
|
||||
|
||||
if (!xml_document.ParseMemory(xml.c_str(), xml.size())) {
|
||||
error.SetErrorString("Failed to parse memory map xml");
|
||||
return error;
|
||||
}
|
||||
|
||||
XMLNode map_node = xml_document.GetRootElement("memory-map");
|
||||
if (!map_node) {
|
||||
error.SetErrorString("Invalid root node in memory map xml");
|
||||
return error;
|
||||
}
|
||||
|
||||
m_qXfer_memory_map.clear();
|
||||
|
||||
map_node.ForEachChildElement([this](const XMLNode &memory_node) -> bool {
|
||||
if (!memory_node.IsElement())
|
||||
return true;
|
||||
if (memory_node.GetName() != "memory")
|
||||
return true;
|
||||
auto type = memory_node.GetAttributeValue("type", "");
|
||||
uint64_t start;
|
||||
uint64_t length;
|
||||
if (!memory_node.GetAttributeValueAsUnsigned("start", start))
|
||||
return true;
|
||||
if (!memory_node.GetAttributeValueAsUnsigned("length", length))
|
||||
return true;
|
||||
MemoryRegionInfo region;
|
||||
region.GetRange().SetRangeBase(start);
|
||||
region.GetRange().SetByteSize(length);
|
||||
if (type == "rom") {
|
||||
region.SetReadable(MemoryRegionInfo::eYes);
|
||||
this->m_qXfer_memory_map.push_back(region);
|
||||
} else if (type == "ram") {
|
||||
region.SetReadable(MemoryRegionInfo::eYes);
|
||||
region.SetWritable(MemoryRegionInfo::eYes);
|
||||
this->m_qXfer_memory_map.push_back(region);
|
||||
} else if (type == "flash") {
|
||||
region.SetFlash(MemoryRegionInfo::eYes);
|
||||
memory_node.ForEachChildElement(
|
||||
[®ion](const XMLNode &prop_node) -> bool {
|
||||
if (!prop_node.IsElement())
|
||||
return true;
|
||||
if (prop_node.GetName() != "property")
|
||||
return true;
|
||||
auto propname = prop_node.GetAttributeValue("name", "");
|
||||
if (propname == "blocksize") {
|
||||
uint64_t blocksize;
|
||||
if (prop_node.GetElementTextAsUnsigned(blocksize))
|
||||
region.SetBlocksize(blocksize);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
this->m_qXfer_memory_map.push_back(region);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
m_qXfer_memory_map_loaded = true;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
@ -355,6 +355,8 @@ public:
|
|||
|
||||
bool GetQXferFeaturesReadSupported();
|
||||
|
||||
bool GetQXferMemoryMapReadSupported();
|
||||
|
||||
LazyBool SupportsAllocDeallocMemory() // const
|
||||
{
|
||||
// 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_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 @@ protected:
|
|||
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 @@ protected:
|
|||
llvm::MutableArrayRef<uint8_t> &buffer,
|
||||
size_t offset);
|
||||
|
||||
Status LoadQXferMemoryMap();
|
||||
|
||||
Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr,
|
||||
MemoryRegionInfo ®ion);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient);
|
||||
};
|
||||
|
|
|
@ -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 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp,
|
|||
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,142 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
|
|||
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 size, Status &error) {
|
||||
GetMaxMemorySize();
|
||||
|
@ -2810,10 +2948,33 @@ size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf,
|
|||
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) ==
|
||||
|
|
|
@ -144,6 +144,8 @@ public:
|
|||
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
||||
Status &error) override;
|
||||
|
||||
Status WriteObjectFile(std::vector<WriteEntry> entries) override;
|
||||
|
||||
size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
|
||||
Status &error) override;
|
||||
|
||||
|
@ -302,6 +304,11 @@ protected:
|
|||
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 +415,12 @@ protected:
|
|||
|
||||
Status UpdateAutomaticSignalFiltering() override;
|
||||
|
||||
Status FlashErase(lldb::addr_t addr, size_t size);
|
||||
|
||||
Status FlashDone();
|
||||
|
||||
bool HasErased(FlashRange range);
|
||||
|
||||
private:
|
||||
//------------------------------------------------------------------
|
||||
// For ProcessGDBRemote only
|
||||
|
|
|
@ -659,22 +659,31 @@ Status ObjectFile::LoadInMemory(Target &target, bool set_pc) {
|
|||
SectionList *section_list = GetSectionList();
|
||||
if (!section_list)
|
||||
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);
|
||||
for (size_t i = 0; i < section_count; ++i) {
|
||||
Process::WriteEntry entry;
|
||||
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;
|
||||
}
|
||||
entry.Dest = target.GetSectionLoadList().GetSectionLoadAddress(section_sp);
|
||||
if (entry.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);
|
||||
entry.Contents = llvm::ArrayRef<uint8_t>(section_data.GetDataStart(),
|
||||
section_data.GetByteSize());
|
||||
writeEntries.push_back(entry);
|
||||
}
|
||||
|
||||
error = process->WriteObjectFile(std::move(writeEntries));
|
||||
if (!error.Success())
|
||||
return error;
|
||||
|
||||
if (set_pc) {
|
||||
ThreadList &thread_list = process->GetThreadList();
|
||||
ThreadSP curr_thread(thread_list.GetSelectedThread());
|
||||
|
|
|
@ -2533,6 +2533,17 @@ size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size,
|
|||
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
|
||||
addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
|
||||
Status &error) {
|
||||
|
|
Loading…
Reference in New Issue