forked from OSchip/llvm-project
Add a new Process plugin for Linux.
This component is still at an early stage, but allows for simple breakpoint/step-over operations and basic process control. The makefiles are set up to build the plugin under Linux only. llvm-svn: 109318
This commit is contained in:
parent
ddb46efcca
commit
e6f9f66b39
|
@ -33,7 +33,6 @@ USEDLIBS = lldbAPI.a \
|
|||
lldbPluginObjectFileELF.a \
|
||||
lldbPluginSymbolFileDWARF.a \
|
||||
lldbPluginSymbolFileSymtab.a \
|
||||
lldbPluginSymbolVendorMacOSX.a \
|
||||
lldbSymbol.a \
|
||||
lldbTarget.a \
|
||||
lldbUtility.a \
|
||||
|
@ -65,11 +64,13 @@ ifeq ($(HOST_OS),Darwin)
|
|||
lldbPluginObjectContainerUniversalMachO.a \
|
||||
lldbPluginObjectFileMachO.a \
|
||||
lldbPluginProcessGDBRemote.a \
|
||||
lldbPluginUtility.a
|
||||
lldbPluginUtility.a \
|
||||
lldbSymbolVendorMaxOSX.a
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),Linux)
|
||||
USEDLIBS += lldbHostLinux.a
|
||||
USEDLIBS += lldbHostLinux.a \
|
||||
lldbPluginProcessLinux.a
|
||||
endif
|
||||
|
||||
include $(LEVEL)/Makefile.common
|
||||
|
|
|
@ -12,10 +12,17 @@ LLDB_LEVEL := ../..
|
|||
include $(LLDB_LEVEL)/../../Makefile.config
|
||||
|
||||
|
||||
DIRS := ABI/MacOSX-i386 ABI/SysV-x86_64 Disassembler/llvm ObjectContainer/BSD-Archive ObjectFile/ELF SymbolFile/DWARF SymbolFile/Symtab
|
||||
DIRS := ABI/MacOSX-i386 ABI/SysV-x86_64 Disassembler/llvm \
|
||||
ObjectContainer/BSD-Archive ObjectFile/ELF SymbolFile/DWARF \
|
||||
SymbolFile/Symtab
|
||||
|
||||
ifeq ($(HOST_OS),Darwin)
|
||||
DIRS += DynamicLoader/MacOSX-DYLD ObjectContainer/Universal-Mach-O ObjectFile/Mach-O Process/gdb-remote Process/Utility SymbolVendor/MacOSX
|
||||
DIRS += DynamicLoader/MacOSX-DYLD ObjectContainer/Universal-Mach-O \
|
||||
ObjectFile/Mach-O Process/gdb-remote Process/Utility SymbolVendor/MacOSX
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),Linux)
|
||||
DIRS += Process/Linux
|
||||
endif
|
||||
|
||||
include $(LLDB_LEVEL)/Makefile
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
//===-- LinuxThread.cpp -----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
// C Includes
|
||||
// C++ Includes
|
||||
// Other libraries and framework includes
|
||||
#include "lldb/Target/Process.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
|
||||
#include "LinuxThread.h"
|
||||
#include "ProcessLinux.h"
|
||||
#include "ProcessMonitor.h"
|
||||
#include "RegisterContextLinux_x86_64.h"
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
LinuxThread::LinuxThread(Process &process, lldb::tid_t tid)
|
||||
: Thread(process, tid),
|
||||
m_frame_ap(0),
|
||||
m_register_ap(0),
|
||||
m_note(eNone)
|
||||
{
|
||||
ArchSpec arch = process.GetTarget().GetArchitecture();
|
||||
|
||||
switch (arch.GetGenericCPUType())
|
||||
{
|
||||
default:
|
||||
assert(false && "CPU type not supported!");
|
||||
break;
|
||||
|
||||
case ArchSpec::eCPU_x86_64:
|
||||
m_register_ap.reset(new RegisterContextLinux_x86_64(*this, NULL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessMonitor &
|
||||
LinuxThread::GetMonitor()
|
||||
{
|
||||
ProcessLinux *process = static_cast<ProcessLinux*>(CalculateProcess());
|
||||
return process->GetMonitor();
|
||||
}
|
||||
|
||||
void
|
||||
LinuxThread::RefreshStateAfterStop()
|
||||
{
|
||||
}
|
||||
|
||||
const char *
|
||||
LinuxThread::GetInfo()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
LinuxThread::GetStackFrameCount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
lldb::StackFrameSP
|
||||
LinuxThread::GetStackFrameAtIndex(uint32_t idx)
|
||||
{
|
||||
if (idx == 0)
|
||||
{
|
||||
RegisterContextLinux *regs = GetRegisterContext();
|
||||
StackFrame *frame = new StackFrame(
|
||||
idx, *this, regs->GetFP(), regs->GetPC());
|
||||
return lldb::StackFrameSP(frame);
|
||||
}
|
||||
else
|
||||
return lldb::StackFrameSP();
|
||||
}
|
||||
|
||||
RegisterContextLinux *
|
||||
LinuxThread::GetRegisterContext()
|
||||
{
|
||||
return m_register_ap.get();
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxThread::SaveFrameZeroState(RegisterCheckpoint &checkpoint)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxThread::RestoreSaveFrameZero(const RegisterCheckpoint &checkpoint)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RegisterContextLinux *
|
||||
LinuxThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame)
|
||||
{
|
||||
return new RegisterContextLinux_x86_64(*this, frame);
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxThread::GetRawStopReason(StopInfo *stop_info)
|
||||
{
|
||||
stop_info->Clear();
|
||||
|
||||
switch (m_note)
|
||||
{
|
||||
default:
|
||||
stop_info->SetStopReasonToNone();
|
||||
break;
|
||||
|
||||
case eBreak:
|
||||
stop_info->SetStopReasonWithBreakpointSiteID(m_breakpoint->GetID());
|
||||
break;
|
||||
|
||||
case eTrace:
|
||||
stop_info->SetStopReasonToTrace();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxThread::WillResume(lldb::StateType resume_state)
|
||||
{
|
||||
SetResumeState(resume_state);
|
||||
return Thread::WillResume(resume_state);
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxThread::Resume()
|
||||
{
|
||||
lldb::StateType resume_state = GetResumeState();
|
||||
ProcessMonitor &monitor = GetMonitor();
|
||||
bool status;
|
||||
|
||||
switch (GetResumeState())
|
||||
{
|
||||
default:
|
||||
assert(false && "Unexpected state for resume!");
|
||||
status = false;
|
||||
break;
|
||||
|
||||
case lldb::eStateSuspended:
|
||||
// FIXME: Implement process suspension.
|
||||
status = false;
|
||||
|
||||
case lldb::eStateRunning:
|
||||
SetState(resume_state);
|
||||
status = monitor.Resume(GetID());
|
||||
break;
|
||||
|
||||
case lldb::eStateStepping:
|
||||
SetState(resume_state);
|
||||
status = GetRegisterContext()->HardwareSingleStep(true);
|
||||
break;
|
||||
}
|
||||
|
||||
m_note = eNone;
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
LinuxThread::BreakNotify()
|
||||
{
|
||||
bool status;
|
||||
|
||||
status = GetRegisterContext()->UpdateAfterBreakpoint();
|
||||
assert(status && "Breakpoint update failed!");
|
||||
|
||||
// With our register state restored, resolve the breakpoint object
|
||||
// corresponding to our current PC.
|
||||
lldb::addr_t pc = GetRegisterContext()->GetPC();
|
||||
lldb::BreakpointSiteSP bp_site =
|
||||
GetProcess().GetBreakpointSiteList().FindByAddress(pc);
|
||||
assert(bp_site && bp_site->ValidForThisThread(this));
|
||||
|
||||
m_note = eBreak;
|
||||
m_breakpoint = bp_site;
|
||||
}
|
||||
|
||||
void
|
||||
LinuxThread::TraceNotify()
|
||||
{
|
||||
m_note = eTrace;
|
||||
}
|
||||
|
||||
void
|
||||
LinuxThread::ExitNotify()
|
||||
{
|
||||
m_note = eExit;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
//===-- LinuxThread.h -------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_LinuxThread_H_
|
||||
#define liblldb_LinuxThread_H_
|
||||
|
||||
// C Includes
|
||||
// C++ Includes
|
||||
#include <memory>
|
||||
|
||||
// Other libraries and framework includes
|
||||
#include "lldb/Target/Thread.h"
|
||||
#include "RegisterContextLinux.h"
|
||||
|
||||
class ProcessMonitor;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// @class LinuxThread
|
||||
// @brief Abstraction of a linux process (thread).
|
||||
class LinuxThread
|
||||
: public lldb_private::Thread
|
||||
{
|
||||
public:
|
||||
LinuxThread(lldb_private::Process &process, lldb::tid_t tid);
|
||||
|
||||
void
|
||||
RefreshStateAfterStop();
|
||||
|
||||
bool
|
||||
WillResume(lldb::StateType resume_state);
|
||||
|
||||
const char *
|
||||
GetInfo();
|
||||
|
||||
uint32_t
|
||||
GetStackFrameCount();
|
||||
|
||||
lldb::StackFrameSP
|
||||
GetStackFrameAtIndex(uint32_t idx);
|
||||
|
||||
RegisterContextLinux *
|
||||
GetRegisterContext();
|
||||
|
||||
bool
|
||||
SaveFrameZeroState(RegisterCheckpoint &checkpoint);
|
||||
|
||||
bool
|
||||
RestoreSaveFrameZero(const RegisterCheckpoint &checkpoint);
|
||||
|
||||
RegisterContextLinux *
|
||||
CreateRegisterContextForFrame(lldb_private::StackFrame *frame);
|
||||
|
||||
bool
|
||||
GetRawStopReason(StopInfo *stop_info);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// These methods form a specialized interface to linux threads.
|
||||
//
|
||||
bool Resume();
|
||||
|
||||
void BreakNotify();
|
||||
void TraceNotify();
|
||||
void ExitNotify();
|
||||
|
||||
private:
|
||||
std::auto_ptr<lldb_private::StackFrame> m_frame_ap;
|
||||
std::auto_ptr<RegisterContextLinux> m_register_ap;
|
||||
|
||||
lldb::BreakpointSiteSP m_breakpoint;
|
||||
|
||||
enum Notification {
|
||||
eNone,
|
||||
eBreak,
|
||||
eTrace,
|
||||
eExit
|
||||
};
|
||||
|
||||
Notification m_note;
|
||||
|
||||
ProcessMonitor &GetMonitor();
|
||||
};
|
||||
|
||||
#endif // #ifndef liblldb_LinuxThread_H_
|
|
@ -0,0 +1,14 @@
|
|||
##===- source/Plugins/Process/Linux/Makefile ---------------*- Makefile -*-===##
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
##===----------------------------------------------------------------------===##
|
||||
|
||||
LLDB_LEVEL := ../../../..
|
||||
LIBRARYNAME := lldbPluginProcessLinux
|
||||
BUILD_ARCHIVE = 1
|
||||
|
||||
include $(LLDB_LEVEL)/Makefile
|
|
@ -0,0 +1,443 @@
|
|||
//===-- ProcessLinux.cpp ----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// C Includes
|
||||
// C++ Includes
|
||||
// Other libraries and framework includes
|
||||
#include "lldb/Core/PluginManager.h"
|
||||
#include "lldb/Host/Host.h"
|
||||
#include "lldb/Symbol/ObjectFile.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
|
||||
#include "ProcessLinux.h"
|
||||
#include "ProcessMonitor.h"
|
||||
#include "LinuxThread.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Static functions.
|
||||
|
||||
Process*
|
||||
ProcessLinux::CreateInstance(Target& target, Listener &listener)
|
||||
{
|
||||
return new ProcessLinux(target, listener);
|
||||
}
|
||||
|
||||
void
|
||||
ProcessLinux::Initialize()
|
||||
{
|
||||
static bool g_initialized = false;
|
||||
|
||||
if (!g_initialized)
|
||||
{
|
||||
PluginManager::RegisterPlugin(GetPluginNameStatic(),
|
||||
GetPluginDescriptionStatic(),
|
||||
CreateInstance);
|
||||
g_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ProcessLinux::Terminate()
|
||||
{
|
||||
}
|
||||
|
||||
const char *
|
||||
ProcessLinux::GetPluginNameStatic()
|
||||
{
|
||||
return "plugin.process.linux";
|
||||
}
|
||||
|
||||
const char *
|
||||
ProcessLinux::GetPluginDescriptionStatic()
|
||||
{
|
||||
return "Process plugin for Linux";
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constructors and destructors.
|
||||
|
||||
ProcessLinux::ProcessLinux(Target& target, Listener &listener)
|
||||
: Process(target, listener),
|
||||
m_monitor(NULL),
|
||||
m_module(NULL)
|
||||
{
|
||||
// FIXME: Putting this code in the ctor and saving the byte order in a
|
||||
// member variable is a hack to avoid const qual issues in GetByteOrder.
|
||||
ObjectFile *obj_file = GetTarget().GetExecutableModule()->GetObjectFile();
|
||||
m_byte_order = obj_file->GetByteOrder();
|
||||
}
|
||||
|
||||
ProcessLinux::~ProcessLinux()
|
||||
{
|
||||
delete m_monitor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Process protocol.
|
||||
|
||||
bool
|
||||
ProcessLinux::CanDebug(Target &target)
|
||||
{
|
||||
// For now we are just making sure the file exists for a given module
|
||||
ModuleSP exe_module_sp(target.GetExecutableModule());
|
||||
if (exe_module_sp.get())
|
||||
return exe_module_sp->GetFileSpec().Exists();
|
||||
return false;
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DoAttachToProcessWithID(lldb::pid_t pid)
|
||||
{
|
||||
return Error(1, eErrorTypeGeneric);
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DoLaunch(Module *module,
|
||||
char const *argv[],
|
||||
char const *envp[],
|
||||
const char *stdin_path,
|
||||
const char *stdout_path,
|
||||
const char *stderr_path)
|
||||
{
|
||||
Error error;
|
||||
assert(m_monitor == NULL);
|
||||
|
||||
SetPrivateState(eStateLaunching);
|
||||
m_monitor = new ProcessMonitor(this, module,
|
||||
argv, envp,
|
||||
stdin_path, stdout_path, stderr_path,
|
||||
error);
|
||||
|
||||
m_module = module;
|
||||
|
||||
if (!error.Success())
|
||||
return error;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessLinux::DidLaunch()
|
||||
{
|
||||
UpdateLoadedSections();
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DoResume()
|
||||
{
|
||||
assert(GetPrivateState() == eStateStopped && "Bad state for DoResume!");
|
||||
|
||||
// Set our state to running. This ensures inferior threads do not post a
|
||||
// state change first.
|
||||
SetPrivateState(eStateRunning);
|
||||
|
||||
bool did_resume = false;
|
||||
uint32_t thread_count = m_thread_list.GetSize(false);
|
||||
for (uint32_t i = 0; i < thread_count; ++i)
|
||||
{
|
||||
LinuxThread *thread = static_cast<LinuxThread*>(
|
||||
m_thread_list.GetThreadAtIndex(i, false).get());
|
||||
did_resume = thread->Resume() || did_resume;
|
||||
}
|
||||
assert(did_resume && "Process resume failed!");
|
||||
|
||||
return Error();
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DoHalt()
|
||||
{
|
||||
return Error(1, eErrorTypeGeneric);
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DoDetach()
|
||||
{
|
||||
return Error(1, eErrorTypeGeneric);
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DoSignal(int signal)
|
||||
{
|
||||
return Error(1, eErrorTypeGeneric);
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DoDestroy()
|
||||
{
|
||||
Error error;
|
||||
|
||||
if (!HasExited())
|
||||
{
|
||||
// Shut down the private state thread as we will syncronize with events
|
||||
// ourselves. Discard all current thread plans.
|
||||
PausePrivateStateThread();
|
||||
GetThreadList().DiscardThreadPlans();
|
||||
|
||||
// Bringing the inferior into limbo will be caught by our monitor
|
||||
// thread, in turn updating the process state.
|
||||
if (!m_monitor->BringProcessIntoLimbo())
|
||||
{
|
||||
error.SetErrorToGenericError();
|
||||
error.SetErrorString("Process termination failed.");
|
||||
return error;
|
||||
}
|
||||
|
||||
// Wait for the event to arrive. This guaranteed to be an exit event.
|
||||
StateType state;
|
||||
EventSP event;
|
||||
do {
|
||||
state = WaitForStateChangedEventsPrivate(NULL, event);
|
||||
} while (state != eStateExited);
|
||||
|
||||
// Restart standard event handling and send the process the final kill,
|
||||
// driving it out of limbo.
|
||||
ResumePrivateStateThread();
|
||||
}
|
||||
|
||||
if (kill(m_monitor->GetPID(), SIGKILL))
|
||||
error.SetErrorToErrno();
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessLinux::SendMessage(const ProcessMessage &message)
|
||||
{
|
||||
Mutex::Locker lock(m_message_mutex);
|
||||
m_message_queue.push(message);
|
||||
|
||||
switch (message.GetKind())
|
||||
{
|
||||
default:
|
||||
SetPrivateState(eStateStopped);
|
||||
break;
|
||||
|
||||
case ProcessMessage::eExitMessage:
|
||||
SetExitStatus(message.GetExitStatus(), NULL);
|
||||
break;
|
||||
|
||||
case ProcessMessage::eSignalMessage:
|
||||
SetExitStatus(-1, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ProcessLinux::RefreshStateAfterStop()
|
||||
{
|
||||
Mutex::Locker lock(m_message_mutex);
|
||||
if (m_message_queue.empty())
|
||||
return;
|
||||
|
||||
ProcessMessage &message = m_message_queue.front();
|
||||
|
||||
// Resolve the thread this message corresponds to.
|
||||
lldb::tid_t tid = message.GetTID();
|
||||
LinuxThread *thread = static_cast<LinuxThread*>(
|
||||
GetThreadList().FindThreadByID(tid, false).get());
|
||||
|
||||
switch (message.GetKind())
|
||||
{
|
||||
default:
|
||||
assert(false && "Unexpected message kind!");
|
||||
break;
|
||||
|
||||
case ProcessMessage::eExitMessage:
|
||||
case ProcessMessage::eSignalMessage:
|
||||
thread->ExitNotify();
|
||||
break;
|
||||
|
||||
case ProcessMessage::eTraceMessage:
|
||||
thread->TraceNotify();
|
||||
break;
|
||||
|
||||
case ProcessMessage::eBreakpointMessage:
|
||||
thread->BreakNotify();
|
||||
break;
|
||||
}
|
||||
|
||||
m_message_queue.pop();
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessLinux::IsAlive()
|
||||
{
|
||||
StateType state = GetPrivateState();
|
||||
return state != eStateExited && state != eStateInvalid;
|
||||
}
|
||||
|
||||
size_t
|
||||
ProcessLinux::DoReadMemory(addr_t vm_addr,
|
||||
void *buf, size_t size, Error &error)
|
||||
{
|
||||
return m_monitor->ReadMemory(vm_addr, buf, size, error);
|
||||
}
|
||||
|
||||
size_t
|
||||
ProcessLinux::DoWriteMemory(addr_t vm_addr, const void *buf, size_t size,
|
||||
Error &error)
|
||||
{
|
||||
return m_monitor->WriteMemory(vm_addr, buf, size, error);
|
||||
}
|
||||
|
||||
addr_t
|
||||
ProcessLinux::DoAllocateMemory(size_t size, uint32_t permissions,
|
||||
Error &error)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr_t
|
||||
ProcessLinux::AllocateMemory(size_t size, uint32_t permissions, Error &error)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DoDeallocateMemory(lldb::addr_t ptr)
|
||||
{
|
||||
return Error(1, eErrorTypeGeneric);
|
||||
}
|
||||
|
||||
size_t
|
||||
ProcessLinux::GetSoftwareBreakpointTrapOpcode(BreakpointSite* bp_site)
|
||||
{
|
||||
static const uint8_t g_i386_opcode[] = { 0xCC };
|
||||
|
||||
ArchSpec arch = GetTarget().GetArchitecture();
|
||||
const uint8_t *opcode = NULL;
|
||||
size_t opcode_size = 0;
|
||||
|
||||
switch (arch.GetGenericCPUType())
|
||||
{
|
||||
default:
|
||||
assert(false && "CPU type not supported!");
|
||||
break;
|
||||
|
||||
case ArchSpec::eCPU_i386:
|
||||
case ArchSpec::eCPU_x86_64:
|
||||
opcode = g_i386_opcode;
|
||||
opcode_size = sizeof(g_i386_opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
bp_site->SetTrapOpcode(opcode, opcode_size);
|
||||
return opcode_size;
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::EnableBreakpoint(BreakpointSite *bp_site)
|
||||
{
|
||||
return EnableSoftwareBreakpoint(bp_site);
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::DisableBreakpoint(BreakpointSite *bp_site)
|
||||
{
|
||||
return DisableSoftwareBreakpoint(bp_site);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ProcessLinux::UpdateThreadListIfNeeded()
|
||||
{
|
||||
// Do not allow recursive updates.
|
||||
return m_thread_list.GetSize(false);
|
||||
}
|
||||
|
||||
ByteOrder
|
||||
ProcessLinux::GetByteOrder() const
|
||||
{
|
||||
// FIXME: We should be able to extract this value directly. See comment in
|
||||
// ProcessLinux().
|
||||
return m_byte_order;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ProcessInterface protocol.
|
||||
|
||||
const char *
|
||||
ProcessLinux::GetPluginName()
|
||||
{
|
||||
return "process.linux";
|
||||
}
|
||||
|
||||
const char *
|
||||
ProcessLinux::GetShortPluginName()
|
||||
{
|
||||
return "process.linux";
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ProcessLinux::GetPluginVersion()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessLinux::GetPluginCommandHelp(const char *command, Stream *strm)
|
||||
{
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessLinux::ExecutePluginCommand(Args &command, Stream *strm)
|
||||
{
|
||||
return Error(1, eErrorTypeGeneric);
|
||||
}
|
||||
|
||||
Log *
|
||||
ProcessLinux::EnablePluginLogging(Stream *strm, Args &command)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Utility functions.
|
||||
|
||||
void
|
||||
ProcessLinux::UpdateLoadedSections()
|
||||
{
|
||||
ObjectFile *obj_file = m_module->GetObjectFile();
|
||||
SectionList *sections = obj_file->GetSectionList();
|
||||
|
||||
// FIXME: SectionList provides iterator types, but no begin/end methods.
|
||||
size_t num_sections = sections->GetSize();
|
||||
for (unsigned i = 0; i < num_sections; ++i)
|
||||
{
|
||||
Section *section = sections->GetSectionAtIndex(i).get();
|
||||
|
||||
lldb::addr_t new_load_addr = section->GetFileAddress();
|
||||
lldb::addr_t old_load_addr = GetSectionLoadAddress(section);
|
||||
|
||||
if (old_load_addr == LLDB_INVALID_ADDRESS ||
|
||||
old_load_addr != new_load_addr)
|
||||
SectionLoaded(section, new_load_addr);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessLinux::HasExited()
|
||||
{
|
||||
switch (GetPrivateState())
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case eStateUnloaded:
|
||||
case eStateCrashed:
|
||||
case eStateDetached:
|
||||
case eStateExited:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
//===-- ProcessLinux.h ------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_ProcessLinux_H_
|
||||
#define liblldb_ProcessLinux_H_
|
||||
|
||||
// C Includes
|
||||
|
||||
// C++ Includes
|
||||
#include <queue>
|
||||
|
||||
// Other libraries and framework includes
|
||||
#include "lldb/Target/Process.h"
|
||||
#include "ProcessMessage.h"
|
||||
|
||||
class ProcessMonitor;
|
||||
|
||||
class ProcessLinux :
|
||||
public lldb_private::Process
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------
|
||||
// Static functions.
|
||||
//------------------------------------------------------------------
|
||||
static Process*
|
||||
CreateInstance(lldb_private::Target& target,
|
||||
lldb_private::Listener &listener);
|
||||
|
||||
static void
|
||||
Initialize();
|
||||
|
||||
static void
|
||||
Terminate();
|
||||
|
||||
static const char *
|
||||
GetPluginNameStatic();
|
||||
|
||||
static const char *
|
||||
GetPluginDescriptionStatic();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Constructors and destructors
|
||||
//------------------------------------------------------------------
|
||||
ProcessLinux(lldb_private::Target& target,
|
||||
lldb_private::Listener &listener);
|
||||
|
||||
virtual
|
||||
~ProcessLinux();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Process protocol.
|
||||
//------------------------------------------------------------------
|
||||
virtual bool
|
||||
CanDebug(lldb_private::Target &target);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoAttachToProcessWithID(lldb::pid_t pid);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoLaunch(lldb_private::Module *module,
|
||||
char const *argv[],
|
||||
char const *envp[],
|
||||
const char *stdin_path,
|
||||
const char *stdout_path,
|
||||
const char *stderr_path);
|
||||
|
||||
virtual void
|
||||
DidLaunch();
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoResume();
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoHalt();
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoDetach();
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoSignal(int signal);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoDestroy();
|
||||
|
||||
virtual void
|
||||
RefreshStateAfterStop();
|
||||
|
||||
virtual bool
|
||||
IsAlive();
|
||||
|
||||
virtual size_t
|
||||
DoReadMemory(lldb::addr_t vm_addr,
|
||||
void *buf,
|
||||
size_t size,
|
||||
lldb_private::Error &error);
|
||||
|
||||
virtual size_t
|
||||
DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
|
||||
lldb_private::Error &error);
|
||||
|
||||
virtual lldb::addr_t
|
||||
DoAllocateMemory(size_t size, uint32_t permissions,
|
||||
lldb_private::Error &error);
|
||||
|
||||
lldb::addr_t
|
||||
AllocateMemory(size_t size, uint32_t permissions,
|
||||
lldb_private::Error &error);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DoDeallocateMemory(lldb::addr_t ptr);
|
||||
|
||||
virtual size_t
|
||||
GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site);
|
||||
|
||||
virtual lldb_private::Error
|
||||
EnableBreakpoint(lldb_private::BreakpointSite *bp_site);
|
||||
|
||||
virtual lldb_private::Error
|
||||
DisableBreakpoint(lldb_private::BreakpointSite *bp_site);
|
||||
|
||||
virtual uint32_t
|
||||
UpdateThreadListIfNeeded();
|
||||
|
||||
virtual lldb::ByteOrder
|
||||
GetByteOrder() const;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// PluginInterface protocol
|
||||
//------------------------------------------------------------------
|
||||
virtual const char *
|
||||
GetPluginName();
|
||||
|
||||
virtual const char *
|
||||
GetShortPluginName();
|
||||
|
||||
virtual uint32_t
|
||||
GetPluginVersion();
|
||||
|
||||
virtual void
|
||||
GetPluginCommandHelp(const char *command, lldb_private::Stream *strm);
|
||||
|
||||
virtual lldb_private::Error
|
||||
ExecutePluginCommand(lldb_private::Args &command,
|
||||
lldb_private::Stream *strm);
|
||||
|
||||
virtual lldb_private::Log *
|
||||
EnablePluginLogging(lldb_private::Stream *strm,
|
||||
lldb_private::Args &command);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ProcessLinux internal API.
|
||||
|
||||
/// Registers the given message with this process.
|
||||
void SendMessage(const ProcessMessage &message);
|
||||
|
||||
ProcessMonitor &GetMonitor() { return *m_monitor; }
|
||||
|
||||
private:
|
||||
/// Target byte order.
|
||||
lldb::ByteOrder m_byte_order;
|
||||
|
||||
/// Process monitor;
|
||||
ProcessMonitor *m_monitor;
|
||||
|
||||
/// The module we are executing.
|
||||
lldb_private::Module *m_module;
|
||||
|
||||
/// Message queue notifying this instance of inferior process state changes.
|
||||
lldb_private::Mutex m_message_mutex;
|
||||
std::queue<ProcessMessage> m_message_queue;
|
||||
|
||||
/// Updates the loaded sections provided by the executable.
|
||||
///
|
||||
/// FIXME: It would probably be better to delegate this task to the
|
||||
/// DynamicLoader plugin, when we have one.
|
||||
void UpdateLoadedSections();
|
||||
|
||||
/// Returns true if the process has exited.
|
||||
bool HasExited();
|
||||
};
|
||||
|
||||
#endif // liblldb_MacOSXProcess_H_
|
|
@ -0,0 +1,88 @@
|
|||
//===-- ProcessMessage.h ----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_ProcessMessage_H_
|
||||
#define liblldb_ProcessMessage_H_
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "lldb/lldb-defines.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
|
||||
class ProcessMessage
|
||||
{
|
||||
public:
|
||||
|
||||
/// The type of signal this message can correspond to.
|
||||
enum Kind
|
||||
{
|
||||
eInvalidMessage,
|
||||
eExitMessage,
|
||||
eLimboMessage,
|
||||
eSignalMessage,
|
||||
eTraceMessage,
|
||||
eBreakpointMessage
|
||||
};
|
||||
|
||||
ProcessMessage()
|
||||
: m_kind(eInvalidMessage),
|
||||
m_tid(LLDB_INVALID_PROCESS_ID),
|
||||
m_data(0) { }
|
||||
|
||||
Kind GetKind() const { return m_kind; }
|
||||
|
||||
lldb::tid_t GetTID() const { return m_tid; }
|
||||
|
||||
static ProcessMessage Exit(lldb::tid_t tid, int status) {
|
||||
return ProcessMessage(tid, eExitMessage, status);
|
||||
}
|
||||
|
||||
static ProcessMessage Limbo(lldb::tid_t tid, int status) {
|
||||
return ProcessMessage(tid, eLimboMessage, status);
|
||||
}
|
||||
|
||||
static ProcessMessage Signal(lldb::tid_t tid, int signum) {
|
||||
return ProcessMessage(tid, eSignalMessage, signum);
|
||||
}
|
||||
|
||||
static ProcessMessage Trace(lldb::tid_t tid) {
|
||||
return ProcessMessage(tid, eTraceMessage);
|
||||
}
|
||||
|
||||
static ProcessMessage Break(lldb::tid_t tid) {
|
||||
return ProcessMessage(tid, eBreakpointMessage);
|
||||
}
|
||||
|
||||
int GetExitStatus() const {
|
||||
assert(GetKind() == eExitMessage || GetKind() == eLimboMessage);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
int GetSignal() const {
|
||||
assert(GetKind() == eSignalMessage);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
int GetStopStatus() const {
|
||||
assert(GetKind() == eSignalMessage);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
private:
|
||||
ProcessMessage(lldb::tid_t tid, Kind kind, int data = 0)
|
||||
: m_kind(kind),
|
||||
m_tid(tid),
|
||||
m_data(data) { }
|
||||
|
||||
Kind m_kind;
|
||||
lldb::tid_t m_tid;
|
||||
int m_data;
|
||||
};
|
||||
|
||||
#endif // #ifndef liblldb_ProcessMessage_H_
|
|
@ -0,0 +1,925 @@
|
|||
//===-- ProcessMonitor.cpp ------------------------------------ -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// C Includes
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
// C++ Includes
|
||||
// Other libraries and framework includes
|
||||
#include "lldb/Core/Error.h"
|
||||
#include "lldb/Core/Scalar.h"
|
||||
#include "lldb/Host/Host.h"
|
||||
#include "lldb/Target/Thread.h"
|
||||
#include "lldb/Target/RegisterContext.h"
|
||||
#include "lldb/Utility/PseudoTerminal.h"
|
||||
|
||||
#include "LinuxThread.h"
|
||||
#include "ProcessLinux.h"
|
||||
#include "ProcessMonitor.h"
|
||||
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Static implementations of ProcessMonitor::ReadMemory and
|
||||
// ProcessMonitor::WriteMemory. This enables mutual recursion between these
|
||||
// functions without needed to go thru the thread funnel.
|
||||
|
||||
static size_t
|
||||
DoReadMemory(lldb::pid_t pid, unsigned word_size,
|
||||
lldb::addr_t vm_addr, void *buf, size_t size, Error &error)
|
||||
{
|
||||
unsigned char *dst = static_cast<unsigned char*>(buf);
|
||||
size_t bytes_read;
|
||||
size_t remainder;
|
||||
long data;
|
||||
|
||||
for (bytes_read = 0; bytes_read < size; bytes_read += remainder)
|
||||
{
|
||||
errno = 0;
|
||||
data = ptrace(PTRACE_PEEKDATA, pid, vm_addr, NULL);
|
||||
|
||||
if (data == -1L && errno)
|
||||
{
|
||||
error.SetErrorToErrno();
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
remainder = size - bytes_read;
|
||||
remainder = remainder > word_size ? word_size : remainder;
|
||||
for (unsigned i = 0; i < remainder; ++i)
|
||||
dst[i] = ((data >> i*8) & 0xFF);
|
||||
vm_addr += word_size;
|
||||
dst += word_size;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static size_t
|
||||
DoWriteMemory(lldb::pid_t pid, unsigned word_size,
|
||||
lldb::addr_t vm_addr, const void *buf, size_t size, Error &error)
|
||||
{
|
||||
const unsigned char *src = static_cast<const unsigned char*>(buf);
|
||||
size_t bytes_written = 0;
|
||||
size_t remainder;
|
||||
|
||||
for (bytes_written = 0; bytes_written < size; bytes_written += remainder)
|
||||
{
|
||||
remainder = size - bytes_written;
|
||||
remainder = remainder > word_size ? word_size : remainder;
|
||||
|
||||
if (remainder == word_size)
|
||||
{
|
||||
unsigned long data = 0;
|
||||
for (unsigned i = 0; i < word_size; ++i)
|
||||
data |= (unsigned long)src[i] << i*8;
|
||||
|
||||
if (ptrace(PTRACE_POKEDATA, pid, vm_addr, data))
|
||||
{
|
||||
error.SetErrorToErrno();
|
||||
return bytes_written;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char buff[8];
|
||||
if (DoReadMemory(pid, word_size, vm_addr,
|
||||
buff, word_size, error) != word_size)
|
||||
return bytes_written;
|
||||
|
||||
memcpy(buff, src, remainder);
|
||||
|
||||
if (DoWriteMemory(pid, word_size, vm_addr,
|
||||
buff, word_size, error) != word_size)
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
vm_addr += word_size;
|
||||
src += word_size;
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class Operation
|
||||
/// @brief Represents a ProcessMonitor operation.
|
||||
///
|
||||
/// Under Linux, it is not possible to ptrace() from any other thread but the
|
||||
/// one that spawned or attached to the process from the start. Therefore, when
|
||||
/// a ProcessMonitor is asked to deliver or change the state of an inferior
|
||||
/// process the operation must be "funneled" to a specific thread to perform the
|
||||
/// task. The Operation class provides an abstract base for all services the
|
||||
/// ProcessMonitor must perform via the single virtual function Execute, thus
|
||||
/// encapsulating the code that needs to run in the privileged context.
|
||||
class Operation
|
||||
{
|
||||
public:
|
||||
virtual void Execute(ProcessMonitor *monitor) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class ReadOperation
|
||||
/// @brief Implements ProcessMonitor::ReadMemory.
|
||||
class ReadOperation : public Operation
|
||||
{
|
||||
public:
|
||||
ReadOperation(lldb::addr_t addr, void *buff, size_t size,
|
||||
Error &error, size_t &result)
|
||||
: m_addr(addr), m_buff(buff), m_size(size),
|
||||
m_error(error), m_result(result)
|
||||
{ }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
lldb::addr_t m_addr;
|
||||
void *m_buff;
|
||||
size_t m_size;
|
||||
Error &m_error;
|
||||
size_t &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
ReadOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
const unsigned word_size = monitor->GetProcess().GetAddressByteSize();
|
||||
lldb::pid_t pid = monitor->GetPID();
|
||||
|
||||
m_result = DoReadMemory(pid, word_size, m_addr, m_buff, m_size, m_error);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class ReadOperation
|
||||
/// @brief Implements ProcessMonitor::WriteMemory.
|
||||
class WriteOperation : public Operation
|
||||
{
|
||||
public:
|
||||
WriteOperation(lldb::addr_t addr, const void *buff, size_t size,
|
||||
Error &error, size_t &result)
|
||||
: m_addr(addr), m_buff(buff), m_size(size),
|
||||
m_error(error), m_result(result)
|
||||
{ }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
lldb::addr_t m_addr;
|
||||
const void *m_buff;
|
||||
size_t m_size;
|
||||
Error &m_error;
|
||||
size_t &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
WriteOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
const unsigned word_size = monitor->GetProcess().GetAddressByteSize();
|
||||
lldb::pid_t pid = monitor->GetPID();
|
||||
|
||||
m_result = DoWriteMemory(pid, word_size, m_addr, m_buff, m_size, m_error);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class ReadRegOperation
|
||||
/// @brief Implements ProcessMonitor::ReadRegisterValue.
|
||||
class ReadRegOperation : public Operation
|
||||
{
|
||||
public:
|
||||
ReadRegOperation(unsigned offset, Scalar &value, bool &result)
|
||||
: m_offset(offset), m_value(value), m_result(result)
|
||||
{ }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
unsigned m_offset;
|
||||
Scalar &m_value;
|
||||
bool &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
ReadRegOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
lldb::pid_t pid = monitor->GetPID();
|
||||
|
||||
// Set errno to zero so that we can detect a failed peek.
|
||||
errno = 0;
|
||||
unsigned long data = ptrace(PTRACE_PEEKUSER, pid, m_offset, NULL);
|
||||
|
||||
if (data == -1UL && errno)
|
||||
m_result = false;
|
||||
else
|
||||
{
|
||||
m_value = data;
|
||||
m_result = true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class WriteRegOperation
|
||||
/// @brief Implements ProcessMonitor::WriteRegisterValue.
|
||||
class WriteRegOperation : public Operation
|
||||
{
|
||||
public:
|
||||
WriteRegOperation(unsigned offset, const Scalar &value, bool &result)
|
||||
: m_offset(offset), m_value(value), m_result(result)
|
||||
{ }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
unsigned m_offset;
|
||||
const Scalar &m_value;
|
||||
bool &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
WriteRegOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
lldb::pid_t pid = monitor->GetPID();
|
||||
|
||||
if (ptrace(PTRACE_POKEUSER, pid, m_offset, m_value.ULong()))
|
||||
m_result = false;
|
||||
else
|
||||
m_result = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class ResumeOperation
|
||||
/// @brief Implements ProcessMonitor::Resume.
|
||||
class ResumeOperation : public Operation
|
||||
{
|
||||
public:
|
||||
ResumeOperation(lldb::tid_t tid, bool &result) :
|
||||
m_tid(tid), m_result(result) { }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
lldb::tid_t m_tid;
|
||||
bool &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
ResumeOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
if (ptrace(PTRACE_CONT, m_tid, NULL, NULL))
|
||||
m_result = false;
|
||||
else
|
||||
m_result = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class ResumeOperation
|
||||
/// @brief Implements ProcessMonitor::SingleStep.
|
||||
class SingleStepOperation : public Operation
|
||||
{
|
||||
public:
|
||||
SingleStepOperation(lldb::tid_t tid, bool &result)
|
||||
: m_tid(tid), m_result(result) { }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
lldb::tid_t m_tid;
|
||||
bool &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
SingleStepOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
if (ptrace(PTRACE_SINGLESTEP, m_tid, NULL, NULL))
|
||||
m_result = false;
|
||||
else
|
||||
m_result = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class SiginfoOperation
|
||||
/// @brief Implements ProcessMonitor::GetSignalInfo.
|
||||
class SiginfoOperation : public Operation
|
||||
{
|
||||
public:
|
||||
SiginfoOperation(lldb::tid_t tid, void *info, bool &result)
|
||||
: m_tid(tid), m_info(info), m_result(result) { }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
lldb::tid_t m_tid;
|
||||
void *m_info;
|
||||
bool &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
SiginfoOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
if (ptrace(PTRACE_GETSIGINFO, m_tid, NULL, m_info))
|
||||
m_result = false;
|
||||
else
|
||||
m_result = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class EventMessageOperation
|
||||
/// @brief Implements ProcessMonitor::GetEventMessage.
|
||||
class EventMessageOperation : public Operation
|
||||
{
|
||||
public:
|
||||
EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result)
|
||||
: m_tid(tid), m_message(message), m_result(result) { }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
lldb::tid_t m_tid;
|
||||
unsigned long *m_message;
|
||||
bool &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
EventMessageOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
if (ptrace(PTRACE_GETEVENTMSG, m_tid, NULL, m_message))
|
||||
m_result = false;
|
||||
else
|
||||
m_result = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class KillOperation
|
||||
/// @brief Implements ProcessMonitor::BringProcessIntoLimbo.
|
||||
class KillOperation : public Operation
|
||||
{
|
||||
public:
|
||||
KillOperation(bool &result) : m_result(result) { }
|
||||
|
||||
void Execute(ProcessMonitor *monitor);
|
||||
|
||||
private:
|
||||
bool &m_result;
|
||||
};
|
||||
|
||||
void
|
||||
KillOperation::Execute(ProcessMonitor *monitor)
|
||||
{
|
||||
lldb::pid_t pid = monitor->GetPID();
|
||||
|
||||
if (ptrace(PTRACE_KILL, pid, NULL, NULL))
|
||||
m_result = false;
|
||||
else
|
||||
m_result = true;
|
||||
|
||||
#if 0
|
||||
// First, stop the inferior process.
|
||||
if (kill(pid, SIGSTOP))
|
||||
{
|
||||
m_result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear any ptrace options. When PTRACE_O_TRACEEXIT is set, a plain
|
||||
// PTRACE_KILL (or any termination signal) will not truely terminate the
|
||||
// inferior process. Instead, the process is left in a state of "limbo"
|
||||
// allowing us to interrogate its state. However in this case we really do
|
||||
// want the process gone.
|
||||
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, 0UL))
|
||||
{
|
||||
m_result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Kill it.
|
||||
if (ptrace(PTRACE_KILL, pid, NULL, NULL))
|
||||
m_result = false;
|
||||
else
|
||||
m_result = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor,
|
||||
lldb_private::Module *module,
|
||||
char const **argv,
|
||||
char const **envp,
|
||||
const char *stdin_path,
|
||||
const char *stdout_path,
|
||||
const char *stderr_path)
|
||||
: m_monitor(monitor),
|
||||
m_module(module),
|
||||
m_argv(argv),
|
||||
m_envp(envp),
|
||||
m_stdin_path(stdin_path),
|
||||
m_stdout_path(stdout_path),
|
||||
m_stderr_path(stderr_path)
|
||||
{
|
||||
sem_init(&m_semaphore, 0, 0);
|
||||
}
|
||||
|
||||
ProcessMonitor::LaunchArgs::~LaunchArgs()
|
||||
{
|
||||
sem_destroy(&m_semaphore);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// The basic design of the ProcessMonitor is built around two threads.
|
||||
///
|
||||
/// One thread (@see SignalThread) simply blocks on a call to waitpid() looking
|
||||
/// for changes in the debugee state. When a change is detected a
|
||||
/// ProcessMessage is sent to the associated ProcessLinux instance. This thread
|
||||
/// "drives" state changes in the debugger.
|
||||
///
|
||||
/// The second thread (@see OperationThread) is responsible for two things 1)
|
||||
/// lauching or attaching to the inferior process, and then 2) servicing
|
||||
/// operations such as register reads/writes, stepping, etc. See the comments
|
||||
/// on the Operation class for more info as to why this is needed.
|
||||
ProcessMonitor::ProcessMonitor(ProcessLinux *process,
|
||||
Module *module,
|
||||
const char *argv[],
|
||||
const char *envp[],
|
||||
const char *stdin_path,
|
||||
const char *stdout_path,
|
||||
const char *stderr_path,
|
||||
lldb_private::Error &error)
|
||||
: m_process(process),
|
||||
m_operation_thread(LLDB_INVALID_HOST_THREAD),
|
||||
m_pid(LLDB_INVALID_PROCESS_ID),
|
||||
m_terminal_fd(-1),
|
||||
m_monitor_handle(0),
|
||||
m_client_fd(-1),
|
||||
m_server_fd(-1)
|
||||
{
|
||||
LaunchArgs args(this, module, argv, envp,
|
||||
stdin_path, stdout_path, stderr_path);
|
||||
|
||||
// Server/client descriptors.
|
||||
if (!EnableIPC())
|
||||
{
|
||||
error.SetErrorToGenericError();
|
||||
error.SetErrorString("Monitor failed to initialize.");
|
||||
}
|
||||
|
||||
StartOperationThread(&args, error);
|
||||
if (!error.Success())
|
||||
return;
|
||||
|
||||
WAIT_AGAIN:
|
||||
// Wait for the operation thread to initialize.
|
||||
if (sem_wait(&args.m_semaphore))
|
||||
{
|
||||
if (errno == EINTR)
|
||||
goto WAIT_AGAIN;
|
||||
else
|
||||
{
|
||||
error.SetErrorToErrno();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the launch was a success.
|
||||
if (!args.m_error.Success())
|
||||
{
|
||||
StopOperationThread();
|
||||
error = args.m_error;
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, start monitoring the child process for change in state.
|
||||
if (!(m_monitor_handle = Host::StartMonitoringChildProcess(
|
||||
ProcessMonitor::MonitorCallback, this, GetPID(), true)))
|
||||
{
|
||||
error.SetErrorToGenericError();
|
||||
error.SetErrorString("Process launch failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessMonitor::~ProcessMonitor()
|
||||
{
|
||||
Host::StopMonitoringChildProcess(m_monitor_handle);
|
||||
StopOperationThread();
|
||||
|
||||
close(m_terminal_fd);
|
||||
close(m_client_fd);
|
||||
close(m_server_fd);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Thread setup and tear down.
|
||||
void
|
||||
ProcessMonitor::StartOperationThread(LaunchArgs *args, Error &error)
|
||||
{
|
||||
static const char *g_thread_name = "lldb.process.linux.operation";
|
||||
|
||||
if (m_operation_thread != LLDB_INVALID_HOST_THREAD)
|
||||
return;
|
||||
|
||||
m_operation_thread =
|
||||
Host::ThreadCreate(g_thread_name, OperationThread, args, &error);
|
||||
}
|
||||
|
||||
void
|
||||
ProcessMonitor::StopOperationThread()
|
||||
{
|
||||
lldb::thread_result_t result;
|
||||
|
||||
if (m_operation_thread == LLDB_INVALID_HOST_THREAD)
|
||||
return;
|
||||
|
||||
Host::ThreadCancel(m_operation_thread, NULL);
|
||||
Host::ThreadJoin(m_operation_thread, &result, NULL);
|
||||
}
|
||||
|
||||
void *
|
||||
ProcessMonitor::OperationThread(void *arg)
|
||||
{
|
||||
LaunchArgs *args = static_cast<LaunchArgs*>(arg);
|
||||
|
||||
if (!Launch(args))
|
||||
return NULL;
|
||||
|
||||
ServeOperation(args->m_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::Launch(LaunchArgs *args)
|
||||
{
|
||||
ProcessMonitor *monitor = args->m_monitor;
|
||||
ProcessLinux &process = monitor->GetProcess();
|
||||
const char **argv = args->m_argv;
|
||||
const char **envp = args->m_envp;
|
||||
const char *stdin_path = args->m_stdin_path;
|
||||
const char *stdout_path = args->m_stdout_path;
|
||||
const char *stderr_path = args->m_stderr_path;
|
||||
|
||||
lldb_utility::PseudoTerminal terminal;
|
||||
const size_t err_len = 1024;
|
||||
char err_str[err_len];
|
||||
lldb::pid_t pid;
|
||||
|
||||
lldb::ThreadSP inferior;
|
||||
|
||||
// Pseudo terminal setup.
|
||||
if (!terminal.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, err_str, err_len))
|
||||
{
|
||||
args->m_error.SetErrorToGenericError();
|
||||
args->m_error.SetErrorString("Could not open controlling TTY.");
|
||||
goto FINISH;
|
||||
}
|
||||
|
||||
if ((pid = terminal.Fork(err_str, err_len)) < 0)
|
||||
{
|
||||
args->m_error.SetErrorToGenericError();
|
||||
args->m_error.SetErrorString("Process fork failed.");
|
||||
goto FINISH;
|
||||
}
|
||||
|
||||
// Child process.
|
||||
if (pid == 0)
|
||||
{
|
||||
// Trace this process.
|
||||
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
|
||||
|
||||
// Do not inherit setgid powers.
|
||||
setgid(getgid());
|
||||
|
||||
// Let us have our own process group.
|
||||
setpgid(0, 0);
|
||||
|
||||
// Dup file discriptors if needed.
|
||||
//
|
||||
// FIXME: If two or more of the paths are the same we needlessly open
|
||||
// the same file multiple times.
|
||||
if (stdin_path != NULL && stdin_path[0])
|
||||
if (!DupDescriptor(stdin_path, STDIN_FILENO, O_RDONLY | O_CREAT))
|
||||
exit(1);
|
||||
|
||||
if (stdout_path != NULL && stdout_path[0])
|
||||
if (!DupDescriptor(stdout_path, STDOUT_FILENO, O_WRONLY | O_CREAT))
|
||||
exit(1);
|
||||
|
||||
if (stderr_path != NULL && stderr_path[0])
|
||||
if (!DupDescriptor(stderr_path, STDOUT_FILENO, O_WRONLY | O_CREAT))
|
||||
exit(1);
|
||||
|
||||
// Execute. We should never return.
|
||||
execve(argv[0],
|
||||
const_cast<char *const *>(argv),
|
||||
const_cast<char *const *>(envp));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Wait for the child process to to trap on its call to execve.
|
||||
int status;
|
||||
if ((status = waitpid(pid, NULL, 0)) < 0)
|
||||
{
|
||||
// execve likely failed for some reason.
|
||||
args->m_error.SetErrorToErrno();
|
||||
goto FINISH;
|
||||
}
|
||||
assert(status == pid && "Could not sync with inferior process.");
|
||||
|
||||
// Have the child raise an event on exit. This is used to keep the child in
|
||||
// limbo until it is destroyed.
|
||||
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0)
|
||||
{
|
||||
args->m_error.SetErrorToErrno();
|
||||
goto FINISH;
|
||||
}
|
||||
|
||||
// Release the master terminal descriptor and pass it off to the
|
||||
// ProcessMonitor instance. Similarly stash the inferior pid.
|
||||
monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor();
|
||||
monitor->m_pid = pid;
|
||||
|
||||
// Update the process thread list with this new thread and mark it as
|
||||
// current.
|
||||
inferior.reset(new LinuxThread(process, pid));
|
||||
process.GetThreadList().AddThread(inferior);
|
||||
process.GetThreadList().SetCurrentThreadByID(pid);
|
||||
|
||||
// Let our process instance know the thread has stopped.
|
||||
process.SendMessage(ProcessMessage::Trace(pid));
|
||||
|
||||
FINISH:
|
||||
// Sync with our parent thread now that the launch operation is complete.
|
||||
sem_post(&args->m_semaphore);
|
||||
return args->m_error.Success();
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::EnableIPC()
|
||||
{
|
||||
int fd[2];
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd))
|
||||
return false;
|
||||
|
||||
m_client_fd = fd[0];
|
||||
m_server_fd = fd[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::MonitorCallback(void *callback_baton,
|
||||
lldb::pid_t pid,
|
||||
int signal,
|
||||
int status)
|
||||
{
|
||||
ProcessMessage message;
|
||||
ProcessMonitor *monitor = static_cast<ProcessMonitor*>(callback_baton);
|
||||
ProcessLinux *process = monitor->m_process;
|
||||
|
||||
switch (signal)
|
||||
{
|
||||
case 0:
|
||||
// No signal. The child has exited normally.
|
||||
message = ProcessMessage::Exit(pid, status);
|
||||
break;
|
||||
|
||||
case SIGTRAP:
|
||||
// Specially handle SIGTRAP and form the appropriate message.
|
||||
message = MonitorSIGTRAP(monitor, pid);
|
||||
break;
|
||||
|
||||
default:
|
||||
// For all other signals simply notify the process instance. Note that
|
||||
// the process exit status is set when the signal resulted in
|
||||
// termination.
|
||||
//
|
||||
// FIXME: We need a specialized message to inform the process instance
|
||||
// about "crashes".
|
||||
if (status)
|
||||
message = ProcessMessage::Exit(pid, status);
|
||||
else
|
||||
message = ProcessMessage::Signal(pid, signal);
|
||||
}
|
||||
|
||||
process->SendMessage(message);
|
||||
bool stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage;
|
||||
return stop_monitoring;
|
||||
}
|
||||
|
||||
ProcessMessage
|
||||
ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, lldb::pid_t pid)
|
||||
{
|
||||
siginfo_t info;
|
||||
ProcessMessage message;
|
||||
bool status;
|
||||
|
||||
status = monitor->GetSignalInfo(pid, &info);
|
||||
assert(status && "GetSignalInfo failed!");
|
||||
|
||||
assert(info.si_signo == SIGTRAP && "Unexpected child signal!");
|
||||
|
||||
switch (info.si_code)
|
||||
{
|
||||
default:
|
||||
assert(false && "Unexpected SIGTRAP code!");
|
||||
break;
|
||||
|
||||
case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)):
|
||||
{
|
||||
// The inferior process is about to exit. Maintain the process in a
|
||||
// state of "limbo" until we are explicitly commanded to detach,
|
||||
// destroy, resume, etc.
|
||||
unsigned long data = 0;
|
||||
if (!monitor->GetEventMessage(pid, &data))
|
||||
data = -1;
|
||||
message = ProcessMessage::Exit(pid, (data >> 8));
|
||||
break;
|
||||
}
|
||||
|
||||
case 0:
|
||||
case TRAP_TRACE:
|
||||
message = ProcessMessage::Trace(pid);
|
||||
break;
|
||||
|
||||
case SI_KERNEL:
|
||||
case TRAP_BRKPT:
|
||||
message = ProcessMessage::Break(pid);
|
||||
break;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessMonitor::ServeOperation(ProcessMonitor *monitor)
|
||||
{
|
||||
int status;
|
||||
pollfd fdset;
|
||||
|
||||
fdset.fd = monitor->m_server_fd;
|
||||
fdset.events = POLLIN | POLLPRI;
|
||||
fdset.revents = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if ((status = poll(&fdset, 1, -1)) < 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
default:
|
||||
assert(false && "Unexpected poll() failure!");
|
||||
continue;
|
||||
|
||||
case EINTR: continue; // Just poll again.
|
||||
case EBADF: return; // Connection terminated.
|
||||
}
|
||||
}
|
||||
|
||||
assert(status == 1 && "Too many descriptors!");
|
||||
|
||||
if (fdset.revents & POLLIN)
|
||||
{
|
||||
Operation *op = NULL;
|
||||
|
||||
READ_AGAIN:
|
||||
if ((status = read(fdset.fd, &op, sizeof(op))) < 0)
|
||||
{
|
||||
// There is only one acceptable failure.
|
||||
assert(errno == EINTR);
|
||||
goto READ_AGAIN;
|
||||
}
|
||||
|
||||
assert(status == sizeof(op));
|
||||
op->Execute(monitor);
|
||||
write(fdset.fd, &op, sizeof(op));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ProcessMonitor::DoOperation(Operation *op)
|
||||
{
|
||||
int status;
|
||||
Operation *ack = NULL;
|
||||
Mutex::Locker lock(m_server_mutex);
|
||||
|
||||
// FIXME: Do proper error checking here.
|
||||
write(m_client_fd, &op, sizeof(op));
|
||||
|
||||
READ_AGAIN:
|
||||
if ((status = read(m_client_fd, &ack, sizeof(ack))) < 0)
|
||||
{
|
||||
// If interrupted by a signal handler try again. Otherwise the monitor
|
||||
// thread probably died and we have a stale file descriptor -- abort the
|
||||
// operation.
|
||||
if (errno == EINTR)
|
||||
goto READ_AGAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(status == sizeof(ack));
|
||||
assert(ack == op && "Invalid monitor thread response!");
|
||||
}
|
||||
|
||||
size_t
|
||||
ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
|
||||
Error &error)
|
||||
{
|
||||
size_t result;
|
||||
ReadOperation op(vm_addr, buf, size, error, result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t
|
||||
ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
|
||||
lldb_private::Error &error)
|
||||
{
|
||||
size_t result;
|
||||
WriteOperation op(vm_addr, buf, size, error, result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::ReadRegisterValue(unsigned offset, Scalar &value)
|
||||
{
|
||||
bool result;
|
||||
ReadRegOperation op(offset, value, result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::WriteRegisterValue(unsigned offset, const Scalar &value)
|
||||
{
|
||||
bool result;
|
||||
WriteRegOperation op(offset, value, result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::Resume(lldb::tid_t tid)
|
||||
{
|
||||
bool result;
|
||||
ResumeOperation op(tid, result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::SingleStep(lldb::tid_t tid)
|
||||
{
|
||||
bool result;
|
||||
SingleStepOperation op(tid, result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::BringProcessIntoLimbo()
|
||||
{
|
||||
bool result;
|
||||
KillOperation op(result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::GetSignalInfo(lldb::tid_t tid, void *siginfo)
|
||||
{
|
||||
bool result;
|
||||
SiginfoOperation op(tid, siginfo, result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message)
|
||||
{
|
||||
bool result;
|
||||
EventMessageOperation op(tid, message, result);
|
||||
DoOperation(&op);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessMonitor::DupDescriptor(const char *path, int fd, int flags)
|
||||
{
|
||||
int target_fd = open(path, flags);
|
||||
|
||||
if (target_fd == -1)
|
||||
return false;
|
||||
|
||||
return (dup2(fd, target_fd) == -1) ? false : true;
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
//===-- ProcessMonitor.h -------------------------------------- -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_ProcessMonitor_H_
|
||||
#define liblldb_ProcessMonitor_H_
|
||||
|
||||
// C Includes
|
||||
#include <semaphore.h>
|
||||
|
||||
// C++ Includes
|
||||
// Other libraries and framework includes
|
||||
#include "lldb/lldb-types.h"
|
||||
#include "lldb/Host/Mutex.h"
|
||||
|
||||
namespace lldb_private
|
||||
{
|
||||
class Error;
|
||||
class Module;
|
||||
class Scalar;
|
||||
} // End lldb_private namespace.
|
||||
|
||||
class ProcessLinux;
|
||||
class Operation;
|
||||
|
||||
/// @class ProcessMonitor
|
||||
/// @brief Manages communication with the inferior (debugee) process.
|
||||
///
|
||||
/// Upon construction, this class prepares and launches an inferior process for
|
||||
/// debugging.
|
||||
///
|
||||
/// Changes in the inferior process state are propagated to the associated
|
||||
/// ProcessLinux instance by calling ProcessLinux::SendMessage with the
|
||||
/// appropriate ProcessMessage events.
|
||||
///
|
||||
/// A purposely minimal set of operations are provided to interrogate and change
|
||||
/// the inferior process state.
|
||||
class ProcessMonitor
|
||||
{
|
||||
public:
|
||||
|
||||
/// Launches an inferior process ready for debugging. Forms the
|
||||
/// implementation of Process::DoLaunch.
|
||||
ProcessMonitor(ProcessLinux *process,
|
||||
lldb_private::Module *module,
|
||||
char const *argv[],
|
||||
char const *envp[],
|
||||
const char *stdin_path,
|
||||
const char *stdout_path,
|
||||
const char *stderr_path,
|
||||
lldb_private::Error &error);
|
||||
|
||||
~ProcessMonitor();
|
||||
|
||||
/// Provides the process number of debugee.
|
||||
lldb::pid_t
|
||||
GetPID() const { return m_pid; }
|
||||
|
||||
/// Returns the process associated with this ProcessMonitor.
|
||||
ProcessLinux &
|
||||
GetProcess() { return *m_process; }
|
||||
|
||||
/// Returns a file descriptor to the controling terminal of the inferior
|
||||
/// process.
|
||||
///
|
||||
/// Reads from this file descriptor yeild both the standard output and
|
||||
/// standard error of this debugee. Even if stderr and stdout were
|
||||
/// redirected on launch it may still happen that data is available on this
|
||||
/// descriptor (if the inferior process opens /dev/tty, for example).
|
||||
///
|
||||
/// If this monitor was attached to an existing process this method returns
|
||||
/// -1.
|
||||
int
|
||||
GetTerminalFD() const { return m_terminal_fd; }
|
||||
|
||||
/// Reads @p size bytes from address @vm_adder in the inferior process
|
||||
/// address space.
|
||||
///
|
||||
/// This method is provided to implement Process::DoReadMemory.
|
||||
size_t
|
||||
ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
|
||||
lldb_private::Error &error);
|
||||
|
||||
/// Writes @p size bytes from address @p vm_adder in the inferior process
|
||||
/// address space.
|
||||
///
|
||||
/// This method is provided to implement Process::DoWriteMemory.
|
||||
size_t
|
||||
WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
|
||||
lldb_private::Error &error);
|
||||
|
||||
/// Reads the contents from the register identified by the given (architecture
|
||||
/// dependent) offset.
|
||||
///
|
||||
/// This method is provided for use by RegisterContextLinux derivatives.
|
||||
bool
|
||||
ReadRegisterValue(unsigned offset, lldb_private::Scalar &value);
|
||||
|
||||
/// Writes the given value to the register identified by the given
|
||||
/// (architecture dependent) offset.
|
||||
///
|
||||
/// This method is provided for use by RegisterContextLinux derivatives.
|
||||
bool
|
||||
WriteRegisterValue(unsigned offset, const lldb_private::Scalar &value);
|
||||
|
||||
/// Writes a siginfo_t structure corresponding to the given thread ID to the
|
||||
/// memory region pointed to by @p siginfo.
|
||||
bool
|
||||
GetSignalInfo(lldb::tid_t tid, void *siginfo);
|
||||
|
||||
/// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
|
||||
/// corresponding to the given thread IDto the memory pointed to by @p
|
||||
/// message.
|
||||
bool
|
||||
GetEventMessage(lldb::tid_t tid, unsigned long *message);
|
||||
|
||||
/// Resumes the given thread.
|
||||
bool
|
||||
Resume(lldb::tid_t tid);
|
||||
|
||||
/// Single steps the given thread.
|
||||
bool
|
||||
SingleStep(lldb::tid_t tid);
|
||||
|
||||
/// Sends the inferior process a PTRACE_KILL signal. The inferior will
|
||||
/// still exists and can be interrogated. Once resumed it will exit as
|
||||
/// though it received a SIGKILL.
|
||||
bool
|
||||
BringProcessIntoLimbo();
|
||||
|
||||
private:
|
||||
ProcessLinux *m_process;
|
||||
|
||||
lldb::thread_t m_operation_thread;
|
||||
lldb::pid_t m_pid;
|
||||
int m_terminal_fd;
|
||||
|
||||
uint32_t m_monitor_handle;
|
||||
|
||||
lldb_private::Mutex m_server_mutex;
|
||||
int m_client_fd;
|
||||
int m_server_fd;
|
||||
|
||||
/// @class LauchArgs
|
||||
///
|
||||
/// @brief Simple structure to pass data to the thread responsible for
|
||||
/// launching a child process.
|
||||
struct LaunchArgs
|
||||
{
|
||||
LaunchArgs(ProcessMonitor *monitor,
|
||||
lldb_private::Module *module,
|
||||
char const **argv,
|
||||
char const **envp,
|
||||
const char *stdin_path,
|
||||
const char *stdout_path,
|
||||
const char *stderr_path);
|
||||
|
||||
~LaunchArgs();
|
||||
|
||||
ProcessMonitor *m_monitor; // The monitor performing the launch.
|
||||
lldb_private::Module *m_module; // The executable image to launch.
|
||||
char const **m_argv; // Process arguments.
|
||||
char const **m_envp; // Process environment.
|
||||
const char *m_stdin_path; // Redirect stdin or NULL.
|
||||
const char *m_stdout_path; // Redirect stdout or NULL.
|
||||
const char *m_stderr_path; // Redirect stderr or NULL.
|
||||
sem_t m_semaphore; // Posted to once launch complete.
|
||||
lldb_private::Error m_error; // Set if process launch failed.
|
||||
};
|
||||
|
||||
void
|
||||
StartOperationThread(LaunchArgs *args, lldb_private::Error &error);
|
||||
|
||||
void
|
||||
StopOperationThread();
|
||||
|
||||
static void *
|
||||
OperationThread(void *arg);
|
||||
|
||||
static bool
|
||||
Launch(LaunchArgs *args);
|
||||
|
||||
bool
|
||||
EnableIPC();
|
||||
|
||||
static void
|
||||
ServeOperation(ProcessMonitor *monitor);
|
||||
|
||||
static bool
|
||||
DupDescriptor(const char *path, int fd, int flags);
|
||||
|
||||
static bool
|
||||
MonitorCallback(void *callback_baton,
|
||||
lldb::pid_t pid, int signal, int status);
|
||||
|
||||
static ProcessMessage
|
||||
MonitorSIGTRAP(ProcessMonitor *monitor, lldb::pid_t pid);
|
||||
|
||||
void
|
||||
DoOperation(Operation *op);
|
||||
};
|
||||
|
||||
#endif // #ifndef liblldb_ProcessMonitor_H_
|
|
@ -0,0 +1,40 @@
|
|||
//===-- RegisterContext_x86_64.h --------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_RegisterContextLinux_H_
|
||||
#define liblldb_RegisterContextLinux_H_
|
||||
|
||||
// C Includes
|
||||
// C++ Includes
|
||||
// Other libraries and framework includes
|
||||
#include "lldb/Target/RegisterContext.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @class RegisterContextLinux
|
||||
///
|
||||
/// @brief Extends RegisterClass with a few virtual operations useful on Linux.
|
||||
class RegisterContextLinux
|
||||
: public lldb_private::RegisterContext
|
||||
{
|
||||
public:
|
||||
RegisterContextLinux(lldb_private::Thread &thread,
|
||||
lldb_private::StackFrame *frame)
|
||||
: RegisterContext(thread, frame) { }
|
||||
|
||||
/// Updates the register state of the associated thread after hitting a
|
||||
/// breakpoint (if that make sense for the architecture). Default
|
||||
/// implementation simply returns true for architectures which do not
|
||||
/// require any upadte.
|
||||
///
|
||||
/// @return
|
||||
/// True if the operation succeeded and false otherwise.
|
||||
virtual bool UpdateAfterBreakpoint() { return true; }
|
||||
};
|
||||
|
||||
#endif // #ifndef liblldb_RegisterContextLinux_H_
|
|
@ -0,0 +1,653 @@
|
|||
//===-- RegisterContextLinux_x86_64.cpp -------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lldb/Core/Scalar.h"
|
||||
#include "lldb/Target/Thread.h"
|
||||
|
||||
#include "ProcessLinux.h"
|
||||
#include "ProcessMonitor.h"
|
||||
#include "RegisterContextLinux_x86_64.h"
|
||||
|
||||
using namespace lldb_private;
|
||||
using namespace lldb;
|
||||
|
||||
// Internal codes for all x86_64 registers.
|
||||
enum
|
||||
{
|
||||
gpr_rax = 0,
|
||||
gpr_rbx,
|
||||
gpr_rcx,
|
||||
gpr_rdx,
|
||||
gpr_rdi,
|
||||
gpr_rsi,
|
||||
gpr_rbp,
|
||||
gpr_rsp,
|
||||
gpr_r8,
|
||||
gpr_r9,
|
||||
gpr_r10,
|
||||
gpr_r11,
|
||||
gpr_r12,
|
||||
gpr_r13,
|
||||
gpr_r14,
|
||||
gpr_r15,
|
||||
gpr_rip,
|
||||
gpr_rflags,
|
||||
gpr_cs,
|
||||
gpr_fs,
|
||||
gpr_gs,
|
||||
gpr_ss,
|
||||
gpr_ds,
|
||||
gpr_es,
|
||||
|
||||
// Number of GPR's.
|
||||
k_num_gpr_registers,
|
||||
|
||||
fpu_fcw = k_num_gpr_registers,
|
||||
fpu_fsw,
|
||||
fpu_ftw,
|
||||
fpu_fop,
|
||||
fpu_ip,
|
||||
fpu_cs,
|
||||
fpu_dp,
|
||||
fpu_ds,
|
||||
fpu_mxcsr,
|
||||
fpu_mxcsrmask,
|
||||
fpu_stmm0,
|
||||
fpu_stmm1,
|
||||
fpu_stmm2,
|
||||
fpu_stmm3,
|
||||
fpu_stmm4,
|
||||
fpu_stmm5,
|
||||
fpu_stmm6,
|
||||
fpu_stmm7,
|
||||
fpu_xmm0,
|
||||
fpu_xmm1,
|
||||
fpu_xmm2,
|
||||
fpu_xmm3,
|
||||
fpu_xmm4,
|
||||
fpu_xmm5,
|
||||
fpu_xmm6,
|
||||
fpu_xmm7,
|
||||
fpu_xmm8,
|
||||
fpu_xmm9,
|
||||
fpu_xmm10,
|
||||
fpu_xmm11,
|
||||
fpu_xmm12,
|
||||
fpu_xmm13,
|
||||
fpu_xmm14,
|
||||
fpu_xmm15,
|
||||
|
||||
// Total number of registers.
|
||||
k_num_registers,
|
||||
|
||||
// Number of FPR's.
|
||||
k_num_fpu_registers = k_num_registers - k_num_gpr_registers
|
||||
};
|
||||
|
||||
// Number of register sets provided by this context.
|
||||
enum
|
||||
{
|
||||
k_num_register_sets = 2
|
||||
};
|
||||
|
||||
enum gcc_dwarf_regnums
|
||||
{
|
||||
gcc_dwarf_gpr_rax = 0,
|
||||
gcc_dwarf_gpr_rdx,
|
||||
gcc_dwarf_gpr_rcx,
|
||||
gcc_dwarf_gpr_rbx,
|
||||
gcc_dwarf_gpr_rsi,
|
||||
gcc_dwarf_gpr_rdi,
|
||||
gcc_dwarf_gpr_rbp,
|
||||
gcc_dwarf_gpr_rsp,
|
||||
gcc_dwarf_gpr_r8,
|
||||
gcc_dwarf_gpr_r9,
|
||||
gcc_dwarf_gpr_r10,
|
||||
gcc_dwarf_gpr_r11,
|
||||
gcc_dwarf_gpr_r12,
|
||||
gcc_dwarf_gpr_r13,
|
||||
gcc_dwarf_gpr_r14,
|
||||
gcc_dwarf_gpr_r15,
|
||||
gcc_dwarf_gpr_rip,
|
||||
gcc_dwarf_fpu_xmm0,
|
||||
gcc_dwarf_fpu_xmm1,
|
||||
gcc_dwarf_fpu_xmm2,
|
||||
gcc_dwarf_fpu_xmm3,
|
||||
gcc_dwarf_fpu_xmm4,
|
||||
gcc_dwarf_fpu_xmm5,
|
||||
gcc_dwarf_fpu_xmm6,
|
||||
gcc_dwarf_fpu_xmm7,
|
||||
gcc_dwarf_fpu_xmm8,
|
||||
gcc_dwarf_fpu_xmm9,
|
||||
gcc_dwarf_fpu_xmm10,
|
||||
gcc_dwarf_fpu_xmm11,
|
||||
gcc_dwarf_fpu_xmm12,
|
||||
gcc_dwarf_fpu_xmm13,
|
||||
gcc_dwarf_fpu_xmm14,
|
||||
gcc_dwarf_fpu_xmm15,
|
||||
gcc_dwarf_fpu_stmm0,
|
||||
gcc_dwarf_fpu_stmm1,
|
||||
gcc_dwarf_fpu_stmm2,
|
||||
gcc_dwarf_fpu_stmm3,
|
||||
gcc_dwarf_fpu_stmm4,
|
||||
gcc_dwarf_fpu_stmm5,
|
||||
gcc_dwarf_fpu_stmm6,
|
||||
gcc_dwarf_fpu_stmm7
|
||||
};
|
||||
|
||||
enum gdb_regnums
|
||||
{
|
||||
gdb_gpr_rax = 0,
|
||||
gdb_gpr_rbx = 1,
|
||||
gdb_gpr_rcx = 2,
|
||||
gdb_gpr_rdx = 3,
|
||||
gdb_gpr_rsi = 4,
|
||||
gdb_gpr_rdi = 5,
|
||||
gdb_gpr_rbp = 6,
|
||||
gdb_gpr_rsp = 7,
|
||||
gdb_gpr_r8 = 8,
|
||||
gdb_gpr_r9 = 9,
|
||||
gdb_gpr_r10 = 10,
|
||||
gdb_gpr_r11 = 11,
|
||||
gdb_gpr_r12 = 12,
|
||||
gdb_gpr_r13 = 13,
|
||||
gdb_gpr_r14 = 14,
|
||||
gdb_gpr_r15 = 15,
|
||||
gdb_gpr_rip = 16,
|
||||
gdb_gpr_rflags = 17,
|
||||
gdb_gpr_cs = 18,
|
||||
gdb_gpr_ss = 19,
|
||||
gdb_gpr_ds = 20,
|
||||
gdb_gpr_es = 21,
|
||||
gdb_gpr_fs = 22,
|
||||
gdb_gpr_gs = 23,
|
||||
gdb_fpu_stmm0 = 24,
|
||||
gdb_fpu_stmm1 = 25,
|
||||
gdb_fpu_stmm2 = 26,
|
||||
gdb_fpu_stmm3 = 27,
|
||||
gdb_fpu_stmm4 = 28,
|
||||
gdb_fpu_stmm5 = 29,
|
||||
gdb_fpu_stmm6 = 30,
|
||||
gdb_fpu_stmm7 = 31,
|
||||
gdb_fpu_fcw = 32,
|
||||
gdb_fpu_fsw = 33,
|
||||
gdb_fpu_ftw = 34,
|
||||
gdb_fpu_cs = 35,
|
||||
gdb_fpu_ip = 36,
|
||||
gdb_fpu_ds = 37,
|
||||
gdb_fpu_dp = 38,
|
||||
gdb_fpu_fop = 39,
|
||||
gdb_fpu_xmm0 = 40,
|
||||
gdb_fpu_xmm1 = 41,
|
||||
gdb_fpu_xmm2 = 42,
|
||||
gdb_fpu_xmm3 = 43,
|
||||
gdb_fpu_xmm4 = 44,
|
||||
gdb_fpu_xmm5 = 45,
|
||||
gdb_fpu_xmm6 = 46,
|
||||
gdb_fpu_xmm7 = 47,
|
||||
gdb_fpu_xmm8 = 48,
|
||||
gdb_fpu_xmm9 = 49,
|
||||
gdb_fpu_xmm10 = 50,
|
||||
gdb_fpu_xmm11 = 51,
|
||||
gdb_fpu_xmm12 = 52,
|
||||
gdb_fpu_xmm13 = 53,
|
||||
gdb_fpu_xmm14 = 54,
|
||||
gdb_fpu_xmm15 = 55,
|
||||
gdb_fpu_mxcsr = 56
|
||||
};
|
||||
|
||||
static const
|
||||
uint32_t g_gpr_regnums[k_num_gpr_registers] =
|
||||
{
|
||||
gpr_rax,
|
||||
gpr_rbx,
|
||||
gpr_rcx,
|
||||
gpr_rdx,
|
||||
gpr_rdi,
|
||||
gpr_rsi,
|
||||
gpr_rbp,
|
||||
gpr_rsp,
|
||||
gpr_r8,
|
||||
gpr_r9,
|
||||
gpr_r10,
|
||||
gpr_r11,
|
||||
gpr_r12,
|
||||
gpr_r13,
|
||||
gpr_r14,
|
||||
gpr_r15,
|
||||
gpr_rip,
|
||||
gpr_rflags,
|
||||
gpr_cs,
|
||||
gpr_fs,
|
||||
gpr_gs,
|
||||
gpr_ss,
|
||||
gpr_ds,
|
||||
gpr_es
|
||||
};
|
||||
|
||||
static const uint32_t
|
||||
g_fpu_regnums[k_num_fpu_registers] =
|
||||
{
|
||||
fpu_fcw,
|
||||
fpu_fsw,
|
||||
fpu_ftw,
|
||||
fpu_fop,
|
||||
fpu_ip,
|
||||
fpu_cs,
|
||||
fpu_dp,
|
||||
fpu_ds,
|
||||
fpu_mxcsr,
|
||||
fpu_mxcsrmask,
|
||||
fpu_stmm0,
|
||||
fpu_stmm1,
|
||||
fpu_stmm2,
|
||||
fpu_stmm3,
|
||||
fpu_stmm4,
|
||||
fpu_stmm5,
|
||||
fpu_stmm6,
|
||||
fpu_stmm7,
|
||||
fpu_xmm0,
|
||||
fpu_xmm1,
|
||||
fpu_xmm2,
|
||||
fpu_xmm3,
|
||||
fpu_xmm4,
|
||||
fpu_xmm5,
|
||||
fpu_xmm6,
|
||||
fpu_xmm7,
|
||||
fpu_xmm8,
|
||||
fpu_xmm9,
|
||||
fpu_xmm10,
|
||||
fpu_xmm11,
|
||||
fpu_xmm12,
|
||||
fpu_xmm13,
|
||||
fpu_xmm14,
|
||||
fpu_xmm15
|
||||
};
|
||||
|
||||
static const RegisterSet
|
||||
g_reg_sets[k_num_register_sets] =
|
||||
{
|
||||
{ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums },
|
||||
{ "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }
|
||||
};
|
||||
|
||||
// Computes the offset of the given GPR in the user data area.
|
||||
#define GPR_OFFSET(regname) \
|
||||
(offsetof(RegisterContextLinux_x86_64::UserArea, regs) + \
|
||||
offsetof(RegisterContextLinux_x86_64::GPR, regname))
|
||||
|
||||
// Computes the offset of the given FPR in the user data area.
|
||||
#define FPR_OFFSET(regname) \
|
||||
(offsetof(RegisterContextLinux_x86_64::UserArea, i387) + \
|
||||
offsetof(RegisterContextLinux_x86_64::FPU, regname))
|
||||
|
||||
// Number of bytes needed to represet a GPR.
|
||||
#define GPR_SIZE(reg) sizeof(((RegisterContextLinux_x86_64::GPR*)NULL)->reg)
|
||||
|
||||
// Number of bytes needed to represet a FPR.
|
||||
#define FPR_SIZE(reg) sizeof(((RegisterContextLinux_x86_64::FPU*)NULL)->reg)
|
||||
|
||||
// Number of bytes needed to represet the i'th FP register.
|
||||
#define FP_SIZE sizeof(((RegisterContextLinux_x86_64::MMSReg*)NULL)->bytes)
|
||||
|
||||
// Number of bytes needed to represet an XMM register.
|
||||
#define XMM_SIZE sizeof(RegisterContextLinux_x86_64::XMMReg)
|
||||
|
||||
#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
|
||||
{ #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, \
|
||||
eFormatHex, gpr_##reg, { kind1, kind2, kind3, kind4 } }
|
||||
|
||||
#define DEFINE_FPR(reg, kind1, kind2, kind3, kind4) \
|
||||
{ #reg, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \
|
||||
eFormatHex, fpu_##reg, { kind1, kind2, kind3, kind4 } }
|
||||
|
||||
#define DEFINE_FP(reg, i) \
|
||||
{ #reg#i, NULL, FP_SIZE, FPR_OFFSET(reg[i]), eEncodingVector, \
|
||||
eFormatVectorOfUInt8, fpu_##reg##i, \
|
||||
{ gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \
|
||||
LLDB_INVALID_REGNUM, gdb_fpu_##reg##i } }
|
||||
|
||||
#define DEFINE_XMM(reg, i) \
|
||||
{ #reg#i, NULL, XMM_SIZE, FPR_OFFSET(reg[i]), eEncodingVector, \
|
||||
eFormatVectorOfUInt8, fpu_##reg##i, \
|
||||
{ gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \
|
||||
LLDB_INVALID_REGNUM, gdb_fpu_##reg##i } }
|
||||
|
||||
static RegisterInfo
|
||||
g_register_infos[k_num_registers] =
|
||||
{
|
||||
// General purpose registers.
|
||||
DEFINE_GPR(rax, NULL, gcc_dwarf_gpr_rax, gcc_dwarf_gpr_rax, LLDB_INVALID_REGNUM, gdb_gpr_rax),
|
||||
DEFINE_GPR(rbx, NULL, gcc_dwarf_gpr_rbx, gcc_dwarf_gpr_rbx, LLDB_INVALID_REGNUM, gdb_gpr_rbx),
|
||||
DEFINE_GPR(rcx, NULL, gcc_dwarf_gpr_rcx, gcc_dwarf_gpr_rcx, LLDB_INVALID_REGNUM, gdb_gpr_rcx),
|
||||
DEFINE_GPR(rdx, NULL, gcc_dwarf_gpr_rdx, gcc_dwarf_gpr_rdx, LLDB_INVALID_REGNUM, gdb_gpr_rdx),
|
||||
DEFINE_GPR(rdi, NULL, gcc_dwarf_gpr_rdi, gcc_dwarf_gpr_rdi, LLDB_INVALID_REGNUM, gdb_gpr_rdi),
|
||||
DEFINE_GPR(rsi, NULL, gcc_dwarf_gpr_rsi, gcc_dwarf_gpr_rsi, LLDB_INVALID_REGNUM, gdb_gpr_rsi),
|
||||
DEFINE_GPR(rbp, "fp", gcc_dwarf_gpr_rbp, gcc_dwarf_gpr_rbp, LLDB_REGNUM_GENERIC_FP, gdb_gpr_rbp),
|
||||
DEFINE_GPR(rsp, "sp", gcc_dwarf_gpr_rsp, gcc_dwarf_gpr_rsp, LLDB_REGNUM_GENERIC_SP, gdb_gpr_rsp),
|
||||
DEFINE_GPR(r8, NULL, gcc_dwarf_gpr_r8, gcc_dwarf_gpr_r8, LLDB_INVALID_REGNUM, gdb_gpr_r8),
|
||||
DEFINE_GPR(r9, NULL, gcc_dwarf_gpr_r9, gcc_dwarf_gpr_r9, LLDB_INVALID_REGNUM, gdb_gpr_r9),
|
||||
DEFINE_GPR(r10, NULL, gcc_dwarf_gpr_r10, gcc_dwarf_gpr_r10, LLDB_INVALID_REGNUM, gdb_gpr_r10),
|
||||
DEFINE_GPR(r11, NULL, gcc_dwarf_gpr_r11, gcc_dwarf_gpr_r11, LLDB_INVALID_REGNUM, gdb_gpr_r11),
|
||||
DEFINE_GPR(r12, NULL, gcc_dwarf_gpr_r12, gcc_dwarf_gpr_r12, LLDB_INVALID_REGNUM, gdb_gpr_r12),
|
||||
DEFINE_GPR(r13, NULL, gcc_dwarf_gpr_r13, gcc_dwarf_gpr_r13, LLDB_INVALID_REGNUM, gdb_gpr_r13),
|
||||
DEFINE_GPR(r14, NULL, gcc_dwarf_gpr_r14, gcc_dwarf_gpr_r14, LLDB_INVALID_REGNUM, gdb_gpr_r14),
|
||||
DEFINE_GPR(r15, NULL, gcc_dwarf_gpr_r15, gcc_dwarf_gpr_r15, LLDB_INVALID_REGNUM, gdb_gpr_r15),
|
||||
DEFINE_GPR(rip, "pc", gcc_dwarf_gpr_rip, gcc_dwarf_gpr_rip, LLDB_REGNUM_GENERIC_PC, gdb_gpr_rip),
|
||||
DEFINE_GPR(rflags, "flags", LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, gdb_gpr_rflags),
|
||||
DEFINE_GPR(cs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_cs),
|
||||
DEFINE_GPR(fs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_fs),
|
||||
DEFINE_GPR(gs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_gs),
|
||||
DEFINE_GPR(ss, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_ss),
|
||||
DEFINE_GPR(ds, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_ds),
|
||||
DEFINE_GPR(es, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_es),
|
||||
|
||||
// i387 Floating point registers.
|
||||
DEFINE_FPR(fcw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fcw),
|
||||
DEFINE_FPR(fsw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fsw),
|
||||
DEFINE_FPR(ftw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ftw),
|
||||
DEFINE_FPR(fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fop),
|
||||
DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ip),
|
||||
// FIXME: Extract segment from ip.
|
||||
DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_cs),
|
||||
DEFINE_FPR(dp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_dp),
|
||||
// FIXME: Extract segment from dp.
|
||||
DEFINE_FPR(dp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ds),
|
||||
DEFINE_FPR(mxcsr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_mxcsr),
|
||||
DEFINE_FPR(mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
|
||||
|
||||
// FP registers.
|
||||
DEFINE_FP(stmm, 0),
|
||||
DEFINE_FP(stmm, 1),
|
||||
DEFINE_FP(stmm, 2),
|
||||
DEFINE_FP(stmm, 3),
|
||||
DEFINE_FP(stmm, 4),
|
||||
DEFINE_FP(stmm, 5),
|
||||
DEFINE_FP(stmm, 6),
|
||||
DEFINE_FP(stmm, 7),
|
||||
|
||||
// XMM registers
|
||||
DEFINE_XMM(xmm, 0),
|
||||
DEFINE_XMM(xmm, 1),
|
||||
DEFINE_XMM(xmm, 2),
|
||||
DEFINE_XMM(xmm, 3),
|
||||
DEFINE_XMM(xmm, 4),
|
||||
DEFINE_XMM(xmm, 5),
|
||||
DEFINE_XMM(xmm, 6),
|
||||
DEFINE_XMM(xmm, 7),
|
||||
DEFINE_XMM(xmm, 8),
|
||||
DEFINE_XMM(xmm, 9),
|
||||
DEFINE_XMM(xmm, 10),
|
||||
DEFINE_XMM(xmm, 11),
|
||||
DEFINE_XMM(xmm, 12),
|
||||
DEFINE_XMM(xmm, 13),
|
||||
DEFINE_XMM(xmm, 14),
|
||||
DEFINE_XMM(xmm, 15)
|
||||
};
|
||||
|
||||
static unsigned GetRegOffset(unsigned reg)
|
||||
{
|
||||
assert(reg < k_num_registers && "Invalid register number.");
|
||||
return g_register_infos[reg].byte_offset;
|
||||
}
|
||||
|
||||
RegisterContextLinux_x86_64::RegisterContextLinux_x86_64(Thread &thread,
|
||||
StackFrame *frame)
|
||||
: RegisterContextLinux(thread, frame)
|
||||
{
|
||||
}
|
||||
|
||||
RegisterContextLinux_x86_64::~RegisterContextLinux_x86_64()
|
||||
{
|
||||
}
|
||||
|
||||
ProcessMonitor &
|
||||
RegisterContextLinux_x86_64::GetMonitor()
|
||||
{
|
||||
ProcessLinux *process = static_cast<ProcessLinux*>(CalculateProcess());
|
||||
return process->GetMonitor();
|
||||
}
|
||||
|
||||
void
|
||||
RegisterContextLinux_x86_64::Invalidate()
|
||||
{
|
||||
}
|
||||
|
||||
size_t
|
||||
RegisterContextLinux_x86_64::GetRegisterCount()
|
||||
{
|
||||
return k_num_registers;
|
||||
}
|
||||
|
||||
const RegisterInfo *
|
||||
RegisterContextLinux_x86_64::GetRegisterInfoAtIndex(uint32_t reg)
|
||||
{
|
||||
if (reg < k_num_registers)
|
||||
return &g_register_infos[reg];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
RegisterContextLinux_x86_64::GetRegisterSetCount()
|
||||
{
|
||||
return k_num_register_sets;
|
||||
}
|
||||
|
||||
const RegisterSet *
|
||||
RegisterContextLinux_x86_64::GetRegisterSet(uint32_t set)
|
||||
{
|
||||
if (set < k_num_register_sets)
|
||||
return &g_reg_sets[set];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterContextLinux_x86_64::ReadRegisterValue(uint32_t reg,
|
||||
Scalar &value)
|
||||
{
|
||||
ProcessMonitor &monitor = GetMonitor();
|
||||
return monitor.ReadRegisterValue(GetRegOffset(reg), value);
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterContextLinux_x86_64::ReadRegisterBytes(uint32_t reg,
|
||||
DataExtractor &data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterContextLinux_x86_64::ReadAllRegisterValues(DataBufferSP &data_sp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterContextLinux_x86_64::WriteRegisterValue(uint32_t reg,
|
||||
const Scalar &value)
|
||||
{
|
||||
ProcessMonitor &monitor = GetMonitor();
|
||||
return monitor.WriteRegisterValue(GetRegOffset(reg), value);
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterContextLinux_x86_64::WriteRegisterBytes(uint32_t reg,
|
||||
DataExtractor &data,
|
||||
uint32_t data_offset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterContextLinux_x86_64::WriteAllRegisterValues(const DataBufferSP &data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterContextLinux_x86_64::UpdateAfterBreakpoint()
|
||||
{
|
||||
// PC points one byte past the int3 responsible for the breakpoint.
|
||||
lldb::addr_t pc;
|
||||
|
||||
if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
|
||||
return false;
|
||||
|
||||
SetPC(pc - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
RegisterContextLinux_x86_64::ConvertRegisterKindToRegisterNumber(uint32_t kind,
|
||||
uint32_t num)
|
||||
{
|
||||
if (kind == eRegisterKindGeneric)
|
||||
{
|
||||
switch (num)
|
||||
{
|
||||
case LLDB_REGNUM_GENERIC_PC: return gpr_rip;
|
||||
case LLDB_REGNUM_GENERIC_SP: return gpr_rsp;
|
||||
case LLDB_REGNUM_GENERIC_FP: return gpr_rbp;
|
||||
case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags;
|
||||
case LLDB_REGNUM_GENERIC_RA:
|
||||
default:
|
||||
return LLDB_INVALID_REGNUM;
|
||||
}
|
||||
}
|
||||
|
||||
if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
|
||||
{
|
||||
switch (num)
|
||||
{
|
||||
case gcc_dwarf_gpr_rax: return gpr_rax;
|
||||
case gcc_dwarf_gpr_rdx: return gpr_rdx;
|
||||
case gcc_dwarf_gpr_rcx: return gpr_rcx;
|
||||
case gcc_dwarf_gpr_rbx: return gpr_rbx;
|
||||
case gcc_dwarf_gpr_rsi: return gpr_rsi;
|
||||
case gcc_dwarf_gpr_rdi: return gpr_rdi;
|
||||
case gcc_dwarf_gpr_rbp: return gpr_rbp;
|
||||
case gcc_dwarf_gpr_rsp: return gpr_rsp;
|
||||
case gcc_dwarf_gpr_r8: return gpr_r8;
|
||||
case gcc_dwarf_gpr_r9: return gpr_r9;
|
||||
case gcc_dwarf_gpr_r10: return gpr_r10;
|
||||
case gcc_dwarf_gpr_r11: return gpr_r11;
|
||||
case gcc_dwarf_gpr_r12: return gpr_r12;
|
||||
case gcc_dwarf_gpr_r13: return gpr_r13;
|
||||
case gcc_dwarf_gpr_r14: return gpr_r14;
|
||||
case gcc_dwarf_gpr_r15: return gpr_r15;
|
||||
case gcc_dwarf_gpr_rip: return gpr_rip;
|
||||
case gcc_dwarf_fpu_xmm0: return fpu_xmm0;
|
||||
case gcc_dwarf_fpu_xmm1: return fpu_xmm1;
|
||||
case gcc_dwarf_fpu_xmm2: return fpu_xmm2;
|
||||
case gcc_dwarf_fpu_xmm3: return fpu_xmm3;
|
||||
case gcc_dwarf_fpu_xmm4: return fpu_xmm4;
|
||||
case gcc_dwarf_fpu_xmm5: return fpu_xmm5;
|
||||
case gcc_dwarf_fpu_xmm6: return fpu_xmm6;
|
||||
case gcc_dwarf_fpu_xmm7: return fpu_xmm7;
|
||||
case gcc_dwarf_fpu_xmm8: return fpu_xmm8;
|
||||
case gcc_dwarf_fpu_xmm9: return fpu_xmm9;
|
||||
case gcc_dwarf_fpu_xmm10: return fpu_xmm10;
|
||||
case gcc_dwarf_fpu_xmm11: return fpu_xmm11;
|
||||
case gcc_dwarf_fpu_xmm12: return fpu_xmm12;
|
||||
case gcc_dwarf_fpu_xmm13: return fpu_xmm13;
|
||||
case gcc_dwarf_fpu_xmm14: return fpu_xmm14;
|
||||
case gcc_dwarf_fpu_xmm15: return fpu_xmm15;
|
||||
case gcc_dwarf_fpu_stmm0: return fpu_stmm0;
|
||||
case gcc_dwarf_fpu_stmm1: return fpu_stmm1;
|
||||
case gcc_dwarf_fpu_stmm2: return fpu_stmm2;
|
||||
case gcc_dwarf_fpu_stmm3: return fpu_stmm3;
|
||||
case gcc_dwarf_fpu_stmm4: return fpu_stmm4;
|
||||
case gcc_dwarf_fpu_stmm5: return fpu_stmm5;
|
||||
case gcc_dwarf_fpu_stmm6: return fpu_stmm6;
|
||||
case gcc_dwarf_fpu_stmm7: return fpu_stmm7;
|
||||
default:
|
||||
return LLDB_INVALID_REGNUM;
|
||||
}
|
||||
}
|
||||
|
||||
if (kind == eRegisterKindGDB)
|
||||
{
|
||||
switch (num)
|
||||
{
|
||||
case gdb_gpr_rax : return gpr_rax;
|
||||
case gdb_gpr_rbx : return gpr_rbx;
|
||||
case gdb_gpr_rcx : return gpr_rcx;
|
||||
case gdb_gpr_rdx : return gpr_rdx;
|
||||
case gdb_gpr_rsi : return gpr_rsi;
|
||||
case gdb_gpr_rdi : return gpr_rdi;
|
||||
case gdb_gpr_rbp : return gpr_rbp;
|
||||
case gdb_gpr_rsp : return gpr_rsp;
|
||||
case gdb_gpr_r8 : return gpr_r8;
|
||||
case gdb_gpr_r9 : return gpr_r9;
|
||||
case gdb_gpr_r10 : return gpr_r10;
|
||||
case gdb_gpr_r11 : return gpr_r11;
|
||||
case gdb_gpr_r12 : return gpr_r12;
|
||||
case gdb_gpr_r13 : return gpr_r13;
|
||||
case gdb_gpr_r14 : return gpr_r14;
|
||||
case gdb_gpr_r15 : return gpr_r15;
|
||||
case gdb_gpr_rip : return gpr_rip;
|
||||
case gdb_gpr_rflags : return gpr_rflags;
|
||||
case gdb_gpr_cs : return gpr_cs;
|
||||
case gdb_gpr_ss : return gpr_ss;
|
||||
case gdb_gpr_ds : return gpr_ds;
|
||||
case gdb_gpr_es : return gpr_es;
|
||||
case gdb_gpr_fs : return gpr_fs;
|
||||
case gdb_gpr_gs : return gpr_gs;
|
||||
case gdb_fpu_stmm0 : return fpu_stmm0;
|
||||
case gdb_fpu_stmm1 : return fpu_stmm1;
|
||||
case gdb_fpu_stmm2 : return fpu_stmm2;
|
||||
case gdb_fpu_stmm3 : return fpu_stmm3;
|
||||
case gdb_fpu_stmm4 : return fpu_stmm4;
|
||||
case gdb_fpu_stmm5 : return fpu_stmm5;
|
||||
case gdb_fpu_stmm6 : return fpu_stmm6;
|
||||
case gdb_fpu_stmm7 : return fpu_stmm7;
|
||||
case gdb_fpu_fcw : return fpu_fcw;
|
||||
case gdb_fpu_fsw : return fpu_fsw;
|
||||
case gdb_fpu_ftw : return fpu_ftw;
|
||||
case gdb_fpu_cs : return fpu_cs;
|
||||
case gdb_fpu_ip : return fpu_ip;
|
||||
case gdb_fpu_ds : return fpu_ds;
|
||||
case gdb_fpu_dp : return fpu_dp;
|
||||
case gdb_fpu_fop : return fpu_fop;
|
||||
case gdb_fpu_xmm0 : return fpu_xmm0;
|
||||
case gdb_fpu_xmm1 : return fpu_xmm1;
|
||||
case gdb_fpu_xmm2 : return fpu_xmm2;
|
||||
case gdb_fpu_xmm3 : return fpu_xmm3;
|
||||
case gdb_fpu_xmm4 : return fpu_xmm4;
|
||||
case gdb_fpu_xmm5 : return fpu_xmm5;
|
||||
case gdb_fpu_xmm6 : return fpu_xmm6;
|
||||
case gdb_fpu_xmm7 : return fpu_xmm7;
|
||||
case gdb_fpu_xmm8 : return fpu_xmm8;
|
||||
case gdb_fpu_xmm9 : return fpu_xmm9;
|
||||
case gdb_fpu_xmm10 : return fpu_xmm10;
|
||||
case gdb_fpu_xmm11 : return fpu_xmm11;
|
||||
case gdb_fpu_xmm12 : return fpu_xmm12;
|
||||
case gdb_fpu_xmm13 : return fpu_xmm13;
|
||||
case gdb_fpu_xmm14 : return fpu_xmm14;
|
||||
case gdb_fpu_xmm15 : return fpu_xmm15;
|
||||
case gdb_fpu_mxcsr : return fpu_mxcsr;
|
||||
default:
|
||||
return LLDB_INVALID_REGNUM;
|
||||
}
|
||||
}
|
||||
|
||||
return LLDB_INVALID_REGNUM;
|
||||
}
|
||||
|
||||
bool
|
||||
RegisterContextLinux_x86_64::HardwareSingleStep(bool enable)
|
||||
{
|
||||
return GetMonitor().SingleStep(GetThreadID());
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
//===-- RegisterContextLinux_x86_64.h ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_RegisterContextLinux_x86_64_H_
|
||||
#define liblldb_RegisterContextLinux_x86_64_H_
|
||||
|
||||
#include "RegisterContextLinux.h"
|
||||
|
||||
class ProcessMonitor;
|
||||
|
||||
class RegisterContextLinux_x86_64
|
||||
: public RegisterContextLinux
|
||||
{
|
||||
public:
|
||||
RegisterContextLinux_x86_64(lldb_private::Thread &thread,
|
||||
lldb_private::StackFrame *frame);
|
||||
|
||||
~RegisterContextLinux_x86_64();
|
||||
|
||||
void
|
||||
Invalidate();
|
||||
|
||||
size_t
|
||||
GetRegisterCount();
|
||||
|
||||
const lldb::RegisterInfo *
|
||||
GetRegisterInfoAtIndex(uint32_t reg);
|
||||
|
||||
size_t
|
||||
GetRegisterSetCount();
|
||||
|
||||
const lldb::RegisterSet *
|
||||
GetRegisterSet(uint32_t set);
|
||||
|
||||
bool
|
||||
ReadRegisterValue(uint32_t reg, lldb_private::Scalar &value);
|
||||
|
||||
bool
|
||||
ReadRegisterBytes(uint32_t reg, lldb_private::DataExtractor &data);
|
||||
|
||||
bool
|
||||
ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
|
||||
|
||||
bool
|
||||
WriteRegisterValue(uint32_t reg, const lldb_private::Scalar &value);
|
||||
|
||||
bool
|
||||
WriteRegisterBytes(uint32_t reg, lldb_private::DataExtractor &data,
|
||||
uint32_t data_offset = 0);
|
||||
|
||||
bool
|
||||
WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
|
||||
|
||||
uint32_t
|
||||
ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num);
|
||||
|
||||
bool
|
||||
HardwareSingleStep(bool enable);
|
||||
|
||||
bool
|
||||
UpdateAfterBreakpoint();
|
||||
|
||||
struct GPR
|
||||
{
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
uint64_t rbp;
|
||||
uint64_t rbx;
|
||||
uint64_t r11;
|
||||
uint64_t r10;
|
||||
uint64_t r9;
|
||||
uint64_t r8;
|
||||
uint64_t rax;
|
||||
uint64_t rcx;
|
||||
uint64_t rdx;
|
||||
uint64_t rsi;
|
||||
uint64_t rdi;
|
||||
uint64_t orig_ax;
|
||||
uint64_t rip;
|
||||
uint64_t cs;
|
||||
uint64_t rflags;
|
||||
uint64_t rsp;
|
||||
uint64_t ss;
|
||||
uint64_t fs_base;
|
||||
uint64_t gs_base;
|
||||
uint64_t ds;
|
||||
uint64_t es;
|
||||
uint64_t fs;
|
||||
uint64_t gs;
|
||||
};
|
||||
|
||||
struct MMSReg
|
||||
{
|
||||
uint8_t bytes[10];
|
||||
uint8_t pad[6];
|
||||
};
|
||||
|
||||
struct XMMReg
|
||||
{
|
||||
uint8_t bytes[16];
|
||||
};
|
||||
|
||||
struct FPU
|
||||
{
|
||||
uint16_t fcw;
|
||||
uint16_t fsw;
|
||||
uint16_t ftw;
|
||||
uint16_t fop;
|
||||
uint64_t ip;
|
||||
uint64_t dp;
|
||||
uint32_t mxcsr;
|
||||
uint32_t mxcsrmask;
|
||||
MMSReg stmm[8];
|
||||
XMMReg xmm[16];
|
||||
uint32_t padding[24];
|
||||
};
|
||||
|
||||
struct UserArea
|
||||
{
|
||||
GPR regs; // General purpose registers.
|
||||
int32_t fpvalid; // True if FPU is being used.
|
||||
int32_t pad0;
|
||||
FPU i387; // FPU registers.
|
||||
uint64_t tsize; // Text segment size.
|
||||
uint64_t dsize; // Data segment size.
|
||||
uint64_t ssize; // Stack segement size.
|
||||
uint64_t start_code; // VM address of text.
|
||||
uint64_t start_stack; // VM address of stack bottom (top in rsp).
|
||||
int64_t signal; // Signal causing core dump.
|
||||
int32_t reserved; // Unused.
|
||||
int32_t pad1;
|
||||
uint64_t ar0; // Location of GPR's.
|
||||
FPU* fpstate; // Location of FPR's.
|
||||
uint64_t magic; // Identifier for core dumps.
|
||||
char u_comm[32]; // Command causing core dump.
|
||||
uint64_t u_debugreg[8]; // Debug registers (DR0 - DR7).
|
||||
uint64_t error_code; // CPU error code.
|
||||
uint64_t fault_address; // Control register CR3.
|
||||
};
|
||||
|
||||
private:
|
||||
UserArea user;
|
||||
|
||||
ProcessMonitor &GetMonitor();
|
||||
};
|
||||
|
||||
#endif // #ifndef liblldb_RegisterContextLinux_x86_64_H_
|
|
@ -22,6 +22,7 @@
|
|||
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
|
||||
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h"
|
||||
#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h"
|
||||
#include "Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h"
|
||||
|
@ -32,6 +33,10 @@
|
|||
#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include "Plugins/Process/Linux/ProcessLinux.h"
|
||||
#endif
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
|
||||
|
@ -55,10 +60,10 @@ lldb_private::Initialize ()
|
|||
DisassemblerLLVM::Initialize();
|
||||
ObjectContainerBSDArchive::Initialize();
|
||||
ObjectFileELF::Initialize();
|
||||
SymbolVendorMacOSX::Initialize();
|
||||
SymbolFileDWARF::Initialize();
|
||||
SymbolFileDWARFDebugMap::Initialize();
|
||||
SymbolFileSymtab::Initialize();
|
||||
|
||||
#ifdef __APPLE__
|
||||
ABIMacOSX_i386::Initialize();
|
||||
ABISysV_x86_64::Initialize();
|
||||
|
@ -66,7 +71,12 @@ lldb_private::Initialize ()
|
|||
ObjectContainerUniversalMachO::Initialize();
|
||||
ObjectFileMachO::Initialize();
|
||||
ProcessGDBRemote::Initialize();
|
||||
// ProcessMacOSX::Initialize();
|
||||
ProcessMacOSX::Initialize();
|
||||
SymbolVendorMacOSX::Initialize();
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
ProcessLinux::Initialize();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -84,16 +94,21 @@ lldb_private::Terminate ()
|
|||
DisassemblerLLVM::Terminate();
|
||||
ObjectContainerBSDArchive::Terminate();
|
||||
ObjectFileELF::Terminate();
|
||||
SymbolVendorMacOSX::Terminate();
|
||||
SymbolFileDWARF::Terminate();
|
||||
SymbolFileDWARFDebugMap::Terminate();
|
||||
SymbolFileSymtab::Terminate();
|
||||
|
||||
#ifdef __APPLE__
|
||||
DynamicLoaderMacOSXDYLD::Terminate();
|
||||
ObjectContainerUniversalMachO::Terminate();
|
||||
ObjectFileMachO::Terminate();
|
||||
ProcessGDBRemote::Terminate();
|
||||
// ProcessMacOSX::Terminate();
|
||||
ProcessMacOSX::Terminate();
|
||||
SymbolVendorMacOSX::Terminate();
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
ProcessLinux::Terminate();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue