jasonmolenda created this revision.
jasonmolenda added a reviewer: jingham.
Herald added a subscriber: abidh.
Herald added a project: LLDB.

On the upcoming version of macOS, 10.15 or Catalina, there are userlang kernel 
drivers ("DriverKit" processes) and one unique thing about a DriverKit main 
binary is that it has no start address load command in the same way that a 
normal user process binary has.  lldb uses the entry address ("main") as a 
function address that is never called during the normal execution of the 
program, after the process has started, for doing inferior function calls.  It 
creates a fake stack frame with "main" as the return address, puts a breakpoint 
on main, and uses that breakpoint hit to indicate that the function call 
completed successfully.  Because DriverKit processes don't have a traditional 
main, lldb can't do inferior function calls.

Currently this is found in ThreadPlanCallFunction::ConstructorSetup - it finds 
the Target's main executable, asks for its entry address.  This patch moves 
that code into a new Target::GetEntryPointAddress method which tries the main 
executable, but if that executable fails, steps through every other Module 
asking if there is an entry address for that module.  The patch also changes 
ObjectFileMachO to look for the function _dyld_start in the dyld binary -- 
another function that is never called -- and uses that as a backup entry 
address function.

Testing this is a bit difficult - these DriverKit userland drivers are not 
launched like normal processes, we can't run a test DriverKit from the 
testsuite under lldb.  I'm still thinking about this; to test this by hand I 
#if 0'ed out ObjectFileMachO's normal entry address logic (looking at LC_MAIN 
load command etc) so it had to fall back to the _dyld_start one.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D64897

Files:
  include/lldb/Target/Target.h
  source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
  source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
  source/Target/Target.cpp
  source/Target/ThreadPlanCallFunction.cpp

Index: source/Target/ThreadPlanCallFunction.cpp
===================================================================
--- source/Target/ThreadPlanCallFunction.cpp
+++ source/Target/ThreadPlanCallFunction.cpp
@@ -65,38 +65,17 @@
     return false;
   }
 
-  Module *exe_module = GetTarget().GetExecutableModulePointer();
+  m_start_addr = GetTarget().GetEntryPointAddress(error);
 
-  if (exe_module == nullptr) {
-    m_constructor_errors.Printf(
-        "Can't execute code without an executable module.");
-    if (log)
-      log->Printf("ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this),
-                  m_constructor_errors.GetData());
+  if (log && error.Fail()) {
+    m_constructor_errors.Printf("%s", error.AsCString());
+    log->Printf("ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this),
+                 m_constructor_errors.GetData());
     return false;
-  } else {
-    ObjectFile *objectFile = exe_module->GetObjectFile();
-    if (!objectFile) {
-      m_constructor_errors.Printf(
-          "Could not find object file for module \"%s\".",
-          exe_module->GetFileSpec().GetFilename().AsCString());
+  }
 
-      if (log)
-        log->Printf("ThreadPlanCallFunction(%p): %s.",
-                    static_cast<void *>(this), m_constructor_errors.GetData());
-      return false;
-    }
-
-    m_start_addr = objectFile->GetEntryPointAddress();
-    if (!m_start_addr.IsValid()) {
-      m_constructor_errors.Printf(
-          "Could not find entry point address for executable module \"%s\".",
-          exe_module->GetFileSpec().GetFilename().AsCString());
-      if (log)
-        log->Printf("ThreadPlanCallFunction(%p): %s.",
-                    static_cast<void *>(this), m_constructor_errors.GetData());
-      return false;
-    }
+  if (!m_start_addr.IsValid()) {
+    return false;
   }
 
   start_load_addr = m_start_addr.GetLoadAddress(&GetTarget());
Index: source/Target/Target.cpp
===================================================================
--- source/Target/Target.cpp
+++ source/Target/Target.cpp
@@ -2448,6 +2448,44 @@
   return address;
 }
 
+lldb_private::Address Target::GetEntryPointAddress(Status &err) {
+  err.Clear();
+  Address entry_addr;
+  Module *exe_module = GetExecutableModulePointer();
+
+  if (!exe_module || !exe_module->GetObjectFile()) {
+    err.SetErrorStringWithFormat("No primary executable found");
+  } else {
+    entry_addr = exe_module->GetObjectFile()->GetEntryPointAddress();
+    if (!entry_addr.IsValid()) {
+      err.SetErrorStringWithFormat(
+         "Could not find entry point address for executable module \"%s\".",
+         exe_module->GetFileSpec().GetFilename().AsCString());
+    }
+  }
+
+  if (!entry_addr.IsValid()) {
+    const ModuleList &modules = GetImages();
+    const size_t num_images = modules.GetSize();
+    for (size_t idx = 0; idx < num_images; ++idx) {
+      ModuleSP module_sp(modules.GetModuleAtIndex(idx));
+      if (module_sp && module_sp->GetObjectFile()) {
+        entry_addr = module_sp->GetObjectFile()->GetEntryPointAddress();
+        if (entry_addr.IsValid()) {
+          // Clear out any old error messages from the original
+          // main-executable-binary search; one of the other modules
+          // was able to provide an address.
+          err.Clear();
+          break;
+        }
+      }
+    }
+  }
+
+  return entry_addr;
+}
+
+
 lldb::addr_t Target::GetCallableLoadAddress(lldb::addr_t load_addr,
                                             AddressClass addr_class) const {
   auto arch_plugin = GetArchitecturePlugin();
Index: source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
===================================================================
--- source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
+++ source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
@@ -73,6 +73,8 @@
 
   bool IsExecutable() const override;
 
+  bool IsDynamicLoader() const;
+
   uint32_t GetAddressByteSize() const override;
 
   lldb_private::AddressClass GetAddressClass(lldb::addr_t file_addr) override;
Index: source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
===================================================================
--- source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -1144,6 +1144,10 @@
   return m_header.filetype == MH_EXECUTE;
 }
 
+bool ObjectFileMachO::IsDynamicLoader() const {
+  return m_header.filetype == MH_DYLINKER;
+}
+
 uint32_t ObjectFileMachO::GetAddressByteSize() const {
   return m_data.GetAddressByteSize();
 }
@@ -5177,8 +5181,10 @@
   // return that. If m_entry_point_address is valid it means we've found it
   // already, so return the cached value.
 
-  if (!IsExecutable() || m_entry_point_address.IsValid())
+  if ((!IsExecutable() && !IsDynamicLoader()) || 
+      m_entry_point_address.IsValid()) {
     return m_entry_point_address;
+  }
 
   // Otherwise, look for the UnixThread or Thread command.  The data for the
   // Thread command is given in /usr/include/mach-o.h, but it is basically:
@@ -5300,6 +5306,17 @@
       offset = cmd_offset + load_cmd.cmdsize;
     }
 
+    if (start_address == LLDB_INVALID_ADDRESS && IsDynamicLoader()) {
+      if (GetSymtab()) {
+        Symbol *dyld_start_sym = GetSymtab()->FindFirstSymbolWithNameAndType(
+                      ConstString("_dyld_start"), SymbolType::eSymbolTypeCode, 
+                      Symtab::eDebugAny, Symtab::eVisibilityAny);
+        if (dyld_start_sym && dyld_start_sym->GetAddress().IsValid()) {
+          start_address = dyld_start_sym->GetAddress().GetFileAddress();
+        }
+      }
+    }
+
     if (start_address != LLDB_INVALID_ADDRESS) {
       // We got the start address from the load commands, so now resolve that
       // address in the sections of this ObjectFile:
Index: include/lldb/Target/Target.h
===================================================================
--- include/lldb/Target/Target.h
+++ include/lldb/Target/Target.h
@@ -1116,6 +1116,24 @@
 
   lldb::addr_t GetPersistentSymbol(ConstString name);
 
+  /// This method will return the address of the starting function for
+  /// this binary, e.g. main() or its equivalent.  This can be used as
+  /// an address of a function that is not called once a binary has 
+  /// started running - e.g. as a return address for inferior function
+  /// calls that are unambiguous completion of the function call, not
+  /// called during the course of the inferior function code running.
+  ///
+  /// If no entry point can be found, an invalid address is returned.
+  ///
+  /// \param [out] err
+  ///     This object will be set to failure if no entry address could
+  ///     be found, and may contain a helpful error message.
+  //
+  /// \return
+  ///     Returns the entry address for this program, LLDB_INVALID_ADDRESS
+  ///     if none can be found.
+  lldb_private::Address GetEntryPointAddress(Status &err);
+
   // Target Stop Hooks
   class StopHook : public UserID {
   public:
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to