summaryrefslogtreecommitdiff
path: root/source/Plugins/Process/Linux
diff options
context:
space:
mode:
Diffstat (limited to 'source/Plugins/Process/Linux')
-rw-r--r--source/Plugins/Process/Linux/CMakeLists.txt2
-rw-r--r--source/Plugins/Process/Linux/Makefile17
-rw-r--r--source/Plugins/Process/Linux/NativeProcessLinux.cpp693
-rw-r--r--source/Plugins/Process/Linux/NativeProcessLinux.h69
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp43
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h5
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp44
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h5
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp6
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.cpp716
-rw-r--r--source/Plugins/Process/Linux/NativeRegisterContextLinux_s390x.h141
-rw-r--r--source/Plugins/Process/Linux/NativeThreadLinux.cpp143
-rw-r--r--source/Plugins/Process/Linux/NativeThreadLinux.h28
-rw-r--r--source/Plugins/Process/Linux/SingleStepCheck.cpp177
-rw-r--r--source/Plugins/Process/Linux/SingleStepCheck.h41
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 &reg_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(&regs, sizeof(regs));
+ if (error.Fail())
+ return error;
+
+ uint8_t *src = (uint8_t *)&regs + 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 &reg_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(&regs, sizeof(regs));
+ if (error.Fail())
+ return error;
+
+ uint8_t *dst = (uint8_t *)&regs + 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(&regs, 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 &reg_value) override;
+
+ Error
+ WriteRegister(const RegisterInfo *reg_info, const RegisterValue &reg_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_