This revision was automatically updated to reflect the committed changes.
Closed by commit rG28fb39f16af1: [lldb] Adjust for changes in objc runtime 
(authored by bulbazord).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153597

Files:
  
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
  
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
  
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
  lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
  lldb/test/API/lang/objc/exceptions/TestObjCExceptions.py

Index: lldb/test/API/lang/objc/exceptions/TestObjCExceptions.py
===================================================================
--- lldb/test/API/lang/objc/exceptions/TestObjCExceptions.py
+++ lldb/test/API/lang/objc/exceptions/TestObjCExceptions.py
@@ -87,7 +87,6 @@
                 "userInfo = ",
                 "1 key/value pair",
                 "reserved = ",
-                "nil",
             ],
         )
 
@@ -105,7 +104,6 @@
         self.assertEqual(
             userInfo.GetChildAtIndex(0).GetChildAtIndex(1).description, "some_value"
         )
-        self.assertEqual(e1.GetChildMemberWithName("reserved").description, "<nil>")
 
         self.expect(
             "frame variable e2", substrs=["(NSException *) e2 = ", '"SomeReason"']
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -19,6 +19,8 @@
 
 #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
 
+#include "llvm/ADT/BitVector.h"
+
 class RemoteNXMapTable;
 
 namespace lldb_private {
@@ -96,6 +98,12 @@
   void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
                                     lldb::addr_t &cf_false) override;
 
+  void ModulesDidLoad(const ModuleList &module_list) override;
+
+  bool IsSharedCacheImageLoaded(uint16_t image_index);
+
+  std::optional<uint64_t> GetSharedCacheImageHeaderVersion();
+
 protected:
   lldb::BreakpointResolverSP
   CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
@@ -374,6 +382,35 @@
     lldb::addr_t m_args = LLDB_INVALID_ADDRESS;
   };
 
+  class SharedCacheImageHeaders {
+  public:
+    static std::unique_ptr<SharedCacheImageHeaders>
+    CreateSharedCacheImageHeaders(AppleObjCRuntimeV2 &runtime);
+
+    void SetNeedsUpdate() { m_needs_update = true; }
+
+    bool IsImageLoaded(uint16_t image_index);
+
+    uint64_t GetVersion();
+
+  private:
+    SharedCacheImageHeaders(AppleObjCRuntimeV2 &runtime,
+                            lldb::addr_t headerInfoRWs_ptr, uint32_t count,
+                            uint32_t entsize)
+        : m_runtime(runtime), m_headerInfoRWs_ptr(headerInfoRWs_ptr),
+          m_loaded_images(count, false), m_version(0), m_count(count),
+          m_entsize(entsize), m_needs_update(true) {}
+    llvm::Error UpdateIfNeeded();
+
+    AppleObjCRuntimeV2 &m_runtime;
+    lldb::addr_t m_headerInfoRWs_ptr;
+    llvm::BitVector m_loaded_images;
+    uint64_t m_version;
+    uint32_t m_count;
+    uint32_t m_entsize;
+    bool m_needs_update;
+  };
+
   AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
 
   ObjCISA GetPointerISA(ObjCISA isa);
@@ -435,6 +472,7 @@
   std::once_flag m_no_expanded_cache_warning;
   std::optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
   uint64_t m_realized_class_generation_count;
+  std::unique_ptr<SharedCacheImageHeaders> m_shared_cache_image_headers_up;
 };
 
 } // namespace lldb_private
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -1619,6 +1619,146 @@
   return m_isa_hash_table_ptr;
 }
 
+std::unique_ptr<AppleObjCRuntimeV2::SharedCacheImageHeaders>
+AppleObjCRuntimeV2::SharedCacheImageHeaders::CreateSharedCacheImageHeaders(
+    AppleObjCRuntimeV2 &runtime) {
+  Log *log = GetLog(LLDBLog::Process | LLDBLog::Types);
+  Process *process = runtime.GetProcess();
+  ModuleSP objc_module_sp(runtime.GetObjCModule());
+  if (!objc_module_sp || !process)
+    return nullptr;
+
+  const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
+      ConstString("objc_debug_headerInfoRWs"), lldb::eSymbolTypeAny);
+  if (!symbol) {
+    LLDB_LOG(log, "Symbol 'objc_debug_headerInfoRWs' unavailable. Some "
+                  "information concerning the shared cache may be unavailable");
+    return nullptr;
+  }
+
+  lldb::addr_t objc_debug_headerInfoRWs_addr =
+      symbol->GetLoadAddress(&process->GetTarget());
+  if (objc_debug_headerInfoRWs_addr == LLDB_INVALID_ADDRESS) {
+    LLDB_LOG(log, "Symbol 'objc_debug_headerInfoRWs' was found but we were "
+                  "unable to get its load address");
+    return nullptr;
+  }
+
+  Status error;
+  lldb::addr_t objc_debug_headerInfoRWs_ptr =
+      process->ReadPointerFromMemory(objc_debug_headerInfoRWs_addr, error);
+  if (error.Fail()) {
+    LLDB_LOG(log,
+             "Failed to read address of 'objc_debug_headerInfoRWs' at {0:x}",
+             objc_debug_headerInfoRWs_addr);
+    return nullptr;
+  }
+
+  const size_t metadata_size =
+      sizeof(uint32_t) + sizeof(uint32_t); // count + entsize
+  DataBufferHeap metadata_buffer(metadata_size, '\0');
+  process->ReadMemory(objc_debug_headerInfoRWs_ptr, metadata_buffer.GetBytes(),
+                      metadata_size, error);
+  if (error.Fail()) {
+    LLDB_LOG(log,
+             "Unable to read metadata for 'objc_debug_headerInfoRWs' at {0:x}",
+             objc_debug_headerInfoRWs_ptr);
+    return nullptr;
+  }
+
+  DataExtractor metadata_extractor(metadata_buffer.GetBytes(), metadata_size,
+                                   process->GetByteOrder(),
+                                   process->GetAddressByteSize());
+  lldb::offset_t cursor = 0;
+  uint32_t count = metadata_extractor.GetU32_unchecked(&cursor);
+  uint32_t entsize = metadata_extractor.GetU32_unchecked(&cursor);
+  if (count == 0 || entsize == 0) {
+    LLDB_LOG(log,
+             "'objc_debug_headerInfoRWs' had count {0} with entsize {1}. These "
+             "should both be non-zero.",
+             count, entsize);
+    return nullptr;
+  }
+
+  std::unique_ptr<SharedCacheImageHeaders> shared_cache_image_headers(
+      new SharedCacheImageHeaders(runtime, objc_debug_headerInfoRWs_ptr, count,
+                                  entsize));
+  if (auto Err = shared_cache_image_headers->UpdateIfNeeded()) {
+    LLDB_LOG_ERROR(log, std::move(Err),
+                   "Failed to update SharedCacheImageHeaders");
+    return nullptr;
+  }
+
+  return shared_cache_image_headers;
+}
+
+llvm::Error AppleObjCRuntimeV2::SharedCacheImageHeaders::UpdateIfNeeded() {
+  if (!m_needs_update)
+    return llvm::Error::success();
+
+  Process *process = m_runtime.GetProcess();
+  constexpr lldb::addr_t metadata_size =
+      sizeof(uint32_t) + sizeof(uint32_t); // count + entsize
+
+  Status error;
+  const lldb::addr_t first_header_addr = m_headerInfoRWs_ptr + metadata_size;
+  DataBufferHeap header_buffer(m_entsize, '\0');
+  lldb::offset_t cursor = 0;
+  for (uint32_t i = 0; i < m_count; i++) {
+    const lldb::addr_t header_addr = first_header_addr + (i * m_entsize);
+    process->ReadMemory(header_addr, header_buffer.GetBytes(), m_entsize,
+                        error);
+    if (error.Fail())
+      return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                     "Failed to read memory from inferior when "
+                                     "populating SharedCacheImageHeaders");
+
+    DataExtractor header_extractor(header_buffer.GetBytes(), m_entsize,
+                                   process->GetByteOrder(),
+                                   process->GetAddressByteSize());
+    cursor = 0;
+    bool is_loaded = false;
+    if (m_entsize == 4) {
+      uint32_t header = header_extractor.GetU32_unchecked(&cursor);
+      if (header & 1)
+        is_loaded = true;
+    } else {
+      uint64_t header = header_extractor.GetU64_unchecked(&cursor);
+      if (header & 1)
+        is_loaded = true;
+    }
+
+    if (is_loaded)
+      m_loaded_images.set(i);
+    else
+      m_loaded_images.reset(i);
+  }
+  m_needs_update = false;
+  m_version++;
+  return llvm::Error::success();
+}
+
+bool AppleObjCRuntimeV2::SharedCacheImageHeaders::IsImageLoaded(
+    uint16_t image_index) {
+  if (image_index >= m_count)
+    return false;
+  if (auto Err = UpdateIfNeeded()) {
+    Log *log = GetLog(LLDBLog::Process | LLDBLog::Types);
+    LLDB_LOG_ERROR(log, std::move(Err),
+                   "Failed to update SharedCacheImageHeaders");
+  }
+  return m_loaded_images.test(image_index);
+}
+
+uint64_t AppleObjCRuntimeV2::SharedCacheImageHeaders::GetVersion() {
+  if (auto Err = UpdateIfNeeded()) {
+    Log *log = GetLog(LLDBLog::Process | LLDBLog::Types);
+    LLDB_LOG_ERROR(log, std::move(Err),
+                   "Failed to update SharedCacheImageHeaders");
+  }
+  return m_version;
+}
+
 std::unique_ptr<UtilityFunction>
 AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoUtilityFunctionImpl(
     ExecutionContext &exe_ctx, Helper helper, std::string code,
@@ -3239,6 +3379,34 @@
     this->AppleObjCRuntime::GetValuesForGlobalCFBooleans(cf_true, cf_false);
 }
 
+void AppleObjCRuntimeV2::ModulesDidLoad(const ModuleList &module_list) {
+  AppleObjCRuntime::ModulesDidLoad(module_list);
+  if (HasReadObjCLibrary() && m_shared_cache_image_headers_up)
+    m_shared_cache_image_headers_up->SetNeedsUpdate();
+}
+
+bool AppleObjCRuntimeV2::IsSharedCacheImageLoaded(uint16_t image_index) {
+  if (!m_shared_cache_image_headers_up) {
+    m_shared_cache_image_headers_up =
+        SharedCacheImageHeaders::CreateSharedCacheImageHeaders(*this);
+  }
+  if (m_shared_cache_image_headers_up)
+    return m_shared_cache_image_headers_up->IsImageLoaded(image_index);
+
+  return false;
+}
+
+std::optional<uint64_t> AppleObjCRuntimeV2::GetSharedCacheImageHeaderVersion() {
+  if (!m_shared_cache_image_headers_up) {
+    m_shared_cache_image_headers_up =
+        SharedCacheImageHeaders::CreateSharedCacheImageHeaders(*this);
+  }
+  if (m_shared_cache_image_headers_up)
+    return m_shared_cache_image_headers_up->GetVersion();
+
+  return std::nullopt;
+}
+
 #pragma mark Frame recognizers
 
 class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame {
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
@@ -146,6 +146,9 @@
     bool Read(Process *process, lldb::addr_t addr);
   };
 
+  std::optional<method_list_t>
+  GetMethodList(Process *process, lldb::addr_t method_list_ptr) const;
+
   struct method_t {
     lldb::addr_t m_name_ptr;
     lldb::addr_t m_types_ptr;
@@ -201,6 +204,21 @@
     bool Read(Process *process, lldb::addr_t addr);
   };
 
+  struct relative_list_entry_t {
+    uint16_t m_image_index;
+    int64_t m_list_offset;
+
+    bool Read(Process *process, lldb::addr_t addr);
+  };
+
+  struct relative_list_list_t {
+    uint32_t m_entsize;
+    uint32_t m_count;
+    lldb::addr_t m_first_ptr;
+
+    bool Read(Process *process, lldb::addr_t addr);
+  };
+
   class iVarsStorage {
   public:
     iVarsStorage();
@@ -223,7 +241,8 @@
   ClassDescriptorV2(AppleObjCRuntimeV2 &runtime,
                     ObjCLanguageRuntime::ObjCISA isa, const char *name)
       : m_runtime(runtime), m_objc_class_ptr(isa), m_name(name),
-        m_ivars_storage() {}
+        m_ivars_storage(), m_image_to_method_lists(), m_last_version_updated() {
+  }
 
   bool Read_objc_class(Process *process,
                        std::unique_ptr<objc_class_t> &objc_class) const;
@@ -232,6 +251,15 @@
                       std::unique_ptr<class_ro_t> &class_ro,
                       std::unique_ptr<class_rw_t> &class_rw) const;
 
+  bool ProcessMethodList(std::function<bool(const char *, const char *)> const
+                             &instance_method_func,
+                         method_list_t &method_list) const;
+
+  bool ProcessRelativeMethodLists(
+      std::function<bool(const char *, const char *)> const
+          &instance_method_func,
+      lldb::addr_t relative_method_list_ptr) const;
+
   AppleObjCRuntimeV2
       &m_runtime; // The runtime, so we can read information lazily.
   lldb::addr_t m_objc_class_ptr; // The address of the objc_class_t.  (I.e.,
@@ -239,6 +267,10 @@
                                  // their ISA)
   ConstString m_name;            // May be NULL
   iVarsStorage m_ivars_storage;
+
+  mutable std::map<uint16_t, std::vector<method_list_t>>
+      m_image_to_method_lists;
+  mutable std::optional<uint64_t> m_last_version_updated;
 };
 
 // tagged pointer descriptor
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
@@ -370,6 +370,155 @@
   return !error.Fail();
 }
 
+bool ClassDescriptorV2::relative_list_entry_t::Read(Process *process,
+                                                    lldb::addr_t addr) {
+  Log *log = GetLog(LLDBLog::Types);
+  size_t size = sizeof(uint64_t); // m_image_index : 16
+                                  // m_list_offset : 48
+
+  DataBufferHeap buffer(size, '\0');
+  Status error;
+
+  process->ReadMemory(addr, buffer.GetBytes(), size, error);
+  // FIXME: Propagate this error up
+  if (error.Fail()) {
+    LLDB_LOG(log, "Failed to read relative_list_entry_t at address {0:x}",
+             addr);
+    return false;
+  }
+
+  DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+                          process->GetAddressByteSize());
+  lldb::offset_t cursor = 0;
+  uint64_t raw_entry = extractor.GetU64_unchecked(&cursor);
+  m_image_index = raw_entry & 0xFFFF;
+  m_list_offset = (int64_t)(raw_entry >> 16);
+  return true;
+}
+
+bool ClassDescriptorV2::relative_list_list_t::Read(Process *process,
+                                                   lldb::addr_t addr) {
+  Log *log = GetLog(LLDBLog::Types);
+  size_t size = sizeof(uint32_t)    // m_entsize
+                + sizeof(uint32_t); // m_count
+
+  DataBufferHeap buffer(size, '\0');
+  Status error;
+
+  // FIXME: Propagate this error up
+  process->ReadMemory(addr, buffer.GetBytes(), size, error);
+  if (error.Fail()) {
+    LLDB_LOG(log, "Failed to read relative_list_list_t at address 0x" PRIx64,
+             addr);
+    return false;
+  }
+
+  DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+                          process->GetAddressByteSize());
+  lldb::offset_t cursor = 0;
+  m_entsize = extractor.GetU32_unchecked(&cursor);
+  m_count = extractor.GetU32_unchecked(&cursor);
+  m_first_ptr = addr + cursor;
+  return true;
+}
+
+std::optional<ClassDescriptorV2::method_list_t>
+ClassDescriptorV2::GetMethodList(Process *process,
+                                 lldb::addr_t method_list_ptr) const {
+  Log *log = GetLog(LLDBLog::Types);
+  ClassDescriptorV2::method_list_t method_list;
+  if (!method_list.Read(process, method_list_ptr))
+    return std::nullopt;
+
+  const size_t method_size = method_t::GetSize(process, method_list.m_is_small);
+  if (method_list.m_entsize != method_size) {
+    LLDB_LOG(log,
+             "method_list_t at address 0x" PRIx64 " has an entsize of " PRIu16
+             " but method size should be " PRIu64,
+             method_list_ptr, method_list.m_entsize, method_size);
+    return std::nullopt;
+  }
+
+  return method_list;
+}
+
+bool ClassDescriptorV2::ProcessMethodList(
+    std::function<bool(const char *, const char *)> const &instance_method_func,
+    ClassDescriptorV2::method_list_t &method_list) const {
+  lldb_private::Process *process = m_runtime.GetProcess();
+  auto method = std::make_unique<method_t>();
+  lldb::addr_t relative_selector_base_addr =
+      m_runtime.GetRelativeSelectorBaseAddr();
+  for (uint32_t i = 0, e = method_list.m_count; i < e; ++i) {
+    method->Read(process, method_list.m_first_ptr + (i * method_list.m_entsize),
+                 relative_selector_base_addr, method_list.m_is_small,
+                 method_list.m_has_direct_selector);
+    if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
+      break;
+  }
+  return true;
+}
+
+// The relevant data structures:
+//  - relative_list_list_t
+//    - uint32_t count
+//    - uint32_t entsize
+//    - Followed by <count> number of relative_list_entry_t of size <entsize>
+//
+//  - relative_list_entry_t
+//    - uint64_t image_index : 16
+//    - int64_t list_offset : 48
+//    - Note: The above 2 fit into 8 bytes always
+//
+//    image_index corresponds to an image in the shared cache
+//    list_offset is used to calculate the address of the method_list_t we want
+bool ClassDescriptorV2::ProcessRelativeMethodLists(
+    std::function<bool(const char *, const char *)> const &instance_method_func,
+    lldb::addr_t relative_method_list_ptr) const {
+  lldb_private::Process *process = m_runtime.GetProcess();
+  auto relative_method_lists = std::make_unique<relative_list_list_t>();
+
+  // 1. Process the count and entsize of the relative_list_list_t
+  if (!relative_method_lists->Read(process, relative_method_list_ptr))
+    return false;
+
+  auto entry = std::make_unique<relative_list_entry_t>();
+  for (uint32_t i = 0; i < relative_method_lists->m_count; i++) {
+    // 2. Extract the image index and the list offset from the
+    // relative_list_entry_t
+    const lldb::addr_t entry_addr = relative_method_lists->m_first_ptr +
+                                    (i * relative_method_lists->m_entsize);
+    if (!entry->Read(process, entry_addr))
+      return false;
+
+    // 3. Calculate the pointer to the method_list_t from the
+    // relative_list_entry_t
+    const lldb::addr_t method_list_addr = entry_addr + entry->m_list_offset;
+
+    // 4. Get the method_list_t from the pointer
+    std::optional<method_list_t> method_list =
+        GetMethodList(process, method_list_addr);
+    if (!method_list)
+      return false;
+
+    // 5. Cache the result so we don't need to reconstruct it later.
+    m_image_to_method_lists[entry->m_image_index].emplace_back(*method_list);
+
+    // 6. If the relevant image is loaded, add the methods to the Decl
+    if (!m_runtime.IsSharedCacheImageLoaded(entry->m_image_index))
+      continue;
+
+    if (!ProcessMethodList(instance_method_func, *method_list))
+      return false;
+  }
+
+  // We need to keep track of the last time we updated so we can re-update the
+  // type information in the future
+  m_last_version_updated = m_runtime.GetSharedCacheImageHeaderVersion();
+
+  return true;
+}
+
 bool ClassDescriptorV2::Describe(
     std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
     std::function<bool(const char *, const char *)> const &instance_method_func,
@@ -393,29 +542,18 @@
     superclass_func(objc_class->m_superclass);
 
   if (instance_method_func) {
-    std::unique_ptr<method_list_t> base_method_list;
-
-    base_method_list = std::make_unique<method_list_t>();
-    if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr))
-      return false;
-
-    bool is_small = base_method_list->m_is_small;
-    bool has_direct_selector = base_method_list->m_has_direct_selector;
-
-    if (base_method_list->m_entsize != method_t::GetSize(process, is_small))
-      return false;
-
-    std::unique_ptr<method_t> method = std::make_unique<method_t>();
-    lldb::addr_t relative_selector_base_addr =
-        m_runtime.GetRelativeSelectorBaseAddr();
-    for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) {
-      method->Read(process,
-                   base_method_list->m_first_ptr +
-                       (i * base_method_list->m_entsize),
-                   relative_selector_base_addr, is_small, has_direct_selector);
-
-      if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
-        break;
+    // This is a relative list of lists
+    if (class_ro->m_baseMethods_ptr & 1) {
+      if (!ProcessRelativeMethodLists(instance_method_func,
+                                      class_ro->m_baseMethods_ptr ^ 1))
+        return false;
+    } else {
+      std::optional<method_list_t> base_method_list =
+          GetMethodList(process, class_ro->m_baseMethods_ptr);
+      if (!base_method_list)
+        return false;
+      if (!ProcessMethodList(instance_method_func, *base_method_list))
+        return false;
     }
   }
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to