diff options
| author | Stephen Wilson <wilsons@start.ca> | 2010-07-24 02:19:04 +0000 |
|---|---|---|
| committer | Stephen Wilson <wilsons@start.ca> | 2010-07-24 02:19:04 +0000 |
| commit | e6f9f66b39c05f2b5f0b708095686d6fa17dc6e8 (patch) | |
| tree | fc94bdc38261cce829aa4975a17500356de9a64f /lldb | |
| parent | ddb46efcca906855905d301c42f321a391428ac5 (diff) | |
| download | bcm5719-llvm-e6f9f66b39c05f2b5f0b708095686d6fa17dc6e8.tar.gz bcm5719-llvm-e6f9f66b39c05f2b5f0b708095686d6fa17dc6e8.zip | |
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
Diffstat (limited to 'lldb')
| -rw-r--r-- | lldb/lib/Makefile | 7 | ||||
| -rw-r--r-- | lldb/source/Plugins/Makefile | 11 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/LinuxThread.cpp | 198 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/LinuxThread.h | 89 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/Makefile | 14 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessLinux.cpp | 443 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessLinux.h | 188 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessMessage.h | 88 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp | 925 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/ProcessMonitor.h | 208 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/RegisterContextLinux.h | 40 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.cpp | 653 | ||||
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.h | 155 | ||||
| -rw-r--r-- | lldb/source/lldb.cpp | 23 |
14 files changed, 3033 insertions, 9 deletions
diff --git a/lldb/lib/Makefile b/lldb/lib/Makefile index 50df5831d30..f651e4233e5 100644 --- a/lldb/lib/Makefile +++ b/lldb/lib/Makefile @@ -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 diff --git a/lldb/source/Plugins/Makefile b/lldb/source/Plugins/Makefile index 4b7a2b8d758..ddaa21d12ea 100644 --- a/lldb/source/Plugins/Makefile +++ b/lldb/source/Plugins/Makefile @@ -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 diff --git a/lldb/source/Plugins/Process/Linux/LinuxThread.cpp b/lldb/source/Plugins/Process/Linux/LinuxThread.cpp new file mode 100644 index 00000000000..6d4733afcb2 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/LinuxThread.cpp @@ -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; +} diff --git a/lldb/source/Plugins/Process/Linux/LinuxThread.h b/lldb/source/Plugins/Process/Linux/LinuxThread.h new file mode 100644 index 00000000000..79ccd0a6e5e --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/LinuxThread.h @@ -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_ diff --git a/lldb/source/Plugins/Process/Linux/Makefile b/lldb/source/Plugins/Process/Linux/Makefile new file mode 100644 index 00000000000..0454e6d3415 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/Makefile @@ -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 diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp new file mode 100644 index 00000000000..ce12ee92c56 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp @@ -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; +} diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.h b/lldb/source/Plugins/Process/Linux/ProcessLinux.h new file mode 100644 index 00000000000..522cc4b4c0e --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.h @@ -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_ diff --git a/lldb/source/Plugins/Process/Linux/ProcessMessage.h b/lldb/source/Plugins/Process/Linux/ProcessMessage.h new file mode 100644 index 00000000000..b13321ef6f5 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/ProcessMessage.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_ diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp new file mode 100644 index 00000000000..22b65e1a4b4 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp @@ -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; +} diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.h b/lldb/source/Plugins/Process/Linux/ProcessMonitor.h new file mode 100644 index 00000000000..cd23d98d8d8 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.h @@ -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_ diff --git a/lldb/source/Plugins/Process/Linux/RegisterContextLinux.h b/lldb/source/Plugins/Process/Linux/RegisterContextLinux.h new file mode 100644 index 00000000000..4badd4bb28b --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/RegisterContextLinux.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_ diff --git a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.cpp b/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.cpp new file mode 100644 index 00000000000..2a052682802 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.cpp @@ -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()); +} diff --git a/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.h b/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.h new file mode 100644 index 00000000000..b61de407da9 --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/RegisterContextLinux_x86_64.h @@ -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_ diff --git a/lldb/source/lldb.cpp b/lldb/source/lldb.cpp index 0afcb370215..fc9d0e83f6b 100644 --- a/lldb/source/lldb.cpp +++ b/lldb/source/lldb.cpp @@ -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 } |

