Index: include/lldb/Host/linux/ProcessLauncherLinux.h =================================================================== --- include/lldb/Host/linux/ProcessLauncherLinux.h +++ include/lldb/Host/linux/ProcessLauncherLinux.h @@ -1,25 +0,0 @@ -//===-- ProcessLauncherAndroid.h --------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef lldb_Host_android_ProcessLauncherAndroid_h_ -#define lldb_Host_android_ProcessLauncherAndroid_h_ - -#include "lldb/Host/ProcessLauncher.h" - -namespace lldb_private { - -class ProcessLauncherLinux : public ProcessLauncher { -public: - virtual HostProcess LaunchProcess(const ProcessLaunchInfo &launch_info, - Error &error); -}; - -} // end of namespace lldb_private - -#endif Index: include/lldb/Host/posix/ProcessLauncherPosixFork.h =================================================================== --- /dev/null +++ include/lldb/Host/posix/ProcessLauncherPosixFork.h @@ -0,0 +1,25 @@ +//===-- ProcessLauncherPosixFork.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Host_posix_ProcessLauncherPosixFork_h_ +#define lldb_Host_posix_ProcessLauncherPosixFork_h_ + +#include "lldb/Host/ProcessLauncher.h" + +namespace lldb_private { + +class ProcessLauncherPosixFork : public ProcessLauncher { +public: + HostProcess LaunchProcess(const ProcessLaunchInfo &launch_info, + Error &error) override; +}; + +} // end of namespace lldb_private + +#endif Index: source/Host/CMakeLists.txt =================================================================== --- source/Host/CMakeLists.txt +++ source/Host/CMakeLists.txt @@ -90,6 +90,7 @@ posix/LockFilePosix.cpp posix/MainLoopPosix.cpp posix/PipePosix.cpp + posix/ProcessLauncherPosixFork.cpp ) if (NOT (CMAKE_SYSTEM_NAME MATCHES "Android")) @@ -122,7 +123,6 @@ linux/HostInfoLinux.cpp linux/HostThreadLinux.cpp linux/LibcGlue.cpp - linux/ProcessLauncherLinux.cpp linux/ThisThread.cpp ) list(APPEND LLDB_PLUGINS lldbPluginProcessLinux) Index: source/Host/common/Host.cpp =================================================================== --- source/Host/common/Host.cpp +++ source/Host/common/Host.cpp @@ -71,8 +71,8 @@ #if defined(_WIN32) #include "lldb/Host/windows/ProcessLauncherWindows.h" -#elif defined(__linux__) -#include "lldb/Host/linux/ProcessLauncherLinux.h" +#elif defined(__linux__) || defined(__NetBSD__) +#include "lldb/Host/posix/ProcessLauncherPosixFork.h" #else #include "lldb/Host/posix/ProcessLauncherPosix.h" #endif @@ -975,8 +975,8 @@ std::unique_ptr delegate_launcher; #if defined(_WIN32) delegate_launcher.reset(new ProcessLauncherWindows()); -#elif defined(__linux__) - delegate_launcher.reset(new ProcessLauncherLinux()); +#elif defined(__linux__) || defined(__NetBSD__) + delegate_launcher.reset(new ProcessLauncherPosixFork()); #else delegate_launcher.reset(new ProcessLauncherPosix()); #endif Index: source/Host/linux/ProcessLauncherLinux.cpp =================================================================== --- source/Host/linux/ProcessLauncherLinux.cpp +++ source/Host/linux/ProcessLauncherLinux.cpp @@ -1,225 +0,0 @@ -//===-- 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 -#include -#include - -#include - -#ifdef __ANDROID__ -#include -#endif - -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 -#include -#else -#include -#endif - -using namespace lldb; -using namespace lldb_private; - -static void FixupEnvironment(Args &env) { -#ifdef __ANDROID__ - // 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="; - for (auto &entry : env.entries()) { - if (entry.ref.startswith(path)) - return; - } - env.AppendArgument(llvm::StringRef("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(argv), - const_cast(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(argv), - const_cast(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(); -} Index: source/Host/posix/ProcessLauncherPosixFork.cpp =================================================================== --- /dev/null +++ source/Host/posix/ProcessLauncherPosixFork.cpp @@ -0,0 +1,231 @@ +//===-- 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/posix/ProcessLauncherPosixFork.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 +#include +#include + +#include + +#ifdef __ANDROID__ +#include +#endif + +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 +#include +#elif defined(__linux__) +#include +#endif + +using namespace lldb; +using namespace lldb_private; + +static void FixupEnvironment(Args &env) { +#ifdef __ANDROID__ + // 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="; + for (auto &entry : env.entries()) { + if (entry.ref.startswith(path)) + return; + } + env.AppendArgument(llvm::StringRef("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 DisableASLRIfRequested(int error_fd, const ProcessLaunchInfo &info) { +#if defined(__linux__) + 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"); + } +#endif +} + +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"); + + DisableASLRIfRequested(error_fd, info); + 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(PT_TRACE_ME, 0, nullptr, 0) == -1) + ExitWithError(error_fd, "ptrace"); + } + + // Execute. We should never return... + execve(argv[0], const_cast(argv), + const_cast(envp)); + +#if defined(__linux__) + 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(argv), + const_cast(envp)); + } +#endif + + // ...unless exec fails. In which case we definitely need to end the child + // here. + ExitWithError(error_fd, "execve"); +} + +HostProcess +ProcessLauncherPosixFork::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(); +} Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -33,7 +33,7 @@ #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/Host/posix/ProcessLauncherPosixFork.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Process.h" #include "lldb/Target/ProcessLaunchInfo.h" @@ -332,7 +332,7 @@ MaybeLogLaunchInfo(launch_info); ::pid_t pid = - ProcessLauncherLinux().LaunchProcess(launch_info, error).GetProcessId(); + ProcessLauncherPosixFork().LaunchProcess(launch_info, error).GetProcessId(); if (error.Fail()) return error;