[Lldb-commits] [lldb] [lldb] Search for a kernel's kexts before loading any of them (PR #231211)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Mon Oct 12 08:56:45 PDT 2026


https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/231211

This is the final piece of work to parallelize kext lookups. Loading the kexts is now split into two phases:

1. Determining what to search for, which involves the process and target.
2. Adding the kext to the target.

Both phases remain serial, but the searches between them are batched and those the platform cannot answer can run in parallel. The kexts are added to the target in the order the kernel lists them. The progress report counts a kext when it is being added rather than when preparing for the symbol lookup. The search has its own progress reporting.

rdar://122977780

>From 6c461df8807766937972897d665ff54316e3723a Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Wed, 7 Oct 2026 17:24:55 -0700
Subject: [PATCH] [lldb] Search for a kernel's kexts before loading any of them

This is the final piece of work to parallelize kext lookups. Loading the
kexts is now split into two phases:

1. Determining what to search for, which involves the process and
   target.
2. Adding the kext to the target.

Both phases remain serial, but the searches between them are batched and
those the platform cannot answer can run in parallel. The kexts are added
to the target in the order the kernel lists them. The progress report
counts a kext when it is being added rather than when preparing for the
symbol lookup.

rdar://122977780
---
 .../DynamicLoaderDarwinKernel.cpp             | 360 ++++++++++--------
 .../Darwin-Kernel/DynamicLoaderDarwinKernel.h |  39 +-
 2 files changed, 247 insertions(+), 152 deletions(-)

diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
index 3e5938a6cce19..e4401a848dd35 100644
--- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
+++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
@@ -33,6 +33,8 @@
 
 #include "DynamicLoaderDarwinKernel.h"
 
+#include "llvm/ADT/STLExtras.h"
+
 #include <algorithm>
 #include <memory>
 
@@ -728,12 +730,25 @@ void DynamicLoaderDarwinKernel::KextImageInfo::SetIsKernel(bool is_kernel) {
   m_kernel_image = is_kernel;
 }
 
-bool DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule(
-    Process *process, Progress *progress) {
-  Log *log = GetLog(LLDBLog::DynamicLoader);
-  if (IsLoaded())
-    return true;
+static std::string GetProgressDetail(llvm::StringRef name, const UUID &uuid,
+                                     addr_t load_address) {
+  StreamString prog_str;
+  // 'mach_kernel' is a fake name we make up to find kernels
+  // that were located by the local filesystem scan.
+  if (name != "mach_kernel")
+    prog_str << name << " ";
+  if (uuid.IsValid())
+    prog_str << uuid.GetAsString() << " ";
+  if (load_address != LLDB_INVALID_ADDRESS) {
+    prog_str << "at 0x";
+    prog_str.PutHex64(load_address);
+  }
+  return prog_str.GetString().str();
+}
 
+bool DynamicLoaderDarwinKernel::KextImageInfo::PrepareSearch(
+    Process *process, std::optional<SymbolLocator::Request> &request) {
+  assert(!IsLoaded() && "kext already loaded");
   Target &target = process->GetTarget();
 
   // kexts will have a uuid from the table.
@@ -766,148 +781,156 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule(
     target.GetImages().Remove(incorrect_kernels);
   }
 
-  if (!m_module_sp) {
-    // See if the kext has already been loaded into the target, probably by the
-    // user doing target modules add.
-    const ModuleList &target_images = target.GetImages();
-    m_module_sp = target_images.FindModule(m_uuid);
-
-    StreamString prog_str;
-    // 'mach_kernel' is a fake name we make up to find kernels
-    // that were located by the local filesystem scan.
-    if (GetName() != "mach_kernel")
-      prog_str << GetName() << " ";
-    if (GetUUID().IsValid())
-      prog_str << GetUUID().GetAsString() << " ";
-    if (GetLoadAddress() != LLDB_INVALID_ADDRESS) {
-      prog_str << "at 0x";
-      prog_str.PutHex64(GetLoadAddress());
+  if (m_module_sp)
+    return true;
+
+  // See if the kext has already been loaded into the target, probably by the
+  // user doing target modules add.
+  const ModuleList &target_images = target.GetImages();
+  m_module_sp = target_images.FindModule(m_uuid);
+  if (m_module_sp || !m_uuid.IsValid())
+    return true;
+
+  // A kext in the shared module list needs no search, unless its file changed:
+  // registering evicts a stale copy.
+  if (!IsKernel()) {
+    ModuleSP shared_module_sp = ModuleList::FindSharedModule(m_uuid);
+    if (shared_module_sp && !shared_module_sp->FileHasChanged())
+      return true;
+  }
+
+  // SymbolLocator asks the platform first, and PlatformDarwinKernel answers
+  // from its kext index, using the bundle ID in the file name.
+  request.emplace();
+  request->module_spec.SetTarget(target.shared_from_this());
+  request->module_spec.GetUUID() = m_uuid;
+  request->module_spec.GetFileSpec() = FileSpec(m_name);
+  request->platform = target.GetPlatform();
+  request->description = GetProgressDetail(m_name, m_uuid, m_load_address);
+  return true;
+}
+
+void DynamicLoaderDarwinKernel::KextImageInfo::FindKernel(
+    Target &target, ModuleSpec module_spec) {
+  // The platform's index has to win over the shared module list.
+  PlatformSP platform_sp = target.GetPlatform();
+  if (platform_sp)
+    platform_sp->GetSharedModule(module_spec, target, m_module_sp, nullptr,
+                                 nullptr);
+
+  // Ask the Target to find this file on the local system, if possible. This
+  // will search in the list of currently-loaded files, look in the standard
+  // search paths on the system, and on a Mac it will try calling the
+  // DebugSymbols framework with the UUID to find the binary via its search
+  // methods.
+  if (!m_module_sp)
+    m_module_sp = target.GetOrCreateModule(module_spec, true /* notify */);
+
+  // For the kernel, we really do need an on-disk file copy of the binary to do
+  // anything useful. This will force a call to dsymForUUID if it exists,
+  // instead of depending on the DebugSymbols preferences being set.
+  Status kernel_search_error;
+  if (!m_module_sp || !m_module_sp->GetSymbolFileFileSpec()) {
+    if (PluginManager::DownloadObjectAndSymbolFile(module_spec,
+                                                   kernel_search_error, true)) {
+      if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
+        m_module_sp = std::make_shared<Module>(module_spec.GetFileSpec(),
+                                               target.GetArchitecture());
+      }
     }
+  }
 
-    std::unique_ptr<Progress> progress_up;
-    if (progress)
-      progress->Increment(1, prog_str.GetString().str());
-    else {
-      if (IsKernel())
-        progress_up = std::make_unique<Progress>("Loading kernel",
-                                                 prog_str.GetString().str());
-      else
-        progress_up = std::make_unique<Progress>("Loading kext",
-                                                 prog_str.GetString().str());
+  if (!m_module_sp) {
+    lldb::StreamUP s = target.GetDebugger().GetAsyncErrorStream();
+    s->Printf("WARNING: Unable to locate kernel binary on the debugger "
+              "system.\n");
+    if (kernel_search_error.Fail() && kernel_search_error.AsCString("") &&
+        kernel_search_error.AsCString("")[0] != '\0') {
+      *s << kernel_search_error.AsCString();
     }
+  }
+}
 
-    // Search for the kext on the local filesystem via the UUID
-    if (!m_module_sp && m_uuid.IsValid()) {
-      ModuleSpec module_spec;
-      module_spec.SetTarget(target.shared_from_this());
-      module_spec.GetUUID() = m_uuid;
-      module_spec.GetFileSpec() = FileSpec(m_name);
-
-      if (IsKernel()) {
-        // The platform's index has to win over the shared module list.
-        PlatformSP platform_sp(target.GetPlatform());
-        if (platform_sp) {
-          platform_sp->GetSharedModule(module_spec, target, m_module_sp,
-                                       nullptr, nullptr);
-        }
+bool DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule(
+    Process *process) {
+  if (IsLoaded())
+    return true;
 
-        // Ask the Target to find this file on the local system, if possible.
-        // This will search in the list of currently-loaded files, look in the
-        // standard search paths on the system, and on a Mac it will try
-        // calling the DebugSymbols framework with the UUID to find the binary
-        // via its search methods.
-        if (!m_module_sp) {
-          m_module_sp =
-              target.GetOrCreateModule(module_spec, true /* notify */);
-        }
-      } else {
-        // SymbolLocator asks the platform first, and PlatformDarwinKernel
-        // answers from its kext index, using the bundle ID in the file name.
-        StatisticsMap statistics;
-        ModuleSP shared_module_sp;
-        ModuleList::GetSharedModule(module_spec, shared_module_sp, nullptr,
-                                    nullptr, /*invoke_locate_callback=*/false,
-                                    /*invoke_symbol_locators=*/false);
-        if (!shared_module_sp) {
-          SymbolLocator::Request request;
-          request.module_spec = module_spec;
-          request.platform = target.GetPlatform();
-
-          llvm::Expected<SymbolLocator::Result> located =
-              SymbolLocator::Locate(request, target.GetDebugFileSearchPaths());
-          if (located) {
-            module_spec = located->module_spec;
-            module_spec.SetTarget(target.shared_from_this());
-            statistics = std::move(located->statistics);
-          } else {
-            // The caller reports the kexts that failed to load.
-            llvm::consumeError(located.takeError());
-          }
-        }
+  std::optional<SymbolLocator::Request> request;
+  if (!PrepareSearch(process, request))
+    return false;
 
-        // Already searched, so the Target must not search again.
-        m_module_sp =
-            target.GetOrCreateModule(module_spec, false /* notify */, nullptr,
-                                     /*invoke_symbol_locators=*/false);
-        if (m_module_sp)
-          m_module_sp->GetSymbolLocatorStatistics().merge(statistics);
-      }
+  std::optional<llvm::Expected<SymbolLocator::Result>> located;
+  if (request) {
+    Progress progress(IsKernel() ? "Loading kernel" : "Loading kext",
+                      request->description);
+    if (IsKernel())
+      FindKernel(process->GetTarget(), request->module_spec);
+    else
+      located = SymbolLocator::Locate(
+          *request, process->GetTarget().GetDebugFileSearchPaths());
+  }
 
-      // For the kernel, we really do need an on-disk file copy of the binary
-      // to do anything useful. This will force a call to dsymForUUID if it
-      // exists, instead of depending on the DebugSymbols preferences being
-      // set.
-      Status kernel_search_error;
-      if (IsKernel() &&
-          (!m_module_sp || !m_module_sp->GetSymbolFileFileSpec())) {
-        if (PluginManager::DownloadObjectAndSymbolFile(
-                module_spec, kernel_search_error, true)) {
-          if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
-            m_module_sp = std::make_shared<Module>(module_spec.GetFileSpec(),
-                                                   target.GetArchitecture());
-          }
-        }
-      }
+  return LoadLocatedImage(process, std::move(located));
+}
 
-      if (IsKernel() && !m_module_sp) {
-        lldb::StreamUP s = target.GetDebugger().GetAsyncErrorStream();
-        s->Printf("WARNING: Unable to locate kernel binary on the debugger "
-                  "system.\n");
-        if (kernel_search_error.Fail() && kernel_search_error.AsCString("") &&
-            kernel_search_error.AsCString("")[0] != '\0') {
-          *s << kernel_search_error.AsCString();
-        }
-      }
+bool DynamicLoaderDarwinKernel::KextImageInfo::LoadLocatedImage(
+    Process *process,
+    std::optional<llvm::Expected<SymbolLocator::Result>> located) {
+  Log *log = GetLog(LLDBLog::DynamicLoader);
+  Target &target = process->GetTarget();
+
+  // A kext found in the shared module list has no search result. Its bundle ID
+  // and UUID find it again.
+  ModuleSpec module_spec(FileSpec(m_name), m_uuid);
+  StatisticsMap statistics;
+  if (located) {
+    if (*located) {
+      module_spec = (*located)->module_spec;
+      statistics = std::move((*located)->statistics);
+    } else {
+      // The caller reports the kexts that failed to load.
+      llvm::consumeError(located->takeError());
     }
+  }
 
-    if (m_module_sp && m_uuid.IsValid() && m_module_sp->GetUUID() == m_uuid &&
-        m_module_sp->GetObjectFile()) {
-      if (ObjectFileMachO *ondisk_objfile_macho =
-              llvm::dyn_cast<ObjectFileMachO>(m_module_sp->GetObjectFile())) {
-        if (!IsKernel() && !ondisk_objfile_macho->IsKext()) {
-          // We have a non-kext, non-kernel binary.  If we already have this
-          // loaded in the Target with load addresses, don't re-load it again.
-          ModuleSP existing_module_sp = target.GetImages().FindModule(m_uuid);
-          if (existing_module_sp &&
-              existing_module_sp->IsLoadedInTarget(&target)) {
-            LLDB_LOGF(log,
-                      "'%s' with UUID %s is not a kext or kernel, and is "
-                      "already registered in target, not loading.",
-                      m_name.c_str(), m_uuid.GetAsString().c_str());
-            // It's already loaded, return true.
-            return true;
-          }
+  if (!IsKernel() && !m_module_sp && m_uuid.IsValid()) {
+    module_spec.SetTarget(target.shared_from_this());
+    // The search has run or was not needed, so the Target must not search.
+    m_module_sp =
+        target.GetOrCreateModule(module_spec, false /* notify */, nullptr,
+                                 /*invoke_symbol_locators=*/false);
+    if (m_module_sp)
+      m_module_sp->GetSymbolLocatorStatistics().merge(statistics);
+  }
+
+  if (m_module_sp && m_uuid.IsValid() && m_module_sp->GetUUID() == m_uuid &&
+      m_module_sp->GetObjectFile()) {
+    if (ObjectFileMachO *ondisk_objfile_macho =
+            llvm::dyn_cast<ObjectFileMachO>(m_module_sp->GetObjectFile())) {
+      if (!IsKernel() && !ondisk_objfile_macho->IsKext()) {
+        // We have a non-kext, non-kernel binary.  If we already have this
+        // loaded in the Target with load addresses, don't re-load it again.
+        ModuleSP existing_module_sp = target.GetImages().FindModule(m_uuid);
+        if (existing_module_sp &&
+            existing_module_sp->IsLoadedInTarget(&target)) {
+          LLDB_LOGF(log,
+                    "'%s' with UUID %s is not a kext or kernel, and is "
+                    "already registered in target, not loading.",
+                    m_name.c_str(), m_uuid.GetAsString().c_str());
+          // It's already loaded, return true.
+          return true;
         }
       }
     }
+  }
 
-    // If we managed to find a module, append it to the target's list of
-    // images. If we also have a memory module, require that they have matching
-    // UUIDs
-    if (m_module_sp) {
-      if (m_uuid.IsValid() && m_module_sp->GetUUID() == m_uuid) {
-        target.GetImages().AppendIfNeeded(m_module_sp, false);
-      }
+  // If we managed to find a module, append it to the target's list of
+  // images. If we also have a memory module, require that they have matching
+  // UUIDs
+  if (m_module_sp) {
+    if (m_uuid.IsValid() && m_module_sp->GetUUID() == m_uuid) {
+      target.GetImages().AppendIfNeeded(m_module_sp, false);
     }
   }
 
@@ -1407,29 +1430,66 @@ bool DynamicLoaderDarwinKernel::ParseKextSummaries(
     Progress progress("Loading kext", "", number_of_new_kexts_being_added);
 
     const uint32_t num_of_new_kexts = kext_summaries.size();
-    for (uint32_t new_kext = 0; new_kext < num_of_new_kexts; new_kext++) {
-      if (to_be_added[new_kext]) {
-        KextImageInfo &image_info = kext_summaries[new_kext];
-        if (load_kexts) {
-          if (!image_info.LoadImageUsingMemoryModule(m_process, &progress)) {
-            kexts_failed_to_load.push_back(std::pair<std::string, UUID>(
-                kext_summaries[new_kext].GetName(),
-                kext_summaries[new_kext].GetUUID()));
-            image_info.LoadImageAtFileAddress(m_process);
-          }
+    Target &target = m_process->GetTarget();
+
+    // A search can shell out to a symbol server, so the searches run as one
+    // batch that SymbolLocator may run concurrently. PrepareSearch and
+    // LoadLocatedImage are not thread-safe and run on this thread.
+    std::vector<bool> can_load(num_of_new_kexts, false);
+    std::vector<std::optional<llvm::Expected<SymbolLocator::Result>>> located(
+        num_of_new_kexts);
+    if (load_kexts) {
+      std::vector<SymbolLocator::Request> requests;
+      std::vector<uint32_t> searched;
+      for (uint32_t new_kext = 0; new_kext < num_of_new_kexts; new_kext++) {
+        if (!to_be_added[new_kext] || kext_summaries[new_kext].IsLoaded())
+          continue;
+        std::optional<SymbolLocator::Request> request;
+        can_load[new_kext] =
+            kext_summaries[new_kext].PrepareSearch(m_process, request);
+        if (request) {
+          searched.push_back(new_kext);
+          requests.push_back(std::move(*request));
         }
+      }
 
-        m_known_kexts.push_back(image_info);
+      std::vector<llvm::Expected<SymbolLocator::Result>> results =
+          SymbolLocator::Locate(requests, target.GetDebugFileSearchPaths(),
+                                target.GetParallelModuleLoad());
 
-        if (image_info.GetModule() &&
-            m_process->GetStopID() == image_info.GetProcessStopId())
-          loaded_module_list.AppendIfNeeded(image_info.GetModule());
+      for (auto [new_kext, result] : llvm::zip_equal(searched, results))
+        located[new_kext] = std::move(result);
+    }
 
-        if (log)
-          kext_summaries[new_kext].PutToLog(log);
+    // The install order decides the Target's module order.
+    for (uint32_t new_kext = 0; new_kext < num_of_new_kexts; new_kext++) {
+      if (!to_be_added[new_kext])
+        continue;
+
+      KextImageInfo &image_info = kext_summaries[new_kext];
+      if (load_kexts && !image_info.IsLoaded()) {
+        progress.Increment(1, GetProgressDetail(image_info.GetName(),
+                                                image_info.GetUUID(),
+                                                image_info.GetLoadAddress()));
+        if (!can_load[new_kext] ||
+            !image_info.LoadLocatedImage(m_process,
+                                         std::move(located[new_kext]))) {
+          kexts_failed_to_load.push_back(std::pair<std::string, UUID>(
+              image_info.GetName(), image_info.GetUUID()));
+          image_info.LoadImageAtFileAddress(m_process);
+        }
       }
+
+      m_known_kexts.push_back(image_info);
+
+      if (image_info.GetModule() &&
+          m_process->GetStopID() == image_info.GetProcessStopId())
+        loaded_module_list.AppendIfNeeded(image_info.GetModule());
+
+      if (log)
+        kext_summaries[new_kext].PutToLog(log);
     }
-    m_process->GetTarget().ModulesDidLoad(loaded_module_list);
+    target.ModulesDidLoad(loaded_module_list);
   }
 
   if (number_of_old_kexts_being_removed > 0) {
diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h
index 69dd8ca579158..6512eaf1a7615 100644
--- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h
+++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h
@@ -10,6 +10,7 @@
 #define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_DARWIN_KERNEL_DYNAMICLOADERDARWINKERNEL_H
 
 #include <mutex>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -17,6 +18,7 @@
 #include "lldb/Host/SafeMachO.h"
 
 #include "lldb/Core/Progress.h"
+#include "lldb/Symbol/SymbolLocator.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Utility/FileSpec.h"
@@ -138,8 +140,41 @@ class DynamicLoaderDarwinKernel : public lldb_private::DynamicLoader {
 
     bool LoadImageAtFileAddress(lldb_private::Process *process);
 
-    bool LoadImageUsingMemoryModule(lldb_private::Process *process,
-                                    lldb_private::Progress *progress = nullptr);
+    /// Determine what the search for this binary needs: its UUID, read from
+    /// memory if necessary, and whether the Target or the shared module list
+    /// already has it. For the kernel, also remove any kernel with a different
+    /// UUID from the Target.
+    ///
+    /// This function is not thread-safe. It reads process memory and modifies
+    /// the Target, so concurrent calls require external synchronization.
+    ///
+    /// \param request Set to the search to run, or left empty if none is
+    ///   needed.
+    ///
+    /// \return Whether the binary can be loaded.
+    bool
+    PrepareSearch(lldb_private::Process *process,
+                  std::optional<lldb_private::SymbolLocator::Request> &request);
+
+    /// Add the module to the Target and set its load addresses.
+    ///
+    /// This function is not thread-safe. It modifies the Target, so concurrent
+    /// calls require external synchronization. The order of the calls
+    /// determines the order of the Target's modules.
+    ///
+    /// \param located The search result, or empty if PrepareSearch requested
+    ///   no search.
+    bool LoadLocatedImage(
+        lldb_private::Process *process,
+        std::optional<llvm::Expected<lldb_private::SymbolLocator::Result>>
+            located);
+
+    /// PrepareSearch, the search and LoadLocatedImage for a single binary.
+    bool LoadImageUsingMemoryModule(lldb_private::Process *process);
+
+    /// Searches for the kernel and sets the module, without SymbolLocator.
+    void FindKernel(lldb_private::Target &target,
+                    lldb_private::ModuleSpec module_spec);
 
     bool IsLoaded() { return m_load_process_stop_id != UINT32_MAX; }
 



More information about the lldb-commits mailing list