<rdar://problem/12649160>
Added the ability to debug through your process exec'ing itself to the same architecture.
git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@169340 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/lldb/API/SBThread.h b/include/lldb/API/SBThread.h
index 947ef14..a77bd5b 100644
--- a/include/lldb/API/SBThread.h
+++ b/include/lldb/API/SBThread.h
@@ -67,6 +67,7 @@
/// eStopReasonWatchpoint 1 watchpoint id
/// eStopReasonSignal 1 unix signal number
/// eStopReasonException N exception data
+ /// eStopReasonExec 0
/// eStopReasonPlanComplete 0
//--------------------------------------------------------------------------
uint64_t
diff --git a/include/lldb/Target/DynamicLoader.h b/include/lldb/Target/DynamicLoader.h
index ec982fd..2c87657 100644
--- a/include/lldb/Target/DynamicLoader.h
+++ b/include/lldb/Target/DynamicLoader.h
@@ -93,7 +93,20 @@
DidLaunch () = 0;
-
+ //------------------------------------------------------------------
+ /// Helper function that can be used to detect when a process has
+ /// called exec and is now a new and different process. This can
+ /// be called when necessary to try and detect the exec. The process
+ /// might be able to answer this question, but sometimes it might
+ /// not be able and the dynamic loader often knows what the program
+ /// entry point is. So the process and the dynamic loader can work
+ /// together to detect this.
+ //------------------------------------------------------------------
+ virtual bool
+ ProcessDidExec ()
+ {
+ return false;
+ }
//------------------------------------------------------------------
/// Get whether the process should stop when images change.
///
diff --git a/include/lldb/Target/Process.h b/include/lldb/Target/Process.h
index d9a087c..2409bdb 100644
--- a/include/lldb/Target/Process.h
+++ b/include/lldb/Target/Process.h
@@ -2055,6 +2055,27 @@
//------------------------------------------------------------------
+ /// Called after a process re-execs itself.
+ ///
+ /// Allow Process plug-ins to execute some code after a process has
+ /// exec'ed itself. Subclasses typically should override DoDidExec()
+ /// as the lldb_private::Process class needs to remove its dynamic
+ /// loader, runtime, ABI and other plug-ins, as well as unload all
+ /// shared libraries.
+ //------------------------------------------------------------------
+ virtual void
+ DidExec ();
+
+ //------------------------------------------------------------------
+ /// Subclasses of Process should implement this function if they
+ /// need to do anything after a process exec's itself.
+ //------------------------------------------------------------------
+ virtual void
+ DoDidExec ()
+ {
+ }
+
+ //------------------------------------------------------------------
/// Called before launching to a process.
///
/// Allow Process plug-ins to execute some code before launching a
diff --git a/include/lldb/Target/StopInfo.h b/include/lldb/Target/StopInfo.h
index cf2719c..0bb15fb 100644
--- a/include/lldb/Target/StopInfo.h
+++ b/include/lldb/Target/StopInfo.h
@@ -129,6 +129,9 @@
static lldb::StopInfoSP
CreateStopReasonWithException (Thread &thread, const char *description);
+ static lldb::StopInfoSP
+ CreateStopReasonWithExec (Thread &thread);
+
static lldb::ValueObjectSP
GetReturnValueObject (lldb::StopInfoSP &stop_info_sp);
diff --git a/include/lldb/Target/Target.h b/include/lldb/Target/Target.h
index e19b910..5306c32 100644
--- a/include/lldb/Target/Target.h
+++ b/include/lldb/Target/Target.h
@@ -390,6 +390,8 @@
void
DeleteCurrentProcess ();
+ void
+ CleanupProcess ();
//------------------------------------------------------------------
/// Dump a description of this object to a Stream.
///
diff --git a/include/lldb/lldb-enumerations.h b/include/lldb/lldb-enumerations.h
index ce28142..99c5673 100644
--- a/include/lldb/lldb-enumerations.h
+++ b/include/lldb/lldb-enumerations.h
@@ -176,6 +176,7 @@
eStopReasonWatchpoint,
eStopReasonSignal,
eStopReasonException,
+ eStopReasonExec, // Program was re-exec'ed
eStopReasonPlanComplete
} StopReason;
diff --git a/scripts/Python/interface/SBThread.i b/scripts/Python/interface/SBThread.i
index 76a014a..0401ec1 100644
--- a/scripts/Python/interface/SBThread.i
+++ b/scripts/Python/interface/SBThread.i
@@ -88,6 +88,7 @@
/// eStopReasonWatchpoint 1 watchpoint id
/// eStopReasonSignal 1 unix signal number
/// eStopReasonException N exception data
+ /// eStopReasonExec 0
/// eStopReasonPlanComplete 0
//--------------------------------------------------------------------------
") GetStopReasonDataAtIndex;
diff --git a/source/API/SBThread.cpp b/source/API/SBThread.cpp
index 4dd29d7..e083939 100644
--- a/source/API/SBThread.cpp
+++ b/source/API/SBThread.cpp
@@ -147,6 +147,7 @@
case eStopReasonInvalid:
case eStopReasonNone:
case eStopReasonTrace:
+ case eStopReasonExec:
case eStopReasonPlanComplete:
// There is no data for these stop reasons.
return 0;
@@ -204,6 +205,7 @@
case eStopReasonInvalid:
case eStopReasonNone:
case eStopReasonTrace:
+ case eStopReasonExec:
case eStopReasonPlanComplete:
// There is no data for these stop reasons.
return 0;
@@ -336,6 +338,14 @@
}
break;
+ case eStopReasonExec:
+ {
+ char exc_desc[] = "exec";
+ stop_desc = exc_desc;
+ stop_desc_len = sizeof(exc_desc); // Include the NULL byte for size
+ }
+ break;
+
default:
break;
}
diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp
index 466e664..8cedda6 100644
--- a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp
+++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp
@@ -139,7 +139,8 @@
m_break_id(LLDB_INVALID_BREAK_ID),
m_dyld_image_infos(),
m_dyld_image_infos_stop_id (UINT32_MAX),
- m_mutex(Mutex::eMutexTypeRecursive)
+ m_mutex(Mutex::eMutexTypeRecursive),
+ m_process_image_addr_is_all_images_infos (false)
{
}
@@ -179,6 +180,54 @@
SetNotificationBreakpoint ();
}
+bool
+DynamicLoaderMacOSXDYLD::ProcessDidExec ()
+{
+ if (m_process)
+ {
+ // If we are stopped after an exec, we will have only one thread...
+ if (m_process->GetThreadList().GetSize() == 1)
+ {
+ // We know if a process has exec'ed if our "m_dyld_all_image_infos_addr"
+ // value differs from the Process' image info address. When a process
+ // execs itself it might cause a change if ASLR is enabled.
+ const addr_t shlib_addr = m_process->GetImageInfoAddress ();
+ if (m_process_image_addr_is_all_images_infos == true && shlib_addr != m_dyld_all_image_infos_addr)
+ {
+ // The image info address from the process is the 'dyld_all_image_infos'
+ // address and it has changed.
+ return true;
+ }
+
+ if (m_process_image_addr_is_all_images_infos == false && shlib_addr == m_dyld.address)
+ {
+ // The image info address from the process is the mach_header
+ // address for dyld and it has changed.
+ return true;
+ }
+
+ // ASLR might be disabled and dyld could have ended up in the same
+ // location. We should try and detect if we are stopped at '_dyld_start'
+ ThreadSP thread_sp (m_process->GetThreadList().GetThreadAtIndex(0));
+ if (thread_sp)
+ {
+ lldb::StackFrameSP frame_sp (thread_sp->GetStackFrameAtIndex(0));
+ if (frame_sp)
+ {
+ const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol;
+ if (symbol)
+ {
+ if (symbol->GetName() == ConstString("_dyld_start"))
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
//----------------------------------------------------------------------
// Clear out the state of this class.
@@ -224,7 +273,6 @@
// mach header for dyld, or it might point to the
// dyld_all_image_infos struct
const addr_t shlib_addr = m_process->GetImageInfoAddress ();
-
ByteOrder byte_order = m_process->GetTarget().GetArchitecture().GetByteOrder();
uint8_t buf[4];
DataExtractor data (buf, sizeof(buf), byte_order, 4);
@@ -239,6 +287,7 @@
case llvm::MachO::HeaderMagic64:
case llvm::MachO::HeaderMagic32Swapped:
case llvm::MachO::HeaderMagic64Swapped:
+ m_process_image_addr_is_all_images_infos = false;
return ReadDYLDInfoFromMemoryAndSetNotificationCallback(shlib_addr);
default:
@@ -247,6 +296,7 @@
}
// Maybe it points to the all image infos?
m_dyld_all_image_infos_addr = shlib_addr;
+ m_process_image_addr_is_all_images_infos = true;
}
if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS)
diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h
index f7e9ac5..2e0d250 100644
--- a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h
+++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h
@@ -62,6 +62,9 @@
virtual void
DidLaunch ();
+ virtual bool
+ ProcessDidExec ();
+
virtual lldb::ThreadPlanSP
GetStepThroughTrampolinePlan (lldb_private::Thread &thread,
bool stop_others);
@@ -372,6 +375,7 @@
uint32_t m_dyld_image_infos_stop_id; // The process stop ID that "m_dyld_image_infos" is valid for
mutable lldb_private::Mutex m_mutex;
lldb_private::Process::Notifications m_notification_callbacks;
+ bool m_process_image_addr_is_all_images_infos;
private:
DISALLOW_COPY_AND_ASSIGN (DynamicLoaderMacOSXDYLD);
diff --git a/source/Plugins/Process/Utility/StopInfoMachException.cpp b/source/Plugins/Process/Utility/StopInfoMachException.cpp
index 7db5729..5999e13 100644
--- a/source/Plugins/Process/Utility/StopInfoMachException.cpp
+++ b/source/Plugins/Process/Utility/StopInfoMachException.cpp
@@ -16,6 +16,8 @@
#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/StreamString.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/DynamicLoader.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
@@ -300,7 +302,39 @@
case 5: // EXC_SOFTWARE
if (exc_code == 0x10003) // EXC_SOFT_SIGNAL
+ {
+ if (exc_sub_code == 5)
+ {
+ // On MacOSX, a SIGTRAP can signify that a process has called
+ // exec, so we should check with our dynamic loader to verify.
+ ProcessSP process_sp (thread.GetProcess());
+ if (process_sp)
+ {
+ DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader();
+ if (dynamic_loader && dynamic_loader->ProcessDidExec())
+ {
+ // The program was re-exec'ed
+ return StopInfo::CreateStopReasonWithExec (thread);
+ }
+// if (!process_did_exec)
+// {
+// // We have a SIGTRAP, make sure we didn't exec by checking
+// // for the PC being at "_dyld_start"...
+// lldb::StackFrameSP frame_sp (thread.GetStackFrameAtIndex(0));
+// if (frame_sp)
+// {
+// const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol;
+// if (symbol)
+// {
+// if (symbol->GetName() == ConstString("_dyld_start"))
+// process_did_exec = true;
+// }
+// }
+// }
+ }
+ }
return StopInfo::CreateStopReasonWithSignal (thread, exc_sub_code);
+ }
break;
case 6: // EXC_BREAKPOINT
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 475e6d2..7bc0300 100644
--- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1058,6 +1058,17 @@
DidLaunchOrAttach ();
}
+void
+ProcessGDBRemote::DoDidExec ()
+{
+ // The process exec'ed itself, figure out the dynamic loader, etc...
+ BuildDynamicRegisterInfo (true);
+ m_gdb_comm.ResetDiscoverableSettings();
+ DidLaunchOrAttach ();
+}
+
+
+
Error
ProcessGDBRemote::WillResume ()
{
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 4cf2a4f..d19878f 100644
--- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -113,6 +113,9 @@
virtual void
DidAttach ();
+ virtual void
+ DoDidExec ();
+
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp
index 0f43040..5455a24 100644
--- a/source/Target/Process.cpp
+++ b/source/Target/Process.cpp
@@ -5111,3 +5111,22 @@
{
m_thread_list.Flush();
}
+
+void
+Process::DidExec ()
+{
+ Target &target = GetTarget();
+ target.CleanupProcess ();
+ ModuleList unloaded_modules (target.GetImages());
+ target.ModulesDidUnload (unloaded_modules);
+ target.GetSectionLoadList().Clear();
+ m_dynamic_checkers_ap.reset();
+ m_abi_sp.reset();
+ m_os_ap.reset();
+ m_dyld_ap.reset();
+ m_image_tokens.clear();
+ m_allocated_memory_cache.Clear();
+ m_language_runtimes.clear();
+ DoDidExec();
+ CompleteAttach ();
+}
diff --git a/source/Target/StackFrameList.cpp b/source/Target/StackFrameList.cpp
index 082fddd..0a45d33 100644
--- a/source/Target/StackFrameList.cpp
+++ b/source/Target/StackFrameList.cpp
@@ -147,6 +147,7 @@
{
case eStopReasonWatchpoint:
case eStopReasonException:
+ case eStopReasonExec:
case eStopReasonSignal:
// In all these cases we want to stop in the deepest most frame.
m_current_inlined_pc = curr_pc;
diff --git a/source/Target/StopInfo.cpp b/source/Target/StopInfo.cpp
index bae1e4a..de76748 100644
--- a/source/Target/StopInfo.cpp
+++ b/source/Target/StopInfo.cpp
@@ -862,6 +862,49 @@
ThreadPlanSP m_plan_sp;
ValueObjectSP m_return_valobj_sp;
};
+
+class StopInfoExec : public StopInfo
+{
+public:
+
+ StopInfoExec (Thread &thread) :
+ StopInfo (thread, LLDB_INVALID_UID),
+ m_performed_action (false)
+ {
+ }
+
+ virtual
+ ~StopInfoExec ()
+ {
+ }
+
+ virtual StopReason
+ GetStopReason () const
+ {
+ return eStopReasonExec;
+ }
+
+ virtual const char *
+ GetDescription ()
+ {
+ return "exec";
+ }
+protected:
+protected:
+
+ virtual void
+ PerformAction (Event *event_ptr)
+ {
+ // Only perform the action once
+ if (m_performed_action)
+ return;
+ m_performed_action = true;
+ m_thread.GetProcess()->DidExec();
+ }
+
+ bool m_performed_action;
+};
+
} // namespace lldb_private
StopInfoSP
@@ -906,6 +949,12 @@
return StopInfoSP (new StopInfoException (thread, description));
}
+StopInfoSP
+StopInfo::CreateStopReasonWithExec (Thread &thread)
+{
+ return StopInfoSP (new StopInfoExec (thread));
+}
+
ValueObjectSP
StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp)
{
diff --git a/source/Target/Target.cpp b/source/Target/Target.cpp
index 77daef6..433ddd0 100644
--- a/source/Target/Target.cpp
+++ b/source/Target/Target.cpp
@@ -130,6 +130,21 @@
}
void
+Target::CleanupProcess ()
+{
+ // Do any cleanup of the target we need to do between process instances.
+ // NB It is better to do this before destroying the process in case the
+ // clean up needs some help from the process.
+ m_breakpoint_list.ClearAllBreakpointSites();
+ m_internal_breakpoint_list.ClearAllBreakpointSites();
+ // Disable watchpoints just on the debugger side.
+ Mutex::Locker locker;
+ this->GetWatchpointList().GetListMutex(locker);
+ DisableAllWatchpoints(false);
+ ClearAllWatchpointHitCounts();
+}
+
+void
Target::DeleteCurrentProcess ()
{
if (m_process_sp.get())
@@ -140,16 +155,8 @@
m_process_sp->Finalize();
- // Do any cleanup of the target we need to do between process instances.
- // NB It is better to do this before destroying the process in case the
- // clean up needs some help from the process.
- m_breakpoint_list.ClearAllBreakpointSites();
- m_internal_breakpoint_list.ClearAllBreakpointSites();
- // Disable watchpoints just on the debugger side.
- Mutex::Locker locker;
- this->GetWatchpointList().GetListMutex(locker);
- DisableAllWatchpoints(false);
- ClearAllWatchpointHitCounts();
+ CleanupProcess ();
+
m_process_sp.reset();
}
}
diff --git a/source/Target/Thread.cpp b/source/Target/Thread.cpp
index 4c6995c..7a5aafb 100644
--- a/source/Target/Thread.cpp
+++ b/source/Target/Thread.cpp
@@ -1574,6 +1574,7 @@
case eStopReasonWatchpoint: return "watchpoint";
case eStopReasonSignal: return "signal";
case eStopReasonException: return "exception";
+ case eStopReasonExec: return "exec";
case eStopReasonPlanComplete: return "plan complete";
}
diff --git a/source/Target/ThreadPlanBase.cpp b/source/Target/ThreadPlanBase.cpp
index f8e4b6a..ebf4686 100644
--- a/source/Target/ThreadPlanBase.cpp
+++ b/source/Target/ThreadPlanBase.cpp
@@ -139,6 +139,14 @@
m_thread.DiscardThreadPlans(false);
return true;
+ case eStopReasonExec:
+ // If we crashed, discard thread plans and stop. Don't force the discard, however,
+ // since on rerun the target may clean up this exception and continue normally from there.
+ if (log)
+ log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 " (exec.)", m_thread.GetID());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+
case eStopReasonSignal:
if (stop_info_sp->ShouldStop(event_ptr))
{
diff --git a/source/Target/ThreadPlanStepInRange.cpp b/source/Target/ThreadPlanStepInRange.cpp
index ca24172..f4339a4 100644
--- a/source/Target/ThreadPlanStepInRange.cpp
+++ b/source/Target/ThreadPlanStepInRange.cpp
@@ -335,6 +335,7 @@
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
+ case eStopReasonExec:
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
if (log)
diff --git a/source/Target/ThreadPlanStepOut.cpp b/source/Target/ThreadPlanStepOut.cpp
index 540539e..5343324 100644
--- a/source/Target/ThreadPlanStepOut.cpp
+++ b/source/Target/ThreadPlanStepOut.cpp
@@ -251,6 +251,7 @@
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
+ case eStopReasonExec:
return false;
default:
diff --git a/source/Target/ThreadPlanStepOverRange.cpp b/source/Target/ThreadPlanStepOverRange.cpp
index ba21eda..66754d2 100644
--- a/source/Target/ThreadPlanStepOverRange.cpp
+++ b/source/Target/ThreadPlanStepOverRange.cpp
@@ -319,6 +319,7 @@
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
+ case eStopReasonExec:
default:
if (log)
log->PutCString ("ThreadPlanStepInRange got asked if it explains the stop for some reason other than step.");
diff --git a/source/Target/ThreadPlanStepUntil.cpp b/source/Target/ThreadPlanStepUntil.cpp
index 1e454e7..95b0a35 100644
--- a/source/Target/ThreadPlanStepUntil.cpp
+++ b/source/Target/ThreadPlanStepUntil.cpp
@@ -291,6 +291,7 @@
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
+ case eStopReasonExec:
m_explains_stop = false;
break;
default:
diff --git a/tools/driver/Driver.cpp b/tools/driver/Driver.cpp
index 7a4eded..29fa37d 100644
--- a/tools/driver/Driver.cpp
+++ b/tools/driver/Driver.cpp
@@ -806,6 +806,7 @@
case eStopReasonWatchpoint:
case eStopReasonSignal:
case eStopReasonException:
+ case eStopReasonExec:
if (!other_thread.IsValid())
other_thread = thread;
break;