This revision was automatically updated to reflect the committed changes.
Closed by commit rG77a38f683998: jGetLoadedDynamicLibrariesInfos can inspect 
machos not yet loaded (authored by jasonmolenda).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128956/new/

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.h
  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
@@ -746,6 +746,16 @@
   return nullptr;
 }
 
+static bool mach_header_validity_test (uint32_t magic, uint32_t cputype) {
+  if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 &&
+      magic != MH_CIGAM_64)
+    return false;
+  if (cputype != CPU_TYPE_X86_64 && cputype != CPU_TYPE_ARM &&
+      cputype != CPU_TYPE_ARM64 && cputype != CPU_TYPE_ARM64_32)
+    return false;
+  return true;
+}
+
 // Given an address, read the mach-o header and load commands out of memory to
 // fill in
 // the mach_o_information "inf" object.
@@ -757,12 +767,16 @@
     uint32_t dyld_platform, nub_addr_t mach_o_header_addr, int wordsize,
     struct mach_o_information &inf) {
   uint64_t load_cmds_p;
+
   if (wordsize == 4) {
     struct mach_header header;
     if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header), &header) !=
         sizeof(struct mach_header)) {
       return false;
     }
+    if (!mach_header_validity_test(header.magic, header.cputype))
+      return false;
+
     load_cmds_p = mach_o_header_addr + sizeof(struct mach_header);
     inf.mach_header.magic = header.magic;
     inf.mach_header.cputype = header.cputype;
@@ -779,6 +793,8 @@
                    &header) != sizeof(struct mach_header_64)) {
       return false;
     }
+    if (!mach_header_validity_test(header.magic, header.cputype))
+      return false;
     load_cmds_p = mach_o_header_addr + sizeof(struct mach_header_64);
     inf.mach_header.magic = header.magic;
     inf.mach_header.cputype = header.cputype;
@@ -896,6 +912,8 @@
   const size_t image_count = image_infos.size();
 
   for (size_t i = 0; i < image_count; i++) {
+    if (!image_infos[i].is_valid_mach_header)
+      continue;
     JSONGenerator::DictionarySP image_info_dict_sp(
         new JSONGenerator::Dictionary());
     image_info_dict_sp->AddIntegerItem("load_address",
@@ -970,7 +988,6 @@
   }
 
   JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary());
-  ;
   reply_sp->AddItem("images", image_infos_array_sp);
 
   return reply_sp;
@@ -985,8 +1002,8 @@
 // information.
 JSONGenerator::ObjectSP MachProcess::GetLoadedDynamicLibrariesInfos(
     nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
-  JSONGenerator::DictionarySP reply_sp;
 
+  JSONGenerator::ObjectSP empty_reply_sp(new JSONGenerator::Dictionary());
   int pointer_size = GetInferiorAddrSize(pid);
 
   std::vector<struct binary_image_information> image_infos;
@@ -994,91 +1011,89 @@
 
   uint8_t *image_info_buf = (uint8_t *)malloc(image_infos_size);
   if (image_info_buf == NULL) {
-    return reply_sp;
+    return empty_reply_sp;
+  }
+  if (ReadMemory(image_list_address, image_infos_size, image_info_buf) !=
+      image_infos_size) {
+    return empty_reply_sp;
   }
-    if (ReadMemory(image_list_address, image_infos_size, image_info_buf) !=
-        image_infos_size) {
-      return reply_sp;
-    }
 
-    ////  First the image_infos array with (load addr, pathname, mod date)
-    ///tuples
-
-    for (size_t i = 0; i < image_count; i++) {
-      struct binary_image_information info;
-      nub_addr_t pathname_address;
-      if (pointer_size == 4) {
-        uint32_t load_address_32;
-        uint32_t pathname_address_32;
-        uint32_t mod_date_32;
-        ::memcpy(&load_address_32, image_info_buf + (i * 3 * pointer_size), 4);
-        ::memcpy(&pathname_address_32,
-                 image_info_buf + (i * 3 * pointer_size) + pointer_size, 4);
-        ::memcpy(&mod_date_32, image_info_buf + (i * 3 * pointer_size) +
-                                   pointer_size + pointer_size,
-                 4);
-        info.load_address = load_address_32;
-        info.mod_date = mod_date_32;
-        pathname_address = pathname_address_32;
-      } else {
-        uint64_t load_address_64;
-        uint64_t pathname_address_64;
-        uint64_t mod_date_64;
-        ::memcpy(&load_address_64, image_info_buf + (i * 3 * pointer_size), 8);
-        ::memcpy(&pathname_address_64,
-                 image_info_buf + (i * 3 * pointer_size) + pointer_size, 8);
-        ::memcpy(&mod_date_64, image_info_buf + (i * 3 * pointer_size) +
-                                   pointer_size + pointer_size,
-                 8);
-        info.load_address = load_address_64;
-        info.mod_date = mod_date_64;
-        pathname_address = pathname_address_64;
-      }
-      char strbuf[17];
-      info.filename = "";
-      uint64_t pathname_ptr = pathname_address;
-      bool still_reading = true;
-      while (still_reading &&
-             ReadMemory(pathname_ptr, sizeof(strbuf) - 1, strbuf) ==
-                 sizeof(strbuf) - 1) {
-        strbuf[sizeof(strbuf) - 1] = '\0';
-        info.filename += strbuf;
-        pathname_ptr += sizeof(strbuf) - 1;
-        // Stop if we found nul byte indicating the end of the string
-        for (size_t i = 0; i < sizeof(strbuf) - 1; i++) {
-          if (strbuf[i] == '\0') {
-            still_reading = false;
-            break;
-          }
+  /// First the image_infos array with (load addr, pathname, mod date)
+  /// tuples
+
+  for (size_t i = 0; i < image_count; i++) {
+    struct binary_image_information info;
+    nub_addr_t pathname_address;
+    if (pointer_size == 4) {
+      uint32_t load_address_32;
+      uint32_t pathname_address_32;
+      uint32_t mod_date_32;
+      ::memcpy(&load_address_32, image_info_buf + (i * 3 * pointer_size), 4);
+      ::memcpy(&pathname_address_32,
+               image_info_buf + (i * 3 * pointer_size) + pointer_size, 4);
+      ::memcpy(&mod_date_32,
+               image_info_buf + (i * 3 * pointer_size) + pointer_size +
+                   pointer_size,
+               4);
+      info.load_address = load_address_32;
+      info.mod_date = mod_date_32;
+      pathname_address = pathname_address_32;
+    } else {
+      uint64_t load_address_64;
+      uint64_t pathname_address_64;
+      uint64_t mod_date_64;
+      ::memcpy(&load_address_64, image_info_buf + (i * 3 * pointer_size), 8);
+      ::memcpy(&pathname_address_64,
+               image_info_buf + (i * 3 * pointer_size) + pointer_size, 8);
+      ::memcpy(&mod_date_64,
+               image_info_buf + (i * 3 * pointer_size) + pointer_size +
+                   pointer_size,
+               8);
+      info.load_address = load_address_64;
+      info.mod_date = mod_date_64;
+      pathname_address = pathname_address_64;
+    }
+    char strbuf[17];
+    info.filename = "";
+    uint64_t pathname_ptr = pathname_address;
+    bool still_reading = true;
+    while (still_reading && ReadMemory(pathname_ptr, sizeof(strbuf) - 1,
+                                       strbuf) == sizeof(strbuf) - 1) {
+      strbuf[sizeof(strbuf) - 1] = '\0';
+      info.filename += strbuf;
+      pathname_ptr += sizeof(strbuf) - 1;
+      // Stop if we found nul byte indicating the end of the string
+      for (size_t i = 0; i < sizeof(strbuf) - 1; i++) {
+        if (strbuf[i] == '\0') {
+          still_reading = false;
+          break;
         }
       }
-      uuid_clear(info.macho_info.uuid);
-      image_infos.push_back(info);
-    }
-    if (image_infos.size() == 0) {
-      return reply_sp;
     }
+    uuid_clear(info.macho_info.uuid);
+    image_infos.push_back(info);
+  }
+  if (image_infos.size() == 0) {
+    return empty_reply_sp;
+  }
 
-    free(image_info_buf);
+  free(image_info_buf);
 
-    ////  Second, read the mach header / load commands for all the dylibs
+  ///  Second, read the mach header / load commands for all the dylibs
 
-    for (size_t i = 0; i < image_count; i++) {
-      // The SPI to provide platform is not available on older systems.
-      uint32_t platform = 0;
-      if (!GetMachOInformationFromMemory(platform,
-                                         image_infos[i].load_address,
-                                         pointer_size,
-                                         image_infos[i].macho_info)) {
-        return reply_sp;
-      }
+  for (size_t i = 0; i < image_count; i++) {
+    // The SPI to provide platform is not available on older systems.
+    uint32_t platform = 0;
+    if (GetMachOInformationFromMemory(platform, image_infos[i].load_address,
+                                      pointer_size,
+                                      image_infos[i].macho_info)) {
+      image_infos[i].is_valid_mach_header = true;
     }
+  }
 
-    ////  Third, format all of the above in the JSONGenerator object.
-
-    return FormatDynamicLibrariesIntoJSON(image_infos);
+  ///  Third, format all of the above in the JSONGenerator object.
 
-  return reply_sp;
+  return FormatDynamicLibrariesIntoJSON(image_infos);
 }
 
 /// From dyld SPI header dyld_process_info.h
@@ -1141,7 +1156,6 @@
 // macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer.
 JSONGenerator::ObjectSP
 MachProcess::GetAllLoadedLibrariesInfos(nub_process_t pid) {
-  JSONGenerator::DictionarySP reply_sp;
 
   int pointer_size = GetInferiorAddrSize(pid);
   std::vector<struct binary_image_information> image_infos;
@@ -1149,8 +1163,11 @@
   uint32_t platform = GetPlatform();
   const size_t image_count = image_infos.size();
   for (size_t i = 0; i < image_count; i++) {
-    GetMachOInformationFromMemory(platform, image_infos[i].load_address,
-                                  pointer_size, image_infos[i].macho_info);
+    if (GetMachOInformationFromMemory(platform, image_infos[i].load_address,
+                                      pointer_size,
+                                      image_infos[i].macho_info)) {
+      image_infos[i].is_valid_mach_header = true;
+    }
   }
     return FormatDynamicLibrariesIntoJSON(image_infos);
 }
@@ -1160,10 +1177,11 @@
 // dyld SPIs that exist in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer.
 JSONGenerator::ObjectSP MachProcess::GetLibrariesInfoForAddresses(
     nub_process_t pid, std::vector<uint64_t> &macho_addresses) {
-  JSONGenerator::DictionarySP reply_sp;
 
   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,19 +1189,35 @@
   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();
     for (size_t i = 0; i < image_infos_count; i++) {
-      GetMachOInformationFromMemory(platform,
-                                    image_infos[i].load_address, pointer_size,
-                                    image_infos[i].macho_info);
+      if (GetMachOInformationFromMemory(platform, image_infos[i].load_address,
+                                        pointer_size,
+                                        image_infos[i].macho_info)) {
+        image_infos[i].is_valid_mach_header = true;
+      }
     }
     return FormatDynamicLibrariesIntoJSON(image_infos);
 }
Index: lldb/tools/debugserver/source/MacOSX/MachProcess.h
===================================================================
--- lldb/tools/debugserver/source/MacOSX/MachProcess.h
+++ lldb/tools/debugserver/source/MacOSX/MachProcess.h
@@ -73,9 +73,11 @@
     uint64_t load_address;
     uint64_t mod_date; // may not be available - 0 if so
     struct mach_o_information macho_info;
+    bool is_valid_mach_header;
 
     binary_image_information()
-        : filename(), load_address(INVALID_NUB_ADDRESS), mod_date(0) {}
+        : filename(), load_address(INVALID_NUB_ADDRESS), mod_date(0),
+          is_valid_mach_header(false) {}
   };
 
   // Child process control
Index: lldb/test/API/macosx/unregistered-macho/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/macosx/unregistered-macho/main.c
@@ -0,0 +1,63 @@
+#include <mach-o/loader.h>
+#include <mach/machine.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+int main() {
+  int size_of_load_cmds =
+      sizeof(struct segment_command_64) + sizeof(struct uuid_command);
+  uint8_t *macho_buf =
+      (uint8_t *)malloc(sizeof(struct mach_header_64) + size_of_load_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_load_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,47 @@
+"""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")
+        macho_addr = macho_buf.GetValueAsUnsigned()
+        invalid_macho_addr = macho_buf.GetValueAsUnsigned() + 4
+        gdb_packet = "process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{\"solib_addresses\":[%d]}]'" % macho_addr
+
+        # 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"'])
+
+        no_macho_gdb_packet = "process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{\"solib_addresses\":[%d]}]'" % invalid_macho_addr
+        self.expect (no_macho_gdb_packet, substrs=['response: {"images":[]}'])
+
+        # Test that we get back the information for the properly
+        # formatted Mach-O binary in memory, but do not get an
+        # entry for the invalid Mach-O address.
+        both_gdb_packet = "process plugin packet send 'jGetLoadedDynamicLibrariesInfos:{\"solib_addresses\":[%d,%d]}]'" % (macho_addr, invalid_macho_addr)
+        self.expect (both_gdb_packet, substrs=['"load_address":%d,' % macho_addr])
+        self.expect (both_gdb_packet, substrs=['"load_address":%d,' % invalid_macho_addr], matching=False)
+
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