forked from OSchip/llvm-project
<rdar://problem/12649160>
Added the ability to debug through your process exec'ing itself to the same architecture. llvm-svn: 169340
This commit is contained in:
parent
d31802c1f6
commit
90ba81150e
|
@ -67,6 +67,7 @@ public:
|
|||
/// eStopReasonWatchpoint 1 watchpoint id
|
||||
/// eStopReasonSignal 1 unix signal number
|
||||
/// eStopReasonException N exception data
|
||||
/// eStopReasonExec 0
|
||||
/// eStopReasonPlanComplete 0
|
||||
//--------------------------------------------------------------------------
|
||||
uint64_t
|
||||
|
|
|
@ -93,7 +93,20 @@ public:
|
|||
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.
|
||||
///
|
||||
|
|
|
@ -2054,6 +2054,27 @@ public:
|
|||
DidAttach () {}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// 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.
|
||||
///
|
||||
|
|
|
@ -129,6 +129,9 @@ public:
|
|||
static lldb::StopInfoSP
|
||||
CreateStopReasonWithException (Thread &thread, const char *description);
|
||||
|
||||
static lldb::StopInfoSP
|
||||
CreateStopReasonWithExec (Thread &thread);
|
||||
|
||||
static lldb::ValueObjectSP
|
||||
GetReturnValueObject (lldb::StopInfoSP &stop_info_sp);
|
||||
|
||||
|
|
|
@ -390,6 +390,8 @@ public:
|
|||
void
|
||||
DeleteCurrentProcess ();
|
||||
|
||||
void
|
||||
CleanupProcess ();
|
||||
//------------------------------------------------------------------
|
||||
/// Dump a description of this object to a Stream.
|
||||
///
|
||||
|
|
|
@ -176,6 +176,7 @@ namespace lldb {
|
|||
eStopReasonWatchpoint,
|
||||
eStopReasonSignal,
|
||||
eStopReasonException,
|
||||
eStopReasonExec, // Program was re-exec'ed
|
||||
eStopReasonPlanComplete
|
||||
} StopReason;
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
/// eStopReasonWatchpoint 1 watchpoint id
|
||||
/// eStopReasonSignal 1 unix signal number
|
||||
/// eStopReasonException N exception data
|
||||
/// eStopReasonExec 0
|
||||
/// eStopReasonPlanComplete 0
|
||||
//--------------------------------------------------------------------------
|
||||
") GetStopReasonDataAtIndex;
|
||||
|
|
|
@ -147,6 +147,7 @@ SBThread::GetStopReasonDataCount ()
|
|||
case eStopReasonInvalid:
|
||||
case eStopReasonNone:
|
||||
case eStopReasonTrace:
|
||||
case eStopReasonExec:
|
||||
case eStopReasonPlanComplete:
|
||||
// There is no data for these stop reasons.
|
||||
return 0;
|
||||
|
@ -204,6 +205,7 @@ SBThread::GetStopReasonDataAtIndex (uint32_t idx)
|
|||
case eStopReasonInvalid:
|
||||
case eStopReasonNone:
|
||||
case eStopReasonTrace:
|
||||
case eStopReasonExec:
|
||||
case eStopReasonPlanComplete:
|
||||
// There is no data for these stop reasons.
|
||||
return 0;
|
||||
|
@ -336,6 +338,14 @@ SBThread::GetStopDescription (char *dst, size_t dst_len)
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -139,7 +139,8 @@ DynamicLoaderMacOSXDYLD::DynamicLoaderMacOSXDYLD (Process* process) :
|
|||
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 @@ DynamicLoaderMacOSXDYLD::DidLaunch ()
|
|||
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 @@ DynamicLoaderMacOSXDYLD::LocateDYLD()
|
|||
// 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 @@ DynamicLoaderMacOSXDYLD::LocateDYLD()
|
|||
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 @@ DynamicLoaderMacOSXDYLD::LocateDYLD()
|
|||
}
|
||||
// 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)
|
||||
|
|
|
@ -62,6 +62,9 @@ public:
|
|||
virtual void
|
||||
DidLaunch ();
|
||||
|
||||
virtual bool
|
||||
ProcessDidExec ();
|
||||
|
||||
virtual lldb::ThreadPlanSP
|
||||
GetStepThroughTrampolinePlan (lldb_private::Thread &thread,
|
||||
bool stop_others);
|
||||
|
@ -372,6 +375,7 @@ protected:
|
|||
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);
|
||||
|
|
|
@ -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 @@ StopInfoMachException::CreateStopReasonWithMachException
|
|||
|
||||
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
|
||||
|
|
|
@ -1058,6 +1058,17 @@ ProcessGDBRemote::DidAttach ()
|
|||
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 ()
|
||||
{
|
||||
|
|
|
@ -113,6 +113,9 @@ public:
|
|||
virtual void
|
||||
DidAttach ();
|
||||
|
||||
virtual void
|
||||
DoDidExec ();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// PluginInterface protocol
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -5111,3 +5111,22 @@ Process::Flush ()
|
|||
{
|
||||
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 ();
|
||||
}
|
||||
|
|
|
@ -147,6 +147,7 @@ StackFrameList::ResetCurrentInlinedDepth ()
|
|||
{
|
||||
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;
|
||||
|
|
|
@ -862,6 +862,49 @@ private:
|
|||
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 @@ StopInfo::CreateStopReasonWithException (Thread &thread, const char *description
|
|||
return StopInfoSP (new StopInfoException (thread, description));
|
||||
}
|
||||
|
||||
StopInfoSP
|
||||
StopInfo::CreateStopReasonWithExec (Thread &thread)
|
||||
{
|
||||
return StopInfoSP (new StopInfoExec (thread));
|
||||
}
|
||||
|
||||
ValueObjectSP
|
||||
StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp)
|
||||
{
|
||||
|
|
|
@ -129,6 +129,21 @@ Target::Dump (Stream *s, lldb::DescriptionLevel description_level)
|
|||
}
|
||||
}
|
||||
|
||||
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 ()
|
||||
{
|
||||
|
@ -140,16 +155,8 @@ Target::DeleteCurrentProcess ()
|
|||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1574,6 +1574,7 @@ Thread::StopReasonAsCString (lldb::StopReason reason)
|
|||
case eStopReasonWatchpoint: return "watchpoint";
|
||||
case eStopReasonSignal: return "signal";
|
||||
case eStopReasonException: return "exception";
|
||||
case eStopReasonExec: return "exec";
|
||||
case eStopReasonPlanComplete: return "plan complete";
|
||||
}
|
||||
|
||||
|
|
|
@ -139,6 +139,14 @@ ThreadPlanBase::ShouldStop (Event *event_ptr)
|
|||
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))
|
||||
{
|
||||
|
|
|
@ -335,6 +335,7 @@ ThreadPlanStepInRange::PlanExplainsStop ()
|
|||
case eStopReasonWatchpoint:
|
||||
case eStopReasonSignal:
|
||||
case eStopReasonException:
|
||||
case eStopReasonExec:
|
||||
{
|
||||
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
|
||||
if (log)
|
||||
|
|
|
@ -251,6 +251,7 @@ ThreadPlanStepOut::PlanExplainsStop ()
|
|||
case eStopReasonWatchpoint:
|
||||
case eStopReasonSignal:
|
||||
case eStopReasonException:
|
||||
case eStopReasonExec:
|
||||
return false;
|
||||
|
||||
default:
|
||||
|
|
|
@ -319,6 +319,7 @@ ThreadPlanStepOverRange::PlanExplainsStop ()
|
|||
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.");
|
||||
|
|
|
@ -291,6 +291,7 @@ ThreadPlanStepUntil::AnalyzeStop()
|
|||
case eStopReasonWatchpoint:
|
||||
case eStopReasonSignal:
|
||||
case eStopReasonException:
|
||||
case eStopReasonExec:
|
||||
m_explains_stop = false;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -806,6 +806,7 @@ Driver::UpdateSelectedThread ()
|
|||
case eStopReasonWatchpoint:
|
||||
case eStopReasonSignal:
|
||||
case eStopReasonException:
|
||||
case eStopReasonExec:
|
||||
if (!other_thread.IsValid())
|
||||
other_thread = thread;
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue