diff options
Diffstat (limited to 'source/Plugins/Process/Linux')
15 files changed, 1554 insertions, 576 deletions
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_ |