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:
Stephen Wilson 2010-07-24 02:19:04 +00:00
parent ddb46efcca
commit e6f9f66b39
14 changed files with 3033 additions and 9 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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_

View File

@ -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

View File

@ -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;
}

View File

@ -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_

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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_

View File

@ -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());
}

View File

@ -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_

View File

@ -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
}