diff options
| author | Pavel Labath <labath@google.com> | 2016-07-21 14:54:03 +0000 |
|---|---|---|
| committer | Pavel Labath <labath@google.com> | 2016-07-21 14:54:03 +0000 |
| commit | 5ad891f7193b2d7cc6578c2cbffe7d3a04e4617b (patch) | |
| tree | fade2b50428c5fbf65bb88b92e583301c890e8a0 /lldb/source/Host | |
| parent | 4caefdf834b20104ddaabe61221de469b52e6b0e (diff) | |
| download | bcm5719-llvm-5ad891f7193b2d7cc6578c2cbffe7d3a04e4617b.tar.gz bcm5719-llvm-5ad891f7193b2d7cc6578c2cbffe7d3a04e4617b.zip | |
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/Host')
| -rw-r--r-- | lldb/source/Host/CMakeLists.txt | 25 | ||||
| -rw-r--r-- | lldb/source/Host/android/ProcessLauncherAndroid.cpp | 108 | ||||
| -rw-r--r-- | lldb/source/Host/common/Host.cpp | 8 | ||||
| -rw-r--r-- | lldb/source/Host/linux/ProcessLauncherLinux.cpp | 213 |
4 files changed, 226 insertions, 128 deletions
diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index 776fcfb44ff..94f6975b91c 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -120,24 +120,17 @@ else() add_host_subdirectory(android android/HostInfoAndroid.cpp android/LibcGlue.cpp - android/ProcessLauncherAndroid.cpp - linux/AbstractSocket.cpp - linux/Host.cpp - linux/HostInfoLinux.cpp - linux/HostThreadLinux.cpp - linux/LibcGlue.cpp - linux/ThisThread.cpp - ) - else() - add_host_subdirectory(linux - linux/AbstractSocket.cpp - linux/Host.cpp - linux/HostInfoLinux.cpp - linux/HostThreadLinux.cpp - linux/LibcGlue.cpp - linux/ThisThread.cpp ) endif() + add_host_subdirectory(linux + linux/AbstractSocket.cpp + linux/Host.cpp + linux/HostInfoLinux.cpp + linux/HostThreadLinux.cpp + linux/LibcGlue.cpp + linux/ProcessLauncherLinux.cpp + linux/ThisThread.cpp + ) elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") add_host_subdirectory(freebsd diff --git a/lldb/source/Host/android/ProcessLauncherAndroid.cpp b/lldb/source/Host/android/ProcessLauncherAndroid.cpp deleted file mode 100644 index 24eebc8c030..00000000000 --- a/lldb/source/Host/android/ProcessLauncherAndroid.cpp +++ /dev/null @@ -1,108 +0,0 @@ -//===-- ProcessLauncherAndroid.cpp ------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Host/FileSpec.h" -#include "lldb/Host/Host.h" -#include "lldb/Host/HostProcess.h" -#include "lldb/Host/android/ProcessLauncherAndroid.h" - -#include "lldb/Target/ProcessLaunchInfo.h" - -#include <limits.h> - -using namespace lldb; -using namespace lldb_private; - -static bool -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; -} - -// If there is no PATH variable specified inside the environment then set the path to /system/bin. -// It is required because the default path used by execve() is wrong on android. -static void -FixupEnvironment(Args& env) -{ - static const char* path = "PATH="; - static const int path_len = ::strlen(path); - for (const char** args = env.GetConstArgumentVector(); *args; ++args) - if (::strncmp(path, *args, path_len) == 0) - return; - env.AppendArgument("PATH=/system/bin"); -} - -HostProcess -ProcessLauncherAndroid::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) -{ - // TODO: Handle other launch parameters specified in launc_info - - char exe_path[PATH_MAX]; - launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); - - lldb::pid_t pid = ::fork(); - if (pid == static_cast<lldb::pid_t>(-1)) - { - // Fork failed - error.SetErrorStringWithFormat("Fork failed with error message: %s", strerror(errno)); - return HostProcess(LLDB_INVALID_PROCESS_ID); - } - else if (pid == 0) - { - if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO)) { - FileSpec file_spec = file_action->GetFileSpec(); - if (file_spec) - if (!DupDescriptor(file_spec, STDIN_FILENO, O_RDONLY)) - exit(-1); - } - - if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDOUT_FILENO)) { - FileSpec file_spec = file_action->GetFileSpec(); - if (file_spec) - if (!DupDescriptor(file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) - exit(-1); - } - - if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDERR_FILENO)) { - FileSpec file_spec = file_action->GetFileSpec(); - if (file_spec) - if (!DupDescriptor(file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC)) - exit(-1); - } - - // Child process - const char **argv = launch_info.GetArguments().GetConstArgumentVector(); - - Args env = launch_info.GetEnvironmentEntries(); - FixupEnvironment(env); - const char **envp = env.GetConstArgumentVector(); - - FileSpec working_dir = launch_info.GetWorkingDirectory(); - if (working_dir) - { - if (::chdir(working_dir.GetCString()) != 0) - exit(-1); - } - - execve(argv[0], - const_cast<char *const *>(argv), - const_cast<char *const *>(envp)); - exit(-1); - } - - return HostProcess(pid); -} diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 656caa5e0d1..5a02c33be83 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -66,8 +66,8 @@ #if defined(_WIN32) #include "lldb/Host/windows/ProcessLauncherWindows.h" -#elif defined(__ANDROID__) || defined(__ANDROID_NDK__) -#include "lldb/Host/android/ProcessLauncherAndroid.h" +#elif defined(__linux__) +#include "lldb/Host/linux/ProcessLauncherLinux.h" #else #include "lldb/Host/posix/ProcessLauncherPosix.h" #endif @@ -1009,8 +1009,8 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info) std::unique_ptr<ProcessLauncher> delegate_launcher; #if defined(_WIN32) delegate_launcher.reset(new ProcessLauncherWindows()); -#elif defined(__ANDROID__) || defined(__ANDROID_NDK__) - delegate_launcher.reset(new ProcessLauncherAndroid()); +#elif defined(__linux__) + delegate_launcher.reset(new ProcessLauncherLinux()); #else delegate_launcher.reset(new ProcessLauncherPosix()); #endif diff --git a/lldb/source/Host/linux/ProcessLauncherLinux.cpp b/lldb/source/Host/linux/ProcessLauncherLinux.cpp new file mode 100644 index 00000000000..2e5f4e5e822 --- /dev/null +++ b/lldb/source/Host/linux/ProcessLauncherLinux.cpp @@ -0,0 +1,213 @@ +//===-- ProcessLauncherLinux.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/linux/ProcessLauncherLinux.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Target/ProcessLaunchInfo.h" + +#include <limits.h> +#include <sys/personality.h> +#include <sys/ptrace.h> +#include <sys/wait.h> + +#include <sstream> + +using namespace lldb; +using namespace lldb_private; + +static void +FixupEnvironment(Args &env) +{ +#ifdef __ANDROID_NDK__ + // If there is no PATH variable specified inside the environment then set the path to /system/bin. + // It is required because the default path used by execve() is wrong on android. + static const char *path = "PATH="; + static const int path_len = ::strlen(path); + for (const char **args = env.GetConstArgumentVector(); *args; ++args) + if (::strncmp(path, *args, path_len) == 0) + return; + env.AppendArgument("PATH=/system/bin"); +#endif +} + +static void LLVM_ATTRIBUTE_NORETURN +ExitWithError(int error_fd, const char *operation) +{ + std::ostringstream os; + os << operation << " failed: " << strerror(errno); + write(error_fd, os.str().data(), os.str().size()); + close(error_fd); + _exit(1); +} + +static void +DupDescriptor(int error_fd, const FileSpec &file_spec, int fd, int flags) +{ + int target_fd = ::open(file_spec.GetCString(), flags, 0666); + + if (target_fd == -1) + ExitWithError(error_fd, "DupDescriptor-open"); + + if (target_fd == fd) + return; + + if (::dup2(target_fd, fd) == -1) + ExitWithError(error_fd, "DupDescriptor-dup2"); + + ::close(target_fd); + return; +} + +static void LLVM_ATTRIBUTE_NORETURN +ChildFunc(int error_fd, const ProcessLaunchInfo &info) +{ + // First, make sure we disable all logging. If we are logging to stdout, our logs can be + // mistaken for inferior output. + Log::DisableAllLogChannels(nullptr); + + // Do not inherit setgid powers. + if (setgid(getgid()) != 0) + ExitWithError(error_fd, "setgid"); + + if (info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)) + { + if (setpgid(0, 0) != 0) + ExitWithError(error_fd, "setpgid"); + } + + for (size_t i = 0; i < info.GetNumFileActions(); ++i) + { + const FileAction &action = *info.GetFileActionAtIndex(i); + switch (action.GetAction()) + { + case FileAction::eFileActionClose: + if (close(action.GetFD()) != 0) + ExitWithError(error_fd, "close"); + break; + case FileAction::eFileActionDuplicate: + if (dup2(action.GetFD(), action.GetActionArgument()) == -1) + ExitWithError(error_fd, "dup2"); + break; + case FileAction::eFileActionOpen: + DupDescriptor(error_fd, action.GetFileSpec(), action.GetFD(), action.GetActionArgument()); + break; + case FileAction::eFileActionNone: + break; + } + } + + const char **argv = info.GetArguments().GetConstArgumentVector(); + + // Change working directory + if (info.GetWorkingDirectory() && 0 != ::chdir(info.GetWorkingDirectory().GetCString())) + ExitWithError(error_fd, "chdir"); + + // Disable ASLR if requested. + if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) + { + const unsigned long personality_get_current = 0xffffffff; + int value = personality(personality_get_current); + if (value == -1) + ExitWithError(error_fd, "personality get"); + + value = personality(ADDR_NO_RANDOMIZE | value); + if (value == -1) + ExitWithError(error_fd, "personality set"); + } + + Args env = info.GetEnvironmentEntries(); + FixupEnvironment(env); + const char **envp = env.GetConstArgumentVector(); + + // 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) + ExitWithError(error_fd, "pthread_sigmask"); + + if (info.GetFlags().Test(eLaunchFlagDebug)) + { + // HACK: + // Close everything besides stdin, stdout, and stderr that has no file + // action to avoid leaking. Only do this when debugging, as elsewhere we actually rely on + // passing open descriptors to child processes. + for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) + if (!info.GetFileActionForFD(fd) && fd != error_fd) + close(fd); + + // Start tracing this child that is about to exec. + if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) + ExitWithError(error_fd, "ptrace"); + } + + // 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. + ExitWithError(error_fd, "execve"); +} + +HostProcess +ProcessLauncherLinux::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + char exe_path[PATH_MAX]; + launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + + // A pipe used by the child process to report errors. + PipePosix pipe; + const bool child_processes_inherit = false; + error = pipe.CreateNew(child_processes_inherit); + if (error.Fail()) + return HostProcess(); + + ::pid_t pid = ::fork(); + if (pid == -1) + { + // Fork failed + error.SetErrorStringWithFormat("Fork failed with error message: %s", strerror(errno)); + return HostProcess(LLDB_INVALID_PROCESS_ID); + } + if (pid == 0) + { + // child process + pipe.CloseReadFileDescriptor(); + ChildFunc(pipe.ReleaseWriteFileDescriptor(), launch_info); + } + + // parent process + + pipe.CloseWriteFileDescriptor(); + char buf[1000]; + int r = read(pipe.GetReadFileDescriptor(), buf, sizeof buf); + + if (r == 0) + return HostProcess(pid); // No error. We're done. + + error.SetErrorString(buf); + + waitpid(pid, nullptr, 0); + + return HostProcess(); +} |

