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

darwin-debug is used to launch a binary while setting the current working 
directory/env vars/architecture, exec'ing it stopped so lldb can attach to it.  
This is used on macOS to launch a binary in a new Terminal window via 
AppleScript.  If lldb attaches to the inferior, stopped on the first 
instruction, all is good.  But if lldb attaches before the inferior is running, 
it will attach to darwin-debug, see the Exec mach exception when the inferior 
is run, but instead of the normal suspend count of 1 at this point, because 
darwin-debug asked it to be launch suspended, the suspend count will be 2.  
debugserver needs to double-resume the inferior to make it run.

This patch adds a specially named segment to darwin-debug so that it can be 
detected unambiguously by debugserver.

In debugserver, if we attach to a process with the specially named segment, set 
a flag that will be checked the next time we received an Exec mach exception.  
When that Exec comes in, set a flag that will be checked the next time we go to 
resume the inferior to indicate that we need to resume it twice to allow it to 
run.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D72963

Files:
  lldb/tools/darwin-debug/CMakeLists.txt
  lldb/tools/debugserver/source/MacOSX/MachProcess.mm
  lldb/tools/debugserver/source/MacOSX/MachTask.h
  lldb/tools/debugserver/source/MacOSX/MachTask.mm

Index: lldb/tools/debugserver/source/MacOSX/MachTask.mm
===================================================================
--- lldb/tools/debugserver/source/MacOSX/MachTask.mm
+++ lldb/tools/debugserver/source/MacOSX/MachTask.mm
@@ -73,7 +73,8 @@
 //----------------------------------------------------------------------
 MachTask::MachTask(MachProcess *process)
     : m_process(process), m_task(TASK_NULL), m_vm_memory(),
-      m_exception_thread(0), m_exception_port(MACH_PORT_NULL) {
+      m_exception_thread(0), m_exception_port(MACH_PORT_NULL),
+      m_exec_will_be_suspended(false), m_do_double_resume(false) {
   memset(&m_exc_port_info, 0, sizeof(m_exc_port_info));
 }
 
@@ -107,6 +108,14 @@
   err = BasicInfo(task, &task_info);
 
   if (err.Success()) {
+    if (m_do_double_resume && task_info.suspend_count == 2) {
+      err = ::task_resume(task);
+      if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+        err.LogThreaded("::task_resume double-resume after exec-start-stopped "
+                        "( target_task = 0x%4.4x )", task);
+    }
+    m_do_double_resume = false;
+      
     // task_resume isn't counted like task_suspend calls are, are, so if the
     // task is not suspended, don't try and resume it since it is already
     // running
@@ -139,6 +148,8 @@
   m_task = TASK_NULL;
   m_exception_thread = 0;
   m_exception_port = MACH_PORT_NULL;
+  m_exec_will_be_suspended = false;
+  m_do_double_resume = false;
 }
 
 //----------------------------------------------------------------------
@@ -665,6 +676,9 @@
     err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )",
                     task_self, exception_port);
 
+  m_exec_will_be_suspended = false;
+  m_do_double_resume = false;
+
   return err.Status();
 }
 
@@ -974,4 +988,14 @@
 void MachTask::TaskPortChanged(task_t task)
 {
   m_task = task;
+
+  // If we've just exec'd to a new process, and it
+  // is started suspended, we'll need to do two
+  // task_resume's to get the inferior process to
+  // continue.
+  if (m_exec_will_be_suspended)
+    m_do_double_resume = true;
+  else
+    m_do_double_resume = false;
+  m_exec_will_be_suspended = false;
 }
Index: lldb/tools/debugserver/source/MacOSX/MachTask.h
===================================================================
--- lldb/tools/debugserver/source/MacOSX/MachTask.h
+++ lldb/tools/debugserver/source/MacOSX/MachTask.h
@@ -85,6 +85,7 @@
   const MachProcess *Process() const { return m_process; }
 
   nub_size_t PageSize();
+  void TaskWillExecProcessesSuspended() { m_exec_will_be_suspended = true; }
 
 protected:
   MachProcess *m_process; // The mach process that owns this MachTask
@@ -97,6 +98,12 @@
                                 // need it
   mach_port_t m_exception_port; // Exception port on which we will receive child
                                 // exceptions
+  bool m_exec_will_be_suspended; // If this task exec's another process, that
+                                // process will be launched suspended and we will
+                                // need to execute one extra Resume to get it
+                                // to progress from dyld_start.
+  bool m_do_double_resume;      // next time we task_resume(), do it twice to
+                                // fix a too-high suspend count.
 
   typedef std::map<mach_vm_address_t, size_t> allocation_collection;
   allocation_collection m_allocations;
Index: lldb/tools/debugserver/source/MacOSX/MachProcess.mm
===================================================================
--- lldb/tools/debugserver/source/MacOSX/MachProcess.mm
+++ lldb/tools/debugserver/source/MacOSX/MachProcess.mm
@@ -730,6 +730,8 @@
       this_seg.nsects = seg.nsects;
       this_seg.flags = seg.flags;
       inf.segments.push_back(this_seg);
+      if (this_seg.name == "ExecExtraSuspend")
+        m_task.TaskWillExecProcessesSuspended();
     }
     if (lc.cmd == LC_SEGMENT_64) {
       struct segment_command_64 seg;
@@ -751,6 +753,8 @@
       this_seg.nsects = seg.nsects;
       this_seg.flags = seg.flags;
       inf.segments.push_back(this_seg);
+      if (this_seg.name == "ExecExtraSuspend")
+        m_task.TaskWillExecProcessesSuspended();
     }
     if (lc.cmd == LC_UUID) {
       struct uuid_command uuidcmd;
Index: lldb/tools/darwin-debug/CMakeLists.txt
===================================================================
--- lldb/tools/darwin-debug/CMakeLists.txt
+++ lldb/tools/darwin-debug/CMakeLists.txt
@@ -1,3 +1,11 @@
+
+# Create an LC_SEGMENT with the special name ExecExtraSuspend which
+# debugserver can detect - it tells debugserver that it will exec a
+# process and that process will start suspended, so debugserver will
+# need to double-resume it to make it run.  A random file is copied
+# into the segment.
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,ExecExtraSuspend,ExecExtraSuspend,${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt")
+
 add_lldb_tool(darwin-debug ADD_TO_FRAMEWORK
   darwin-debug.cpp
 )
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to