diff options
Diffstat (limited to 'source/Plugins/Process')
83 files changed, 4496 insertions, 1714 deletions
diff --git a/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp b/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp index 2b292442399ff..3cb1cec6983ff 100644 --- a/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp +++ b/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp @@ -558,22 +558,40 @@ FreeBSDThread::WatchNotify(const ProcessMessage &message) void FreeBSDThread::TraceNotify(const ProcessMessage &message) { - POSIXBreakpointProtocol* reg_ctx = GetPOSIXBreakpointProtocol(); - if (reg_ctx) + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + + // Try to resolve the breakpoint object corresponding to the current PC. + assert(GetRegisterContext()); + lldb::addr_t pc = GetRegisterContext()->GetPC(); + if (log) + log->Printf ("FreeBSDThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc); + lldb::BreakpointSiteSP bp_site(GetProcess()->GetBreakpointSiteList().FindByAddress(pc)); + + // If the current pc is a breakpoint site then set the StopInfo to Breakpoint. + // Otherwise, set the StopInfo to Watchpoint or Trace. + // If we have an operating system plug-in, we might have set a thread specific breakpoint using the + // operating system thread ID, so we can't make any assumptions about the thread ID so we must always + // report the breakpoint regardless of the thread. + if (bp_site && (bp_site->ValidForThisThread(this) || GetProcess()->GetOperatingSystem () != NULL)) + SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_site->GetID())); + else { - uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints(); - uint32_t wp_idx; - for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) + POSIXBreakpointProtocol* reg_ctx = GetPOSIXBreakpointProtocol(); + if (reg_ctx) { - if (reg_ctx->IsWatchpointHit(wp_idx)) + uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints(); + uint32_t wp_idx; + for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) { - WatchNotify(message); - return; + if (reg_ctx->IsWatchpointHit(wp_idx)) + { + WatchNotify(message); + return; + } } } + SetStopInfo (StopInfo::CreateStopReasonToTrace(*this)); } - - SetStopInfo (StopInfo::CreateStopReasonToTrace(*this)); } void diff --git a/source/Plugins/Process/FreeBSD/Makefile b/source/Plugins/Process/FreeBSD/Makefile deleted file mode 100644 index 7f546540e5566..0000000000000 --- a/source/Plugins/Process/FreeBSD/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -##===- source/Plugins/Process/FreeBSD/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 := lldbPluginProcessFreeBSD -BUILD_ARCHIVE = 1 - -# Extend the include path so we may locate UnwindLLDB.h -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility - -include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp index 769ccd7248ac1..3a72a65da696f 100644 --- a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp @@ -63,12 +63,12 @@ namespace lldb::ProcessSP ProcessFreeBSD::CreateInstance(lldb::TargetSP target_sp, - Listener &listener, + lldb::ListenerSP listener_sp, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) - process_sp.reset(new ProcessFreeBSD (target_sp, listener, GetFreeBSDSignals())); + process_sp.reset(new ProcessFreeBSD (target_sp, listener_sp, GetFreeBSDSignals())); return process_sp; } @@ -143,7 +143,7 @@ ProcessFreeBSD::DoResume() SetPrivateState(eStateRunning); - Mutex::Locker lock(m_thread_list.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); bool do_step = false; for (tid_collection::const_iterator t_pos = m_run_tids.begin(), t_end = m_run_tids.end(); t_pos != t_end; ++t_pos) @@ -229,7 +229,7 @@ ProcessFreeBSD::WillResume() void ProcessFreeBSD::SendMessage(const ProcessMessage &message) { - Mutex::Locker lock(m_message_mutex); + std::lock_guard<std::recursive_mutex> guard(m_message_mutex); switch (message.GetKind()) { @@ -269,12 +269,12 @@ ProcessFreeBSD::SendMessage(const ProcessMessage &message) //------------------------------------------------------------------------------ // Constructors and destructors. -ProcessFreeBSD::ProcessFreeBSD(lldb::TargetSP target_sp, Listener &listener, UnixSignalsSP &unix_signals_sp) - : Process(target_sp, listener, unix_signals_sp), +ProcessFreeBSD::ProcessFreeBSD(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, UnixSignalsSP &unix_signals_sp) + : Process(target_sp, listener_sp, unix_signals_sp), m_byte_order(endian::InlHostByteOrder()), m_monitor(NULL), m_module(NULL), - m_message_mutex (Mutex::eMutexTypeRecursive), + m_message_mutex(), m_exit_now(false), m_seen_initial_stop(), m_resume_signo(0) @@ -603,7 +603,7 @@ ProcessFreeBSD::RefreshStateAfterStop() if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("ProcessFreeBSD::%s(), message_queue size = %d", __FUNCTION__, (int)m_message_queue.size()); - Mutex::Locker lock(m_message_mutex); + std::lock_guard<std::recursive_mutex> guard(m_message_mutex); // This method used to only handle one message. Changing it to loop allows // it to handle the case where we hit a breakpoint while handling a different @@ -630,7 +630,7 @@ ProcessFreeBSD::RefreshStateAfterStop() if (log) log->Printf ("ProcessFreeBSD::%s() removing thread, tid = %" PRIi64, __FUNCTION__, tid); - Mutex::Locker lock(m_thread_list.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false); thread_sp.reset(); @@ -801,7 +801,7 @@ ProcessFreeBSD::EnableWatchpoint(Watchpoint *wp, bool notify) // Try to find a vacant watchpoint slot in the inferiors' main thread uint32_t wp_hw_index = LLDB_INVALID_INDEX32; - Mutex::Locker lock(m_thread_list.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); FreeBSDThread *thread = static_cast<FreeBSDThread*>( m_thread_list.GetThreadAtIndex(0, false).get()); @@ -871,7 +871,7 @@ ProcessFreeBSD::DisableWatchpoint(Watchpoint *wp, bool notify) if (wp->IsHardware()) { bool wp_disabled = true; - Mutex::Locker lock(m_thread_list.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); uint32_t thread_count = m_thread_list.GetSize(false); for (uint32_t i = 0; i < thread_count; ++i) { @@ -901,7 +901,7 @@ Error ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num) { Error error; - Mutex::Locker lock(m_thread_list.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); FreeBSDThread *thread = static_cast<FreeBSDThread*>( m_thread_list.GetThreadAtIndex(0, false).get()); if (thread) @@ -924,7 +924,7 @@ ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num, bool &after) uint32_t ProcessFreeBSD::UpdateThreadListIfNeeded() { - Mutex::Locker lock(m_thread_list.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); // Do not allow recursive updates. return m_thread_list.GetSize(false); } @@ -1015,7 +1015,7 @@ bool ProcessFreeBSD::IsAThreadRunning() { bool is_running = false; - Mutex::Locker lock(m_thread_list.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex()); uint32_t thread_count = m_thread_list.GetSize(false); for (uint32_t i = 0; i < thread_count; ++i) { diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h index 5f9365418d7af..888e2a90ad76b 100644 --- a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h @@ -13,8 +13,9 @@ // C Includes // C++ Includes -#include <set> +#include <mutex> #include <queue> +#include <set> // Other libraries and framework includes #include "lldb/Target/Process.h" @@ -35,7 +36,7 @@ public: //------------------------------------------------------------------ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener_sp, const lldb_private::FileSpec *crash_file_path); static void @@ -54,7 +55,7 @@ public: // Constructors and destructors //------------------------------------------------------------------ ProcessFreeBSD(lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener_sp, lldb::UnixSignalsSP &unix_signals_sp); ~ProcessFreeBSD(); @@ -212,7 +213,7 @@ protected: lldb_private::Module *m_module; /// Message queue notifying this instance of inferior process state changes. - lldb_private::Mutex m_message_mutex; + std::recursive_mutex m_message_mutex; std::queue<ProcessMessage> m_message_queue; /// Drive any exit events to completion. diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp index cd016fbd4b8c5..16707a5c8b971 100644 --- a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -819,6 +819,8 @@ ProcessMonitor::ProcessMonitor(ProcessFreeBSD *process, m_terminal_fd(-1), m_operation(0) { + using namespace std::placeholders; + std::unique_ptr<LaunchArgs> args(new LaunchArgs(this, module, argv, envp, stdin_file_spec, stdout_file_spec, @@ -856,7 +858,7 @@ WAIT_AGAIN: // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( - ProcessMonitor::MonitorCallback, this, GetPID(), true); + std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4), GetPID(), true); if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); @@ -873,6 +875,8 @@ ProcessMonitor::ProcessMonitor(ProcessFreeBSD *process, m_terminal_fd(-1), m_operation(0) { + using namespace std::placeholders; + sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); @@ -906,7 +910,7 @@ WAIT_AGAIN: // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( - ProcessMonitor::MonitorCallback, this, GetPID(), true); + std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4), GetPID(), true); if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); @@ -1180,14 +1184,9 @@ ProcessMonitor::GetCurrentThreadIDs(std::vector<lldb::tid_t>&thread_ids) } bool -ProcessMonitor::MonitorCallback(void *callback_baton, - lldb::pid_t pid, - bool exited, - int signal, - int status) +ProcessMonitor::MonitorCallback(ProcessMonitor *monitor, lldb::pid_t pid, bool exited, int signal, int status) { ProcessMessage message; - ProcessMonitor *monitor = static_cast<ProcessMonitor*>(callback_baton); ProcessFreeBSD *process = monitor->m_process; assert(process); bool stop_monitoring; @@ -1349,7 +1348,7 @@ ProcessMonitor::ServeOperation(OperationArgs *args) void ProcessMonitor::DoOperation(Operation *op) { - Mutex::Locker lock(m_operation_mutex); + std::lock_guard<std::mutex> guard(m_operation_mutex); m_operation = op; diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/source/Plugins/Process/FreeBSD/ProcessMonitor.h index 07fa6b7869add..93f6be1113610 100644 --- a/source/Plugins/Process/FreeBSD/ProcessMonitor.h +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.h @@ -15,11 +15,12 @@ #include <signal.h> // C++ Includes +#include <mutex> + // Other libraries and framework includes #include "lldb/lldb-types.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/HostThread.h" -#include "lldb/Host/Mutex.h" namespace lldb_private { @@ -223,7 +224,7 @@ private: // current operation which must be executed on the privileged thread Operation *m_operation; - lldb_private::Mutex m_operation_mutex; + std::mutex m_operation_mutex; // semaphores notified when Operation is ready to be processed and when // the operation is complete. @@ -302,8 +303,7 @@ private: DupDescriptor(const lldb_private::FileSpec &file_spec, int fd, int flags); static bool - MonitorCallback(void *callback_baton, - lldb::pid_t pid, bool exited, int signal, int status); + MonitorCallback(ProcessMonitor *monitor, lldb::pid_t pid, bool exited, int signal, int status); static ProcessMessage MonitorSIGTRAP(ProcessMonitor *monitor, diff --git a/source/Plugins/Process/Linux/CMakeLists.txt b/source/Plugins/Process/Linux/CMakeLists.txt index 80de8413d2093..8291fef467e38 100644 --- a/source/Plugins/Process/Linux/CMakeLists.txt +++ b/source/Plugins/Process/Linux/CMakeLists.txt @@ -9,6 +9,8 @@ add_lldb_library(lldbPluginProcessLinux NativeRegisterContextLinux_arm64.cpp NativeRegisterContextLinux_x86_64.cpp NativeRegisterContextLinux_mips64.cpp + NativeRegisterContextLinux_s390x.cpp NativeThreadLinux.cpp ProcFileReader.cpp + SingleStepCheck.cpp ) diff --git a/source/Plugins/Process/Linux/Makefile b/source/Plugins/Process/Linux/Makefile deleted file mode 100644 index 239e94d608e9b..0000000000000 --- a/source/Plugins/Process/Linux/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -##===- 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 - -# Extend the include path so we may locate UnwindLLDB.h -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility - -include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 87c76f57830c5..b3842302c6db5 100644 --- a/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -25,15 +25,14 @@ // Other libraries and framework includes #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/Error.h" -#include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" -#include "lldb/Host/common/NativeBreakpoint.h" -#include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/Host.h" #include "lldb/Host/ThreadLauncher.h" -#include "lldb/Target/Platform.h" +#include "lldb/Host/common/NativeBreakpoint.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Process.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/Target.h" @@ -58,7 +57,6 @@ #include "lldb/Host/linux/Personality.h" #include "lldb/Host/linux/Ptrace.h" -#include "lldb/Host/linux/Signalfd.h" #include "lldb/Host/linux/Uio.h" #include "lldb/Host/android/Android.h" @@ -111,45 +109,96 @@ static bool ProcessVmReadvSupported() namespace { - Error - ResolveProcessArchitecture (lldb::pid_t pid, Platform &platform, ArchSpec &arch) - { - // Grab process info for the running process. - ProcessInstanceInfo process_info; - if (!platform.GetProcessInfo (pid, process_info)) - return Error("failed to get process info"); +Error +ResolveProcessArchitecture(lldb::pid_t pid, ArchSpec &arch) +{ + // Grab process info for the running process. + ProcessInstanceInfo process_info; + if (!Host::GetProcessInfo(pid, process_info)) + return Error("failed to get process info"); - // Resolve the executable module. - ModuleSP exe_module_sp; - ModuleSpec exe_module_spec(process_info.GetExecutableFile(), process_info.GetArchitecture()); - FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths ()); - Error error = platform.ResolveExecutable( - exe_module_spec, - exe_module_sp, - executable_search_paths.GetSize () ? &executable_search_paths : NULL); + // Resolve the executable module. + ModuleSpecList module_specs; + if (!ObjectFile::GetModuleSpecifications(process_info.GetExecutableFile(), 0, 0, module_specs)) + return Error("failed to get module specifications"); + assert(module_specs.GetSize() == 1); - if (!error.Success ()) - return error; + arch = module_specs.GetModuleSpecRefAtIndex(0).GetArchitecture(); + if (arch.IsValid()) + return Error(); + else + return Error("failed to retrieve a valid architecture from the exe module"); +} - // Check if we've got our architecture from the exe_module. - arch = exe_module_sp->GetArchitecture (); - if (arch.IsValid ()) - return Error(); - else - return Error("failed to retrieve a valid architecture from the exe module"); - } +// Used to notify the parent about which part of the launch sequence failed. +enum LaunchCallSpecifier +{ + ePtraceFailed, + eDupStdinFailed, + eDupStdoutFailed, + eDupStderrFailed, + eChdirFailed, + eExecFailed, + eSetGidFailed, + eSetSigMaskFailed, + eLaunchCallMax = eSetSigMaskFailed +}; - void - DisplayBytes (StreamString &s, void *bytes, uint32_t count) +static uint8_t LLVM_ATTRIBUTE_NORETURN +ExitChildAbnormally(LaunchCallSpecifier spec) +{ + static_assert(eLaunchCallMax < 0x8, "Have more launch calls than we are able to represent"); + // This may truncate the topmost bits of the errno because the exit code is only 8 bits wide. + // However, it should still give us a pretty good indication of what went wrong. (And the + // most common errors have small numbers anyway). + _exit(unsigned(spec) | (errno << 3)); +} + +// The second member is the errno (or its 5 lowermost bits anyway). +inline std::pair<LaunchCallSpecifier, uint8_t> +DecodeChildExitCode(int exit_code) +{ + return std::make_pair(LaunchCallSpecifier(exit_code & 0x7), exit_code >> 3); +} + +void +MaybeLogLaunchInfo(const ProcessLaunchInfo &info) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (!log) + return; + + if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO)) + log->Printf("%s: setting STDIN to '%s'", __FUNCTION__, action->GetFileSpec().GetCString()); + else + log->Printf("%s leaving STDIN as is", __FUNCTION__); + + if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO)) + log->Printf("%s setting STDOUT to '%s'", __FUNCTION__, action->GetFileSpec().GetCString()); + else + log->Printf("%s leaving STDOUT as is", __FUNCTION__); + + if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO)) + log->Printf("%s setting STDERR to '%s'", __FUNCTION__, action->GetFileSpec().GetCString()); + else + log->Printf("%s leaving STDERR as is", __FUNCTION__); + + int i = 0; + for (const char **args = info.GetArguments().GetConstArgumentVector(); *args; ++args, ++i) + log->Printf("%s arg %d: \"%s\"", __FUNCTION__, i, *args ? *args : "nullptr"); +} + +void +DisplayBytes(StreamString &s, void *bytes, uint32_t count) +{ + uint8_t *ptr = (uint8_t *)bytes; + const uint32_t loop_count = std::min<uint32_t>(DEBUG_PTRACE_MAXBYTES, count); + for (uint32_t i = 0; i < loop_count; i++) { - uint8_t *ptr = (uint8_t *)bytes; - const uint32_t loop_count = std::min<uint32_t>(DEBUG_PTRACE_MAXBYTES, count); - for(uint32_t i=0; i<loop_count; i++) - { - s.Printf ("[%x]", *ptr); - ptr++; - } + s.Printf("[%x]", *ptr); + ptr++; } +} void PtraceDisplayBytes(int &req, void *data, size_t data_size) @@ -239,28 +288,6 @@ EnsureFDFlags(int fd, int flags) return error; } -NativeProcessLinux::LaunchArgs::LaunchArgs(Module *module, - char const **argv, - char const **envp, - const FileSpec &stdin_file_spec, - const FileSpec &stdout_file_spec, - const FileSpec &stderr_file_spec, - const FileSpec &working_dir, - const ProcessLaunchInfo &launch_info) - : m_module(module), - m_argv(argv), - m_envp(envp), - m_stdin_file_spec(stdin_file_spec), - m_stdout_file_spec(stdout_file_spec), - m_stderr_file_spec(stderr_file_spec), - m_working_dir(working_dir), - m_launch_info(launch_info) -{ -} - -NativeProcessLinux::LaunchArgs::~LaunchArgs() -{ } - // ----------------------------------------------------------------------------- // Public Static Methods // ----------------------------------------------------------------------------- @@ -274,15 +301,7 @@ NativeProcessProtocol::Launch ( { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - lldb::ModuleSP exe_module_sp; - PlatformSP platform_sp (Platform::GetHostPlatform ()); - Error error = platform_sp->ResolveExecutable( - ModuleSpec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()), - exe_module_sp, - nullptr); - - if (! error.Success()) - return error; + Error error; // Verify the working directory is valid if one was specified. FileSpec working_dir{launch_info.GetWorkingDirectory()}; @@ -295,59 +314,9 @@ NativeProcessProtocol::Launch ( return error; } - const FileAction *file_action; - - // Default of empty will mean to use existing open file descriptors. - FileSpec stdin_file_spec{}; - FileSpec stdout_file_spec{}; - FileSpec stderr_file_spec{}; - - file_action = launch_info.GetFileActionForFD (STDIN_FILENO); - if (file_action) - stdin_file_spec = file_action->GetFileSpec(); - - file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); - if (file_action) - stdout_file_spec = file_action->GetFileSpec(); - - file_action = launch_info.GetFileActionForFD (STDERR_FILENO); - if (file_action) - stderr_file_spec = file_action->GetFileSpec(); - - if (log) - { - if (stdin_file_spec) - log->Printf ("NativeProcessLinux::%s setting STDIN to '%s'", - __FUNCTION__, stdin_file_spec.GetCString()); - else - log->Printf ("NativeProcessLinux::%s leaving STDIN as is", __FUNCTION__); - - if (stdout_file_spec) - log->Printf ("NativeProcessLinux::%s setting STDOUT to '%s'", - __FUNCTION__, stdout_file_spec.GetCString()); - else - log->Printf ("NativeProcessLinux::%s leaving STDOUT as is", __FUNCTION__); - - if (stderr_file_spec) - log->Printf ("NativeProcessLinux::%s setting STDERR to '%s'", - __FUNCTION__, stderr_file_spec.GetCString()); - else - log->Printf ("NativeProcessLinux::%s leaving STDERR as is", __FUNCTION__); - } - // Create the NativeProcessLinux in launch mode. native_process_sp.reset (new NativeProcessLinux ()); - if (log) - { - int i = 0; - for (const char **args = launch_info.GetArguments ().GetConstArgumentVector (); *args; ++args, ++i) - { - log->Printf ("NativeProcessLinux::%s arg %d: \"%s\"", __FUNCTION__, i, *args ? *args : "nullptr"); - ++i; - } - } - if (!native_process_sp->RegisterNativeDelegate (native_delegate)) { native_process_sp.reset (); @@ -355,17 +324,7 @@ NativeProcessProtocol::Launch ( return error; } - std::static_pointer_cast<NativeProcessLinux> (native_process_sp)->LaunchInferior ( - mainloop, - exe_module_sp.get(), - launch_info.GetArguments ().GetConstArgumentVector (), - launch_info.GetEnvironmentEntries ().GetConstArgumentVector (), - stdin_file_spec, - stdout_file_spec, - stderr_file_spec, - working_dir, - launch_info, - error); + error = std::static_pointer_cast<NativeProcessLinux>(native_process_sp)->LaunchInferior(mainloop, launch_info); if (error.Fail ()) { @@ -391,15 +350,9 @@ NativeProcessProtocol::Attach ( if (log && log->GetMask ().Test (POSIX_LOG_VERBOSE)) log->Printf ("NativeProcessLinux::%s(pid = %" PRIi64 ")", __FUNCTION__, pid); - // Grab the current platform architecture. This should be Linux, - // since this code is only intended to run on a Linux host. - PlatformSP platform_sp (Platform::GetHostPlatform ()); - if (!platform_sp) - return Error("failed to get a valid default platform"); - // Retrieve the architecture for the running process. ArchSpec process_arch; - Error error = ResolveProcessArchitecture (pid, *platform_sp.get (), process_arch); + Error error = ResolveProcessArchitecture(pid, process_arch); if (!error.Success ()) return error; @@ -428,227 +381,170 @@ NativeProcessLinux::NativeProcessLinux () : m_arch (), m_supports_mem_region (eLazyBoolCalculate), m_mem_region_cache (), - m_mem_region_cache_mutex(), m_pending_notification_tid(LLDB_INVALID_THREAD_ID) { } void -NativeProcessLinux::LaunchInferior ( - MainLoop &mainloop, - Module *module, - const char *argv[], - const char *envp[], - const FileSpec &stdin_file_spec, - const FileSpec &stdout_file_spec, - const FileSpec &stderr_file_spec, - const FileSpec &working_dir, - const ProcessLaunchInfo &launch_info, - Error &error) +NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error) { + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ")", __FUNCTION__, pid); + m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD, [this] (MainLoopBase &) { SigchldHandler(); }, error); if (! m_sigchld_handle) return; - if (module) - m_arch = module->GetArchitecture (); + error = ResolveProcessArchitecture(pid, m_arch); + if (!error.Success()) + return; - SetState (eStateLaunching); + // Set the architecture to the exe architecture. + if (log) + log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ") detected architecture %s", __FUNCTION__, pid, m_arch.GetArchitectureName ()); - std::unique_ptr<LaunchArgs> args( - new LaunchArgs(module, argv, envp, - stdin_file_spec, - stdout_file_spec, - stderr_file_spec, - working_dir, - launch_info)); + m_pid = pid; + SetState(eStateAttaching); - Launch(args.get(), error); + Attach(pid, error); } void -NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error) +NativeProcessLinux::ChildFunc(const ProcessLaunchInfo &info) { - Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ")", __FUNCTION__, pid); + // Start tracing this child that is about to exec. + if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) + ExitChildAbnormally(ePtraceFailed); - m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD, - [this] (MainLoopBase &) { SigchldHandler(); }, error); - if (! m_sigchld_handle) - return; + // Do not inherit setgid powers. + if (setgid(getgid()) != 0) + ExitChildAbnormally(eSetGidFailed); - // We can use the Host for everything except the ResolveExecutable portion. - PlatformSP platform_sp = Platform::GetHostPlatform (); - if (!platform_sp) + // Attempt to have our own process group. + if (setpgid(0, 0) != 0) { - if (log) - log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 "): no default platform set", __FUNCTION__, pid); - error.SetErrorString ("no default platform available"); - return; + // FIXME log that this failed. This is common. + // Don't allow this to prevent an inferior exec. } - // Gather info about the process. - ProcessInstanceInfo process_info; - if (!platform_sp->GetProcessInfo (pid, process_info)) + // Dup file descriptors if needed. + if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO)) + if (!DupDescriptor(action->GetFileSpec(), STDIN_FILENO, O_RDONLY)) + ExitChildAbnormally(eDupStdinFailed); + + if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO)) + if (!DupDescriptor(action->GetFileSpec(), STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + ExitChildAbnormally(eDupStdoutFailed); + + if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO)) + if (!DupDescriptor(action->GetFileSpec(), STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) + ExitChildAbnormally(eDupStderrFailed); + + // Close everything besides stdin, stdout, and stderr that has no file + // action to avoid leaking + for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) + if (!info.GetFileActionForFD(fd)) + close(fd); + + // Change working directory + if (info.GetWorkingDirectory() && 0 != ::chdir(info.GetWorkingDirectory().GetCString())) + ExitChildAbnormally(eChdirFailed); + + // Disable ASLR if requested. + if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) { - if (log) - log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 "): failed to get process info", __FUNCTION__, pid); - error.SetErrorString ("failed to get process info"); - return; + const int old_personality = personality(LLDB_PERSONALITY_GET_CURRENT_SETTINGS); + if (old_personality == -1) + { + // Can't retrieve Linux personality. Cannot disable ASLR. + } + else + { + const int new_personality = personality(ADDR_NO_RANDOMIZE | old_personality); + if (new_personality == -1) + { + // Disabling ASLR failed. + } + else + { + // Disabling ASLR succeeded. + } + } } - // Resolve the executable module - ModuleSP exe_module_sp; - FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); - ModuleSpec exe_module_spec(process_info.GetExecutableFile(), process_info.GetArchitecture()); - error = platform_sp->ResolveExecutable(exe_module_spec, exe_module_sp, - executable_search_paths.GetSize() ? &executable_search_paths : NULL); - if (!error.Success()) - return; + // Clear the signal mask to prevent the child from being affected by + // any masking done by the parent. + sigset_t set; + if (sigemptyset(&set) != 0 || pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) + ExitChildAbnormally(eSetSigMaskFailed); - // Set the architecture to the exe architecture. - m_arch = exe_module_sp->GetArchitecture(); - if (log) - log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ") detected architecture %s", __FUNCTION__, pid, m_arch.GetArchitectureName ()); + const char **argv = info.GetArguments().GetConstArgumentVector(); - m_pid = pid; - SetState(eStateAttaching); + // Propagate the environment if one is not supplied. + const char **envp = info.GetEnvironmentEntries().GetConstArgumentVector(); + if (envp == NULL || envp[0] == NULL) + envp = const_cast<const char **>(environ); - Attach(pid, error); + // Execute. We should never return... + execve(argv[0], const_cast<char *const *>(argv), const_cast<char *const *>(envp)); + + if (errno == ETXTBSY) + { + // On android M and earlier we can get this error because the adb deamon can hold a write + // handle on the executable even after it has finished uploading it. This state lasts + // only a short time and happens only when there are many concurrent adb commands being + // issued, such as when running the test suite. (The file remains open when someone does + // an "adb shell" command in the fork() child before it has had a chance to exec.) Since + // this state should clear up quickly, wait a while and then give it one more go. + usleep(50000); + execve(argv[0], const_cast<char *const *>(argv), const_cast<char *const *>(envp)); + } + + // ...unless exec fails. In which case we definitely need to end the child here. + ExitChildAbnormally(eExecFailed); } -::pid_t -NativeProcessLinux::Launch(LaunchArgs *args, Error &error) +Error +NativeProcessLinux::LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch_info) { - assert (args && "null args"); + Error error; + m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error); + if (!m_sigchld_handle) + return error; - const char **argv = args->m_argv; - const char **envp = args->m_envp; - const FileSpec working_dir = args->m_working_dir; + SetState(eStateLaunching); lldb_utility::PseudoTerminal terminal; const size_t err_len = 1024; char err_str[err_len]; lldb::pid_t pid; - // Propagate the environment if one is not supplied. - if (envp == NULL || envp[0] == NULL) - envp = const_cast<const char **>(environ); + MaybeLogLaunchInfo(launch_info); if ((pid = terminal.Fork(err_str, err_len)) == static_cast<lldb::pid_t> (-1)) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("Process fork failed: %s", err_str); - return -1; + return error; } - // Recognized child exit status codes. - enum { - ePtraceFailed = 1, - eDupStdinFailed, - eDupStdoutFailed, - eDupStderrFailed, - eChdirFailed, - eExecFailed, - eSetGidFailed, - eSetSigMaskFailed - }; - // Child process. if (pid == 0) { // First, make sure we disable all logging. If we are logging to stdout, our logs can be // mistaken for inferior output. Log::DisableAllLogChannels(nullptr); - // FIXME consider opening a pipe between parent/child and have this forked child - // send log info to parent re: launch status. - - // Start tracing this child that is about to exec. - error = PtraceWrapper(PTRACE_TRACEME, 0); - if (error.Fail()) - exit(ePtraceFailed); // terminal has already dupped the tty descriptors to stdin/out/err. // This closes original fd from which they were copied (and avoids // leaking descriptors to the debugged process. terminal.CloseSlaveFileDescriptor(); - // Do not inherit setgid powers. - if (setgid(getgid()) != 0) - exit(eSetGidFailed); - - // Attempt to have our own process group. - if (setpgid(0, 0) != 0) - { - // FIXME log that this failed. This is common. - // Don't allow this to prevent an inferior exec. - } - - // Dup file descriptors if needed. - if (args->m_stdin_file_spec) - if (!DupDescriptor(args->m_stdin_file_spec, STDIN_FILENO, O_RDONLY)) - exit(eDupStdinFailed); - - if (args->m_stdout_file_spec) - if (!DupDescriptor(args->m_stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) - exit(eDupStdoutFailed); - - if (args->m_stderr_file_spec) - if (!DupDescriptor(args->m_stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) - exit(eDupStderrFailed); - - // Close everything besides stdin, stdout, and stderr that has no file - // action to avoid leaking - for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) - if (!args->m_launch_info.GetFileActionForFD(fd)) - close(fd); - - // Change working directory - if (working_dir && 0 != ::chdir(working_dir.GetCString())) - exit(eChdirFailed); - - // Disable ASLR if requested. - if (args->m_launch_info.GetFlags ().Test (lldb::eLaunchFlagDisableASLR)) - { - const int old_personality = personality (LLDB_PERSONALITY_GET_CURRENT_SETTINGS); - if (old_personality == -1) - { - // Can't retrieve Linux personality. Cannot disable ASLR. - } - else - { - const int new_personality = personality (ADDR_NO_RANDOMIZE | old_personality); - if (new_personality == -1) - { - // Disabling ASLR failed. - } - else - { - // Disabling ASLR succeeded. - } - } - } - - // Clear the signal mask to prevent the child from being affected by - // any masking done by the parent. - sigset_t set; - if (sigemptyset(&set) != 0 || pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) - exit(eSetSigMaskFailed); - - // Execute. We should never return... - execve(argv[0], - const_cast<char *const *>(argv), - const_cast<char *const *>(envp)); - - // ...unless exec fails. In which case we definitely need to end the child here. - exit(eExecFailed); + ChildFunc(launch_info); } - // - // This is the parent code here. - // Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); // Wait for the child process to trap on its call to execve. @@ -665,42 +561,41 @@ NativeProcessLinux::Launch(LaunchArgs *args, Error &error) // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. SetState (StateType::eStateInvalid); - return -1; + return error; } else if (WIFEXITED(status)) { - // open, dup or execve likely failed for some reason. - error.SetErrorToGenericError(); - switch (WEXITSTATUS(status)) + auto p = DecodeChildExitCode(WEXITSTATUS(status)); + Error child_error(p.second, eErrorTypePOSIX); + const char *failure_reason; + switch (p.first) { case ePtraceFailed: - error.SetErrorString("Child ptrace failed."); + failure_reason = "Child ptrace failed"; break; case eDupStdinFailed: - error.SetErrorString("Child open stdin failed."); + failure_reason = "Child open stdin failed"; break; case eDupStdoutFailed: - error.SetErrorString("Child open stdout failed."); + failure_reason = "Child open stdout failed"; break; case eDupStderrFailed: - error.SetErrorString("Child open stderr failed."); + failure_reason = "Child open stderr failed"; break; case eChdirFailed: - error.SetErrorString("Child failed to set working directory."); + failure_reason = "Child failed to set working directory"; break; case eExecFailed: - error.SetErrorString("Child exec failed."); + failure_reason = "Child exec failed"; break; case eSetGidFailed: - error.SetErrorString("Child setgid failed."); + failure_reason = "Child setgid failed"; break; case eSetSigMaskFailed: - error.SetErrorString("Child failed to set signal mask."); - break; - default: - error.SetErrorString("Child returned unknown exit status."); + failure_reason = "Child failed to set signal mask"; break; } + error.SetErrorStringWithFormat("%s: %d - %s (error code truncated)", failure_reason, child_error.GetError(), child_error.AsCString()); if (log) { @@ -713,7 +608,7 @@ NativeProcessLinux::Launch(LaunchArgs *args, Error &error) // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. SetState (StateType::eStateInvalid); - return -1; + return error; } assert(WIFSTOPPED(status) && (wpid == static_cast< ::pid_t> (pid)) && "Could not sync with inferior process."); @@ -732,13 +627,14 @@ NativeProcessLinux::Launch(LaunchArgs *args, Error &error) // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. SetState (StateType::eStateInvalid); - return -1; + return error; } // Release the master terminal descriptor and pass it off to the // NativeProcessLinux instance. Similarly stash the inferior pid. m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); m_pid = pid; + launch_info.SetProcessID(pid); // Set the terminal fd to be in non blocking mode (it simplifies the // implementation of ProcessLinux::GetSTDOUT to have a non-blocking @@ -754,12 +650,13 @@ NativeProcessLinux::Launch(LaunchArgs *args, Error &error) // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid. SetState (StateType::eStateInvalid); - return -1; + return error; } if (log) log->Printf ("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, pid); + ResolveProcessArchitecture(m_pid, m_arch); NativeThreadLinuxSP thread_sp = AddThread(pid); assert (thread_sp && "AddThread() returned a nullptr thread"); thread_sp->SetStoppedBySignal(SIGSTOP); @@ -772,17 +669,11 @@ NativeProcessLinux::Launch(LaunchArgs *args, Error &error) if (log) { if (error.Success ()) - { - log->Printf ("NativeProcessLinux::%s inferior launching succeeded", __FUNCTION__); - } + log->Printf("NativeProcessLinux::%s inferior launching succeeded", __FUNCTION__); else - { - log->Printf ("NativeProcessLinux::%s inferior launching failed: %s", - __FUNCTION__, error.AsCString ()); - return -1; - } + log->Printf("NativeProcessLinux::%s inferior launching failed: %s", __FUNCTION__, error.AsCString()); } - return pid; + return error; } ::pid_t @@ -1150,8 +1041,6 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thr assert(info.si_signo == SIGTRAP && "Unexpected child signal!"); - Mutex::Locker locker (m_threads_mutex); - switch (info.si_code) { // TODO: these two cases are required if we want to support tracing of the inferiors' children. We'd need this to debug a monitor. @@ -1187,7 +1076,7 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thr // Exec clears any pending notifications. m_pending_notification_tid = LLDB_INVALID_THREAD_ID; - // Remove all but the main thread here. Linux fork creates a new process which only copies the main thread. Mutexes are in undefined state. + // Remove all but the main thread here. Linux fork creates a new process which only copies the main thread. if (log) log->Printf ("NativeProcessLinux::%s exec received, stop tracking all but main thread", __FUNCTION__); @@ -1413,8 +1302,6 @@ NativeProcessLinux::MonitorSignal(const siginfo_t &info, NativeThreadLinux &thre // // Similarly, ACK signals generated by this monitor. - Mutex::Locker locker (m_threads_mutex); - // Handle the signal. if (info.si_code == SI_TKILL || info.si_code == SI_USER) { @@ -1713,8 +1600,6 @@ NativeProcessLinux::Resume (const ResumeActionList &resume_actions) bool software_single_step = !SupportHardwareSingleStepping(); - Mutex::Locker locker (m_threads_mutex); - if (software_single_step) { for (auto thread_sp : m_threads) @@ -1840,8 +1725,6 @@ NativeProcessLinux::Interrupt () if (log) log->Printf ("NativeProcessLinux::%s selecting running thread for interrupt target", __FUNCTION__); - Mutex::Locker locker (m_threads_mutex); - for (auto thread_sp : m_threads) { // The thread shouldn't be null but lets just cover that here. @@ -1956,6 +1839,9 @@ ParseMemoryRegionInfoFromProcMapsLine (const std::string &maps_line, MemoryRegio memory_region_info.GetRange ().SetRangeBase (start_address); memory_region_info.GetRange ().SetRangeEnd (end_address); + // Any memory region in /proc/{pid}/maps is by definition mapped into the process. + memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); + // Parse out each permission entry. if (line_extractor.GetBytesLeft () < 4) return Error ("malformed /proc/{pid}/maps entry, missing some portion of permissions"); @@ -1964,31 +1850,28 @@ ParseMemoryRegionInfoFromProcMapsLine (const std::string &maps_line, MemoryRegio const char read_perm_char = line_extractor.GetChar (); if (read_perm_char == 'r') memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eYes); - else - { - assert ( (read_perm_char == '-') && "unexpected /proc/{pid}/maps read permission char" ); + else if (read_perm_char == '-') memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); - } + else + return Error ("unexpected /proc/{pid}/maps read permission char"); // Handle write permission. const char write_perm_char = line_extractor.GetChar (); if (write_perm_char == 'w') memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eYes); - else - { - assert ( (write_perm_char == '-') && "unexpected /proc/{pid}/maps write permission char" ); + else if (write_perm_char == '-') memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); - } + else + return Error ("unexpected /proc/{pid}/maps write permission char"); // Handle execute permission. const char exec_perm_char = line_extractor.GetChar (); if (exec_perm_char == 'x') memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eYes); - else - { - assert ( (exec_perm_char == '-') && "unexpected /proc/{pid}/maps exec permission char" ); + else if (exec_perm_char == '-') memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); - } + else + return Error ("unexpected /proc/{pid}/maps exec permission char"); return Error (); } @@ -2002,7 +1885,6 @@ NativeProcessLinux::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInf // Use an approach that reads memory regions from /proc/{pid}/maps. // Assume proc maps entries are in ascending order. // FIXME assert if we find differently. - Mutex::Locker locker (m_mem_region_cache_mutex); Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); Error error; @@ -2085,6 +1967,7 @@ NativeProcessLinux::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInf range_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } @@ -2102,21 +1985,11 @@ NativeProcessLinux::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInf // load_addr as start and the amount of bytes betwwen load address and the end of the memory as // size. range_info.GetRange ().SetRangeBase (load_addr); - switch (m_arch.GetAddressByteSize()) - { - case 4: - range_info.GetRange ().SetByteSize (0x100000000ull - load_addr); - break; - case 8: - range_info.GetRange ().SetByteSize (0ull - load_addr); - break; - default: - assert(false && "Unrecognized data byte size"); - break; - } + range_info.GetRange ().SetRangeEnd(LLDB_INVALID_ADDRESS); range_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } @@ -2127,12 +2000,9 @@ NativeProcessLinux::DoStopIDBumped (uint32_t newBumpId) if (log) log->Printf ("NativeProcessLinux::%s(newBumpId=%" PRIu32 ") called", __FUNCTION__, newBumpId); - { - Mutex::Locker locker (m_mem_region_cache_mutex); if (log) log->Printf ("NativeProcessLinux::%s clearing %" PRIu64 " entries from the cache", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ())); m_mem_region_cache.clear (); - } } Error @@ -2179,49 +2049,8 @@ NativeProcessLinux::DeallocateMemory (lldb::addr_t addr) lldb::addr_t NativeProcessLinux::GetSharedLibraryInfoAddress () { -#if 1 // punt on this for now return LLDB_INVALID_ADDRESS; -#else - // Return the image info address for the exe module -#if 1 - Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - - ModuleSP module_sp; - Error error = GetExeModuleSP (module_sp); - if (error.Fail ()) - { - if (log) - log->Warning ("NativeProcessLinux::%s failed to retrieve exe module: %s", __FUNCTION__, error.AsCString ()); - return LLDB_INVALID_ADDRESS; - } - - if (module_sp == nullptr) - { - if (log) - log->Warning ("NativeProcessLinux::%s exe module returned was NULL", __FUNCTION__); - return LLDB_INVALID_ADDRESS; - } - - ObjectFileSP object_file_sp = module_sp->GetObjectFile (); - if (object_file_sp == nullptr) - { - if (log) - log->Warning ("NativeProcessLinux::%s exe module returned a NULL object file", __FUNCTION__); - return LLDB_INVALID_ADDRESS; - } - - return obj_file_sp->GetImageInfoAddress(); -#else - Target *target = &GetTarget(); - ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); - Address addr = obj_file->GetImageInfoAddress(target); - - if (addr.IsValid()) - return addr.GetLoadAddress(target); - return LLDB_INVALID_ADDRESS; -#endif -#endif // punt on this for now } size_t @@ -2231,7 +2060,6 @@ NativeProcessLinux::UpdateThreads () // with respect to thread state and they keep the thread list // populated properly. All this method needs to do is return the // thread count. - Mutex::Locker locker (m_threads_mutex); return m_threads.size (); } @@ -2248,6 +2076,7 @@ NativeProcessLinux::GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size) // FIXME put this behind a breakpoint protocol class that can be // set per architecture. Need ARM, MIPS support here. static const uint8_t g_i386_opcode [] = { 0xCC }; + static const uint8_t g_s390x_opcode[] = { 0x00, 0x01 }; switch (m_arch.GetMachine ()) { @@ -2256,6 +2085,10 @@ NativeProcessLinux::GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size) actual_opcode_size = static_cast<uint32_t> (sizeof(g_i386_opcode)); return Error (); + case llvm::Triple::systemz: + actual_opcode_size = static_cast<uint32_t> (sizeof(g_s390x_opcode)); + return Error (); + case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::mips64: @@ -2295,6 +2128,7 @@ NativeProcessLinux::GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hin static const uint8_t g_i386_opcode [] = { 0xCC }; static const uint8_t g_mips64_opcode[] = { 0x00, 0x00, 0x00, 0x0d }; static const uint8_t g_mips64el_opcode[] = { 0x0d, 0x00, 0x00, 0x00 }; + static const uint8_t g_s390x_opcode[] = { 0x00, 0x01 }; static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde }; switch (m_arch.GetMachine ()) @@ -2338,6 +2172,11 @@ NativeProcessLinux::GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hin actual_opcode_size = sizeof(g_mips64el_opcode); return Error (); + case llvm::Triple::systemz: + trap_opcode_bytes = g_s390x_opcode; + actual_opcode_size = sizeof(g_s390x_opcode); + return Error (); + default: assert(false && "CPU type not supported!"); return Error ("CPU type not supported"); @@ -2656,43 +2495,6 @@ NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, } Error -NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo) -{ - Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - - if (log) - log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid, - Host::GetSignalAsCString(signo)); - - - - intptr_t data = 0; - - if (signo != LLDB_INVALID_SIGNAL_NUMBER) - data = signo; - - Error error = PtraceWrapper(PTRACE_CONT, tid, nullptr, (void*)data); - - if (log) - log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " result = %s", __FUNCTION__, tid, error.Success() ? "true" : "false"); - return error; -} - -Error -NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) -{ - intptr_t data = 0; - - if (signo != LLDB_INVALID_SIGNAL_NUMBER) - data = signo; - - // If hardware single-stepping is not supported, we just do a continue. The breakpoint on the - // next instruction has been setup in NativeProcessLinux::Resume. - return PtraceWrapper(SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT, - tid, nullptr, (void*)data); -} - -Error NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) { return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo); @@ -2754,7 +2556,6 @@ NativeProcessLinux::StopTrackingThread (lldb::tid_t thread_id) bool found = false; - Mutex::Locker locker (m_threads_mutex); for (auto it = m_threads.begin (); it != m_threads.end (); ++it) { if (*it && ((*it)->GetID () == thread_id)) @@ -2775,8 +2576,6 @@ NativeProcessLinux::AddThread (lldb::tid_t thread_id) { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); - Mutex::Locker locker (m_threads_mutex); - if (log) { log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " adding thread with tid %" PRIu64, @@ -2980,16 +2779,14 @@ NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType stat { case eStateRunning: { - thread.SetRunning(); - const auto resume_result = Resume(thread.GetID(), signo); + const auto resume_result = thread.Resume(signo); if (resume_result.Success()) SetState(eStateRunning, true); return resume_result; } case eStateStepping: { - thread.SetStepping(); - const auto step_result = SingleStep(thread.GetID(), signo); + const auto step_result = thread.SingleStep(signo); if (step_result.Success()) SetState(eStateRunning, true); return step_result; diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.h b/source/Plugins/Process/Linux/NativeProcessLinux.h index 7bac1dc8dbb7e..d5be06f0cb580 100644 --- a/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -19,7 +19,6 @@ #include "lldb/Host/Debug.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/HostThread.h" -#include "lldb/Host/Mutex.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Host/common/NativeProcessProtocol.h" @@ -27,7 +26,6 @@ namespace lldb_private { class Error; - class Module; class Scalar; namespace process_linux { @@ -127,6 +125,9 @@ namespace process_linux { size_t data_size = 0, long *result = nullptr); + bool + SupportHardwareSingleStepping() const; + protected: // --------------------------------------------------------------------- // NativeProcessProtocol protected interface @@ -141,7 +142,6 @@ namespace process_linux { LazyBool m_supports_mem_region; std::vector<MemoryRegionInfo> m_mem_region_cache; - Mutex m_mem_region_cache_mutex; lldb::tid_t m_pending_notification_tid; @@ -149,54 +149,14 @@ namespace process_linux { // the relevan breakpoint std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint; - /// @class LauchArgs - /// - /// @brief Simple structure to pass data to the thread responsible for - /// launching a child process. - struct LaunchArgs - { - LaunchArgs(Module *module, - char const **argv, - char const **envp, - const FileSpec &stdin_file_spec, - const FileSpec &stdout_file_spec, - const FileSpec &stderr_file_spec, - const FileSpec &working_dir, - const ProcessLaunchInfo &launch_info); - - ~LaunchArgs(); - - Module *m_module; // The executable image to launch. - char const **m_argv; // Process arguments. - char const **m_envp; // Process environment. - const FileSpec m_stdin_file_spec; // Redirect stdin if not empty. - const FileSpec m_stdout_file_spec; // Redirect stdout if not empty. - const FileSpec m_stderr_file_spec; // Redirect stderr if not empty. - const FileSpec m_working_dir; // Working directory or empty. - const ProcessLaunchInfo &m_launch_info; - }; - - typedef std::function< ::pid_t(Error &)> InitialOperation; // --------------------------------------------------------------------- // Private Instance Methods // --------------------------------------------------------------------- NativeProcessLinux (); - /// Launches an inferior process ready for debugging. Forms the - /// implementation of Process::DoLaunch. - void - LaunchInferior ( - MainLoop &mainloop, - Module *module, - char const *argv[], - char const *envp[], - const FileSpec &stdin_file_spec, - const FileSpec &stdout_file_spec, - const FileSpec &stderr_file_spec, - const FileSpec &working_dir, - const ProcessLaunchInfo &launch_info, - Error &error); + Error + LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch_info); /// Attaches to an existing process. Forms the /// implementation of Process::DoAttach @@ -204,11 +164,11 @@ namespace process_linux { AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error); ::pid_t - Launch(LaunchArgs *args, Error &error); - - ::pid_t Attach(lldb::pid_t pid, Error &error); + static void + ChildFunc(const ProcessLaunchInfo &launch_info) LLVM_ATTRIBUTE_NORETURN; + static Error SetDefaultPtraceOpts(const lldb::pid_t); @@ -239,9 +199,6 @@ namespace process_linux { void MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited); - bool - SupportHardwareSingleStepping() const; - Error SetupSoftwareSingleStepping(NativeThreadLinux &thread); @@ -285,16 +242,6 @@ namespace process_linux { Error GetEventMessage(lldb::tid_t tid, unsigned long *message); - /// Resumes the given thread. If @p signo is anything but - /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. - Error - Resume(lldb::tid_t tid, uint32_t signo); - - /// Single steps the given thread. If @p signo is anything but - /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. - Error - SingleStep(lldb::tid_t tid, uint32_t signo); - void NotifyThreadDeath (lldb::tid_t tid); diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index 79653fc626860..5dfbaff90891c 100644 --- a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -592,7 +592,7 @@ NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints () error = ReadHardwareDebugInfo (); if (error.Fail()) - return LLDB_INVALID_INDEX32; + return 0; return m_max_hwp_supported; } @@ -614,6 +614,7 @@ NativeRegisterContextLinux_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t return LLDB_INVALID_INDEX32; uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0; + lldb::addr_t real_addr = addr; // Check if we are setting watchpoint other than read/write/access // Also update watchpoint flag to match Arm write-read bit configuration. @@ -637,7 +638,24 @@ NativeRegisterContextLinux_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t if (size == 0 || size > 4) return LLDB_INVALID_INDEX32; - // We can only watch up to four bytes that follow a 4 byte aligned address + // Check 4-byte alignment for hardware watchpoint target address. + // Below is a hack to recalculate address and size in order to + // make sure we can watch non 4-byte alligned addresses as well. + if (addr & 0x03) + { + uint8_t watch_mask = (addr & 0x03) + size; + + if (watch_mask > 0x04) + return LLDB_INVALID_INDEX32; + else if (watch_mask <= 0x02) + size = 2; + else if (watch_mask <= 0x04) + size = 4; + + addr = addr & (~0x03); + } + + // We can only watch up to four bytes that follow a 4 byte aligned address // per watchpoint register pair, so make sure we can properly encode this. addr_word_offset = addr % 4; byte_mask = ((1u << size) - 1u) << addr_word_offset; @@ -682,6 +700,7 @@ NativeRegisterContextLinux_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t if ((m_hwp_regs[wp_index].control & 1) == 0) { // Update watchpoint in local cache + m_hwp_regs[wp_index].real_addr = real_addr; m_hwp_regs[wp_index].address = addr; m_hwp_regs[wp_index].control = control_value; m_hwp_regs[wp_index].refcount = 1; @@ -864,6 +883,7 @@ NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, lldb:: if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; return Error(); } } @@ -884,7 +904,24 @@ NativeRegisterContextLinux_arm::GetWatchpointAddress (uint32_t wp_index) return LLDB_INVALID_ADDRESS; if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].address; + return m_hwp_regs[wp_index].real_addr; + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +NativeRegisterContextLinux_arm::GetWatchpointHitAddress (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; else return LLDB_INVALID_ADDRESS; } diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h index 349564970428c..452f13258c2b2 100644 --- a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -74,6 +74,9 @@ namespace process_linux { GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; lldb::addr_t + GetWatchpointHitAddress (uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress (uint32_t wp_index) override; uint32_t @@ -162,6 +165,8 @@ namespace process_linux { struct DREG { lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception occurred. + lldb::addr_t real_addr; // Address value that should cause target to stop. uint32_t control; // Breakpoint/watchpoint control value. uint32_t refcount; // Serves as enable/disable and refernce counter. }; diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp index 22cdbb40d15ca..9489f00c1afe4 100644 --- a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -32,6 +32,8 @@ #include <sys/socket.h> // NT_PRSTATUS and NT_FPREGSET definition #include <elf.h> +// user_hwdebug_state definition +#include <asm/ptrace.h> #define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize()) @@ -542,7 +544,7 @@ NativeRegisterContextLinux_arm64::NumSupportedHardwareWatchpoints () error = ReadHardwareDebugInfo (); if (error.Fail()) - return LLDB_INVALID_INDEX32; + return 0; return m_max_hwp_supported; } @@ -564,6 +566,7 @@ NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size return LLDB_INVALID_INDEX32; uint32_t control_value = 0, wp_index = 0; + lldb::addr_t real_addr = addr; // Check if we are setting watchpoint other than read/write/access // Also update watchpoint flag to match AArch64 write-read bit configuration. @@ -586,9 +589,23 @@ NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size return LLDB_INVALID_INDEX32; // Check 8-byte alignment for hardware watchpoint target address. - // TODO: Add support for watching un-aligned addresses + // Below is a hack to recalculate address and size in order to + // make sure we can watch non 8-byte alligned addresses as well. if (addr & 0x07) - return LLDB_INVALID_INDEX32; + { + uint8_t watch_mask = (addr & 0x07) + size; + + if (watch_mask > 0x08) + return LLDB_INVALID_INDEX32; + else if (watch_mask <= 0x02) + size = 2; + else if (watch_mask <= 0x04) + size = 4; + else + size = 8; + + addr = addr & (~0x07); + } // Setup control value control_value = watch_flags << 3; @@ -618,6 +635,7 @@ NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size if ((m_hwp_regs[wp_index].control & 1) == 0) { // Update watchpoint in local cache + m_hwp_regs[wp_index].real_addr = real_addr; m_hwp_regs[wp_index].address = addr; m_hwp_regs[wp_index].control = control_value; m_hwp_regs[wp_index].refcount = 1; @@ -799,6 +817,7 @@ NativeRegisterContextLinux_arm64::GetWatchpointHitIndex(uint32_t &wp_index, lldb if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; return Error(); } } @@ -819,7 +838,24 @@ NativeRegisterContextLinux_arm64::GetWatchpointAddress (uint32_t wp_index) return LLDB_INVALID_ADDRESS; if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].address; + return m_hwp_regs[wp_index].real_addr; + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +NativeRegisterContextLinux_arm64::GetWatchpointHitAddress (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; else return LLDB_INVALID_ADDRESS; } diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h index c60baa637b8ae..4d9a9902ac3c2 100644 --- a/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -74,6 +74,9 @@ namespace process_linux { GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; lldb::addr_t + GetWatchpointHitAddress (uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress (uint32_t wp_index) override; uint32_t @@ -161,6 +164,8 @@ namespace process_linux { struct DREG { lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception occurred. + lldb::addr_t real_addr; // Address value that should cause target to stop. uint32_t control; // Breakpoint/watchpoint control value. uint32_t refcount; // Serves as enable/disable and refernce counter. }; diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp index 54d6f721c9d8e..d5a61722da87d 100644 --- a/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp @@ -824,7 +824,8 @@ NativeRegisterContextLinux_mips64::ReadCP1() error = NativeRegisterContextLinux::ReadFPR(); } - if (IsFR0() || IsFRE()) + // TODO: Add support for FRE + if (IsFR0()) { src = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4); dst = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4); @@ -851,7 +852,8 @@ NativeRegisterContextLinux_mips64::WriteCP1() uint32_t IsBigEndian = (byte_order == lldb::eByteOrderBig); - if (IsFR0() || IsFRE()) + // TODO: Add support for FRE + if (IsFR0()) { src = (uint8_t *)&m_fpr + 8 + (IsBigEndian * 4); dst = (uint8_t *)&m_fpr + 4 + (IsBigEndian * 4); diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.cpp b/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.cpp new file mode 100644 index 0000000000000..b09ad400d909d --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.cpp @@ -0,0 +1,716 @@ +//===-- NativeRegisterContextLinux_s390x.cpp --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__s390x__) && defined(__linux__) + +#include "NativeRegisterContextLinux_s390x.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Host/HostInfo.h" + +#include "Plugins/Process/Utility/RegisterContextLinux_s390x.h" + +#include <asm/ptrace.h> +#include <linux/uio.h> +#include <sys/ptrace.h> + +using namespace lldb_private; +using namespace lldb_private::process_linux; + +// ---------------------------------------------------------------------------- +// Private namespace. +// ---------------------------------------------------------------------------- + +namespace +{ + // s390x 64-bit general purpose registers. + static const uint32_t g_gpr_regnums_s390x[] = + { + lldb_r0_s390x, + lldb_r1_s390x, + lldb_r2_s390x, + lldb_r3_s390x, + lldb_r4_s390x, + lldb_r5_s390x, + lldb_r6_s390x, + lldb_r7_s390x, + lldb_r8_s390x, + lldb_r9_s390x, + lldb_r10_s390x, + lldb_r11_s390x, + lldb_r12_s390x, + lldb_r13_s390x, + lldb_r14_s390x, + lldb_r15_s390x, + lldb_acr0_s390x, + lldb_acr1_s390x, + lldb_acr2_s390x, + lldb_acr3_s390x, + lldb_acr4_s390x, + lldb_acr5_s390x, + lldb_acr6_s390x, + lldb_acr7_s390x, + lldb_acr8_s390x, + lldb_acr9_s390x, + lldb_acr10_s390x, + lldb_acr11_s390x, + lldb_acr12_s390x, + lldb_acr13_s390x, + lldb_acr14_s390x, + lldb_acr15_s390x, + lldb_pswm_s390x, + lldb_pswa_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) - 1 == k_num_gpr_registers_s390x, + "g_gpr_regnums_s390x has wrong number of register infos"); + + // s390x 64-bit floating point registers. + static const uint32_t g_fpu_regnums_s390x[] = + { + lldb_f0_s390x, + lldb_f1_s390x, + lldb_f2_s390x, + lldb_f3_s390x, + lldb_f4_s390x, + lldb_f5_s390x, + lldb_f6_s390x, + lldb_f7_s390x, + lldb_f8_s390x, + lldb_f9_s390x, + lldb_f10_s390x, + lldb_f11_s390x, + lldb_f12_s390x, + lldb_f13_s390x, + lldb_f14_s390x, + lldb_f15_s390x, + lldb_fpc_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) - 1 == k_num_fpr_registers_s390x, + "g_fpu_regnums_s390x has wrong number of register infos"); + + // s390x Linux operating-system information. + static const uint32_t g_linux_regnums_s390x[] = + { + lldb_orig_r2_s390x, + lldb_last_break_s390x, + lldb_system_call_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag + }; + static_assert((sizeof(g_linux_regnums_s390x) / sizeof(g_linux_regnums_s390x[0])) - 1 == k_num_linux_registers_s390x, + "g_linux_regnums_s390x has wrong number of register infos"); + + // Number of register sets provided by this context. + enum + { + k_num_register_sets = 3 + }; + + // Register sets for s390x 64-bit. + static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = + { + { "General Purpose Registers", "gpr", k_num_gpr_registers_s390x, g_gpr_regnums_s390x }, + { "Floating Point Registers", "fpr", k_num_fpr_registers_s390x, g_fpu_regnums_s390x }, + { "Linux Operating System Data", "linux", k_num_linux_registers_s390x, g_linux_regnums_s390x }, + }; +} + +#define REG_CONTEXT_SIZE (sizeof(s390_regs) + sizeof(s390_fp_regs) + 4) + +// ---------------------------------------------------------------------------- +// Required ptrace defines. +// ---------------------------------------------------------------------------- + +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ + +NativeRegisterContextLinux * +NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) +{ + return new NativeRegisterContextLinux_s390x(target_arch, native_thread, concrete_frame_idx); +} + +// ---------------------------------------------------------------------------- +// NativeRegisterContextLinux_s390x members. +// ---------------------------------------------------------------------------- + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) +{ + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + return new RegisterContextLinux_s390x(target_arch); +} + +NativeRegisterContextLinux_s390x::NativeRegisterContextLinux_s390x(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx) + : NativeRegisterContextLinux(native_thread, concrete_frame_idx, CreateRegisterInfoInterface(target_arch)) +{ + // Set up data about ranges of valid registers. + switch (target_arch.GetMachine()) + { + case llvm::Triple::systemz: + m_reg_info.num_registers = k_num_registers_s390x; + m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x; + m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x; + m_reg_info.last_gpr = k_last_gpr_s390x; + m_reg_info.first_fpr = k_first_fpr_s390x; + m_reg_info.last_fpr = k_last_fpr_s390x; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + // Clear out the watchpoint state. + m_watchpoint_addr = LLDB_INVALID_ADDRESS; +} + +uint32_t +NativeRegisterContextLinux_s390x::GetRegisterSetCount() const +{ + uint32_t sets = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + { + if (IsRegisterSetAvailable(set_index)) + ++sets; + } + + return sets; +} + +uint32_t +NativeRegisterContextLinux_s390x::GetUserRegisterCount() const +{ + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) + { + const RegisterSet *set = GetRegisterSet(set_index); + if (set) + count += set->num_registers; + } + return count; +} + +const RegisterSet * +NativeRegisterContextLinux_s390x::GetRegisterSet(uint32_t set_index) const +{ + if (!IsRegisterSetAvailable(set_index)) + return nullptr; + + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) + { + case llvm::Triple::systemz: + return &g_reg_sets_s390x[set_index]; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } + + return nullptr; +} + +bool +NativeRegisterContextLinux_s390x::IsRegisterSetAvailable(uint32_t set_index) const +{ + return set_index < k_num_register_sets; +} + +bool +NativeRegisterContextLinux_s390x::IsGPR(uint32_t reg_index) const +{ + // GPRs come first. "orig_r2" counts as GPR since it is part of the GPR register area. + return reg_index <= m_reg_info.last_gpr || reg_index == lldb_orig_r2_s390x; +} + +bool +NativeRegisterContextLinux_s390x::IsFPR(uint32_t reg_index) const +{ + return (m_reg_info.first_fpr <= reg_index && reg_index <= m_reg_info.last_fpr); +} + +Error +NativeRegisterContextLinux_s390x::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) +{ + if (!reg_info) + return Error("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return Error("register \"%s\" is an internal-only lldb register, cannot read directly", reg_info->name); + + if (IsGPR(reg)) + { + s390_regs regs; + Error error = DoReadGPR(®s, sizeof(regs)); + if (error.Fail()) + return error; + + uint8_t *src = (uint8_t *)®s + reg_info->byte_offset; + assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(regs)); + switch (reg_info->byte_size) + { + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + default: + assert(false && "Unhandled data size."); + return Error("unhandled byte size: %" PRIu32, reg_info->byte_size); + } + return Error(); + } + + if (IsFPR(reg)) + { + s390_fp_regs fp_regs; + Error error = DoReadFPR(&fp_regs, sizeof(fp_regs)); + if (error.Fail()) + return error; + + // byte_offset is just the offset within FPR, not the whole user area. + uint8_t *src = (uint8_t *)&fp_regs + reg_info->byte_offset; + assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(fp_regs)); + switch (reg_info->byte_size) + { + case 4: + reg_value.SetUInt32(*(uint32_t *)src); + break; + case 8: + reg_value.SetUInt64(*(uint64_t *)src); + break; + default: + assert(false && "Unhandled data size."); + return Error("unhandled byte size: %" PRIu32, reg_info->byte_size); + } + return Error(); + } + + if (reg == lldb_last_break_s390x) + { + uint64_t last_break; + Error error = DoReadRegisterSet(NT_S390_LAST_BREAK, &last_break, 8); + if (error.Fail()) + return error; + + reg_value.SetUInt64(last_break); + return Error(); + } + + if (reg == lldb_system_call_s390x) + { + uint32_t system_call; + Error error = DoReadRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); + if (error.Fail()) + return error; + + reg_value.SetUInt32(system_call); + return Error(); + } + + return Error("failed - register wasn't recognized"); +} + +Error +NativeRegisterContextLinux_s390x::WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (!reg_info) + return Error("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return Error("register \"%s\" is an internal-only lldb register, cannot write directly", reg_info->name); + + if (IsGPR(reg)) + { + s390_regs regs; + Error error = DoReadGPR(®s, sizeof(regs)); + if (error.Fail()) + return error; + + uint8_t *dst = (uint8_t *)®s + reg_info->byte_offset; + assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(regs)); + switch (reg_info->byte_size) + { + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error("unhandled byte size: %" PRIu32, reg_info->byte_size); + } + return DoWriteGPR(®s, sizeof(regs)); + } + + if (IsFPR(reg)) + { + s390_fp_regs fp_regs; + Error error = DoReadFPR(&fp_regs, sizeof(fp_regs)); + if (error.Fail()) + return error; + + // byte_offset is just the offset within fp_regs, not the whole user area. + uint8_t *dst = (uint8_t *)&fp_regs + reg_info->byte_offset; + assert(reg_info->byte_offset + reg_info->byte_size <= sizeof(fp_regs)); + switch (reg_info->byte_size) + { + case 4: + *(uint32_t *)dst = reg_value.GetAsUInt32(); + break; + case 8: + *(uint64_t *)dst = reg_value.GetAsUInt64(); + break; + default: + assert(false && "Unhandled data size."); + return Error("unhandled byte size: %" PRIu32, reg_info->byte_size); + } + return DoWriteFPR(&fp_regs, sizeof(fp_regs)); + } + + if (reg == lldb_last_break_s390x) + { + return Error("The last break address is read-only"); + } + + if (reg == lldb_system_call_s390x) + { + uint32_t system_call = reg_value.GetAsUInt32(); + return DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); + } + + return Error("failed - register wasn't recognized"); +} + +Error +NativeRegisterContextLinux_s390x::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + Error error; + + data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); + if (!data_sp) + { + error.SetErrorStringWithFormat("failed to allocate DataBufferHeap instance of size %" PRIu64, REG_CONTEXT_SIZE); + return error; + } + + uint8_t *dst = data_sp->GetBytes(); + if (dst == nullptr) + { + error.SetErrorStringWithFormat("DataBufferHeap instance of size %" PRIu64 " returned a null pointer", + REG_CONTEXT_SIZE); + return error; + } + + error = DoReadGPR(dst, sizeof(s390_regs)); + dst += sizeof(s390_regs); + if (error.Fail()) + return error; + + error = DoReadFPR(dst, sizeof(s390_fp_regs)); + dst += sizeof(s390_fp_regs); + if (error.Fail()) + return error; + + // Ignore errors if the regset is unsupported (happens on older kernels). + DoReadRegisterSet(NT_S390_SYSTEM_CALL, dst, 4); + dst += 4; + + // To enable inferior function calls while the process is stopped in + // an interrupted system call, we need to clear the system call flag. + // It will be restored to its original value by WriteAllRegisterValues. + // Again we ignore error if the regset is unsupported. + uint32_t system_call = 0; + DoWriteRegisterSet(NT_S390_SYSTEM_CALL, &system_call, 4); + + return error; +} + +Error +NativeRegisterContextLinux_s390x::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + Error error; + + if (!data_sp) + { + error.SetErrorStringWithFormat("NativeRegisterContextLinux_s390x::%s invalid data_sp provided", __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) + { + error.SetErrorStringWithFormat( + "NativeRegisterContextLinux_s390x::%s data_sp contained mismatched data size, expected %" PRIu64 + ", actual %" PRIu64, + __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); + return error; + } + + uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) + { + error.SetErrorStringWithFormat( + "NativeRegisterContextLinux_s390x::%s DataBuffer::GetBytes() returned a null pointer", __FUNCTION__); + return error; + } + + error = DoWriteGPR(src, sizeof(s390_regs)); + src += sizeof(s390_regs); + if (error.Fail()) + return error; + + error = DoWriteFPR(src, sizeof(s390_fp_regs)); + src += sizeof(s390_fp_regs); + if (error.Fail()) + return error; + + // Ignore errors if the regset is unsupported (happens on older kernels). + DoWriteRegisterSet(NT_S390_SYSTEM_CALL, src, 4); + src += 4; + + return error; +} + +Error +NativeRegisterContextLinux_s390x::DoReadRegisterValue(uint32_t offset, const char *reg_name, uint32_t size, + RegisterValue &value) +{ + return Error("DoReadRegisterValue unsupported"); +} + +Error +NativeRegisterContextLinux_s390x::DoWriteRegisterValue(uint32_t offset, const char *reg_name, + const RegisterValue &value) +{ + return Error("DoWriteRegisterValue unsupported"); +} + +Error +NativeRegisterContextLinux_s390x::PeekUserArea(uint32_t offset, void *buf, size_t buf_size) +{ + ptrace_area parea; + parea.len = buf_size; + parea.process_addr = (addr_t)buf; + parea.kernel_addr = offset; + + return NativeProcessLinux::PtraceWrapper(PTRACE_PEEKUSR_AREA, m_thread.GetID(), &parea); +} + +Error +NativeRegisterContextLinux_s390x::PokeUserArea(uint32_t offset, const void *buf, size_t buf_size) +{ + ptrace_area parea; + parea.len = buf_size; + parea.process_addr = (addr_t)buf; + parea.kernel_addr = offset; + + return NativeProcessLinux::PtraceWrapper(PTRACE_POKEUSR_AREA, m_thread.GetID(), &parea); +} + +Error +NativeRegisterContextLinux_s390x::DoReadGPR(void *buf, size_t buf_size) +{ + assert(buf_size == sizeof(s390_regs)); + return PeekUserArea(offsetof(user_regs_struct, psw), buf, buf_size); +} + +Error +NativeRegisterContextLinux_s390x::DoWriteGPR(void *buf, size_t buf_size) +{ + assert(buf_size == sizeof(s390_regs)); + return PokeUserArea(offsetof(user_regs_struct, psw), buf, buf_size); +} + +Error +NativeRegisterContextLinux_s390x::DoReadFPR(void *buf, size_t buf_size) +{ + assert(buf_size == sizeof(s390_fp_regs)); + return PeekUserArea(offsetof(user_regs_struct, fp_regs), buf, buf_size); +} + +Error +NativeRegisterContextLinux_s390x::DoWriteFPR(void *buf, size_t buf_size) +{ + assert(buf_size == sizeof(s390_fp_regs)); + return PokeUserArea(offsetof(user_regs_struct, fp_regs), buf, buf_size); +} + +Error +NativeRegisterContextLinux_s390x::DoReadRegisterSet(uint32_t regset, void *buf, size_t buf_size) +{ + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_size; + + return ReadRegisterSet(&iov, buf_size, regset); +} + +Error +NativeRegisterContextLinux_s390x::DoWriteRegisterSet(uint32_t regset, const void *buf, size_t buf_size) +{ + struct iovec iov; + iov.iov_base = const_cast<void *>(buf); + iov.iov_len = buf_size; + + return WriteRegisterSet(&iov, buf_size, regset); +} + +Error +NativeRegisterContextLinux_s390x::IsWatchpointHit(uint32_t wp_index, bool &is_hit) +{ + per_lowcore_bits per_lowcore; + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + if (m_watchpoint_addr == LLDB_INVALID_ADDRESS) + { + is_hit = false; + return Error(); + } + + Error error = PeekUserArea(offsetof(user_regs_struct, per_info.lowcore), &per_lowcore, sizeof(per_lowcore)); + if (error.Fail()) + { + is_hit = false; + return error; + } + + is_hit = (per_lowcore.perc_storage_alteration == 1 && per_lowcore.perc_store_real_address == 0); + + if (is_hit) + { + // Do not report this watchpoint again. + memset(&per_lowcore, 0, sizeof(per_lowcore)); + PokeUserArea(offsetof(user_regs_struct, per_info.lowcore), &per_lowcore, sizeof(per_lowcore)); + } + + return Error(); +} + +Error +NativeRegisterContextLinux_s390x::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) +{ + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) + { + bool is_hit; + Error error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) + { + wp_index = LLDB_INVALID_INDEX32; + return error; + } + else if (is_hit) + { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +Error +NativeRegisterContextLinux_s390x::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Error("Watchpoint index out of range"); + + is_vacant = m_watchpoint_addr == LLDB_INVALID_ADDRESS; + + return Error(); +} + +bool +NativeRegisterContextLinux_s390x::ClearHardwareWatchpoint(uint32_t wp_index) +{ + per_struct per_info; + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + Error error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info, sizeof(per_info)); + if (error.Fail()) + return false; + + per_info.control_regs.bits.em_storage_alteration = 0; + per_info.control_regs.bits.storage_alt_space_ctl = 0; + per_info.starting_addr = 0; + per_info.ending_addr = 0; + + error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info, sizeof(per_info)); + if (error.Fail()) + return false; + + m_watchpoint_addr = LLDB_INVALID_ADDRESS; + return true; +} + +Error +NativeRegisterContextLinux_s390x::ClearAllHardwareWatchpoints() +{ + if (ClearHardwareWatchpoint(0)) + return Error(); + return Error("Clearing all hardware watchpoints failed."); +} + +uint32_t +NativeRegisterContextLinux_s390x::SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + per_struct per_info; + + if (watch_flags != 0x1) + return LLDB_INVALID_INDEX32; + + if (m_watchpoint_addr != LLDB_INVALID_ADDRESS) + return LLDB_INVALID_INDEX32; + + Error error = PeekUserArea(offsetof(user_regs_struct, per_info), &per_info, sizeof(per_info)); + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + per_info.control_regs.bits.em_storage_alteration = 1; + per_info.control_regs.bits.storage_alt_space_ctl = 1; + per_info.starting_addr = addr; + per_info.ending_addr = addr + size - 1; + + error = PokeUserArea(offsetof(user_regs_struct, per_info), &per_info, sizeof(per_info)); + if (error.Fail()) + return LLDB_INVALID_INDEX32; + + m_watchpoint_addr = addr; + return 0; +} + +lldb::addr_t +NativeRegisterContextLinux_s390x::GetWatchpointAddress(uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + return m_watchpoint_addr; +} + +uint32_t +NativeRegisterContextLinux_s390x::NumSupportedHardwareWatchpoints() +{ + return 1; +} + +#endif // defined(__s390x__) && defined(__linux__) diff --git a/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.h b/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.h new file mode 100644 index 0000000000000..8cd4ab7f12427 --- /dev/null +++ b/source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.h @@ -0,0 +1,141 @@ +//===-- NativeRegisterContextLinux_s390x.h ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__s390x__) && defined(__linux__) + +#ifndef lldb_NativeRegisterContextLinux_s390x_h +#define lldb_NativeRegisterContextLinux_s390x_h + +#include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/RegisterContext_s390x.h" +#include "Plugins/Process/Utility/lldb-s390x-register-enums.h" + +namespace lldb_private +{ +namespace process_linux +{ + +class NativeProcessLinux; + +class NativeRegisterContextLinux_s390x : public NativeRegisterContextLinux +{ +public: + NativeRegisterContextLinux_s390x(const ArchSpec &target_arch, NativeThreadProtocol &native_thread, + uint32_t concrete_frame_idx); + + uint32_t + GetRegisterSetCount() const override; + + const RegisterSet * + GetRegisterSet(uint32_t set_index) const override; + + uint32_t + GetUserRegisterCount() const override; + + Error + ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; + + Error + WriteRegister(const RegisterInfo *reg_info, const RegisterValue ®_value) override; + + Error + ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Error + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Error + IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + Error + IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool + ClearHardwareWatchpoint(uint32_t wp_index) override; + + Error + ClearAllHardwareWatchpoints() override; + + uint32_t + SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) override; + + lldb::addr_t + GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t + NumSupportedHardwareWatchpoints() override; + +protected: + Error + DoReadRegisterValue(uint32_t offset, const char *reg_name, uint32_t size, RegisterValue &value) override; + + Error + DoWriteRegisterValue(uint32_t offset, const char *reg_name, const RegisterValue &value) override; + + Error + DoReadGPR(void *buf, size_t buf_size) override; + + Error + DoWriteGPR(void *buf, size_t buf_size) override; + + Error + DoReadFPR(void *buf, size_t buf_size) override; + + Error + DoWriteFPR(void *buf, size_t buf_size) override; + +private: + // Info about register ranges. + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + }; + + // Private member variables. + RegInfo m_reg_info; + lldb::addr_t m_watchpoint_addr; + + // Private member methods. + bool + IsRegisterSetAvailable(uint32_t set_index) const; + + bool + IsGPR(uint32_t reg_index) const; + + bool + IsFPR(uint32_t reg_index) const; + + Error + PeekUserArea(uint32_t offset, void *buf, size_t buf_size); + + Error + PokeUserArea(uint32_t offset, const void *buf, size_t buf_size); + + Error + DoReadRegisterSet(uint32_t regset, void *buf, size_t buf_size); + + Error + DoWriteRegisterSet(uint32_t regset, const void *buf, size_t buf_size); +}; + +} // namespace process_linux +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextLinux_s390x_h + +#endif // defined(__s390x__) && defined(__linux__) diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/source/Plugins/Process/Linux/NativeThreadLinux.cpp index cbf82885e23a6..070b1bcda3b87 100644 --- a/source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ b/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -14,10 +14,12 @@ #include "NativeProcessLinux.h" #include "NativeRegisterContextLinux.h" +#include "SingleStepCheck.h" #include "lldb/Core/Log.h" #include "lldb/Core/State.h" #include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/linux/Ptrace.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/lldb-enumerations.h" @@ -199,8 +201,8 @@ NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr) return Error ("Clearing hardware watchpoint failed."); } -void -NativeThreadLinux::SetRunning () +Error +NativeThreadLinux::Resume(uint32_t signo) { const StateType new_state = StateType::eStateRunning; MaybeLogStateChange (new_state); @@ -213,29 +215,92 @@ NativeThreadLinux::SetRunning () // then this is a new thread. So set all existing watchpoints. if (m_watchpoint_index_map.empty()) { - const auto process_sp = GetProcess(); - if (process_sp) + NativeProcessLinux &process = GetProcess(); + + const auto &watchpoint_map = process.GetWatchpointMap(); + GetRegisterContext()->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) { - const auto &watchpoint_map = process_sp->GetWatchpointMap(); - if (watchpoint_map.empty()) return; - GetRegisterContext()->ClearAllHardwareWatchpoints(); - for (const auto &pair : watchpoint_map) - { - const auto& wp = pair.second; - SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); - } + const auto &wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); } } + + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + return NativeProcessLinux::PtraceWrapper(PTRACE_CONT, GetID(), nullptr, reinterpret_cast<void *>(data)); } void -NativeThreadLinux::SetStepping () +NativeThreadLinux::MaybePrepareSingleStepWorkaround() +{ + if (!SingleStepWorkaroundNeeded()) + return; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + + if (sched_getaffinity(static_cast<::pid_t>(m_tid), sizeof m_original_cpu_set, &m_original_cpu_set) != 0) + { + // This should really not fail. But, just in case... + if (log) + { + Error error(errno, eErrorTypePOSIX); + log->Printf("NativeThreadLinux::%s Unable to get cpu affinity for thread %" PRIx64 ": %s", __FUNCTION__, + m_tid, error.AsCString()); + } + return; + } + + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(0, &set); + if (sched_setaffinity(static_cast<::pid_t>(m_tid), sizeof set, &set) != 0 && log) + { + // This may fail in very locked down systems, if the thread is not allowed to run on + // cpu 0. If that happens, only thing we can do is it log it and continue... + Error error(errno, eErrorTypePOSIX); + log->Printf("NativeThreadLinux::%s Unable to set cpu affinity for thread %" PRIx64 ": %s", __FUNCTION__, m_tid, + error.AsCString()); + } +} + +void +NativeThreadLinux::MaybeCleanupSingleStepWorkaround() +{ + if (!SingleStepWorkaroundNeeded()) + return; + + if (sched_setaffinity(static_cast<::pid_t>(m_tid), sizeof m_original_cpu_set, &m_original_cpu_set) != 0) + { + Error error(errno, eErrorTypePOSIX); + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + log->Printf("NativeThreadLinux::%s Unable to reset cpu affinity for thread %" PRIx64 ": %s", __FUNCTION__, + m_tid, error.AsCString()); + } +} + +Error +NativeThreadLinux::SingleStep(uint32_t signo) { const StateType new_state = StateType::eStateStepping; MaybeLogStateChange (new_state); m_state = new_state; - m_stop_info.reason = StopReason::eStopReasonNone; + + MaybePrepareSingleStepWorkaround(); + + intptr_t data = 0; + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // If hardware single-stepping is not supported, we just do a continue. The breakpoint on the + // next instruction has been setup in NativeProcessLinux::Resume. + return NativeProcessLinux::PtraceWrapper(GetProcess().SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP + : PTRACE_CONT, + m_tid, nullptr, reinterpret_cast<void *>(data)); } void @@ -245,9 +310,7 @@ NativeThreadLinux::SetStoppedBySignal(uint32_t signo, const siginfo_t *info) if (log) log->Printf ("NativeThreadLinux::%s called with signal 0x%02" PRIx32, __FUNCTION__, signo); - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonSignal; m_stop_info.details.signal.signo = signo; @@ -288,6 +351,17 @@ NativeThreadLinux::IsStopped (int *signo) return true; } +void +NativeThreadLinux::SetStopped() +{ + if (m_state == StateType::eStateStepping) + MaybeCleanupSingleStepWorkaround(); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_description.clear(); +} void NativeThreadLinux::SetStoppedByExec () @@ -296,9 +370,7 @@ NativeThreadLinux::SetStoppedByExec () if (log) log->Printf ("NativeThreadLinux::%s()", __FUNCTION__); - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonExec; m_stop_info.details.signal.signo = SIGSTOP; @@ -307,9 +379,7 @@ NativeThreadLinux::SetStoppedByExec () void NativeThreadLinux::SetStoppedByBreakpoint () { - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonBreakpoint; m_stop_info.details.signal.signo = SIGTRAP; @@ -319,10 +389,7 @@ NativeThreadLinux::SetStoppedByBreakpoint () void NativeThreadLinux::SetStoppedByWatchpoint (uint32_t wp_index) { - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; - m_stop_description.clear (); + SetStopped(); lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); @@ -363,9 +430,7 @@ NativeThreadLinux::IsStoppedAtWatchpoint () void NativeThreadLinux::SetStoppedByTrace () { - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonTrace; m_stop_info.details.signal.signo = SIGTRAP; @@ -374,9 +439,7 @@ NativeThreadLinux::SetStoppedByTrace () void NativeThreadLinux::SetStoppedWithNoReason () { - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonNone; m_stop_info.details.signal.signo = 0; @@ -397,11 +460,9 @@ NativeThreadLinux::RequestStop () { Log* log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); - const auto process_sp = GetProcess(); - if (! process_sp) - return Error("Process is null."); + NativeProcessLinux &process = GetProcess(); - lldb::pid_t pid = process_sp->GetID(); + lldb::pid_t pid = process.GetID(); lldb::tid_t tid = GetID(); if (log) @@ -438,3 +499,11 @@ NativeThreadLinux::MaybeLogStateChange (lldb::StateType new_state) // Log it. log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") changing from state %s to %s", pid, GetID (), StateAsCString (old_state), StateAsCString (new_state)); } + +NativeProcessLinux & +NativeThreadLinux::GetProcess() +{ + auto process_sp = std::static_pointer_cast<NativeProcessLinux>(NativeThreadProtocol::GetProcess()); + assert(process_sp); + return *process_sp; +} diff --git a/source/Plugins/Process/Linux/NativeThreadLinux.h b/source/Plugins/Process/Linux/NativeThreadLinux.h index bf6b00a78cfda..f1b6a6e447828 100644 --- a/source/Plugins/Process/Linux/NativeThreadLinux.h +++ b/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -13,6 +13,8 @@ #include "lldb/lldb-private-forward.h" #include "lldb/Host/common/NativeThreadProtocol.h" +#include <sched.h> + #include <map> #include <memory> #include <string> @@ -54,11 +56,16 @@ namespace process_linux { // --------------------------------------------------------------------- // Interface for friend classes // --------------------------------------------------------------------- - void - SetRunning (); - void - SetStepping (); + /// Resumes the thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Error + Resume(uint32_t signo); + + /// Single steps the thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Error + SingleStep(uint32_t signo); void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); @@ -102,6 +109,18 @@ namespace process_linux { void MaybeLogStateChange (lldb::StateType new_state); + NativeProcessLinux & + GetProcess(); + + void + SetStopped(); + + inline void + MaybePrepareSingleStepWorkaround(); + + inline void + MaybeCleanupSingleStepWorkaround(); + // --------------------------------------------------------------------- // Member Variables // --------------------------------------------------------------------- @@ -111,6 +130,7 @@ namespace process_linux { std::string m_stop_description; using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>; WatchpointIndexMap m_watchpoint_index_map; + cpu_set_t m_original_cpu_set; // For single-step workaround. }; typedef std::shared_ptr<NativeThreadLinux> NativeThreadLinuxSP; diff --git a/source/Plugins/Process/Linux/SingleStepCheck.cpp b/source/Plugins/Process/Linux/SingleStepCheck.cpp new file mode 100644 index 0000000000000..8c557d4b6ff86 --- /dev/null +++ b/source/Plugins/Process/Linux/SingleStepCheck.cpp @@ -0,0 +1,177 @@ +//===-- SingleStepCheck.cpp ----------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SingleStepCheck.h" + +#include <sched.h> +#include <signal.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "NativeProcessLinux.h" + +#include "llvm/Support/Compiler.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/linux/Ptrace.h" + +using namespace lldb_private::process_linux; + +#if defined(__arm64__) || defined(__aarch64__) +namespace +{ + +void LLVM_ATTRIBUTE_NORETURN +Child() +{ + if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) + _exit(1); + + // We just do an endless loop SIGSTOPPING ourselves until killed. The tracer will fiddle with our cpu + // affinities and monitor the behaviour. + for (;;) + { + raise(SIGSTOP); + + // Generate a bunch of instructions here, so that a single-step does not land in the + // raise() accidentally. If single-stepping works, we will be spinning in this loop. If + // it doesn't, we'll land in the raise() call above. + for (volatile unsigned i = 0; i < CPU_SETSIZE; ++i) + ; + } +} + +struct ChildDeleter +{ + ::pid_t pid; + + ~ChildDeleter() + { + int status; + kill(pid, SIGKILL); // Kill the child. + waitpid(pid, &status, __WALL); // Pick up the remains. + } +}; + +} // end anonymous namespace + +bool +impl::SingleStepWorkaroundNeeded() +{ + // We shall spawn a child, and use it to verify the debug capabilities of the cpu. We shall + // iterate through the cpus, bind the child to each one in turn, and verify that + // single-stepping works on that cpu. A workaround is needed if we find at least one broken + // cpu. + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + Error error; + ::pid_t child_pid = fork(); + if (child_pid == -1) + { + if (log) + { + error.SetErrorToErrno(); + log->Printf("%s failed to fork(): %s", __FUNCTION__, error.AsCString()); + } + return false; + } + if (child_pid == 0) + Child(); + + ChildDeleter child_deleter{child_pid}; + cpu_set_t available_cpus; + if (sched_getaffinity(child_pid, sizeof available_cpus, &available_cpus) == -1) + { + if (log) + { + error.SetErrorToErrno(); + log->Printf("%s failed to get available cpus: %s", __FUNCTION__, error.AsCString()); + } + return false; + } + + int status; + ::pid_t wpid = waitpid(child_pid, &status, __WALL); + if (wpid != child_pid || !WIFSTOPPED(status)) + { + if (log) + { + error.SetErrorToErrno(); + log->Printf("%s waitpid() failed (status = %x): %s", __FUNCTION__, status, error.AsCString()); + } + return false; + } + + unsigned cpu; + for (cpu = 0; cpu < CPU_SETSIZE; ++cpu) + { + if (!CPU_ISSET(cpu, &available_cpus)) + continue; + + cpu_set_t cpus; + CPU_ZERO(&cpus); + CPU_SET(cpu, &cpus); + if (sched_setaffinity(child_pid, sizeof cpus, &cpus) == -1) + { + if (log) + { + error.SetErrorToErrno(); + log->Printf("%s failed to switch to cpu %u: %s", __FUNCTION__, cpu, error.AsCString()); + } + continue; + } + + int status; + error = NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid); + if (error.Fail()) + { + if (log) + log->Printf("%s single step failed: %s", __FUNCTION__, error.AsCString()); + break; + } + + wpid = waitpid(child_pid, &status, __WALL); + if (wpid != child_pid || !WIFSTOPPED(status)) + { + if (log) + { + error.SetErrorToErrno(); + log->Printf("%s waitpid() failed (status = %x): %s", __FUNCTION__, status, error.AsCString()); + } + break; + } + if (WSTOPSIG(status) != SIGTRAP) + { + if (log) + log->Printf("%s single stepping on cpu %d failed with status %x", __FUNCTION__, cpu, status); + break; + } + } + + // cpu is either the index of the first broken cpu, or CPU_SETSIZE. + if (cpu == 0) + { + if (log) + log->Printf("%s SINGLE STEPPING ON FIRST CPU IS NOT WORKING. DEBUGGING LIKELY TO BE UNRELIABLE.", + __FUNCTION__); + // No point in trying to fiddle with the affinities, just give it our best shot and see how it goes. + return false; + } + + return cpu != CPU_SETSIZE; +} + +#else // !arm64 +bool +impl::SingleStepWorkaroundNeeded() +{ + return false; +} +#endif diff --git a/source/Plugins/Process/Linux/SingleStepCheck.h b/source/Plugins/Process/Linux/SingleStepCheck.h new file mode 100644 index 0000000000000..f83f7c973f8d8 --- /dev/null +++ b/source/Plugins/Process/Linux/SingleStepCheck.h @@ -0,0 +1,41 @@ +//===-- SingleStepCheck.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_SingleStepCheck_H_ +#define liblldb_SingleStepCheck_H_ + +namespace lldb_private +{ +namespace process_linux +{ + +namespace impl +{ +extern bool +SingleStepWorkaroundNeeded(); +} + +// arm64 linux had a bug which prevented single-stepping and watchpoints from working on non-boot +// cpus, due to them being incorrectly initialized after coming out of suspend. This issue is +// particularly affecting android M, which uses suspend ("doze mode") quite aggressively. This +// code detects that situation and makes single-stepping work by doing all the step operations on +// the boot cpu. +// +// The underlying issue has been fixed in android N and linux 4.4. This code can be removed once +// these systems become obsolete. +inline bool +SingleStepWorkaroundNeeded() +{ + static bool value = impl::SingleStepWorkaroundNeeded(); + return value; +} +} // end namespace process_linux +} // end namespace lldb_private + +#endif // #ifndef liblldb_SingleStepCheck_H_ diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp index 5c1c3284a35c2..29a0d58f7080e 100644 --- a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp +++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp @@ -37,21 +37,21 @@ using namespace lldb_private; //---------------------------------------------------------------------- // CommunicationKDP constructor //---------------------------------------------------------------------- -CommunicationKDP::CommunicationKDP (const char *comm_name) : - Communication(comm_name), - m_addr_byte_size (4), - m_byte_order (eByteOrderLittle), - m_packet_timeout (5), - m_sequence_mutex (Mutex::eMutexTypeRecursive), - m_is_running (false), - m_session_key (0u), - m_request_sequence_id (0u), - m_exception_sequence_id (0u), - m_kdp_version_version (0u), - m_kdp_version_feature (0u), - m_kdp_hostinfo_cpu_mask (0u), - m_kdp_hostinfo_cpu_type (0u), - m_kdp_hostinfo_cpu_subtype (0u) +CommunicationKDP::CommunicationKDP(const char *comm_name) + : Communication(comm_name), + m_addr_byte_size(4), + m_byte_order(eByteOrderLittle), + m_packet_timeout(5), + m_sequence_mutex(), + m_is_running(false), + m_session_key(0u), + m_request_sequence_id(0u), + m_exception_sequence_id(0u), + m_kdp_version_version(0u), + m_kdp_version_feature(0u), + m_kdp_hostinfo_cpu_mask(0u), + m_kdp_hostinfo_cpu_type(0u), + m_kdp_hostinfo_cpu_subtype(0u) { } @@ -69,7 +69,7 @@ CommunicationKDP::~CommunicationKDP() bool CommunicationKDP::SendRequestPacket (const PacketStreamType &request_packet) { - Mutex::Locker locker(m_sequence_mutex); + std::lock_guard<std::recursive_mutex> guard(m_sequence_mutex); return SendRequestPacketNoLock (request_packet); } @@ -111,7 +111,7 @@ CommunicationKDP::SendRequestAndGetReply (const CommandType command, return false; } - Mutex::Locker locker(m_sequence_mutex); + std::lock_guard<std::recursive_mutex> guard(m_sequence_mutex); #ifdef LLDB_CONFIGURATION_DEBUG // NOTE: this only works for packets that are in native endian byte order assert (request_packet.GetSize() == *((uint16_t *)(request_packet.GetData() + 2))); @@ -205,9 +205,9 @@ CommunicationKDP::SendRequestPacketNoLock (const PacketStreamType &request_packe } bool -CommunicationKDP::GetSequenceMutex (Mutex::Locker& locker) +CommunicationKDP::GetSequenceMutex(std::unique_lock<std::recursive_mutex> &lock) { - return locker.TryLock (m_sequence_mutex); + return (lock = std::unique_lock<std::recursive_mutex>(m_sequence_mutex, std::try_to_lock)).owns_lock(); } @@ -220,7 +220,7 @@ CommunicationKDP::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) size_t CommunicationKDP::WaitForPacketWithTimeoutMicroSeconds (DataExtractor &packet, uint32_t timeout_usec) { - Mutex::Locker locker(m_sequence_mutex); + std::lock_guard<std::recursive_mutex> guard(m_sequence_mutex); return WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); } @@ -284,8 +284,8 @@ bool CommunicationKDP::CheckForPacket (const uint8_t *src, size_t src_len, DataExtractor &packet) { // Put the packet data into the buffer in a thread safe fashion - Mutex::Locker locker(m_bytes_mutex); - + std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex); + Log *log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS)); if (src && src_len > 0) @@ -326,6 +326,7 @@ CommunicationKDP::CheckForPacket (const uint8_t *src, size_t src_len, DataExtrac SendRequestPacketNoLock (request_ack_packet); } // Fall through to case below to get packet contents + LLVM_FALLTHROUGH; case ePacketTypeReply | KDP_CONNECT: case ePacketTypeReply | KDP_DISCONNECT: case ePacketTypeReply | KDP_HOSTINFO: diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h index 98a146d5a06da..89e55a561e744 100644 --- a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h +++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.h @@ -13,6 +13,7 @@ // C Includes // C++ Includes #include <list> +#include <mutex> #include <string> // Other libraries and framework includes @@ -21,7 +22,6 @@ #include "lldb/Core/Communication.h" #include "lldb/Core/Listener.h" #include "lldb/Core/StreamBuffer.h" -#include "lldb/Host/Mutex.h" #include "lldb/Host/Predicate.h" #include "lldb/Host/TimeValue.h" @@ -109,7 +109,7 @@ public: uint32_t usec); bool - GetSequenceMutex(lldb_private::Mutex::Locker& locker); + GetSequenceMutex(std::unique_lock<std::recursive_mutex> &lock); bool CheckForPacket (const uint8_t *src, @@ -324,7 +324,7 @@ protected: uint32_t m_addr_byte_size; lldb::ByteOrder m_byte_order; uint32_t m_packet_timeout; - lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time + std::recursive_mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time lldb_private::Predicate<bool> m_is_running; uint32_t m_session_key; uint8_t m_request_sequence_id; diff --git a/source/Plugins/Process/MacOSX-Kernel/Makefile b/source/Plugins/Process/MacOSX-Kernel/Makefile deleted file mode 100644 index e42f390ffe0d6..0000000000000 --- a/source/Plugins/Process/MacOSX-Kernel/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- source/Plugins/Process/MacOSX-Darwin/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 := lldbPluginProcessDarwin -BUILD_ARCHIVE = 1 - -include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp index 628f76d104fe3..898677df616b4 100644 --- a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp @@ -132,12 +132,12 @@ ProcessKDP::Terminate() lldb::ProcessSP ProcessKDP::CreateInstance (TargetSP target_sp, - Listener &listener, + ListenerSP listener_sp, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) - process_sp.reset(new ProcessKDP (target_sp, listener)); + process_sp.reset(new ProcessKDP (target_sp, listener_sp)); return process_sp; } @@ -178,8 +178,8 @@ ProcessKDP::CanDebug(TargetSP target_sp, bool plugin_specified_by_name) //---------------------------------------------------------------------- // ProcessKDP constructor //---------------------------------------------------------------------- -ProcessKDP::ProcessKDP(TargetSP target_sp, Listener &listener) : - Process (target_sp, listener), +ProcessKDP::ProcessKDP(TargetSP target_sp, ListenerSP listener_sp) : + Process (target_sp, listener_sp), m_comm("lldb.process.kdp-remote.communication"), m_async_broadcaster (NULL, "lldb.process.kdp-remote.async-broadcaster"), m_dyld_plugin_name (), @@ -927,13 +927,13 @@ ProcessKDP::AsyncThread (void *arg) if (log) log->Printf ("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread starting...", arg, pid); - Listener listener ("ProcessKDP::AsyncThread"); + ListenerSP listener_sp (Listener::MakeListener("ProcessKDP::AsyncThread")); EventSP event_sp; const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; - if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) + if (listener_sp->StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) { bool done = false; while (!done) @@ -941,7 +941,7 @@ ProcessKDP::AsyncThread (void *arg) if (log) log->Printf ("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", pid); - if (listener.WaitForEvent (NULL, event_sp)) + if (listener_sp->WaitForEvent (NULL, event_sp)) { uint32_t event_type = event_sp->GetType(); if (log) @@ -981,7 +981,7 @@ ProcessKDP::AsyncThread (void *arg) // Check to see if we are supposed to exit. There is no way to // interrupt a running kernel, so all we can do is wait for an // exception or detach... - if (listener.GetNextEvent(event_sp)) + if (listener_sp->GetNextEvent(event_sp)) { // We got an event, go through the loop again event_type = event_sp->GetType(); @@ -1187,11 +1187,9 @@ public: class CommandObjectMultiwordProcessKDP : public CommandObjectMultiword { public: - CommandObjectMultiwordProcessKDP (CommandInterpreter &interpreter) : - CommandObjectMultiword (interpreter, - "process plugin", - "A set of commands for operating on a ProcessKDP process.", - "process plugin <subcommand> [<subcommand-options>]") + CommandObjectMultiwordProcessKDP(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "process plugin", "Commands for operating on a ProcessKDP process.", + "process plugin <subcommand> [<subcommand-options>]") { LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessKDPPacket (interpreter))); } diff --git a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h index fe9a4e2844bf3..49f6362425103 100644 --- a/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h +++ b/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h @@ -40,7 +40,7 @@ public: //------------------------------------------------------------------ static lldb::ProcessSP CreateInstance (lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener_sp, const lldb_private::FileSpec *crash_file_path); static void @@ -61,7 +61,7 @@ public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ - ProcessKDP(lldb::TargetSP target_sp, lldb_private::Listener &listener); + ProcessKDP(lldb::TargetSP target_sp, lldb::ListenerSP listener); virtual ~ProcessKDP(); diff --git a/source/Plugins/Process/POSIX/Makefile b/source/Plugins/Process/POSIX/Makefile deleted file mode 100644 index e8ac3a8ae0ed5..0000000000000 --- a/source/Plugins/Process/POSIX/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -##===- source/Plugins/Process/POSIX/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 := lldbPluginProcessPOSIX -BUILD_ARCHIVE = 1 - -include $(LLDB_LEVEL)/../../Makefile.config - -# Extend the include path so we may locate UnwindLLDB.h -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Utility - -ifeq ($(HOST_OS),Linux) -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/Linux - -# Disable warning for now as offsetof is used with an index into a structure member array -# in defining register info tables. -CPP.Flags += -Wno-extended-offsetof -endif - -ifneq (,$(filter $(HOST_OS), FreeBSD GNU/kFreeBSD)) -# Extend the include path so we may locate ProcessMonitor -CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/FreeBSD -endif - -include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Utility/CMakeLists.txt b/source/Plugins/Process/Utility/CMakeLists.txt index 4a847b4f966a4..eb0c81fd7979f 100644 --- a/source/Plugins/Process/Utility/CMakeLists.txt +++ b/source/Plugins/Process/Utility/CMakeLists.txt @@ -28,16 +28,19 @@ add_lldb_library(lldbPluginProcessUtility RegisterContextLinux_x86_64.cpp RegisterContextLinux_mips64.cpp RegisterContextLinux_mips.cpp + RegisterContextLinux_s390x.cpp RegisterContextLLDB.cpp RegisterContextMacOSXFrameBackchain.cpp RegisterContextMach_arm.cpp RegisterContextMach_i386.cpp RegisterContextMach_x86_64.cpp RegisterContextMemory.cpp + RegisterContextNetBSD_x86_64.cpp RegisterContextPOSIX_arm.cpp RegisterContextPOSIX_arm64.cpp RegisterContextPOSIX_mips64.cpp RegisterContextPOSIX_powerpc.cpp + RegisterContextPOSIX_s390x.cpp RegisterContextPOSIX_x86.cpp RegisterContextThreadMemory.cpp StopInfoMachException.cpp diff --git a/source/Plugins/Process/Utility/HistoryThread.cpp b/source/Plugins/Process/Utility/HistoryThread.cpp index 206b8290c5fd1..956539da219c9 100644 --- a/source/Plugins/Process/Utility/HistoryThread.cpp +++ b/source/Plugins/Process/Utility/HistoryThread.cpp @@ -22,28 +22,24 @@ using namespace lldb_private; // Constructor -HistoryThread::HistoryThread (lldb_private::Process &process, - lldb::tid_t tid, - std::vector<lldb::addr_t> pcs, - uint32_t stop_id, - bool stop_id_is_valid) : - Thread (process, tid, true), - m_framelist_mutex(), - m_framelist(), - m_pcs (pcs), - m_stop_id (stop_id), - m_stop_id_is_valid (stop_id_is_valid), - m_extended_unwind_token (LLDB_INVALID_ADDRESS), - m_queue_name (), - m_thread_name (), - m_originating_unique_thread_id (tid), - m_queue_id (LLDB_INVALID_QUEUE_ID) +HistoryThread::HistoryThread(lldb_private::Process &process, lldb::tid_t tid, std::vector<lldb::addr_t> pcs, + uint32_t stop_id, bool stop_id_is_valid) + : Thread(process, tid, true), + m_framelist_mutex(), + m_framelist(), + m_pcs(pcs), + m_stop_id(stop_id), + m_stop_id_is_valid(stop_id_is_valid), + m_extended_unwind_token(LLDB_INVALID_ADDRESS), + m_queue_name(), + m_thread_name(), + m_originating_unique_thread_id(tid), + m_queue_id(LLDB_INVALID_QUEUE_ID) { - m_unwinder_ap.reset (new HistoryUnwind (*this, pcs, stop_id_is_valid)); - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + m_unwinder_ap.reset(new HistoryUnwind(*this, pcs, stop_id_is_valid)); + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) - log->Printf ("%p HistoryThread::HistoryThread", - static_cast<void*>(this)); + log->Printf("%p HistoryThread::HistoryThread", static_cast<void *>(this)); } // Destructor @@ -78,7 +74,9 @@ HistoryThread::CreateRegisterContextForFrame (StackFrame *frame) lldb::StackFrameListSP HistoryThread::GetStackFrameList () { - Mutex::Locker (m_framelist_mutex); // FIXME do not throw away the lock after we acquire it.. + // FIXME do not throw away the lock after we acquire it.. + std::unique_lock<std::mutex> lock(m_framelist_mutex); + lock.unlock(); if (m_framelist.get() == NULL) { m_framelist.reset (new StackFrameList (*this, StackFrameListSP(), true)); diff --git a/source/Plugins/Process/Utility/HistoryThread.h b/source/Plugins/Process/Utility/HistoryThread.h index e87f6496134b4..43ac13c2d8bcb 100644 --- a/source/Plugins/Process/Utility/HistoryThread.h +++ b/source/Plugins/Process/Utility/HistoryThread.h @@ -12,10 +12,11 @@ // C Includes // C++ Includes +#include <mutex> + // Other libraries and framework includes // Project includes #include "lldb/lldb-private.h" -#include "lldb/Host/Mutex.h" #include "lldb/Core/Broadcaster.h" #include "lldb/Core/Event.h" #include "lldb/Core/UserID.h" @@ -125,7 +126,7 @@ protected: virtual lldb::StackFrameListSP GetStackFrameList (); - mutable Mutex m_framelist_mutex; + mutable std::mutex m_framelist_mutex; lldb::StackFrameListSP m_framelist; std::vector<lldb::addr_t> m_pcs; uint32_t m_stop_id; diff --git a/source/Plugins/Process/Utility/HistoryUnwind.cpp b/source/Plugins/Process/Utility/HistoryUnwind.cpp index 14afcbee0b49c..01d8c3ebdcd3e 100644 --- a/source/Plugins/Process/Utility/HistoryUnwind.cpp +++ b/source/Plugins/Process/Utility/HistoryUnwind.cpp @@ -40,7 +40,7 @@ HistoryUnwind::~HistoryUnwind () void HistoryUnwind::DoClear () { - Mutex::Locker locker(m_unwind_mutex); + std::lock_guard<std::recursive_mutex> guard(m_unwind_mutex); m_pcs.clear(); m_stop_id_is_valid = false; } @@ -64,7 +64,9 @@ HistoryUnwind::DoCreateRegisterContextForFrame (StackFrame *frame) bool HistoryUnwind::DoGetFrameInfoAtIndex (uint32_t frame_idx, lldb::addr_t& cfa, lldb::addr_t& pc) { - Mutex::Locker (m_unwind_mutex); // FIXME do not throw away the lock after we acquire it.. + // FIXME do not throw away the lock after we acquire it.. + std::unique_lock<std::recursive_mutex> guard(m_unwind_mutex); + guard.unlock(); if (frame_idx < m_pcs.size()) { cfa = frame_idx; diff --git a/source/Plugins/Process/Utility/HistoryUnwind.h b/source/Plugins/Process/Utility/HistoryUnwind.h index 2cb78bc1dc637..890604fcb680a 100644 --- a/source/Plugins/Process/Utility/HistoryUnwind.h +++ b/source/Plugins/Process/Utility/HistoryUnwind.h @@ -17,7 +17,6 @@ // Other libraries and framework includes // Project includes #include "lldb/lldb-private.h" -#include "lldb/Host/Mutex.h" #include "lldb/Target/Unwind.h" namespace lldb_private { diff --git a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index ebeba8c46a74a..b694b833cb482 100644 --- a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -11,6 +11,8 @@ #include "lldb/Core/Address.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/ValueObject.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Host/Config.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ExecutionContext.h" @@ -18,7 +20,6 @@ #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadPlanCallFunction.h" -#include "lldb/Host/Config.h" #ifndef LLDB_DISABLE_POSIX #include <sys/mman.h> @@ -43,7 +44,7 @@ lldb_private::InferiorCallMmap (Process *process, addr_t fd, addr_t offset) { - Thread *thread = process->GetThreadList().GetSelectedThread().get(); + Thread *thread = process->GetThreadList().GetExpressionExecutionThread().get(); if (thread == NULL) return false; @@ -96,27 +97,21 @@ lldb_private::InferiorCallMmap (Process *process, ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); lldb::addr_t args[] = { addr, length, prot_arg, flags_arg, fd, offset }; - lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread, - mmap_range.GetBaseAddress(), - clang_void_ptr_type, - args, - options)); + lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), + clang_void_ptr_type, args, options)); if (call_plan_sp) { - StreamFile error_strm; - - StackFrame *frame = thread->GetStackFrameAtIndex (0).get(); + DiagnosticManager diagnostics; + + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); if (frame) { ExecutionContext exe_ctx; frame->CalculateExecutionContext (exe_ctx); - ExpressionResults result = process->RunThreadPlan (exe_ctx, - call_plan_sp, - options, - error_strm); + ExpressionResults result = process->RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics); if (result == eExpressionCompleted) { - + allocated_addr = call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); if (process->GetAddressByteSize() == 4) { @@ -144,7 +139,7 @@ lldb_private::InferiorCallMunmap (Process *process, addr_t addr, addr_t length) { - Thread *thread = process->GetThreadList().GetSelectedThread().get(); + Thread *thread = process->GetThreadList().GetExpressionExecutionThread().get(); if (thread == NULL) return false; @@ -179,24 +174,18 @@ lldb_private::InferiorCallMunmap (Process *process, if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, munmap_range)) { lldb::addr_t args[] = { addr, length }; - lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread, - munmap_range.GetBaseAddress(), - CompilerType(), - args, - options)); + lldb::ThreadPlanSP call_plan_sp( + new ThreadPlanCallFunction(*thread, munmap_range.GetBaseAddress(), CompilerType(), args, options)); if (call_plan_sp) { - StreamFile error_strm; - - StackFrame *frame = thread->GetStackFrameAtIndex (0).get(); + DiagnosticManager diagnostics; + + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); if (frame) { ExecutionContext exe_ctx; frame->CalculateExecutionContext (exe_ctx); - ExpressionResults result = process->RunThreadPlan (exe_ctx, - call_plan_sp, - options, - error_strm); + ExpressionResults result = process->RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics); if (result == eExpressionCompleted) { return true; @@ -219,7 +208,7 @@ lldb_private::InferiorCall (Process *process, addr_t &returned_func, bool trap_exceptions) { - Thread *thread = process->GetThreadList().GetSelectedThread().get(); + Thread *thread = process->GetThreadList().GetExpressionExecutionThread().get(); if (thread == NULL || address == NULL) return false; @@ -234,24 +223,18 @@ lldb_private::InferiorCall (Process *process, ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); CompilerType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); - lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread, - *address, - clang_void_ptr_type, - llvm::ArrayRef<addr_t>(), - options)); + lldb::ThreadPlanSP call_plan_sp( + new ThreadPlanCallFunction(*thread, *address, clang_void_ptr_type, llvm::ArrayRef<addr_t>(), options)); if (call_plan_sp) { - StreamString error_strm; + DiagnosticManager diagnostics; - StackFrame *frame = thread->GetStackFrameAtIndex (0).get(); + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); if (frame) { ExecutionContext exe_ctx; frame->CalculateExecutionContext (exe_ctx); - ExpressionResults result = process->RunThreadPlan (exe_ctx, - call_plan_sp, - options, - error_strm); + ExpressionResults result = process->RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics); if (result == eExpressionCompleted) { returned_func = call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); diff --git a/source/Plugins/Process/Utility/Makefile b/source/Plugins/Process/Utility/Makefile deleted file mode 100644 index eb0caf3f92fe5..0000000000000 --- a/source/Plugins/Process/Utility/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- source/Plugins/Utility/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 := lldbPluginProcessUtility -BUILD_ARCHIVE = 1 - -include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp index 452fb47ebc8a7..aa1bace772034 100644 --- a/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp +++ b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp @@ -596,6 +596,7 @@ RegisterContextDarwin_arm::ReadRegisterSet (uint32_t set, bool force) switch (set) { case GPRRegSet: return ReadGPR(force); + case GPRAltRegSet: return ReadGPR(force); case FPURegSet: return ReadFPU(force); case EXCRegSet: return ReadEXC(force); case DBGRegSet: return ReadDBG(force); @@ -613,6 +614,7 @@ RegisterContextDarwin_arm::WriteRegisterSet (uint32_t set) switch (set) { case GPRRegSet: return WriteGPR(); + case GPRAltRegSet: return WriteGPR(); case FPURegSet: return WriteFPU(); case EXCRegSet: return WriteEXC(); case DBGRegSet: return WriteDBG(); diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h index f4d82259f9df9..4e831b5a8da77 100644 --- a/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h +++ b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h @@ -162,10 +162,11 @@ public: protected: enum { - GPRRegSet = 1, // ARM_THREAD_STATE - FPURegSet = 2, // ARM_VFP_STATE - EXCRegSet = 3, // ARM_EXCEPTION_STATE - DBGRegSet = 4 // ARM_DEBUG_STATE + GPRRegSet = 1, // ARM_THREAD_STATE + GPRAltRegSet = 9, // ARM_THREAD_STATE32 + FPURegSet = 2, // ARM_VFP_STATE + EXCRegSet = 3, // ARM_EXCEPTION_STATE + DBGRegSet = 4 // ARM_DEBUG_STATE }; enum diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp index efda0edb70c96..8bbaeb8e9a598 100644 --- a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -470,11 +470,13 @@ RegisterContextLLDB::InitializeNonZerothFrame() return; } - bool resolve_tail_call_address = true; // m_current_pc can be one past the address range of the function... - // This will handle the case where the saved pc does not point to - // a function/symbol because it is beyond the bounds of the correct - // function and there's no symbol there. ResolveSymbolContextForAddress - // will fail to find a symbol, back up the pc by 1 and re-search. + bool resolve_tail_call_address = false; // m_current_pc can be one past the address range of the function... + // If the saved pc does not point to a function/symbol because it is + // beyond the bounds of the correct function and there's no symbol there, + // we do *not* want ResolveSymbolContextForAddress to back up the pc by 1, + // because then we might not find the correct unwind information later. + // Instead, let ResolveSymbolContextForAddress fail, and handle the case + // via decr_pc_and_recompute_addr_range below. const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; uint32_t resolved_scope = pc_module_sp->ResolveSymbolContextForAddress (m_current_pc, resolve_scope, @@ -1390,45 +1392,28 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat } } - if (have_unwindplan_regloc == false) - { - // Did the UnwindPlan fail to give us the caller's stack pointer? - // The stack pointer is defined to be the same as THIS frame's CFA, so return the CFA value as - // the caller's stack pointer. This is true on x86-32/x86-64 at least. - - RegisterNumber sp_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); - if (sp_regnum.GetAsKind (eRegisterKindLLDB) != LLDB_INVALID_REGNUM - && sp_regnum.GetAsKind (eRegisterKindLLDB) == regnum.GetAsKind (eRegisterKindLLDB)) - { - // make sure we won't lose precision copying an addr_t (m_cfa) into a uint64_t (.inferred_value) - assert (sizeof (addr_t) <= sizeof (uint64_t)); - regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; - regloc.location.inferred_value = m_cfa; - m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; - UnwindLogMsg ("supplying caller's stack pointer %s (%d) value, computed from CFA", - regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); - return UnwindLLDB::RegisterSearchResult::eRegisterFound; - } - } - ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); if (have_unwindplan_regloc == false) { - // If a volatile register is being requested, we don't want to forward the next frame's register contents - // up the stack -- the register is not retrievable at this frame. + // If the UnwindPlan failed to give us an unwind location for this register, we may be able to fall back + // to some ABI-defined default. For example, some ABIs allow to determine the caller's SP via the CFA. + // Also, the ABI may set volatile registers to the undefined state. ABI *abi = process ? process->GetABI().get() : NULL; if (abi) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(regnum.GetAsKind (eRegisterKindLLDB)); - if (reg_info && abi->RegisterIsVolatile (reg_info)) + if (reg_info && abi->GetFallbackRegisterLocation (reg_info, unwindplan_regloc)) { - UnwindLogMsg ("did not supply reg location for %s (%d) because it is volatile", + UnwindLogMsg ("supplying caller's saved %s (%d)'s location using ABI default", regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); - return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile; + have_unwindplan_regloc = true; } } + } + if (have_unwindplan_regloc == false) + { if (IsFrameZero ()) { // This is frame 0 - we should return the actual live register context value @@ -1468,15 +1453,33 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } + if (unwindplan_regloc.IsUndefined()) + { + UnwindLogMsg ("did not supply reg location for %s (%d) because it is volatile", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); + return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile; + } + if (unwindplan_regloc.IsSame()) { - regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; - regloc.location.register_number = regnum.GetAsKind (eRegisterKindLLDB); - m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; - UnwindLogMsg ("supplying caller's register %s (%d), saved in register %s (%d)", - regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), - regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); - return UnwindLLDB::RegisterSearchResult::eRegisterFound; + if (IsFrameZero() == false + && (regnum.GetAsKind (eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_PC + || regnum.GetAsKind (eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_RA)) + { + UnwindLogMsg ("register %s (%d) is marked as 'IsSame' - it is a pc or return address reg on a non-zero frame -- treat as if we have no information", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + else + { + regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; + regloc.location.register_number = regnum.GetAsKind (eRegisterKindLLDB); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; + UnwindLogMsg ("supplying caller's register %s (%d), saved in register %s (%d)", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } } if (unwindplan_regloc.IsCFAPlusOffset()) @@ -1536,7 +1539,7 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat dwarfexpr.SetRegisterKind (unwindplan_registerkind); Value result; Error error; - if (dwarfexpr.Evaluate (&exe_ctx, NULL, NULL, this, 0, NULL, result, &error)) + if (dwarfexpr.Evaluate (&exe_ctx, nullptr, nullptr, this, 0, nullptr, nullptr, result, &error)) { addr_t val; val = result.GetScalar().ULongLong(); @@ -1836,7 +1839,7 @@ RegisterContextLLDB::ReadCFAValueForRow (lldb::RegisterKind row_register_kind, dwarfexpr.SetRegisterKind (row_register_kind); Value result; Error error; - if (dwarfexpr.Evaluate (&exe_ctx, NULL, NULL, this, 0, NULL, result, &error)) + if (dwarfexpr.Evaluate (&exe_ctx, nullptr, nullptr, this, 0, nullptr, nullptr, result, &error)) { cfa_value = result.GetScalar().ULongLong(); @@ -1897,12 +1900,13 @@ RegisterContextLLDB::ReadGPRValue (lldb::RegisterKind register_kind, uint32_t re bool pc_register = false; uint32_t generic_regnum; - if (register_kind == eRegisterKindGeneric && regnum == LLDB_REGNUM_GENERIC_PC) + if (register_kind == eRegisterKindGeneric + && (regnum == LLDB_REGNUM_GENERIC_PC || regnum == LLDB_REGNUM_GENERIC_RA)) { pc_register = true; } else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindGeneric, generic_regnum) - && generic_regnum == LLDB_REGNUM_GENERIC_PC) + && (generic_regnum == LLDB_REGNUM_GENERIC_PC || generic_regnum == LLDB_REGNUM_GENERIC_RA)) { pc_register = true; } @@ -1944,9 +1948,16 @@ RegisterContextLLDB::ReadRegister (const RegisterInfo *reg_info, RegisterValue & return m_thread.GetRegisterContext()->ReadRegister (reg_info, value); } + bool is_pc_regnum = false; + if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC + || reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA) + { + is_pc_regnum = true; + } + lldb_private::UnwindLLDB::RegisterLocation regloc; // Find out where the NEXT frame saved THIS frame's register contents - if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, false)) + if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, is_pc_regnum)) return false; return ReadRegisterValueFromRegisterLocation (regloc, reg_info, value); diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp b/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp new file mode 100644 index 0000000000000..9aef1e9830e2b --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp @@ -0,0 +1,98 @@ +//===-- RegisterContextLinux_s390x.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextPOSIX_s390x.h" +#include "RegisterContextLinux_s390x.h" + +using namespace lldb_private; +using namespace lldb; + +//--------------------------------------------------------------------------- +// Include RegisterInfos_s390x to declare our g_register_infos_s390x structure. +//--------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_S390X_STRUCT +#include "RegisterInfos_s390x.h" +#undef DECLARE_REGISTER_INFOS_S390X_STRUCT + +static const RegisterInfo * +GetRegisterInfoPtr(const ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::systemz: + return g_register_infos_s390x; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +GetRegisterInfoCount(const ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::systemz: + return k_num_registers_s390x; + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +static uint32_t +GetUserRegisterInfoCount(const ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::systemz: + return k_num_user_registers_s390x + k_num_linux_registers_s390x; + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextLinux_s390x::RegisterContextLinux_s390x(const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)), + m_user_register_count(GetUserRegisterInfoCount(target_arch)) +{ +} + +const std::vector<lldb_private::RegisterInfo> * +RegisterContextLinux_s390x::GetDynamicRegisterInfoP() const +{ + return &d_register_infos; +} + +const RegisterInfo * +RegisterContextLinux_s390x::GetRegisterInfo() const +{ + return m_register_info_p; +} + +uint32_t +RegisterContextLinux_s390x::GetRegisterCount() const +{ + return m_register_info_count; +} + +uint32_t +RegisterContextLinux_s390x::GetUserRegisterCount() const +{ + return m_user_register_count; +} + +size_t +RegisterContextLinux_s390x::GetGPRSize() const +{ + return 0; +} diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h b/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h new file mode 100644 index 0000000000000..bdc7f34f62e77 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h @@ -0,0 +1,42 @@ +//===-- RegisterContextLinux_s390x.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_s390x_h_ +#define liblldb_RegisterContextLinux_s390x_h_ + +#include "RegisterInfoInterface.h" + +class RegisterContextLinux_s390x : public lldb_private::RegisterInfoInterface +{ +public: + RegisterContextLinux_s390x(const lldb_private::ArchSpec &target_arch); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount() const override; + + uint32_t + GetUserRegisterCount() const override; + + const std::vector<lldb_private::RegisterInfo> * + GetDynamicRegisterInfoP() const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; + uint32_t m_user_register_count; + std::vector<lldb_private::RegisterInfo> d_register_infos; +}; + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp b/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp new file mode 100644 index 0000000000000..71ece160d4d46 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp @@ -0,0 +1,357 @@ +//===-- RegisterContextNetBSD_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 <cstddef> +#include <vector> + +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_x86.h" +#include "RegisterContextNetBSD_x86_64.h" + +using namespace lldb_private; +using namespace lldb; + +// src/sys/arch/amd64/include/frame_regs.h +typedef struct _GPR +{ + uint64_t rdi; /* 0 */ + uint64_t rsi; /* 1 */ + uint64_t rdx; /* 2 */ + uint64_t rcx; /* 3 */ + uint64_t r8; /* 4 */ + uint64_t r9; /* 5 */ + uint64_t r10; /* 6 */ + uint64_t r11; /* 7 */ + uint64_t r12; /* 8 */ + uint64_t r13; /* 9 */ + uint64_t r14; /* 10 */ + uint64_t r15; /* 11 */ + uint64_t rbp; /* 12 */ + uint64_t rbx; /* 13 */ + uint64_t rax; /* 14 */ + uint64_t gs; /* 15 */ + uint64_t fs; /* 16 */ + uint64_t es; /* 17 */ + uint64_t ds; /* 18 */ + uint64_t trapno; /* 19 */ + uint64_t err; /* 20 */ + uint64_t rip; /* 21 */ + uint64_t cs; /* 22 */ + uint64_t rflags; /* 23 */ + uint64_t rsp; /* 24 */ + uint64_t ss; /* 25 */ +} GPR; + +/* + * As of NetBSD-7.99.25 there is no support for debug registers + * https://en.wikipedia.org/wiki/X86_debug_register + */ + +/* + * src/sys/arch/amd64/include/mcontext.h + * + * typedef struct { + * __gregset_t __gregs; + * __greg_t _mc_tlsbase; + * __fpregset_t __fpregs; + * } mcontext_t; + */ + +struct UserArea { + GPR gpr; + uint64_t mc_tlsbase; + FPR fpr; +}; + + +//--------------------------------------------------------------------------- +// Cherry-pick parts of RegisterInfos_x86_64.h, without debug registers +//--------------------------------------------------------------------------- +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(GPR, regname)) + +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xstate) + \ + LLVM_EXTENSION offsetof(FXSAVE, regname)) + +// Computes the offset of the YMM register assembled from register halves. +// Based on DNBArchImplX86_64.cpp from debugserver +#define YMM_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xstate) + \ + LLVM_EXTENSION offsetof(XSAVE, ymmh[0]) + \ + (32 * reg_index)) + +// Number of bytes needed to represent a FPR. +#define FPR_SIZE(reg) sizeof(((FXSAVE*)NULL)->reg) + +// Number of bytes needed to represent the i'th FP register. +#define FP_SIZE sizeof(((MMSReg*)NULL)->bytes) + +// Number of bytes needed to represent an XMM register. +#define XMM_SIZE sizeof(XMMReg) + +// Number of bytes needed to represent a YMM register. +#define YMM_SIZE sizeof(YMMReg) + +// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((GPR*)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, lldb_##reg##_x86_64 }, NULL, NULL } + +#define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \ + { #name, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, lldb_##name##_x86_64 }, NULL, NULL } + +#define DEFINE_FP_ST(reg, i) \ + { #reg#i, NULL, FP_SIZE, LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + { dwarf_st##i##_x86_64, dwarf_st##i##_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_st##i##_x86_64 }, \ + NULL, NULL } + +#define DEFINE_FP_MM(reg, i) \ + { #reg#i, NULL, sizeof(uint64_t), LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ + eEncodingUint, eFormatHex, \ + { dwarf_mm##i##_x86_64, dwarf_mm##i##_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_mm##i##_x86_64 }, \ + NULL, NULL } + +#define DEFINE_XMM(reg, i) \ + { #reg#i, NULL, XMM_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + { dwarf_##reg##i##_x86_64, dwarf_##reg##i##_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg##i##_x86_64}, \ + NULL, NULL } + +#define DEFINE_YMM(reg, i) \ + { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(i), \ + eEncodingVector, eFormatVectorOfUInt8, \ + { dwarf_##reg##i##h_x86_64, dwarf_##reg##i##h_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg##i##_x86_64 }, \ + NULL, NULL } + +#define DEFINE_GPR_PSEUDO_32(reg32, reg64) \ + { #reg32, NULL, 4, GPR_OFFSET(reg64), eEncodingUint, \ + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg32##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } +#define DEFINE_GPR_PSEUDO_16(reg16, reg64) \ + { #reg16, NULL, 2, GPR_OFFSET(reg64), eEncodingUint, \ + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg16##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } +#define DEFINE_GPR_PSEUDO_8H(reg8, reg64) \ + { #reg8, NULL, 1, GPR_OFFSET(reg64)+1, eEncodingUint, \ + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg8##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } +#define DEFINE_GPR_PSEUDO_8L(reg8, reg64) \ + { #reg8, NULL, 1, GPR_OFFSET(reg64), eEncodingUint, \ + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg8##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } + +static RegisterInfo +g_register_infos_x86_64[] = +{ + // General purpose registers. EH_Frame, DWARF, Generic, Process Plugin + DEFINE_GPR(rax, nullptr, dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbx, nullptr, dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rcx, "arg4", dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdx, "arg3", dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdi, "arg1", dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsi, "arg2", dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbp, "fp", dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsp, "sp", dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, "arg5", dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, "arg6", dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, nullptr, dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, nullptr, dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r12, nullptr, dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, nullptr, dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, nullptr, dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, nullptr, dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rip, "pc", dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(rflags, "flags", dwarf_rflags_x86_64, dwarf_rflags_x86_64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, dwarf_cs_x86_64, dwarf_cs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, dwarf_fs_x86_64, dwarf_fs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, dwarf_gs_x86_64, dwarf_gs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, dwarf_ss_x86_64, dwarf_ss_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, dwarf_ds_x86_64, dwarf_ds_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, dwarf_es_x86_64, dwarf_es_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + DEFINE_GPR_PSEUDO_32(eax, rax), + DEFINE_GPR_PSEUDO_32(ebx, rbx), + DEFINE_GPR_PSEUDO_32(ecx, rcx), + DEFINE_GPR_PSEUDO_32(edx, rdx), + DEFINE_GPR_PSEUDO_32(edi, rdi), + DEFINE_GPR_PSEUDO_32(esi, rsi), + DEFINE_GPR_PSEUDO_32(ebp, rbp), + DEFINE_GPR_PSEUDO_32(esp, rsp), + DEFINE_GPR_PSEUDO_32(r8d, r8), + DEFINE_GPR_PSEUDO_32(r9d, r9), + DEFINE_GPR_PSEUDO_32(r10d, r10), + DEFINE_GPR_PSEUDO_32(r11d, r11), + DEFINE_GPR_PSEUDO_32(r12d, r12), + DEFINE_GPR_PSEUDO_32(r13d, r13), + DEFINE_GPR_PSEUDO_32(r14d, r14), + DEFINE_GPR_PSEUDO_32(r15d, r15), + DEFINE_GPR_PSEUDO_16(ax, rax), + DEFINE_GPR_PSEUDO_16(bx, rbx), + DEFINE_GPR_PSEUDO_16(cx, rcx), + DEFINE_GPR_PSEUDO_16(dx, rdx), + DEFINE_GPR_PSEUDO_16(di, rdi), + DEFINE_GPR_PSEUDO_16(si, rsi), + DEFINE_GPR_PSEUDO_16(bp, rbp), + DEFINE_GPR_PSEUDO_16(sp, rsp), + DEFINE_GPR_PSEUDO_16(r8w, r8), + DEFINE_GPR_PSEUDO_16(r9w, r9), + DEFINE_GPR_PSEUDO_16(r10w, r10), + DEFINE_GPR_PSEUDO_16(r11w, r11), + DEFINE_GPR_PSEUDO_16(r12w, r12), + DEFINE_GPR_PSEUDO_16(r13w, r13), + DEFINE_GPR_PSEUDO_16(r14w, r14), + DEFINE_GPR_PSEUDO_16(r15w, r15), + DEFINE_GPR_PSEUDO_8H(ah, rax), + DEFINE_GPR_PSEUDO_8H(bh, rbx), + DEFINE_GPR_PSEUDO_8H(ch, rcx), + DEFINE_GPR_PSEUDO_8H(dh, rdx), + DEFINE_GPR_PSEUDO_8L(al, rax), + DEFINE_GPR_PSEUDO_8L(bl, rbx), + DEFINE_GPR_PSEUDO_8L(cl, rcx), + DEFINE_GPR_PSEUDO_8L(dl, rdx), + DEFINE_GPR_PSEUDO_8L(dil, rdi), + DEFINE_GPR_PSEUDO_8L(sil, rsi), + DEFINE_GPR_PSEUDO_8L(bpl, rbp), + DEFINE_GPR_PSEUDO_8L(spl, rsp), + DEFINE_GPR_PSEUDO_8L(r8l, r8), + DEFINE_GPR_PSEUDO_8L(r9l, r9), + DEFINE_GPR_PSEUDO_8L(r10l, r10), + DEFINE_GPR_PSEUDO_8L(r11l, r11), + DEFINE_GPR_PSEUDO_8L(r12l, r12), + DEFINE_GPR_PSEUDO_8L(r13l, r13), + DEFINE_GPR_PSEUDO_8L(r14l, r14), + DEFINE_GPR_PSEUDO_8L(r15l, r15), + + // i387 Floating point registers. EH_frame, DWARF, Generic, Process Plugin + DEFINE_FPR(fctrl, fctrl, dwarf_fctrl_x86_64, dwarf_fctrl_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fstat, fstat, dwarf_fstat_x86_64, dwarf_fstat_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(ftag, ftag, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fop, fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fiseg, ptr.i386_.fiseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fioff, ptr.i386_.fioff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(foseg, ptr.i386_.foseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fooff, ptr.i386_.fooff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(mxcsr, mxcsr, dwarf_mxcsr_x86_64, dwarf_mxcsr_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(mxcsrmask, mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + // FP registers. + DEFINE_FP_ST(st, 0), + DEFINE_FP_ST(st, 1), + DEFINE_FP_ST(st, 2), + DEFINE_FP_ST(st, 3), + DEFINE_FP_ST(st, 4), + DEFINE_FP_ST(st, 5), + DEFINE_FP_ST(st, 6), + DEFINE_FP_ST(st, 7), + DEFINE_FP_MM(mm, 0), + DEFINE_FP_MM(mm, 1), + DEFINE_FP_MM(mm, 2), + DEFINE_FP_MM(mm, 3), + DEFINE_FP_MM(mm, 4), + DEFINE_FP_MM(mm, 5), + DEFINE_FP_MM(mm, 6), + DEFINE_FP_MM(mm, 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), + + // Copy of YMM registers assembled from xmm and ymmh + DEFINE_YMM(ymm, 0), + DEFINE_YMM(ymm, 1), + DEFINE_YMM(ymm, 2), + DEFINE_YMM(ymm, 3), + DEFINE_YMM(ymm, 4), + DEFINE_YMM(ymm, 5), + DEFINE_YMM(ymm, 6), + DEFINE_YMM(ymm, 7), + DEFINE_YMM(ymm, 8), + DEFINE_YMM(ymm, 9), + DEFINE_YMM(ymm, 10), + DEFINE_YMM(ymm, 11), + DEFINE_YMM(ymm, 12), + DEFINE_YMM(ymm, 13), + DEFINE_YMM(ymm, 14), + DEFINE_YMM(ymm, 15), +}; + +//--------------------------------------------------------------------------- +// End of cherry-pick of RegisterInfos_x86_64.h +//--------------------------------------------------------------------------- + +static const RegisterInfo * +PrivateGetRegisterInfoPtr (const lldb_private::ArchSpec& target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::x86_64: + return g_register_infos_x86_64; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +PrivateGetRegisterCount (const lldb_private::ArchSpec& target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::x86_64: + return static_cast<uint32_t> (sizeof (g_register_infos_x86_64) / sizeof (g_register_infos_x86_64 [0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextNetBSD_x86_64::RegisterContextNetBSD_x86_64(const ArchSpec &target_arch) : + lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p (PrivateGetRegisterInfoPtr (target_arch)), + m_register_count (PrivateGetRegisterCount (target_arch)) +{ +} + +size_t +RegisterContextNetBSD_x86_64::GetGPRSize() const +{ + return sizeof(GPR); +} + +const RegisterInfo * +RegisterContextNetBSD_x86_64::GetRegisterInfo() const +{ + return m_register_info_p; +} + +uint32_t +RegisterContextNetBSD_x86_64::GetRegisterCount () const +{ + return m_register_count; +} diff --git a/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h b/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h new file mode 100644 index 0000000000000..c267278c418c8 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h @@ -0,0 +1,35 @@ +//===-- RegisterContextNetBSD_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_RegisterContextNetBSD_x86_64_H_ +#define liblldb_RegisterContextNetBSD_x86_64_H_ + +#include "RegisterInfoInterface.h" + +class RegisterContextNetBSD_x86_64: + public lldb_private::RegisterInfoInterface +{ +public: + RegisterContextNetBSD_x86_64(const lldb_private::ArchSpec &target_arch); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount () const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + const uint32_t m_register_count; +}; + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp b/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp new file mode 100644 index 0000000000000..960be50c7be36 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp @@ -0,0 +1,265 @@ +//===-- RegisterContextPOSIX_x86.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/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Host/Endian.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContext_s390x.h" +#include "RegisterContextPOSIX_s390x.h" + +using namespace lldb_private; +using namespace lldb; + +// s390x 64-bit general purpose registers. +static const uint32_t g_gpr_regnums_s390x[] = +{ + lldb_r0_s390x, + lldb_r1_s390x, + lldb_r2_s390x, + lldb_r3_s390x, + lldb_r4_s390x, + lldb_r5_s390x, + lldb_r6_s390x, + lldb_r7_s390x, + lldb_r8_s390x, + lldb_r9_s390x, + lldb_r10_s390x, + lldb_r11_s390x, + lldb_r12_s390x, + lldb_r13_s390x, + lldb_r14_s390x, + lldb_r15_s390x, + lldb_acr0_s390x, + lldb_acr1_s390x, + lldb_acr2_s390x, + lldb_acr3_s390x, + lldb_acr4_s390x, + lldb_acr5_s390x, + lldb_acr6_s390x, + lldb_acr7_s390x, + lldb_acr8_s390x, + lldb_acr9_s390x, + lldb_acr10_s390x, + lldb_acr11_s390x, + lldb_acr12_s390x, + lldb_acr13_s390x, + lldb_acr14_s390x, + lldb_acr15_s390x, + lldb_pswm_s390x, + lldb_pswa_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) - 1 == k_num_gpr_registers_s390x, + "g_gpr_regnums_s390x has wrong number of register infos"); + +// s390x 64-bit floating point registers. +static const uint32_t g_fpu_regnums_s390x[] = +{ + lldb_f0_s390x, + lldb_f1_s390x, + lldb_f2_s390x, + lldb_f3_s390x, + lldb_f4_s390x, + lldb_f5_s390x, + lldb_f6_s390x, + lldb_f7_s390x, + lldb_f8_s390x, + lldb_f9_s390x, + lldb_f10_s390x, + lldb_f11_s390x, + lldb_f12_s390x, + lldb_f13_s390x, + lldb_f14_s390x, + lldb_f15_s390x, + lldb_fpc_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) - 1 == k_num_fpr_registers_s390x, + "g_fpu_regnums_s390x has wrong number of register infos"); + +// Number of register sets provided by this context. +enum +{ + k_num_register_sets = 2 +}; + +// Register sets for s390x 64-bit. +static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers_s390x, g_gpr_regnums_s390x }, + { "Floating Point Registers", "fpr", k_num_fpr_registers_s390x, g_fpu_regnums_s390x }, +}; + +bool +RegisterContextPOSIX_s390x::IsGPR(unsigned reg) +{ + return reg <= m_reg_info.last_gpr; // GPRs come first. +} + +bool +RegisterContextPOSIX_s390x::IsFPR(unsigned reg) +{ + return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); +} + +RegisterContextPOSIX_s390x::RegisterContextPOSIX_s390x(Thread &thread, uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info) + : RegisterContext(thread, concrete_frame_idx) +{ + m_register_info_ap.reset(register_info); + + switch (register_info->m_target_arch.GetMachine()) + { + case llvm::Triple::systemz: + m_reg_info.num_registers = k_num_registers_s390x; + m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x; + m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x; + m_reg_info.last_gpr = k_last_gpr_s390x; + m_reg_info.first_fpr = k_first_fpr_s390x; + m_reg_info.last_fpr = k_last_fpr_s390x; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } +} + +RegisterContextPOSIX_s390x::~RegisterContextPOSIX_s390x() +{ +} + +void +RegisterContextPOSIX_s390x::Invalidate() +{ +} + +void +RegisterContextPOSIX_s390x::InvalidateAllRegisters() +{ +} + +const RegisterInfo * +RegisterContextPOSIX_s390x::GetRegisterInfo() +{ + return m_register_info_ap->GetRegisterInfo(); +} + +const RegisterInfo * +RegisterContextPOSIX_s390x::GetRegisterInfoAtIndex(size_t reg) +{ + if (reg < m_reg_info.num_registers) + return &GetRegisterInfo()[reg]; + else + return NULL; +} + +size_t +RegisterContextPOSIX_s390x::GetRegisterCount() +{ + return m_reg_info.num_registers; +} + +unsigned +RegisterContextPOSIX_s390x::GetRegisterOffset(unsigned reg) +{ + assert(reg < m_reg_info.num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +unsigned +RegisterContextPOSIX_s390x::GetRegisterSize(unsigned reg) +{ + assert(reg < m_reg_info.num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +const char * +RegisterContextPOSIX_s390x::GetRegisterName(unsigned reg) +{ + assert(reg < m_reg_info.num_registers && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +bool +RegisterContextPOSIX_s390x::IsRegisterSetAvailable(size_t set_index) +{ + return set_index < k_num_register_sets; +} + +size_t +RegisterContextPOSIX_s390x::GetRegisterSetCount() +{ + size_t sets = 0; + for (size_t set = 0; set < k_num_register_sets; ++set) + { + if (IsRegisterSetAvailable(set)) + ++sets; + } + + return sets; +} + +const RegisterSet * +RegisterContextPOSIX_s390x::GetRegisterSet(size_t set) +{ + if (IsRegisterSetAvailable(set)) + { + switch (m_register_info_ap->m_target_arch.GetMachine()) + { + case llvm::Triple::systemz: + return &g_reg_sets_s390x[set]; + default: + assert(false && "Unhandled target architecture."); + return NULL; + } + } + return NULL; +} + +lldb::ByteOrder +RegisterContextPOSIX_s390x::GetByteOrder() +{ + // Get the target process whose privileged thread was used for the register read. + lldb::ByteOrder byte_order = eByteOrderInvalid; + Process *process = CalculateProcess().get(); + + if (process) + byte_order = process->GetByteOrder(); + return byte_order; +} + +// Used when parsing DWARF and EH frame information and any other +// object file sections that contain register numbers in them. +uint32_t +RegisterContextPOSIX_s390x::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) +{ + const uint32_t num_regs = GetRegisterCount(); + + assert(kind < kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h b/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h new file mode 100644 index 0000000000000..4904b2857c43c --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h @@ -0,0 +1,103 @@ +//===-- RegisterContextPOSIX_s390x.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_RegisterContextPOSIX_s390x_h_ +#define liblldb_RegisterContextPOSIX_s390x_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Target/RegisterContext.h" +#include "RegisterInfoInterface.h" +#include "RegisterContext_s390x.h" +#include "lldb-s390x-register-enums.h" + +class ProcessMonitor; + +class RegisterContextPOSIX_s390x : public lldb_private::RegisterContext +{ +public: + RegisterContextPOSIX_s390x(lldb_private::Thread &thread, uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + + ~RegisterContextPOSIX_s390x() override; + + void + Invalidate(); + + void + InvalidateAllRegisters() override; + + size_t + GetRegisterCount() override; + + virtual unsigned + GetRegisterSize(unsigned reg); + + virtual unsigned + GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex(size_t reg) override; + + size_t + GetRegisterSetCount() override; + + const lldb_private::RegisterSet * + GetRegisterSet(size_t set) override; + + const char * + GetRegisterName(unsigned reg); + + uint32_t + ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num) override; + +protected: + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + }; + + RegInfo m_reg_info; + std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_ap; + + virtual bool + IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfo(); + + bool + IsGPR(unsigned reg); + + bool + IsFPR(unsigned reg); + + lldb::ByteOrder + GetByteOrder(); + + virtual bool + ReadGPR() = 0; + virtual bool + ReadFPR() = 0; + virtual bool + WriteGPR() = 0; + virtual bool + WriteFPR() = 0; +}; + +#endif // liblldb_RegisterContextPOSIX_s390x_h_ diff --git a/source/Plugins/Process/Utility/RegisterContext_s390x.h b/source/Plugins/Process/Utility/RegisterContext_s390x.h new file mode 100644 index 0000000000000..9777c77444098 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContext_s390x.h @@ -0,0 +1,93 @@ +//===-- RegisterContext_s390x.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_RegisterContext_s390x_h_ +#define liblldb_RegisterContext_s390x_h_ + +//--------------------------------------------------------------------------- +// SystemZ ehframe, dwarf regnums +//--------------------------------------------------------------------------- + +// EHFrame and DWARF Register numbers (eRegisterKindEHFrame & eRegisterKindDWARF) +enum +{ + // General Purpose Registers + dwarf_r0_s390x = 0, + dwarf_r1_s390x, + dwarf_r2_s390x, + dwarf_r3_s390x, + dwarf_r4_s390x, + dwarf_r5_s390x, + dwarf_r6_s390x, + dwarf_r7_s390x, + dwarf_r8_s390x, + dwarf_r9_s390x, + dwarf_r10_s390x, + dwarf_r11_s390x, + dwarf_r12_s390x, + dwarf_r13_s390x, + dwarf_r14_s390x, + dwarf_r15_s390x, + // Floating Point Registers / Vector Registers 0-15 + dwarf_f0_s390x = 16, + dwarf_f2_s390x, + dwarf_f4_s390x, + dwarf_f6_s390x, + dwarf_f1_s390x, + dwarf_f3_s390x, + dwarf_f5_s390x, + dwarf_f7_s390x, + dwarf_f8_s390x, + dwarf_f10_s390x, + dwarf_f12_s390x, + dwarf_f14_s390x, + dwarf_f9_s390x, + dwarf_f11_s390x, + dwarf_f13_s390x, + dwarf_f15_s390x, + // Access Registers + dwarf_acr0_s390x = 48, + dwarf_acr1_s390x, + dwarf_acr2_s390x, + dwarf_acr3_s390x, + dwarf_acr4_s390x, + dwarf_acr5_s390x, + dwarf_acr6_s390x, + dwarf_acr7_s390x, + dwarf_acr8_s390x, + dwarf_acr9_s390x, + dwarf_acr10_s390x, + dwarf_acr11_s390x, + dwarf_acr12_s390x, + dwarf_acr13_s390x, + dwarf_acr14_s390x, + dwarf_acr15_s390x, + // Program Status Word + dwarf_pswm_s390x = 64, + dwarf_pswa_s390x, + // Vector Registers 16-31 + dwarf_v16_s390x = 68, + dwarf_v18_s390x, + dwarf_v20_s390x, + dwarf_v22_s390x, + dwarf_v17_s390x, + dwarf_v19_s390x, + dwarf_v21_s390x, + dwarf_v23_s390x, + dwarf_v24_s390x, + dwarf_v26_s390x, + dwarf_v28_s390x, + dwarf_v30_s390x, + dwarf_v25_s390x, + dwarf_v27_s390x, + dwarf_v29_s390x, + dwarf_v31_s390x, +}; + +#endif diff --git a/source/Plugins/Process/Utility/RegisterInfos_mips.h b/source/Plugins/Process/Utility/RegisterInfos_mips.h index eef27f0208e03..3b81acf261466 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_mips.h +++ b/source/Plugins/Process/Utility/RegisterInfos_mips.h @@ -39,8 +39,12 @@ eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_mips }, NULL, NULL } #define DEFINE_FPR(reg, alt, kind1, kind2, kind3, kind4) \ - { #reg, alt, sizeof(((FPR_linux_mips*)NULL)->reg), FPR_OFFSET(reg), eEncodingUint, \ - eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips }, NULL, NULL } + { #reg, alt, sizeof(((FPR_linux_mips*)NULL)->reg), FPR_OFFSET(reg), eEncodingIEEE754, \ + eFormatFloat, { kind1, kind2, kind3, kind4, fpr_##reg##_mips }, NULL, NULL } + +#define DEFINE_FPR_INFO(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((FPR_linux_mips*)NULL)->reg), FPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips }, NULL, NULL } #define DEFINE_MSA(reg, alt, kind1, kind2, kind3, kind4) \ { #reg, alt, sizeof(((MSA_linux_mips*)0)->reg), MSA_OFFSET(reg), eEncodingVector, \ @@ -126,9 +130,9 @@ g_register_infos_mips[] = DEFINE_FPR (f29, nullptr, dwarf_f29_mips, dwarf_f29_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_FPR (f30, nullptr, dwarf_f30_mips, dwarf_f30_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_FPR (f31, nullptr, dwarf_f31_mips, dwarf_f31_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_FPR (fcsr, nullptr, dwarf_fcsr_mips, dwarf_fcsr_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_FPR (fir, nullptr, dwarf_fir_mips, dwarf_fir_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_FPR (config5, nullptr, dwarf_config5_mips, dwarf_config5_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO (fcsr, nullptr, dwarf_fcsr_mips, dwarf_fcsr_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO (fir, nullptr, dwarf_fir_mips, dwarf_fir_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO (config5, nullptr, dwarf_config5_mips, dwarf_config5_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_MSA (w0, nullptr, dwarf_w0_mips, dwarf_w0_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_MSA (w1, nullptr, dwarf_w1_mips, dwarf_w1_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_MSA (w2, nullptr, dwarf_w2_mips, dwarf_w2_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), @@ -176,6 +180,7 @@ static_assert((sizeof(g_register_infos_mips) / sizeof(g_register_infos_mips[0])) #undef MSA_OFFSET #undef DEFINE_GPR #undef DEFINE_FPR +#undef DEFINE_FPR_INFO #undef DEFINE_MSA #undef DEFINE_MSA_INFO diff --git a/source/Plugins/Process/Utility/RegisterInfos_mips64.h b/source/Plugins/Process/Utility/RegisterInfos_mips64.h index 45853d7931ddb..8dbfa6da94a2b 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_mips64.h +++ b/source/Plugins/Process/Utility/RegisterInfos_mips64.h @@ -56,6 +56,10 @@ eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_mips64 }, NULL, NULL } #define DEFINE_FPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((FPR_linux_mips*)0)->reg), FPR_OFFSET(reg), eEncodingIEEE754, \ + eFormatFloat, { kind1, kind2, kind3, kind4, fpr_##reg##_mips64 }, NULL, NULL } + +#define DEFINE_FPR_INFO(reg, alt, kind1, kind2, kind3, kind4) \ { #reg, alt, sizeof(((FPR_linux_mips*)0)->reg), FPR_OFFSET(reg), eEncodingUint, \ eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips64 }, NULL, NULL } @@ -184,9 +188,9 @@ g_register_infos_mips64[] = DEFINE_FPR (f29, nullptr, dwarf_f29_mips64, dwarf_f29_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_FPR (f30, nullptr, dwarf_f30_mips64, dwarf_f30_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_FPR (f31, nullptr, dwarf_f31_mips64, dwarf_f31_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_FPR (fcsr, nullptr, dwarf_fcsr_mips64, dwarf_fcsr_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_FPR (fir, nullptr, dwarf_fir_mips64, dwarf_fir_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_FPR (config5, nullptr, dwarf_config5_mips64, dwarf_config5_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO (fcsr, nullptr, dwarf_fcsr_mips64, dwarf_fcsr_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO (fir, nullptr, dwarf_fir_mips64, dwarf_fir_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO (config5, nullptr, dwarf_config5_mips64, dwarf_config5_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_MSA (w0, nullptr, dwarf_w0_mips64, dwarf_w0_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_MSA (w1, nullptr, dwarf_w1_mips64, dwarf_w1_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_MSA (w2, nullptr, dwarf_w2_mips64, dwarf_w2_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), @@ -233,6 +237,7 @@ static_assert((sizeof(g_register_infos_mips64) / sizeof(g_register_infos_mips64[ #undef DEFINE_GPR #undef DEFINE_GPR_INFO #undef DEFINE_FPR +#undef DEFINE_FPR_INFO #undef DEFINE_MSA #undef DEFINE_MSA_INFO #undef GPR_OFFSET diff --git a/source/Plugins/Process/Utility/RegisterInfos_s390x.h b/source/Plugins/Process/Utility/RegisterInfos_s390x.h new file mode 100644 index 0000000000000..43152640297e7 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterInfos_s390x.h @@ -0,0 +1,132 @@ +//===-- RegisterInfos_s390x.h -----------------------------------*- 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 <stddef.h> + +// C++ Includes +// Other libraries and framework includes +#include "llvm/Support/Compiler.h" + +// Project includes + +#ifdef DECLARE_REGISTER_INFOS_S390X_STRUCT + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(num) (16 + 8 * num) +// Computes the offset of the given ACR in the user data area. +#define ACR_OFFSET(num) (16 + 8 * 16 + 4 * num) +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(num) (8 + 8 * num) + +// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB + +#define DEFINE_GPR(name, size, offset, alt, generic) \ + { \ + #name, alt, size, offset, eEncodingUint, eFormatHex, \ + { dwarf_##name##_s390x, dwarf_##name##_s390x, generic, LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \ + NULL, NULL, \ + } + +#define DEFINE_GPR_NODWARF(name, size, offset, alt, generic) \ + { \ + #name, alt, size, offset, eEncodingUint, eFormatHex, \ + { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, generic, LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \ + NULL, NULL, \ + } + +#define DEFINE_FPR(name, size, offset) \ + { \ + #name, NULL, size, offset, eEncodingUint, eFormatHex, \ + { dwarf_##name##_s390x, dwarf_##name##_s390x, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \ + NULL, NULL, \ + } + +#define DEFINE_FPR_NODWARF(name, size, offset) \ + { \ + #name, NULL, size, offset, eEncodingUint, eFormatHex, \ + { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \ + NULL, NULL, \ + } + +static RegisterInfo g_register_infos_s390x[] = +{ + // General purpose registers. + DEFINE_GPR(r0, 8, GPR_OFFSET(0), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r1, 8, GPR_OFFSET(1), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r2, 8, GPR_OFFSET(2), "arg1", LLDB_REGNUM_GENERIC_ARG1), + DEFINE_GPR(r3, 8, GPR_OFFSET(3), "arg2", LLDB_REGNUM_GENERIC_ARG2), + DEFINE_GPR(r4, 8, GPR_OFFSET(4), "arg3", LLDB_REGNUM_GENERIC_ARG3), + DEFINE_GPR(r5, 8, GPR_OFFSET(5), "arg4", LLDB_REGNUM_GENERIC_ARG4), + DEFINE_GPR(r6, 8, GPR_OFFSET(6), "arg5", LLDB_REGNUM_GENERIC_ARG5), + DEFINE_GPR(r7, 8, GPR_OFFSET(7), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, 8, GPR_OFFSET(8), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, 8, GPR_OFFSET(9), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, 8, GPR_OFFSET(10), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, 8, GPR_OFFSET(11), "fp", LLDB_REGNUM_GENERIC_FP), + DEFINE_GPR(r12, 8, GPR_OFFSET(12), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, 8, GPR_OFFSET(13), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, 8, GPR_OFFSET(14), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, 8, GPR_OFFSET(15), "sp", LLDB_REGNUM_GENERIC_SP), + DEFINE_GPR(acr0, 4, ACR_OFFSET(0), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr1, 4, ACR_OFFSET(1), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr2, 4, ACR_OFFSET(2), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr3, 4, ACR_OFFSET(3), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr4, 4, ACR_OFFSET(4), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr5, 4, ACR_OFFSET(5), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr6, 4, ACR_OFFSET(6), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr7, 4, ACR_OFFSET(7), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr8, 4, ACR_OFFSET(8), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr9, 4, ACR_OFFSET(9), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr10, 4, ACR_OFFSET(10), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr11, 4, ACR_OFFSET(11), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr12, 4, ACR_OFFSET(12), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr13, 4, ACR_OFFSET(13), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr14, 4, ACR_OFFSET(14), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr15, 4, ACR_OFFSET(15), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(pswm, 8, 0, "flags", LLDB_REGNUM_GENERIC_FLAGS), + DEFINE_GPR(pswa, 8, 8, "pc", LLDB_REGNUM_GENERIC_PC), + + // Floating point registers. + DEFINE_FPR(f0, 8, FPR_OFFSET(0)), + DEFINE_FPR(f1, 8, FPR_OFFSET(1)), + DEFINE_FPR(f2, 8, FPR_OFFSET(2)), + DEFINE_FPR(f3, 8, FPR_OFFSET(3)), + DEFINE_FPR(f4, 8, FPR_OFFSET(4)), + DEFINE_FPR(f5, 8, FPR_OFFSET(5)), + DEFINE_FPR(f6, 8, FPR_OFFSET(6)), + DEFINE_FPR(f7, 8, FPR_OFFSET(7)), + DEFINE_FPR(f8, 8, FPR_OFFSET(8)), + DEFINE_FPR(f9, 8, FPR_OFFSET(9)), + DEFINE_FPR(f10, 8, FPR_OFFSET(10)), + DEFINE_FPR(f11, 8, FPR_OFFSET(11)), + DEFINE_FPR(f12, 8, FPR_OFFSET(12)), + DEFINE_FPR(f13, 8, FPR_OFFSET(13)), + DEFINE_FPR(f14, 8, FPR_OFFSET(14)), + DEFINE_FPR(f15, 8, FPR_OFFSET(15)), + DEFINE_FPR_NODWARF(fpc, 4, 0), + + // Linux operating-specific info. + DEFINE_GPR_NODWARF(orig_r2, 8, 16 + 16 * 8 + 16 * 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR_NODWARF(last_break, 8, 0, nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR_NODWARF(system_call, 4, 0, nullptr, LLDB_INVALID_REGNUM), +}; + +static_assert((sizeof(g_register_infos_s390x) / sizeof(g_register_infos_s390x[0])) == k_num_registers_s390x, + "g_register_infos_s390x has wrong number of register infos"); + +#undef GPR_OFFSET +#undef ACR_OFFSET +#undef FPR_OFFSET +#undef DEFINE_GPR +#undef DEFINE_GPR_NODWARF +#undef DEFINE_FPR +#undef DEFINE_FPR_NODWARF + +#endif // DECLARE_REGISTER_INFOS_S390X_STRUCT diff --git a/source/Plugins/Process/Utility/StopInfoMachException.cpp b/source/Plugins/Process/Utility/StopInfoMachException.cpp index 3bf766e875c90..7c0487b1d43b3 100644 --- a/source/Plugins/Process/Utility/StopInfoMachException.cpp +++ b/source/Plugins/Process/Utility/StopInfoMachException.cpp @@ -507,6 +507,8 @@ StopInfoMachException::CreateStopReasonWithMachException // report the breakpoint regardless of the thread. if (bp_site_sp->ValidForThisThread (&thread) || thread.GetProcess()->GetOperatingSystem () != NULL) return StopInfo::CreateStopReasonWithBreakpointSiteID (thread, bp_site_sp->GetID()); + else if (is_trace_if_actual_breakpoint_missing) + return StopInfo::CreateStopReasonToTrace (thread); else return StopInfoSP(); } diff --git a/source/Plugins/Process/Utility/lldb-s390x-register-enums.h b/source/Plugins/Process/Utility/lldb-s390x-register-enums.h new file mode 100644 index 0000000000000..174daa25e21bc --- /dev/null +++ b/source/Plugins/Process/Utility/lldb-s390x-register-enums.h @@ -0,0 +1,94 @@ +//===-- lldb-s390x-register-enums.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_s390x_register_enums_h +#define lldb_s390x_register_enums_h + +namespace lldb_private +{ +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +//--------------------------------------------------------------------------- +// Internal codes for all s390x registers. +//--------------------------------------------------------------------------- +enum +{ + k_first_gpr_s390x, + lldb_r0_s390x = k_first_gpr_s390x, + lldb_r1_s390x, + lldb_r2_s390x, + lldb_r3_s390x, + lldb_r4_s390x, + lldb_r5_s390x, + lldb_r6_s390x, + lldb_r7_s390x, + lldb_r8_s390x, + lldb_r9_s390x, + lldb_r10_s390x, + lldb_r11_s390x, + lldb_r12_s390x, + lldb_r13_s390x, + lldb_r14_s390x, + lldb_r15_s390x, + lldb_acr0_s390x, + lldb_acr1_s390x, + lldb_acr2_s390x, + lldb_acr3_s390x, + lldb_acr4_s390x, + lldb_acr5_s390x, + lldb_acr6_s390x, + lldb_acr7_s390x, + lldb_acr8_s390x, + lldb_acr9_s390x, + lldb_acr10_s390x, + lldb_acr11_s390x, + lldb_acr12_s390x, + lldb_acr13_s390x, + lldb_acr14_s390x, + lldb_acr15_s390x, + lldb_pswm_s390x, + lldb_pswa_s390x, + k_last_gpr_s390x = lldb_pswa_s390x, + + k_first_fpr_s390x, + lldb_f0_s390x = k_first_fpr_s390x, + lldb_f1_s390x, + lldb_f2_s390x, + lldb_f3_s390x, + lldb_f4_s390x, + lldb_f5_s390x, + lldb_f6_s390x, + lldb_f7_s390x, + lldb_f8_s390x, + lldb_f9_s390x, + lldb_f10_s390x, + lldb_f11_s390x, + lldb_f12_s390x, + lldb_f13_s390x, + lldb_f14_s390x, + lldb_f15_s390x, + lldb_fpc_s390x, + k_last_fpr_s390x = lldb_fpc_s390x, + + // These are only available on Linux. + k_first_linux_s390x, + lldb_orig_r2_s390x = k_first_linux_s390x, + lldb_last_break_s390x, + lldb_system_call_s390x, + k_last_linux_s390x = lldb_system_call_s390x, + + k_num_registers_s390x, + k_num_gpr_registers_s390x = k_last_gpr_s390x - k_first_gpr_s390x + 1, + k_num_fpr_registers_s390x = k_last_fpr_s390x - k_first_fpr_s390x + 1, + k_num_linux_registers_s390x = k_last_linux_s390x - k_first_linux_s390x + 1, + k_num_user_registers_s390x = k_num_gpr_registers_s390x + k_num_fpr_registers_s390x, +}; +} + +#endif // #ifndef lldb_s390x_register_enums_h diff --git a/source/Plugins/Process/Windows/Common/NtStructures.h b/source/Plugins/Process/Windows/Common/NtStructures.h new file mode 100644 index 0000000000000..6c688d9068d5d --- /dev/null +++ b/source/Plugins/Process/Windows/Common/NtStructures.h @@ -0,0 +1,32 @@ +//===-- NtStructures.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_Plugins_Process_Windows_Common_NtStructures_h_ +#define liblldb_Plugins_Process_Windows_Common_NtStructures_h_ + +#include "lldb/Host/windows/windows.h" + +// This describes the layout of a TEB (Thread Environment Block) for a 64-bit +// process. It's adapted from the 32-bit TEB in winternl.h. Currently, we care +// only about the position of the TlsSlots. +struct TEB64 +{ + ULONG64 Reserved1[12]; + ULONG64 ProcessEnvironmentBlock; + ULONG64 Reserved2[399]; + BYTE Reserved3[1952]; + ULONG64 TlsSlots[64]; + BYTE Reserved4[8]; + ULONG64 Reserved5[26]; + ULONG64 ReservedForOle; // Windows 2000 only + ULONG64 Reserved6[4]; + ULONG64 TlsExpansionSlots; +}; + +#endif diff --git a/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 0e6900d8fb7f5..2c3f9fbecf92a 100644 --- a/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -29,8 +29,8 @@ namespace lldb_private //------------------------------------------------------------------------------ // Constructors and destructors. -ProcessWindows::ProcessWindows(lldb::TargetSP target_sp, Listener &listener) - : lldb_private::Process(target_sp, listener) +ProcessWindows::ProcessWindows(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) + : lldb_private::Process(target_sp, listener_sp) { } diff --git a/source/Plugins/Process/Windows/Common/ProcessWindows.h b/source/Plugins/Process/Windows/Common/ProcessWindows.h index 2a437c0ca9092..0ee42e2ae1f1d 100644 --- a/source/Plugins/Process/Windows/Common/ProcessWindows.h +++ b/source/Plugins/Process/Windows/Common/ProcessWindows.h @@ -25,7 +25,7 @@ public: // Constructors and destructors //------------------------------------------------------------------ ProcessWindows(lldb::TargetSP target_sp, - lldb_private::Listener &listener); + lldb::ListenerSP listener_sp); ~ProcessWindows(); diff --git a/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp index 103cff4a2a563..3a9c31a0b776f 100644 --- a/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp +++ b/source/Plugins/Process/Windows/Common/x64/RegisterContextWindows_x64.cpp @@ -136,6 +136,8 @@ RegisterInfo g_register_infos[] = { nullptr}, }; +static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos); + // Array of lldb register numbers used to define the set of all General Purpose Registers uint32_t g_gpr_reg_indices[] = {eRegisterIndexRax, eRegisterIndexRbx, eRegisterIndexRcx, eRegisterIndexRdx, eRegisterIndexRdi, eRegisterIndexRsi, eRegisterIndexR8, eRegisterIndexR9, @@ -169,7 +171,9 @@ RegisterContextWindows_x64::GetRegisterCount() const RegisterInfo * RegisterContextWindows_x64::GetRegisterInfoAtIndex(size_t reg) { - return &g_register_infos[reg]; + if (reg < k_num_register_infos) + return &g_register_infos[reg]; + return NULL; } size_t diff --git a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp index e57e1effec9c9..11733eee7cb47 100644 --- a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp +++ b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.cpp @@ -64,6 +64,7 @@ RegisterInfo g_register_infos[] = { DEFINE_GPR(eip, "pc"), { ehframe_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, lldb_eip_i386 }, nullptr, nullptr}, { DEFINE_GPR_BIN(eflags, "flags"), { ehframe_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, lldb_eflags_i386}, nullptr, nullptr}, }; +static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos); // Array of lldb register numbers used to define the set of all General Purpose Registers uint32_t g_gpr_reg_indices[] = @@ -106,7 +107,9 @@ RegisterContextWindows_x86::GetRegisterCount() const RegisterInfo * RegisterContextWindows_x86::GetRegisterInfoAtIndex(size_t reg) { - return &g_register_infos[reg]; + if (reg < k_num_register_infos) + return &g_register_infos[reg]; + return NULL; } size_t @@ -131,48 +134,42 @@ RegisterContextWindows_x86::ReadRegister(const RegisterInfo *reg_info, RegisterV switch (reg) { case lldb_eax_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EAX", m_context.Eax); - reg_value.SetUInt32(m_context.Eax); - break; + return ReadRegisterHelper(CONTEXT_INTEGER, "EAX", m_context.Eax, reg_value); case lldb_ebx_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBX", m_context.Ebx); - reg_value.SetUInt32(m_context.Ebx); - break; + return ReadRegisterHelper(CONTEXT_INTEGER, "EBX", m_context.Ebx, reg_value); case lldb_ecx_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ECX", m_context.Ecx); - reg_value.SetUInt32(m_context.Ecx); - break; + return ReadRegisterHelper(CONTEXT_INTEGER, "ECX", m_context.Ecx, reg_value); case lldb_edx_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDX", m_context.Edx); - reg_value.SetUInt32(m_context.Edx); - break; + return ReadRegisterHelper(CONTEXT_INTEGER, "EDX", m_context.Edx, reg_value); case lldb_edi_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDI", m_context.Edi); - reg_value.SetUInt32(m_context.Edi); - break; + return ReadRegisterHelper(CONTEXT_INTEGER, "EDI", m_context.Edi, reg_value); case lldb_esi_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESI", m_context.Esi); - reg_value.SetUInt32(m_context.Esi); - break; + return ReadRegisterHelper(CONTEXT_INTEGER, "ESI", m_context.Esi, reg_value); case lldb_ebp_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBP", m_context.Ebp); - reg_value.SetUInt32(m_context.Ebp); - break; + return ReadRegisterHelper(CONTEXT_CONTROL, "EBP", m_context.Ebp, reg_value); case lldb_esp_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESP", m_context.Esp); - reg_value.SetUInt32(m_context.Esp); - break; + return ReadRegisterHelper(CONTEXT_CONTROL, "ESP", m_context.Esp, reg_value); case lldb_eip_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EIP", m_context.Eip); - reg_value.SetUInt32(m_context.Eip); - break; + return ReadRegisterHelper(CONTEXT_CONTROL, "EIP", m_context.Eip, reg_value); case lldb_eflags_i386: - WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EFLAGS", m_context.EFlags); - reg_value.SetUInt32(m_context.EFlags); - break; + return ReadRegisterHelper(CONTEXT_CONTROL, "EFLAGS", m_context.EFlags, reg_value); default: WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Requested unknown register %u", reg); break; } + return false; +} + +bool +RegisterContextWindows_x86::ReadRegisterHelper(DWORD flags_required, const char *reg_name, DWORD value, + RegisterValue ®_value) const +{ + if ((m_context.ContextFlags & flags_required) != flags_required) + { + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Thread context doesn't have %s", reg_name); + return false; + } + WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from %s", value, reg_name); + reg_value.SetUInt32(value); return true; } diff --git a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h index 7d854ef64a5ca..6c29d54dcae2f 100644 --- a/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h +++ b/source/Plugins/Process/Windows/Common/x86/RegisterContextWindows_x86.h @@ -41,6 +41,9 @@ class RegisterContextWindows_x86 : public RegisterContextWindows bool ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) override; +private: + bool + ReadRegisterHelper(DWORD flags_required, const char *reg_name, DWORD value, RegisterValue ®_value) const; }; } diff --git a/source/Plugins/Process/Windows/Live/DebuggerThread.cpp b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp index d058a412c8968..2823474cbd5ee 100644 --- a/source/Plugins/Process/Windows/Live/DebuggerThread.cpp +++ b/source/Plugins/Process/Windows/Live/DebuggerThread.cpp @@ -27,6 +27,7 @@ #include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/raw_ostream.h" using namespace lldb; @@ -378,7 +379,7 @@ DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thr { WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_PROCESS, "Breakpoint exception is cue to detach from process 0x%x", - m_pid_to_detach); + m_pid_to_detach.load()); ::DebugActiveProcessStop(m_pid_to_detach); m_detached = true; } @@ -484,13 +485,15 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread return DBG_CONTINUE; } - std::vector<char> buffer(1); - DWORD required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS); + std::vector<wchar_t> buffer(1); + DWORD required_size = GetFinalPathNameByHandleW(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS); if (required_size > 0) { buffer.resize(required_size + 1); - required_size = GetFinalPathNameByHandle(info.hFile, &buffer[0], required_size + 1, VOLUME_NAME_DOS); - llvm::StringRef path_str(&buffer[0]); + required_size = GetFinalPathNameByHandleW(info.hFile, &buffer[0], required_size, VOLUME_NAME_DOS); + std::string path_str_utf8; + llvm::convertWideToUTF8(buffer.data(), path_str_utf8); + llvm::StringRef path_str = path_str_utf8; const char *path = path_str.data(); if (path_str.startswith("\\\\?\\")) path += 4; diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp index 855289d67bc72..300e0caa43781 100644 --- a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp @@ -47,6 +47,7 @@ #include "ProcessWindowsLive.h" #include "TargetThreadWindowsLive.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" @@ -61,17 +62,19 @@ namespace std::string GetProcessExecutableName(HANDLE process_handle) { - std::vector<char> file_name; + std::vector<wchar_t> file_name; DWORD file_name_size = MAX_PATH; // first guess, not an absolute limit DWORD copied = 0; do { file_name_size *= 2; file_name.resize(file_name_size); - copied = ::GetModuleFileNameEx(process_handle, NULL, file_name.data(), file_name_size); + copied = ::GetModuleFileNameExW(process_handle, NULL, file_name.data(), file_name_size); } while (copied >= file_name_size); file_name.resize(copied); - return std::string(file_name.begin(), file_name.end()); + std::string result; + llvm::convertWideToUTF8(file_name.data(), result); + return result; } std::string @@ -121,9 +124,9 @@ class ProcessWindowsData // Static functions. ProcessSP -ProcessWindowsLive::CreateInstance(lldb::TargetSP target_sp, Listener &listener, const FileSpec *) +ProcessWindowsLive::CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec *) { - return ProcessSP(new ProcessWindowsLive(target_sp, listener)); + return ProcessSP(new ProcessWindowsLive(target_sp, listener_sp)); } void @@ -142,8 +145,8 @@ ProcessWindowsLive::Initialize() //------------------------------------------------------------------------------ // Constructors and destructors. -ProcessWindowsLive::ProcessWindowsLive(lldb::TargetSP target_sp, Listener &listener) - : lldb_private::ProcessWindows(target_sp, listener) +ProcessWindowsLive::ProcessWindowsLive(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) + : lldb_private::ProcessWindows(target_sp, listener_sp) { } @@ -189,7 +192,7 @@ ProcessWindowsLive::DisableBreakpointSite(BreakpointSite *bp_site) { WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite called with bp_site 0x%p " "(id=%d, addr=0x%x)", - bp_site->GetID(), bp_site->GetLoadAddress()); + bp_site, bp_site->GetID(), bp_site->GetLoadAddress()); Error error = DisableSoftwareBreakpoint(bp_site); @@ -554,11 +557,25 @@ ProcessWindowsLive::RefreshStateAfterStop() { case EXCEPTION_SINGLE_STEP: { - stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); - stop_thread->SetStopInfo(stop_info); - WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, "RefreshStateAfterStop single stepping thread %u", - stop_thread->GetID()); - stop_thread->SetStopInfo(stop_info); + RegisterContextSP register_context = stop_thread->GetRegisterContext(); + const uint64_t pc = register_context->GetPC(); + BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc)); + if (site && site->ValidForThisThread(stop_thread.get())) + { + WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, + "Single-stepped onto a breakpoint in process %I64u at " + "address 0x%I64x with breakpoint site %d", + m_session_data->m_debugger->GetProcess().GetProcessId(), pc, site->GetID()); + stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*stop_thread, site->GetID()); + stop_thread->SetStopInfo(stop_info); + } + else + { + WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, + "RefreshStateAfterStop single stepping thread %u", stop_thread->GetID()); + stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); + stop_thread->SetStopInfo(stop_info); + } return; } @@ -731,6 +748,7 @@ ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo & { Error error; llvm::sys::ScopedLock lock(m_mutex); + info.Clear(); if (!m_session_data) { @@ -738,7 +756,6 @@ ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo & WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); return error; } - HostProcess process = m_session_data->m_debugger->GetProcess(); lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); if (handle == nullptr || handle == LLDB_INVALID_PROCESS) @@ -755,22 +772,67 @@ ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo & SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info)); if (result == 0) { - error.SetError(::GetLastError(), eErrorTypeWin32); - WINERR_IFALL(WINDOWS_LOG_MEMORY, - "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", - error.GetError(), vm_addr); - return error; + if (::GetLastError() == ERROR_INVALID_PARAMETER) + { + // ERROR_INVALID_PARAMETER is returned if VirtualQueryEx is called with an address + // past the highest accessible address. We should return a range from the vm_addr + // to LLDB_INVALID_ADDRESS + info.GetRange().SetRangeBase(vm_addr); + info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + info.SetReadable(MemoryRegionInfo::eNo); + info.SetExecutable(MemoryRegionInfo::eNo); + info.SetWritable(MemoryRegionInfo::eNo); + info.SetMapped(MemoryRegionInfo::eNo); + return error; + } + else + { + error.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_MEMORY, + "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", + error.GetError(), vm_addr); + return error; + } + } + + // Protect bits are only valid for MEM_COMMIT regions. + if (mem_info.State == MEM_COMMIT) { + const bool readable = IsPageReadable(mem_info.Protect); + const bool executable = IsPageExecutable(mem_info.Protect); + const bool writable = IsPageWritable(mem_info.Protect); + info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + } + else + { + info.SetReadable(MemoryRegionInfo::eNo); + info.SetExecutable(MemoryRegionInfo::eNo); + info.SetWritable(MemoryRegionInfo::eNo); + } + + // AllocationBase is defined for MEM_COMMIT and MEM_RESERVE but not MEM_FREE. + if (mem_info.State != MEM_FREE) { + info.GetRange().SetRangeBase(reinterpret_cast<addr_t>(mem_info.AllocationBase)); + info.GetRange().SetRangeEnd(reinterpret_cast<addr_t>(mem_info.BaseAddress) + mem_info.RegionSize); + info.SetMapped(MemoryRegionInfo::eYes); + } + else + { + // In the unmapped case we need to return the distance to the next block of memory. + // VirtualQueryEx nearly does that except that it gives the distance from the start + // of the page containing vm_addr. + SYSTEM_INFO data; + GetSystemInfo(&data); + DWORD page_offset = vm_addr % data.dwPageSize; + info.GetRange().SetRangeBase(vm_addr); + info.GetRange().SetByteSize(mem_info.RegionSize - page_offset); + info.SetMapped(MemoryRegionInfo::eNo); } - const bool readable = IsPageReadable(mem_info.Protect); - const bool executable = IsPageExecutable(mem_info.Protect); - const bool writable = IsPageWritable(mem_info.Protect); - info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); - info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); - info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); error.SetError(::GetLastError(), eErrorTypeWin32); WINLOGV_IFALL(WINDOWS_LOG_MEMORY, "Memory region info for address 0x%I64u: readable=%s, executable=%s, writable=%s", - BOOL_STR(readable), BOOL_STR(executable), BOOL_STR(writable)); + BOOL_STR(info.GetReadable()), BOOL_STR(info.GetExecutable()), BOOL_STR(info.GetWritable())); return error; } @@ -803,7 +865,7 @@ ProcessWindowsLive::OnExitProcess(uint32_t exit_code) target->ModulesDidUnload(unloaded_modules, true); } - SetProcessExitStatus(nullptr, GetID(), true, 0, exit_code); + SetProcessExitStatus(GetID(), true, 0, exit_code); SetPrivateState(eStateExited); } diff --git a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h index 2429f873c8231..657877f529b28 100644 --- a/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h +++ b/source/Plugins/Process/Windows/Live/ProcessWindowsLive.h @@ -43,7 +43,7 @@ public: //------------------------------------------------------------------ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener_sp, const lldb_private::FileSpec *); static void @@ -62,7 +62,7 @@ public: // Constructors and destructors //------------------------------------------------------------------ ProcessWindowsLive(lldb::TargetSP target_sp, - lldb_private::Listener &listener); + lldb::ListenerSP listener_sp); ~ProcessWindowsLive(); diff --git a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp index fbc96f085ed4c..05839667688f6 100644 --- a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp +++ b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp @@ -35,137 +35,131 @@ #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" +#include "Plugins/Process/Windows/Common/NtStructures.h" +#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" + #include "ExceptionRecord.h" #include "ThreadWinMiniDump.h" using namespace lldb_private; -namespace +// Implementation class for ProcessWinMiniDump encapsulates the Windows-specific +// code, keeping non-portable types out of the header files. +// TODO(amccarth): Determine if we need a mutex for access. Given that this is +// postmortem debugging, I don't think so. +class ProcessWinMiniDump::Impl { +public: + Impl(const FileSpec &core_file, ProcessWinMiniDump *self); + ~Impl(); -// Getting a string out of a mini dump is a chore. You're usually given a -// relative virtual address (RVA), which points to a counted string that's in -// Windows Unicode (UTF-16). This wrapper handles all the redirection and -// returns a UTF-8 copy of the string. -std::string -GetMiniDumpString(const void *base_addr, const RVA rva) -{ - std::string result; - if (!base_addr) + Error + DoLoadCore(); + + bool + UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list); + + void + RefreshStateAfterStop(); + + size_t + DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error); + + Error + GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &info); + +private: + // Describes a range of memory captured in the mini dump. + struct Range { - return result; - } - auto md_string = reinterpret_cast<const MINIDUMP_STRING *>(static_cast<const char *>(base_addr) + rva); - auto source_start = reinterpret_cast<const UTF16 *>(md_string->Buffer); - const auto source_length = ::wcslen(md_string->Buffer); - const auto source_end = source_start + source_length; - result.resize(4*source_length); // worst case length - auto result_start = reinterpret_cast<UTF8 *>(&result[0]); - const auto result_end = result_start + result.size(); - ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end, strictConversion); - const auto result_size = std::distance(reinterpret_cast<UTF8 *>(&result[0]), result_start); - result.resize(result_size); // shrink to actual length - return result; -} + lldb::addr_t start; // virtual address of the beginning of the range + size_t size; // size of the range in bytes + const uint8_t *ptr; // absolute pointer to the first byte of the range + }; -} // anonymous namespace + // If the mini dump has a memory range that contains the desired address, it + // returns true with the details of the range in *range_out. Otherwise, it + // returns false. + bool + FindMemoryRange(lldb::addr_t addr, Range *range_out) const; -// Encapsulates the private data for ProcessWinMiniDump. -// TODO(amccarth): Determine if we need a mutex for access. -class ProcessWinMiniDump::Data -{ -public: - Data(); - ~Data(); + lldb_private::Error + MapMiniDumpIntoMemory(); + lldb_private::ArchSpec + DetermineArchitecture(); + + void + ReadExceptionRecord(); + + void + ReadMiscInfo(); + + void + ReadModuleList(); + + // A thin wrapper around WinAPI's MiniDumpReadDumpStream to avoid redundant + // checks. If there's a failure (e.g., if the requested stream doesn't exist), + // the function returns nullptr and sets *size_out to 0. + void * + FindDumpStream(unsigned stream_number, size_t *size_out) const; + + // Getting a string out of a mini dump is a chore. You're usually given a + // relative virtual address (RVA), which points to a counted string that's in + // Windows Unicode (UTF-16). This wrapper handles all the redirection and + // returns a UTF-8 copy of the string. + std::string + GetMiniDumpString(RVA rva) const; + + ProcessWinMiniDump *m_self; // non-owning back pointer FileSpec m_core_file; HANDLE m_dump_file; // handle to the open minidump file HANDLE m_mapping; // handle to the file mapping for the minidump file void * m_base_addr; // base memory address of the minidump std::shared_ptr<ExceptionRecord> m_exception_sp; + bool m_is_wow64; // minidump is of a 32-bit process captured with a 64-bit debugger }; -ConstString -ProcessWinMiniDump::GetPluginNameStatic() +ProcessWinMiniDump::Impl::Impl(const FileSpec &core_file, ProcessWinMiniDump *self) + : m_self(self), + m_core_file(core_file), + m_dump_file(INVALID_HANDLE_VALUE), + m_mapping(NULL), + m_base_addr(nullptr), + m_exception_sp(), + m_is_wow64(false) { - static ConstString g_name("win-minidump"); - return g_name; } -const char * -ProcessWinMiniDump::GetPluginDescriptionStatic() +ProcessWinMiniDump::Impl::~Impl() { - return "Windows minidump plug-in."; -} - -void -ProcessWinMiniDump::Terminate() -{ - PluginManager::UnregisterPlugin(ProcessWinMiniDump::CreateInstance); -} - - -lldb::ProcessSP -ProcessWinMiniDump::CreateInstance(lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file) -{ - lldb::ProcessSP process_sp; - if (crash_file) + if (m_base_addr) { - process_sp.reset(new ProcessWinMiniDump(target_sp, listener, *crash_file)); + ::UnmapViewOfFile(m_base_addr); + m_base_addr = nullptr; + } + if (m_mapping) + { + ::CloseHandle(m_mapping); + m_mapping = NULL; + } + if (m_dump_file != INVALID_HANDLE_VALUE) + { + ::CloseHandle(m_dump_file); + m_dump_file = INVALID_HANDLE_VALUE; } - return process_sp; -} - -bool -ProcessWinMiniDump::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) -{ - // TODO(amccarth): Eventually, this needs some actual logic. - return true; -} - -ProcessWinMiniDump::ProcessWinMiniDump(lldb::TargetSP target_sp, Listener &listener, - const FileSpec &core_file) : - ProcessWindows(target_sp, listener), - m_data_up(new Data) -{ - m_data_up->m_core_file = core_file; -} - -ProcessWinMiniDump::~ProcessWinMiniDump() -{ - Clear(); - // We need to call finalize on the process before destroying ourselves - // to make sure all of the broadcaster cleanup goes as planned. If we - // destruct this class, then Process::~Process() might have problems - // trying to fully destroy the broadcaster. - Finalize(); -} - -ConstString -ProcessWinMiniDump::GetPluginName() -{ - return GetPluginNameStatic(); -} - -uint32_t -ProcessWinMiniDump::GetPluginVersion() -{ - return 1; } - Error -ProcessWinMiniDump::DoLoadCore() +ProcessWinMiniDump::Impl::DoLoadCore() { - Error error; - - error = MapMiniDumpIntoMemory(m_data_up->m_core_file.GetCString()); + Error error = MapMiniDumpIntoMemory(); if (error.Fail()) { return error; } - GetTarget().SetArchitecture(DetermineArchitecture()); + m_self->GetTarget().SetArchitecture(DetermineArchitecture()); ReadMiscInfo(); // notably for process ID ReadModuleList(); ReadExceptionRecord(); @@ -174,16 +168,8 @@ ProcessWinMiniDump::DoLoadCore() } -DynamicLoader * -ProcessWinMiniDump::GetDynamicLoader() -{ - if (m_dyld_ap.get() == NULL) - m_dyld_ap.reset (DynamicLoader::FindPlugin(this, DynamicLoaderWindowsDYLD::GetPluginNameStatic().GetCString())); - return m_dyld_ap.get(); -} - bool -ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +ProcessWinMiniDump::Impl::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { size_t size = 0; auto thread_list_ptr = static_cast<const MINIDUMP_THREAD_LIST *>(FindDumpStream(ThreadListStream, &size)); @@ -192,10 +178,50 @@ ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &ne const ULONG32 thread_count = thread_list_ptr->NumberOfThreads; for (ULONG32 i = 0; i < thread_count; ++i) { const auto &mini_dump_thread = thread_list_ptr->Threads[i]; - auto thread_sp = std::make_shared<ThreadWinMiniDump>(*this, mini_dump_thread.ThreadId); + auto thread_sp = std::make_shared<ThreadWinMiniDump>(*m_self, mini_dump_thread.ThreadId); if (mini_dump_thread.ThreadContext.DataSize >= sizeof(CONTEXT)) { - const CONTEXT *context = reinterpret_cast<const CONTEXT *>(static_cast<const char *>(m_data_up->m_base_addr) + mini_dump_thread.ThreadContext.Rva); + const CONTEXT *context = reinterpret_cast<const CONTEXT *>(static_cast<const char *>(m_base_addr) + + mini_dump_thread.ThreadContext.Rva); + + if (m_is_wow64) + { + // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. + // If the minidump was captured with a 64-bit debugger, then the CONTEXT + // we just grabbed from the mini_dump_thread is the one for the 64-bit + // "native" process rather than the 32-bit "guest" process we care about. + // In this case, we can get the 32-bit CONTEXT from the TEB (Thread + // Environment Block) of the 64-bit process. + Error error; + TEB64 wow64teb = {0}; + m_self->ReadMemory(mini_dump_thread.Teb, &wow64teb, sizeof(wow64teb), error); + if (error.Success()) + { + // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure + // that includes the 32-bit CONTEXT (after a ULONG). + // See: https://msdn.microsoft.com/en-us/library/ms681670.aspx + const size_t addr = wow64teb.TlsSlots[1]; + Range range = {0}; + if (FindMemoryRange(addr, &range)) + { + lldbassert(range.start <= addr); + const size_t offset = addr - range.start + sizeof(ULONG); + if (offset < range.size) + { + const size_t overlap = range.size - offset; + if (overlap >= sizeof(CONTEXT)) + { + context = reinterpret_cast<const CONTEXT *>(range.ptr + offset); + } + } + } + } + + // NOTE: We don't currently use the TEB for anything else. If we need it in + // the future, the 32-bit TEB is located according to the address stored in the + // first slot of the 64-bit TEB (wow64teb.Reserved1[0]). + } + thread_sp->SetContext(context); } new_thread_list.AddThread(thread_sp); @@ -206,54 +232,24 @@ ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &ne } void -ProcessWinMiniDump::RefreshStateAfterStop() +ProcessWinMiniDump::Impl::RefreshStateAfterStop() { - if (!m_data_up) return; - if (!m_data_up->m_exception_sp) return; + if (!m_exception_sp) + return; - auto active_exception = m_data_up->m_exception_sp; + auto active_exception = m_exception_sp; std::string desc; llvm::raw_string_ostream desc_stream(desc); - desc_stream << "Exception " - << llvm::format_hex(active_exception->GetExceptionCode(), 8) - << " encountered at address " - << llvm::format_hex(active_exception->GetExceptionAddress(), 8); - m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); - auto stop_thread = m_thread_list.GetSelectedThread(); + desc_stream << "Exception " << llvm::format_hex(active_exception->GetExceptionCode(), 8) + << " encountered at address " << llvm::format_hex(active_exception->GetExceptionAddress(), 8); + m_self->m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID()); + auto stop_thread = m_self->m_thread_list.GetSelectedThread(); auto stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str()); stop_thread->SetStopInfo(stop_info); } -Error -ProcessWinMiniDump::DoDestroy() -{ - return Error(); -} - -bool -ProcessWinMiniDump::IsAlive() -{ - return true; -} - -bool -ProcessWinMiniDump::WarnBeforeDetach () const -{ - // Since this is post-mortem debugging, there's no need to warn the user - // that quitting the debugger will terminate the process. - return false; -} - size_t -ProcessWinMiniDump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) -{ - // Don't allow the caching that lldb_private::Process::ReadMemory does - // since we have it all cached our our dump file anyway. - return DoReadMemory(addr, buf, size, error); -} - -size_t -ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +ProcessWinMiniDump::Impl::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) { // I don't have a sense of how frequently this is called or how many memory // ranges a mini dump typically has, so I'm not sure if searching for the @@ -277,10 +273,11 @@ ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Erro } Error -ProcessWinMiniDump::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &info) +ProcessWinMiniDump::Impl::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &info) { Error error; size_t size; + info.Clear(); const auto list = reinterpret_cast<const MINIDUMP_MEMORY_INFO_LIST *>(FindDumpStream(MemoryInfoListStream, &size)); if (list == nullptr || size < sizeof(MINIDUMP_MEMORY_INFO_LIST)) { @@ -300,6 +297,8 @@ ProcessWinMiniDump::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::Me return error; } + const MINIDUMP_MEMORY_INFO *next_entry = nullptr; + for (int i = 0; i < list->NumberOfEntries; ++i) { const auto entry = reinterpret_cast<const MINIDUMP_MEMORY_INFO *>(reinterpret_cast<const char *>(list) + @@ -308,80 +307,46 @@ ProcessWinMiniDump::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::Me const auto tail = head + entry->RegionSize; if (head <= load_addr && load_addr < tail) { + info.GetRange().SetRangeBase((entry->State != MEM_FREE) ? head : load_addr); + info.GetRange().SetRangeEnd(tail); info.SetReadable(IsPageReadable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); info.SetWritable(IsPageWritable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); info.SetExecutable(IsPageExecutable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetMapped((entry->State != MEM_FREE) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); return error; } + else if (head > load_addr && (next_entry == nullptr || head < next_entry->BaseAddress) ) + { + // In case there is no region containing load_addr keep track of the nearest region + // after load_addr so we can return the distance to it. + next_entry = entry; + } } + + // No containing region found. Create an unmapped region that extends to the next region + // or LLDB_INVALID_ADDRESS + info.GetRange().SetRangeBase(load_addr); + info.GetRange().SetRangeEnd((next_entry != nullptr)?next_entry->BaseAddress:LLDB_INVALID_ADDRESS); + info.SetReadable(MemoryRegionInfo::eNo); + info.SetWritable(MemoryRegionInfo::eNo); + info.SetExecutable(MemoryRegionInfo::eNo); + info.SetMapped(MemoryRegionInfo::eNo); + // Note that the memory info list doesn't seem to contain ranges in kernel space, // so if you're walking a stack that has kernel frames, the stack may appear // truncated. - error.SetErrorString("address is not in a known range"); return error; } -void -ProcessWinMiniDump::Clear() -{ - m_thread_list.Clear(); -} - -void -ProcessWinMiniDump::Initialize() -{ - static std::once_flag g_once_flag; - - std::call_once(g_once_flag, []() - { - PluginManager::RegisterPlugin(GetPluginNameStatic(), - GetPluginDescriptionStatic(), - CreateInstance); - }); -} - -ArchSpec -ProcessWinMiniDump::GetArchitecture() -{ - // TODO - return ArchSpec(); -} - - -ProcessWinMiniDump::Data::Data() : - m_dump_file(INVALID_HANDLE_VALUE), - m_mapping(NULL), - m_base_addr(nullptr) -{ -} - -ProcessWinMiniDump::Data::~Data() -{ - if (m_base_addr) - { - ::UnmapViewOfFile(m_base_addr); - m_base_addr = nullptr; - } - if (m_mapping) - { - ::CloseHandle(m_mapping); - m_mapping = NULL; - } - if (m_dump_file != INVALID_HANDLE_VALUE) - { - ::CloseHandle(m_dump_file); - m_dump_file = INVALID_HANDLE_VALUE; - } -} - bool -ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const +ProcessWinMiniDump::Impl::FindMemoryRange(lldb::addr_t addr, Range *range_out) const { size_t stream_size = 0; auto mem_list_stream = static_cast<const MINIDUMP_MEMORY_LIST *>(FindDumpStream(MemoryListStream, &stream_size)); if (mem_list_stream) { - for (ULONG32 i = 0; i < mem_list_stream->NumberOfMemoryRanges; ++i) { + for (ULONG32 i = 0; i < mem_list_stream->NumberOfMemoryRanges; ++i) + { const MINIDUMP_MEMORY_DESCRIPTOR &mem_desc = mem_list_stream->MemoryRanges[i]; const MINIDUMP_LOCATION_DESCRIPTOR &loc_desc = mem_desc.Memory; const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; @@ -390,7 +355,7 @@ ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const { range_out->start = range_start; range_out->size = range_size; - range_out->ptr = reinterpret_cast<const uint8_t *>(m_data_up->m_base_addr) + loc_desc.Rva; + range_out->ptr = reinterpret_cast<const uint8_t *>(m_base_addr) + loc_desc.Rva; return true; } } @@ -411,7 +376,7 @@ ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const { range_out->start = range_start; range_out->size = range_size; - range_out->ptr = reinterpret_cast<const uint8_t *>(m_data_up->m_base_addr) + base_rva; + range_out->ptr = reinterpret_cast<const uint8_t *>(m_base_addr) + base_rva; return true; } base_rva += range_size; @@ -421,31 +386,34 @@ ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const return false; } - Error -ProcessWinMiniDump::MapMiniDumpIntoMemory(const char *file) +ProcessWinMiniDump::Impl::MapMiniDumpIntoMemory() { Error error; - - m_data_up->m_dump_file = ::CreateFile(file, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (m_data_up->m_dump_file == INVALID_HANDLE_VALUE) + const char *file = m_core_file.GetCString(); + std::wstring wfile; + if (!llvm::ConvertUTF8toWide(file, wfile)) + { + error.SetErrorString("Error converting path to UTF-16"); + return error; + } + m_dump_file = + ::CreateFileW(wfile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (m_dump_file == INVALID_HANDLE_VALUE) { error.SetError(::GetLastError(), lldb::eErrorTypeWin32); return error; } - m_data_up->m_mapping = ::CreateFileMapping(m_data_up->m_dump_file, NULL, - PAGE_READONLY, 0, 0, NULL); - if (m_data_up->m_mapping == NULL) + m_mapping = ::CreateFileMappingW(m_dump_file, NULL, PAGE_READONLY, 0, 0, NULL); + if (m_mapping == NULL) { error.SetError(::GetLastError(), lldb::eErrorTypeWin32); return error; } - m_data_up->m_base_addr = ::MapViewOfFile(m_data_up->m_mapping, FILE_MAP_READ, 0, 0, 0); - if (m_data_up->m_base_addr == NULL) + m_base_addr = ::MapViewOfFile(m_mapping, FILE_MAP_READ, 0, 0, 0); + if (m_base_addr == nullptr) { error.SetError(::GetLastError(), lldb::eErrorTypeWin32); return error; @@ -454,9 +422,8 @@ ProcessWinMiniDump::MapMiniDumpIntoMemory(const char *file) return error; } - ArchSpec -ProcessWinMiniDump::DetermineArchitecture() +ProcessWinMiniDump::Impl::DetermineArchitecture() { size_t size = 0; auto system_info_ptr = static_cast<const MINIDUMP_SYSTEM_INFO *>(FindDumpStream(SystemInfoStream, &size)); @@ -465,9 +432,17 @@ ProcessWinMiniDump::DetermineArchitecture() switch (system_info_ptr->ProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_INTEL: - return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_I386, LLDB_INVALID_CPUTYPE); + if (system_info_ptr->ProcessorLevel == 6) + { + return ArchSpec("i686-pc-windows"); + } + else + { + return ArchSpec("i386-pc-windows"); + } + break; case PROCESSOR_ARCHITECTURE_AMD64: - return ArchSpec(eArchTypeCOFF, IMAGE_FILE_MACHINE_AMD64, LLDB_INVALID_CPUTYPE); + return ArchSpec("x86_64-pc-windows"); default: break; } @@ -477,18 +452,24 @@ ProcessWinMiniDump::DetermineArchitecture() } void -ProcessWinMiniDump::ReadExceptionRecord() +ProcessWinMiniDump::Impl::ReadExceptionRecord() { size_t size = 0; auto exception_stream_ptr = static_cast<MINIDUMP_EXCEPTION_STREAM*>(FindDumpStream(ExceptionStream, &size)); if (exception_stream_ptr) { - m_data_up->m_exception_sp.reset(new ExceptionRecord(exception_stream_ptr->ExceptionRecord, exception_stream_ptr->ThreadId)); + m_exception_sp.reset( + new ExceptionRecord(exception_stream_ptr->ExceptionRecord, exception_stream_ptr->ThreadId)); + } + else + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Minidump has no exception record."); + // TODO: See if we can recover the exception from the TEB. } } void -ProcessWinMiniDump::ReadMiscInfo() +ProcessWinMiniDump::Impl::ReadMiscInfo() { size_t size = 0; const auto misc_info_ptr = static_cast<MINIDUMP_MISC_INFO*>(FindDumpStream(MiscInfoStream, &size)); @@ -498,12 +479,12 @@ ProcessWinMiniDump::ReadMiscInfo() if ((misc_info_ptr->Flags1 & MINIDUMP_MISC1_PROCESS_ID) != 0) { // This misc info record has the process ID. - SetID(misc_info_ptr->ProcessId); + m_self->SetID(misc_info_ptr->ProcessId); } } void -ProcessWinMiniDump::ReadModuleList() +ProcessWinMiniDump::Impl::ReadModuleList() { size_t size = 0; auto module_list_ptr = static_cast<MINIDUMP_MODULE_LIST*>(FindDumpStream(ModuleListStream, &size)); @@ -515,36 +496,215 @@ ProcessWinMiniDump::ReadModuleList() for (ULONG32 i = 0; i < module_list_ptr->NumberOfModules; ++i) { const auto &module = module_list_ptr->Modules[i]; - const auto file_name = GetMiniDumpString(m_data_up->m_base_addr, module.ModuleNameRva); - ModuleSpec module_spec = FileSpec(file_name, true); + const auto file_name = GetMiniDumpString(module.ModuleNameRva); + const auto file_spec = FileSpec(file_name, true); + if (FileSpec::Compare(file_spec, FileSpec("wow64.dll", false), false) == 0) + { + WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Minidump is for a WOW64 process."); + m_is_wow64 = true; + } + ModuleSpec module_spec = file_spec; - lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec); + lldb::ModuleSP module_sp = m_self->GetTarget().GetSharedModule(module_spec); if (!module_sp) { continue; } bool load_addr_changed = false; - module_sp->SetLoadAddress(GetTarget(), module.BaseOfImage, false, load_addr_changed); + module_sp->SetLoadAddress(m_self->GetTarget(), module.BaseOfImage, false, load_addr_changed); } } void * -ProcessWinMiniDump::FindDumpStream(unsigned stream_number, size_t *size_out) const +ProcessWinMiniDump::Impl::FindDumpStream(unsigned stream_number, size_t *size_out) const { void *stream = nullptr; *size_out = 0; - assert(m_data_up != nullptr); - assert(m_data_up->m_base_addr != 0); - MINIDUMP_DIRECTORY *dir = nullptr; - if (::MiniDumpReadDumpStream(m_data_up->m_base_addr, stream_number, &dir, nullptr, nullptr) && - dir != nullptr && dir->Location.DataSize > 0) + if (::MiniDumpReadDumpStream(m_base_addr, stream_number, &dir, nullptr, nullptr) && dir != nullptr && + dir->Location.DataSize > 0) { assert(dir->StreamType == stream_number); *size_out = dir->Location.DataSize; - stream = static_cast<void*>(static_cast<char*>(m_data_up->m_base_addr) + dir->Location.Rva); + stream = static_cast<void *>(static_cast<char *>(m_base_addr) + dir->Location.Rva); } return stream; } + +std::string +ProcessWinMiniDump::Impl::GetMiniDumpString(RVA rva) const +{ + std::string result; + if (!m_base_addr) + { + return result; + } + auto md_string = reinterpret_cast<const MINIDUMP_STRING *>(static_cast<const char *>(m_base_addr) + rva); + auto source_start = reinterpret_cast<const UTF16 *>(md_string->Buffer); + const auto source_length = ::wcslen(md_string->Buffer); + const auto source_end = source_start + source_length; + result.resize(UNI_MAX_UTF8_BYTES_PER_CODE_POINT * source_length); // worst case length + auto result_start = reinterpret_cast<UTF8 *>(&result[0]); + const auto result_end = result_start + result.size(); + ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end, strictConversion); + const auto result_size = std::distance(reinterpret_cast<UTF8 *>(&result[0]), result_start); + result.resize(result_size); // shrink to actual length + return result; +} + +ConstString +ProcessWinMiniDump::GetPluginNameStatic() +{ + static ConstString g_name("win-minidump"); + return g_name; +} + +const char * +ProcessWinMiniDump::GetPluginDescriptionStatic() +{ + return "Windows minidump plug-in."; +} + +void +ProcessWinMiniDump::Terminate() +{ + PluginManager::UnregisterPlugin(ProcessWinMiniDump::CreateInstance); +} + +lldb::ProcessSP +ProcessWinMiniDump::CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec *crash_file) +{ + lldb::ProcessSP process_sp; + if (crash_file) + { + process_sp.reset(new ProcessWinMiniDump(target_sp, listener_sp, *crash_file)); + } + return process_sp; +} + +bool +ProcessWinMiniDump::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) +{ + // TODO(amccarth): Eventually, this needs some actual logic. + return true; +} + +ProcessWinMiniDump::ProcessWinMiniDump(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec &core_file) + : ProcessWindows(target_sp, listener_sp), m_impl_up(new Impl(core_file, this)) +{ +} + +ProcessWinMiniDump::~ProcessWinMiniDump() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +ConstString +ProcessWinMiniDump::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWinMiniDump::GetPluginVersion() +{ + return 1; +} + +Error +ProcessWinMiniDump::DoLoadCore() +{ + return m_impl_up->DoLoadCore(); +} + +DynamicLoader * +ProcessWinMiniDump::GetDynamicLoader() +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset(DynamicLoader::FindPlugin(this, DynamicLoaderWindowsDYLD::GetPluginNameStatic().GetCString())); + return m_dyld_ap.get(); +} + +bool +ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + return m_impl_up->UpdateThreadList(old_thread_list, new_thread_list); +} + +void +ProcessWinMiniDump::RefreshStateAfterStop() +{ + if (!m_impl_up) + return; + return m_impl_up->RefreshStateAfterStop(); +} + +Error +ProcessWinMiniDump::DoDestroy() +{ + return Error(); +} + +bool +ProcessWinMiniDump::IsAlive() +{ + return true; +} + +bool +ProcessWinMiniDump::WarnBeforeDetach() const +{ + // Since this is post-mortem debugging, there's no need to warn the user + // that quitting the debugger will terminate the process. + return false; +} + +size_t +ProcessWinMiniDump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since we have it all cached our our dump file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t +ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + return m_impl_up->DoReadMemory(addr, buf, size, error); +} + +Error +ProcessWinMiniDump::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &info) +{ + return m_impl_up->GetMemoryRegionInfo(load_addr, info); +} + +void +ProcessWinMiniDump::Clear() +{ + m_thread_list.Clear(); +} + +void +ProcessWinMiniDump::Initialize() +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); + }); +} + +ArchSpec +ProcessWinMiniDump::GetArchitecture() +{ + // TODO + return ArchSpec(); +} diff --git a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h index 12864be371275..3e1ac4bffbe38 100644 --- a/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h +++ b/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h @@ -26,7 +26,7 @@ class ProcessWinMiniDump : public lldb_private::ProcessWindows public: static lldb::ProcessSP CreateInstance (lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener_sp, const lldb_private::FileSpec *crash_file_path); static void @@ -42,7 +42,7 @@ class ProcessWinMiniDump : public lldb_private::ProcessWindows GetPluginDescriptionStatic(); ProcessWinMiniDump(lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener_sp, const lldb_private::FileSpec &core_file); virtual @@ -96,45 +96,9 @@ class ProcessWinMiniDump : public lldb_private::ProcessWindows lldb_private::ThreadList &new_thread_list) override; private: - // Describes a range of memory captured in the mini dump. - struct Range { - lldb::addr_t start; // virtual address of the beginning of the range - size_t size; // size of the range in bytes - const uint8_t *ptr; // absolute pointer to the first byte of the range - }; - - // If the mini dump has a memory range that contains the desired address, it - // returns true with the details of the range in *range_out. Otherwise, it - // returns false. - bool - FindMemoryRange(lldb::addr_t addr, Range *range_out) const; - - lldb_private::Error - MapMiniDumpIntoMemory(const char *file); - - lldb_private::ArchSpec - DetermineArchitecture(); - - void - ReadExceptionRecord(); - - void - ReadMiscInfo(); - - void - ReadModuleList(); - - // A thin wrapper around WinAPI's MiniDumpReadDumpStream to avoid redundant - // checks. If there's a failure (e.g., if the requested stream doesn't exist), - // the function returns nullptr and sets *size_out to 0. - void * - FindDumpStream(unsigned stream_number, size_t *size_out) const; - - // Isolate the data to keep Windows-specific types out of this header. Can't - // use the typical pimpl idiom because the implementation of this class also - // needs access to public and protected members of the base class. - class Data; - std::unique_ptr<Data> m_data_up; + // Keep Windows-specific types out of this header. + class Impl; + std::unique_ptr<Impl> m_impl_up; }; #endif // liblldb_ProcessWinMiniDump_h_ diff --git a/source/Plugins/Process/elf-core/CMakeLists.txt b/source/Plugins/Process/elf-core/CMakeLists.txt index 1a4dd7e9d333d..b9f0b6cdfb7c0 100644 --- a/source/Plugins/Process/elf-core/CMakeLists.txt +++ b/source/Plugins/Process/elf-core/CMakeLists.txt @@ -7,5 +7,6 @@ add_lldb_library(lldbPluginProcessElfCore RegisterContextPOSIXCore_arm64.cpp RegisterContextPOSIXCore_mips64.cpp RegisterContextPOSIXCore_powerpc.cpp + RegisterContextPOSIXCore_s390x.cpp RegisterContextPOSIXCore_x86_64.cpp ) diff --git a/source/Plugins/Process/elf-core/Makefile b/source/Plugins/Process/elf-core/Makefile deleted file mode 100644 index 8c5b3b800f5ab..0000000000000 --- a/source/Plugins/Process/elf-core/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- source/Plugins/Process/elf-core/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 := lldbPluginProcessElfCore -BUILD_ARCHIVE = 1 - -include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/source/Plugins/Process/elf-core/ProcessElfCore.cpp index 5b5d98a86d5e2..a729d2beee77d 100644 --- a/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -14,15 +14,16 @@ #include <mutex> // Other libraries and framework includes -#include "lldb/Core/PluginManager.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Core/State.h" -#include "lldb/Core/DataBufferHeap.h" -#include "lldb/Core/Log.h" -#include "lldb/Target/Target.h" #include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" #include "llvm/Support/ELF.h" @@ -57,7 +58,7 @@ ProcessElfCore::Terminate() lldb::ProcessSP -ProcessElfCore::CreateInstance (lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file) +ProcessElfCore::CreateInstance (lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec *crash_file) { lldb::ProcessSP process_sp; if (crash_file) @@ -75,7 +76,7 @@ ProcessElfCore::CreateInstance (lldb::TargetSP target_sp, Listener &listener, co if (elf_header.Parse(data, &data_offset)) { if (elf_header.e_type == llvm::ELF::ET_CORE) - process_sp.reset(new ProcessElfCore (target_sp, listener, *crash_file)); + process_sp.reset(new ProcessElfCore (target_sp, listener_sp, *crash_file)); } } } @@ -104,9 +105,9 @@ ProcessElfCore::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name //---------------------------------------------------------------------- // ProcessElfCore constructor //---------------------------------------------------------------------- -ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp, Listener &listener, +ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec &core_file) : - Process (target_sp, listener), + Process (target_sp, listener_sp), m_core_module_sp (), m_core_file (core_file), m_dyld_plugin_name (), @@ -148,7 +149,7 @@ ProcessElfCore::GetPluginVersion() lldb::addr_t ProcessElfCore::AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader *header) { - lldb::addr_t addr = header->p_vaddr; + const lldb::addr_t addr = header->p_vaddr; FileRange file_range (header->p_offset, header->p_filesz); VMRangeToFileOffset::Entry range_entry(addr, header->p_memsz, file_range); @@ -166,6 +167,14 @@ ProcessElfCore::AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader *head m_core_aranges.Append(range_entry); } + // Keep a separate map of permissions that that isn't coalesced so all ranges + // are maintained. + const uint32_t permissions = ((header->p_flags & llvm::ELF::PF_R) ? lldb::ePermissionsReadable : 0) | + ((header->p_flags & llvm::ELF::PF_W) ? lldb::ePermissionsWritable : 0) | + ((header->p_flags & llvm::ELF::PF_X) ? lldb::ePermissionsExecutable : 0); + + m_core_range_infos.Append(VMRangeToPermissions::Entry(addr, header->p_memsz, permissions)); + return addr; } @@ -227,7 +236,10 @@ ProcessElfCore::DoLoadCore () } if (!ranges_are_sorted) + { m_core_aranges.Sort(); + m_core_range_infos.Sort(); + } // Even if the architecture is set in the target, we need to override // it to match the core file which is always single arch. @@ -315,6 +327,47 @@ ProcessElfCore::ReadMemory (lldb::addr_t addr, void *buf, size_t size, Error &er return DoReadMemory (addr, buf, size, error); } +Error +ProcessElfCore::GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) +{ + region_info.Clear(); + const VMRangeToPermissions::Entry *permission_entry = m_core_range_infos.FindEntryThatContainsOrFollows(load_addr); + if (permission_entry) + { + if (permission_entry->Contains(load_addr)) + { + region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase()); + region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd()); + const Flags permissions(permission_entry->data); + region_info.SetReadable(permissions.Test(lldb::ePermissionsReadable) ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetWritable(permissions.Test(lldb::ePermissionsWritable) ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetExecutable(permissions.Test(lldb::ePermissionsExecutable) ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eYes); + } + else if (load_addr < permission_entry->GetRangeBase()) + { + region_info.GetRange().SetRangeBase(load_addr); + region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase()); + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + } + return Error(); + } + + region_info.GetRange().SetRangeBase(load_addr); + region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + return Error(); +} + size_t ProcessElfCore::DoReadMemory (lldb::addr_t addr, void *buf, size_t size, Error &error) { @@ -517,7 +570,7 @@ ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader * size_t note_start, note_size; note_start = offset; - note_size = llvm::RoundUpToAlignment(note.n_descsz, 4); + note_size = llvm::alignTo(note.n_descsz, 4); // Store the NOTE information in the current thread DataExtractor note_data (segment_data, note_start, note_size); @@ -559,11 +612,10 @@ ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader * have_prstatus = true; prstatus.Parse(note_data, arch); thread_data->signo = prstatus.pr_cursig; + thread_data->tid = prstatus.pr_pid; header_size = ELFLinuxPrStatus::GetSize(arch); len = note_data.GetByteSize() - header_size; thread_data->gpregset = DataExtractor(note_data, header_size, len); - // FIXME: Obtain actual tid on Linux - thread_data->tid = m_thread_data.size(); break; case NT_FPREGSET: thread_data->fpregset = note_data; @@ -572,6 +624,7 @@ ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader * have_prpsinfo = true; prpsinfo.Parse(note_data, arch); thread_data->name = prpsinfo.pr_fname; + SetID(prpsinfo.pr_pid); break; case NT_AUXV: m_auxv = DataExtractor(note_data); @@ -637,3 +690,18 @@ ProcessElfCore::GetAuxvData() lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(start, len)); return buffer; } + +bool +ProcessElfCore::GetProcessInfo(ProcessInstanceInfo &info) +{ + info.Clear(); + info.SetProcessID(GetID()); + info.SetArchitecture(GetArchitecture()); + lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); + if (module_sp) + { + const bool add_exe_file_as_first_arg = false; + info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), add_exe_file_as_first_arg); + } + return true; +} diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.h b/source/Plugins/Process/elf-core/ProcessElfCore.h index 12ce04c5ce38b..4bcbb363d3f85 100644 --- a/source/Plugins/Process/elf-core/ProcessElfCore.h +++ b/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -40,7 +40,7 @@ public: //------------------------------------------------------------------ static lldb::ProcessSP CreateInstance (lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener_sp, const lldb_private::FileSpec *crash_file_path); static void @@ -59,7 +59,7 @@ public: // Constructors and Destructors //------------------------------------------------------------------ ProcessElfCore(lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener_sp, const lldb_private::FileSpec &core_file); ~ProcessElfCore() override; @@ -102,6 +102,9 @@ public: size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + lldb_private::Error + GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo ®ion_info) override; + lldb::addr_t GetImageInfoAddress() override; lldb_private::ArchSpec @@ -111,6 +114,9 @@ public: const lldb::DataBufferSP GetAuxvData() override; + bool + GetProcessInfo(lldb_private::ProcessInstanceInfo &info) override; + protected: void Clear ( ); @@ -132,6 +138,7 @@ private: //------------------------------------------------------------------ typedef lldb_private::Range<lldb::addr_t, lldb::addr_t> FileRange; typedef lldb_private::RangeDataArray<lldb::addr_t, lldb::addr_t, FileRange, 1> VMRangeToFileOffset; + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, uint32_t> VMRangeToPermissions; lldb::ModuleSP m_core_module_sp; lldb_private::FileSpec m_core_file; @@ -152,6 +159,9 @@ private: // Address ranges found in the core VMRangeToFileOffset m_core_aranges; + // Permissions for all ranges + VMRangeToPermissions m_core_range_infos; + // NT_FILE entries found from the NOTE segment std::vector<NT_FILE_Entry> m_nt_file_entries; diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp new file mode 100644 index 0000000000000..d2f0a8dd36717 --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp @@ -0,0 +1,115 @@ +//===-- RegisterContextCorePOSIX_s390x.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" +#include "RegisterContextPOSIXCore_s390x.h" + +using namespace lldb_private; + +RegisterContextCorePOSIX_s390x::RegisterContextCorePOSIX_s390x(Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset, + const DataExtractor &fpregset) + : RegisterContextPOSIX_s390x(thread, 0, register_info) +{ + m_gpr_buffer.reset(new DataBufferHeap(gpregset.GetDataStart(), gpregset.GetByteSize())); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + m_fpr_buffer.reset(new DataBufferHeap(fpregset.GetDataStart(), fpregset.GetByteSize())); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_s390x::~RegisterContextCorePOSIX_s390x() +{ +} + +bool +RegisterContextCorePOSIX_s390x::ReadGPR() +{ + return true; +} + +bool +RegisterContextCorePOSIX_s390x::ReadFPR() +{ + return true; +} + +bool +RegisterContextCorePOSIX_s390x::WriteGPR() +{ + assert(0); + return false; +} + +bool +RegisterContextCorePOSIX_s390x::WriteFPR() +{ + assert(0); + return false; +} + +bool +RegisterContextCorePOSIX_s390x::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return false; + + if (IsGPR(reg)) + { + lldb::offset_t offset = reg_info->byte_offset; + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) + { + value.SetUInt(v, reg_info->byte_size); + return true; + } + } + + if (IsFPR(reg)) + { + lldb::offset_t offset = reg_info->byte_offset; + uint64_t v = m_fpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) + { + value.SetUInt(v, reg_info->byte_size); + return true; + } + } + + return false; +} + +bool +RegisterContextCorePOSIX_s390x::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCorePOSIX_s390x::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) +{ + return false; +} + +bool +RegisterContextCorePOSIX_s390x::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCorePOSIX_s390x::HardwareSingleStep(bool enable) +{ + return false; +} diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h new file mode 100644 index 0000000000000..8bb6fe1771ef9 --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h @@ -0,0 +1,65 @@ +//===-- RegisterContextCorePOSIX_s390x.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_RegisterContextCorePOSIX_s390x_h_ +#define liblldb_RegisterContextCorePOSIX_s390x_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_s390x.h" + +class RegisterContextCorePOSIX_s390x : public RegisterContextPOSIX_s390x +{ +public: + RegisterContextCorePOSIX_s390x(lldb_private::Thread &thread, lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + const lldb_private::DataExtractor &fpregset); + + ~RegisterContextCorePOSIX_s390x() override; + + bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value) override; + + bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value) override; + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool + HardwareSingleStep(bool enable) override; + +protected: + bool + ReadGPR() override; + + bool + ReadFPR() override; + + bool + WriteGPR() override; + + bool + WriteFPR() override; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb_private::DataExtractor m_gpr; + + lldb::DataBufferSP m_fpr_buffer; + lldb_private::DataExtractor m_fpr; +}; + +#endif // liblldb_RegisterContextCorePOSIX_s390x_h_ diff --git a/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/source/Plugins/Process/elf-core/ThreadElfCore.cpp index 9cc7829fc3913..e4cfa68044f10 100644 --- a/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ b/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -18,6 +18,7 @@ #include "ProcessElfCore.h" #include "Plugins/Process/Utility/RegisterContextLinux_arm.h" #include "Plugins/Process/Utility/RegisterContextLinux_arm64.h" +#include "Plugins/Process/Utility/RegisterContextLinux_s390x.h" #include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_arm.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_arm64.h" @@ -29,6 +30,7 @@ #include "RegisterContextPOSIXCore_arm64.h" #include "RegisterContextPOSIXCore_mips64.h" #include "RegisterContextPOSIXCore_powerpc.h" +#include "RegisterContextPOSIXCore_s390x.h" #include "RegisterContextPOSIXCore_x86_64.h" using namespace lldb; @@ -139,6 +141,9 @@ ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame) case llvm::Triple::aarch64: reg_interface = new RegisterContextLinux_arm64(arch); break; + case llvm::Triple::systemz: + reg_interface = new RegisterContextLinux_s390x(arch); + break; case llvm::Triple::x86_64: reg_interface = new RegisterContextLinux_x86_64(arch); break; @@ -174,6 +179,9 @@ ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame) case llvm::Triple::ppc64: m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_powerpc (*this, reg_interface, m_gpregset_data, m_fpregset_data, m_vregset_data)); break; + case llvm::Triple::systemz: + m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_s390x (*this, reg_interface, m_gpregset_data, m_fpregset_data)); + break; case llvm::Triple::x86: case llvm::Triple::x86_64: m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_x86_64 (*this, reg_interface, m_gpregset_data, m_fpregset_data)); @@ -218,6 +226,7 @@ ELFLinuxPrStatus::Parse(DataExtractor &data, ArchSpec &arch) size_t len; switch(arch.GetCore()) { + case ArchSpec::eCore_s390x_generic: case ArchSpec::eCore_x86_64_x86_64: len = data.ExtractBytes(0, ELFLINUXPRSTATUS64_SIZE, byteorder, this); return len == ELFLINUXPRSTATUS64_SIZE; @@ -241,6 +250,7 @@ ELFLinuxPrPsInfo::Parse(DataExtractor &data, ArchSpec &arch) size_t len; switch(arch.GetCore()) { + case ArchSpec::eCore_s390x_generic: case ArchSpec::eCore_x86_64_x86_64: len = data.ExtractBytes(0, ELFLINUXPRPSINFO64_SIZE, byteorder, this); return len == ELFLINUXPRPSINFO64_SIZE; diff --git a/source/Plugins/Process/elf-core/ThreadElfCore.h b/source/Plugins/Process/elf-core/ThreadElfCore.h index d3a42e0eb54dd..b4e990140675b 100644 --- a/source/Plugins/Process/elf-core/ThreadElfCore.h +++ b/source/Plugins/Process/elf-core/ThreadElfCore.h @@ -68,6 +68,7 @@ struct ELFLinuxPrStatus { switch(arch.GetCore()) { + case lldb_private::ArchSpec::eCore_s390x_generic: case lldb_private::ArchSpec::eCore_x86_64_x86_64: return ELFLINUXPRSTATUS64_SIZE; default: @@ -102,6 +103,7 @@ struct ELFLinuxPrPsInfo { switch(arch.GetCore()) { + case lldb_private::ArchSpec::eCore_s390x_generic: case lldb_private::ArchSpec::eCore_x86_64_x86_64: return ELFLINUXPRPSINFO64_SIZE; default: diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index 2ea1f206008a7..f164b1411be8f 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -793,8 +793,8 @@ GDBRemoteCommunication::PacketType GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet) { // Put the packet data into the buffer in a thread safe fashion - Mutex::Locker locker(m_bytes_mutex); - + std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex); + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); if (src && src_len > 0) @@ -845,7 +845,7 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri case '%': // Async notify packet isNotifyPacket = true; - // Intentional fall through + LLVM_FALLTHROUGH; case '$': // Look for a standard gdb packet? @@ -1120,7 +1120,7 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *url, { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) - log->Printf ("GDBRemoteCommunication::%s(url=%s, port=%" PRIu16, __FUNCTION__, url ? url : "<empty>", port ? *port : uint16_t(0)); + log->Printf ("GDBRemoteCommunication::%s(url=%s, port=%" PRIu16 ")", __FUNCTION__, url ? url : "<empty>", port ? *port : uint16_t(0)); Error error; // If we locate debugserver, keep that located version around @@ -1352,7 +1352,14 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *url, launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true); launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true); - + + if (log) + { + StreamString string_stream; + Platform *const platform = nullptr; + launch_info.Dump(string_stream, platform); + log->Printf("launch info for gdb-remote stub:\n%s", string_stream.GetString().c_str()); + } error = Host::LaunchProcess(launch_info); if (error.Success() && diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index c0ea9cceea2e4..c90706a88b84d 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -55,79 +55,79 @@ using namespace lldb_private::process_gdb_remote; //---------------------------------------------------------------------- // GDBRemoteCommunicationClient constructor //---------------------------------------------------------------------- -GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() : - GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet"), - m_supports_not_sending_acks (eLazyBoolCalculate), - m_supports_thread_suffix (eLazyBoolCalculate), - m_supports_threads_in_stop_reply (eLazyBoolCalculate), - m_supports_vCont_all (eLazyBoolCalculate), - m_supports_vCont_any (eLazyBoolCalculate), - m_supports_vCont_c (eLazyBoolCalculate), - m_supports_vCont_C (eLazyBoolCalculate), - m_supports_vCont_s (eLazyBoolCalculate), - m_supports_vCont_S (eLazyBoolCalculate), - m_qHostInfo_is_valid (eLazyBoolCalculate), - m_curr_pid_is_valid (eLazyBoolCalculate), - m_qProcessInfo_is_valid (eLazyBoolCalculate), - m_qGDBServerVersion_is_valid (eLazyBoolCalculate), - m_supports_alloc_dealloc_memory (eLazyBoolCalculate), - m_supports_memory_region_info (eLazyBoolCalculate), - m_supports_watchpoint_support_info (eLazyBoolCalculate), - m_supports_detach_stay_stopped (eLazyBoolCalculate), - m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), - m_attach_or_wait_reply(eLazyBoolCalculate), - m_prepare_for_reg_writing_reply (eLazyBoolCalculate), - m_supports_p (eLazyBoolCalculate), - m_supports_x (eLazyBoolCalculate), - m_avoid_g_packets (eLazyBoolCalculate), - m_supports_QSaveRegisterState (eLazyBoolCalculate), - m_supports_qXfer_auxv_read (eLazyBoolCalculate), - m_supports_qXfer_libraries_read (eLazyBoolCalculate), - m_supports_qXfer_libraries_svr4_read (eLazyBoolCalculate), - m_supports_qXfer_features_read (eLazyBoolCalculate), - m_supports_augmented_libraries_svr4_read (eLazyBoolCalculate), - m_supports_jThreadExtendedInfo (eLazyBoolCalculate), - m_supports_jLoadedDynamicLibrariesInfos (eLazyBoolCalculate), - m_supports_qProcessInfoPID (true), - m_supports_qfProcessInfo (true), - m_supports_qUserName (true), - m_supports_qGroupName (true), - m_supports_qThreadStopInfo (true), - m_supports_z0 (true), - m_supports_z1 (true), - m_supports_z2 (true), - m_supports_z3 (true), - m_supports_z4 (true), - m_supports_QEnvironment (true), - m_supports_QEnvironmentHexEncoded (true), - m_supports_qSymbol (true), - m_qSymbol_requests_done (false), - m_supports_qModuleInfo (true), - m_supports_jThreadsInfo (true), - m_curr_pid (LLDB_INVALID_PROCESS_ID), - m_curr_tid (LLDB_INVALID_THREAD_ID), - m_curr_tid_run (LLDB_INVALID_THREAD_ID), - m_num_supported_hardware_watchpoints (0), - m_async_mutex (Mutex::eMutexTypeRecursive), - m_async_packet_predicate (false), - m_async_packet (), - m_async_result (PacketResult::Success), - m_async_response (), - m_async_signal (-1), - m_interrupt_sent (false), - m_thread_id_to_used_usec_map (), - m_host_arch(), - m_process_arch(), - m_os_version_major (UINT32_MAX), - m_os_version_minor (UINT32_MAX), - m_os_version_update (UINT32_MAX), - m_os_build (), - m_os_kernel (), - m_hostname (), - m_gdb_server_name(), - m_gdb_server_version(UINT32_MAX), - m_default_packet_timeout (0), - m_max_packet_size (0) +GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() + : GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet"), + m_supports_not_sending_acks(eLazyBoolCalculate), + m_supports_thread_suffix(eLazyBoolCalculate), + m_supports_threads_in_stop_reply(eLazyBoolCalculate), + m_supports_vCont_all(eLazyBoolCalculate), + m_supports_vCont_any(eLazyBoolCalculate), + m_supports_vCont_c(eLazyBoolCalculate), + m_supports_vCont_C(eLazyBoolCalculate), + m_supports_vCont_s(eLazyBoolCalculate), + m_supports_vCont_S(eLazyBoolCalculate), + m_qHostInfo_is_valid(eLazyBoolCalculate), + m_curr_pid_is_valid(eLazyBoolCalculate), + m_qProcessInfo_is_valid(eLazyBoolCalculate), + m_qGDBServerVersion_is_valid(eLazyBoolCalculate), + m_supports_alloc_dealloc_memory(eLazyBoolCalculate), + m_supports_memory_region_info(eLazyBoolCalculate), + m_supports_watchpoint_support_info(eLazyBoolCalculate), + m_supports_detach_stay_stopped(eLazyBoolCalculate), + m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), + m_attach_or_wait_reply(eLazyBoolCalculate), + m_prepare_for_reg_writing_reply(eLazyBoolCalculate), + m_supports_p(eLazyBoolCalculate), + m_supports_x(eLazyBoolCalculate), + m_avoid_g_packets(eLazyBoolCalculate), + m_supports_QSaveRegisterState(eLazyBoolCalculate), + m_supports_qXfer_auxv_read(eLazyBoolCalculate), + m_supports_qXfer_libraries_read(eLazyBoolCalculate), + m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate), + m_supports_qXfer_features_read(eLazyBoolCalculate), + m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate), + m_supports_jThreadExtendedInfo(eLazyBoolCalculate), + m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate), + m_supports_qProcessInfoPID(true), + m_supports_qfProcessInfo(true), + m_supports_qUserName(true), + m_supports_qGroupName(true), + m_supports_qThreadStopInfo(true), + m_supports_z0(true), + m_supports_z1(true), + m_supports_z2(true), + m_supports_z3(true), + m_supports_z4(true), + m_supports_QEnvironment(true), + m_supports_QEnvironmentHexEncoded(true), + m_supports_qSymbol(true), + m_qSymbol_requests_done(false), + m_supports_qModuleInfo(true), + m_supports_jThreadsInfo(true), + m_curr_pid(LLDB_INVALID_PROCESS_ID), + m_curr_tid(LLDB_INVALID_THREAD_ID), + m_curr_tid_run(LLDB_INVALID_THREAD_ID), + m_num_supported_hardware_watchpoints(0), + m_async_mutex(), + m_async_packet_predicate(false), + m_async_packet(), + m_async_result(PacketResult::Success), + m_async_response(), + m_async_signal(-1), + m_interrupt_sent(false), + m_thread_id_to_used_usec_map(), + m_host_arch(), + m_process_arch(), + m_os_version_major(UINT32_MAX), + m_os_version_minor(UINT32_MAX), + m_os_version_update(UINT32_MAX), + m_os_build(), + m_os_kernel(), + m_hostname(), + m_gdb_server_name(), + m_gdb_server_version(UINT32_MAX), + m_default_packet_timeout(0), + m_max_packet_size(0) { } @@ -623,6 +623,7 @@ GDBRemoteCommunicationClient::GetThreadsInfo() if (m_supports_jThreadsInfo) { StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) @@ -765,9 +766,29 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponseNoLock (const char *pa size_t payload_length, StringExtractorGDBRemote &response) { - PacketResult packet_result = SendPacketNoLock (payload, payload_length); + PacketResult packet_result = SendPacketNoLock(payload, payload_length); if (packet_result == PacketResult::Success) - packet_result = ReadPacket (response, GetPacketTimeoutInMicroSeconds (), true); + { + const size_t max_response_retries = 3; + for (size_t i=0; i<max_response_retries; ++i) + { + packet_result = ReadPacket(response, GetPacketTimeoutInMicroSeconds (), true); + // Make sure we received a response + if (packet_result != PacketResult::Success) + return packet_result; + // Make sure our response is valid for the payload that was sent + if (response.ValidateResponse()) + return packet_result; + // Response says it wasn't valid + Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS); + if (log) + log->Printf("error: packet with payload \"%*s\" got invalid response \"%s\": %s", + (int)payload_length, + payload, + response.GetStringRef().c_str(), + (i == (max_response_retries - 1)) ? "using invalid response and giving up" : "ignoring response and waiting for another"); + } + } return packet_result; } @@ -786,8 +807,8 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponse // In order to stop async notifications from being processed in the middle of the // send/receive sequence Hijack the broadcast. Then rebroadcast any events when we are done. - static Listener hijack_listener("lldb.NotifyHijacker"); - HijackBroadcaster(&hijack_listener, eBroadcastBitGdbReadThreadGotNotify); + static ListenerSP hijack_listener_sp(Listener::MakeListener("lldb.NotifyHijacker")); + HijackBroadcaster(hijack_listener_sp, eBroadcastBitGdbReadThreadGotNotify); if (GetSequenceMutex (locker)) { @@ -799,8 +820,9 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponse { if (IsRunning()) { - Mutex::Locker async_locker (m_async_mutex); + std::lock_guard<std::recursive_mutex> guard(m_async_mutex); m_async_packet.assign(payload, payload_length); + m_async_response.CopyResponseValidator(response); m_async_packet_predicate.SetValue (true, eBroadcastNever); if (log) @@ -867,6 +889,9 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponse if (log) log->Printf ("async: failed to interrupt"); } + + m_async_response.SetResponseValidator(nullptr, nullptr); + } else { @@ -886,7 +911,7 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponse // If a notification event occurred, rebroadcast since it can now be processed safely. EventSP event_sp; - if (hijack_listener.GetNextEvent(event_sp)) + if (hijack_listener_sp->GetNextEvent(event_sp)) BroadcastEvent(event_sp); return packet_result; @@ -1136,13 +1161,17 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse // which will just re-send a copy of the last stop reply // packet. If we don't do this, then the reply for our // async packet will be the repeat stop reply packet and cause - // a lot of trouble for us! - if (signo != sigint_signo && signo != sigstop_signo) + // a lot of trouble for us! We also have some debugserver + // binaries that would send two stop replies anytime the process + // was interrupted, so we need to also check for an extra + // stop reply packet if we interrupted the process + const bool received_nonstop_signal = signo != sigint_signo && signo != sigstop_signo; + if (m_interrupt_sent || received_nonstop_signal) { - continue_after_async = false; + if (received_nonstop_signal) + continue_after_async = false; - // We didn't get a SIGINT or SIGSTOP, so try for a - // very brief time (0.1s) to get another stop reply + // Try for a very brief time (0.1s) to get another stop reply // packet to make sure it doesn't get in the way StringExtractorGDBRemote extra_stop_reply_packet; uint32_t timeout_usec = 100000; @@ -1343,7 +1372,7 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse bool GDBRemoteCommunicationClient::SendAsyncSignal (int signo) { - Mutex::Locker async_locker (m_async_mutex); + std::lock_guard<std::recursive_mutex> guard(m_async_mutex); m_async_signal = signo; bool timed_out = false; Mutex::Locker locker; @@ -2064,7 +2093,8 @@ GDBRemoteCommunicationClient::GetHostInfo (bool force) if (pointer_byte_size != 0) ++num_keys_decoded; } - else if (name.compare("os_version") == 0) + else if ((name.compare("os_version") == 0) || + (name.compare("version") == 0)) // Older debugserver binaries used the "version" key instead of "os_version"... { Args::StringToVersion (value.c_str(), m_os_version_major, @@ -2114,20 +2144,6 @@ GDBRemoteCommunicationClient::GetHostInfo (bool force) assert (byte_order == m_host_arch.GetByteOrder()); } - if (!os_name.empty() && vendor_name.compare("apple") == 0 && os_name.find("darwin") == 0) - { - switch (m_host_arch.GetMachine()) - { - case llvm::Triple::aarch64: - case llvm::Triple::arm: - case llvm::Triple::thumb: - os_name = "ios"; - break; - default: - os_name = "macosx"; - break; - } - } if (!vendor_name.empty()) m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); if (!os_name.empty()) @@ -2411,6 +2427,8 @@ GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr, region_info.SetExecutable (MemoryRegionInfo::eYes); else region_info.SetExecutable (MemoryRegionInfo::eNo); + + region_info.SetMapped(MemoryRegionInfo::eYes); } else { @@ -2418,6 +2436,7 @@ GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr, region_info.SetReadable (MemoryRegionInfo::eNo); region_info.SetWritable (MemoryRegionInfo::eNo); region_info.SetExecutable (MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); } } else if (name.compare ("error") == 0) @@ -2437,6 +2456,7 @@ GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr, region_info.SetReadable (MemoryRegionInfo::eNo); region_info.SetWritable (MemoryRegionInfo::eNo); region_info.SetExecutable (MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); } } else @@ -3571,6 +3591,8 @@ GDBRemoteCommunicationClient::SendGDBStoppointTypePacket (GDBStoppointType type, // Check we haven't overwritten the end of the packet buffer assert (packet_len + 1 < (int)sizeof(packet)); StringExtractorGDBRemote response; + // Make sure the response is either "OK", "EXX" where XX are two hex digits, or "" (unsupported) + response.SetResponseValidatorToOKErrorNotSupported(); // Try to send the breakpoint packet, and check that it was correctly sent if (SendPacketAndWaitForResponse(packet, packet_len, response, true) == PacketResult::Success) { @@ -4417,7 +4439,7 @@ GDBRemoteCommunicationClient::ReadExtFeature (const lldb_private::ConstString ob // last chunk case ( 'l' ): active = false; - // fall through intentional + LLVM_FALLTHROUGH; // more chunks case ( 'm' ) : diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 311b0f3267c8e..096c4cf81015e 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -13,6 +13,7 @@ // C Includes // C++ Includes #include <map> +#include <mutex> #include <string> #include <vector> @@ -527,7 +528,7 @@ public: bool ReadRegister(lldb::tid_t tid, - uint32_t reg_num, + uint32_t reg_num, // Must be the eRegisterKindProcessPlugin register number, to be sent to the remote StringExtractorGDBRemote &response); bool @@ -631,7 +632,7 @@ protected: // If we need to send a packet while the target is running, the m_async_XXX // member variables take care of making this happen. - Mutex m_async_mutex; + std::recursive_mutex m_async_mutex; Predicate<bool> m_async_packet_predicate; std::string m_async_packet; PacketResult m_async_result; diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h index f55b2eb3f4dc7..d2fd70042ccc9 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h @@ -17,7 +17,6 @@ // Other libraries and framework includes // Project includes #include "lldb/lldb-private-forward.h" -#include "lldb/Host/Mutex.h" #include "lldb/Target/Process.h" #include "GDBRemoteCommunicationServer.h" diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 921369c7ef21c..fc6b31ec088ed 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -39,7 +39,6 @@ #include "lldb/Host/TimeValue.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/MemoryRegionInfo.h" -#include "lldb/Target/Platform.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeThreadProtocol.h" @@ -76,25 +75,21 @@ namespace //---------------------------------------------------------------------- // GDBRemoteCommunicationServerLLGS constructor //---------------------------------------------------------------------- -GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS( - const lldb::PlatformSP& platform_sp, - MainLoop &mainloop) : - GDBRemoteCommunicationServerCommon ("gdb-remote.server", "gdb-remote.server.rx_packet"), - m_platform_sp (platform_sp), - m_mainloop (mainloop), - m_current_tid (LLDB_INVALID_THREAD_ID), - m_continue_tid (LLDB_INVALID_THREAD_ID), - m_debugged_process_mutex (Mutex::eMutexTypeRecursive), - m_debugged_process_sp (), - m_stdio_communication ("process.stdio"), - m_inferior_prev_state (StateType::eStateInvalid), - m_active_auxv_buffer_sp (), - m_saved_registers_mutex (), - m_saved_registers_map (), - m_next_saved_registers_id (1), - m_handshake_completed (false) -{ - assert(platform_sp); +GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS(MainLoop &mainloop) + : GDBRemoteCommunicationServerCommon("gdb-remote.server", "gdb-remote.server.rx_packet"), + m_mainloop(mainloop), + m_current_tid(LLDB_INVALID_THREAD_ID), + m_continue_tid(LLDB_INVALID_THREAD_ID), + m_debugged_process_mutex(), + m_debugged_process_sp(), + m_stdio_communication("process.stdio"), + m_inferior_prev_state(StateType::eStateInvalid), + m_active_auxv_buffer_sp(), + m_saved_registers_mutex(), + m_saved_registers_map(), + m_next_saved_registers_id(1), + m_handshake_completed(false) +{ RegisterPacketHandlers(); } @@ -210,7 +205,7 @@ GDBRemoteCommunicationServerLLGS::LaunchProcess () Error error; { - Mutex::Locker locker (m_debugged_process_mutex); + std::lock_guard<std::recursive_mutex> guard(m_debugged_process_mutex); assert (!m_debugged_process_sp && "lldb-gdbserver creating debugged process but one already exists"); error = NativeProcessProtocol::Launch( m_process_launch_info, @@ -1367,7 +1362,7 @@ GDBRemoteCommunicationServerLLGS::Handle_vCont (StringExtractorGDBRemote &packet thread_action.signal = packet.GetHexMaxU32 (false, 0); if (thread_action.signal == 0) return SendIllFormedResponse (packet, "Could not parse signal in vCont packet C action"); - // Fall through to next case... + LLVM_FALLTHROUGH; case 'c': // Continue @@ -1378,7 +1373,7 @@ GDBRemoteCommunicationServerLLGS::Handle_vCont (StringExtractorGDBRemote &packet thread_action.signal = packet.GetHexMaxU32 (false, 0); if (thread_action.signal == 0) return SendIllFormedResponse (packet, "Could not parse signal in vCont packet S action"); - // Fall through to next case... + LLVM_FALLTHROUGH; case 's': // Step @@ -2593,7 +2588,7 @@ GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState (StringExtractorGDBR // Save the register data buffer under the save id. { - Mutex::Locker locker (m_saved_registers_mutex); + std::lock_guard<std::mutex> guard(m_saved_registers_mutex); m_saved_registers_map[save_id] = register_data_sp; } @@ -2643,7 +2638,7 @@ GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState (StringExtractorG // Retrieve register state buffer, then remove from the list. DataBufferSP register_data_sp; { - Mutex::Locker locker (m_saved_registers_mutex); + std::lock_guard<std::mutex> guard(m_saved_registers_mutex); // Find the register set buffer for the given save id. auto it = m_saved_registers_map.find (save_id); @@ -2947,7 +2942,7 @@ GDBRemoteCommunicationServerLLGS::GetCurrentThreadID () const uint32_t GDBRemoteCommunicationServerLLGS::GetNextSavedRegistersID () { - Mutex::Locker locker (m_saved_registers_mutex); + std::lock_guard<std::mutex> guard(m_saved_registers_mutex); return m_next_saved_registers_id++; } diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h index f16057781ddc4..caf6eb319e63b 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -12,12 +12,12 @@ // C Includes // C++ Includes +#include <mutex> #include <unordered_map> // Other libraries and framework includes #include "lldb/lldb-private-forward.h" #include "lldb/Core/Communication.h" -#include "lldb/Host/Mutex.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/MainLoop.h" @@ -40,7 +40,7 @@ public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ - GDBRemoteCommunicationServerLLGS(const lldb::PlatformSP& platform_sp, MainLoop &mainloop); + GDBRemoteCommunicationServerLLGS(MainLoop &mainloop); //------------------------------------------------------------------ /// Specify the program to launch and its arguments. @@ -114,12 +114,11 @@ public: InitializeConnection (std::unique_ptr<Connection> &&connection); protected: - lldb::PlatformSP m_platform_sp; MainLoop &m_mainloop; MainLoop::ReadHandleUP m_network_handle_up; lldb::tid_t m_current_tid; lldb::tid_t m_continue_tid; - Mutex m_debugged_process_mutex; + std::recursive_mutex m_debugged_process_mutex; NativeProcessProtocolSP m_debugged_process_sp; Communication m_stdio_communication; @@ -127,7 +126,7 @@ protected: lldb::StateType m_inferior_prev_state; lldb::DataBufferSP m_active_auxv_buffer_sp; - Mutex m_saved_registers_mutex; + std::mutex m_saved_registers_mutex; std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map; uint32_t m_next_saved_registers_id; bool m_handshake_completed : 1; diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp index f88ac12475260..d6900c27293c7 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -48,14 +48,13 @@ using namespace lldb_private::process_gdb_remote; // GDBRemoteCommunicationServerPlatform constructor //---------------------------------------------------------------------- GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(const Socket::SocketProtocol socket_protocol, - const char* socket_scheme) : - GDBRemoteCommunicationServerCommon ("gdb-remote.server", "gdb-remote.server.rx_packet"), - m_socket_protocol(socket_protocol), - m_socket_scheme(socket_scheme), - m_spawned_pids_mutex (Mutex::eMutexTypeRecursive), - m_platform_sp (Platform::GetHostPlatform ()), - m_port_map (), - m_port_offset(0) + const char *socket_scheme) + : GDBRemoteCommunicationServerCommon("gdb-remote.server", "gdb-remote.server.rx_packet"), + m_socket_protocol(socket_protocol), + m_socket_scheme(socket_scheme), + m_spawned_pids_mutex(), + m_port_map(), + m_port_offset(0) { m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID; m_pending_gdb_server.port = 0; @@ -78,11 +77,7 @@ GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(const &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo); RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt, - [this](StringExtractorGDBRemote packet, - Error &error, - bool &interrupt, - bool &quit) - { + [this](StringExtractorGDBRemote packet, Error &error, bool &interrupt, bool &quit) { error.SetErrorString("interrupt received"); interrupt = true; return PacketResult::Success; @@ -124,7 +119,8 @@ GDBRemoteCommunicationServerPlatform::LaunchGDBServer(const lldb_private::Args& // Do not run in a new session so that it can not linger after the // platform closes. debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); - debugserver_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false); + debugserver_launch_info.SetMonitorProcessCallback( + std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, this, std::placeholders::_1), false); std::string platform_scheme; std::string platform_ip; @@ -135,6 +131,10 @@ GDBRemoteCommunicationServerPlatform::LaunchGDBServer(const lldb_private::Args& assert(ok); std::ostringstream url; + // debugserver does not accept the URL scheme prefix. +#if !defined(__APPLE__) + url << m_socket_scheme << "://"; +#endif uint16_t* port_ptr = &port; if (m_socket_protocol == Socket::ProtocolTcp) url << platform_ip << ":" << port; @@ -154,7 +154,7 @@ GDBRemoteCommunicationServerPlatform::LaunchGDBServer(const lldb_private::Args& pid = debugserver_launch_info.GetProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) { - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); m_spawned_pids.insert(pid); if (port > 0) AssociatePortWithProcess(port, pid); @@ -259,7 +259,7 @@ GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess (StringExtracto // verify that we know anything about this pid. // Scope for locker { - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { // not a pid we know about @@ -279,7 +279,7 @@ GDBRemoteCommunicationServerPlatform::KillSpawnedProcess (lldb::pid_t pid) { // make sure we know about this process { - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) return false; } @@ -291,7 +291,7 @@ GDBRemoteCommunicationServerPlatform::KillSpawnedProcess (lldb::pid_t pid) for (size_t i=0; i<10; ++i) { { - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { // it is now killed @@ -303,7 +303,7 @@ GDBRemoteCommunicationServerPlatform::KillSpawnedProcess (lldb::pid_t pid) // check one more time after the final usleep { - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) return true; } @@ -315,7 +315,7 @@ GDBRemoteCommunicationServerPlatform::KillSpawnedProcess (lldb::pid_t pid) for (size_t i=0; i<10; ++i) { { - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { // it is now killed @@ -328,7 +328,7 @@ GDBRemoteCommunicationServerPlatform::KillSpawnedProcess (lldb::pid_t pid) // check one more time after the final usleep // Scope for locker { - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); if (m_spawned_pids.find(pid) == m_spawned_pids.end()) return true; } @@ -442,20 +442,9 @@ GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo(StringExtractorGDBRemo bool GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped (lldb::pid_t pid) { - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); FreePortForProcess(pid); - return m_spawned_pids.erase(pid) > 0; -} - -bool -GDBRemoteCommunicationServerPlatform::ReapDebugserverProcess (void *callback_baton, - lldb::pid_t pid, - bool exited, - int signal, // Zero for no signal - int status) // Exit value of process if signal is zero -{ - GDBRemoteCommunicationServerPlatform *server = (GDBRemoteCommunicationServerPlatform *)callback_baton; - server->DebugserverProcessReaped (pid); + m_spawned_pids.erase(pid); return true; } @@ -469,9 +458,11 @@ GDBRemoteCommunicationServerPlatform::LaunchProcess () // generally be what happens since we need to reap started // processes. if (!m_process_launch_info.GetMonitorProcessCallback ()) - m_process_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false); + m_process_launch_info.SetMonitorProcessCallback( + std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, this, std::placeholders::_1), + false); - Error error = m_platform_sp->LaunchProcess (m_process_launch_info); + Error error = Host::LaunchProcess(m_process_launch_info); if (!error.Success ()) { fprintf (stderr, "%s: failed to launch executable %s", __FUNCTION__, m_process_launch_info.GetArguments ().GetArgumentAtIndex (0)); @@ -486,7 +477,7 @@ GDBRemoteCommunicationServerPlatform::LaunchProcess () if (pid != LLDB_INVALID_PROCESS_ID) { // add to spawned pids - Mutex::Locker locker (m_spawned_pids_mutex); + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); m_spawned_pids.insert(pid); } diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h index 1fe7207d2bc2e..1f4d08c64e009 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h @@ -13,6 +13,7 @@ // C Includes // C++ Includes #include <map> +#include <mutex> #include <set> // Other libraries and framework includes @@ -82,9 +83,8 @@ public: protected: const Socket::SocketProtocol m_socket_protocol; const std::string m_socket_scheme; - Mutex m_spawned_pids_mutex; + std::recursive_mutex m_spawned_pids_mutex; std::set<lldb::pid_t> m_spawned_pids; - lldb::PlatformSP m_platform_sp; PortMap m_port_map; uint16_t m_port_offset; @@ -121,13 +121,6 @@ private: bool DebugserverProcessReaped (lldb::pid_t pid); - static bool - ReapDebugserverProcess (void *callback_baton, - lldb::pid_t pid, - bool exited, - int signal, - int status); - static const FileSpec& GetDomainSocketDir(); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp index b0a1eaaeb79c0..e5b347c9f72db 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -198,10 +198,11 @@ bool GDBRemoteRegisterContext::GetPrimordialRegister(const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) { - const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + const uint32_t lldb_reg = reg_info->kinds[eRegisterKindLLDB]; + const uint32_t remote_reg = reg_info->kinds[eRegisterKindProcessPlugin]; StringExtractorGDBRemote response; - if (gdb_comm.ReadRegister(m_thread.GetProtocolID(), reg, response)) - return PrivateSetRegisterValue (reg, response); + if (gdb_comm.ReadRegister(m_thread.GetProtocolID(), remote_reg, response)) + return PrivateSetRegisterValue (lldb_reg, response); return false; } @@ -316,7 +317,7 @@ GDBRemoteRegisterContext::SetPrimordialRegister(const RegisterInfo *reg_info, StreamString packet; StringExtractorGDBRemote response; const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; - packet.Printf ("P%x=", reg); + packet.Printf ("P%x=", reg_info->kinds[eRegisterKindProcessPlugin]); packet.PutBytesAsRawHex8 (m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), reg_info->byte_size, endian::InlHostByteOrder(), @@ -813,7 +814,7 @@ GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data if (restore_src) { StreamString packet; - packet.Printf ("P%x=", reg); + packet.Printf ("P%x=", reg_info->kinds[eRegisterKindProcessPlugin]); packet.PutBytesAsRawHex8 (restore_src, reg_byte_size, endian::InlHostByteOrder(), @@ -836,7 +837,7 @@ GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data if (write_reg) { StreamString packet; - packet.Printf ("P%x=", reg); + packet.Printf ("P%x=", reg_info->kinds[eRegisterKindProcessPlugin]); packet.PutBytesAsRawHex8 (restore_src, reg_byte_size, endian::InlHostByteOrder(), @@ -894,7 +895,7 @@ GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data continue; } StreamString packet; - packet.Printf ("P%x=", reg_info->kinds[eRegisterKindLLDB]); + packet.Printf ("P%x=", reg_info->kinds[eRegisterKindProcessPlugin]); packet.PutBytesAsRawHex8 (data_sp->GetBytes() + reg_info->byte_offset, reg_info->byte_size, endian::InlHostByteOrder(), endian::InlHostByteOrder()); if (thread_suffix_supported) packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID()); diff --git a/source/Plugins/Process/gdb-remote/Makefile b/source/Plugins/Process/gdb-remote/Makefile deleted file mode 100644 index 8a9b61077875e..0000000000000 --- a/source/Plugins/Process/gdb-remote/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- source/Plugins/Process/gdb-remote/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 := lldbPluginProcessGDBRemote -BUILD_ARCHIVE = 1 - -include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 856ea35aef994..4d56f6ea3ba1b 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -223,11 +223,11 @@ ProcessGDBRemote::Terminate() lldb::ProcessSP -ProcessGDBRemote::CreateInstance (lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file_path) +ProcessGDBRemote::CreateInstance (lldb::TargetSP target_sp, ListenerSP listener_sp, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) - process_sp.reset (new ProcessGDBRemote (target_sp, listener)); + process_sp.reset (new ProcessGDBRemote (target_sp, listener_sp)); return process_sp; } @@ -267,51 +267,51 @@ ProcessGDBRemote::CanDebug (lldb::TargetSP target_sp, bool plugin_specified_by_n //---------------------------------------------------------------------- // ProcessGDBRemote constructor //---------------------------------------------------------------------- -ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, Listener &listener) : - Process (target_sp, listener), - m_flags (0), - m_gdb_comm (), - m_debugserver_pid (LLDB_INVALID_PROCESS_ID), - m_last_stop_packet_mutex (Mutex::eMutexTypeRecursive), - m_register_info (), - m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"), - m_async_listener("lldb.process.gdb-remote.async-listener"), - m_async_thread_state_mutex(Mutex::eMutexTypeRecursive), - m_thread_ids (), - m_thread_pcs (), - m_jstopinfo_sp (), - m_jthreadsinfo_sp (), - m_continue_c_tids (), - m_continue_C_tids (), - m_continue_s_tids (), - m_continue_S_tids (), - m_max_memory_size (0), - m_remote_stub_max_memory_size (0), - m_addr_to_mmap_size (), - m_thread_create_bp_sp (), - m_waiting_for_attach (false), - m_destroy_tried_resuming (false), - m_command_sp (), - m_breakpoint_pc_offset (0), - m_initial_tid (LLDB_INVALID_THREAD_ID) +ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, ListenerSP listener_sp) + : Process(target_sp, listener_sp), + m_flags(0), + m_gdb_comm(), + m_debugserver_pid(LLDB_INVALID_PROCESS_ID), + m_last_stop_packet_mutex(), + m_register_info(), + m_async_broadcaster(NULL, "lldb.process.gdb-remote.async-broadcaster"), + m_async_listener_sp(Listener::MakeListener("lldb.process.gdb-remote.async-listener")), + m_async_thread_state_mutex(), + m_thread_ids(), + m_thread_pcs(), + m_jstopinfo_sp(), + m_jthreadsinfo_sp(), + m_continue_c_tids(), + m_continue_C_tids(), + m_continue_s_tids(), + m_continue_S_tids(), + m_max_memory_size(0), + m_remote_stub_max_memory_size(0), + m_addr_to_mmap_size(), + m_thread_create_bp_sp(), + m_waiting_for_attach(false), + m_destroy_tried_resuming(false), + m_command_sp(), + m_breakpoint_pc_offset(0), + m_initial_tid(LLDB_INVALID_THREAD_ID) { - m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); - m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); - m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadDidExit, "async thread did exit"); + m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); + m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, "async thread continue"); + m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit, "async thread did exit"); - Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_ASYNC)); + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC)); const uint32_t async_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; - if (m_async_listener.StartListeningForEvents(&m_async_broadcaster, async_event_mask) != async_event_mask) + if (m_async_listener_sp->StartListeningForEvents(&m_async_broadcaster, async_event_mask) != async_event_mask) { if (log) log->Printf("ProcessGDBRemote::%s failed to listen for m_async_broadcaster events", __FUNCTION__); } - const uint32_t gdb_event_mask = Communication::eBroadcastBitReadThreadDidExit | - GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify; - if (m_async_listener.StartListeningForEvents(&m_gdb_comm, gdb_event_mask) != gdb_event_mask) + const uint32_t gdb_event_mask = + Communication::eBroadcastBitReadThreadDidExit | GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify; + if (m_async_listener_sp->StartListeningForEvents(&m_gdb_comm, gdb_event_mask) != gdb_event_mask) { if (log) log->Printf("ProcessGDBRemote::%s failed to listen for m_gdb_comm events", __FUNCTION__); @@ -500,7 +500,21 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) } } - if (GetGDBServerRegisterInfo ()) + const ArchSpec &target_arch = GetTarget().GetArchitecture(); + const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); + const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); + + // Use the process' architecture instead of the host arch, if available + ArchSpec arch_to_use; + if (remote_process_arch.IsValid ()) + arch_to_use = remote_process_arch; + else + arch_to_use = remote_host_arch; + + if (!arch_to_use.IsValid()) + arch_to_use = target_arch; + + if (GetGDBServerRegisterInfo (arch_to_use)) return; char packet[128]; @@ -640,7 +654,12 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) reg_info.invalidate_regs = invalidate_regs.data(); } - AugmentRegisterInfoViaABI (reg_info, reg_name, GetABI ()); + // We have to make a temporary ABI here, and not use the GetABI because this code + // gets called in DidAttach, when the target architecture (and consequently the ABI we'll get from + // the process) may be wrong. + ABISP abi_to_use = ABI::FindPlugin(arch_to_use); + + AugmentRegisterInfoViaABI (reg_info, reg_name, abi_to_use); m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); } @@ -668,22 +687,11 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) // add composite registers to the existing primordial ones. bool from_scratch = (m_register_info.GetNumRegisters() == 0); - const ArchSpec &target_arch = GetTarget().GetArchitecture(); - const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); - const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); - - // Use the process' architecture instead of the host arch, if available - ArchSpec remote_arch; - if (remote_process_arch.IsValid ()) - remote_arch = remote_process_arch; - else - remote_arch = remote_host_arch; - if (!target_arch.IsValid()) { - if (remote_arch.IsValid() - && (remote_arch.GetMachine() == llvm::Triple::arm || remote_arch.GetMachine() == llvm::Triple::thumb) - && remote_arch.GetTriple().getVendor() == llvm::Triple::Apple) + if (arch_to_use.IsValid() + && (arch_to_use.GetMachine() == llvm::Triple::arm || arch_to_use.GetMachine() == llvm::Triple::thumb) + && arch_to_use.GetTriple().getVendor() == llvm::Triple::Apple) m_register_info.HardcodeARMRegisters(from_scratch); } else if (target_arch.GetMachine() == llvm::Triple::arm @@ -1360,10 +1368,10 @@ ProcessGDBRemote::DoResume () if (log) log->Printf ("ProcessGDBRemote::Resume()"); - Listener listener ("gdb-remote.resume-packet-sent"); - if (listener.StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) + ListenerSP listener_sp (Listener::MakeListener("gdb-remote.resume-packet-sent")); + if (listener_sp->StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { - listener.StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); + listener_sp->StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); const size_t num_threads = GetThreadList().GetSize(); @@ -1595,7 +1603,7 @@ ProcessGDBRemote::DoResume () m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (continue_packet.GetData(), continue_packet.GetSize())); - if (listener.WaitForEvent (&timeout, event_sp) == false) + if (listener_sp->WaitForEvent (&timeout, event_sp) == false) { error.SetErrorString("Resume timed out."); if (log) @@ -1638,7 +1646,7 @@ ProcessGDBRemote::HandleStopReplySequence () void ProcessGDBRemote::ClearThreadIDList () { - Mutex::Locker locker(m_thread_list_real.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); } @@ -1688,7 +1696,7 @@ ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue (std::string &value) bool ProcessGDBRemote::UpdateThreadIDList () { - Mutex::Locker locker(m_thread_list_real.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); if (m_jthreadsinfo_sp) { @@ -1721,8 +1729,8 @@ ProcessGDBRemote::UpdateThreadIDList () // Lock the thread stack while we access it //Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); - Mutex::Locker stop_stack_lock; - if (stop_stack_lock.TryLock(m_last_stop_packet_mutex)) + std::unique_lock<std::recursive_mutex> stop_stack_lock(m_last_stop_packet_mutex, std::defer_lock); + if (stop_stack_lock.try_lock()) { // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); @@ -1832,7 +1840,7 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new } } } - new_thread_list.AddThread(thread_sp); + new_thread_list.AddThreadSortedByIndexID (thread_sp); } } @@ -1936,7 +1944,7 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid, // m_thread_list_real does have its own mutex, but we need to // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...) // and the m_thread_list_real.AddThread(...) so it doesn't change on us - Mutex::Locker locker (m_thread_list_real.GetMutex ()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); if (!thread_sp) @@ -2001,7 +2009,18 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid, { if (reason.compare("trace") == 0) { - thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); + addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + + // If the current pc is a breakpoint site then the StopInfo should be set to Breakpoint + // Otherwise, it will be set to Trace. + if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) + { + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonWithBreakpointSiteID(*thread_sp, bp_site_sp->GetID())); + } + else + thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); handled = true; } else if (reason.compare("breakpoint") == 0) @@ -2040,7 +2059,8 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid, { WatchpointSP wp_sp; ArchSpec::Core core = GetTarget().GetArchitecture().GetCore(); - if (core >= ArchSpec::kCore_mips_first && core <= ArchSpec::kCore_mips_last) + if ((core >= ArchSpec::kCore_mips_first && core <= ArchSpec::kCore_mips_last) || + (core >= ArchSpec::eCore_arm_generic && core <= ArchSpec::eCore_arm_aarch64)) wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_hit_addr); if (!wp_sp) wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); @@ -2070,6 +2090,23 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid, handled = true; } } + else if (!signo) + { + addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site_sp = + thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + + // If the current pc is a breakpoint site then the StopInfo should be set to Breakpoint + // even though the remote stub did not set it as such. This can happen when + // the thread is involuntarily interrupted (e.g. due to stops on other + // threads) just as it is about to execute the breakpoint instruction. + if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) + { + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonWithBreakpointSiteID(*thread_sp, bp_site_sp->GetID())); + handled = true; + } + } if (!handled && signo && did_exec == false) { @@ -2404,7 +2441,8 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) } else if (key.compare("threads") == 0) { - Mutex::Locker locker(m_thread_list_real.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); + m_thread_ids.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply @@ -2627,7 +2665,8 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) void ProcessGDBRemote::RefreshStateAfterStop () { - Mutex::Locker locker(m_thread_list_real.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); + m_thread_ids.clear(); m_thread_pcs.clear(); // Set the thread stop info. It might have a "threads" key whose value is @@ -2637,7 +2676,7 @@ ProcessGDBRemote::RefreshStateAfterStop () // Scope for the lock { // Lock the thread stack while we access it - Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); + std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex); // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them @@ -2782,7 +2821,7 @@ ProcessGDBRemote::DoDestroy () ThreadList &threads = GetThreadList(); { - Mutex::Locker locker(threads.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) @@ -2817,7 +2856,7 @@ ProcessGDBRemote::DoDestroy () // have to run the risk of letting those threads proceed a bit. { - Mutex::Locker locker(threads.GetMutex()); + std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) @@ -2939,7 +2978,7 @@ ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response) // Scope the lock { // Lock the thread stack while we access it - Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); + std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex); // We are are not using non-stop mode, there can only be one last stop // reply packet, so clear the list. @@ -3119,35 +3158,33 @@ ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &er Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; - LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); - switch (supported) + if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) { - case eLazyBoolCalculate: - case eLazyBoolYes: - allocated_addr = m_gdb_comm.AllocateMemory (size, permissions); - if (allocated_addr != LLDB_INVALID_ADDRESS || supported == eLazyBoolYes) - return allocated_addr; + allocated_addr = m_gdb_comm.AllocateMemory (size, permissions); + if (allocated_addr != LLDB_INVALID_ADDRESS || m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) + return allocated_addr; + } - case eLazyBoolNo: - // Call mmap() to create memory in the inferior.. - unsigned prot = 0; - if (permissions & lldb::ePermissionsReadable) - prot |= eMmapProtRead; - if (permissions & lldb::ePermissionsWritable) - prot |= eMmapProtWrite; - if (permissions & lldb::ePermissionsExecutable) - prot |= eMmapProtExec; - - if (InferiorCallMmap(this, allocated_addr, 0, size, prot, - eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) - m_addr_to_mmap_size[allocated_addr] = size; - else - { - allocated_addr = LLDB_INVALID_ADDRESS; - if (log) - log->Printf ("ProcessGDBRemote::%s no direct stub support for memory allocation, and InferiorCallMmap also failed - is stub missing register context save/restore capability?", __FUNCTION__); - } - break; + if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) + { + // Call mmap() to create memory in the inferior.. + unsigned prot = 0; + if (permissions & lldb::ePermissionsReadable) + prot |= eMmapProtRead; + if (permissions & lldb::ePermissionsWritable) + prot |= eMmapProtWrite; + if (permissions & lldb::ePermissionsExecutable) + prot |= eMmapProtExec; + + if (InferiorCallMmap(this, allocated_addr, 0, size, prot, + eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) + m_addr_to_mmap_size[allocated_addr] = size; + else + { + allocated_addr = LLDB_INVALID_ADDRESS; + if (log) + log->Printf ("ProcessGDBRemote::%s no direct stub support for memory allocation, and InferiorCallMmap also failed - is stub missing register context save/restore capability?", __FUNCTION__); + } } if (allocated_addr == LLDB_INVALID_ADDRESS) @@ -3273,7 +3310,8 @@ ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && (!bp_site->HardwareRequired())) { // Try to send off a software breakpoint packet ($Z0) - if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, true, addr, bp_op_size) == 0) + uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, true, addr, bp_op_size); + if (error_no == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); @@ -3289,7 +3327,13 @@ ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) // with the error code. If they are now unsupported, then we would like to fall through // and try another form of breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) + { + if (error_no != UINT8_MAX) + error.SetErrorStringWithFormat("error: %d sending the breakpoint request", errno); + else + error.SetErrorString("error sending the breakpoint request"); return error; + } // We reach here when software breakpoints have been found to be unsupported. For future // calls to set a breakpoint, we will not attempt to set a breakpoint with a type that is @@ -3306,7 +3350,8 @@ ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Try to send off a hardware breakpoint packet ($Z1) - if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, true, addr, bp_op_size) == 0) + uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, true, addr, bp_op_size); + if (error_no == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); @@ -3318,7 +3363,13 @@ ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Unable to set this hardware breakpoint - error.SetErrorString("failed to set hardware breakpoint (hardware breakpoint resources might be exhausted or unavailable)"); + if (error_no != UINT8_MAX) + error.SetErrorStringWithFormat("error: %d sending the hardware breakpoint request " + "(hardware breakpoint resources might be exhausted or unavailable)", + error_no); + else + error.SetErrorString("error sending the hardware breakpoint request (hardware breakpoint resources " + "might be exhausted or unavailable)"); return error; } @@ -3550,6 +3601,8 @@ ProcessGDBRemote::EstablishConnectionIfNeeded (const ProcessInfo &process_info) Error ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info) { + using namespace std::placeholders; // For _1, _2, etc. + Error error; if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { @@ -3561,7 +3614,9 @@ ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info // special terminal key sequences (^C) don't affect debugserver. debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); - debugserver_launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false); + const std::weak_ptr<ProcessGDBRemote> this_wp = std::static_pointer_cast<ProcessGDBRemote>(shared_from_this()); + debugserver_launch_info.SetMonitorProcessCallback(std::bind(MonitorDebugserverProcess, this_wp, _1, _2, _3, _4), + false); debugserver_launch_info.SetUserID(process_info.GetUserID()); #if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) @@ -3623,91 +3678,58 @@ ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info } bool -ProcessGDBRemote::MonitorDebugserverProcess -( - void *callback_baton, - lldb::pid_t debugserver_pid, - bool exited, // True if the process did exit - int signo, // Zero for no signal - int exit_status // Exit value of process if signal is zero -) +ProcessGDBRemote::MonitorDebugserverProcess(std::weak_ptr<ProcessGDBRemote> process_wp, lldb::pid_t debugserver_pid, + bool exited, // True if the process did exit + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero + ) { - // The baton is a "ProcessGDBRemote *". Now this class might be gone - // and might not exist anymore, so we need to carefully try to get the - // target for this process first since we have a race condition when - // we are done running between getting the notice that the inferior - // process has died and the debugserver that was debugging this process. - // In our test suite, we are also continually running process after - // process, so we must be very careful to make sure: - // 1 - process object hasn't been deleted already - // 2 - that a new process object hasn't been recreated in its place - // "debugserver_pid" argument passed in is the process ID for // debugserver that we are tracking... Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + const bool handled = true; - ProcessGDBRemote *process = (ProcessGDBRemote *)callback_baton; - - // Get a shared pointer to the target that has a matching process pointer. - // This target could be gone, or the target could already have a new process - // object inside of it - TargetSP target_sp (Debugger::FindTargetWithProcess(process)); + if (log) + log->Printf("ProcessGDBRemote::%s(process_wp, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", __FUNCTION__, + debugserver_pid, signo, signo, exit_status); + std::shared_ptr<ProcessGDBRemote> process_sp = process_wp.lock(); if (log) - log->Printf ("ProcessGDBRemote::MonitorDebugserverProcess (baton=%p, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", callback_baton, debugserver_pid, signo, signo, exit_status); - - if (target_sp) - { - // We found a process in a target that matches, but another thread - // might be in the process of launching a new process that will - // soon replace it, so get a shared pointer to the process so we - // can keep it alive. - ProcessSP process_sp (target_sp->GetProcessSP()); - // Now we have a shared pointer to the process that can't go away on us - // so we now make sure it was the same as the one passed in, and also make - // sure that our previous "process *" didn't get deleted and have a new - // "process *" created in its place with the same pointer. To verify this - // we make sure the process has our debugserver process ID. If we pass all - // of these tests, then we are sure that this process is the one we were - // looking for. - if (process_sp && process == process_sp.get() && process->m_debugserver_pid == debugserver_pid) + log->Printf("ProcessGDBRemote::%s(process = %p)", __FUNCTION__, static_cast<void *>(process_sp.get())); + if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid) + return handled; + + // Sleep for a half a second to make sure our inferior process has + // time to set its exit status before we set it incorrectly when + // both the debugserver and the inferior process shut down. + usleep(500000); + // If our process hasn't yet exited, debugserver might have died. + // If the process did exit, then we are reaping it. + const StateType state = process_sp->GetState(); + + if (state != eStateInvalid && state != eStateUnloaded && state != eStateExited && state != eStateDetached) + { + char error_str[1024]; + if (signo) { - // Sleep for a half a second to make sure our inferior process has - // time to set its exit status before we set it incorrectly when - // both the debugserver and the inferior process shut down. - usleep (500000); - // If our process hasn't yet exited, debugserver might have died. - // If the process did exit, the we are reaping it. - const StateType state = process->GetState(); - - if (process->m_debugserver_pid != LLDB_INVALID_PROCESS_ID && - state != eStateInvalid && - state != eStateUnloaded && - state != eStateExited && - state != eStateDetached) - { - char error_str[1024]; - if (signo) - { - const char *signal_cstr = process->GetUnixSignals()->GetSignalAsCString(signo); - if (signal_cstr) - ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); - else - ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); - } - else - { - ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); - } - - process->SetExitStatus (-1, error_str); - } - // Debugserver has exited we need to let our ProcessGDBRemote - // know that it no longer has a debugserver instance - process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + const char *signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo); + if (signal_cstr) + ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); + else + ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); } + else + { + ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", + exit_status); + } + + process_sp->SetExitStatus(-1, error_str); } - return true; + // Debugserver has exited we need to let our ProcessGDBRemote + // know that it no longer has a debugserver instance + process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + return handled; } void @@ -3756,7 +3778,7 @@ ProcessGDBRemote::StartAsyncThread () if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); - Mutex::Locker start_locker(m_async_thread_state_mutex); + std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); if (!m_async_thread.IsJoinable()) { // Create a thread that watches our internal state and controls which @@ -3778,7 +3800,7 @@ ProcessGDBRemote::StopAsyncThread () if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); - Mutex::Locker start_locker(m_async_thread_state_mutex); + std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); @@ -3838,7 +3860,7 @@ ProcessGDBRemote::AsyncThread (void *arg) { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); - if (process->m_async_listener.WaitForEvent (NULL, event_sp)) + if (process->m_async_listener_sp->WaitForEvent (NULL, event_sp)) { const uint32_t event_type = event_sp->GetType(); if (event_sp->BroadcasterIs (&process->m_async_broadcaster)) @@ -4153,6 +4175,7 @@ ProcessGDBRemote::GetExtendedInfoForThread (lldb::tid_t tid) packet << (char) (0x7d ^ 0x20); StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); @@ -4194,6 +4217,7 @@ ProcessGDBRemote::GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_addres packet << (char) (0x7d ^ 0x20); StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); @@ -4336,14 +4360,11 @@ struct GdbServerTargetInfo }; bool -ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp) +ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp, uint32_t &cur_reg_num, uint32_t ®_offset) { if (!feature_node) return false; - uint32_t cur_reg_num = 0; - uint32_t reg_offset = 0; - feature_node.ForEachChildElementWithName("reg", [&target_info, &dyn_reg_info, &cur_reg_num, ®_offset, &abi_sp](const XMLNode ®_node) -> bool { std::string gdb_group; std::string gdb_type; @@ -4520,7 +4541,7 @@ ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemot // return: 'true' on success // 'false' on failure bool -ProcessGDBRemote::GetGDBServerRegisterInfo () +ProcessGDBRemote::GetGDBServerRegisterInfo (ArchSpec &arch_to_use) { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) @@ -4599,9 +4620,16 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () return true; // Keep iterating through all children of the target_node }); + // Initialize these outside of ParseRegisters, since they should not be reset inside each include feature + uint32_t cur_reg_num = 0; + uint32_t reg_offset = 0; + + // Don't use Process::GetABI, this code gets called from DidAttach, and in that context we haven't + // set the Target's architecture yet, so the ABI is also potentially incorrect. + ABISP abi_to_use_sp = ABI::FindPlugin(arch_to_use); if (feature_node) { - ParseRegisters(feature_node, target_info, this->m_register_info, GetABI()); + ParseRegisters(feature_node, target_info, this->m_register_info, abi_to_use_sp, cur_reg_num, reg_offset); } for (const auto &include : target_info.includes) @@ -4619,10 +4647,10 @@ ProcessGDBRemote::GetGDBServerRegisterInfo () XMLNode include_feature_node = include_xml_document.GetRootElement("feature"); if (include_feature_node) { - ParseRegisters(include_feature_node, target_info, this->m_register_info, GetABI()); + ParseRegisters(include_feature_node, target_info, this->m_register_info, abi_to_use_sp, cur_reg_num, reg_offset); } } - this->m_register_info.Finalize(GetTarget().GetArchitecture()); + this->m_register_info.Finalize(arch_to_use); } } @@ -4784,25 +4812,14 @@ ProcessGDBRemote::GetLoadedModuleList (LoadedModuleInfoList & list) } lldb::ModuleSP -ProcessGDBRemote::LoadModuleAtAddress (const FileSpec &file, lldb::addr_t base_addr, bool value_is_offset) +ProcessGDBRemote::LoadModuleAtAddress (const FileSpec &file, lldb::addr_t link_map, + lldb::addr_t base_addr, bool value_is_offset) { - Target &target = m_process->GetTarget(); - ModuleList &modules = target.GetImages(); - ModuleSP module_sp; - - bool changed = false; - - ModuleSpec module_spec (file, target.GetArchitecture()); - if ((module_sp = modules.FindFirstModule (module_spec))) - { - module_sp->SetLoadAddress (target, base_addr, value_is_offset, changed); - } - else if ((module_sp = target.GetSharedModule (module_spec))) - { - module_sp->SetLoadAddress (target, base_addr, value_is_offset, changed); - } + DynamicLoader *loader = GetDynamicLoader(); + if (!loader) + return nullptr; - return module_sp; + return loader->LoadModuleAtAddress(file, link_map, base_addr, value_is_offset); } size_t @@ -4821,6 +4838,7 @@ ProcessGDBRemote::LoadModules (LoadedModuleInfoList &module_list) { std::string mod_name; lldb::addr_t mod_base; + lldb::addr_t link_map; bool mod_base_is_offset; bool valid = true; @@ -4830,15 +4848,12 @@ ProcessGDBRemote::LoadModules (LoadedModuleInfoList &module_list) if (!valid) continue; - // hack (cleaner way to get file name only?) (win/unix compat?) - size_t marker = mod_name.rfind ('/'); - if (marker == std::string::npos) - marker = 0; - else - marker += 1; + if (!modInfo.get_link_map (link_map)) + link_map = LLDB_INVALID_ADDRESS; - FileSpec file (mod_name.c_str()+marker, true); - lldb::ModuleSP module_sp = LoadModuleAtAddress (file, mod_base, mod_base_is_offset); + FileSpec file (mod_name.c_str(), true); + lldb::ModuleSP module_sp = LoadModuleAtAddress (file, link_map, mod_base, + mod_base_is_offset); if (module_sp.get()) new_modules.Append (module_sp); @@ -4846,7 +4861,30 @@ ProcessGDBRemote::LoadModules (LoadedModuleInfoList &module_list) if (new_modules.GetSize() > 0) { + ModuleList removed_modules; Target &target = GetTarget(); + ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + + for (size_t i = 0; i < loaded_modules.GetSize(); ++i) + { + const lldb::ModuleSP loaded_module = loaded_modules.GetModuleAtIndex(i); + + bool found = false; + for (size_t j = 0; j < new_modules.GetSize(); ++j) + { + if (new_modules.GetModuleAtIndex(j).get() == loaded_module.get()) + found = true; + } + + // The main executable will never be included in libraries-svr4, don't remove it + if (!found && loaded_module.get() != target.GetExecutableModulePointer()) + { + removed_modules.Append (loaded_module); + } + } + + loaded_modules.Remove (removed_modules); + m_process->GetTarget().ModulesDidUnload (removed_modules, false); new_modules.ForEach ([&target](const lldb::ModuleSP module_sp) -> bool { @@ -4862,13 +4900,11 @@ ProcessGDBRemote::LoadModules (LoadedModuleInfoList &module_list) return false; }); - ModuleList &loaded_modules = m_process->GetTarget().GetImages(); loaded_modules.AppendIfNeeded (new_modules); m_process->GetTarget().ModulesDidLoad (new_modules); } return new_modules.GetSize(); - } size_t @@ -5230,11 +5266,9 @@ public: class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { public: - CommandObjectMultiwordProcessGDBRemote (CommandInterpreter &interpreter) : - CommandObjectMultiword (interpreter, - "process plugin", - "A set of commands for operating on a ProcessGDBRemote process.", - "process plugin <subcommand> [<subcommand-options>]") + CommandObjectMultiwordProcessGDBRemote(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "process plugin", "Commands for operating on a ProcessGDBRemote process.", + "process plugin <subcommand> [<subcommand-options>]") { LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessGDBRemotePacket (interpreter))); } diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index b48edd836a743..6d373965fc42d 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -14,6 +14,7 @@ // C++ Includes #include <atomic> #include <map> +#include <mutex> #include <string> #include <vector> @@ -45,13 +46,13 @@ class ThreadGDBRemote; class ProcessGDBRemote : public Process { public: - ProcessGDBRemote(lldb::TargetSP target_sp, Listener &listener); + ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); ~ProcessGDBRemote() override; static lldb::ProcessSP CreateInstance (lldb::TargetSP target_sp, - Listener &listener, + lldb::ListenerSP listener_sp, const FileSpec *crash_file_path); static void @@ -279,12 +280,12 @@ protected: GDBRemoteCommunicationClient m_gdb_comm; std::atomic<lldb::pid_t> m_debugserver_pid; std::vector<StringExtractorGDBRemote> m_stop_packet_stack; // The stop packet stack replaces the last stop packet variable - Mutex m_last_stop_packet_mutex; + std::recursive_mutex m_last_stop_packet_mutex; GDBRemoteDynamicRegisterInfo m_register_info; Broadcaster m_async_broadcaster; - Listener m_async_listener; + lldb::ListenerSP m_async_listener_sp; HostThread m_async_thread; - Mutex m_async_thread_state_mutex; + std::recursive_mutex m_async_thread_state_mutex; typedef std::vector<lldb::tid_t> tid_collection; typedef std::vector< std::pair<lldb::tid_t,int> > tid_sig_collection; typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap; @@ -405,11 +406,8 @@ protected: AsyncThread (void *arg); static bool - MonitorDebugserverProcess (void *callback_baton, - lldb::pid_t pid, - bool exited, - int signo, - int exit_status); + MonitorDebugserverProcess(std::weak_ptr<ProcessGDBRemote> process_wp, lldb::pid_t pid, bool exited, int signo, + int exit_status); lldb::StateType SetThreadStopInfo (StringExtractor& stop_packet); @@ -461,14 +459,15 @@ protected: // Query remote GDBServer for register information bool - GetGDBServerRegisterInfo (); + GetGDBServerRegisterInfo (ArchSpec &arch); // Query remote GDBServer for a detailed loaded library list Error GetLoadedModuleList (LoadedModuleInfoList &); lldb::ModuleSP - LoadModuleAtAddress (const FileSpec &file, lldb::addr_t base_addr, bool value_is_offset); + LoadModuleAtAddress (const FileSpec &file, lldb::addr_t link_map, lldb::addr_t base_addr, + bool value_is_offset); private: //------------------------------------------------------------------ diff --git a/source/Plugins/Process/mach-core/Makefile b/source/Plugins/Process/mach-core/Makefile deleted file mode 100644 index 6db8498722671..0000000000000 --- a/source/Plugins/Process/mach-core/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- source/Plugins/Process/mach-core/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 := lldbPluginProcessMachCore -BUILD_ARCHIVE = 1 - -include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/source/Plugins/Process/mach-core/ProcessMachCore.cpp index b199ec6063678..6bf198ca2f37d 100644 --- a/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -18,13 +18,15 @@ // Other libraries and framework includes #include "lldb/Core/DataBuffer.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" @@ -64,7 +66,7 @@ ProcessMachCore::Terminate() lldb::ProcessSP -ProcessMachCore::CreateInstance (lldb::TargetSP target_sp, Listener &listener, const FileSpec *crash_file) +ProcessMachCore::CreateInstance (lldb::TargetSP target_sp, ListenerSP listener_sp, const FileSpec *crash_file) { lldb::ProcessSP process_sp; if (crash_file) @@ -80,7 +82,7 @@ ProcessMachCore::CreateInstance (lldb::TargetSP target_sp, Listener &listener, c if (ObjectFileMachO::ParseHeader(data, &data_offset, mach_header)) { if (mach_header.filetype == llvm::MachO::MH_CORE) - process_sp.reset(new ProcessMachCore (target_sp, listener, *crash_file)); + process_sp.reset(new ProcessMachCore (target_sp, listener_sp, *crash_file)); } } @@ -121,14 +123,15 @@ ProcessMachCore::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_nam //---------------------------------------------------------------------- // ProcessMachCore constructor //---------------------------------------------------------------------- -ProcessMachCore::ProcessMachCore(lldb::TargetSP target_sp, Listener &listener, const FileSpec &core_file) : - Process (target_sp, listener), - m_core_aranges (), - m_core_module_sp (), - m_core_file (core_file), - m_dyld_addr (LLDB_INVALID_ADDRESS), - m_mach_kernel_addr (LLDB_INVALID_ADDRESS), - m_dyld_plugin_name () +ProcessMachCore::ProcessMachCore(lldb::TargetSP target_sp, ListenerSP listener_sp, const FileSpec &core_file) + : Process(target_sp, listener_sp), + m_core_aranges(), + m_core_range_infos(), + m_core_module_sp(), + m_core_file(core_file), + m_dyld_addr(LLDB_INVALID_ADDRESS), + m_mach_kernel_addr(LLDB_INVALID_ADDRESS), + m_dyld_plugin_name() { } @@ -163,6 +166,7 @@ ProcessMachCore::GetPluginVersion() bool ProcessMachCore::GetDynamicLoaderAddress (lldb::addr_t addr) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER | LIBLLDB_LOG_PROCESS)); llvm::MachO::mach_header header; Error error; if (DoReadMemory (addr, &header, sizeof(header), error) != sizeof(header)) @@ -194,6 +198,8 @@ ProcessMachCore::GetDynamicLoaderAddress (lldb::addr_t addr) case llvm::MachO::MH_DYLINKER: //printf("0x%16.16" PRIx64 ": file_type = MH_DYLINKER\n", vaddr); // Address of dyld "struct mach_header" in the core file + if (log) + log->Printf ("ProcessMachCore::GetDynamicLoaderAddress found a user process dyld binary image at 0x%" PRIx64, addr); m_dyld_addr = addr; return true; @@ -203,6 +209,8 @@ ProcessMachCore::GetDynamicLoaderAddress (lldb::addr_t addr) // is NOT set. If it isn't, then we have a mach_kernel. if ((header.flags & llvm::MachO::MH_DYLDLINK) == 0) { + if (log) + log->Printf ("ProcessMachCore::GetDynamicLoaderAddress found a mach kernel binary image at 0x%" PRIx64, addr); // Address of the mach kernel "struct mach_header" in the core file. m_mach_kernel_addr = addr; return true; @@ -219,6 +227,7 @@ ProcessMachCore::GetDynamicLoaderAddress (lldb::addr_t addr) Error ProcessMachCore::DoLoadCore () { + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER | LIBLLDB_LOG_PROCESS)); Error error; if (!m_core_module_sp) { @@ -297,11 +306,21 @@ ProcessMachCore::DoLoadCore () { m_core_aranges.Append(range_entry); } + // Some core files don't fill in the permissions correctly. If that is the case + // assume read + execute so clients don't think the memory is not readable, + // or executable. The memory isn't writable since this plug-in doesn't implement + // DoWriteMemory. + uint32_t permissions = section->GetPermissions(); + if (permissions == 0) + permissions = lldb::ePermissionsReadable | lldb::ePermissionsExecutable; + m_core_range_infos.Append( + VMRangeToPermissions::Entry(section_vm_addr, section->GetByteSize(), permissions)); } } if (!ranges_are_sorted) { m_core_aranges.Sort(); + m_core_range_infos.Sort(); } if (m_dyld_addr == LLDB_INVALID_ADDRESS || m_mach_kernel_addr == LLDB_INVALID_ADDRESS) @@ -314,9 +333,7 @@ ProcessMachCore::DoLoadCore () // later if both are present. const size_t num_core_aranges = m_core_aranges.GetSize(); - for (size_t i = 0; - i < num_core_aranges && (m_dyld_addr == LLDB_INVALID_ADDRESS || m_mach_kernel_addr == LLDB_INVALID_ADDRESS); - ++i) + for (size_t i = 0; i < num_core_aranges; ++i) { const VMRangeToFileOffset::Entry *entry = m_core_aranges.GetEntryAtIndex(i); lldb::addr_t section_vm_addr_start = entry->GetRangeBase(); @@ -330,16 +347,58 @@ ProcessMachCore::DoLoadCore () } } + + if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) + { + // In the case of multiple kernel images found in the core file via exhaustive + // search, we may not pick the correct one. See if the DynamicLoaderDarwinKernel's + // search heuristics might identify the correct one. + // Most of the time, I expect the address from SearchForDarwinKernel() will be the + // same as the address we found via exhaustive search. + + if (GetTarget().GetArchitecture().IsValid() == false && m_core_module_sp.get()) + { + GetTarget().SetArchitecture (m_core_module_sp->GetArchitecture()); + } + + // SearchForDarwinKernel will end up calling back into this this class in the GetImageInfoAddress + // method which will give it the m_mach_kernel_addr/m_dyld_addr it already has. Save that aside + // and set m_mach_kernel_addr/m_dyld_addr to an invalid address temporarily so + // DynamicLoaderDarwinKernel does a real search for the kernel using its own heuristics. + + addr_t saved_mach_kernel_addr = m_mach_kernel_addr; + addr_t saved_user_dyld_addr = m_dyld_addr; + m_mach_kernel_addr = LLDB_INVALID_ADDRESS; + m_dyld_addr = LLDB_INVALID_ADDRESS; + + addr_t better_kernel_address = DynamicLoaderDarwinKernel::SearchForDarwinKernel (this); + + m_mach_kernel_addr = saved_mach_kernel_addr; + m_dyld_addr = saved_user_dyld_addr; + + if (better_kernel_address != LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("ProcessMachCore::DoLoadCore: Using the kernel address from DynamicLoaderDarwinKernel"); + m_mach_kernel_addr = better_kernel_address; + } + } + + // If we found both a user-process dyld and a kernel binary, we need to decide // which to prefer. if (GetCorefilePreference() == eKernelCorefile) { if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { + if (log) + log->Printf ("ProcessMachCore::DoLoadCore: Using kernel corefile image at 0x%" PRIx64, m_mach_kernel_addr); m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); } else if (m_dyld_addr != LLDB_INVALID_ADDRESS) { + if (log) + log->Printf ("ProcessMachCore::DoLoadCore: Using user process dyld image at 0x%" PRIx64, m_dyld_addr); m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); } } @@ -347,10 +406,14 @@ ProcessMachCore::DoLoadCore () { if (m_dyld_addr != LLDB_INVALID_ADDRESS) { + if (log) + log->Printf ("ProcessMachCore::DoLoadCore: Using user process dyld image at 0x%" PRIx64, m_dyld_addr); m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); } else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { + if (log) + log->Printf ("ProcessMachCore::DoLoadCore: Using kernel corefile image at 0x%" PRIx64, m_mach_kernel_addr); m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); } } @@ -450,25 +513,95 @@ size_t ProcessMachCore::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) { ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + size_t bytes_read = 0; if (core_objfile) { - const VMRangeToFileOffset::Entry *core_memory_entry = m_core_aranges.FindEntryThatContains (addr); - if (core_memory_entry) + //---------------------------------------------------------------------- + // Segments are not always contiguous in mach-o core files. We have core + // files that have segments like: + // Address Size File off File size + // ---------- ---------- ---------- ---------- + // LC_SEGMENT 0x000f6000 0x00001000 0x1d509ee8 0x00001000 --- --- 0 0x00000000 __TEXT + // LC_SEGMENT 0x0f600000 0x00100000 0x1d50aee8 0x00100000 --- --- 0 0x00000000 __TEXT + // LC_SEGMENT 0x000f7000 0x00001000 0x1d60aee8 0x00001000 --- --- 0 0x00000000 __TEXT + // + // Any if the user executes the following command: + // + // (lldb) mem read 0xf6ff0 + // + // We would attempt to read 32 bytes from 0xf6ff0 but would only + // get 16 unless we loop through consecutive memory ranges that are + // contiguous in the address space, but not in the file data. + //---------------------------------------------------------------------- + while (bytes_read < size) { - const addr_t offset = addr - core_memory_entry->GetRangeBase(); - const addr_t bytes_left = core_memory_entry->GetRangeEnd() - addr; - size_t bytes_to_read = size; - if (bytes_to_read > bytes_left) - bytes_to_read = bytes_left; - return core_objfile->CopyData (core_memory_entry->data.GetRangeBase() + offset, bytes_to_read, buf); + const addr_t curr_addr = addr + bytes_read; + const VMRangeToFileOffset::Entry *core_memory_entry = m_core_aranges.FindEntryThatContains(curr_addr); + + if (core_memory_entry) + { + const addr_t offset = curr_addr - core_memory_entry->GetRangeBase(); + const addr_t bytes_left = core_memory_entry->GetRangeEnd() - curr_addr; + const size_t bytes_to_read = std::min(size - bytes_read, (size_t)bytes_left); + const size_t curr_bytes_read = core_objfile->CopyData(core_memory_entry->data.GetRangeBase() + offset, + bytes_to_read, (char *)buf + bytes_read); + if (curr_bytes_read == 0) + break; + bytes_read += curr_bytes_read; + } + else + { + // Only set the error if we didn't read any bytes + if (bytes_read == 0) + error.SetErrorStringWithFormat("core file does not contain 0x%" PRIx64, curr_addr); + break; + } } - else + } + + return bytes_read; +} + +Error +ProcessMachCore::GetMemoryRegionInfo(addr_t load_addr, MemoryRegionInfo ®ion_info) +{ + region_info.Clear(); + const VMRangeToPermissions::Entry *permission_entry = m_core_range_infos.FindEntryThatContainsOrFollows(load_addr); + if (permission_entry) + { + if (permission_entry->Contains(load_addr)) { - error.SetErrorStringWithFormat ("core file does not contain 0x%" PRIx64, addr); + region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase()); + region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd()); + const Flags permissions(permission_entry->data); + region_info.SetReadable(permissions.Test(ePermissionsReadable) ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetWritable(permissions.Test(ePermissionsWritable) ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetExecutable(permissions.Test(ePermissionsExecutable) ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eYes); } + else if (load_addr < permission_entry->GetRangeBase()) + { + region_info.GetRange().SetRangeBase(load_addr); + region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase()); + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + } + return Error(); } - return 0; + + region_info.GetRange().SetRangeBase(load_addr); + region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + return Error(); } void diff --git a/source/Plugins/Process/mach-core/ProcessMachCore.h b/source/Plugins/Process/mach-core/ProcessMachCore.h index 2de0b772370c5..21a0083b2d124 100644 --- a/source/Plugins/Process/mach-core/ProcessMachCore.h +++ b/source/Plugins/Process/mach-core/ProcessMachCore.h @@ -30,14 +30,14 @@ public: // Constructors and Destructors //------------------------------------------------------------------ ProcessMachCore(lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener, const lldb_private::FileSpec &core_file); ~ProcessMachCore() override; static lldb::ProcessSP CreateInstance (lldb::TargetSP target_sp, - lldb_private::Listener &listener, + lldb::ListenerSP listener, const lldb_private::FileSpec *crash_file_path); static void @@ -103,7 +103,10 @@ public: size_t DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; - + + lldb_private::Error + GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo ®ion_info) override; + lldb::addr_t GetImageInfoAddress () override; @@ -131,7 +134,7 @@ private: /// /// If a core file contains both a kernel binary and a user-process /// dynamic loader, lldb needs to pick one over the other. This could - /// be a kernel corefile that happens to have a coyp of dyld in its + /// be a kernel corefile that happens to have a copy of dyld in its /// memory. Or it could be a user process coredump of lldb while doing /// kernel debugging - so a copy of the kernel is in its heap. This /// should become a setting so it can be over-ridden when necessary. @@ -150,8 +153,10 @@ private: //------------------------------------------------------------------ typedef lldb_private::Range<lldb::addr_t, lldb::addr_t> FileRange; typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, FileRange> VMRangeToFileOffset; + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, uint32_t> VMRangeToPermissions; VMRangeToFileOffset m_core_aranges; + VMRangeToPermissions m_core_range_infos; lldb::ModuleSP m_core_module_sp; lldb_private::FileSpec m_core_file; lldb::addr_t m_dyld_addr; |