aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
diff options
context:
space:
mode:
authorPavel Labath <labath@google.com>2016-07-21 14:54:03 +0000
committerPavel Labath <labath@google.com>2016-07-21 14:54:03 +0000
commit5ad891f7193b2d7cc6578c2cbffe7d3a04e4617b (patch)
treefade2b50428c5fbf65bb88b92e583301c890e8a0 /lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
parent4caefdf834b20104ddaabe61221de469b52e6b0e (diff)
downloadllvm-5ad891f7193b2d7cc6578c2cbffe7d3a04e4617b.zip
llvm-5ad891f7193b2d7cc6578c2cbffe7d3a04e4617b.tar.gz
llvm-5ad891f7193b2d7cc6578c2cbffe7d3a04e4617b.tar.bz2
Unify process launching code on linux
Summary: We've had two copies of code for launching processes: - one in NativeProcessLinux, used for launching debugged processes - one in ProcessLauncherAndroid, used on android for launching all other kinds of processes These have over time acquired support for various launch options, but neither supported all of them. I now replace them with a single implementation ProcessLauncherLinux, which supports all the options the individual versions supported and set it to be used to launch all processes on linux. This also works around the ETXTBSY issue on android when the process is started from the platform instance, as that used to go through the version which did not contain the workaround. Reviewers: tberghammer Subscribers: tberghammer, danalbert, srhines, lldb-commits Differential Revision: https://reviews.llvm.org/D22457 llvm-svn: 276288
Diffstat (limited to 'lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp')
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp247
1 files changed, 19 insertions, 228 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index b384230..ae32a77 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -29,9 +29,11 @@
#include "lldb/Core/RegisterValue.h"
#include "lldb/Core/State.h"
#include "lldb/Host/Host.h"
+#include "lldb/Host/HostProcess.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Host/common/NativeBreakpoint.h"
#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/linux/ProcessLauncherLinux.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/ProcessLaunchInfo.h"
@@ -60,8 +62,6 @@
#include "lldb/Host/linux/Uio.h"
#include "lldb/Host/android/Android.h"
-#define LLDB_PERSONALITY_GET_CURRENT_SETTINGS 0xffffffff
-
// Support hardware breakpoints in case it has not been defined
#ifndef TRAP_HWBKPT
#define TRAP_HWBKPT 4
@@ -130,37 +130,6 @@ ResolveProcessArchitecture(lldb::pid_t pid, ArchSpec &arch)
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
-};
-
-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)
{
@@ -411,101 +380,6 @@ NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error
Attach(pid, error);
}
-void
-NativeProcessLinux::ChildFunc(const ProcessLaunchInfo &info)
-{
- // Start tracing this child that is about to exec.
- if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1)
- ExitChildAbnormally(ePtraceFailed);
-
- // Do not inherit setgid powers.
- if (setgid(getgid()) != 0)
- ExitChildAbnormally(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 (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))
- {
- 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)
- ExitChildAbnormally(eSetSigMaskFailed);
-
- const char **argv = info.GetArguments().GetConstArgumentVector();
-
- // 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);
-
- // 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);
-}
-
Error
NativeProcessLinux::LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch_info)
{
@@ -516,34 +390,11 @@ NativeProcessLinux::LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch
SetState(eStateLaunching);
- lldb_utility::PseudoTerminal terminal;
- const size_t err_len = 1024;
- char err_str[err_len];
- lldb::pid_t pid;
-
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);
+ ::pid_t pid = ProcessLauncherLinux().LaunchProcess(launch_info, error).GetProcessId();
+ if (error.Fail())
return error;
- }
-
- // 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);
-
- // 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();
-
- ChildFunc(launch_info);
- }
Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
@@ -563,53 +414,6 @@ NativeProcessLinux::LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch
return error;
}
- else if (WIFEXITED(status))
- {
- auto p = DecodeChildExitCode(WEXITSTATUS(status));
- Error child_error(p.second, eErrorTypePOSIX);
- const char *failure_reason;
- switch (p.first)
- {
- case ePtraceFailed:
- failure_reason = "Child ptrace failed";
- break;
- case eDupStdinFailed:
- failure_reason = "Child open stdin failed";
- break;
- case eDupStdoutFailed:
- failure_reason = "Child open stdout failed";
- break;
- case eDupStderrFailed:
- failure_reason = "Child open stderr failed";
- break;
- case eChdirFailed:
- failure_reason = "Child failed to set working directory";
- break;
- case eExecFailed:
- failure_reason = "Child exec failed";
- break;
- case eSetGidFailed:
- failure_reason = "Child setgid failed";
- break;
- case eSetSigMaskFailed:
- 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)
- {
- log->Printf ("NativeProcessLinux::%s inferior exited with status %d before issuing a STOP",
- __FUNCTION__,
- WEXITSTATUS(status));
- }
-
- // Mark the inferior as invalid.
- // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
- SetState (StateType::eStateInvalid);
-
- return error;
- }
assert(WIFSTOPPED(status) && (wpid == static_cast< ::pid_t> (pid)) &&
"Could not sync with inferior process.");
@@ -632,29 +436,30 @@ NativeProcessLinux::LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch
// Release the master terminal descriptor and pass it off to the
// NativeProcessLinux instance. Similarly stash the inferior pid.
- m_terminal_fd = terminal.ReleaseMasterFileDescriptor();
+ m_terminal_fd = launch_info.GetPTY().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
- // descriptor to read from).
- error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
- if (error.Fail())
+ if (m_terminal_fd != -1)
{
- if (log)
- log->Printf ("NativeProcessLinux::%s inferior EnsureFDFlags failed for ensuring terminal O_NONBLOCK setting: %s",
- __FUNCTION__, error.AsCString ());
+ error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
+ if (error.Fail())
+ {
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s inferior EnsureFDFlags failed for ensuring terminal O_NONBLOCK setting: %s",
+ __FUNCTION__, error.AsCString());
- // Mark the inferior as invalid.
- // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
- SetState (StateType::eStateInvalid);
+ // Mark the inferior as invalid.
+ // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
+ SetState(StateType::eStateInvalid);
- return error;
+ return error;
+ }
}
if (log)
- log->Printf ("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, pid);
+ log->Printf("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, uint64_t(pid));
ResolveProcessArchitecture(m_pid, m_arch);
NativeThreadLinuxSP thread_sp = AddThread(pid);
@@ -2516,20 +2321,6 @@ NativeProcessLinux::Detach(lldb::tid_t tid)
}
bool
-NativeProcessLinux::DupDescriptor(const FileSpec &file_spec, int fd, int flags)
-{
- int target_fd = open(file_spec.GetCString(), flags, 0666);
-
- if (target_fd == -1)
- return false;
-
- if (dup2(target_fd, fd) == -1)
- return false;
-
- return (close(target_fd) == -1) ? false : true;
-}
-
-bool
NativeProcessLinux::HasThreadNoLock (lldb::tid_t thread_id)
{
for (auto thread_sp : m_threads)