diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -425,6 +425,9 @@ bool GetSharedCacheInfoSupported(); + /// qOffsets + llvm::Optional GetExecutableOffset(); + bool GetModuleInfo(const FileSpec &module_file_spec, const ArchSpec &arch_spec, ModuleSpec &module_spec); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -3531,6 +3531,31 @@ return error; } +llvm::Optional GDBRemoteCommunicationClient::GetExecutableOffset() { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse( + "qOffsets", response, /*send_async=*/false) != PacketResult::Success) + return llvm::None; + if (!response.IsNormalResponse()) + return llvm::None; + + llvm::Optional offset; + llvm::SmallVector pairs; + response.GetStringRef().split(pairs, ';'); + for (llvm::StringRef pair : pairs) { + llvm::StringRef name, value; + std::tie(name, value) = pair.split('='); + + addr_t cur_offset; + if (!to_integer(value, cur_offset, 16)) + return llvm::None; + if (offset && *offset != cur_offset) + return llvm::None; + offset = cur_offset; + } + return offset; +} + bool GDBRemoteCommunicationClient::GetModuleInfo( const FileSpec &module_file_spec, const lldb_private::ArchSpec &arch_spec, ModuleSpec &module_spec) { diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1102,6 +1102,19 @@ } } + if (ModuleSP module_sp = GetTarget().GetExecutableModule()) { + if (llvm::Optional offset = m_gdb_comm.GetExecutableOffset()) { + bool changed = false; + module_sp->SetLoadAddress(GetTarget(), *offset, /*value_is_offset=*/true, + changed); + if (changed) { + ModuleList list; + list.Append(module_sp); + m_process->GetTarget().ModulesDidLoad(list); + } + } + } + // Find out which StructuredDataPlugins are supported by the debug monitor. // These plugins transmit data over async $J packets. if (StructuredData::Array *supported_packets = diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py b/lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py @@ -0,0 +1,28 @@ +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from gdbclientutils import * + + +class TestqOffsets(GDBRemoteTestBase): + + class Responder(MockGDBServerResponder): + def qOffsets(self): + return 'Text=470000;Data=470000' + + def setUp(self): + super(TestqOffsets, self).setUp() + self._initial_platform = lldb.DBG.GetSelectedPlatform() + + def tearDown(self): + lldb.DBG.SetSelectedPlatform(self._initial_platform) + super(TestqOffsets, self).tearDown() + + def test(self): + self.server.responder = TestqOffsets.Responder() + target = self.createTarget("qOffsets.yaml") + text = target.modules[0].FindSection(".text") + self.assertEquals(text.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS) + + process = self.connect(target) + self.assertEquals(text.GetLoadAddress(target), 0x471000) diff --git a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py --- a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py +++ b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py @@ -172,6 +172,8 @@ return self.qHostInfo() if packet == "qGetWorkingDir": return self.qGetWorkingDir() + if packet == "qOffsets": + return self.qOffsets(); if packet == "qsProcessInfo": return self.qsProcessInfo() if packet.startswith("qfProcessInfo"): @@ -188,6 +190,9 @@ def qGetWorkingDir(self): return "2f" + def qOffsets(self): + return "" + def qHostInfo(self): return "ptrsize:8;endian:little;" diff --git a/lldb/test/API/functionalities/gdb_remote_client/qOffsets.yaml b/lldb/test/API/functionalities/gdb_remote_client/qOffsets.yaml new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/qOffsets.yaml @@ -0,0 +1,19 @@ +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AARCH64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x4 + Content: "c3c3c3c3" + - Name: .note.ABI-tag + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x1004 + AddressAlign: 0x4 + Content: 040000001000000001000000474e550000000000030000000700000000000000 diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp --- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -552,3 +552,24 @@ incorrect_custom_params2); ASSERT_FALSE(result4.get().Success()); } + +TEST_F(GDBRemoteCommunicationClientTest, GetOffsets) { + const auto &GetExecutableOffset = [&](llvm::StringRef response) { + std::future> result = std::async( + std::launch::async, [&] { return client.GetExecutableOffset(); }); + + HandlePacket(server, "qOffsets", response); + return result.get(); + }; + EXPECT_EQ(addr_t(0x1234), GetExecutableOffset("Text=1234;Data=1234")); + EXPECT_EQ(addr_t(0x1234), + GetExecutableOffset("Text=1234;Data=1234;Bss=1234")); + EXPECT_EQ(addr_t(0x1234), GetExecutableOffset("TextSeg=1234;DataSeg=1234")); + + EXPECT_EQ(llvm::None, GetExecutableOffset("E05")); + EXPECT_EQ(llvm::None, GetExecutableOffset("Text=bogus")); + + // NB: These are technically correct responses, but we don't handle them yet. + EXPECT_EQ(llvm::None, GetExecutableOffset("Text=1234;Data=4321")); + EXPECT_EQ(llvm::None, GetExecutableOffset("TextSeg=1234;DataSeg=4321")); +}