[Lldb-commits] [lldb] d4eca12 - [lldb/gdb-remote] Add support for the qOffsets packet

Pavel Labath via lldb-commits lldb-commits at lists.llvm.org
Wed Feb 26 01:19:11 PST 2020


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"));
+}


        


More information about the lldb-commits mailing list