jasonmolenda created this revision.
jasonmolenda added a reviewer: JDevlieghere.
jasonmolenda added a project: LLDB.
Herald added a project: All.
jasonmolenda requested review of this revision.
Herald added a subscriber: lldb-commits.

lldb sends the jGetLoadedDynamicLibrariesInfos packet to debugserver with a 
list of addresses of mach-o binaries.  Today, debugserver collects a list of 
all binaries that are loaded in the process, along with their filenames, and 
then debugserver parsers the mach-o binaries in memory and returns the 
important details in a JSON response so lldb doesn't need to read this data 
from the inferior itself.  If lldb asks about a binary that isn't officially 
loaded in the process yet -- but is present in memory -- then this packet will 
return nothing about it.

This patch handles the case where dyld does not know about the binary.  We 
cannot get a filepath for the binary, so "" is returned there, but debugserver 
can examine memory and if there's something that looks like a mach-o header + 
load commands, return that information to lldb.

In macOS Ventura, as part of the early startup, we launch with one dynamic 
linker (dyld) and that dyld hands off control to a dyld in the shared cache.  
During that transition, the new dyld is not officially loaded in the process 
yet.  I'd like to switch to having lldb parse the not-yet-running dyld and put 
a breakpoint in it.  This is needed first, to avoid a perf hit when lldb 
inspects the load commands of new-dyld.

I added a test case that constructs a tiny little mach-o binary in a memory 
buffer and have lldb send that buffer address to 
jGetLoadedDynamicLibrariesInfos.  Current debugserver will return an empty list 
of binaries found; with this patch, debugserver will have an empty filepath but 
return everything from the load commands.

There's no real good reviewer in this case, this is all jason code, but if 
anyone has any questions or comments they'd like to make, I'd be interested in 
hearing them.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128956

Files:
  lldb/test/API/macosx/unregistered-macho/Makefile
  lldb/test/API/macosx/unregistered-macho/TestUnregisteredMacho.py
  lldb/test/API/macosx/unregistered-macho/main.c
  lldb/tools/debugserver/source/MacOSX/MachProcess.mm

Index: lldb/tools/debugserver/source/MacOSX/MachProcess.mm
===================================================================
--- lldb/tools/debugserver/source/MacOSX/MachProcess.mm
+++ lldb/tools/debugserver/source/MacOSX/MachProcess.mm
@@ -1164,6 +1164,8 @@
 
   int pointer_size = GetInferiorAddrSize(pid);
 
+  // Collect the list of all binaries that dyld knows about in
+  // the inferior process.
   std::vector<struct binary_image_information> all_image_infos;
   GetAllLoadedBinariesViaDYLDSPI(all_image_infos);
   uint32_t platform = GetPlatform();
@@ -1171,12 +1173,26 @@
   std::vector<struct binary_image_information> image_infos;
   const size_t macho_addresses_count = macho_addresses.size();
   const size_t all_image_infos_count = all_image_infos.size();
+
   for (size_t i = 0; i < macho_addresses_count; i++) {
+    bool found_matching_entry = false;
     for (size_t j = 0; j < all_image_infos_count; j++) {
       if (all_image_infos[j].load_address == macho_addresses[i]) {
         image_infos.push_back(all_image_infos[j]);
+        found_matching_entry = true;
       }
     }
+    if (!found_matching_entry) {
+      // dyld doesn't think there is a binary at this address,
+      // but maybe there isn't a binary YET - let's look in memory
+      // for a proper mach-o header etc and return what we can.
+      // We will have an empty filename for the binary (because dyld
+      // doesn't know about it yet) but we can read all of the mach-o
+      // load commands from memory directly.
+      struct binary_image_information entry;
+      entry.load_address = macho_addresses[i];
+      image_infos.push_back(entry);
+    }
   }
 
     const size_t image_infos_count = image_infos.size();
Index: lldb/test/API/macosx/unregistered-macho/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/macosx/unregistered-macho/main.c
@@ -0,0 +1,64 @@
+#include <mach/machine.h>
+#include <mach-o/loader.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+int main ()
+{
+  int size_of_mh_and_cmds = sizeof (struct mach_header_64) + 
+                          sizeof (struct segment_command_64) +
+                          sizeof (struct uuid_command);
+  uint8_t *macho_buf = (uint8_t *) malloc 
+                         (size_of_mh_and_cmds);
+  uint8_t *p = macho_buf;
+  struct mach_header_64 mh;
+  mh.magic = MH_MAGIC_64;
+  mh.cputype = CPU_TYPE_ARM64;
+  mh.cpusubtype = 0;
+  mh.filetype = MH_EXECUTE;
+  mh.ncmds = 2;
+  mh.sizeofcmds = size_of_mh_and_cmds;
+  mh.flags = MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL | MH_PIE;
+
+  memcpy (p, &mh, sizeof (mh));
+  p += sizeof (mh);
+
+  struct segment_command_64 seg;
+  seg.cmd = LC_SEGMENT_64;
+  seg.cmdsize = sizeof (seg);
+  strcpy (seg.segname, "__TEXT");
+  seg.vmaddr = 0x5000;
+  seg.vmsize = 0x1000;
+  seg.fileoff = 0;
+  seg.filesize = 0;
+  seg.maxprot = 0;
+  seg.initprot = 0;
+  seg.nsects = 0;
+  seg.flags = 0;
+
+  memcpy (p, &seg, sizeof (seg));
+  p += sizeof (seg);
+
+  struct uuid_command uuid;
+  uuid.cmd = LC_UUID;
+  uuid.cmdsize = sizeof (uuid);
+  uuid_clear (uuid.uuid);
+  uuid_parse ("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb", uuid.uuid);
+
+  memcpy (p, &uuid, sizeof (uuid));
+  p += sizeof (uuid);
+
+  // If this needs to be debugged, the memory buffer can be written
+  // to a file with
+  // (lldb) mem rea -b -o /tmp/t -c `p - macho_buf` macho_buf
+  // (lldb) platform shell otool -hlv /tmp/t
+  // to verify that it is well formed.
+
+  // And inside lldb, it should be inspectable via
+  // (lldb) script print(lldb.frame.locals["macho_buf"][0].GetValueAsUnsigned())
+  // 105553162403968
+  // (lldb) process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[105553162403968]}]'
+
+  return 0;  // break here
+}
Index: lldb/test/API/macosx/unregistered-macho/TestUnregisteredMacho.py
===================================================================
--- /dev/null
+++ lldb/test/API/macosx/unregistered-macho/TestUnregisteredMacho.py
@@ -0,0 +1,34 @@
+"""Test that debugserver will parse a mach-o in inferior memory even if it's not loaded."""
+
+import os
+import re
+import subprocess
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestUnregisteredMacho(TestBase):
+
+    # newer debugserver required for jGetLoadedDynamicLibrariesInfos 
+    # to support this
+    @skipIfOutOfTreeDebugserver  
+    @no_debug_info_test
+    @skipUnlessDarwin
+    def test(self):
+        self.build()
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.c"))
+
+        frame = thread.GetFrameAtIndex(0)
+        macho_buf = frame.GetValueForVariablePath("macho_buf")
+        gdb_packet = "process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{\"solib_addresses\":[%d]}]'" % macho_buf.GetValueAsUnsigned() 
+
+        # Send the jGetLoadedDynamicLibrariesInfos packet
+        # to debugserver, asking it to parse the mach-o binary
+        # at this address and give us the UUID etc, even though
+        # dyld doesn't think there is a binary at that address.
+        # We won't get a pathname for the binary (from dyld), but
+        # we will get to the LC_UUID and include that.
+        self.expect (gdb_packet, substrs=['"pathname":""', '"uuid":"1B4E28BA-2FA1-11D2-883F-B9A761BDE3FB"'])
Index: lldb/test/API/macosx/unregistered-macho/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/macosx/unregistered-macho/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES = main.c
+
+include Makefile.rules
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to