Author: Pavel Labath Date: 2020-02-26T10:18:58+01:00 New Revision: d4eca120ac0af2a805c19301412bf843a71c14b5
URL: https://github.com/llvm/llvm-project/commit/d4eca120ac0af2a805c19301412bf843a71c14b5 DIFF: https://github.com/llvm/llvm-project/commit/d4eca120ac0af2a805c19301412bf843a71c14b5.diff LOG: [lldb/gdb-remote] Add support for the qOffsets packet Summary: This packet is necessary to make lldb work with the remote-gdb stub in user mode qemu when running position-independent binaries. It reports the relative position (load bias) of the loaded executable wrt. the addresses in the file itself. Lldb needs to know this information in order to correctly set the load address of the executable. Normally, lldb would be able to find this out on its own by following the breadcrumbs in the process auxiliary vector, but we can't do this here because qemu does not support the qXfer:auxv:read packet. This patch does not implement full scope of the qOffsets packet (it only supports packets with identical code, data and bss offsets), because it is not fully clear how should the different offsets be handled and I am not aware of a producer which would make use of this feature (qemu will always <https://github.com/qemu/qemu/blob/master/linux-user/elfload.c#L2436> return the same value for code and data offsets). In fact, even gdb ignores the offset for the bss sections, and uses the "data" offset instead. So, until the we need more of this packet, I think it's best to stick to the simplest solution possible. This patch simply rejects replies with non-uniform offsets. Reviewers: clayborg, jasonmolenda Subscribers: lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D74598 Added: lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py lldb/test/API/functionalities/gdb_remote_client/qOffsets.yaml Modified: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 018b753bebc6..67e5d59d199e 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -45,6 +45,13 @@ using namespace lldb_private::process_gdb_remote; using namespace lldb_private; using namespace std::chrono; +llvm::raw_ostream &process_gdb_remote::operator<<(llvm::raw_ostream &os, + const QOffsets &offsets) { + return os << llvm::formatv( + "QOffsets({0}, [{1:@[x]}])", offsets.segments, + llvm::make_range(offsets.offsets.begin(), offsets.offsets.end())); +} + // GDBRemoteCommunicationClient constructor GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() : GDBRemoteClientBase("gdb-remote.client", "gdb-remote.client.rx_packet"), @@ -3531,6 +3538,46 @@ Status GDBRemoteCommunicationClient::SendGetTraceDataPacket( return error; } +llvm::Optional<QOffsets> GDBRemoteCommunicationClient::GetQOffsets() { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse( + "qOffsets", response, /*send_async=*/false) != PacketResult::Success) + return llvm::None; + if (!response.IsNormalResponse()) + return llvm::None; + + QOffsets result; + llvm::StringRef ref = response.GetStringRef(); + const auto &GetOffset = [&] { + addr_t offset; + if (ref.consumeInteger(16, offset)) + return false; + result.offsets.push_back(offset); + return true; + }; + + if (ref.consume_front("Text=")) { + result.segments = false; + if (!GetOffset()) + return llvm::None; + if (!ref.consume_front(";Data=") || !GetOffset()) + return llvm::None; + if (ref.empty()) + return result; + if (ref.consume_front(";Bss=") && GetOffset() && ref.empty()) + return result; + } else if (ref.consume_front("TextSeg=")) { + result.segments = true; + if (!GetOffset()) + return llvm::None; + if (ref.empty()) + return result; + if (ref.consume_front(";DataSeg=") && GetOffset() && ref.empty()) + return result; + } + return llvm::None; +} + 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/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index c2549749e814..ff3836d467a4 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -31,6 +31,22 @@ namespace lldb_private { namespace process_gdb_remote { +/// The offsets used by the target when relocating the executable. Decoded from +/// qOffsets packet response. +struct QOffsets { + /// If true, the offsets field describes segments. Otherwise, it describes + /// sections. + bool segments; + + /// The individual offsets. Section offsets have two or three members. + /// Segment offsets have either one of two. + std::vector<uint64_t> offsets; +}; +inline bool operator==(const QOffsets &a, const QOffsets &b) { + return a.segments == b.segments && a.offsets == b.offsets; +} +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const QOffsets &offsets); + class GDBRemoteCommunicationClient : public GDBRemoteClientBase { public: GDBRemoteCommunicationClient(); @@ -425,6 +441,11 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { bool GetSharedCacheInfoSupported(); + /// Use qOffsets to query the offset used when relocating the target + /// executable. If successful, the returned structure will contain at least + /// one value in the offsets field. + llvm::Optional<QOffsets> GetQOffsets(); + bool GetModuleInfo(const FileSpec &module_file_spec, const 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 index c7fc0161d53a..156f6f7f4fc9 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1102,6 +1102,8 @@ void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { } } + MaybeLoadExecutableModule(); + // Find out which StructuredDataPlugins are supported by the debug monitor. // These plugins transmit data over async $J packets. if (StructuredData::Array *supported_packets = @@ -1109,6 +1111,31 @@ void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { MapSupportedStructuredDataPlugins(*supported_packets); } +void ProcessGDBRemote::MaybeLoadExecutableModule() { + ModuleSP module_sp = GetTarget().GetExecutableModule(); + if (!module_sp) + return; + + llvm::Optional<QOffsets> offsets = m_gdb_comm.GetQOffsets(); + if (!offsets) + return; + + bool is_uniform = + size_t(llvm::count(offsets->offsets, offsets->offsets[0])) == + offsets->offsets.size(); + if (!is_uniform) + return; // TODO: Handle non-uniform responses. + + bool changed = false; + module_sp->SetLoadAddress(GetTarget(), offsets->offsets[0], + /*value_is_offset=*/true, changed); + if (changed) { + ModuleList list; + list.Append(module_sp); + m_process->GetTarget().ModulesDidLoad(list); + } +} + void ProcessGDBRemote::DidLaunch() { ArchSpec process_arch; DidLaunchOrAttach(process_arch); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index c465acf8357b..9063fcb00622 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -377,6 +377,7 @@ class ProcessGDBRemote : public Process, bool UpdateThreadIDList(); void DidLaunchOrAttach(ArchSpec &process_arch); + void MaybeLoadExecutableModule(); Status ConnectToDebugserver(llvm::StringRef host_port); 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 index 000000000000..44028e561ec3 --- /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 index 486485c8e28d..5b0247994ed5 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py +++ b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py @@ -172,6 +172,8 @@ def respond(self, packet): 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 qfProcessInfo(self, packet): 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 index 000000000000..d498984cb361 --- /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 index f81a7f2d6333..6fba1cbb53b0 100644 --- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp @@ -552,3 +552,29 @@ TEST_F(GDBRemoteCommunicationClientTest, SendGetTraceConfigPacket) { incorrect_custom_params2); ASSERT_FALSE(result4.get().Success()); } + +TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) { + const auto &GetQOffsets = [&](llvm::StringRef response) { + std::future<Optional<QOffsets>> result = std::async( + std::launch::async, [&] { return client.GetQOffsets(); }); + + HandlePacket(server, "qOffsets", response); + return result.get(); + }; + EXPECT_EQ((QOffsets{false, {0x1234, 0x1234}}), + GetQOffsets("Text=1234;Data=1234")); + EXPECT_EQ((QOffsets{false, {0x1234, 0x1234, 0x1234}}), + GetQOffsets("Text=1234;Data=1234;Bss=1234")); + EXPECT_EQ((QOffsets{true, {0x1234}}), GetQOffsets("TextSeg=1234")); + EXPECT_EQ((QOffsets{true, {0x1234, 0x2345}}), + GetQOffsets("TextSeg=1234;DataSeg=2345")); + + EXPECT_EQ(llvm::None, GetQOffsets("E05")); + EXPECT_EQ(llvm::None, GetQOffsets("Text=bogus")); + EXPECT_EQ(llvm::None, GetQOffsets("Text=1234")); + EXPECT_EQ(llvm::None, GetQOffsets("Text=1234;Data=1234;")); + EXPECT_EQ(llvm::None, GetQOffsets("Text=1234;Data=1234;Bss=1234;")); + EXPECT_EQ(llvm::None, GetQOffsets("TEXTSEG=1234")); + EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=0x1234")); + EXPECT_EQ(llvm::None, GetQOffsets("TextSeg=12345678123456789")); +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits