[Lldb-commits] [lldb] 8113a8b - [lldb/ObjectFileMachO] Fetch shared cache images from our own shared cache

Fred Riss via lldb-commits lldb-commits at lists.llvm.org
Thu Jul 16 10:40:01 PDT 2020


Author: Fred Riss
Date: 2020-07-16T10:37:37-07:00
New Revision: 8113a8bb793453832301e2684dc2b8cebec331b0

URL: https://github.com/llvm/llvm-project/commit/8113a8bb793453832301e2684dc2b8cebec331b0
DIFF: https://github.com/llvm/llvm-project/commit/8113a8bb793453832301e2684dc2b8cebec331b0.diff

LOG: [lldb/ObjectFileMachO] Fetch shared cache images from our own shared cache

Summary:
On macOS 11, the libraries that have been integrated in the system
shared cache are not present on the filesystem anymore. LLDB was
using those files to get access to the symbols of those libraries.
LLDB can get the images from the target process memory though.

This has 2 consequences:
 - LLDB cannot load the images before the process starts, reporting
   an error if someone tries to break on a system symbol.
 - Loading the symbols by downloading the data from the inferior
   is super slow. It takes tens of seconds at the start of the
   debug session to populate the Module list.

To fix this, we can use the library images LLDB has in its own
mapping of the shared cache. Shared cache images are somewhat
special as their LINKEDIT segment is moved to the end of the cache
and thus the images are not contiguous in memory. All of this can
hidden in ObjectFileMachO.

This patch fixes a number of test failures on macOS 11 due to the
first problem described above and adds some specific unittesting
for the new SharedCache Host utilities.

Reviewers: jasonmolenda, labath

Subscribers: llvm-commits, lldb-commits

Tags: #lldb, #llvm

Differential Revision: https://reviews.llvm.org/D83023

Added: 
    lldb/unittests/ObjectFile/MachO/CMakeLists.txt
    lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp

Modified: 
    lldb/include/lldb/Host/HostInfoBase.h
    lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
    lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
    lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
    lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
    lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
    lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
    lldb/unittests/ObjectFile/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Host/HostInfoBase.h b/lldb/include/lldb/Host/HostInfoBase.h
index 70682c9b685e..15bb168aad97 100644
--- a/lldb/include/lldb/Host/HostInfoBase.h
+++ b/lldb/include/lldb/Host/HostInfoBase.h
@@ -11,6 +11,7 @@
 
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/UUID.h"
 #include "lldb/Utility/UserIDResolver.h"
 #include "lldb/Utility/XcodeSDK.h"
 #include "lldb/lldb-enumerations.h"
@@ -24,6 +25,11 @@ namespace lldb_private {
 
 class FileSpec;
 
+struct SharedCacheImageInfo {
+  UUID uuid;
+  lldb::DataBufferSP data_sp;
+};
+
 class HostInfoBase {
 private:
   // Static class, unconstructable.
@@ -98,6 +104,13 @@ class HostInfoBase {
   /// Return the directory containing a specific Xcode SDK.
   static llvm::StringRef GetXcodeSDKPath(XcodeSDK sdk) { return {}; }
 
+  /// Return information about module \p image_name if it is loaded in
+  /// the current process's address space.
+  static SharedCacheImageInfo
+  GetSharedCacheImageInfo(llvm::StringRef image_name) {
+    return {};
+  }
+
 protected:
   static bool ComputeSharedLibraryDirectory(FileSpec &file_spec);
   static bool ComputeSupportExeDirectory(FileSpec &file_spec);

diff  --git a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
index 3941414f8abd..ee9f12a90943 100644
--- a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
+++ b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
@@ -37,6 +37,11 @@ class HostInfoMacOSX : public HostInfoPosix {
 
   /// Query xcrun to find an Xcode SDK directory.
   static llvm::StringRef GetXcodeSDKPath(XcodeSDK sdk);
+
+  /// Shared cache utilities
+  static SharedCacheImageInfo
+  GetSharedCacheImageInfo(llvm::StringRef image_name);
+
 protected:
   static bool ComputeSupportExeDirectory(FileSpec &file_spec);
   static void ComputeHostArchitectureSupport(ArchSpec &arch_32,

diff  --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
index 60eacb1e49b2..b325bd2c5b74 100644
--- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
+++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
@@ -12,8 +12,10 @@
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Utility/Args.h"
 #include "lldb/Utility/Log.h"
+#include "Utility/UuidCompatibility.h"
 
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
@@ -457,3 +459,64 @@ FileSpec path(
   auto it_new = g_sdk_path.insert({sdk.GetString(), GetXcodeSDK(sdk)});
   return it_new.first->second;
 }
+
+namespace {
+struct dyld_shared_cache_dylib_text_info {
+  uint64_t version; // current version 1
+  // following fields all exist in version 1
+  uint64_t loadAddressUnslid;
+  uint64_t textSegmentSize;
+  uuid_t dylibUuid;
+  const char *path; // pointer invalid at end of iterations
+  // following fields all exist in version 2
+  uint64_t textSegmentOffset; // offset from start of cache
+};
+typedef struct dyld_shared_cache_dylib_text_info
+    dyld_shared_cache_dylib_text_info;
+}
+
+extern "C" int dyld_shared_cache_iterate_text(
+    const uuid_t cacheUuid,
+    void (^callback)(const dyld_shared_cache_dylib_text_info *info));
+extern "C" uint8_t *_dyld_get_shared_cache_range(size_t *length);
+extern "C" bool _dyld_get_shared_cache_uuid(uuid_t uuid);
+
+namespace {
+class SharedCacheInfo {
+public:
+  const UUID &GetUUID() const { return m_uuid; };
+  const llvm::StringMap<SharedCacheImageInfo> &GetImages() const {
+    return m_images;
+  };
+
+  SharedCacheInfo();
+
+private:
+  llvm::StringMap<SharedCacheImageInfo> m_images;
+  UUID m_uuid;
+};
+}
+
+SharedCacheInfo::SharedCacheInfo() {
+  size_t shared_cache_size;
+  uint8_t *shared_cache_start =
+      _dyld_get_shared_cache_range(&shared_cache_size);
+  uuid_t dsc_uuid;
+  _dyld_get_shared_cache_uuid(dsc_uuid);
+  m_uuid = UUID::fromData(dsc_uuid);
+
+  dyld_shared_cache_iterate_text(
+      dsc_uuid, ^(const dyld_shared_cache_dylib_text_info *info) {
+        m_images[info->path] = SharedCacheImageInfo{
+            UUID::fromData(info->dylibUuid, 16),
+            std::make_shared<DataBufferUnowned>(
+                shared_cache_start + info->textSegmentOffset,
+                shared_cache_size - info->textSegmentOffset)};
+      });
+}
+
+SharedCacheImageInfo
+HostInfoMacOSX::GetSharedCacheImageInfo(llvm::StringRef image_name) {
+  static SharedCacheInfo g_shared_cache_info;
+  return g_shared_cache_info.GetImages().lookup(image_name);
+}

diff  --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
index 7b0d6f343c03..731004340434 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
@@ -16,6 +16,7 @@
 #include "lldb/Core/Section.h"
 #include "lldb/Expression/DiagnosticManager.h"
 #include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
 #include "lldb/Symbol/Function.h"
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Target/ABI.h"
@@ -123,19 +124,39 @@ ModuleSP DynamicLoaderDarwin::FindTargetModuleForImageInfo(
       module_sp.reset();
   }
 
-  if (!module_sp) {
-    if (can_create) {
-      // We'll call Target::ModulesDidLoad after all the modules have been
-      // added to the target, don't let it be called for every one.
-      module_sp = target.GetOrCreateModule(module_spec, false /* notify */);
-      if (!module_sp || module_sp->GetObjectFile() == nullptr)
-        module_sp = m_process->ReadModuleFromMemory(image_info.file_spec,
-                                                    image_info.address);
-
-      if (did_create_ptr)
-        *did_create_ptr = (bool)module_sp;
+  if (module_sp || !can_create)
+    return module_sp;
+
+  if (HostInfo::GetArchitecture().IsCompatibleMatch(target.GetArchitecture())) {
+    // When debugging on the host, we are most likely using the same shared
+    // cache as our inferior. The dylibs from the shared cache might not
+    // exist on the filesystem, so let's use the images in our own memory
+    // to create the modules.
+    // Check if the requested image is in our shared cache.
+    SharedCacheImageInfo image_info =
+        HostInfo::GetSharedCacheImageInfo(module_spec.GetFileSpec().GetPath());
+
+    // If we found it and it has the correct UUID, let's proceed with
+    // creating a module from the memory contents.
+    if (image_info.uuid &&
+        (!module_spec.GetUUID() || module_spec.GetUUID() == image_info.uuid)) {
+      ModuleSpec shared_cache_spec(module_spec.GetFileSpec(), image_info.uuid,
+                                   image_info.data_sp);
+      module_sp =
+          target.GetOrCreateModule(shared_cache_spec, false /* notify */);
     }
   }
+  // We'll call Target::ModulesDidLoad after all the modules have been
+  // added to the target, don't let it be called for every one.
+  if (!module_sp)
+    module_sp = target.GetOrCreateModule(module_spec, false /* notify */);
+  if (!module_sp || module_sp->GetObjectFile() == nullptr)
+    module_sp = m_process->ReadModuleFromMemory(image_info.file_spec,
+                                                image_info.address);
+
+  if (did_create_ptr)
+    *did_create_ptr = (bool)module_sp;
+
   return module_sp;
 }
 

diff  --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index 2bb4b21adeae..ab1a6a8bb5f3 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -47,8 +47,8 @@
 
 #include "ObjectFileMachO.h"
 
-#if defined(__APPLE__) &&                                                      \
-    (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
+#if defined(__APPLE__)
+#include <TargetConditionals.h>
 // GetLLDBSharedCacheUUID() needs to call dlsym()
 #include <dlfcn.h>
 #endif
@@ -1328,6 +1328,19 @@ void ObjectFileMachO::SanitizeSegmentCommand(segment_command_64 &seg_cmd,
   if (m_length == 0 || seg_cmd.filesize == 0)
     return;
 
+  if ((m_header.flags & MH_DYLIB_IN_CACHE) && !IsInMemory()) {
+    // In shared cache images, the load commands are relative to the
+    // shared cache file, and not the the specific image we are
+    // examining. Let's fix this up so that it looks like a normal
+    // image.
+    if (strncmp(seg_cmd.segname, "__TEXT", sizeof(seg_cmd.segname)) == 0)
+      m_text_address = seg_cmd.vmaddr;
+    if (strncmp(seg_cmd.segname, "__LINKEDIT", sizeof(seg_cmd.segname)) == 0)
+      m_linkedit_original_offset = seg_cmd.fileoff;
+
+    seg_cmd.fileoff = seg_cmd.vmaddr - m_text_address;
+  }
+
   if (seg_cmd.fileoff > m_length) {
     // We have a load command that says it extends past the end of the file.
     // This is likely a corrupt file.  We don't have any way to return an error
@@ -1664,6 +1677,10 @@ void ObjectFileMachO::ProcessSegmentCommand(const load_command &load_cmd_,
     if (m_data.GetU32(&offset, &sect64.offset, num_u32s) == nullptr)
       break;
 
+    if ((m_header.flags & MH_DYLIB_IN_CACHE) && !IsInMemory()) {
+      sect64.offset = sect64.addr - m_text_address;
+    }
+
     // Keep a list of mach sections around in case we need to get at data that
     // isn't stored in the abstracted Sections.
     m_mach_sections.push_back(sect64);
@@ -2264,14 +2281,17 @@ size_t ObjectFileMachO::ParseSymtab() {
   Process *process = process_sp.get();
 
   uint32_t memory_module_load_level = eMemoryModuleLoadLevelComplete;
+  bool is_shared_cache_image = m_header.flags & MH_DYLIB_IN_CACHE;
+  bool is_local_shared_cache_image = is_shared_cache_image && !IsInMemory();
+  SectionSP linkedit_section_sp(
+      section_list->FindSectionByName(GetSegmentNameLINKEDIT()));
 
-  if (process && m_header.filetype != llvm::MachO::MH_OBJECT) {
+  if (process && m_header.filetype != llvm::MachO::MH_OBJECT &&
+      !is_local_shared_cache_image) {
     Target &target = process->GetTarget();
 
     memory_module_load_level = target.GetMemoryModuleLoadLevel();
 
-    SectionSP linkedit_section_sp(
-        section_list->FindSectionByName(GetSegmentNameLINKEDIT()));
     // Reading mach file from memory in a process or core file...
 
     if (linkedit_section_sp) {
@@ -2293,62 +2313,6 @@ size_t ObjectFileMachO::ParseSymtab() {
       strtab_addr = linkedit_load_addr + symtab_load_command.stroff -
                     linkedit_file_offset;
 
-      bool data_was_read = false;
-
-#if defined(__APPLE__) &&                                                      \
-    (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
-      if (m_header.flags & MH_DYLIB_IN_CACHE &&
-          process->GetAddressByteSize() == sizeof(void *)) {
-        // This mach-o memory file is in the dyld shared cache. If this
-        // program is not remote and this is iOS, then this process will
-        // share the same shared cache as the process we are debugging and we
-        // can read the entire __LINKEDIT from the address space in this
-        // process. This is a needed optimization that is used for local iOS
-        // debugging only since all shared libraries in the shared cache do
-        // not have corresponding files that exist in the file system of the
-        // device. They have been combined into a single file. This means we
-        // always have to load these files from memory. All of the symbol and
-        // string tables from all of the __LINKEDIT sections from the shared
-        // libraries in the shared cache have been merged into a single large
-        // symbol and string table. Reading all of this symbol and string
-        // table data across can slow down debug launch times, so we optimize
-        // this by reading the memory for the __LINKEDIT section from this
-        // process.
-
-        UUID lldb_shared_cache;
-        addr_t lldb_shared_cache_addr;
-        GetLLDBSharedCacheUUID(lldb_shared_cache_addr, lldb_shared_cache);
-        UUID process_shared_cache;
-        addr_t process_shared_cache_addr;
-        GetProcessSharedCacheUUID(process, process_shared_cache_addr,
-                                  process_shared_cache);
-        bool use_lldb_cache = true;
-        if (lldb_shared_cache.IsValid() && process_shared_cache.IsValid() &&
-            (lldb_shared_cache != process_shared_cache ||
-             process_shared_cache_addr != lldb_shared_cache_addr)) {
-          use_lldb_cache = false;
-        }
-
-        PlatformSP platform_sp(target.GetPlatform());
-        if (platform_sp && platform_sp->IsHost() && use_lldb_cache) {
-          data_was_read = true;
-          nlist_data.SetData((void *)symoff_addr, nlist_data_byte_size,
-                             eByteOrderLittle);
-          strtab_data.SetData((void *)strtab_addr, strtab_data_byte_size,
-                              eByteOrderLittle);
-          if (function_starts_load_command.cmd) {
-            const addr_t func_start_addr =
-                linkedit_load_addr + function_starts_load_command.dataoff -
-                linkedit_file_offset;
-            function_starts_data.SetData((void *)func_start_addr,
-                                         function_starts_load_command.datasize,
-                                         eByteOrderLittle);
-          }
-        }
-      }
-#endif
-
-      if (!data_was_read) {
         // Always load dyld - the dynamic linker - from memory if we didn't
         // find a binary anywhere else. lldb will not register
         // dylib/framework/bundle loads/unloads if we don't have the dyld
@@ -2379,7 +2343,7 @@ size_t ObjectFileMachO::ParseSymtab() {
             // problem. For binaries outside the shared cache, it's faster to
             // read the entire strtab at once instead of piece-by-piece as we
             // process the nlist records.
-            if ((m_header.flags & MH_DYLIB_IN_CACHE) == 0) {
+            if (!is_shared_cache_image) {
               DataBufferSP strtab_data_sp(
                   ReadMemory(process_sp, strtab_addr, strtab_data_byte_size));
               if (strtab_data_sp) {
@@ -2388,7 +2352,6 @@ size_t ObjectFileMachO::ParseSymtab() {
               }
             }
           }
-        }
         if (memory_module_load_level >= eMemoryModuleLoadLevelPartial) {
           if (function_starts_load_command.cmd) {
             const addr_t func_start_addr =
@@ -2405,6 +2368,24 @@ size_t ObjectFileMachO::ParseSymtab() {
       }
     }
   } else {
+    if (is_local_shared_cache_image) {
+      // The load commands in shared cache images are relative to the
+      // beginning of the shared cache, not the library image. The
+      // data we get handed when creating the ObjectFileMachO starts
+      // at the beginning of a specific library and spans to the end
+      // of the cache to be able to reach the shared LINKEDIT
+      // segments. We need to convert the load command offsets to be
+      // relative to the beginning of our specific image.
+      lldb::addr_t linkedit_offset = linkedit_section_sp->GetFileOffset();
+      lldb::offset_t linkedit_slide =
+          linkedit_offset - m_linkedit_original_offset;
+      symtab_load_command.symoff += linkedit_slide;
+      symtab_load_command.stroff += linkedit_slide;
+      dyld_info.export_off += linkedit_slide;
+      m_dysymtab.indirectsymoff += linkedit_slide;
+      function_starts_load_command.dataoff += linkedit_slide;
+    }
+
     nlist_data.SetData(m_data, symtab_load_command.symoff,
                        nlist_data_byte_size);
     strtab_data.SetData(m_data, symtab_load_command.stroff,
@@ -5807,8 +5788,7 @@ void ObjectFileMachO::GetLLDBSharedCacheUUID(addr_t &base_addr, UUID &uuid) {
   uuid.Clear();
   base_addr = LLDB_INVALID_ADDRESS;
 
-#if defined(__APPLE__) &&                                                      \
-    (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
+#if defined(__APPLE__)
   uint8_t *(*dyld_get_all_image_infos)(void);
   dyld_get_all_image_infos =
       (uint8_t * (*)()) dlsym(RTLD_DEFAULT, "_dyld_get_all_image_infos");

diff  --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
index 979e637ef6fd..0c1d178b1921 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
@@ -225,6 +225,8 @@ class ObjectFileMachO : public lldb_private::ObjectFile {
   typedef lldb_private::RangeVector<uint32_t, uint32_t> FileRangeArray;
   lldb_private::Address m_entry_point_address;
   FileRangeArray m_thread_context_offsets;
+  lldb::offset_t m_linkedit_original_offset;
+  lldb::addr_t m_text_address;
   bool m_thread_context_offsets_valid;
   lldb_private::FileSpecList m_reexported_dylibs;
   bool m_allow_assembly_emulation_unwind_plans;

diff  --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index f5ec08a1a199..d31559bc9018 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -237,6 +237,30 @@ lldb_private::Status PlatformDarwin::GetSharedModuleWithLocalCache(
 
   Status err;
 
+  if (IsHost()) {
+    // When debugging on the host, we are most likely using the same shared
+    // cache as our inferior. The dylibs from the shared cache might not
+    // exist on the filesystem, so let's use the images in our own memory
+    // to create the modules.
+
+    // Check if the requested image is in our shared cache.
+    SharedCacheImageInfo image_info =
+        HostInfo::GetSharedCacheImageInfo(module_spec.GetFileSpec().GetPath());
+
+    // If we found it and it has the correct UUID, let's proceed with
+    // creating a module from the memory contents.
+    if (image_info.uuid &&
+        (!module_spec.GetUUID() || module_spec.GetUUID() == image_info.uuid)) {
+      ModuleSpec shared_cache_spec(module_spec.GetFileSpec(), image_info.uuid,
+                                   image_info.data_sp);
+      err = ModuleList::GetSharedModule(shared_cache_spec, module_sp,
+                                        module_search_paths_ptr,
+                                        old_module_sp_ptr, did_create_ptr);
+      if (module_sp)
+        return err;
+    }
+  }
+
   err = ModuleList::GetSharedModule(module_spec, module_sp,
                                     module_search_paths_ptr, old_module_sp_ptr,
                                     did_create_ptr);

diff  --git a/lldb/unittests/ObjectFile/CMakeLists.txt b/lldb/unittests/ObjectFile/CMakeLists.txt
index a9b42ea3199d..b5d248e3965d 100644
--- a/lldb/unittests/ObjectFile/CMakeLists.txt
+++ b/lldb/unittests/ObjectFile/CMakeLists.txt
@@ -1,3 +1,4 @@
 add_subdirectory(Breakpad)
 add_subdirectory(ELF)
+add_subdirectory(MachO)
 add_subdirectory(PECOFF)

diff  --git a/lldb/unittests/ObjectFile/MachO/CMakeLists.txt b/lldb/unittests/ObjectFile/MachO/CMakeLists.txt
new file mode 100644
index 000000000000..b6c4225114a3
--- /dev/null
+++ b/lldb/unittests/ObjectFile/MachO/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_lldb_unittest(ObjectFileMachOTests
+  TestObjectFileMachO.cpp
+
+  LINK_LIBS
+    lldbPluginObjectFileMachO
+    lldbPluginSymbolFileSymtab
+    lldbCore
+    lldbUtilityHelpers
+    LLVMTestingSupport
+  )

diff  --git a/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp b/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
new file mode 100644
index 000000000000..119be3822ccb
--- /dev/null
+++ b/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
@@ -0,0 +1,79 @@
+//===-- ObjectFileMachOTest.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/HostInfo.h"
+#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/lldb-defines.h"
+#include "gtest/gtest.h"
+
+#ifdef __APPLE__
+#include <dlfcn.h>
+#endif
+
+using namespace lldb_private;
+using namespace llvm;
+
+namespace {
+class ObjectFileMachOTest : public ::testing::Test {
+  SubsystemRAII<FileSystem, HostInfo, ObjectFileMachO> subsystems;
+};
+} // namespace
+
+#if defined(__APPLE__)
+TEST_F(ObjectFileMachOTest, ModuleFromSharedCacheInfo) {
+  SharedCacheImageInfo image_info =
+      HostInfo::GetSharedCacheImageInfo("/usr/lib/libobjc.A.dylib");
+  EXPECT_TRUE(image_info.uuid);
+  EXPECT_TRUE(image_info.data_sp);
+
+  ModuleSpec spec(FileSpec(), UUID(), image_info.data_sp);
+  lldb::ModuleSP module = std::make_shared<Module>(spec);
+  ObjectFile *OF = module->GetObjectFile();
+  ASSERT_TRUE(llvm::isa<ObjectFileMachO>(OF));
+  EXPECT_TRUE(
+      OF->GetArchitecture().IsCompatibleMatch(HostInfo::GetArchitecture()));
+  Symtab *symtab = OF->GetSymtab();
+  ASSERT_NE(symtab, nullptr);
+  void *libobjc = dlopen("/usr/lib/libobjc.A.dylib", RTLD_LAZY);
+  ASSERT_NE(libobjc, nullptr);
+
+  // This function checks that if we read something from the
+  // ObjectFile we get through the shared cache in-mmeory
+  // buffer, it matches what we get by reading directly the
+  // memory of the symbol.
+  auto check_symbol = [&](const char *sym_name) {
+    std::vector<uint32_t> symbol_indices;
+    symtab->FindAllSymbolsWithNameAndType(ConstString(sym_name),
+                                          lldb::eSymbolTypeAny, symbol_indices);
+    EXPECT_EQ(symbol_indices.size(), 1u);
+
+    Symbol *sym = symtab->SymbolAtIndex(symbol_indices[0]);
+    ASSERT_NE(sym, nullptr);
+    Address base = sym->GetAddress();
+    size_t size = sym->GetByteSize();
+    ASSERT_NE(size, 0u);
+    uint8_t buffer[size];
+    EXPECT_EQ(OF->ReadSectionData(base.GetSection().get(), base.GetOffset(),
+                                  buffer, size),
+              size);
+
+    void *sym_addr = dlsym(libobjc, sym_name);
+    ASSERT_NE(sym_addr, nullptr);
+    EXPECT_EQ(memcmp(buffer, sym_addr, size), 0);
+  };
+
+  // Read a symbol from the __TEXT segment...
+  check_symbol("objc_msgSend");
+  // ... and one from the __DATA segment
+  check_symbol("OBJC_CLASS_$_NSObject");
+}
+#endif


        


More information about the lldb-commits mailing list